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,1778 @@
|
|
|
1
|
+
"""Schema-validation tests for v3.8 claim_ref_alignment_audit_agent (T-S1..T-S8).
|
|
2
|
+
|
|
3
|
+
Per spec §7.1 in
|
|
4
|
+
docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md.
|
|
5
|
+
|
|
6
|
+
The test plan section §7.1 lists T-S1..T-S8 keyed to claim_audit_result
|
|
7
|
+
(§3.1) and claim_intent_manifest (§3.2) and uncited_assertion (§3.3).
|
|
8
|
+
§6 lint coverage is broader: it includes claim_drift D-INV-1..4 (§3.4),
|
|
9
|
+
constraint_violation CV-INV-1..4 (§3.5) and audit_sampling_summary
|
|
10
|
+
S-INV-1..4 (§4 step 3). This file follows §6 because that is the lint
|
|
11
|
+
contract under test — drift / constraint-violation / sampling
|
|
12
|
+
invariants get their own pos/neg fixtures alongside INV / M-INV / U-INV
|
|
13
|
+
so the lint's full 38-invariant surface is covered before the agent
|
|
14
|
+
prompt ships in Step 5.
|
|
15
|
+
|
|
16
|
+
Spec §7 names the test file `tests/test_claim_audit_schema.py`. This
|
|
17
|
+
repo's CI workflows discover tests under `scripts/test_*.py` and the
|
|
18
|
+
30+ existing test files all live there; we honor the repo convention
|
|
19
|
+
and keep the spec name's stem (`test_claim_audit_schema`) so anyone
|
|
20
|
+
greppping the spec lands at the right file.
|
|
21
|
+
|
|
22
|
+
Run:
|
|
23
|
+
python -m unittest scripts.test_claim_audit_schema -v
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import copy
|
|
28
|
+
import json
|
|
29
|
+
import unittest
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
from tests.test_helpers import (
|
|
34
|
+
build_schema_validator,
|
|
35
|
+
load_json_schema,
|
|
36
|
+
run_script,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
REPO = Path(__file__).resolve().parent.parent
|
|
40
|
+
PASSPORT = REPO / "shared/contracts/passport"
|
|
41
|
+
LINT = REPO / "scripts/check_claim_audit_consistency.py"
|
|
42
|
+
|
|
43
|
+
SCHEMA_PATHS: dict[str, Path] = {
|
|
44
|
+
"claim_audit_result": PASSPORT / "claim_audit_result.schema.json",
|
|
45
|
+
"claim_intent_manifest": PASSPORT / "claim_intent_manifest.schema.json",
|
|
46
|
+
"uncited_assertion": PASSPORT / "uncited_assertion.schema.json",
|
|
47
|
+
"claim_drift": PASSPORT / "claim_drift.schema.json",
|
|
48
|
+
"constraint_violation": PASSPORT / "constraint_violation.schema.json",
|
|
49
|
+
"uncited_audit_failure": PASSPORT / "uncited_audit_failure.schema.json",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Canonical fixture builders. Each helper returns a fresh dict so individual
|
|
55
|
+
# tests can mutate one field without leaking to siblings.
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
MANIFEST_ID = "M-2026-05-15T10:00:00Z-a1b2"
|
|
59
|
+
MANIFEST_ID_OTHER = "M-2026-05-15T10:05:00Z-c3d4"
|
|
60
|
+
SENTINEL_MANIFEST_ID = "M-0000-00-00T00:00:00Z-0000"
|
|
61
|
+
AUDIT_RUN_ID = "2026-05-15T10:10:00Z-9f8e"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def supported_entry() -> dict[str, Any]:
|
|
65
|
+
"""Minimal SUPPORTED row — INV-1 positive baseline."""
|
|
66
|
+
return {
|
|
67
|
+
"claim_id": "C-001",
|
|
68
|
+
"scoped_manifest_id": MANIFEST_ID,
|
|
69
|
+
"claim_text": "Sample preprints accounted for 67% of corpus.",
|
|
70
|
+
"ref_slug": "smith2024preprints",
|
|
71
|
+
"anchor_kind": "page",
|
|
72
|
+
"anchor_value": "12",
|
|
73
|
+
"judgment": "SUPPORTED",
|
|
74
|
+
"audit_status": "completed",
|
|
75
|
+
"defect_stage": None,
|
|
76
|
+
"rationale": "The cited page reports the 67% figure verbatim.",
|
|
77
|
+
"judge_model": "gpt-5.5-xhigh",
|
|
78
|
+
"judge_run_at": "2026-05-15T10:11:00Z",
|
|
79
|
+
"ref_retrieval_method": "api",
|
|
80
|
+
"audit_run_id": AUDIT_RUN_ID,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def manifest_entry(
|
|
85
|
+
manifest_id: str = MANIFEST_ID,
|
|
86
|
+
*,
|
|
87
|
+
emitted_by: str = "synthesis_agent",
|
|
88
|
+
claims: list[dict[str, Any]] | None = None,
|
|
89
|
+
mncs: list[dict[str, str]] | None = None,
|
|
90
|
+
) -> dict[str, Any]:
|
|
91
|
+
"""Build a minimal claim_intent_manifest entry."""
|
|
92
|
+
return {
|
|
93
|
+
"manifest_version": "1.0",
|
|
94
|
+
"manifest_id": manifest_id,
|
|
95
|
+
"emitted_by": emitted_by,
|
|
96
|
+
"emitted_at": "2026-05-15T09:55:00Z",
|
|
97
|
+
"claims": claims
|
|
98
|
+
if claims is not None
|
|
99
|
+
else [
|
|
100
|
+
{
|
|
101
|
+
"claim_id": "C-001",
|
|
102
|
+
"claim_text": "Sample preprints accounted for 67% of corpus.",
|
|
103
|
+
"intended_evidence_kind": "empirical",
|
|
104
|
+
"planned_refs": ["smith2024preprints"],
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"manifest_negative_constraints": mncs or [],
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def uncited_assertion_entry() -> dict[str, Any]:
|
|
112
|
+
"""Minimal U-INV-* positive baseline."""
|
|
113
|
+
return {
|
|
114
|
+
"finding_id": "UA-001",
|
|
115
|
+
"sentence_text": "Half of all submissions showed positive results.",
|
|
116
|
+
"section_path": "3. Results > 3.1 Overview",
|
|
117
|
+
"trigger_tokens": ["50%", "showed"],
|
|
118
|
+
"detected_at": "2026-05-15T10:12:00Z",
|
|
119
|
+
"rule_version": "D4-c-v1",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def claim_drift_entry(*, drift_kind: str = "EMITTED_NOT_INTENDED") -> dict[str, Any]:
|
|
124
|
+
"""Minimal D-INV-* positive baseline."""
|
|
125
|
+
base: dict[str, Any] = {
|
|
126
|
+
"finding_id": "CD-001",
|
|
127
|
+
"drift_kind": drift_kind,
|
|
128
|
+
"claim_text": "Drifted prose sentence the writer added without manifesting.",
|
|
129
|
+
"detected_at": "2026-05-15T10:13:00Z",
|
|
130
|
+
"rule_version": "D4-a-v1",
|
|
131
|
+
}
|
|
132
|
+
if drift_kind == "EMITTED_NOT_INTENDED":
|
|
133
|
+
base["section_path"] = "4. Discussion > 4.2 Implications"
|
|
134
|
+
base["manifest_claim_id"] = None
|
|
135
|
+
base["scoped_manifest_id"] = None
|
|
136
|
+
else: # INTENDED_NOT_EMITTED
|
|
137
|
+
base["manifest_claim_id"] = "C-001"
|
|
138
|
+
base["scoped_manifest_id"] = MANIFEST_ID
|
|
139
|
+
return base
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def constraint_violation_entry(
|
|
143
|
+
*,
|
|
144
|
+
constraint_id: str = "MNC-1",
|
|
145
|
+
manifest_claim_id: str | None = None,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
"""Minimal CV-INV-* positive baseline."""
|
|
148
|
+
return {
|
|
149
|
+
"finding_id": "CV-001",
|
|
150
|
+
"claim_text": "We observed causality between A and B.",
|
|
151
|
+
"section_path": "4. Discussion > 4.3 Limitations",
|
|
152
|
+
"violated_constraint_id": constraint_id,
|
|
153
|
+
"scoped_manifest_id": MANIFEST_ID,
|
|
154
|
+
"manifest_claim_id": manifest_claim_id,
|
|
155
|
+
"judge_verdict": "VIOLATED",
|
|
156
|
+
"rationale": "The MNC bars causal language without RCT evidence.",
|
|
157
|
+
"judge_model": "gpt-5.5-xhigh",
|
|
158
|
+
"judge_run_at": "2026-05-15T10:14:00Z",
|
|
159
|
+
"rule_version": "D4-a-v1",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def sampling_summary_entry(
|
|
164
|
+
*,
|
|
165
|
+
total: int = 150,
|
|
166
|
+
cap: int = 100,
|
|
167
|
+
) -> dict[str, Any]:
|
|
168
|
+
"""Minimal S-INV-* positive baseline (sampled run)."""
|
|
169
|
+
audited = list(range(0, total, max(1, total // cap)))[:cap]
|
|
170
|
+
return {
|
|
171
|
+
"audit_run_id": AUDIT_RUN_ID,
|
|
172
|
+
"max_claims_per_paper": cap,
|
|
173
|
+
"total_citation_count": total,
|
|
174
|
+
"audited_count": len(audited),
|
|
175
|
+
"audited_indices": audited,
|
|
176
|
+
"sampling_strategy": "stratified_buckets_v1",
|
|
177
|
+
"emitted_at": "2026-05-15T10:15:00Z",
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def uncited_audit_failure_entry(
|
|
182
|
+
*,
|
|
183
|
+
finding_id: str = "UAF-001",
|
|
184
|
+
fault_class: str = "judge_timeout",
|
|
185
|
+
manifest_claim_id: str | None = None,
|
|
186
|
+
) -> dict[str, Any]:
|
|
187
|
+
"""Minimal UAF-INV-* positive baseline (v3.8.2 / #118)."""
|
|
188
|
+
return {
|
|
189
|
+
"finding_id": finding_id,
|
|
190
|
+
"claim_text": "We observed causality between A and B.",
|
|
191
|
+
"section_path": "4. Discussion > 4.3 Limitations",
|
|
192
|
+
"scoped_manifest_id": MANIFEST_ID,
|
|
193
|
+
"manifest_claim_id": manifest_claim_id,
|
|
194
|
+
"fault_class": fault_class,
|
|
195
|
+
"rationale": f"{fault_class}: judge invocation failed after 30s",
|
|
196
|
+
"judge_model": "gpt-5.5-xhigh",
|
|
197
|
+
"judge_run_at": "2026-05-15T10:14:00Z",
|
|
198
|
+
"rule_version": "D4-c-v1-uaf-v1",
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def build_passport(
|
|
203
|
+
*,
|
|
204
|
+
manifests: list[dict[str, Any]] | None = None,
|
|
205
|
+
results: list[dict[str, Any]] | None = None,
|
|
206
|
+
uncited: list[dict[str, Any]] | None = None,
|
|
207
|
+
drifts: list[dict[str, Any]] | None = None,
|
|
208
|
+
violations: list[dict[str, Any]] | None = None,
|
|
209
|
+
samplings: list[dict[str, Any]] | None = None,
|
|
210
|
+
uaf: list[dict[str, Any]] | None = None,
|
|
211
|
+
) -> dict[str, Any]:
|
|
212
|
+
"""Build a minimal passport JSON wrapping the seven aggregates the lint reads (six pre-v3.8.2 + uncited_audit_failures[])."""
|
|
213
|
+
return {
|
|
214
|
+
"claim_intent_manifests": manifests if manifests is not None else [manifest_entry()],
|
|
215
|
+
"claim_audit_results": results or [],
|
|
216
|
+
"uncited_assertions": uncited or [],
|
|
217
|
+
"claim_drifts": drifts or [],
|
|
218
|
+
"constraint_violations": violations or [],
|
|
219
|
+
"audit_sampling_summaries": samplings or [],
|
|
220
|
+
"uncited_audit_failures": uaf or [],
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def write_passport(tmp: Path, body: dict[str, Any]) -> Path:
|
|
225
|
+
path = tmp / "passport.json"
|
|
226
|
+
path.write_text(json.dumps(body, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
227
|
+
return path
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def run_lint(passport: Path) -> tuple[int, str, str]:
|
|
231
|
+
"""Invoke the lint subprocess; returns (exit_code, stdout, stderr)."""
|
|
232
|
+
proc = run_script(
|
|
233
|
+
LINT,
|
|
234
|
+
"--passport",
|
|
235
|
+
str(passport),
|
|
236
|
+
extra_env={"PYTHONPATH": str(REPO)},
|
|
237
|
+
)
|
|
238
|
+
return proc.returncode, proc.stdout, proc.stderr
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ---------------------------------------------------------------------------
|
|
242
|
+
# T-S1: Schema-shape only — minimal SUPPORTED entry validates against the
|
|
243
|
+
# claim_audit_result schema. No lint invocation; pure Draft 2020-12 validate.
|
|
244
|
+
# ---------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
class TS1ValidMinimalEntry(unittest.TestCase):
|
|
247
|
+
"""T-S1: Valid minimal entry validates (SUPPORTED, all required fields)."""
|
|
248
|
+
|
|
249
|
+
def test_supported_entry_validates(self) -> None:
|
|
250
|
+
schema = load_json_schema(SCHEMA_PATHS["claim_audit_result"])
|
|
251
|
+
validator = build_schema_validator(schema)
|
|
252
|
+
errors = list(validator.iter_errors(supported_entry()))
|
|
253
|
+
self.assertEqual(errors, [], msg=f"unexpected validation errors: {errors}")
|
|
254
|
+
|
|
255
|
+
def test_all_five_schemas_parse_as_draft_2020_12(self) -> None:
|
|
256
|
+
for name, path in SCHEMA_PATHS.items():
|
|
257
|
+
with self.subTest(schema=name):
|
|
258
|
+
load_json_schema(path)
|
|
259
|
+
|
|
260
|
+
def test_subclaim_breakdown_optional_field_validates(self) -> None:
|
|
261
|
+
"""T-S9 (#213): sub_claim_breakdown is an additive optional field."""
|
|
262
|
+
schema = load_json_schema(SCHEMA_PATHS["claim_audit_result"])
|
|
263
|
+
validator = build_schema_validator(schema)
|
|
264
|
+
entry = supported_entry()
|
|
265
|
+
entry["judgment"] = "UNSUPPORTED"
|
|
266
|
+
entry["defect_stage"] = "source_description"
|
|
267
|
+
entry["sub_claim_breakdown"] = [
|
|
268
|
+
{
|
|
269
|
+
"sub_claim_text": "n-values are reported",
|
|
270
|
+
"sub_verdict": "SUPPORTED",
|
|
271
|
+
"evidence_pointer": "p.4 Table 1",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"sub_claim_text": "reporting is consistent across models",
|
|
275
|
+
"sub_verdict": "UNSUPPORTED",
|
|
276
|
+
"evidence_pointer": None,
|
|
277
|
+
},
|
|
278
|
+
]
|
|
279
|
+
errors = list(validator.iter_errors(entry))
|
|
280
|
+
self.assertEqual(errors, [], msg=f"unexpected validation errors: {errors}")
|
|
281
|
+
|
|
282
|
+
def test_subclaim_breakdown_rejects_unknown_subfield(self) -> None:
|
|
283
|
+
"""additionalProperties:false holds inside each breakdown item.
|
|
284
|
+
|
|
285
|
+
Two valid items satisfy minItems:2 so the ONLY violation is the bogus
|
|
286
|
+
subfield — this isolates additionalProperties:false from the array-length
|
|
287
|
+
constraint (round-2 review #2: a one-item fixture also tripped minItems,
|
|
288
|
+
so it would still pass even if additionalProperties were removed).
|
|
289
|
+
"""
|
|
290
|
+
schema = load_json_schema(SCHEMA_PATHS["claim_audit_result"])
|
|
291
|
+
validator = build_schema_validator(schema)
|
|
292
|
+
entry = supported_entry()
|
|
293
|
+
entry["judgment"] = "UNSUPPORTED"
|
|
294
|
+
entry["defect_stage"] = "source_description"
|
|
295
|
+
entry["sub_claim_breakdown"] = [
|
|
296
|
+
{"sub_claim_text": "x", "sub_verdict": "SUPPORTED", "bogus": 1},
|
|
297
|
+
{"sub_claim_text": "y", "sub_verdict": "UNSUPPORTED"},
|
|
298
|
+
]
|
|
299
|
+
errors = list(validator.iter_errors(entry))
|
|
300
|
+
self.assertNotEqual(errors, [], msg="expected unknown subfield to be rejected")
|
|
301
|
+
self.assertTrue(
|
|
302
|
+
any(e.validator == "additionalProperties" for e in errors),
|
|
303
|
+
msg=f"expected an additionalProperties error, got: {[e.validator for e in errors]}",
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
# Lint-driven invariant tests share a base that writes a tmp passport,
|
|
309
|
+
# invokes the lint subprocess, and asserts findings.
|
|
310
|
+
# ---------------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
class _LintTestBase(unittest.TestCase):
|
|
313
|
+
def setUp(self) -> None:
|
|
314
|
+
# Each test owns its tmpdir so concurrent unittest runners don't collide.
|
|
315
|
+
import tempfile
|
|
316
|
+
|
|
317
|
+
self._tmp_obj = tempfile.TemporaryDirectory()
|
|
318
|
+
self.addCleanup(self._tmp_obj.cleanup)
|
|
319
|
+
self.tmp = Path(self._tmp_obj.name)
|
|
320
|
+
|
|
321
|
+
def assertLintFinds(
|
|
322
|
+
self,
|
|
323
|
+
passport_body: dict[str, Any],
|
|
324
|
+
*,
|
|
325
|
+
invariant: str,
|
|
326
|
+
msg: str | None = None,
|
|
327
|
+
) -> tuple[int, str]:
|
|
328
|
+
"""Assert lint exits non-zero AND stdout contains `invariant` tag."""
|
|
329
|
+
path = write_passport(self.tmp, passport_body)
|
|
330
|
+
code, out, err = run_lint(path)
|
|
331
|
+
full_msg = (
|
|
332
|
+
f"\nexpected lint to flag {invariant}\n"
|
|
333
|
+
f"--- exit={code} ---\nstdout:\n{out}\nstderr:\n{err}\n"
|
|
334
|
+
)
|
|
335
|
+
self.assertEqual(code, 1, msg=full_msg)
|
|
336
|
+
self.assertIn(invariant, out, msg=full_msg)
|
|
337
|
+
return code, out
|
|
338
|
+
|
|
339
|
+
def assertLintClean(
|
|
340
|
+
self,
|
|
341
|
+
passport_body: dict[str, Any],
|
|
342
|
+
*,
|
|
343
|
+
msg: str | None = None,
|
|
344
|
+
) -> None:
|
|
345
|
+
"""Assert lint exits 0 (no findings) on a passport that should pass."""
|
|
346
|
+
path = write_passport(self.tmp, passport_body)
|
|
347
|
+
code, out, err = run_lint(path)
|
|
348
|
+
self.assertEqual(
|
|
349
|
+
code,
|
|
350
|
+
0,
|
|
351
|
+
msg=(msg or "")
|
|
352
|
+
+ f"\nexpected lint clean\nexit={code}\nstdout:\n{out}\nstderr:\n{err}\n",
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# ---------------------------------------------------------------------------
|
|
357
|
+
# T-S2: INV-1..INV-19 paired positive + negative fixtures (claim_audit_result).
|
|
358
|
+
# Each invariant is one subTest; baseline = SUPPORTED entry, negative cases
|
|
359
|
+
# mutate the field combination the invariant forbids.
|
|
360
|
+
# ---------------------------------------------------------------------------
|
|
361
|
+
|
|
362
|
+
class TS2ClaimAuditInvariants(_LintTestBase):
|
|
363
|
+
"""T-S2: each INV-N paired positive/negative fixture."""
|
|
364
|
+
|
|
365
|
+
# ----- Positive: every INV holds when the canonical SUPPORTED row stands.
|
|
366
|
+
def test_inv_baseline_positive(self) -> None:
|
|
367
|
+
self.assertLintClean(build_passport(results=[supported_entry()]))
|
|
368
|
+
|
|
369
|
+
# ----- Negative cases per invariant.
|
|
370
|
+
def test_inv_1_supported_with_non_null_defect(self) -> None:
|
|
371
|
+
# INV-1: SUPPORTED -> defect_stage=null AND violated_constraint_id=null
|
|
372
|
+
e = supported_entry()
|
|
373
|
+
e["defect_stage"] = "source_description"
|
|
374
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-1")
|
|
375
|
+
|
|
376
|
+
def test_inv_2_unsupported_with_null_defect(self) -> None:
|
|
377
|
+
# INV-2: UNSUPPORTED -> defect_stage != null
|
|
378
|
+
e = supported_entry()
|
|
379
|
+
e["judgment"] = "UNSUPPORTED"
|
|
380
|
+
e["defect_stage"] = None
|
|
381
|
+
e["rationale"] = "Mismatch found in source description."
|
|
382
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-2")
|
|
383
|
+
|
|
384
|
+
def test_inv_3_ambiguous_with_disallowed_defect(self) -> None:
|
|
385
|
+
# INV-3: AMBIGUOUS -> defect_stage NOT in {metadata, negative_constraint_violation}
|
|
386
|
+
e = supported_entry()
|
|
387
|
+
e["judgment"] = "AMBIGUOUS"
|
|
388
|
+
e["defect_stage"] = "metadata"
|
|
389
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-3")
|
|
390
|
+
|
|
391
|
+
def test_inv_4_retrieval_failed_inconclusive_wrong_defect(self) -> None:
|
|
392
|
+
# INV-4: RETRIEVAL_FAILED + inconclusive -> defect_stage=not_applicable
|
|
393
|
+
e = supported_entry()
|
|
394
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
395
|
+
e["audit_status"] = "inconclusive"
|
|
396
|
+
e["defect_stage"] = "retrieval_existence"
|
|
397
|
+
e["ref_retrieval_method"] = "failed"
|
|
398
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-4")
|
|
399
|
+
|
|
400
|
+
def test_inv_5_retrieval_failed_completed_wrong_defect(self) -> None:
|
|
401
|
+
# INV-5: RETRIEVAL_FAILED + completed -> defect_stage=retrieval_existence
|
|
402
|
+
e = supported_entry()
|
|
403
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
404
|
+
e["audit_status"] = "completed"
|
|
405
|
+
e["defect_stage"] = "source_description"
|
|
406
|
+
e["ref_retrieval_method"] = "not_found"
|
|
407
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-5")
|
|
408
|
+
|
|
409
|
+
def test_inv_6_anchor_none_without_rationale_prefix(self) -> None:
|
|
410
|
+
# INV-6: anchor_kind=none -> rationale starts with the canonical prefix
|
|
411
|
+
e = supported_entry()
|
|
412
|
+
e["anchor_kind"] = "none"
|
|
413
|
+
e["anchor_value"] = ""
|
|
414
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
415
|
+
e["audit_status"] = "inconclusive"
|
|
416
|
+
e["defect_stage"] = "not_applicable"
|
|
417
|
+
e["ref_retrieval_method"] = "not_attempted"
|
|
418
|
+
e["rationale"] = "anchor missing for this claim" # no v3.7.3 prefix
|
|
419
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-6")
|
|
420
|
+
|
|
421
|
+
def test_inv_6_anchor_none_with_non_empty_anchor_value(self) -> None:
|
|
422
|
+
# INV-6: anchor_kind=none MUST carry empty sentinel anchor_value.
|
|
423
|
+
# A stale residual value (e.g. "123") violates the schema contract.
|
|
424
|
+
# Step 13 R1 Gemini finding (a832d3f).
|
|
425
|
+
e = supported_entry()
|
|
426
|
+
e["anchor_kind"] = "none"
|
|
427
|
+
e["anchor_value"] = "123" # stale residual — must be rejected
|
|
428
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
429
|
+
e["audit_status"] = "inconclusive"
|
|
430
|
+
e["defect_stage"] = "not_applicable"
|
|
431
|
+
e["ref_retrieval_method"] = "not_attempted"
|
|
432
|
+
e["rationale"] = (
|
|
433
|
+
"v3.7.3 R-L3-1-A violation: cited claim C-001 carries anchor=none; "
|
|
434
|
+
"v3.7.3 finalizer should have gate-refused upstream."
|
|
435
|
+
)
|
|
436
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-6")
|
|
437
|
+
|
|
438
|
+
def test_inv_7_constraint_violation_without_violated_id(self) -> None:
|
|
439
|
+
# INV-7: negative_constraint_violation -> violated_constraint_id != null
|
|
440
|
+
e = supported_entry()
|
|
441
|
+
e["judgment"] = "UNSUPPORTED"
|
|
442
|
+
e["defect_stage"] = "negative_constraint_violation"
|
|
443
|
+
e["rationale"] = "Violated declared negative constraint."
|
|
444
|
+
e["violated_constraint_id"] = None
|
|
445
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-7")
|
|
446
|
+
|
|
447
|
+
def test_inv_8_constraint_violation_wrong_judgment(self) -> None:
|
|
448
|
+
# INV-8: negative_constraint_violation -> judgment=UNSUPPORTED
|
|
449
|
+
e = supported_entry()
|
|
450
|
+
e["judgment"] = "AMBIGUOUS"
|
|
451
|
+
e["defect_stage"] = "negative_constraint_violation"
|
|
452
|
+
e["violated_constraint_id"] = "MNC-1"
|
|
453
|
+
e["rationale"] = "Ambiguous violation of MNC-1."
|
|
454
|
+
# INV-3 also fires here; we assert INV-8 specifically.
|
|
455
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-8")
|
|
456
|
+
|
|
457
|
+
def test_inv_9_dispute_on_not_applicable(self) -> None:
|
|
458
|
+
# INV-9: upstream_dispute != null -> defect_stage NOT in {null, not_applicable}
|
|
459
|
+
e = supported_entry()
|
|
460
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
461
|
+
e["audit_status"] = "inconclusive"
|
|
462
|
+
e["defect_stage"] = "not_applicable"
|
|
463
|
+
e["ref_retrieval_method"] = "failed"
|
|
464
|
+
e["rationale"] = "Paywalled."
|
|
465
|
+
e["upstream_dispute"] = "Author disputes this paywall classification."
|
|
466
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-9")
|
|
467
|
+
|
|
468
|
+
def test_inv_10_failed_method_wrong_state(self) -> None:
|
|
469
|
+
# INV-10: ref_retrieval_method=failed -> RETRIEVAL_FAILED + inconclusive + not_applicable
|
|
470
|
+
e = supported_entry()
|
|
471
|
+
e["ref_retrieval_method"] = "failed"
|
|
472
|
+
# judgment still SUPPORTED -> INV-10 violation
|
|
473
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-10")
|
|
474
|
+
|
|
475
|
+
def test_inv_11_not_attempted_without_anchor_none(self) -> None:
|
|
476
|
+
# INV-11: ref_retrieval_method=not_attempted iff anchor_kind=none
|
|
477
|
+
e = supported_entry()
|
|
478
|
+
e["ref_retrieval_method"] = "not_attempted"
|
|
479
|
+
# anchor_kind still page -> INV-11 violation
|
|
480
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-11")
|
|
481
|
+
|
|
482
|
+
def test_inv_12_not_found_wrong_state(self) -> None:
|
|
483
|
+
# INV-12: ref_retrieval_method=not_found iff fabricated-reference triple
|
|
484
|
+
e = supported_entry()
|
|
485
|
+
e["ref_retrieval_method"] = "not_found"
|
|
486
|
+
# judgment still SUPPORTED -> INV-12 violation
|
|
487
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-12")
|
|
488
|
+
|
|
489
|
+
def test_inv_13_metadata_wrong_method(self) -> None:
|
|
490
|
+
# INV-13: defect_stage=metadata -> ref_retrieval_method in {api, manual_pdf}
|
|
491
|
+
e = supported_entry()
|
|
492
|
+
e["judgment"] = "UNSUPPORTED"
|
|
493
|
+
e["defect_stage"] = "metadata"
|
|
494
|
+
e["rationale"] = "Author/year mismatch."
|
|
495
|
+
e["ref_retrieval_method"] = "failed"
|
|
496
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-13")
|
|
497
|
+
|
|
498
|
+
def test_inv_14_audit_tool_failure_without_fault_class_prefix(self) -> None:
|
|
499
|
+
# INV-14: ref_retrieval_method=audit_tool_failure -> rationale begins with fault-class tag
|
|
500
|
+
e = supported_entry()
|
|
501
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
502
|
+
e["audit_status"] = "inconclusive"
|
|
503
|
+
e["defect_stage"] = "not_applicable"
|
|
504
|
+
e["ref_retrieval_method"] = "audit_tool_failure"
|
|
505
|
+
e["rationale"] = "Something went wrong." # missing tag
|
|
506
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-14")
|
|
507
|
+
|
|
508
|
+
def test_inv_15_dangling_scoped_manifest_id(self) -> None:
|
|
509
|
+
# INV-15: (scoped_manifest_id, claim_id) must resolve in some manifest entry
|
|
510
|
+
e = supported_entry()
|
|
511
|
+
e["scoped_manifest_id"] = "M-2099-01-01T00:00:00Z-dead" # not in manifests
|
|
512
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-15")
|
|
513
|
+
|
|
514
|
+
def test_inv_15_sentinel_manifest_permitted(self) -> None:
|
|
515
|
+
# INV-15 sentinel positive case — MANIFEST-MISSING fallback row.
|
|
516
|
+
e = supported_entry()
|
|
517
|
+
e["scoped_manifest_id"] = SENTINEL_MANIFEST_ID
|
|
518
|
+
passport = build_passport(manifests=[], results=[e])
|
|
519
|
+
self.assertLintClean(
|
|
520
|
+
passport,
|
|
521
|
+
msg="sentinel scoped_manifest_id must be accepted in MANIFEST-MISSING fallback",
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def test_inv_16_empty_anchor_value_with_non_none_kind(self) -> None:
|
|
525
|
+
# INV-16: anchor_kind != none -> anchor_value non-empty after strip
|
|
526
|
+
e = supported_entry()
|
|
527
|
+
e["anchor_value"] = " " # whitespace only
|
|
528
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-16")
|
|
529
|
+
|
|
530
|
+
def test_inv_16_url_encoded_whitespace_does_not_bypass(self) -> None:
|
|
531
|
+
# INV-16: URL-encoded whitespace (%20, %09) must NOT bypass the firm
|
|
532
|
+
# rule — docstring + schema both require non-empty *after* URL-decode.
|
|
533
|
+
# Step 13 R1 Gemini finding (a832d3f).
|
|
534
|
+
e = supported_entry()
|
|
535
|
+
e["anchor_value"] = "%20%20%09"
|
|
536
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-16")
|
|
537
|
+
|
|
538
|
+
def test_inv_16_url_encoded_non_whitespace_passes(self) -> None:
|
|
539
|
+
# INV-16: URL-encoded printable content must still satisfy the rule.
|
|
540
|
+
e = supported_entry()
|
|
541
|
+
e["anchor_kind"] = "quote"
|
|
542
|
+
e["anchor_value"] = "ten%20cited%20words" # 'ten cited words' after decode
|
|
543
|
+
self.assertLintClean(build_passport(results=[e]))
|
|
544
|
+
|
|
545
|
+
def test_inv_17_constraint_id_inner_hyphen_form(self) -> None:
|
|
546
|
+
# INV-17: NC-C{n}-{m} parse rule — NO inner hyphen between C and digits
|
|
547
|
+
manifest = manifest_entry(
|
|
548
|
+
claims=[
|
|
549
|
+
{
|
|
550
|
+
"claim_id": "C-001",
|
|
551
|
+
"claim_text": "Causal claim.",
|
|
552
|
+
"intended_evidence_kind": "empirical",
|
|
553
|
+
"planned_refs": [],
|
|
554
|
+
"negative_constraints": [
|
|
555
|
+
{
|
|
556
|
+
# malformed: NC-C-001-1 instead of NC-C001-1
|
|
557
|
+
"constraint_id": "NC-C-001-1",
|
|
558
|
+
"rule": "Must not claim causality without RCT.",
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
}
|
|
562
|
+
],
|
|
563
|
+
)
|
|
564
|
+
# Schema pattern itself rejects this — lint surfaces INV-17 explicitly.
|
|
565
|
+
passport = build_passport(manifests=[manifest], results=[])
|
|
566
|
+
self.assertLintFinds(passport, invariant="INV-17")
|
|
567
|
+
|
|
568
|
+
def test_inv_18_inconclusive_not_applicable_wrong_method(self) -> None:
|
|
569
|
+
# INV-18: (RETRIEVAL_FAILED, inconclusive, not_applicable) -> method in
|
|
570
|
+
# {not_attempted, failed, audit_tool_failure}. `api` is forbidden.
|
|
571
|
+
e = supported_entry()
|
|
572
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
573
|
+
e["audit_status"] = "inconclusive"
|
|
574
|
+
e["defect_stage"] = "not_applicable"
|
|
575
|
+
e["ref_retrieval_method"] = "api"
|
|
576
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-18")
|
|
577
|
+
|
|
578
|
+
# ----- INV-19 (#213): sub_claim_breakdown pins the normalized-PARTIAL shape.
|
|
579
|
+
def _partial_entry(self) -> dict[str, Any]:
|
|
580
|
+
"""True-partial UNSUPPORTED row: the INV-19 positive baseline."""
|
|
581
|
+
e = supported_entry()
|
|
582
|
+
e["judgment"] = "UNSUPPORTED"
|
|
583
|
+
e["audit_status"] = "completed"
|
|
584
|
+
e["defect_stage"] = "source_description"
|
|
585
|
+
e["sub_claim_breakdown"] = [
|
|
586
|
+
{"sub_claim_text": "a", "sub_verdict": "SUPPORTED"},
|
|
587
|
+
{"sub_claim_text": "b", "sub_verdict": "UNSUPPORTED"},
|
|
588
|
+
]
|
|
589
|
+
return e
|
|
590
|
+
|
|
591
|
+
def test_inv_19_valid_partial_breakdown_passes(self) -> None:
|
|
592
|
+
# INV-19 positive baseline: a true-partial row is clean.
|
|
593
|
+
self.assertLintClean(build_passport(results=[self._partial_entry()]))
|
|
594
|
+
|
|
595
|
+
def test_inv_19_breakdown_on_supported_row_flagged(self) -> None:
|
|
596
|
+
# INV-19: a sub_claim_breakdown on a non-UNSUPPORTED row is illegal.
|
|
597
|
+
e = supported_entry() # judgment=SUPPORTED, defect_stage=None
|
|
598
|
+
e["sub_claim_breakdown"] = [
|
|
599
|
+
{"sub_claim_text": "a", "sub_verdict": "SUPPORTED"},
|
|
600
|
+
{"sub_claim_text": "b", "sub_verdict": "UNSUPPORTED"},
|
|
601
|
+
]
|
|
602
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
603
|
+
|
|
604
|
+
def test_inv_19_breakdown_wrong_defect_stage_flagged(self) -> None:
|
|
605
|
+
# INV-19: breakdown row must carry defect_stage=source_description.
|
|
606
|
+
e = self._partial_entry()
|
|
607
|
+
e["defect_stage"] = "synthesis_overclaim"
|
|
608
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
609
|
+
|
|
610
|
+
def test_inv_19_all_unsupported_breakdown_flagged(self) -> None:
|
|
611
|
+
# INV-19: not true-partial — needs >=1 SUPPORTED (round-1 review #2).
|
|
612
|
+
e = self._partial_entry()
|
|
613
|
+
e["sub_claim_breakdown"] = [
|
|
614
|
+
{"sub_claim_text": "a", "sub_verdict": "UNSUPPORTED"},
|
|
615
|
+
{"sub_claim_text": "b", "sub_verdict": "UNSUPPORTED"},
|
|
616
|
+
]
|
|
617
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
618
|
+
|
|
619
|
+
def test_inv_19_all_supported_breakdown_flagged(self) -> None:
|
|
620
|
+
# INV-19: not true-partial — needs >=1 non-SUPPORTED.
|
|
621
|
+
e = self._partial_entry()
|
|
622
|
+
e["sub_claim_breakdown"] = [
|
|
623
|
+
{"sub_claim_text": "a", "sub_verdict": "SUPPORTED"},
|
|
624
|
+
{"sub_claim_text": "b", "sub_verdict": "SUPPORTED"},
|
|
625
|
+
]
|
|
626
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
627
|
+
|
|
628
|
+
def test_inv_19_single_item_breakdown_flagged(self) -> None:
|
|
629
|
+
# INV-19: a decomposition with <2 items is not a true partial.
|
|
630
|
+
e = self._partial_entry()
|
|
631
|
+
e["sub_claim_breakdown"] = [
|
|
632
|
+
{"sub_claim_text": "a", "sub_verdict": "SUPPORTED"},
|
|
633
|
+
]
|
|
634
|
+
# schema minItems:2 also rejects this; lint must independently flag it
|
|
635
|
+
# so a future schema relaxation can't silently admit a 1-item breakdown.
|
|
636
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
637
|
+
|
|
638
|
+
def test_inv_19_breakdown_on_ambiguous_row_flagged(self) -> None:
|
|
639
|
+
# INV-19 judgment-pin isolation: AMBIGUOUS + completed + source_description
|
|
640
|
+
# passes INV-3 AND is in ALLOWED_MATRIX, so the ONLY invariant that can
|
|
641
|
+
# fire is INV-19's judgment check. This catches a partially-broken guard
|
|
642
|
+
# that forgets the judgment pin but keeps the defect_stage pin — which the
|
|
643
|
+
# SUPPORTED-row case can't catch (it also has defect_stage=None).
|
|
644
|
+
# (round-2 review #3.)
|
|
645
|
+
e = self._partial_entry()
|
|
646
|
+
e["judgment"] = "AMBIGUOUS" # defect_stage stays source_description
|
|
647
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
648
|
+
|
|
649
|
+
def test_inv_19_missing_sub_verdict_not_counted_as_non_supported(self) -> None:
|
|
650
|
+
# INV-19 must NOT read a missing/out-of-enum sub_verdict as "non-SUPPORTED"
|
|
651
|
+
# (round-2 review #1). [SUPPORTED, <missing>] is NOT true-partial: it has no
|
|
652
|
+
# valid non-SUPPORTED verdict. A guard using `v != "SUPPORTED"` would wrongly
|
|
653
|
+
# accept it. The schema also rejects the missing required key, but INV-19 must
|
|
654
|
+
# independently flag the not-true-partial shape with its own tag.
|
|
655
|
+
e = self._partial_entry()
|
|
656
|
+
e["sub_claim_breakdown"] = [
|
|
657
|
+
{"sub_claim_text": "a", "sub_verdict": "SUPPORTED"},
|
|
658
|
+
{"sub_claim_text": "b"}, # sub_verdict missing -> not a valid non-SUPPORTED
|
|
659
|
+
]
|
|
660
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-19")
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
# ---------------------------------------------------------------------------
|
|
664
|
+
# T-S3: anchor_kind=none + INV-6 violation paths.
|
|
665
|
+
# ---------------------------------------------------------------------------
|
|
666
|
+
|
|
667
|
+
class TS3AnchorNoneInv6(_LintTestBase):
|
|
668
|
+
"""T-S3: anchor=none entries that miss rationale prefix or use wrong method."""
|
|
669
|
+
|
|
670
|
+
def _none_entry(self) -> dict[str, Any]:
|
|
671
|
+
e = supported_entry()
|
|
672
|
+
e["anchor_kind"] = "none"
|
|
673
|
+
e["anchor_value"] = ""
|
|
674
|
+
e["judgment"] = "RETRIEVAL_FAILED"
|
|
675
|
+
e["audit_status"] = "inconclusive"
|
|
676
|
+
e["defect_stage"] = "not_applicable"
|
|
677
|
+
e["ref_retrieval_method"] = "not_attempted"
|
|
678
|
+
e["rationale"] = "v3.7.3 R-L3-1-A violation: no anchor on cited claim."
|
|
679
|
+
return e
|
|
680
|
+
|
|
681
|
+
def test_anchor_none_positive_baseline(self) -> None:
|
|
682
|
+
# Canonical INV-6 compliant row should pass.
|
|
683
|
+
self.assertLintClean(build_passport(results=[self._none_entry()]))
|
|
684
|
+
|
|
685
|
+
def test_anchor_none_missing_rationale_prefix(self) -> None:
|
|
686
|
+
e = self._none_entry()
|
|
687
|
+
e["rationale"] = "anchor missing" # no prefix
|
|
688
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-6")
|
|
689
|
+
|
|
690
|
+
def test_anchor_none_wrong_retrieval_method(self) -> None:
|
|
691
|
+
e = self._none_entry()
|
|
692
|
+
e["ref_retrieval_method"] = "failed" # should be not_attempted
|
|
693
|
+
# INV-11 ↔ INV-6 mismatch — either fires; spec couples the firm rule via INV-6/11.
|
|
694
|
+
self.assertLintFinds(build_passport(results=[e]), invariant="INV-6")
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
# ---------------------------------------------------------------------------
|
|
698
|
+
# T-S4: M-INV-1 duplicate claim_id within ONE manifest.
|
|
699
|
+
# ---------------------------------------------------------------------------
|
|
700
|
+
|
|
701
|
+
class TS4ManifestInv1(_LintTestBase):
|
|
702
|
+
"""T-S4: duplicate claim_id within one manifest is rejected; cross-manifest collision permitted."""
|
|
703
|
+
|
|
704
|
+
def test_duplicate_claim_id_within_one_manifest_rejected(self) -> None:
|
|
705
|
+
manifest = manifest_entry(
|
|
706
|
+
claims=[
|
|
707
|
+
{
|
|
708
|
+
"claim_id": "C-001",
|
|
709
|
+
"claim_text": "First claim.",
|
|
710
|
+
"intended_evidence_kind": "empirical",
|
|
711
|
+
"planned_refs": [],
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
"claim_id": "C-001", # duplicate within ONE manifest
|
|
715
|
+
"claim_text": "Second collision.",
|
|
716
|
+
"intended_evidence_kind": "empirical",
|
|
717
|
+
"planned_refs": [],
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
)
|
|
721
|
+
self.assertLintFinds(
|
|
722
|
+
build_passport(manifests=[manifest]),
|
|
723
|
+
invariant="M-INV-1",
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
def test_cross_manifest_claim_id_collision_permitted(self) -> None:
|
|
727
|
+
manifest_a = manifest_entry(manifest_id=MANIFEST_ID)
|
|
728
|
+
manifest_b = manifest_entry(
|
|
729
|
+
manifest_id=MANIFEST_ID_OTHER,
|
|
730
|
+
emitted_by="draft_writer_agent",
|
|
731
|
+
)
|
|
732
|
+
self.assertLintClean(
|
|
733
|
+
build_passport(manifests=[manifest_a, manifest_b]),
|
|
734
|
+
msg="cross-manifest C-001 collision MUST be permitted (joinable pair)",
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
def test_m_inv_4_duplicate_manifest_id_across_passport_rejected(self) -> None:
|
|
738
|
+
manifest_a = manifest_entry(manifest_id=MANIFEST_ID)
|
|
739
|
+
manifest_b = manifest_entry(
|
|
740
|
+
manifest_id=MANIFEST_ID, # collides with manifest_a
|
|
741
|
+
emitted_by="draft_writer_agent",
|
|
742
|
+
)
|
|
743
|
+
self.assertLintFinds(
|
|
744
|
+
build_passport(manifests=[manifest_a, manifest_b]),
|
|
745
|
+
invariant="M-INV-4",
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
# ---------------------------------------------------------------------------
|
|
750
|
+
# T-S5: M-INV-2 dangling NC-C{n}-{m} (no parent claim with C-{n}).
|
|
751
|
+
# ---------------------------------------------------------------------------
|
|
752
|
+
|
|
753
|
+
class TS5ManifestInv2(_LintTestBase):
|
|
754
|
+
"""T-S5: NC-C{n}-{m} must scope under a claims[] entry with claim_id=C-{n}."""
|
|
755
|
+
|
|
756
|
+
def test_dangling_claim_level_nc(self) -> None:
|
|
757
|
+
manifest = manifest_entry(
|
|
758
|
+
claims=[
|
|
759
|
+
{
|
|
760
|
+
"claim_id": "C-001",
|
|
761
|
+
"claim_text": "First claim.",
|
|
762
|
+
"intended_evidence_kind": "empirical",
|
|
763
|
+
"planned_refs": [],
|
|
764
|
+
"negative_constraints": [
|
|
765
|
+
{
|
|
766
|
+
# NC scoped to C-002, but no C-002 claim exists.
|
|
767
|
+
"constraint_id": "NC-C002-1",
|
|
768
|
+
"rule": "Mismatched parent.",
|
|
769
|
+
}
|
|
770
|
+
],
|
|
771
|
+
}
|
|
772
|
+
],
|
|
773
|
+
)
|
|
774
|
+
self.assertLintFinds(
|
|
775
|
+
build_passport(manifests=[manifest]),
|
|
776
|
+
invariant="M-INV-2",
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
# ---------------------------------------------------------------------------
|
|
781
|
+
# T-S6: M-INV-3 claim-level NC attempting to override MNC.
|
|
782
|
+
# ---------------------------------------------------------------------------
|
|
783
|
+
|
|
784
|
+
class TS6ManifestInv3(_LintTestBase):
|
|
785
|
+
"""T-S6: claim-level NC cannot DROP a global MNC; ADD is permitted."""
|
|
786
|
+
|
|
787
|
+
def test_claim_level_nc_collides_with_mnc_id(self) -> None:
|
|
788
|
+
# M-INV-3 — claim-level constraint reusing an MNC-* id is the override
|
|
789
|
+
# signature the lint must reject (claim level can ADD via fresh NC-C{n}-{m}
|
|
790
|
+
# ids, never via re-using an MNC-* id).
|
|
791
|
+
manifest = manifest_entry(
|
|
792
|
+
claims=[
|
|
793
|
+
{
|
|
794
|
+
"claim_id": "C-001",
|
|
795
|
+
"claim_text": "Causal claim.",
|
|
796
|
+
"intended_evidence_kind": "empirical",
|
|
797
|
+
"planned_refs": [],
|
|
798
|
+
"negative_constraints": [
|
|
799
|
+
{
|
|
800
|
+
"constraint_id": "MNC-1", # disallowed at claim level
|
|
801
|
+
"rule": "Trying to override.",
|
|
802
|
+
}
|
|
803
|
+
],
|
|
804
|
+
}
|
|
805
|
+
],
|
|
806
|
+
mncs=[
|
|
807
|
+
{
|
|
808
|
+
"constraint_id": "MNC-1",
|
|
809
|
+
"rule": "Must not claim causality.",
|
|
810
|
+
}
|
|
811
|
+
],
|
|
812
|
+
)
|
|
813
|
+
self.assertLintFinds(
|
|
814
|
+
build_passport(manifests=[manifest]),
|
|
815
|
+
invariant="M-INV-3",
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
# ---------------------------------------------------------------------------
|
|
820
|
+
# T-S7: U-INV-1..U-INV-4 paired positive/negative.
|
|
821
|
+
# ---------------------------------------------------------------------------
|
|
822
|
+
|
|
823
|
+
class TS7UncitedAssertionInvariants(_LintTestBase):
|
|
824
|
+
"""T-S7: U-INV-1..U-INV-4 paired pos/neg fixtures."""
|
|
825
|
+
|
|
826
|
+
def test_u_baseline_positive(self) -> None:
|
|
827
|
+
self.assertLintClean(build_passport(uncited=[uncited_assertion_entry()]))
|
|
828
|
+
|
|
829
|
+
def test_u_inv_1_duplicate_finding_id(self) -> None:
|
|
830
|
+
a = uncited_assertion_entry()
|
|
831
|
+
b = uncited_assertion_entry() # same UA-001
|
|
832
|
+
self.assertLintFinds(
|
|
833
|
+
build_passport(uncited=[a, b]),
|
|
834
|
+
invariant="U-INV-1",
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
def test_u_inv_2_empty_trigger_tokens(self) -> None:
|
|
838
|
+
e = uncited_assertion_entry()
|
|
839
|
+
e["trigger_tokens"] = []
|
|
840
|
+
# Schema also rejects (minItems=1); lint surfaces U-INV-2 specifically.
|
|
841
|
+
self.assertLintFinds(
|
|
842
|
+
build_passport(uncited=[e]),
|
|
843
|
+
invariant="U-INV-2",
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
def test_u_inv_3_wrong_rule_version(self) -> None:
|
|
847
|
+
e = uncited_assertion_entry()
|
|
848
|
+
e["rule_version"] = "D4-c-v0"
|
|
849
|
+
# Schema const rejects; lint surfaces U-INV-3 specifically.
|
|
850
|
+
self.assertLintFinds(
|
|
851
|
+
build_passport(uncited=[e]),
|
|
852
|
+
invariant="U-INV-3",
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
def test_u_inv_4_orphan_manifest_pointer(self) -> None:
|
|
856
|
+
# manifest_claim_id set without matching manifest entry -> U-INV-4
|
|
857
|
+
e = uncited_assertion_entry()
|
|
858
|
+
e["manifest_claim_id"] = "C-999"
|
|
859
|
+
e["scoped_manifest_id"] = MANIFEST_ID
|
|
860
|
+
self.assertLintFinds(
|
|
861
|
+
build_passport(uncited=[e]),
|
|
862
|
+
invariant="U-INV-4",
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
def test_u_inv_4_null_manifest_id_with_set_claim_id(self) -> None:
|
|
866
|
+
# manifest_claim_id != null requires scoped_manifest_id != null.
|
|
867
|
+
e = uncited_assertion_entry()
|
|
868
|
+
e["manifest_claim_id"] = "C-001"
|
|
869
|
+
e["scoped_manifest_id"] = None
|
|
870
|
+
self.assertLintFinds(
|
|
871
|
+
build_passport(uncited=[e]),
|
|
872
|
+
invariant="U-INV-4",
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
# ---------------------------------------------------------------------------
|
|
877
|
+
# T-S8: (judgment, audit_status, defect_stage) matrix exhaustive coverage.
|
|
878
|
+
# Spec §3.1 table: 9 positive rows, lint rejects every (j, a, d) triple
|
|
879
|
+
# outside the table; ≥5 disallowed combinations exercised explicitly.
|
|
880
|
+
# ---------------------------------------------------------------------------
|
|
881
|
+
|
|
882
|
+
class TS8AllowedMatrix(_LintTestBase):
|
|
883
|
+
"""T-S8: every allowed triple validates; ≥5 disallowed combinations rejected."""
|
|
884
|
+
|
|
885
|
+
# Positive matrix rows — each is a self-contained passport fragment.
|
|
886
|
+
ALLOWED_ROWS = [
|
|
887
|
+
# (judgment, audit_status, defect_stage, ref_retrieval_method overrides)
|
|
888
|
+
("SUPPORTED", "completed", None, {"ref_retrieval_method": "api"}),
|
|
889
|
+
("AMBIGUOUS", "completed", "source_description", {"ref_retrieval_method": "api"}),
|
|
890
|
+
("AMBIGUOUS", "completed", "citation_anchor", {"ref_retrieval_method": "api"}),
|
|
891
|
+
("AMBIGUOUS", "completed", "synthesis_overclaim", {"ref_retrieval_method": "api"}),
|
|
892
|
+
("AMBIGUOUS", "completed", None, {"ref_retrieval_method": "api"}),
|
|
893
|
+
("UNSUPPORTED", "completed", "source_description", {"ref_retrieval_method": "api"}),
|
|
894
|
+
("UNSUPPORTED", "completed", "metadata", {"ref_retrieval_method": "api"}),
|
|
895
|
+
("UNSUPPORTED", "completed", "citation_anchor", {"ref_retrieval_method": "api"}),
|
|
896
|
+
("UNSUPPORTED", "completed", "synthesis_overclaim", {"ref_retrieval_method": "api"}),
|
|
897
|
+
(
|
|
898
|
+
"UNSUPPORTED",
|
|
899
|
+
"completed",
|
|
900
|
+
"negative_constraint_violation",
|
|
901
|
+
{
|
|
902
|
+
"ref_retrieval_method": "api",
|
|
903
|
+
"violated_constraint_id": "MNC-1",
|
|
904
|
+
},
|
|
905
|
+
),
|
|
906
|
+
(
|
|
907
|
+
"RETRIEVAL_FAILED",
|
|
908
|
+
"completed",
|
|
909
|
+
"retrieval_existence",
|
|
910
|
+
{"ref_retrieval_method": "not_found"},
|
|
911
|
+
),
|
|
912
|
+
(
|
|
913
|
+
"RETRIEVAL_FAILED",
|
|
914
|
+
"inconclusive",
|
|
915
|
+
"not_applicable",
|
|
916
|
+
{"ref_retrieval_method": "failed"},
|
|
917
|
+
),
|
|
918
|
+
]
|
|
919
|
+
|
|
920
|
+
# Negative cases — ≥5 representative disallowed combinations.
|
|
921
|
+
DISALLOWED_ROWS = [
|
|
922
|
+
# (judgment, audit_status, defect_stage, overrides, expected_invariant)
|
|
923
|
+
(
|
|
924
|
+
"SUPPORTED",
|
|
925
|
+
"completed",
|
|
926
|
+
"source_description",
|
|
927
|
+
{"ref_retrieval_method": "api"},
|
|
928
|
+
"INV-1",
|
|
929
|
+
),
|
|
930
|
+
(
|
|
931
|
+
"UNSUPPORTED",
|
|
932
|
+
"completed",
|
|
933
|
+
None,
|
|
934
|
+
{"ref_retrieval_method": "api"},
|
|
935
|
+
"INV-2",
|
|
936
|
+
),
|
|
937
|
+
(
|
|
938
|
+
"RETRIEVAL_FAILED",
|
|
939
|
+
"completed",
|
|
940
|
+
"not_applicable",
|
|
941
|
+
{"ref_retrieval_method": "api"},
|
|
942
|
+
"matrix",
|
|
943
|
+
),
|
|
944
|
+
(
|
|
945
|
+
"SUPPORTED",
|
|
946
|
+
"inconclusive",
|
|
947
|
+
None,
|
|
948
|
+
{"ref_retrieval_method": "api"},
|
|
949
|
+
"matrix",
|
|
950
|
+
),
|
|
951
|
+
(
|
|
952
|
+
"AMBIGUOUS",
|
|
953
|
+
"completed",
|
|
954
|
+
"metadata",
|
|
955
|
+
{"ref_retrieval_method": "api"},
|
|
956
|
+
"INV-3",
|
|
957
|
+
),
|
|
958
|
+
(
|
|
959
|
+
"AMBIGUOUS",
|
|
960
|
+
"inconclusive",
|
|
961
|
+
None,
|
|
962
|
+
{"ref_retrieval_method": "api"},
|
|
963
|
+
"matrix",
|
|
964
|
+
),
|
|
965
|
+
]
|
|
966
|
+
|
|
967
|
+
def _build_row(self, j: str, a: str, d: Any, overrides: dict[str, Any]) -> dict[str, Any]:
|
|
968
|
+
e = supported_entry()
|
|
969
|
+
e["judgment"] = j
|
|
970
|
+
e["audit_status"] = a
|
|
971
|
+
e["defect_stage"] = d
|
|
972
|
+
if d == "negative_constraint_violation":
|
|
973
|
+
e["rationale"] = "Violated declared negative constraint."
|
|
974
|
+
elif d == "retrieval_existence":
|
|
975
|
+
e["rationale"] = "Reference does not exist."
|
|
976
|
+
elif d == "not_applicable":
|
|
977
|
+
e["rationale"] = "Paywall — full text not retrievable."
|
|
978
|
+
elif d is not None:
|
|
979
|
+
e["rationale"] = f"Defect at {d}."
|
|
980
|
+
e.update(overrides)
|
|
981
|
+
return e
|
|
982
|
+
|
|
983
|
+
def test_every_allowed_row_passes(self) -> None:
|
|
984
|
+
for j, a, d, overrides in self.ALLOWED_ROWS:
|
|
985
|
+
with self.subTest(judgment=j, audit_status=a, defect_stage=d):
|
|
986
|
+
e = self._build_row(j, a, d, overrides)
|
|
987
|
+
self.assertLintClean(
|
|
988
|
+
build_passport(results=[e]),
|
|
989
|
+
msg=f"row ({j}, {a}, {d}) should pass",
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
def test_disallowed_rows_rejected(self) -> None:
|
|
993
|
+
for j, a, d, overrides, invariant in self.DISALLOWED_ROWS:
|
|
994
|
+
with self.subTest(judgment=j, audit_status=a, defect_stage=d):
|
|
995
|
+
e = self._build_row(j, a, d, overrides)
|
|
996
|
+
self.assertLintFinds(
|
|
997
|
+
build_passport(results=[e]),
|
|
998
|
+
invariant=invariant,
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
# ---------------------------------------------------------------------------
|
|
1003
|
+
# Spec §6 lint 4a — D-INV-1..D-INV-4 claim_drift cross-array integrity.
|
|
1004
|
+
# Not labelled T-Sx in spec §7.1 but mandatory per §6.4a. Paired pos/neg.
|
|
1005
|
+
# ---------------------------------------------------------------------------
|
|
1006
|
+
|
|
1007
|
+
class TSDDriftInvariants(_LintTestBase):
|
|
1008
|
+
"""Spec §6.4a: claim_drift D-INV-1..D-INV-4."""
|
|
1009
|
+
|
|
1010
|
+
def test_d_baseline_emitted_not_intended(self) -> None:
|
|
1011
|
+
self.assertLintClean(build_passport(drifts=[claim_drift_entry()]))
|
|
1012
|
+
|
|
1013
|
+
def test_d_baseline_intended_not_emitted(self) -> None:
|
|
1014
|
+
drift = claim_drift_entry(drift_kind="INTENDED_NOT_EMITTED")
|
|
1015
|
+
self.assertLintClean(build_passport(drifts=[drift]))
|
|
1016
|
+
|
|
1017
|
+
def test_d_inv_1_duplicate_finding_id(self) -> None:
|
|
1018
|
+
a = claim_drift_entry()
|
|
1019
|
+
b = claim_drift_entry() # CD-001 collides
|
|
1020
|
+
self.assertLintFinds(build_passport(drifts=[a, b]), invariant="D-INV-1")
|
|
1021
|
+
|
|
1022
|
+
def test_d_inv_2_intended_not_emitted_missing_manifest_pointer(self) -> None:
|
|
1023
|
+
drift = claim_drift_entry(drift_kind="INTENDED_NOT_EMITTED")
|
|
1024
|
+
drift["manifest_claim_id"] = None # MUST be non-null for INTENDED_NOT_EMITTED
|
|
1025
|
+
drift["scoped_manifest_id"] = None
|
|
1026
|
+
self.assertLintFinds(build_passport(drifts=[drift]), invariant="D-INV-2")
|
|
1027
|
+
|
|
1028
|
+
def test_d_inv_2_emitted_not_intended_with_manifest_pointer(self) -> None:
|
|
1029
|
+
drift = claim_drift_entry(drift_kind="EMITTED_NOT_INTENDED")
|
|
1030
|
+
drift["manifest_claim_id"] = "C-001" # MUST be null for EMITTED_NOT_INTENDED
|
|
1031
|
+
drift["scoped_manifest_id"] = MANIFEST_ID
|
|
1032
|
+
self.assertLintFinds(build_passport(drifts=[drift]), invariant="D-INV-2")
|
|
1033
|
+
|
|
1034
|
+
def test_d_inv_2_dangling_intended_not_emitted_pair(self) -> None:
|
|
1035
|
+
drift = claim_drift_entry(drift_kind="INTENDED_NOT_EMITTED")
|
|
1036
|
+
drift["manifest_claim_id"] = "C-999" # no such claim in manifest
|
|
1037
|
+
drift["scoped_manifest_id"] = MANIFEST_ID
|
|
1038
|
+
self.assertLintFinds(build_passport(drifts=[drift]), invariant="D-INV-2")
|
|
1039
|
+
|
|
1040
|
+
def test_d_inv_3_wrong_rule_version(self) -> None:
|
|
1041
|
+
drift = claim_drift_entry()
|
|
1042
|
+
drift["rule_version"] = "D4-a-v0"
|
|
1043
|
+
self.assertLintFinds(build_passport(drifts=[drift]), invariant="D-INV-3")
|
|
1044
|
+
|
|
1045
|
+
def test_d_inv_4_uncited_and_drift_collision(self) -> None:
|
|
1046
|
+
# A single sentence appears in both uncited_assertions[] and claim_drifts[].
|
|
1047
|
+
sentence = "Half of submissions showed positive results."
|
|
1048
|
+
uncited = uncited_assertion_entry()
|
|
1049
|
+
uncited["sentence_text"] = sentence
|
|
1050
|
+
drift = claim_drift_entry()
|
|
1051
|
+
drift["claim_text"] = sentence
|
|
1052
|
+
self.assertLintFinds(
|
|
1053
|
+
build_passport(uncited=[uncited], drifts=[drift]),
|
|
1054
|
+
invariant="D-INV-4",
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
# ---------------------------------------------------------------------------
|
|
1059
|
+
# Spec §6.4b — CV-INV-1..CV-INV-4 constraint_violation cross-array integrity.
|
|
1060
|
+
# ---------------------------------------------------------------------------
|
|
1061
|
+
|
|
1062
|
+
class TSCVConstraintViolationInvariants(_LintTestBase):
|
|
1063
|
+
"""Spec §6.4b: constraint_violation CV-INV-1..CV-INV-4."""
|
|
1064
|
+
|
|
1065
|
+
def _manifest_with_mnc_and_nc(self) -> dict[str, Any]:
|
|
1066
|
+
return manifest_entry(
|
|
1067
|
+
claims=[
|
|
1068
|
+
{
|
|
1069
|
+
"claim_id": "C-001",
|
|
1070
|
+
"claim_text": "Causal claim.",
|
|
1071
|
+
"intended_evidence_kind": "empirical",
|
|
1072
|
+
"planned_refs": [],
|
|
1073
|
+
"negative_constraints": [
|
|
1074
|
+
{
|
|
1075
|
+
"constraint_id": "NC-C001-1",
|
|
1076
|
+
"rule": "No causal language without RCT.",
|
|
1077
|
+
}
|
|
1078
|
+
],
|
|
1079
|
+
}
|
|
1080
|
+
],
|
|
1081
|
+
mncs=[{"constraint_id": "MNC-1", "rule": "Global rule."}],
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
def test_cv_baseline_mnc_violation(self) -> None:
|
|
1085
|
+
passport = build_passport(
|
|
1086
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1087
|
+
violations=[constraint_violation_entry()],
|
|
1088
|
+
)
|
|
1089
|
+
self.assertLintClean(passport)
|
|
1090
|
+
|
|
1091
|
+
def test_cv_baseline_nc_violation(self) -> None:
|
|
1092
|
+
passport = build_passport(
|
|
1093
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1094
|
+
violations=[
|
|
1095
|
+
constraint_violation_entry(
|
|
1096
|
+
constraint_id="NC-C001-1",
|
|
1097
|
+
manifest_claim_id="C-001",
|
|
1098
|
+
)
|
|
1099
|
+
],
|
|
1100
|
+
)
|
|
1101
|
+
self.assertLintClean(passport)
|
|
1102
|
+
|
|
1103
|
+
def test_cv_inv_1_duplicate_finding_id(self) -> None:
|
|
1104
|
+
a = constraint_violation_entry()
|
|
1105
|
+
b = constraint_violation_entry()
|
|
1106
|
+
passport = build_passport(
|
|
1107
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1108
|
+
violations=[a, b],
|
|
1109
|
+
)
|
|
1110
|
+
self.assertLintFinds(passport, invariant="CV-INV-1")
|
|
1111
|
+
|
|
1112
|
+
def test_cv_inv_2_dangling_mnc(self) -> None:
|
|
1113
|
+
passport = build_passport(
|
|
1114
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1115
|
+
violations=[constraint_violation_entry(constraint_id="MNC-99")],
|
|
1116
|
+
)
|
|
1117
|
+
self.assertLintFinds(passport, invariant="CV-INV-2")
|
|
1118
|
+
|
|
1119
|
+
def test_cv_inv_2_dangling_nc(self) -> None:
|
|
1120
|
+
passport = build_passport(
|
|
1121
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1122
|
+
violations=[
|
|
1123
|
+
constraint_violation_entry(
|
|
1124
|
+
constraint_id="NC-C999-1",
|
|
1125
|
+
manifest_claim_id="C-999",
|
|
1126
|
+
)
|
|
1127
|
+
],
|
|
1128
|
+
)
|
|
1129
|
+
self.assertLintFinds(passport, invariant="CV-INV-2")
|
|
1130
|
+
|
|
1131
|
+
def test_cv_inv_3_mnc_with_set_manifest_claim_id(self) -> None:
|
|
1132
|
+
passport = build_passport(
|
|
1133
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1134
|
+
violations=[
|
|
1135
|
+
constraint_violation_entry(
|
|
1136
|
+
constraint_id="MNC-1",
|
|
1137
|
+
manifest_claim_id="C-001", # MUST be null for MNC
|
|
1138
|
+
)
|
|
1139
|
+
],
|
|
1140
|
+
)
|
|
1141
|
+
self.assertLintFinds(passport, invariant="CV-INV-3")
|
|
1142
|
+
|
|
1143
|
+
def test_cv_inv_3_nc_polarity_mismatch(self) -> None:
|
|
1144
|
+
passport = build_passport(
|
|
1145
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1146
|
+
violations=[
|
|
1147
|
+
constraint_violation_entry(
|
|
1148
|
+
constraint_id="NC-C001-1",
|
|
1149
|
+
manifest_claim_id="C-002", # MUST match the C-001 in NC id
|
|
1150
|
+
)
|
|
1151
|
+
],
|
|
1152
|
+
)
|
|
1153
|
+
self.assertLintFinds(passport, invariant="CV-INV-3")
|
|
1154
|
+
|
|
1155
|
+
def test_cv_inv_4_duplicate_per_constraint(self) -> None:
|
|
1156
|
+
# Two violations for (same sentence, same constraint) -> dedup rule.
|
|
1157
|
+
a = constraint_violation_entry()
|
|
1158
|
+
b = constraint_violation_entry()
|
|
1159
|
+
b["finding_id"] = "CV-002" # avoid CV-INV-1 collision
|
|
1160
|
+
passport = build_passport(
|
|
1161
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1162
|
+
violations=[a, b],
|
|
1163
|
+
)
|
|
1164
|
+
self.assertLintFinds(passport, invariant="CV-INV-4")
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
# ---------------------------------------------------------------------------
|
|
1168
|
+
# Step 13 R8 codex P2-1 — CV-INV-4 dedupe key extension. Two manifests in
|
|
1169
|
+
# the same passport can carry colliding MNC-* / NC-* ids; the same sentence
|
|
1170
|
+
# text may legitimately violate both. The pre-fix dedupe key
|
|
1171
|
+
# (section_path, claim_text_hash, violated_constraint_id) false-positives
|
|
1172
|
+
# these as duplicates. Extend the key to scope by manifest_id as well.
|
|
1173
|
+
# ---------------------------------------------------------------------------
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
class TSCVDedupeManifestScope(_LintTestBase):
|
|
1177
|
+
"""T-SCV-DEDUPE: CV-INV-4 dedupe must scope by scoped_manifest_id."""
|
|
1178
|
+
|
|
1179
|
+
def _two_manifests_with_colliding_mnc(self) -> list[dict[str, Any]]:
|
|
1180
|
+
# Two manifests each carrying the SAME constraint_id "MNC-1" but
|
|
1181
|
+
# bound to different manifests (M-INV-4 permits — manifest_id is
|
|
1182
|
+
# the joinable scope). Schema requires claims[] non-empty so we
|
|
1183
|
+
# add a minimal claim to each manifest; the claim itself is not
|
|
1184
|
+
# under test.
|
|
1185
|
+
return [
|
|
1186
|
+
manifest_entry(
|
|
1187
|
+
manifest_id=MANIFEST_ID,
|
|
1188
|
+
mncs=[{"constraint_id": "MNC-1", "rule": "No comparative claim."}],
|
|
1189
|
+
),
|
|
1190
|
+
manifest_entry(
|
|
1191
|
+
manifest_id=MANIFEST_ID_OTHER,
|
|
1192
|
+
mncs=[{"constraint_id": "MNC-1", "rule": "No temporal claim."}],
|
|
1193
|
+
),
|
|
1194
|
+
]
|
|
1195
|
+
|
|
1196
|
+
def test_same_constraint_id_across_manifests_not_deduped(self) -> None:
|
|
1197
|
+
# Two CV rows on the same sentence text + same constraint_id (MNC-1)
|
|
1198
|
+
# but DIFFERENT scoped_manifest_id. Legitimate per M-INV-4 +
|
|
1199
|
+
# manifest_negative_constraints scoping — dedupe must keep both.
|
|
1200
|
+
a = constraint_violation_entry(constraint_id="MNC-1")
|
|
1201
|
+
a["scoped_manifest_id"] = MANIFEST_ID
|
|
1202
|
+
b = constraint_violation_entry(constraint_id="MNC-1")
|
|
1203
|
+
b["finding_id"] = "CV-002"
|
|
1204
|
+
b["scoped_manifest_id"] = MANIFEST_ID_OTHER
|
|
1205
|
+
passport = build_passport(
|
|
1206
|
+
manifests=self._two_manifests_with_colliding_mnc(),
|
|
1207
|
+
violations=[a, b],
|
|
1208
|
+
)
|
|
1209
|
+
# Pre-fix: CV-INV-4 false-positives → lint reports CV-INV-4 line.
|
|
1210
|
+
# Post-fix: lint MUST NOT fire CV-INV-4 on cross-manifest collision.
|
|
1211
|
+
self.assertLintClean(passport)
|
|
1212
|
+
|
|
1213
|
+
def test_same_constraint_id_same_manifest_still_deduped(self) -> None:
|
|
1214
|
+
# Negative test: dedupe still catches true within-manifest duplicates.
|
|
1215
|
+
a = constraint_violation_entry(constraint_id="MNC-1")
|
|
1216
|
+
b = constraint_violation_entry(constraint_id="MNC-1")
|
|
1217
|
+
b["finding_id"] = "CV-002"
|
|
1218
|
+
# Both rows reference the same manifest_id (default MANIFEST_ID).
|
|
1219
|
+
passport = build_passport(
|
|
1220
|
+
manifests=[self._two_manifests_with_colliding_mnc()[0]], # single manifest
|
|
1221
|
+
violations=[a, b],
|
|
1222
|
+
)
|
|
1223
|
+
self.assertLintFinds(passport, invariant="CV-INV-4")
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
# ---------------------------------------------------------------------------
|
|
1227
|
+
# Spec §6.4c — S-INV-1..S-INV-4 audit_sampling_summary invariants.
|
|
1228
|
+
# ---------------------------------------------------------------------------
|
|
1229
|
+
|
|
1230
|
+
class TSSamplingInvariants(_LintTestBase):
|
|
1231
|
+
"""Spec §6.4c: audit_sampling_summary S-INV-1..S-INV-4."""
|
|
1232
|
+
|
|
1233
|
+
def test_s_baseline(self) -> None:
|
|
1234
|
+
self.assertLintClean(build_passport(samplings=[sampling_summary_entry()]))
|
|
1235
|
+
|
|
1236
|
+
def test_s_inv_1_count_vs_indices_mismatch(self) -> None:
|
|
1237
|
+
e = sampling_summary_entry()
|
|
1238
|
+
e["audited_count"] = e["audited_count"] - 1 # off-by-one
|
|
1239
|
+
self.assertLintFinds(build_passport(samplings=[e]), invariant="S-INV-1")
|
|
1240
|
+
|
|
1241
|
+
def test_s_inv_2_count_exceeds_cap(self) -> None:
|
|
1242
|
+
e = sampling_summary_entry(total=50, cap=10)
|
|
1243
|
+
e["audited_count"] = 20
|
|
1244
|
+
e["audited_indices"] = list(range(20))
|
|
1245
|
+
self.assertLintFinds(build_passport(samplings=[e]), invariant="S-INV-2")
|
|
1246
|
+
|
|
1247
|
+
def test_s_inv_2_count_exceeds_total(self) -> None:
|
|
1248
|
+
e = sampling_summary_entry(total=10, cap=100)
|
|
1249
|
+
e["audited_count"] = 20
|
|
1250
|
+
e["audited_indices"] = list(range(20))
|
|
1251
|
+
self.assertLintFinds(build_passport(samplings=[e]), invariant="S-INV-2")
|
|
1252
|
+
|
|
1253
|
+
def test_s_inv_4_non_ascending_indices(self) -> None:
|
|
1254
|
+
e = sampling_summary_entry(total=20, cap=10)
|
|
1255
|
+
e["audited_indices"] = [5, 3, 7, 9, 11, 13, 15, 17, 19, 2]
|
|
1256
|
+
e["audited_count"] = 10
|
|
1257
|
+
self.assertLintFinds(build_passport(samplings=[e]), invariant="S-INV-4")
|
|
1258
|
+
|
|
1259
|
+
def test_s_inv_4_duplicate_indices(self) -> None:
|
|
1260
|
+
e = sampling_summary_entry(total=20, cap=10)
|
|
1261
|
+
e["audited_indices"] = [0, 1, 1, 3, 4, 5, 6, 7, 8, 9]
|
|
1262
|
+
e["audited_count"] = 10
|
|
1263
|
+
self.assertLintFinds(build_passport(samplings=[e]), invariant="S-INV-4")
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
# ---------------------------------------------------------------------------
|
|
1267
|
+
# Spec §6.4d (v3.8.2 / #118) — UAF-INV-1..UAF-INV-5 uncited_audit_failure
|
|
1268
|
+
# cross-array integrity and dedup. Mirrors CV-INV pattern.
|
|
1269
|
+
# ---------------------------------------------------------------------------
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
class TSUAFUncitedAuditFailureInvariants(_LintTestBase):
|
|
1273
|
+
"""Spec §6.4d: uncited_audit_failure UAF-INV-1..UAF-INV-5 (v3.8.2 / #118)."""
|
|
1274
|
+
|
|
1275
|
+
def _manifest_with_mnc_and_nc(self) -> dict[str, Any]:
|
|
1276
|
+
return manifest_entry(
|
|
1277
|
+
claims=[
|
|
1278
|
+
{
|
|
1279
|
+
"claim_id": "C-001",
|
|
1280
|
+
"claim_text": "Causal claim.",
|
|
1281
|
+
"intended_evidence_kind": "empirical",
|
|
1282
|
+
"planned_refs": [],
|
|
1283
|
+
"negative_constraints": [
|
|
1284
|
+
{
|
|
1285
|
+
"constraint_id": "NC-C001-1",
|
|
1286
|
+
"rule": "No causal language without RCT.",
|
|
1287
|
+
}
|
|
1288
|
+
],
|
|
1289
|
+
}
|
|
1290
|
+
],
|
|
1291
|
+
mncs=[{"constraint_id": "MNC-1", "rule": "Global rule."}],
|
|
1292
|
+
)
|
|
1293
|
+
|
|
1294
|
+
# ----- Schema-shape only.
|
|
1295
|
+
def test_uaf_schema_valid_minimal_entry(self) -> None:
|
|
1296
|
+
schema = load_json_schema(SCHEMA_PATHS["uncited_audit_failure"])
|
|
1297
|
+
validator = build_schema_validator(schema)
|
|
1298
|
+
errors = list(validator.iter_errors(uncited_audit_failure_entry()))
|
|
1299
|
+
self.assertEqual(errors, [], msg=f"unexpected validation errors: {errors}")
|
|
1300
|
+
|
|
1301
|
+
def test_uaf_schema_fault_class_enum_matches_constants(self) -> None:
|
|
1302
|
+
"""The schema's fault_class enum MUST match INV14_FAULT_CLASS_TAGS exactly.
|
|
1303
|
+
|
|
1304
|
+
Without this guard, extending INV14_FAULT_CLASS_TAGS in the Python
|
|
1305
|
+
constants module would silently produce UAF rows that fail schema
|
|
1306
|
+
validation (the pipeline maps a new exception class to a new tag,
|
|
1307
|
+
but the schema's closed enum hasn't been bumped). Per Gemini cross-
|
|
1308
|
+
model review R2 P2 (2026-05-17): both surfaces must move together,
|
|
1309
|
+
and a rule_version bump (D4-c-v1-uaf-v2 or later) should accompany
|
|
1310
|
+
any enum extension.
|
|
1311
|
+
"""
|
|
1312
|
+
from scripts._claim_audit_constants import INV14_FAULT_CLASS_TAGS
|
|
1313
|
+
|
|
1314
|
+
schema = load_json_schema(SCHEMA_PATHS["uncited_audit_failure"])
|
|
1315
|
+
schema_enum = set(schema["properties"]["fault_class"]["enum"])
|
|
1316
|
+
constants_set = set(INV14_FAULT_CLASS_TAGS)
|
|
1317
|
+
self.assertEqual(
|
|
1318
|
+
schema_enum,
|
|
1319
|
+
constants_set,
|
|
1320
|
+
msg=(
|
|
1321
|
+
"uncited_audit_failure.schema.json `fault_class` enum drifted "
|
|
1322
|
+
"from INV14_FAULT_CLASS_TAGS. Either align the schema with the "
|
|
1323
|
+
"python constant, or if intentionally extending the taxonomy "
|
|
1324
|
+
"bump `rule_version` (e.g., D4-c-v1-uaf-v2) and document the "
|
|
1325
|
+
"ramp in spec §3.6."
|
|
1326
|
+
),
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
def test_uaf_schema_rejects_missing_fault_class(self) -> None:
|
|
1330
|
+
schema = load_json_schema(SCHEMA_PATHS["uncited_audit_failure"])
|
|
1331
|
+
validator = build_schema_validator(schema)
|
|
1332
|
+
e = uncited_audit_failure_entry()
|
|
1333
|
+
del e["fault_class"]
|
|
1334
|
+
errors = list(validator.iter_errors(e))
|
|
1335
|
+
self.assertNotEqual(errors, [], msg="missing fault_class must fail schema validation")
|
|
1336
|
+
|
|
1337
|
+
def test_uaf_schema_rejects_unknown_fault_class(self) -> None:
|
|
1338
|
+
schema = load_json_schema(SCHEMA_PATHS["uncited_audit_failure"])
|
|
1339
|
+
validator = build_schema_validator(schema)
|
|
1340
|
+
e = uncited_audit_failure_entry()
|
|
1341
|
+
e["fault_class"] = "made_up_class"
|
|
1342
|
+
errors = list(validator.iter_errors(e))
|
|
1343
|
+
self.assertNotEqual(errors, [], msg="unknown fault_class must fail schema validation")
|
|
1344
|
+
|
|
1345
|
+
# ----- Lint baseline: clean passport with one UAF row passes.
|
|
1346
|
+
def test_uaf_baseline_mnc_failure(self) -> None:
|
|
1347
|
+
passport = build_passport(
|
|
1348
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1349
|
+
uaf=[uncited_audit_failure_entry()],
|
|
1350
|
+
)
|
|
1351
|
+
self.assertLintClean(passport)
|
|
1352
|
+
|
|
1353
|
+
def test_uaf_baseline_nc_failure(self) -> None:
|
|
1354
|
+
# NC-C path: judge failed on a claim-level constraint check; manifest_claim_id set.
|
|
1355
|
+
passport = build_passport(
|
|
1356
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1357
|
+
uaf=[uncited_audit_failure_entry(manifest_claim_id="C-001")],
|
|
1358
|
+
)
|
|
1359
|
+
self.assertLintClean(passport)
|
|
1360
|
+
|
|
1361
|
+
# ----- UAF-INV-1: finding_id uniqueness.
|
|
1362
|
+
def test_uaf_inv_1_duplicate_finding_id(self) -> None:
|
|
1363
|
+
a = uncited_audit_failure_entry()
|
|
1364
|
+
b = uncited_audit_failure_entry() # same finding_id "UAF-001"
|
|
1365
|
+
# Per Gemini cross-model review P2 (2026-05-17): keep this test
|
|
1366
|
+
# isolated to UAF-INV-1 by giving b a different claim_text so the
|
|
1367
|
+
# passport does NOT simultaneously trip UAF-INV-4 (per-(sentence,
|
|
1368
|
+
# manifest) dedup). One test should target one invariant cleanly.
|
|
1369
|
+
b["claim_text"] = "Different text so UAF-INV-4 dedup does not fire."
|
|
1370
|
+
passport = build_passport(
|
|
1371
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1372
|
+
uaf=[a, b],
|
|
1373
|
+
)
|
|
1374
|
+
self.assertLintFinds(passport, invariant="UAF-INV-1")
|
|
1375
|
+
|
|
1376
|
+
# ----- UAF-INV-2: scoped_manifest_id must resolve in claim_intent_manifests[].
|
|
1377
|
+
def test_uaf_inv_2_dangling_manifest_id(self) -> None:
|
|
1378
|
+
e = uncited_audit_failure_entry()
|
|
1379
|
+
e["scoped_manifest_id"] = SENTINEL_MANIFEST_ID # not in manifests
|
|
1380
|
+
passport = build_passport(
|
|
1381
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1382
|
+
uaf=[e],
|
|
1383
|
+
)
|
|
1384
|
+
self.assertLintFinds(passport, invariant="UAF-INV-2")
|
|
1385
|
+
|
|
1386
|
+
# ----- UAF-INV-3: (scoped_manifest_id, manifest_claim_id) pair integrity.
|
|
1387
|
+
def test_uaf_inv_3_dangling_claim_id(self) -> None:
|
|
1388
|
+
e = uncited_audit_failure_entry(manifest_claim_id="C-999") # no such claim in manifest
|
|
1389
|
+
passport = build_passport(
|
|
1390
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1391
|
+
uaf=[e],
|
|
1392
|
+
)
|
|
1393
|
+
self.assertLintFinds(passport, invariant="UAF-INV-3")
|
|
1394
|
+
|
|
1395
|
+
def test_uaf_inv_3_null_claim_id_allowed_when_mnc_only(self) -> None:
|
|
1396
|
+
# manifest_claim_id=null is legitimate when the failure was against MNCs only.
|
|
1397
|
+
passport = build_passport(
|
|
1398
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1399
|
+
uaf=[uncited_audit_failure_entry(manifest_claim_id=None)],
|
|
1400
|
+
)
|
|
1401
|
+
self.assertLintClean(passport)
|
|
1402
|
+
|
|
1403
|
+
# ----- UAF-INV-4: per-(sentence, manifest) dedup.
|
|
1404
|
+
def test_uaf_inv_4_same_sentence_same_manifest_dup(self) -> None:
|
|
1405
|
+
a = uncited_audit_failure_entry()
|
|
1406
|
+
b = uncited_audit_failure_entry(finding_id="UAF-002")
|
|
1407
|
+
# b shares (scoped_manifest_id, section_path, claim_text) with a.
|
|
1408
|
+
passport = build_passport(
|
|
1409
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1410
|
+
uaf=[a, b],
|
|
1411
|
+
)
|
|
1412
|
+
self.assertLintFinds(passport, invariant="UAF-INV-4")
|
|
1413
|
+
|
|
1414
|
+
def test_uaf_inv_4_same_sentence_different_manifests_not_deduped(self) -> None:
|
|
1415
|
+
# Two manifests both failing on the same sentence text emit two rows
|
|
1416
|
+
# legitimately (mirrors CV-INV-4 cross-manifest reasoning).
|
|
1417
|
+
other_manifest = manifest_entry(
|
|
1418
|
+
manifest_id=MANIFEST_ID_OTHER,
|
|
1419
|
+
mncs=[{"constraint_id": "MNC-1", "rule": "Different rule."}],
|
|
1420
|
+
)
|
|
1421
|
+
a = uncited_audit_failure_entry()
|
|
1422
|
+
a["scoped_manifest_id"] = MANIFEST_ID
|
|
1423
|
+
b = uncited_audit_failure_entry(finding_id="UAF-002")
|
|
1424
|
+
b["scoped_manifest_id"] = MANIFEST_ID_OTHER
|
|
1425
|
+
passport = build_passport(
|
|
1426
|
+
manifests=[self._manifest_with_mnc_and_nc(), other_manifest],
|
|
1427
|
+
uaf=[a, b],
|
|
1428
|
+
)
|
|
1429
|
+
self.assertLintClean(passport)
|
|
1430
|
+
|
|
1431
|
+
# ----- UAF-INV-5: rationale fault_class prefix.
|
|
1432
|
+
def test_uaf_inv_5_rationale_missing_fault_prefix(self) -> None:
|
|
1433
|
+
e = uncited_audit_failure_entry()
|
|
1434
|
+
e["rationale"] = "Some narrative without the fault_class prefix"
|
|
1435
|
+
passport = build_passport(
|
|
1436
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1437
|
+
uaf=[e],
|
|
1438
|
+
)
|
|
1439
|
+
self.assertLintFinds(passport, invariant="UAF-INV-5")
|
|
1440
|
+
|
|
1441
|
+
def test_uaf_inv_5_rationale_wrong_fault_prefix(self) -> None:
|
|
1442
|
+
# Rationale starts with "made_up_class:" — schema rejected via fault_class
|
|
1443
|
+
# enum, but UAF-INV-5 also flags rationale prefix mismatch independently.
|
|
1444
|
+
# Here we keep fault_class valid but flip rationale to a different valid
|
|
1445
|
+
# tag — should still trip UAF-INV-5 (prefix must match the row's
|
|
1446
|
+
# fault_class, not just any known tag).
|
|
1447
|
+
e = uncited_audit_failure_entry(fault_class="judge_timeout")
|
|
1448
|
+
e["rationale"] = "judge_api_error: wrong tag for this row"
|
|
1449
|
+
passport = build_passport(
|
|
1450
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1451
|
+
uaf=[e],
|
|
1452
|
+
)
|
|
1453
|
+
self.assertLintFinds(passport, invariant="UAF-INV-5")
|
|
1454
|
+
|
|
1455
|
+
# ----- Cross-aggregate exclusivity: UAF vs constraint_violation.
|
|
1456
|
+
def test_uaf_cross_aggregate_exclusive_with_cv(self) -> None:
|
|
1457
|
+
# Same (sentence, manifest) MUST NOT have both an audit_tool_failure (UAF)
|
|
1458
|
+
# and a VIOLATED row (CV). They are mutually exclusive verdict states.
|
|
1459
|
+
cv = constraint_violation_entry()
|
|
1460
|
+
uaf = uncited_audit_failure_entry()
|
|
1461
|
+
passport = build_passport(
|
|
1462
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1463
|
+
violations=[cv],
|
|
1464
|
+
uaf=[uaf],
|
|
1465
|
+
)
|
|
1466
|
+
self.assertLintFinds(passport, invariant="UAF-INV-6")
|
|
1467
|
+
|
|
1468
|
+
# ----- Malformed-payload hardening (Codex R2 P2-2 + P2-3, 2026-05-17).
|
|
1469
|
+
def test_uaf_malformed_manifest_claim_id_does_not_crash_lint(self) -> None:
|
|
1470
|
+
# Schema validator flags type errors separately; lint walker must
|
|
1471
|
+
# skip cleanly on unhashable values rather than raise TypeError.
|
|
1472
|
+
e = uncited_audit_failure_entry()
|
|
1473
|
+
e["manifest_claim_id"] = ["unhashable", "list", "as", "claim_id"]
|
|
1474
|
+
passport = build_passport(
|
|
1475
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1476
|
+
uaf=[e],
|
|
1477
|
+
)
|
|
1478
|
+
# Lint should report SOMETHING (schema-shape finding), exit 1, NOT crash.
|
|
1479
|
+
path = write_passport(self.tmp, passport)
|
|
1480
|
+
code, _, err = run_lint(path)
|
|
1481
|
+
self.assertEqual(
|
|
1482
|
+
code,
|
|
1483
|
+
1,
|
|
1484
|
+
msg=f"expected clean lint failure on malformed manifest_claim_id, got exit={code}\nstderr:\n{err}",
|
|
1485
|
+
)
|
|
1486
|
+
self.assertNotIn(
|
|
1487
|
+
"Traceback",
|
|
1488
|
+
err,
|
|
1489
|
+
msg="lint MUST NOT crash with a traceback on malformed manifest_claim_id (Codex R2 P2-2)",
|
|
1490
|
+
)
|
|
1491
|
+
|
|
1492
|
+
def test_uaf_malformed_rationale_does_not_crash_lint(self) -> None:
|
|
1493
|
+
e = uncited_audit_failure_entry()
|
|
1494
|
+
e["rationale"] = ["unhashable", "rationale"]
|
|
1495
|
+
passport = build_passport(
|
|
1496
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1497
|
+
uaf=[e],
|
|
1498
|
+
)
|
|
1499
|
+
path = write_passport(self.tmp, passport)
|
|
1500
|
+
code, _, err = run_lint(path)
|
|
1501
|
+
self.assertEqual(
|
|
1502
|
+
code,
|
|
1503
|
+
1,
|
|
1504
|
+
msg=f"expected clean lint failure on malformed rationale, got exit={code}\nstderr:\n{err}",
|
|
1505
|
+
)
|
|
1506
|
+
self.assertNotIn(
|
|
1507
|
+
"Traceback",
|
|
1508
|
+
err,
|
|
1509
|
+
msg="lint MUST NOT crash with a traceback on malformed rationale (Codex R2 P2-3)",
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
# ----- Co-existence with uncited_assertions[] IS permitted.
|
|
1513
|
+
def test_uaf_coexists_with_uncited_assertion(self) -> None:
|
|
1514
|
+
# D4-c detector positive (UA) + judge failure (UAF) on the same sentence
|
|
1515
|
+
# are independent signals; cross-aggregate exclusivity does NOT apply.
|
|
1516
|
+
ua = uncited_assertion_entry()
|
|
1517
|
+
uaf = uncited_audit_failure_entry()
|
|
1518
|
+
passport = build_passport(
|
|
1519
|
+
manifests=[self._manifest_with_mnc_and_nc()],
|
|
1520
|
+
uncited=[ua],
|
|
1521
|
+
uaf=[uaf],
|
|
1522
|
+
)
|
|
1523
|
+
self.assertLintClean(passport)
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
# ---------------------------------------------------------------------------
|
|
1527
|
+
# T-S9: Defensive guard against malformed passports.
|
|
1528
|
+
# Step 13 R4 codex P2 #4 — malformed aggregates (non-list / non-dict entries)
|
|
1529
|
+
# must surface as schema findings rather than crash the lint with an
|
|
1530
|
+
# AttributeError traceback.
|
|
1531
|
+
# ---------------------------------------------------------------------------
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
class TS9MalformedPassportGuard(_LintTestBase):
|
|
1535
|
+
"""T-S9: passport with malformed aggregate yields schema finding, not crash."""
|
|
1536
|
+
|
|
1537
|
+
def test_claim_audit_results_with_non_dict_entry(self) -> None:
|
|
1538
|
+
# Pre-R4: list of non-dict in claim_audit_results raised AttributeError
|
|
1539
|
+
# because dict-only invariant loops ran .get() before noticing the shape.
|
|
1540
|
+
body = build_passport()
|
|
1541
|
+
body["claim_audit_results"] = ["this should be an object, not a string"]
|
|
1542
|
+
path = write_passport(self.tmp, body)
|
|
1543
|
+
code, out, err = run_lint(path)
|
|
1544
|
+
self.assertEqual(
|
|
1545
|
+
code,
|
|
1546
|
+
1,
|
|
1547
|
+
msg=f"expected clean lint failure on malformed aggregate; got exit={code}\nstderr:\n{err}",
|
|
1548
|
+
)
|
|
1549
|
+
self.assertNotIn(
|
|
1550
|
+
"Traceback",
|
|
1551
|
+
err,
|
|
1552
|
+
msg=f"lint must not raise — got traceback:\n{err}",
|
|
1553
|
+
)
|
|
1554
|
+
self.assertIn("schema", out, msg=f"expected schema finding tag in stdout:\n{out}")
|
|
1555
|
+
|
|
1556
|
+
def test_claim_intent_manifests_as_dict_instead_of_list(self) -> None:
|
|
1557
|
+
body = build_passport()
|
|
1558
|
+
body["claim_intent_manifests"] = {"oops": "should be a list"}
|
|
1559
|
+
path = write_passport(self.tmp, body)
|
|
1560
|
+
code, out, err = run_lint(path)
|
|
1561
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1562
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not raise:\n{err}")
|
|
1563
|
+
self.assertIn("schema", out)
|
|
1564
|
+
|
|
1565
|
+
def test_non_object_passport_body_yields_clean_finding(self) -> None:
|
|
1566
|
+
# Step 13 R7 codex P3: `[]`, `null`, scalar top-level JSON would
|
|
1567
|
+
# previously crash with AttributeError on `.get()`. Validate that
|
|
1568
|
+
# each surfaces as a schema finding without traceback.
|
|
1569
|
+
for malformed_body in ([], None, 42, "passport"):
|
|
1570
|
+
with self.subTest(body=malformed_body):
|
|
1571
|
+
path = self.tmp / f"malformed_{type(malformed_body).__name__}.json"
|
|
1572
|
+
path.write_text(json.dumps(malformed_body), encoding="utf-8")
|
|
1573
|
+
code, out, err = run_lint(path)
|
|
1574
|
+
self.assertEqual(
|
|
1575
|
+
code,
|
|
1576
|
+
1,
|
|
1577
|
+
msg=f"expected exit=1 for body={malformed_body!r}; got {code}\nstderr:\n{err}",
|
|
1578
|
+
)
|
|
1579
|
+
self.assertNotIn(
|
|
1580
|
+
"Traceback",
|
|
1581
|
+
err,
|
|
1582
|
+
msg=f"lint must not raise for body={malformed_body!r}:\n{err}",
|
|
1583
|
+
)
|
|
1584
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1585
|
+
|
|
1586
|
+
def test_falsey_malformed_aggregate_does_not_bypass_schema(self) -> None:
|
|
1587
|
+
# Step 13 R5 codex P2 #1: `body.get(k, []) or []` would silently
|
|
1588
|
+
# convert a malformed `{}` / `null` / `0` / `""` value to an empty
|
|
1589
|
+
# list and skip schema validation. Each falsey-but-malformed shape
|
|
1590
|
+
# must surface as a schema finding.
|
|
1591
|
+
for malformed in ({}, None, 0, ""):
|
|
1592
|
+
with self.subTest(malformed=malformed):
|
|
1593
|
+
body = build_passport()
|
|
1594
|
+
body["claim_audit_results"] = malformed
|
|
1595
|
+
path = write_passport(self.tmp, body)
|
|
1596
|
+
code, out, err = run_lint(path)
|
|
1597
|
+
self.assertEqual(
|
|
1598
|
+
code,
|
|
1599
|
+
1,
|
|
1600
|
+
msg=f"expected exit=1 for malformed={malformed!r}; got {code}\nstdout:\n{out}\nstderr:\n{err}",
|
|
1601
|
+
)
|
|
1602
|
+
self.assertIn("schema", out, msg=f"expected schema finding for malformed={malformed!r}:\n{out}")
|
|
1603
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not raise for malformed={malformed!r}:\n{err}")
|
|
1604
|
+
|
|
1605
|
+
# Step 13 R6 codex P2 + R8 P2-2 — nested schema-invalid shapes must not
|
|
1606
|
+
# crash the cross-field invariant walkers. Schema validator records the
|
|
1607
|
+
# finding; the invariant helpers then iterate the malformed nested shape
|
|
1608
|
+
# and previously hit TypeError/AttributeError. Issue #119 + #120 P2-2.
|
|
1609
|
+
|
|
1610
|
+
def test_manifest_claims_as_string_does_not_crash_invariant_walker(self) -> None:
|
|
1611
|
+
# #119 + #120 P2-2: claim_intent_manifests[0].claims is a string,
|
|
1612
|
+
# not a list. Schema validator records the finding; the cross-field
|
|
1613
|
+
# invariant walker (M-INV-1..M-INV-4) must NOT iterate the string
|
|
1614
|
+
# and crash. Expect clean exit=1 with schema finding, no traceback.
|
|
1615
|
+
body = build_passport()
|
|
1616
|
+
body["claim_intent_manifests"][0]["claims"] = "should be a list"
|
|
1617
|
+
path = write_passport(self.tmp, body)
|
|
1618
|
+
code, out, err = run_lint(path)
|
|
1619
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1620
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on nested string:\n{err}")
|
|
1621
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1622
|
+
|
|
1623
|
+
def test_manifest_claim_with_non_string_claim_id_does_not_crash(self) -> None:
|
|
1624
|
+
# #119: manifest.claims[].claim_id is a non-string. Schema records
|
|
1625
|
+
# the type mismatch; invariant walker must skip this manifest's
|
|
1626
|
+
# cross-field checks instead of crashing on .get() / regex match.
|
|
1627
|
+
body = build_passport()
|
|
1628
|
+
body["claim_intent_manifests"][0]["claims"][0]["claim_id"] = 42
|
|
1629
|
+
path = write_passport(self.tmp, body)
|
|
1630
|
+
code, out, err = run_lint(path)
|
|
1631
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1632
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on non-string claim_id:\n{err}")
|
|
1633
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1634
|
+
|
|
1635
|
+
def test_audited_indices_mixed_types_does_not_crash_sampling_walker(self) -> None:
|
|
1636
|
+
# #119: audit_sampling_summaries[].audited_indices contains mixed
|
|
1637
|
+
# str/int types. Schema records the type mismatch; S-INV-4 walker
|
|
1638
|
+
# (indices[i] <= indices[i-1]) must skip instead of raising
|
|
1639
|
+
# TypeError on '<=' between str and int.
|
|
1640
|
+
body = build_passport()
|
|
1641
|
+
body["audit_sampling_summaries"] = [{
|
|
1642
|
+
"audit_run_id": AUDIT_RUN_ID,
|
|
1643
|
+
"max_claims_per_paper": 10,
|
|
1644
|
+
"total_citation_count": 5,
|
|
1645
|
+
"audited_count": 2,
|
|
1646
|
+
"audited_indices": ["a", 1],
|
|
1647
|
+
"sampling_strategy": "stratified_buckets_v1",
|
|
1648
|
+
"emitted_at": "2026-05-15T10:15:00Z",
|
|
1649
|
+
}]
|
|
1650
|
+
path = write_passport(self.tmp, body)
|
|
1651
|
+
code, out, err = run_lint(path)
|
|
1652
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1653
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on mixed-type indices:\n{err}")
|
|
1654
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1655
|
+
|
|
1656
|
+
# v3.8.1 Round 2 codex review P2 + adjacent unhashable-id surfaces.
|
|
1657
|
+
# Schema validator records the type mismatch separately; uniqueness loops
|
|
1658
|
+
# and dedupe key construction must not crash on unhashable nested ids.
|
|
1659
|
+
|
|
1660
|
+
def test_unhashable_claim_id_does_not_crash_m_inv_1(self) -> None:
|
|
1661
|
+
# codex round 2 P2: claim.get("claim_id") returns a list/dict
|
|
1662
|
+
# (unhashable). M-INV-1 uniqueness `cid in claim_ids` crashes with
|
|
1663
|
+
# TypeError. Schema records the type mismatch but the invariant walker
|
|
1664
|
+
# still raises, masking the schema findings.
|
|
1665
|
+
body = build_passport()
|
|
1666
|
+
body["claim_intent_manifests"][0]["claims"][0]["claim_id"] = ["bad", "type"]
|
|
1667
|
+
path = write_passport(self.tmp, body)
|
|
1668
|
+
code, out, err = run_lint(path)
|
|
1669
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1670
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on unhashable claim_id:\n{err}")
|
|
1671
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1672
|
+
|
|
1673
|
+
def test_unhashable_finding_id_does_not_crash_cv_inv_1(self) -> None:
|
|
1674
|
+
# CV-INV-1 finding_id uniqueness `fid in seen` crashes when finding_id
|
|
1675
|
+
# is a list / dict (schema-invalid). Same surface class as the M-INV-1
|
|
1676
|
+
# case codex flagged; included for parity coverage across all four
|
|
1677
|
+
# finding_id uniqueness sites (M-INV-4 / U-INV-1 / D-INV-1 / CV-INV-1).
|
|
1678
|
+
body = build_passport()
|
|
1679
|
+
body["constraint_violations"] = [
|
|
1680
|
+
{
|
|
1681
|
+
"finding_id": ["unhashable"],
|
|
1682
|
+
"claim_text": "x",
|
|
1683
|
+
"section_path": "Results > Findings",
|
|
1684
|
+
"violated_constraint_id": "MNC-1",
|
|
1685
|
+
"scoped_manifest_id": MANIFEST_ID,
|
|
1686
|
+
"manifest_claim_id": None,
|
|
1687
|
+
"judge_verdict": "VIOLATED",
|
|
1688
|
+
"rationale": "rationale",
|
|
1689
|
+
"judge_model": "gpt-5.5-xhigh",
|
|
1690
|
+
"judge_run_at": "2026-05-15T10:14:00Z",
|
|
1691
|
+
"rule_version": "D4-a-v1",
|
|
1692
|
+
}
|
|
1693
|
+
]
|
|
1694
|
+
path = write_passport(self.tmp, body)
|
|
1695
|
+
code, out, err = run_lint(path)
|
|
1696
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1697
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on unhashable finding_id:\n{err}")
|
|
1698
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1699
|
+
|
|
1700
|
+
def test_unhashable_mnc_constraint_id_does_not_crash_m_inv_3(self) -> None:
|
|
1701
|
+
# codex round 3 P2: manifest_negative_constraints[].constraint_id as
|
|
1702
|
+
# list/dict crashes the `mnc_ids = {nc.get("constraint_id") for ...}`
|
|
1703
|
+
# set comprehension when the implementation builds an (unused) set.
|
|
1704
|
+
# Schema records the type mismatch but the lint still raises.
|
|
1705
|
+
body = build_passport()
|
|
1706
|
+
body["claim_intent_manifests"][0]["manifest_negative_constraints"] = [
|
|
1707
|
+
{"constraint_id": ["unhashable"], "rule": "bad"}
|
|
1708
|
+
]
|
|
1709
|
+
path = write_passport(self.tmp, body)
|
|
1710
|
+
code, out, err = run_lint(path)
|
|
1711
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1712
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on unhashable MNC constraint_id:\n{err}")
|
|
1713
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1714
|
+
|
|
1715
|
+
def test_non_string_claim_text_does_not_crash_cv_inv_4_dedupe(self) -> None:
|
|
1716
|
+
# CV-INV-4 dedupe key constructs hashlib.sha256((claim_text or "").encode())
|
|
1717
|
+
# — if claim_text is a list / dict / int (schema-invalid), the `or ""`
|
|
1718
|
+
# idiom passes the non-string through and .encode() raises AttributeError.
|
|
1719
|
+
# Codex round 2 adversarial probing surfaced this same class.
|
|
1720
|
+
body = build_passport()
|
|
1721
|
+
body["constraint_violations"] = [
|
|
1722
|
+
{
|
|
1723
|
+
"finding_id": "CV-001",
|
|
1724
|
+
"claim_text": ["not", "a", "string"],
|
|
1725
|
+
"section_path": "Results > Findings",
|
|
1726
|
+
"violated_constraint_id": "MNC-1",
|
|
1727
|
+
"scoped_manifest_id": MANIFEST_ID,
|
|
1728
|
+
"manifest_claim_id": None,
|
|
1729
|
+
"judge_verdict": "VIOLATED",
|
|
1730
|
+
"rationale": "rationale",
|
|
1731
|
+
"judge_model": "gpt-5.5-xhigh",
|
|
1732
|
+
"judge_run_at": "2026-05-15T10:14:00Z",
|
|
1733
|
+
"rule_version": "D4-a-v1",
|
|
1734
|
+
}
|
|
1735
|
+
]
|
|
1736
|
+
path = write_passport(self.tmp, body)
|
|
1737
|
+
code, out, err = run_lint(path)
|
|
1738
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1739
|
+
self.assertNotIn("Traceback", err, msg=f"lint must not crash on non-string claim_text:\n{err}")
|
|
1740
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
# v3.9.1 / #130 — manifest_id non-string guard in _build_manifest_index +
|
|
1744
|
+
# _build_manifest_constraint_index. Schema validator records the type
|
|
1745
|
+
# mismatch finding; the index builders must skip non-string IDs cleanly
|
|
1746
|
+
# instead of raising TypeError("unhashable type") on setdefault() / [mid]=.
|
|
1747
|
+
class TSManifestIdNonStringGuard(_LintTestBase):
|
|
1748
|
+
"""Issue #130: manifest_id of type list/dict must not crash index builders."""
|
|
1749
|
+
|
|
1750
|
+
def test_manifest_id_as_list_does_not_crash(self) -> None:
|
|
1751
|
+
body = build_passport()
|
|
1752
|
+
body["claim_intent_manifests"][0]["manifest_id"] = ["oops"]
|
|
1753
|
+
path = write_passport(self.tmp, body)
|
|
1754
|
+
code, out, err = run_lint(path)
|
|
1755
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1756
|
+
self.assertNotIn(
|
|
1757
|
+
"Traceback",
|
|
1758
|
+
err,
|
|
1759
|
+
msg=f"lint must not crash on list manifest_id (unhashable in setdefault):\n{err}",
|
|
1760
|
+
)
|
|
1761
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1762
|
+
|
|
1763
|
+
def test_manifest_id_as_dict_does_not_crash(self) -> None:
|
|
1764
|
+
body = build_passport()
|
|
1765
|
+
body["claim_intent_manifests"][0]["manifest_id"] = {"oops": "dict id"}
|
|
1766
|
+
path = write_passport(self.tmp, body)
|
|
1767
|
+
code, out, err = run_lint(path)
|
|
1768
|
+
self.assertEqual(code, 1, msg=f"expected exit=1; got {code}\nstderr:\n{err}")
|
|
1769
|
+
self.assertNotIn(
|
|
1770
|
+
"Traceback",
|
|
1771
|
+
err,
|
|
1772
|
+
msg=f"lint must not crash on dict manifest_id (unhashable in setdefault):\n{err}",
|
|
1773
|
+
)
|
|
1774
|
+
self.assertIn("schema", out, msg=f"expected schema finding:\n{out}")
|
|
1775
|
+
|
|
1776
|
+
|
|
1777
|
+
if __name__ == "__main__":
|
|
1778
|
+
unittest.main()
|