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,1664 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""check_claim_audit_consistency.py — ARS v3.8 claim-faithfulness audit lint.
|
|
3
|
+
|
|
4
|
+
Enforces the 39 cross-field invariants spanning the six aggregates that
|
|
5
|
+
claim_ref_alignment_audit_agent populates:
|
|
6
|
+
|
|
7
|
+
claim_audit_results[] — INV-1..INV-19 (§3.1)
|
|
8
|
+
claim_intent_manifests[] — M-INV-1..M-INV-4 (§3.2)
|
|
9
|
+
uncited_assertions[] — U-INV-1..U-INV-4 (§3.3)
|
|
10
|
+
claim_drifts[] — D-INV-1..D-INV-4 (§3.4)
|
|
11
|
+
constraint_violations[] — CV-INV-1..CV-INV-4 (§3.5)
|
|
12
|
+
audit_sampling_summaries[] — S-INV-1..S-INV-4 (§4 step 3)
|
|
13
|
+
|
|
14
|
+
Plus the §3.1 allowed (judgment, audit_status, defect_stage) matrix, and
|
|
15
|
+
schema-shape validation for every entry in each aggregate against the
|
|
16
|
+
corresponding shipped JSON Schema.
|
|
17
|
+
|
|
18
|
+
Cross-field invariants are intentionally NOT expressed in JSON Schema —
|
|
19
|
+
the matrix is conditional and the cross-array (scoped_manifest_id, *)
|
|
20
|
+
integrity checks span aggregates. This lint is the contract under test
|
|
21
|
+
in scripts/test_claim_audit_schema.py.
|
|
22
|
+
|
|
23
|
+
CLI:
|
|
24
|
+
python3 scripts/check_claim_audit_consistency.py --passport <path>
|
|
25
|
+
|
|
26
|
+
Exit codes:
|
|
27
|
+
0 no findings
|
|
28
|
+
1 one or more invariant violations (printed to stdout, one per line)
|
|
29
|
+
2 internal error (passport not found / malformed JSON / schema not found)
|
|
30
|
+
|
|
31
|
+
See docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md
|
|
32
|
+
§3.1-§3.5 and §6 for the full invariant catalogue.
|
|
33
|
+
"""
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import argparse
|
|
37
|
+
import hashlib
|
|
38
|
+
import json
|
|
39
|
+
import sys
|
|
40
|
+
import urllib.parse
|
|
41
|
+
from dataclasses import dataclass
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Any, Iterable
|
|
44
|
+
|
|
45
|
+
from jsonschema import Draft202012Validator
|
|
46
|
+
|
|
47
|
+
# Allow both CLI invocations (`python3 scripts/check_claim_audit_consistency.py`)
|
|
48
|
+
# AND package-style invocations (`python -m unittest scripts.test_*`) to resolve
|
|
49
|
+
# the shared constants module. The CLI path puts scripts/ on sys.path, the
|
|
50
|
+
# unittest path puts repo root on sys.path — the insert below covers the gap.
|
|
51
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
52
|
+
from _claim_audit_constants import ( # noqa: E402
|
|
53
|
+
INV6_RATIONALE_PREFIX,
|
|
54
|
+
INV14_FAULT_CLASS_TAGS,
|
|
55
|
+
RE_CLAIM_ID,
|
|
56
|
+
RE_MNC_CONSTRAINT,
|
|
57
|
+
RE_NC_CONSTRAINT,
|
|
58
|
+
RE_NC_INNER_HYPHEN,
|
|
59
|
+
SENTINEL_MANIFEST_ID,
|
|
60
|
+
SUBCLAIM_NON_SUPPORTED,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
64
|
+
PASSPORT_SCHEMAS = REPO_ROOT / "shared/contracts/passport"
|
|
65
|
+
|
|
66
|
+
SCHEMA_FILES = {
|
|
67
|
+
"claim_audit_result": PASSPORT_SCHEMAS / "claim_audit_result.schema.json",
|
|
68
|
+
"claim_intent_manifest": PASSPORT_SCHEMAS / "claim_intent_manifest.schema.json",
|
|
69
|
+
"uncited_assertion": PASSPORT_SCHEMAS / "uncited_assertion.schema.json",
|
|
70
|
+
"claim_drift": PASSPORT_SCHEMAS / "claim_drift.schema.json",
|
|
71
|
+
"constraint_violation": PASSPORT_SCHEMAS / "constraint_violation.schema.json",
|
|
72
|
+
"uncited_audit_failure": PASSPORT_SCHEMAS / "uncited_audit_failure.schema.json",
|
|
73
|
+
"experiment_provenance_entry": PASSPORT_SCHEMAS / "experiment_provenance_entry.schema.json",
|
|
74
|
+
"experiment_alignment_result": PASSPORT_SCHEMAS / "experiment_alignment_result.schema.json",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Inline schema for audit_sampling_summary (spec §4 step 3) — not a shipped file.
|
|
78
|
+
AUDIT_SAMPLING_SUMMARY_SCHEMA: dict[str, Any] = {
|
|
79
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
80
|
+
"type": "object",
|
|
81
|
+
"additionalProperties": False,
|
|
82
|
+
"required": [
|
|
83
|
+
"audit_run_id",
|
|
84
|
+
"max_claims_per_paper",
|
|
85
|
+
"total_citation_count",
|
|
86
|
+
"audited_count",
|
|
87
|
+
"audited_indices",
|
|
88
|
+
"sampling_strategy",
|
|
89
|
+
"emitted_at",
|
|
90
|
+
],
|
|
91
|
+
"properties": {
|
|
92
|
+
"audit_run_id": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"pattern": r"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z-[0-9a-f]{4}$",
|
|
95
|
+
},
|
|
96
|
+
"max_claims_per_paper": {"type": "integer", "minimum": 1},
|
|
97
|
+
"total_citation_count": {"type": "integer", "minimum": 0},
|
|
98
|
+
"audited_count": {"type": "integer", "minimum": 0},
|
|
99
|
+
"audited_indices": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": {"type": "integer", "minimum": 0},
|
|
102
|
+
},
|
|
103
|
+
"sampling_strategy": {"const": "stratified_buckets_v1"},
|
|
104
|
+
"emitted_at": {"type": "string", "format": "date-time"},
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Allowed (judgment, audit_status, defect_stage) triples — §3.1 table.
|
|
109
|
+
# Negative-constraint-violation rows still permit any ref_retrieval_method
|
|
110
|
+
# in this matrix; INV-7/INV-8 further restrict the surrounding shape.
|
|
111
|
+
ALLOWED_MATRIX: set[tuple[str, str, Any]] = {
|
|
112
|
+
("SUPPORTED", "completed", None),
|
|
113
|
+
("AMBIGUOUS", "completed", "source_description"),
|
|
114
|
+
("AMBIGUOUS", "completed", "citation_anchor"),
|
|
115
|
+
("AMBIGUOUS", "completed", "synthesis_overclaim"),
|
|
116
|
+
("AMBIGUOUS", "completed", None),
|
|
117
|
+
("UNSUPPORTED", "completed", "source_description"),
|
|
118
|
+
("UNSUPPORTED", "completed", "metadata"),
|
|
119
|
+
("UNSUPPORTED", "completed", "citation_anchor"),
|
|
120
|
+
("UNSUPPORTED", "completed", "synthesis_overclaim"),
|
|
121
|
+
("UNSUPPORTED", "completed", "negative_constraint_violation"),
|
|
122
|
+
("RETRIEVAL_FAILED", "completed", "retrieval_existence"),
|
|
123
|
+
("RETRIEVAL_FAILED", "inconclusive", "not_applicable"),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@dataclass(frozen=True)
|
|
127
|
+
class Finding:
|
|
128
|
+
invariant: str
|
|
129
|
+
detail: str
|
|
130
|
+
|
|
131
|
+
def render(self) -> str:
|
|
132
|
+
return f"{self.invariant}: {self.detail}"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
# Schema loaders (cached once per process to amortise check_schema cost).
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
_VALIDATORS: dict[str, Draft202012Validator] = {}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _validator(name: str) -> Draft202012Validator:
|
|
143
|
+
if name not in _VALIDATORS:
|
|
144
|
+
if name == "audit_sampling_summary":
|
|
145
|
+
schema = AUDIT_SAMPLING_SUMMARY_SCHEMA
|
|
146
|
+
else:
|
|
147
|
+
path = SCHEMA_FILES[name]
|
|
148
|
+
if not path.is_file():
|
|
149
|
+
raise FileNotFoundError(f"schema missing: {path}")
|
|
150
|
+
schema = json.loads(path.read_text(encoding="utf-8"))
|
|
151
|
+
Draft202012Validator.check_schema(schema)
|
|
152
|
+
_VALIDATORS[name] = Draft202012Validator(
|
|
153
|
+
schema, format_checker=Draft202012Validator.FORMAT_CHECKER
|
|
154
|
+
)
|
|
155
|
+
return _VALIDATORS[name]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
# claim_audit_result invariants (INV-1..INV-19).
|
|
160
|
+
# Each helper returns a list of Findings; the caller aggregates across rows.
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _check_inv_1(e: dict[str, Any]) -> list[Finding]:
|
|
165
|
+
"""SUPPORTED -> defect_stage=null AND violated_constraint_id=null AND completed."""
|
|
166
|
+
if e.get("judgment") != "SUPPORTED":
|
|
167
|
+
return []
|
|
168
|
+
findings: list[Finding] = []
|
|
169
|
+
if e.get("defect_stage") is not None:
|
|
170
|
+
findings.append(Finding("INV-1", f"SUPPORTED row has non-null defect_stage={e['defect_stage']!r}"))
|
|
171
|
+
if e.get("violated_constraint_id") not in (None, ""):
|
|
172
|
+
findings.append(Finding("INV-1", "SUPPORTED row has non-null violated_constraint_id"))
|
|
173
|
+
if e.get("audit_status") != "completed":
|
|
174
|
+
findings.append(Finding("INV-1", f"SUPPORTED row has audit_status={e.get('audit_status')!r}"))
|
|
175
|
+
return findings
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _check_inv_2(e: dict[str, Any]) -> list[Finding]:
|
|
179
|
+
"""UNSUPPORTED -> defect_stage in 5-set AND completed."""
|
|
180
|
+
if e.get("judgment") != "UNSUPPORTED":
|
|
181
|
+
return []
|
|
182
|
+
allowed = {
|
|
183
|
+
"source_description",
|
|
184
|
+
"metadata",
|
|
185
|
+
"citation_anchor",
|
|
186
|
+
"synthesis_overclaim",
|
|
187
|
+
"negative_constraint_violation",
|
|
188
|
+
}
|
|
189
|
+
findings: list[Finding] = []
|
|
190
|
+
if e.get("defect_stage") not in allowed:
|
|
191
|
+
findings.append(
|
|
192
|
+
Finding(
|
|
193
|
+
"INV-2",
|
|
194
|
+
f"UNSUPPORTED row has defect_stage={e.get('defect_stage')!r}; must be one of {sorted(allowed)}",
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
if e.get("audit_status") != "completed":
|
|
198
|
+
findings.append(Finding("INV-2", f"UNSUPPORTED row has audit_status={e.get('audit_status')!r}"))
|
|
199
|
+
return findings
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _check_inv_3(e: dict[str, Any]) -> list[Finding]:
|
|
203
|
+
"""AMBIGUOUS -> defect_stage in {source_description, citation_anchor, synthesis_overclaim, None}."""
|
|
204
|
+
if e.get("judgment") != "AMBIGUOUS":
|
|
205
|
+
return []
|
|
206
|
+
allowed = {"source_description", "citation_anchor", "synthesis_overclaim", None}
|
|
207
|
+
findings: list[Finding] = []
|
|
208
|
+
if e.get("defect_stage") not in allowed:
|
|
209
|
+
findings.append(
|
|
210
|
+
Finding(
|
|
211
|
+
"INV-3",
|
|
212
|
+
f"AMBIGUOUS row has defect_stage={e.get('defect_stage')!r}; must be one of "
|
|
213
|
+
f"{sorted([x for x in allowed if x is not None]) + ['null']}",
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
if e.get("audit_status") != "completed":
|
|
217
|
+
findings.append(Finding("INV-3", f"AMBIGUOUS row has audit_status={e.get('audit_status')!r}"))
|
|
218
|
+
return findings
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _check_inv_4(e: dict[str, Any]) -> list[Finding]:
|
|
222
|
+
"""RETRIEVAL_FAILED + inconclusive -> defect_stage=not_applicable."""
|
|
223
|
+
if e.get("judgment") != "RETRIEVAL_FAILED":
|
|
224
|
+
return []
|
|
225
|
+
if e.get("audit_status") != "inconclusive":
|
|
226
|
+
return []
|
|
227
|
+
if e.get("defect_stage") != "not_applicable":
|
|
228
|
+
return [
|
|
229
|
+
Finding(
|
|
230
|
+
"INV-4",
|
|
231
|
+
f"RETRIEVAL_FAILED+inconclusive row has defect_stage={e.get('defect_stage')!r}; must be not_applicable",
|
|
232
|
+
)
|
|
233
|
+
]
|
|
234
|
+
return []
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _check_inv_5(e: dict[str, Any]) -> list[Finding]:
|
|
238
|
+
"""RETRIEVAL_FAILED + completed -> defect_stage=retrieval_existence."""
|
|
239
|
+
if e.get("judgment") != "RETRIEVAL_FAILED":
|
|
240
|
+
return []
|
|
241
|
+
if e.get("audit_status") != "completed":
|
|
242
|
+
return []
|
|
243
|
+
if e.get("defect_stage") != "retrieval_existence":
|
|
244
|
+
return [
|
|
245
|
+
Finding(
|
|
246
|
+
"INV-5",
|
|
247
|
+
f"RETRIEVAL_FAILED+completed row has defect_stage={e.get('defect_stage')!r}; must be retrieval_existence",
|
|
248
|
+
)
|
|
249
|
+
]
|
|
250
|
+
return []
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _check_inv_6(e: dict[str, Any]) -> list[Finding]:
|
|
254
|
+
"""anchor_kind=none firm rule (D1)."""
|
|
255
|
+
if e.get("anchor_kind") != "none":
|
|
256
|
+
return []
|
|
257
|
+
findings: list[Finding] = []
|
|
258
|
+
if e.get("judgment") != "RETRIEVAL_FAILED":
|
|
259
|
+
findings.append(Finding("INV-6", f"anchor=none row has judgment={e.get('judgment')!r}; must be RETRIEVAL_FAILED"))
|
|
260
|
+
if e.get("audit_status") != "inconclusive":
|
|
261
|
+
findings.append(Finding("INV-6", f"anchor=none row has audit_status={e.get('audit_status')!r}; must be inconclusive"))
|
|
262
|
+
if e.get("defect_stage") != "not_applicable":
|
|
263
|
+
findings.append(Finding("INV-6", f"anchor=none row has defect_stage={e.get('defect_stage')!r}; must be not_applicable"))
|
|
264
|
+
if e.get("ref_retrieval_method") != "not_attempted":
|
|
265
|
+
findings.append(
|
|
266
|
+
Finding(
|
|
267
|
+
"INV-6",
|
|
268
|
+
f"anchor=none row has ref_retrieval_method={e.get('ref_retrieval_method')!r}; must be not_attempted",
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
# Schema mandates the empty sentinel for anchor_value when anchor_kind=none.
|
|
272
|
+
# A residual stale anchor (e.g. "123") here violates the schema contract;
|
|
273
|
+
# pipeline _anchorless_entry pins "" but the lint must enforce it for
|
|
274
|
+
# passports authored outside the pipeline (manual fixtures, replays).
|
|
275
|
+
if e.get("anchor_value", "") != "":
|
|
276
|
+
findings.append(
|
|
277
|
+
Finding(
|
|
278
|
+
"INV-6",
|
|
279
|
+
f"anchor=none row has non-empty anchor_value={e.get('anchor_value')!r}; must be empty sentinel string",
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
rationale = e.get("rationale") or ""
|
|
283
|
+
if not rationale.startswith(INV6_RATIONALE_PREFIX):
|
|
284
|
+
findings.append(
|
|
285
|
+
Finding(
|
|
286
|
+
"INV-6",
|
|
287
|
+
f"anchor=none row rationale must start with {INV6_RATIONALE_PREFIX!r}; got {rationale[:60]!r}",
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
return findings
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _check_inv_7(e: dict[str, Any]) -> list[Finding]:
|
|
294
|
+
"""defect_stage=negative_constraint_violation -> violated_constraint_id != null."""
|
|
295
|
+
if e.get("defect_stage") != "negative_constraint_violation":
|
|
296
|
+
return []
|
|
297
|
+
if e.get("violated_constraint_id") in (None, ""):
|
|
298
|
+
return [Finding("INV-7", "negative_constraint_violation row has null violated_constraint_id")]
|
|
299
|
+
return []
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _check_inv_8(e: dict[str, Any]) -> list[Finding]:
|
|
303
|
+
"""defect_stage=negative_constraint_violation -> judgment=UNSUPPORTED."""
|
|
304
|
+
if e.get("defect_stage") != "negative_constraint_violation":
|
|
305
|
+
return []
|
|
306
|
+
if e.get("judgment") != "UNSUPPORTED":
|
|
307
|
+
return [
|
|
308
|
+
Finding(
|
|
309
|
+
"INV-8",
|
|
310
|
+
f"negative_constraint_violation row has judgment={e.get('judgment')!r}; must be UNSUPPORTED",
|
|
311
|
+
)
|
|
312
|
+
]
|
|
313
|
+
return []
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _check_inv_9(e: dict[str, Any]) -> list[Finding]:
|
|
317
|
+
"""upstream_dispute != null -> defect_stage substantive."""
|
|
318
|
+
if not e.get("upstream_dispute"):
|
|
319
|
+
return []
|
|
320
|
+
stage = e.get("defect_stage")
|
|
321
|
+
if stage in (None, "not_applicable"):
|
|
322
|
+
return [
|
|
323
|
+
Finding(
|
|
324
|
+
"INV-9",
|
|
325
|
+
f"upstream_dispute set on non-substantive row (defect_stage={stage!r})",
|
|
326
|
+
)
|
|
327
|
+
]
|
|
328
|
+
return []
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _check_inv_10(e: dict[str, Any]) -> list[Finding]:
|
|
332
|
+
"""ref_retrieval_method=failed -> RETRIEVAL_FAILED + inconclusive + not_applicable."""
|
|
333
|
+
if e.get("ref_retrieval_method") != "failed":
|
|
334
|
+
return []
|
|
335
|
+
findings: list[Finding] = []
|
|
336
|
+
if e.get("judgment") != "RETRIEVAL_FAILED":
|
|
337
|
+
findings.append(Finding("INV-10", f"method=failed but judgment={e.get('judgment')!r}"))
|
|
338
|
+
if e.get("audit_status") != "inconclusive":
|
|
339
|
+
findings.append(Finding("INV-10", f"method=failed but audit_status={e.get('audit_status')!r}"))
|
|
340
|
+
if e.get("defect_stage") != "not_applicable":
|
|
341
|
+
findings.append(Finding("INV-10", f"method=failed but defect_stage={e.get('defect_stage')!r}"))
|
|
342
|
+
return findings
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _check_inv_11(e: dict[str, Any]) -> list[Finding]:
|
|
346
|
+
"""ref_retrieval_method=not_attempted ↔ anchor_kind=none."""
|
|
347
|
+
method = e.get("ref_retrieval_method")
|
|
348
|
+
kind = e.get("anchor_kind")
|
|
349
|
+
if method == "not_attempted" and kind != "none":
|
|
350
|
+
return [
|
|
351
|
+
Finding(
|
|
352
|
+
"INV-11",
|
|
353
|
+
f"method=not_attempted but anchor_kind={kind!r}; must be none",
|
|
354
|
+
)
|
|
355
|
+
]
|
|
356
|
+
if kind == "none" and method != "not_attempted":
|
|
357
|
+
return [
|
|
358
|
+
Finding(
|
|
359
|
+
"INV-11",
|
|
360
|
+
f"anchor=none but ref_retrieval_method={method!r}; must be not_attempted",
|
|
361
|
+
)
|
|
362
|
+
]
|
|
363
|
+
return []
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _check_inv_12(e: dict[str, Any]) -> list[Finding]:
|
|
367
|
+
"""ref_retrieval_method=not_found ↔ RETRIEVAL_FAILED + completed + retrieval_existence."""
|
|
368
|
+
method = e.get("ref_retrieval_method")
|
|
369
|
+
triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
|
|
370
|
+
fabricated = ("RETRIEVAL_FAILED", "completed", "retrieval_existence")
|
|
371
|
+
if method == "not_found" and triple != fabricated:
|
|
372
|
+
return [
|
|
373
|
+
Finding(
|
|
374
|
+
"INV-12",
|
|
375
|
+
f"method=not_found but triple={triple!r}; must be {fabricated!r}",
|
|
376
|
+
)
|
|
377
|
+
]
|
|
378
|
+
if triple == fabricated and method != "not_found":
|
|
379
|
+
return [
|
|
380
|
+
Finding(
|
|
381
|
+
"INV-12",
|
|
382
|
+
f"fabricated triple but method={method!r}; must be not_found",
|
|
383
|
+
)
|
|
384
|
+
]
|
|
385
|
+
return []
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _check_inv_13(e: dict[str, Any]) -> list[Finding]:
|
|
389
|
+
"""defect_stage=metadata -> UNSUPPORTED + completed + method in {api, manual_pdf}."""
|
|
390
|
+
if e.get("defect_stage") != "metadata":
|
|
391
|
+
return []
|
|
392
|
+
findings: list[Finding] = []
|
|
393
|
+
if e.get("judgment") != "UNSUPPORTED":
|
|
394
|
+
findings.append(Finding("INV-13", f"metadata row has judgment={e.get('judgment')!r}"))
|
|
395
|
+
if e.get("audit_status") != "completed":
|
|
396
|
+
findings.append(Finding("INV-13", f"metadata row has audit_status={e.get('audit_status')!r}"))
|
|
397
|
+
if e.get("ref_retrieval_method") not in ("api", "manual_pdf"):
|
|
398
|
+
findings.append(
|
|
399
|
+
Finding(
|
|
400
|
+
"INV-13",
|
|
401
|
+
f"metadata row has ref_retrieval_method={e.get('ref_retrieval_method')!r}; must be api or manual_pdf",
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
return findings
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _check_inv_14(e: dict[str, Any]) -> list[Finding]:
|
|
408
|
+
"""audit_tool_failure -> RETRIEVAL_FAILED + inconclusive + not_applicable + rationale fault-class prefix."""
|
|
409
|
+
if e.get("ref_retrieval_method") != "audit_tool_failure":
|
|
410
|
+
return []
|
|
411
|
+
findings: list[Finding] = []
|
|
412
|
+
if e.get("judgment") != "RETRIEVAL_FAILED":
|
|
413
|
+
findings.append(Finding("INV-14", f"audit_tool_failure row has judgment={e.get('judgment')!r}"))
|
|
414
|
+
if e.get("audit_status") != "inconclusive":
|
|
415
|
+
findings.append(Finding("INV-14", f"audit_tool_failure row has audit_status={e.get('audit_status')!r}"))
|
|
416
|
+
if e.get("defect_stage") != "not_applicable":
|
|
417
|
+
findings.append(Finding("INV-14", f"audit_tool_failure row has defect_stage={e.get('defect_stage')!r}"))
|
|
418
|
+
rationale = e.get("rationale") or ""
|
|
419
|
+
if not any(rationale.startswith(f"{tag}:") for tag in INV14_FAULT_CLASS_TAGS):
|
|
420
|
+
findings.append(
|
|
421
|
+
Finding(
|
|
422
|
+
"INV-14",
|
|
423
|
+
f"audit_tool_failure rationale must begin with one of "
|
|
424
|
+
f"{INV14_FAULT_CLASS_TAGS}+':' got {rationale[:60]!r}",
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
return findings
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _check_inv_15(
|
|
431
|
+
e: dict[str, Any],
|
|
432
|
+
manifest_index: dict[str, set[str]],
|
|
433
|
+
) -> list[Finding]:
|
|
434
|
+
"""(scoped_manifest_id, claim_id) MUST resolve OR sentinel."""
|
|
435
|
+
sm = e.get("scoped_manifest_id")
|
|
436
|
+
cid = e.get("claim_id")
|
|
437
|
+
if sm == SENTINEL_MANIFEST_ID:
|
|
438
|
+
return []
|
|
439
|
+
if sm not in manifest_index:
|
|
440
|
+
return [
|
|
441
|
+
Finding(
|
|
442
|
+
"INV-15",
|
|
443
|
+
f"dangling scoped_manifest_id={sm!r}; no matching claim_intent_manifests[] entry",
|
|
444
|
+
)
|
|
445
|
+
]
|
|
446
|
+
if cid not in manifest_index[sm]:
|
|
447
|
+
return [
|
|
448
|
+
Finding(
|
|
449
|
+
"INV-15",
|
|
450
|
+
f"(scoped_manifest_id={sm!r}, claim_id={cid!r}) pair not present in matching manifest",
|
|
451
|
+
)
|
|
452
|
+
]
|
|
453
|
+
return []
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _check_inv_16(e: dict[str, Any]) -> list[Finding]:
|
|
457
|
+
"""anchor_kind != none -> URL-decoded anchor_value non-empty after strip.
|
|
458
|
+
|
|
459
|
+
Schema mandates non-empty after URL-decoding so URL-encoded whitespace
|
|
460
|
+
(e.g. `%20`, `%09`) cannot bypass the firm rule. `.strip()` on the raw
|
|
461
|
+
string would accept `"%20"` as a non-empty anchor; decoding first makes
|
|
462
|
+
the check faithful to the contract.
|
|
463
|
+
"""
|
|
464
|
+
kind = e.get("anchor_kind")
|
|
465
|
+
if kind in (None, "none"):
|
|
466
|
+
return []
|
|
467
|
+
value = e.get("anchor_value")
|
|
468
|
+
if not isinstance(value, str):
|
|
469
|
+
return [
|
|
470
|
+
Finding(
|
|
471
|
+
"INV-16",
|
|
472
|
+
f"anchor_kind={kind!r} has non-string anchor_value (type={type(value).__name__})",
|
|
473
|
+
)
|
|
474
|
+
]
|
|
475
|
+
if urllib.parse.unquote(value).strip() == "":
|
|
476
|
+
return [
|
|
477
|
+
Finding(
|
|
478
|
+
"INV-16",
|
|
479
|
+
f"anchor_kind={kind!r} has empty anchor_value (URL-decoded whitespace-only or missing)",
|
|
480
|
+
)
|
|
481
|
+
]
|
|
482
|
+
return []
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _check_inv_18(e: dict[str, Any]) -> list[Finding]:
|
|
486
|
+
"""(RETRIEVAL_FAILED, inconclusive, not_applicable) -> method in {not_attempted, failed, audit_tool_failure}."""
|
|
487
|
+
triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
|
|
488
|
+
if triple != ("RETRIEVAL_FAILED", "inconclusive", "not_applicable"):
|
|
489
|
+
return []
|
|
490
|
+
allowed = {"not_attempted", "failed", "audit_tool_failure"}
|
|
491
|
+
method = e.get("ref_retrieval_method")
|
|
492
|
+
if method not in allowed:
|
|
493
|
+
return [
|
|
494
|
+
Finding(
|
|
495
|
+
"INV-18",
|
|
496
|
+
f"(RETRIEVAL_FAILED, inconclusive, not_applicable) row has method={method!r}; must be one of {sorted(allowed)}",
|
|
497
|
+
)
|
|
498
|
+
]
|
|
499
|
+
return []
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _check_inv_19(e: dict[str, Any]) -> list[Finding]:
|
|
503
|
+
"""sub_claim_breakdown present -> normalized-PARTIAL shape pinned (#213).
|
|
504
|
+
|
|
505
|
+
Presence of the breakdown is the machine-readable partial-support signal.
|
|
506
|
+
When present it must pin the full B1 normalization: judgment=UNSUPPORTED,
|
|
507
|
+
defect_stage=source_description, and the breakdown is *true-partial* —
|
|
508
|
+
>=2 items with >=1 SUPPORTED AND >=1 non-SUPPORTED sub_verdict. The
|
|
509
|
+
SUPPORTED-AND-non-SUPPORTED pair is what distinguishes a genuine partial
|
|
510
|
+
from an all-supported or all-unsupported decomposition; "non-SUPPORTED
|
|
511
|
+
alone" would wrongly admit an all-UNSUPPORTED breakdown.
|
|
512
|
+
|
|
513
|
+
The "what counts as non-SUPPORTED" decision is the shared
|
|
514
|
+
`_claim_audit_constants.SUBCLAIM_NON_SUPPORTED` constant (the literal most
|
|
515
|
+
likely to drift) — `is_true_partial_breakdown` in that module is the bool
|
|
516
|
+
form used by the runtime + calibration. This lint deliberately re-expresses
|
|
517
|
+
the same mix test inline (rather than calling the bool helper) because it
|
|
518
|
+
must emit two DISTINCT findings — "needs >=1 SUPPORTED" and "needs >=1
|
|
519
|
+
non-SUPPORTED" — which a single bool cannot carry. The shared constant keeps
|
|
520
|
+
the verdict set in sync; the granular split is lint-specific.
|
|
521
|
+
"""
|
|
522
|
+
if "sub_claim_breakdown" not in e:
|
|
523
|
+
return []
|
|
524
|
+
bd = e["sub_claim_breakdown"]
|
|
525
|
+
findings: list[Finding] = []
|
|
526
|
+
if e.get("judgment") != "UNSUPPORTED":
|
|
527
|
+
findings.append(
|
|
528
|
+
Finding("INV-19", f"sub_claim_breakdown present but judgment={e.get('judgment')!r}; must be UNSUPPORTED")
|
|
529
|
+
)
|
|
530
|
+
if e.get("defect_stage") != "source_description":
|
|
531
|
+
findings.append(
|
|
532
|
+
Finding(
|
|
533
|
+
"INV-19",
|
|
534
|
+
f"sub_claim_breakdown present but defect_stage={e.get('defect_stage')!r}; must be source_description",
|
|
535
|
+
)
|
|
536
|
+
)
|
|
537
|
+
if not isinstance(bd, list) or len(bd) < 2:
|
|
538
|
+
findings.append(Finding("INV-19", "sub_claim_breakdown must have >=2 items (not a true partial)"))
|
|
539
|
+
return findings
|
|
540
|
+
verdicts = [item.get("sub_verdict") for item in _iter_dicts(bd)]
|
|
541
|
+
# "non-SUPPORTED" counts only the *valid* opposing verdicts (the shared
|
|
542
|
+
# SUBCLAIM_NON_SUPPORTED set). A missing or out-of-enum sub_verdict must NOT
|
|
543
|
+
# be read as non-SUPPORTED — that would let `[SUPPORTED, <missing>]`
|
|
544
|
+
# masquerade as true-partial (round-2 review #1). The granular split here is
|
|
545
|
+
# equivalent to `is_true_partial_breakdown(bd)` but yields distinct findings.
|
|
546
|
+
if not any(v == "SUPPORTED" for v in verdicts):
|
|
547
|
+
findings.append(Finding("INV-19", "sub_claim_breakdown is not true-partial: needs >=1 SUPPORTED sub_verdict"))
|
|
548
|
+
if not any(v in SUBCLAIM_NON_SUPPORTED for v in verdicts):
|
|
549
|
+
findings.append(
|
|
550
|
+
Finding("INV-19", "sub_claim_breakdown is not true-partial: needs >=1 non-SUPPORTED sub_verdict")
|
|
551
|
+
)
|
|
552
|
+
return findings
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
# INV-17 surfaces on malformed NC constraint ids encountered in manifests
|
|
556
|
+
# (schema also rejects the wrong shape; lint surfaces the explicit tag).
|
|
557
|
+
def _check_inv_17_for_manifest(manifest: dict[str, Any]) -> list[Finding]:
|
|
558
|
+
findings: list[Finding] = []
|
|
559
|
+
for claim in _iter_dicts(manifest.get("claims")):
|
|
560
|
+
for nc in _iter_dicts(claim.get("negative_constraints")):
|
|
561
|
+
cid_raw = nc.get("constraint_id", "")
|
|
562
|
+
cid = cid_raw if isinstance(cid_raw, str) else ""
|
|
563
|
+
if RE_NC_INNER_HYPHEN.match(cid):
|
|
564
|
+
findings.append(
|
|
565
|
+
Finding(
|
|
566
|
+
"INV-17",
|
|
567
|
+
f"NC constraint_id={cid!r} uses NC-C-{{n}} form; canonical is NC-C{{n}}-{{m}} (no hyphen between C and digits)",
|
|
568
|
+
)
|
|
569
|
+
)
|
|
570
|
+
return findings
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def _check_matrix(e: dict[str, Any]) -> list[Finding]:
|
|
574
|
+
triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
|
|
575
|
+
if triple not in ALLOWED_MATRIX:
|
|
576
|
+
return [
|
|
577
|
+
Finding(
|
|
578
|
+
"matrix",
|
|
579
|
+
f"triple {triple!r} not in §3.1 allowed (judgment, audit_status, defect_stage) matrix",
|
|
580
|
+
)
|
|
581
|
+
]
|
|
582
|
+
return []
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# ---------------------------------------------------------------------------
|
|
586
|
+
# claim_intent_manifest invariants (M-INV-1..M-INV-4).
|
|
587
|
+
# ---------------------------------------------------------------------------
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _iter_dicts(value: Any) -> list[dict[str, Any]]:
|
|
591
|
+
"""Filter a nested 'list of dict' field to actual dict entries.
|
|
592
|
+
|
|
593
|
+
Mirrors `_coerce_aggregate` (which protects top-level aggregates) at the
|
|
594
|
+
nested level: invariant walkers that descend into `claims[]`,
|
|
595
|
+
`negative_constraints[]`, `manifest_negative_constraints[]` etc. expect
|
|
596
|
+
a list of dicts. Schema validation surfaces the type mismatch separately
|
|
597
|
+
as a clean finding; this guard prevents the walker from crashing on a
|
|
598
|
+
string-instead-of-list (`for claim in "broken":`) or a non-dict entry
|
|
599
|
+
(`claim.get(...)`) so the CLI returns actionable lint findings rather
|
|
600
|
+
than a traceback (#119 + #120 P2-2 — Step 13 R6 codex P2).
|
|
601
|
+
"""
|
|
602
|
+
if not isinstance(value, list):
|
|
603
|
+
return []
|
|
604
|
+
return [e for e in value if isinstance(e, dict)]
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _check_manifest_invariants(manifests: list[dict[str, Any]]) -> list[Finding]:
|
|
608
|
+
findings: list[Finding] = []
|
|
609
|
+
# M-INV-4: manifest_id uniqueness across passport.
|
|
610
|
+
# Skip non-string manifest_ids per v3.8.1 round 2 — schema records the
|
|
611
|
+
# type mismatch separately; unhashable list/dict would crash this loop.
|
|
612
|
+
seen_ids: dict[str, int] = {}
|
|
613
|
+
for i, m in enumerate(manifests):
|
|
614
|
+
mid = m.get("manifest_id")
|
|
615
|
+
if not isinstance(mid, str):
|
|
616
|
+
continue
|
|
617
|
+
if mid in seen_ids:
|
|
618
|
+
findings.append(
|
|
619
|
+
Finding(
|
|
620
|
+
"M-INV-4",
|
|
621
|
+
f"duplicate manifest_id={mid!r} (also at claim_intent_manifests[{seen_ids[mid]}])",
|
|
622
|
+
)
|
|
623
|
+
)
|
|
624
|
+
else:
|
|
625
|
+
seen_ids[mid] = i
|
|
626
|
+
|
|
627
|
+
for i, m in enumerate(manifests):
|
|
628
|
+
# M-INV-1: claim_id uniqueness within one manifest.
|
|
629
|
+
# Skip non-string claim_ids: schema validator records the type
|
|
630
|
+
# mismatch separately; treating an unhashable list/dict as a dict
|
|
631
|
+
# key here would raise TypeError and crash the lint instead of
|
|
632
|
+
# returning actionable findings (codex round 2 P2 / v3.8.1).
|
|
633
|
+
claim_ids: dict[str, int] = {}
|
|
634
|
+
for j, claim in enumerate(_iter_dicts(m.get("claims"))):
|
|
635
|
+
cid = claim.get("claim_id")
|
|
636
|
+
if not isinstance(cid, str):
|
|
637
|
+
continue
|
|
638
|
+
if cid in claim_ids:
|
|
639
|
+
findings.append(
|
|
640
|
+
Finding(
|
|
641
|
+
"M-INV-1",
|
|
642
|
+
f"duplicate claim_id={cid!r} within manifest_id={m.get('manifest_id')!r} "
|
|
643
|
+
f"(also at claims[{claim_ids[cid]}])",
|
|
644
|
+
)
|
|
645
|
+
)
|
|
646
|
+
else:
|
|
647
|
+
claim_ids[cid] = j
|
|
648
|
+
|
|
649
|
+
# M-INV-2: NC-C{n}-{m} must scope under a claims[] entry with C-{n}.
|
|
650
|
+
for j, claim in enumerate(_iter_dicts(m.get("claims"))):
|
|
651
|
+
cid_raw = claim.get("claim_id") or ""
|
|
652
|
+
cid = cid_raw if isinstance(cid_raw, str) else ""
|
|
653
|
+
cid_match = RE_CLAIM_ID.match(cid)
|
|
654
|
+
cid_digits = cid_match.group(1) if cid_match else None
|
|
655
|
+
for nc in _iter_dicts(claim.get("negative_constraints")):
|
|
656
|
+
nc_id_raw = nc.get("constraint_id", "")
|
|
657
|
+
nc_id = nc_id_raw if isinstance(nc_id_raw, str) else ""
|
|
658
|
+
nc_match = RE_NC_CONSTRAINT.match(nc_id)
|
|
659
|
+
if nc_match and nc_match.group(1) != cid_digits:
|
|
660
|
+
findings.append(
|
|
661
|
+
Finding(
|
|
662
|
+
"M-INV-2",
|
|
663
|
+
f"NC constraint_id={nc_id!r} scoped under claim_id={cid!r}; "
|
|
664
|
+
f"NC digits {nc_match.group(1)!r} must match claim digits {cid_digits!r}",
|
|
665
|
+
)
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
# M-INV-3: claim-level NC cannot reuse an MNC-* id (only ADD via NC-C{n}-{m}).
|
|
669
|
+
# Per spec §3.2: "claim-level can ADD via NC-C{n}-{m}, never via MNC-*" —
|
|
670
|
+
# the check fires on any claim-level negative_constraint whose id matches
|
|
671
|
+
# the MNC-* pattern, regardless of whether that exact id appears in
|
|
672
|
+
# manifest_negative_constraints[]. (Previous revisions built an unused
|
|
673
|
+
# `mnc_ids` set here that also crashed on schema-invalid unhashable
|
|
674
|
+
# MNC ids — codex round 3 P2; removed in v3.8.1.)
|
|
675
|
+
for j, claim in enumerate(_iter_dicts(m.get("claims"))):
|
|
676
|
+
for nc in _iter_dicts(claim.get("negative_constraints")):
|
|
677
|
+
nc_id_raw = nc.get("constraint_id", "")
|
|
678
|
+
nc_id = nc_id_raw if isinstance(nc_id_raw, str) else ""
|
|
679
|
+
if RE_MNC_CONSTRAINT.match(nc_id):
|
|
680
|
+
findings.append(
|
|
681
|
+
Finding(
|
|
682
|
+
"M-INV-3",
|
|
683
|
+
f"claim-level negative_constraint reuses MNC-* id={nc_id!r}; "
|
|
684
|
+
"claim-level can ADD via NC-C{n}-{m}, never via MNC-*",
|
|
685
|
+
)
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
findings.extend(_check_inv_17_for_manifest(m))
|
|
689
|
+
|
|
690
|
+
return findings
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def _build_manifest_index(manifests: list[dict[str, Any]]) -> dict[str, set[str]]:
|
|
694
|
+
"""Build {manifest_id: {claim_id, ...}} for INV-15 / U-INV-4 / D-INV-2 / CV-INV-2 lookups."""
|
|
695
|
+
index: dict[str, set[str]] = {}
|
|
696
|
+
for m in manifests:
|
|
697
|
+
mid = m.get("manifest_id")
|
|
698
|
+
# #130: skip non-string manifest_id (list/dict would raise
|
|
699
|
+
# TypeError: unhashable type on setdefault). Schema validator
|
|
700
|
+
# already records the type mismatch as a separate finding;
|
|
701
|
+
# the cross-field invariant pass just needs to walk past it.
|
|
702
|
+
if not isinstance(mid, str) or not mid:
|
|
703
|
+
continue
|
|
704
|
+
index.setdefault(mid, set())
|
|
705
|
+
for c in _iter_dicts(m.get("claims")):
|
|
706
|
+
cid = c.get("claim_id")
|
|
707
|
+
if isinstance(cid, str) and cid:
|
|
708
|
+
index[mid].add(cid)
|
|
709
|
+
return index
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
def _build_manifest_constraint_index(
|
|
713
|
+
manifests: list[dict[str, Any]],
|
|
714
|
+
) -> dict[str, dict[str, dict[str, Any]]]:
|
|
715
|
+
"""Build {manifest_id: {constraint_id: {kind: 'MNC'|'NC', claim_id: C-{n}|None}}}.
|
|
716
|
+
|
|
717
|
+
Used by CV-INV-2 to resolve violated_constraint_id against the active manifest.
|
|
718
|
+
"""
|
|
719
|
+
out: dict[str, dict[str, dict[str, Any]]] = {}
|
|
720
|
+
for m in manifests:
|
|
721
|
+
mid = m.get("manifest_id")
|
|
722
|
+
# #130: same guard as _build_manifest_index — non-string mid
|
|
723
|
+
# would raise TypeError on `out[mid] = bucket` (unhashable).
|
|
724
|
+
if not isinstance(mid, str) or not mid:
|
|
725
|
+
continue
|
|
726
|
+
bucket: dict[str, dict[str, Any]] = {}
|
|
727
|
+
for mnc in _iter_dicts(m.get("manifest_negative_constraints")):
|
|
728
|
+
cid = mnc.get("constraint_id")
|
|
729
|
+
if isinstance(cid, str) and cid:
|
|
730
|
+
bucket[cid] = {"kind": "MNC", "claim_id": None}
|
|
731
|
+
for claim in _iter_dicts(m.get("claims")):
|
|
732
|
+
parent = claim.get("claim_id")
|
|
733
|
+
for nc in _iter_dicts(claim.get("negative_constraints")):
|
|
734
|
+
cid = nc.get("constraint_id")
|
|
735
|
+
if isinstance(cid, str) and cid:
|
|
736
|
+
bucket[cid] = {"kind": "NC", "claim_id": parent}
|
|
737
|
+
out[mid] = bucket
|
|
738
|
+
return out
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
# ---------------------------------------------------------------------------
|
|
742
|
+
# experiment provenance / alignment invariants (#260).
|
|
743
|
+
# EP-INV-1 experiment_id unique within the passport
|
|
744
|
+
# EP-INV-2 planned_experiment_ids[] resolve to an experiment_provenance entry
|
|
745
|
+
# (doubles as the rename + forward-reference dangling-pointer guard)
|
|
746
|
+
# EP-INV-3 planned_experiment_ids present => owning claim is empirical
|
|
747
|
+
# EP-INV-4 experiment_intake_declaration <-> experiment_provenance symmetry
|
|
748
|
+
# EP-INV-5 experiment_intake_declaration well-formedness when present
|
|
749
|
+
# (status enum / declared_by==scholar / declared_at date-time)
|
|
750
|
+
# EA-INV-1 experiment_alignment_results[].finding_id unique
|
|
751
|
+
# EA-INV-2 alignment row (scoped_manifest_id, claim_id) + experiment_id resolve
|
|
752
|
+
# These are cross-array integrity checks that JSON Schema cannot express; they
|
|
753
|
+
# mirror the INV-15 / U-INV-4 (scoped_manifest_id, *) resolution pattern.
|
|
754
|
+
# ---------------------------------------------------------------------------
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def _build_claim_detail_index(
|
|
758
|
+
manifests: list[dict[str, Any]],
|
|
759
|
+
) -> dict[tuple[str, str], dict[str, Any]]:
|
|
760
|
+
"""Build {(manifest_id, claim_id): {evidence_kind, planned_experiment_ids}}.
|
|
761
|
+
|
|
762
|
+
Used by EP-INV-3 (planned_experiment_ids => empirical) which needs the
|
|
763
|
+
per-claim intended_evidence_kind, a field _build_manifest_index discards.
|
|
764
|
+
"""
|
|
765
|
+
out: dict[tuple[str, str], dict[str, Any]] = {}
|
|
766
|
+
for m in manifests:
|
|
767
|
+
mid = m.get("manifest_id")
|
|
768
|
+
if not isinstance(mid, str) or not mid:
|
|
769
|
+
continue
|
|
770
|
+
for c in _iter_dicts(m.get("claims")):
|
|
771
|
+
cid = c.get("claim_id")
|
|
772
|
+
if not isinstance(cid, str) or not cid:
|
|
773
|
+
continue
|
|
774
|
+
out[(mid, cid)] = {
|
|
775
|
+
"intended_evidence_kind": c.get("intended_evidence_kind"),
|
|
776
|
+
"planned_experiment_ids": c.get("planned_experiment_ids"),
|
|
777
|
+
}
|
|
778
|
+
return out
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def _build_experiment_id_set(provenance: list[dict[str, Any]]) -> set[str]:
|
|
782
|
+
"""The set of well-formed experiment_id values across experiment_provenance[]."""
|
|
783
|
+
ids: set[str] = set()
|
|
784
|
+
for e in provenance:
|
|
785
|
+
eid = e.get("experiment_id")
|
|
786
|
+
if isinstance(eid, str) and eid:
|
|
787
|
+
ids.add(eid)
|
|
788
|
+
return ids
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def _check_experiment_provenance_invariants(
|
|
792
|
+
provenance: list[dict[str, Any]],
|
|
793
|
+
manifests: list[dict[str, Any]],
|
|
794
|
+
declaration: Any,
|
|
795
|
+
) -> list[Finding]:
|
|
796
|
+
findings: list[Finding] = []
|
|
797
|
+
|
|
798
|
+
# EP-INV-1: experiment_id unique within the passport.
|
|
799
|
+
seen: dict[str, int] = {}
|
|
800
|
+
for i, e in enumerate(provenance):
|
|
801
|
+
eid = e.get("experiment_id")
|
|
802
|
+
if not isinstance(eid, str) or not eid:
|
|
803
|
+
continue # schema-shape surfaces malformed/missing ids separately
|
|
804
|
+
if eid in seen:
|
|
805
|
+
findings.append(
|
|
806
|
+
Finding("EP-INV-1", f"duplicate experiment_id={eid!r} (also at experiment_provenance[{seen[eid]}])")
|
|
807
|
+
)
|
|
808
|
+
else:
|
|
809
|
+
seen[eid] = i
|
|
810
|
+
|
|
811
|
+
experiment_ids = set(seen.keys())
|
|
812
|
+
claim_detail = _build_claim_detail_index(manifests)
|
|
813
|
+
|
|
814
|
+
# EP-INV-2 + EP-INV-3: walk each manifest claim's planned_experiment_ids.
|
|
815
|
+
for (mid, cid), detail in claim_detail.items():
|
|
816
|
+
planned = detail.get("planned_experiment_ids")
|
|
817
|
+
if planned is None:
|
|
818
|
+
continue # optional-absent — nothing to check
|
|
819
|
+
if not isinstance(planned, list):
|
|
820
|
+
continue # schema-shape surfaces the type error
|
|
821
|
+
# EP-INV-2: every value resolves to exactly one provenance entry.
|
|
822
|
+
for pid in planned:
|
|
823
|
+
if not isinstance(pid, str):
|
|
824
|
+
continue
|
|
825
|
+
if pid not in experiment_ids:
|
|
826
|
+
findings.append(
|
|
827
|
+
Finding(
|
|
828
|
+
"EP-INV-2",
|
|
829
|
+
f"planned_experiment_ids value {pid!r} on (manifest={mid!r}, claim={cid!r}) "
|
|
830
|
+
f"resolves to no experiment_provenance[] entry (dangling: rename-without-re-emit "
|
|
831
|
+
f"or writer ran ahead of intake)",
|
|
832
|
+
)
|
|
833
|
+
)
|
|
834
|
+
# EP-INV-3: presence => owning claim is empirical. Mixed evidence
|
|
835
|
+
# (planned_refs AND planned_experiment_ids) is allowed — this only
|
|
836
|
+
# forbids experiment ids on a non-empirical claim.
|
|
837
|
+
kind = detail.get("intended_evidence_kind")
|
|
838
|
+
if kind != "empirical":
|
|
839
|
+
findings.append(
|
|
840
|
+
Finding(
|
|
841
|
+
"EP-INV-3",
|
|
842
|
+
f"planned_experiment_ids present on (manifest={mid!r}, claim={cid!r}) but "
|
|
843
|
+
f"intended_evidence_kind={kind!r}; experiment ids require empirical (mixed "
|
|
844
|
+
f"literature+experiment is allowed, non-empirical is not)",
|
|
845
|
+
)
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
# EP-INV-4: declaration <-> provenance symmetry. The deterministic
|
|
849
|
+
# structural half of D7 FAIL conditions #2 and #3.
|
|
850
|
+
status = None
|
|
851
|
+
if isinstance(declaration, dict):
|
|
852
|
+
status = declaration.get("status")
|
|
853
|
+
has_provenance = len(provenance) > 0
|
|
854
|
+
if status == "experiments_declared" and not has_provenance:
|
|
855
|
+
findings.append(
|
|
856
|
+
Finding(
|
|
857
|
+
"EP-INV-4",
|
|
858
|
+
"experiment_intake_declaration.status == experiments_declared but "
|
|
859
|
+
"experiment_provenance[] is absent/empty",
|
|
860
|
+
)
|
|
861
|
+
)
|
|
862
|
+
if status == "no_experiments_declared" and has_provenance:
|
|
863
|
+
findings.append(
|
|
864
|
+
Finding(
|
|
865
|
+
"EP-INV-4",
|
|
866
|
+
"experiment_intake_declaration.status == no_experiments_declared but "
|
|
867
|
+
"experiment_provenance[] is non-empty (a populated array contradicts the declaration)",
|
|
868
|
+
)
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
# EP-INV-5: when a declaration IS present, it must be well-formed. A
|
|
872
|
+
# malformed declaration (bad status enum, wrong declared_by, missing/empty
|
|
873
|
+
# declared_at) would otherwise pass silently — the symmetry checks above
|
|
874
|
+
# only fire on the two known status literals, so `status: "garbage"` slips
|
|
875
|
+
# through with no finding. This is the deterministic shape half of D7 the
|
|
876
|
+
# symmetry check alone does not cover; it does NOT decide presence/absence
|
|
877
|
+
# (the ars_version legacy gate, a Stage-1 gate check, owns that).
|
|
878
|
+
if declaration is not None:
|
|
879
|
+
if not isinstance(declaration, dict):
|
|
880
|
+
findings.append(
|
|
881
|
+
Finding(
|
|
882
|
+
"EP-INV-5",
|
|
883
|
+
f"experiment_intake_declaration must be a mapping; got {type(declaration).__name__}",
|
|
884
|
+
)
|
|
885
|
+
)
|
|
886
|
+
else:
|
|
887
|
+
allowed_status = {
|
|
888
|
+
"experiments_declared",
|
|
889
|
+
"no_experiments_declared",
|
|
890
|
+
"legacy_unknown",
|
|
891
|
+
}
|
|
892
|
+
if status not in allowed_status:
|
|
893
|
+
findings.append(
|
|
894
|
+
Finding(
|
|
895
|
+
"EP-INV-5",
|
|
896
|
+
f"experiment_intake_declaration.status={status!r} not in "
|
|
897
|
+
f"{sorted(allowed_status)}",
|
|
898
|
+
)
|
|
899
|
+
)
|
|
900
|
+
declared_by = declaration.get("declared_by")
|
|
901
|
+
if declared_by != "scholar":
|
|
902
|
+
findings.append(
|
|
903
|
+
Finding(
|
|
904
|
+
"EP-INV-5",
|
|
905
|
+
f"experiment_intake_declaration.declared_by={declared_by!r}; must be "
|
|
906
|
+
"'scholar' (an intake decision, never an agent emission)",
|
|
907
|
+
)
|
|
908
|
+
)
|
|
909
|
+
declared_at = declaration.get("declared_at")
|
|
910
|
+
if not isinstance(declared_at, str) or not declared_at:
|
|
911
|
+
findings.append(
|
|
912
|
+
Finding(
|
|
913
|
+
"EP-INV-5",
|
|
914
|
+
"experiment_intake_declaration.declared_at must be a non-empty "
|
|
915
|
+
f"date-time string; got {declared_at!r}",
|
|
916
|
+
)
|
|
917
|
+
)
|
|
918
|
+
|
|
919
|
+
return findings
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
def _check_experiment_alignment_invariants(
|
|
923
|
+
entries: list[dict[str, Any]],
|
|
924
|
+
manifest_index: dict[str, set[str]],
|
|
925
|
+
experiment_ids: set[str],
|
|
926
|
+
) -> list[Finding]:
|
|
927
|
+
findings: list[Finding] = []
|
|
928
|
+
|
|
929
|
+
# EA-INV-1: finding_id uniqueness.
|
|
930
|
+
seen: dict[str, int] = {}
|
|
931
|
+
for i, e in enumerate(entries):
|
|
932
|
+
fid = e.get("finding_id")
|
|
933
|
+
if not isinstance(fid, str):
|
|
934
|
+
continue
|
|
935
|
+
if fid in seen:
|
|
936
|
+
findings.append(
|
|
937
|
+
Finding("EA-INV-1", f"duplicate finding_id={fid!r} (also at experiment_alignment_results[{seen[fid]}])")
|
|
938
|
+
)
|
|
939
|
+
else:
|
|
940
|
+
seen[fid] = i
|
|
941
|
+
|
|
942
|
+
# EA-INV-2: (scoped_manifest_id, claim_id) resolves to a real manifest claim
|
|
943
|
+
# AND experiment_id resolves to a real experiment_provenance[] entry. A
|
|
944
|
+
# dangling id is a structural FAIL here (and at EP-INV-2 from the manifest
|
|
945
|
+
# side) — never represented as a judge verdict.
|
|
946
|
+
for e in entries:
|
|
947
|
+
fid = e.get("finding_id")
|
|
948
|
+
smid = e.get("scoped_manifest_id")
|
|
949
|
+
cid = e.get("claim_id")
|
|
950
|
+
if not (isinstance(smid, str) and isinstance(cid, str)):
|
|
951
|
+
continue # schema-shape surfaces the type error
|
|
952
|
+
if smid not in manifest_index or cid not in manifest_index[smid]:
|
|
953
|
+
findings.append(
|
|
954
|
+
Finding(
|
|
955
|
+
"EA-INV-2",
|
|
956
|
+
f"alignment row finding_id={fid!r} has ({smid!r}, {cid!r}) "
|
|
957
|
+
f"not present in any claim_intent_manifests[] entry",
|
|
958
|
+
)
|
|
959
|
+
)
|
|
960
|
+
eid = e.get("experiment_id")
|
|
961
|
+
if isinstance(eid, str) and eid and eid not in experiment_ids:
|
|
962
|
+
findings.append(
|
|
963
|
+
Finding(
|
|
964
|
+
"EA-INV-2",
|
|
965
|
+
f"alignment row finding_id={fid!r} references experiment_id={eid!r} "
|
|
966
|
+
f"with no matching experiment_provenance[] entry (structural FAIL, not a verdict)",
|
|
967
|
+
)
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
return findings
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
# ---------------------------------------------------------------------------
|
|
974
|
+
# uncited_assertion invariants (U-INV-1..U-INV-4).
|
|
975
|
+
# ---------------------------------------------------------------------------
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _check_uncited_invariants(
|
|
979
|
+
entries: list[dict[str, Any]],
|
|
980
|
+
manifest_index: dict[str, set[str]],
|
|
981
|
+
) -> list[Finding]:
|
|
982
|
+
findings: list[Finding] = []
|
|
983
|
+
# U-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
|
|
984
|
+
seen: dict[str, int] = {}
|
|
985
|
+
for i, e in enumerate(entries):
|
|
986
|
+
fid = e.get("finding_id")
|
|
987
|
+
if not isinstance(fid, str):
|
|
988
|
+
continue
|
|
989
|
+
if fid in seen:
|
|
990
|
+
findings.append(
|
|
991
|
+
Finding("U-INV-1", f"duplicate finding_id={fid!r} (also at uncited_assertions[{seen[fid]}])")
|
|
992
|
+
)
|
|
993
|
+
else:
|
|
994
|
+
seen[fid] = i
|
|
995
|
+
|
|
996
|
+
for e in entries:
|
|
997
|
+
# U-INV-2: trigger_tokens non-empty (schema also enforces minItems=1).
|
|
998
|
+
tokens = e.get("trigger_tokens", [])
|
|
999
|
+
if not isinstance(tokens, list) or len(tokens) == 0:
|
|
1000
|
+
findings.append(Finding("U-INV-2", f"empty trigger_tokens on finding_id={e.get('finding_id')!r}"))
|
|
1001
|
+
|
|
1002
|
+
# U-INV-3: rule_version literal.
|
|
1003
|
+
if e.get("rule_version") != "D4-c-v1":
|
|
1004
|
+
findings.append(
|
|
1005
|
+
Finding(
|
|
1006
|
+
"U-INV-3",
|
|
1007
|
+
f"rule_version={e.get('rule_version')!r} on finding_id={e.get('finding_id')!r}; must equal D4-c-v1",
|
|
1008
|
+
)
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
# U-INV-4: cross-array integrity.
|
|
1012
|
+
mcid = e.get("manifest_claim_id")
|
|
1013
|
+
smid = e.get("scoped_manifest_id")
|
|
1014
|
+
if mcid is None and smid is None:
|
|
1015
|
+
continue
|
|
1016
|
+
if mcid is not None and smid is None:
|
|
1017
|
+
findings.append(
|
|
1018
|
+
Finding(
|
|
1019
|
+
"U-INV-4",
|
|
1020
|
+
f"manifest_claim_id set but scoped_manifest_id null on finding_id={e.get('finding_id')!r}",
|
|
1021
|
+
)
|
|
1022
|
+
)
|
|
1023
|
+
continue
|
|
1024
|
+
if mcid is None and smid is not None:
|
|
1025
|
+
findings.append(
|
|
1026
|
+
Finding(
|
|
1027
|
+
"U-INV-4",
|
|
1028
|
+
f"scoped_manifest_id set but manifest_claim_id null on finding_id={e.get('finding_id')!r}",
|
|
1029
|
+
)
|
|
1030
|
+
)
|
|
1031
|
+
continue
|
|
1032
|
+
# Both non-null: must resolve.
|
|
1033
|
+
if smid not in manifest_index or mcid not in manifest_index[smid]:
|
|
1034
|
+
findings.append(
|
|
1035
|
+
Finding(
|
|
1036
|
+
"U-INV-4",
|
|
1037
|
+
f"dangling ({smid!r}, {mcid!r}) on finding_id={e.get('finding_id')!r}",
|
|
1038
|
+
)
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
return findings
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
# ---------------------------------------------------------------------------
|
|
1045
|
+
# claim_drift invariants (D-INV-1..D-INV-4).
|
|
1046
|
+
# ---------------------------------------------------------------------------
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
def _check_drift_invariants(
|
|
1050
|
+
entries: list[dict[str, Any]],
|
|
1051
|
+
uncited_entries: list[dict[str, Any]],
|
|
1052
|
+
manifest_index: dict[str, set[str]],
|
|
1053
|
+
) -> list[Finding]:
|
|
1054
|
+
findings: list[Finding] = []
|
|
1055
|
+
|
|
1056
|
+
# D-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
|
|
1057
|
+
seen: dict[str, int] = {}
|
|
1058
|
+
for i, e in enumerate(entries):
|
|
1059
|
+
fid = e.get("finding_id")
|
|
1060
|
+
if not isinstance(fid, str):
|
|
1061
|
+
continue
|
|
1062
|
+
if fid in seen:
|
|
1063
|
+
findings.append(
|
|
1064
|
+
Finding("D-INV-1", f"duplicate finding_id={fid!r} (also at claim_drifts[{seen[fid]}])")
|
|
1065
|
+
)
|
|
1066
|
+
else:
|
|
1067
|
+
seen[fid] = i
|
|
1068
|
+
|
|
1069
|
+
for e in entries:
|
|
1070
|
+
kind = e.get("drift_kind")
|
|
1071
|
+
mcid = e.get("manifest_claim_id")
|
|
1072
|
+
smid = e.get("scoped_manifest_id")
|
|
1073
|
+
section = e.get("section_path")
|
|
1074
|
+
fid = e.get("finding_id")
|
|
1075
|
+
|
|
1076
|
+
# D-INV-2 by drift_kind.
|
|
1077
|
+
if kind == "INTENDED_NOT_EMITTED":
|
|
1078
|
+
if mcid is None or smid is None:
|
|
1079
|
+
findings.append(
|
|
1080
|
+
Finding(
|
|
1081
|
+
"D-INV-2",
|
|
1082
|
+
f"INTENDED_NOT_EMITTED finding_id={fid!r} missing manifest_claim_id/scoped_manifest_id",
|
|
1083
|
+
)
|
|
1084
|
+
)
|
|
1085
|
+
else:
|
|
1086
|
+
if smid not in manifest_index or mcid not in manifest_index[smid]:
|
|
1087
|
+
findings.append(
|
|
1088
|
+
Finding(
|
|
1089
|
+
"D-INV-2",
|
|
1090
|
+
f"INTENDED_NOT_EMITTED finding_id={fid!r}: dangling ({smid!r}, {mcid!r})",
|
|
1091
|
+
)
|
|
1092
|
+
)
|
|
1093
|
+
elif kind == "EMITTED_NOT_INTENDED":
|
|
1094
|
+
if mcid is not None or smid is not None:
|
|
1095
|
+
findings.append(
|
|
1096
|
+
Finding(
|
|
1097
|
+
"D-INV-2",
|
|
1098
|
+
f"EMITTED_NOT_INTENDED finding_id={fid!r} has non-null manifest_claim_id/scoped_manifest_id",
|
|
1099
|
+
)
|
|
1100
|
+
)
|
|
1101
|
+
if not section:
|
|
1102
|
+
findings.append(
|
|
1103
|
+
Finding(
|
|
1104
|
+
"D-INV-2",
|
|
1105
|
+
f"EMITTED_NOT_INTENDED finding_id={fid!r} missing section_path",
|
|
1106
|
+
)
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
# D-INV-3: rule_version literal.
|
|
1110
|
+
if e.get("rule_version") != "D4-a-v1":
|
|
1111
|
+
findings.append(
|
|
1112
|
+
Finding(
|
|
1113
|
+
"D-INV-3",
|
|
1114
|
+
f"rule_version={e.get('rule_version')!r} on finding_id={fid!r}; must equal D4-a-v1",
|
|
1115
|
+
)
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
# D-INV-4: a sentence cannot appear in BOTH uncited_assertions[] AND claim_drifts[].
|
|
1119
|
+
uncited_texts = {u.get("sentence_text") for u in uncited_entries if u.get("sentence_text")}
|
|
1120
|
+
for e in entries:
|
|
1121
|
+
text = e.get("claim_text")
|
|
1122
|
+
if text and text in uncited_texts:
|
|
1123
|
+
findings.append(
|
|
1124
|
+
Finding(
|
|
1125
|
+
"D-INV-4",
|
|
1126
|
+
f"finding_id={e.get('finding_id')!r}: claim_text overlaps with an uncited_assertion sentence",
|
|
1127
|
+
)
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
return findings
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
# ---------------------------------------------------------------------------
|
|
1134
|
+
# constraint_violation invariants (CV-INV-1..CV-INV-4).
|
|
1135
|
+
# ---------------------------------------------------------------------------
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
def _check_constraint_violation_invariants(
|
|
1139
|
+
entries: list[dict[str, Any]],
|
|
1140
|
+
constraint_index: dict[str, dict[str, dict[str, Any]]],
|
|
1141
|
+
) -> list[Finding]:
|
|
1142
|
+
findings: list[Finding] = []
|
|
1143
|
+
|
|
1144
|
+
# CV-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
|
|
1145
|
+
seen: dict[str, int] = {}
|
|
1146
|
+
for i, e in enumerate(entries):
|
|
1147
|
+
fid = e.get("finding_id")
|
|
1148
|
+
if not isinstance(fid, str):
|
|
1149
|
+
continue
|
|
1150
|
+
if fid in seen:
|
|
1151
|
+
findings.append(
|
|
1152
|
+
Finding(
|
|
1153
|
+
"CV-INV-1",
|
|
1154
|
+
f"duplicate finding_id={fid!r} (also at constraint_violations[{seen[fid]}])",
|
|
1155
|
+
)
|
|
1156
|
+
)
|
|
1157
|
+
else:
|
|
1158
|
+
seen[fid] = i
|
|
1159
|
+
|
|
1160
|
+
# CV-INV-4: a sentence MUST NOT appear in constraint_violations[] more than once
|
|
1161
|
+
# per (scoped_manifest_id, section_path, claim_text_hash, violated_constraint_id).
|
|
1162
|
+
# Step 13 R8 codex P2-1: scope dedupe by manifest_id so two manifests in the
|
|
1163
|
+
# same passport carrying colliding MNC-* / NC-* ids — each legitimately
|
|
1164
|
+
# violated on the same sentence text — do not false-positive as duplicates.
|
|
1165
|
+
# M-INV-4 permits manifest_id uniqueness across passport but constraint_id
|
|
1166
|
+
# uniqueness only within a manifest; dedupe must respect the same scope.
|
|
1167
|
+
# v3.8.1 round 2: coerce each key component to str so schema-invalid
|
|
1168
|
+
# non-string values (lists/dicts) don't crash hashlib.encode() or hash().
|
|
1169
|
+
def _safe_str(value: Any) -> str:
|
|
1170
|
+
return value if isinstance(value, str) else ""
|
|
1171
|
+
|
|
1172
|
+
dedup: dict[tuple[str, str, str, str], int] = {}
|
|
1173
|
+
for i, e in enumerate(entries):
|
|
1174
|
+
key = (
|
|
1175
|
+
_safe_str(e.get("scoped_manifest_id")),
|
|
1176
|
+
_safe_str(e.get("section_path")),
|
|
1177
|
+
hashlib.sha256(_safe_str(e.get("claim_text")).encode("utf-8")).hexdigest(),
|
|
1178
|
+
_safe_str(e.get("violated_constraint_id")),
|
|
1179
|
+
)
|
|
1180
|
+
if key in dedup:
|
|
1181
|
+
findings.append(
|
|
1182
|
+
Finding(
|
|
1183
|
+
"CV-INV-4",
|
|
1184
|
+
f"finding_id={e.get('finding_id')!r}: duplicate (manifest, section, claim, constraint) with constraint_violations[{dedup[key]}]",
|
|
1185
|
+
)
|
|
1186
|
+
)
|
|
1187
|
+
else:
|
|
1188
|
+
dedup[key] = i
|
|
1189
|
+
|
|
1190
|
+
for e in entries:
|
|
1191
|
+
cid = e.get("violated_constraint_id") or ""
|
|
1192
|
+
smid = e.get("scoped_manifest_id")
|
|
1193
|
+
mcid = e.get("manifest_claim_id")
|
|
1194
|
+
fid = e.get("finding_id")
|
|
1195
|
+
|
|
1196
|
+
# CV-INV-2: (scoped_manifest_id, violated_constraint_id) MUST resolve.
|
|
1197
|
+
if smid not in constraint_index or cid not in constraint_index.get(smid, {}):
|
|
1198
|
+
findings.append(
|
|
1199
|
+
Finding(
|
|
1200
|
+
"CV-INV-2",
|
|
1201
|
+
f"finding_id={fid!r}: ({smid!r}, {cid!r}) not present in any manifest's constraint set",
|
|
1202
|
+
)
|
|
1203
|
+
)
|
|
1204
|
+
else:
|
|
1205
|
+
# CV-INV-2 (NC variant): manifest_claim_id MUST equal the C-{n} extracted from NC id.
|
|
1206
|
+
slot = constraint_index[smid][cid]
|
|
1207
|
+
if slot["kind"] == "NC":
|
|
1208
|
+
expected_claim = slot["claim_id"]
|
|
1209
|
+
if mcid != expected_claim:
|
|
1210
|
+
findings.append(
|
|
1211
|
+
Finding(
|
|
1212
|
+
"CV-INV-2",
|
|
1213
|
+
f"finding_id={fid!r}: NC violated_constraint_id={cid!r} scopes claim_id={expected_claim!r}, "
|
|
1214
|
+
f"but manifest_claim_id={mcid!r}",
|
|
1215
|
+
)
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
# CV-INV-3: MNC -> manifest_claim_id null; NC -> manifest_claim_id non-null and matches.
|
|
1219
|
+
mnc_match = RE_MNC_CONSTRAINT.match(cid)
|
|
1220
|
+
nc_match = RE_NC_CONSTRAINT.match(cid)
|
|
1221
|
+
if mnc_match and mcid is not None:
|
|
1222
|
+
findings.append(
|
|
1223
|
+
Finding(
|
|
1224
|
+
"CV-INV-3",
|
|
1225
|
+
f"finding_id={fid!r}: MNC-* constraint with non-null manifest_claim_id={mcid!r}",
|
|
1226
|
+
)
|
|
1227
|
+
)
|
|
1228
|
+
if nc_match:
|
|
1229
|
+
expected_digits = nc_match.group(1)
|
|
1230
|
+
if mcid is None:
|
|
1231
|
+
findings.append(
|
|
1232
|
+
Finding(
|
|
1233
|
+
"CV-INV-3",
|
|
1234
|
+
f"finding_id={fid!r}: NC-* constraint with null manifest_claim_id",
|
|
1235
|
+
)
|
|
1236
|
+
)
|
|
1237
|
+
else:
|
|
1238
|
+
mcid_match = RE_CLAIM_ID.match(mcid)
|
|
1239
|
+
actual_digits = mcid_match.group(1) if mcid_match else None
|
|
1240
|
+
if actual_digits != expected_digits:
|
|
1241
|
+
findings.append(
|
|
1242
|
+
Finding(
|
|
1243
|
+
"CV-INV-3",
|
|
1244
|
+
f"finding_id={fid!r}: NC digits {expected_digits!r} vs manifest_claim_id digits {actual_digits!r}",
|
|
1245
|
+
)
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
return findings
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
# ---------------------------------------------------------------------------
|
|
1252
|
+
# uncited_audit_failure invariants (UAF-INV-1..UAF-INV-6) — v3.8.2 / #118.
|
|
1253
|
+
# Surfaces transient judge outages on the uncited path that pre-v3.8.2 were
|
|
1254
|
+
# silently substituted as NOT_VIOLATED. Mirrors INV-14 semantics on the
|
|
1255
|
+
# cited path but uses a dedicated aggregate because claim_audit_result.ref_slug
|
|
1256
|
+
# is required. See docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md §3.6.
|
|
1257
|
+
# ---------------------------------------------------------------------------
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def _check_uaf_invariants(
|
|
1261
|
+
entries: list[dict[str, Any]],
|
|
1262
|
+
violations: list[dict[str, Any]],
|
|
1263
|
+
manifest_index: dict[str, set[str]],
|
|
1264
|
+
) -> list[Finding]:
|
|
1265
|
+
findings: list[Finding] = []
|
|
1266
|
+
|
|
1267
|
+
def _safe_str(value: Any) -> str:
|
|
1268
|
+
return value if isinstance(value, str) else ""
|
|
1269
|
+
|
|
1270
|
+
# UAF-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2
|
|
1271
|
+
# hardening pattern; schema validator surfaces type mismatch separately.
|
|
1272
|
+
seen: dict[str, int] = {}
|
|
1273
|
+
for i, e in enumerate(entries):
|
|
1274
|
+
fid = e.get("finding_id")
|
|
1275
|
+
if not isinstance(fid, str):
|
|
1276
|
+
continue
|
|
1277
|
+
if fid in seen:
|
|
1278
|
+
findings.append(
|
|
1279
|
+
Finding(
|
|
1280
|
+
"UAF-INV-1",
|
|
1281
|
+
f"duplicate finding_id={fid!r} (also at uncited_audit_failures[{seen[fid]}])",
|
|
1282
|
+
)
|
|
1283
|
+
)
|
|
1284
|
+
else:
|
|
1285
|
+
seen[fid] = i
|
|
1286
|
+
|
|
1287
|
+
# UAF-INV-4: per-(sentence, manifest) dedup with key
|
|
1288
|
+
# (scoped_manifest_id, section_path, claim_text_hash). Two manifests both
|
|
1289
|
+
# failing on the same sentence text emit two distinct rows legitimately
|
|
1290
|
+
# (cross-manifest scope mirrors CV-INV-4).
|
|
1291
|
+
dedup: dict[tuple[str, str, str], int] = {}
|
|
1292
|
+
for i, e in enumerate(entries):
|
|
1293
|
+
key = (
|
|
1294
|
+
_safe_str(e.get("scoped_manifest_id")),
|
|
1295
|
+
_safe_str(e.get("section_path")),
|
|
1296
|
+
hashlib.sha256(_safe_str(e.get("claim_text")).encode("utf-8")).hexdigest(),
|
|
1297
|
+
)
|
|
1298
|
+
if key in dedup:
|
|
1299
|
+
findings.append(
|
|
1300
|
+
Finding(
|
|
1301
|
+
"UAF-INV-4",
|
|
1302
|
+
f"finding_id={e.get('finding_id')!r}: duplicate (manifest, section, claim) with uncited_audit_failures[{dedup[key]}]",
|
|
1303
|
+
)
|
|
1304
|
+
)
|
|
1305
|
+
else:
|
|
1306
|
+
dedup[key] = i
|
|
1307
|
+
|
|
1308
|
+
for e in entries:
|
|
1309
|
+
fid = e.get("finding_id")
|
|
1310
|
+
smid = e.get("scoped_manifest_id")
|
|
1311
|
+
mcid = e.get("manifest_claim_id")
|
|
1312
|
+
rationale = e.get("rationale") or ""
|
|
1313
|
+
row_fault = e.get("fault_class")
|
|
1314
|
+
|
|
1315
|
+
# UAF-INV-2: scoped_manifest_id cross-array integrity.
|
|
1316
|
+
manifest_resolved = isinstance(smid, str) and smid in manifest_index
|
|
1317
|
+
if not manifest_resolved:
|
|
1318
|
+
findings.append(
|
|
1319
|
+
Finding(
|
|
1320
|
+
"UAF-INV-2",
|
|
1321
|
+
f"finding_id={fid!r}: scoped_manifest_id={smid!r} not present in claim_intent_manifests[]",
|
|
1322
|
+
)
|
|
1323
|
+
)
|
|
1324
|
+
# Don't `continue` here — UAF-INV-5 (rationale prefix) is
|
|
1325
|
+
# orthogonal to manifest integrity and we want both findings
|
|
1326
|
+
# to surface together so the user fixes both in one pass
|
|
1327
|
+
# (Gemini cross-model review P1, 2026-05-17).
|
|
1328
|
+
|
|
1329
|
+
# UAF-INV-3: (scoped_manifest_id, manifest_claim_id) pair integrity
|
|
1330
|
+
# when manifest_claim_id is non-null. Null is legitimate when the
|
|
1331
|
+
# failure was against MNCs only (no claim binding). Skip when the
|
|
1332
|
+
# manifest itself failed to resolve — the pair check would
|
|
1333
|
+
# otherwise always fire and double-report the same root cause.
|
|
1334
|
+
# Guard `mcid` with isinstance(str) before set membership: a
|
|
1335
|
+
# malformed passport with mcid as list/dict (schema flags this
|
|
1336
|
+
# separately) would raise TypeError in `mcid not in ...` and crash
|
|
1337
|
+
# the lint instead of returning a clean finding. Mirrors the v3.8.1
|
|
1338
|
+
# round-2 hardening pattern (Codex R2 P2-2, 2026-05-17).
|
|
1339
|
+
if manifest_resolved and mcid is not None and isinstance(mcid, str):
|
|
1340
|
+
claim_ids = manifest_index.get(smid, set())
|
|
1341
|
+
if mcid not in claim_ids:
|
|
1342
|
+
findings.append(
|
|
1343
|
+
Finding(
|
|
1344
|
+
"UAF-INV-3",
|
|
1345
|
+
f"finding_id={fid!r}: (scoped_manifest_id={smid!r}, manifest_claim_id={mcid!r}) not present in any manifest's claims[]",
|
|
1346
|
+
)
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
# UAF-INV-5: rationale MUST begin with this row's own fault_class
|
|
1350
|
+
# value followed by ":" (and " <detail>" when JudgeInvocationError
|
|
1351
|
+
# carried a non-empty detail). Mirrors INV-14 rationale prefix on
|
|
1352
|
+
# the cited path. We check against the row's fault_class field
|
|
1353
|
+
# (not any known tag) so a row with fault_class judge_timeout but
|
|
1354
|
+
# rationale starting with "judge_api_error: ..." still trips this
|
|
1355
|
+
# invariant — the prefix must match the row.
|
|
1356
|
+
# Guard `rationale` with isinstance(str): the row's raw rationale
|
|
1357
|
+
# may be list/dict on a malformed passport — schema flags it
|
|
1358
|
+
# separately and we should skip cleanly rather than crash on
|
|
1359
|
+
# `.startswith()` (Codex R2 P2-3, 2026-05-17). The `or ""`
|
|
1360
|
+
# initialization above only fallbacks on None/empty-string falsy
|
|
1361
|
+
# values; a truthy dict/list slips through and needs this guard.
|
|
1362
|
+
if (
|
|
1363
|
+
isinstance(row_fault, str)
|
|
1364
|
+
and row_fault in INV14_FAULT_CLASS_TAGS
|
|
1365
|
+
and isinstance(rationale, str)
|
|
1366
|
+
):
|
|
1367
|
+
expected_prefix = f"{row_fault}:"
|
|
1368
|
+
if not rationale.startswith(expected_prefix):
|
|
1369
|
+
findings.append(
|
|
1370
|
+
Finding(
|
|
1371
|
+
"UAF-INV-5",
|
|
1372
|
+
f"finding_id={fid!r}: rationale must begin with {expected_prefix!r} got {rationale[:60]!r}",
|
|
1373
|
+
)
|
|
1374
|
+
)
|
|
1375
|
+
|
|
1376
|
+
# UAF-INV-6: cross-aggregate exclusivity with constraint_violations[].
|
|
1377
|
+
# A sentence MUST NOT appear in both UAF and CV for the same
|
|
1378
|
+
# (scoped_manifest_id, section_path, claim_text_hash). VIOLATED (positive
|
|
1379
|
+
# verdict) and audit_tool_failure (no verdict) are mutually exclusive
|
|
1380
|
+
# verdict states at per-(sentence, manifest) level.
|
|
1381
|
+
cv_keys: set[tuple[str, str, str]] = set()
|
|
1382
|
+
for cv in violations:
|
|
1383
|
+
cv_keys.add(
|
|
1384
|
+
(
|
|
1385
|
+
_safe_str(cv.get("scoped_manifest_id")),
|
|
1386
|
+
_safe_str(cv.get("section_path")),
|
|
1387
|
+
hashlib.sha256(_safe_str(cv.get("claim_text")).encode("utf-8")).hexdigest(),
|
|
1388
|
+
)
|
|
1389
|
+
)
|
|
1390
|
+
for uaf in entries:
|
|
1391
|
+
key = (
|
|
1392
|
+
_safe_str(uaf.get("scoped_manifest_id")),
|
|
1393
|
+
_safe_str(uaf.get("section_path")),
|
|
1394
|
+
hashlib.sha256(_safe_str(uaf.get("claim_text")).encode("utf-8")).hexdigest(),
|
|
1395
|
+
)
|
|
1396
|
+
if key in cv_keys:
|
|
1397
|
+
findings.append(
|
|
1398
|
+
Finding(
|
|
1399
|
+
"UAF-INV-6",
|
|
1400
|
+
f"finding_id={uaf.get('finding_id')!r}: (manifest, section, claim) overlaps a constraint_violations[] row — UAF and CV are mutually exclusive verdict states",
|
|
1401
|
+
)
|
|
1402
|
+
)
|
|
1403
|
+
|
|
1404
|
+
return findings
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
# ---------------------------------------------------------------------------
|
|
1408
|
+
# audit_sampling_summary invariants (S-INV-1..S-INV-4).
|
|
1409
|
+
# ---------------------------------------------------------------------------
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
def _check_sampling_invariants(entries: list[dict[str, Any]]) -> list[Finding]:
|
|
1413
|
+
findings: list[Finding] = []
|
|
1414
|
+
for e in entries:
|
|
1415
|
+
indices = e.get("audited_indices", []) or []
|
|
1416
|
+
count = e.get("audited_count")
|
|
1417
|
+
cap = e.get("max_claims_per_paper")
|
|
1418
|
+
total = e.get("total_citation_count")
|
|
1419
|
+
run_id = e.get("audit_run_id")
|
|
1420
|
+
|
|
1421
|
+
# S-INV-1: audited_count == |audited_indices|.
|
|
1422
|
+
if isinstance(indices, list) and count != len(indices):
|
|
1423
|
+
findings.append(
|
|
1424
|
+
Finding(
|
|
1425
|
+
"S-INV-1",
|
|
1426
|
+
f"audit_run_id={run_id!r}: audited_count={count!r} != len(audited_indices)={len(indices)}",
|
|
1427
|
+
)
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
# S-INV-2: count ≤ cap AND count ≤ total.
|
|
1431
|
+
if isinstance(count, int):
|
|
1432
|
+
if isinstance(cap, int) and count > cap:
|
|
1433
|
+
findings.append(
|
|
1434
|
+
Finding(
|
|
1435
|
+
"S-INV-2",
|
|
1436
|
+
f"audit_run_id={run_id!r}: audited_count={count} > max_claims_per_paper={cap}",
|
|
1437
|
+
)
|
|
1438
|
+
)
|
|
1439
|
+
if isinstance(total, int) and count > total:
|
|
1440
|
+
findings.append(
|
|
1441
|
+
Finding(
|
|
1442
|
+
"S-INV-2",
|
|
1443
|
+
f"audit_run_id={run_id!r}: audited_count={count} > total_citation_count={total}",
|
|
1444
|
+
)
|
|
1445
|
+
)
|
|
1446
|
+
|
|
1447
|
+
# S-INV-4: strictly ascending, no duplicates. Guard against mixed-type
|
|
1448
|
+
# indices (#119 R6 codex P2 + Step 13 R8 P2-2) — schema records the
|
|
1449
|
+
# type mismatch separately; if a non-int slips through we skip the
|
|
1450
|
+
# comparison rather than raising TypeError on `<=` between str and int.
|
|
1451
|
+
if isinstance(indices, list):
|
|
1452
|
+
for i in range(1, len(indices)):
|
|
1453
|
+
a, b = indices[i - 1], indices[i]
|
|
1454
|
+
if not (isinstance(a, int) and isinstance(b, int)):
|
|
1455
|
+
continue
|
|
1456
|
+
if b <= a:
|
|
1457
|
+
findings.append(
|
|
1458
|
+
Finding(
|
|
1459
|
+
"S-INV-4",
|
|
1460
|
+
f"audit_run_id={run_id!r}: audited_indices not strictly ascending at position {i} "
|
|
1461
|
+
f"({a!r} -> {b!r})",
|
|
1462
|
+
)
|
|
1463
|
+
)
|
|
1464
|
+
break
|
|
1465
|
+
|
|
1466
|
+
return findings
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
# ---------------------------------------------------------------------------
|
|
1470
|
+
# Schema-shape validation (rule 1).
|
|
1471
|
+
# ---------------------------------------------------------------------------
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
def _validate_against_schema(
|
|
1475
|
+
entries: Any,
|
|
1476
|
+
schema_key: str,
|
|
1477
|
+
aggregate_label: str,
|
|
1478
|
+
) -> list[Finding]:
|
|
1479
|
+
"""Validate every entry against the named schema.
|
|
1480
|
+
|
|
1481
|
+
Accepts `Any` rather than `Iterable[dict]` because callers may hand us a
|
|
1482
|
+
malformed passport — `entries` could be non-list, or a list with
|
|
1483
|
+
non-dict elements. Both must surface as a clean schema finding rather
|
|
1484
|
+
than a TypeError / AttributeError traceback (Step 13 R4 codex P2 #4).
|
|
1485
|
+
"""
|
|
1486
|
+
findings: list[Finding] = []
|
|
1487
|
+
if not isinstance(entries, list):
|
|
1488
|
+
findings.append(
|
|
1489
|
+
Finding(
|
|
1490
|
+
"schema",
|
|
1491
|
+
f"{aggregate_label}: aggregate must be a JSON array; got {type(entries).__name__}",
|
|
1492
|
+
)
|
|
1493
|
+
)
|
|
1494
|
+
return findings
|
|
1495
|
+
validator = _validator(schema_key)
|
|
1496
|
+
for i, entry in enumerate(entries):
|
|
1497
|
+
if not isinstance(entry, dict):
|
|
1498
|
+
findings.append(
|
|
1499
|
+
Finding(
|
|
1500
|
+
"schema",
|
|
1501
|
+
f"{aggregate_label}[{i}]: entry must be a JSON object; got {type(entry).__name__}",
|
|
1502
|
+
)
|
|
1503
|
+
)
|
|
1504
|
+
continue
|
|
1505
|
+
for err in validator.iter_errors(entry):
|
|
1506
|
+
findings.append(
|
|
1507
|
+
Finding(
|
|
1508
|
+
"schema",
|
|
1509
|
+
f"{aggregate_label}[{i}] {'.'.join(str(p) for p in err.absolute_path) or '<root>'}: {err.message}",
|
|
1510
|
+
)
|
|
1511
|
+
)
|
|
1512
|
+
return findings
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
# ---------------------------------------------------------------------------
|
|
1516
|
+
# Driver
|
|
1517
|
+
# ---------------------------------------------------------------------------
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
def validate_passport(body: Any) -> list[Finding]:
|
|
1521
|
+
"""Run all 39 invariants + schema-shape against a passport body. Returns findings."""
|
|
1522
|
+
# Step 13 R7 codex P3: a syntactically valid JSON top level can still
|
|
1523
|
+
# be `[]`, `null`, or a scalar. `.get()` on those raises AttributeError;
|
|
1524
|
+
# surface a clean schema finding instead so the CLI can return findings
|
|
1525
|
+
# rather than tracing back.
|
|
1526
|
+
if not isinstance(body, dict):
|
|
1527
|
+
return [
|
|
1528
|
+
Finding(
|
|
1529
|
+
"schema",
|
|
1530
|
+
f"passport body must be a JSON object; got {type(body).__name__}",
|
|
1531
|
+
)
|
|
1532
|
+
]
|
|
1533
|
+
# Raw aggregates — preserved for schema-shape validation below (which
|
|
1534
|
+
# records findings on non-list / non-dict-entry inputs). MUST NOT use
|
|
1535
|
+
# `or []` because a falsey but malformed value such as `{}`, `null`, `0`,
|
|
1536
|
+
# or `""` would silently be replaced with an empty list and bypass the
|
|
1537
|
+
# schema gate (Step 13 R5 codex P2 #1). Only a missing key falls back to
|
|
1538
|
+
# the empty-list default.
|
|
1539
|
+
manifests_raw = body.get("claim_intent_manifests", [])
|
|
1540
|
+
results_raw = body.get("claim_audit_results", [])
|
|
1541
|
+
uncited_raw = body.get("uncited_assertions", [])
|
|
1542
|
+
drifts_raw = body.get("claim_drifts", [])
|
|
1543
|
+
violations_raw = body.get("constraint_violations", [])
|
|
1544
|
+
samplings_raw = body.get("audit_sampling_summaries", [])
|
|
1545
|
+
uaf_raw = body.get("uncited_audit_failures", [])
|
|
1546
|
+
provenance_raw = body.get("experiment_provenance", []) # #260
|
|
1547
|
+
alignment_raw = body.get("experiment_alignment_results", []) # #260
|
|
1548
|
+
declaration = body.get("experiment_intake_declaration") # #260 (object | None)
|
|
1549
|
+
|
|
1550
|
+
def _coerce_aggregate(value: Any) -> list[dict[str, Any]]:
|
|
1551
|
+
"""Reduce an aggregate to a list of dict entries.
|
|
1552
|
+
|
|
1553
|
+
Schema-shape failures are surfaced separately by
|
|
1554
|
+
`_validate_against_schema` against the raw aggregate. The dict-only
|
|
1555
|
+
invariant loops below attempt `.get()` / dictionary indexing —
|
|
1556
|
+
passing a non-list or a list with non-dict entries would raise
|
|
1557
|
+
AttributeError and crash the lint with a traceback instead of
|
|
1558
|
+
returning a clean failure finding (Step 13 R4 codex P2 #4).
|
|
1559
|
+
"""
|
|
1560
|
+
if not isinstance(value, list):
|
|
1561
|
+
return []
|
|
1562
|
+
return [e for e in value if isinstance(e, dict)]
|
|
1563
|
+
|
|
1564
|
+
manifests = _coerce_aggregate(manifests_raw)
|
|
1565
|
+
results = _coerce_aggregate(results_raw)
|
|
1566
|
+
uncited = _coerce_aggregate(uncited_raw)
|
|
1567
|
+
drifts = _coerce_aggregate(drifts_raw)
|
|
1568
|
+
violations = _coerce_aggregate(violations_raw)
|
|
1569
|
+
samplings = _coerce_aggregate(samplings_raw)
|
|
1570
|
+
uaf = _coerce_aggregate(uaf_raw)
|
|
1571
|
+
provenance = _coerce_aggregate(provenance_raw) # #260
|
|
1572
|
+
alignment = _coerce_aggregate(alignment_raw) # #260
|
|
1573
|
+
|
|
1574
|
+
findings: list[Finding] = []
|
|
1575
|
+
|
|
1576
|
+
# Schema-shape — gives the lint a chance to surface malformed entries with
|
|
1577
|
+
# an actionable rendering before cross-field checks fan out spurious tags.
|
|
1578
|
+
findings.extend(_validate_against_schema(manifests_raw, "claim_intent_manifest", "claim_intent_manifests"))
|
|
1579
|
+
findings.extend(_validate_against_schema(results_raw, "claim_audit_result", "claim_audit_results"))
|
|
1580
|
+
findings.extend(_validate_against_schema(uncited_raw, "uncited_assertion", "uncited_assertions"))
|
|
1581
|
+
findings.extend(_validate_against_schema(drifts_raw, "claim_drift", "claim_drifts"))
|
|
1582
|
+
findings.extend(_validate_against_schema(violations_raw, "constraint_violation", "constraint_violations"))
|
|
1583
|
+
findings.extend(_validate_against_schema(samplings_raw, "audit_sampling_summary", "audit_sampling_summaries"))
|
|
1584
|
+
findings.extend(_validate_against_schema(uaf_raw, "uncited_audit_failure", "uncited_audit_failures"))
|
|
1585
|
+
findings.extend(_validate_against_schema(provenance_raw, "experiment_provenance_entry", "experiment_provenance"))
|
|
1586
|
+
findings.extend(_validate_against_schema(alignment_raw, "experiment_alignment_result", "experiment_alignment_results"))
|
|
1587
|
+
|
|
1588
|
+
# Manifest invariants + index for downstream cross-array checks.
|
|
1589
|
+
findings.extend(_check_manifest_invariants(manifests))
|
|
1590
|
+
manifest_index = _build_manifest_index(manifests)
|
|
1591
|
+
constraint_index = _build_manifest_constraint_index(manifests)
|
|
1592
|
+
|
|
1593
|
+
# claim_audit_result invariants.
|
|
1594
|
+
for e in results:
|
|
1595
|
+
findings.extend(_check_inv_1(e))
|
|
1596
|
+
findings.extend(_check_inv_2(e))
|
|
1597
|
+
findings.extend(_check_inv_3(e))
|
|
1598
|
+
findings.extend(_check_inv_4(e))
|
|
1599
|
+
findings.extend(_check_inv_5(e))
|
|
1600
|
+
findings.extend(_check_inv_6(e))
|
|
1601
|
+
findings.extend(_check_inv_7(e))
|
|
1602
|
+
findings.extend(_check_inv_8(e))
|
|
1603
|
+
findings.extend(_check_inv_9(e))
|
|
1604
|
+
findings.extend(_check_inv_10(e))
|
|
1605
|
+
findings.extend(_check_inv_11(e))
|
|
1606
|
+
findings.extend(_check_inv_12(e))
|
|
1607
|
+
findings.extend(_check_inv_13(e))
|
|
1608
|
+
findings.extend(_check_inv_14(e))
|
|
1609
|
+
findings.extend(_check_inv_15(e, manifest_index))
|
|
1610
|
+
findings.extend(_check_inv_16(e))
|
|
1611
|
+
findings.extend(_check_inv_18(e))
|
|
1612
|
+
findings.extend(_check_inv_19(e))
|
|
1613
|
+
findings.extend(_check_matrix(e))
|
|
1614
|
+
|
|
1615
|
+
findings.extend(_check_uncited_invariants(uncited, manifest_index))
|
|
1616
|
+
findings.extend(_check_drift_invariants(drifts, uncited, manifest_index))
|
|
1617
|
+
findings.extend(_check_constraint_violation_invariants(violations, constraint_index))
|
|
1618
|
+
findings.extend(_check_sampling_invariants(samplings))
|
|
1619
|
+
findings.extend(_check_uaf_invariants(uaf, violations, manifest_index))
|
|
1620
|
+
|
|
1621
|
+
# #260 experiment provenance / alignment cross-array invariants.
|
|
1622
|
+
findings.extend(_check_experiment_provenance_invariants(provenance, manifests, declaration))
|
|
1623
|
+
experiment_ids = _build_experiment_id_set(provenance)
|
|
1624
|
+
findings.extend(_check_experiment_alignment_invariants(alignment, manifest_index, experiment_ids))
|
|
1625
|
+
|
|
1626
|
+
return findings
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
def main(argv: list[str] | None = None) -> int:
|
|
1630
|
+
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
1631
|
+
p.add_argument(
|
|
1632
|
+
"--passport",
|
|
1633
|
+
type=Path,
|
|
1634
|
+
required=True,
|
|
1635
|
+
help="Path to a passport-shaped JSON containing the six aggregates the lint reads.",
|
|
1636
|
+
)
|
|
1637
|
+
args = p.parse_args(argv)
|
|
1638
|
+
|
|
1639
|
+
if not args.passport.is_file():
|
|
1640
|
+
print(f"passport not found: {args.passport}", file=sys.stderr)
|
|
1641
|
+
return 2
|
|
1642
|
+
|
|
1643
|
+
try:
|
|
1644
|
+
body = json.loads(args.passport.read_text(encoding="utf-8"))
|
|
1645
|
+
except json.JSONDecodeError as exc:
|
|
1646
|
+
print(f"malformed JSON in {args.passport}: {exc}", file=sys.stderr)
|
|
1647
|
+
return 2
|
|
1648
|
+
|
|
1649
|
+
try:
|
|
1650
|
+
findings = validate_passport(body)
|
|
1651
|
+
except FileNotFoundError as exc:
|
|
1652
|
+
print(f"internal: {exc}", file=sys.stderr)
|
|
1653
|
+
return 2
|
|
1654
|
+
|
|
1655
|
+
if not findings:
|
|
1656
|
+
return 0
|
|
1657
|
+
|
|
1658
|
+
for f in findings:
|
|
1659
|
+
print(f.render())
|
|
1660
|
+
return 1
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
if __name__ == "__main__":
|
|
1664
|
+
sys.exit(main())
|