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,2133 @@
|
|
|
1
|
+
"""Phase 6.3 acceptance tests for ARS v3.6.7 §3.7 invariant lint.
|
|
2
|
+
|
|
3
|
+
Every row in spec §3.7 families A-F gets at least one positive (valid
|
|
4
|
+
fixture passes) and one negative (deliberately-broken fixture fails)
|
|
5
|
+
test. The aggregator (run_checks) is also smoke-tested on a fully-
|
|
6
|
+
valid synthetic proposal + persisted bundle.
|
|
7
|
+
|
|
8
|
+
Per the user's iron law: positive + negative tests for every rule.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import copy
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
from scripts.check_audit_artifact_consistency import (
|
|
23
|
+
LintContext,
|
|
24
|
+
LintError,
|
|
25
|
+
check_a1,
|
|
26
|
+
check_a2,
|
|
27
|
+
check_a3,
|
|
28
|
+
check_a4,
|
|
29
|
+
check_a5,
|
|
30
|
+
check_a6,
|
|
31
|
+
check_a7,
|
|
32
|
+
check_b1,
|
|
33
|
+
check_b2,
|
|
34
|
+
check_b3,
|
|
35
|
+
check_b4,
|
|
36
|
+
check_b5,
|
|
37
|
+
check_b6,
|
|
38
|
+
check_b7,
|
|
39
|
+
check_b8,
|
|
40
|
+
check_b9,
|
|
41
|
+
check_b10,
|
|
42
|
+
check_c1,
|
|
43
|
+
check_c2,
|
|
44
|
+
check_c3,
|
|
45
|
+
check_c4,
|
|
46
|
+
check_d1,
|
|
47
|
+
check_d2,
|
|
48
|
+
check_d3,
|
|
49
|
+
check_d4,
|
|
50
|
+
check_e1_e2_e6,
|
|
51
|
+
check_e3_e4,
|
|
52
|
+
check_e5,
|
|
53
|
+
check_e7,
|
|
54
|
+
check_f1,
|
|
55
|
+
check_f2,
|
|
56
|
+
check_f3,
|
|
57
|
+
compute_bundle_manifest,
|
|
58
|
+
main,
|
|
59
|
+
run_checks,
|
|
60
|
+
run_example_harness,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
REPO = Path(__file__).resolve().parent.parent
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# Synthetic fixtures (kept in-memory; written to tmp_path when files needed)
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
VALID_RUN_ID = "2026-04-30T15-22-04Z-d8f3"
|
|
71
|
+
VALID_THREAD_ID = "019de371-4c13-7521-8af7-fccf6bd23279"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def make_valid_verdict_file_minor() -> dict[str, Any]:
|
|
75
|
+
return {
|
|
76
|
+
"run_id": VALID_RUN_ID,
|
|
77
|
+
"verdict_status": "MINOR",
|
|
78
|
+
"round": 2,
|
|
79
|
+
"target_rounds": 3,
|
|
80
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 1},
|
|
81
|
+
"findings": [
|
|
82
|
+
{"id": "F-007", "severity": "P3", "dimension": "3.7",
|
|
83
|
+
"file": "chapter_4/synthesis.md", "line": 482,
|
|
84
|
+
"description": "deictic temporal phrase",
|
|
85
|
+
"suggested_fix": "replace with explicit date"},
|
|
86
|
+
],
|
|
87
|
+
"generated_at": "2026-04-30T15:22:58.471Z",
|
|
88
|
+
"generated_by": "scripts/run_codex_audit.sh",
|
|
89
|
+
"generator_version": "1.0.0",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def make_valid_verdict_file_pass() -> dict[str, Any]:
|
|
94
|
+
return {
|
|
95
|
+
"run_id": VALID_RUN_ID,
|
|
96
|
+
"verdict_status": "PASS",
|
|
97
|
+
"round": 1,
|
|
98
|
+
"target_rounds": 3,
|
|
99
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 0},
|
|
100
|
+
"findings": [],
|
|
101
|
+
"generated_at": "2026-04-30T15:22:58.471Z",
|
|
102
|
+
"generated_by": "scripts/run_codex_audit.sh",
|
|
103
|
+
"generator_version": "1.0.0",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def make_valid_verdict_file_audit_failed() -> dict[str, Any]:
|
|
108
|
+
return {
|
|
109
|
+
"run_id": VALID_RUN_ID,
|
|
110
|
+
"verdict_status": "AUDIT_FAILED",
|
|
111
|
+
"round": 1,
|
|
112
|
+
"target_rounds": 3,
|
|
113
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 0},
|
|
114
|
+
"findings": [],
|
|
115
|
+
"failure_reason": "codex exit 70: network timeout",
|
|
116
|
+
"generated_at": "2026-04-30T15:22:58.471Z",
|
|
117
|
+
"generated_by": "scripts/run_codex_audit.sh",
|
|
118
|
+
"generator_version": "1.0.0",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def make_valid_persisted_entry_minor() -> dict[str, Any]:
|
|
123
|
+
return {
|
|
124
|
+
"stage": 2,
|
|
125
|
+
"agent": "synthesis_agent",
|
|
126
|
+
"deliverable_path": "chapter_4/synthesis.md",
|
|
127
|
+
"deliverable_sha": "a" * 64,
|
|
128
|
+
"run_id": VALID_RUN_ID,
|
|
129
|
+
"bundle_id": "phase2-chapter4-2026-04-30",
|
|
130
|
+
"bundle_manifest_sha": "9" * 64,
|
|
131
|
+
"artifact_paths": {
|
|
132
|
+
"jsonl": f"{VALID_RUN_ID}.jsonl",
|
|
133
|
+
"sidecar": f"{VALID_RUN_ID}.meta.json",
|
|
134
|
+
"verdict": f"{VALID_RUN_ID}.verdict.yaml",
|
|
135
|
+
},
|
|
136
|
+
"verdict": {
|
|
137
|
+
"status": "MINOR",
|
|
138
|
+
"round": 2,
|
|
139
|
+
"target_rounds": 3,
|
|
140
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 1},
|
|
141
|
+
"verified_at": "2026-04-30T15:23:11.847Z",
|
|
142
|
+
"verified_by": "pipeline_orchestrator_agent",
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def make_valid_proposal_entry_pass() -> dict[str, Any]:
|
|
148
|
+
return {
|
|
149
|
+
"stage": 2,
|
|
150
|
+
"agent": "synthesis_agent",
|
|
151
|
+
"deliverable_path": "chapter_4/synthesis.md",
|
|
152
|
+
"deliverable_sha": "a" * 64,
|
|
153
|
+
"run_id": VALID_RUN_ID,
|
|
154
|
+
"bundle_id": "phase2-chapter4-2026-04-30",
|
|
155
|
+
"bundle_manifest_sha": "9" * 64,
|
|
156
|
+
"artifact_paths": {
|
|
157
|
+
"jsonl": f"{VALID_RUN_ID}.jsonl",
|
|
158
|
+
"sidecar": f"{VALID_RUN_ID}.meta.json",
|
|
159
|
+
"verdict": f"{VALID_RUN_ID}.verdict.yaml",
|
|
160
|
+
},
|
|
161
|
+
"verdict": {
|
|
162
|
+
"status": "PASS",
|
|
163
|
+
"round": 1,
|
|
164
|
+
"target_rounds": 3,
|
|
165
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 0},
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def make_valid_sidecar(template_sha: str = "1" * 64) -> dict[str, Any]:
|
|
171
|
+
primary = [{"path": "chapter_4/synthesis.md", "sha": "a" * 64}]
|
|
172
|
+
supporting = [{"path": "chapter_4/bibliography.json", "sha": "b" * 64}]
|
|
173
|
+
_, manifest_sha = compute_bundle_manifest(
|
|
174
|
+
primary, supporting, "shared/templates/codex_audit_multifile_template.md", template_sha
|
|
175
|
+
)
|
|
176
|
+
return {
|
|
177
|
+
"run_id": VALID_RUN_ID,
|
|
178
|
+
"codex_cli_version": "0.128.0",
|
|
179
|
+
"runner": {
|
|
180
|
+
"hostname": "runner.example.local",
|
|
181
|
+
"cwd": "/path/to/academic-research-skills",
|
|
182
|
+
"git_sha": "b4fbffd",
|
|
183
|
+
"git_dirty": False,
|
|
184
|
+
},
|
|
185
|
+
"timing": {
|
|
186
|
+
"started_at": "2026-04-30T15:22:04.123Z",
|
|
187
|
+
"ended_at": "2026-04-30T15:22:58.471Z",
|
|
188
|
+
"duration_seconds": 54.348,
|
|
189
|
+
},
|
|
190
|
+
"process": {
|
|
191
|
+
"exit_code": 0,
|
|
192
|
+
"stdout_path": f"audit_artifacts/{VALID_RUN_ID}.stdout",
|
|
193
|
+
"stderr_path": f"audit_artifacts/{VALID_RUN_ID}.stderr",
|
|
194
|
+
},
|
|
195
|
+
"stream": {"jsonl_thread_id": VALID_THREAD_ID},
|
|
196
|
+
"prompt": {
|
|
197
|
+
"audit_template_path": "shared/templates/codex_audit_multifile_template.md",
|
|
198
|
+
"audit_template_sha": template_sha,
|
|
199
|
+
"bundle": {
|
|
200
|
+
"bundle_id": "phase2-chapter4-2026-04-30",
|
|
201
|
+
"bundle_manifest_sha": manifest_sha,
|
|
202
|
+
"primary_deliverables": primary,
|
|
203
|
+
"supporting_context": supporting,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def make_valid_jsonl_events_no_tool() -> list[dict[str, Any]]:
|
|
210
|
+
return [
|
|
211
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
212
|
+
{"type": "turn.started"},
|
|
213
|
+
{"type": "item.completed",
|
|
214
|
+
"item": {"id": "item_0", "type": "agent_message",
|
|
215
|
+
"text": "## Section 6 — Verdict\n\nRound 1: 0 findings of any severity. Convergence reached.\n"}},
|
|
216
|
+
{"type": "turn.completed",
|
|
217
|
+
"usage": {"input_tokens": 100, "cached_input_tokens": 0,
|
|
218
|
+
"output_tokens": 50, "reasoning_output_tokens": 25}},
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def make_valid_jsonl_events_tool_using() -> list[dict[str, Any]]:
|
|
223
|
+
return [
|
|
224
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
225
|
+
{"type": "turn.started"},
|
|
226
|
+
{"type": "item.started", "item": {"id": "item_1", "type": "command_execution"}},
|
|
227
|
+
{"type": "item.completed", "item": {"id": "item_1", "type": "command_execution"}},
|
|
228
|
+
{"type": "item.completed",
|
|
229
|
+
"item": {"id": "item_2", "type": "agent_message",
|
|
230
|
+
"text": "## Section 6 — Verdict\n\nRound 1: 0 findings of any severity. Convergence reached.\n"}},
|
|
231
|
+
{"type": "turn.completed",
|
|
232
|
+
"usage": {"input_tokens": 100, "cached_input_tokens": 0,
|
|
233
|
+
"output_tokens": 50, "reasoning_output_tokens": 25}},
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ---------------------------------------------------------------------------
|
|
238
|
+
# Family A
|
|
239
|
+
# ---------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class TestA1:
|
|
243
|
+
"""A1 — verdict.status agrees with finding_counts and failure_reason."""
|
|
244
|
+
|
|
245
|
+
def test_pass_with_zero_counts_passes(self):
|
|
246
|
+
v = make_valid_verdict_file_pass()
|
|
247
|
+
assert check_a1(None, v) == []
|
|
248
|
+
|
|
249
|
+
def test_pass_with_nonzero_p1_fails(self):
|
|
250
|
+
v = make_valid_verdict_file_pass()
|
|
251
|
+
v["finding_counts"]["p1"] = 1
|
|
252
|
+
findings = check_a1(None, v)
|
|
253
|
+
assert any(f.rule_id == "A1" and "PASS" in f.message for f in findings)
|
|
254
|
+
|
|
255
|
+
def test_minor_with_p3_le_3_passes(self):
|
|
256
|
+
v = make_valid_verdict_file_minor()
|
|
257
|
+
assert check_a1(None, v) == []
|
|
258
|
+
|
|
259
|
+
def test_minor_with_p3_4_fails(self):
|
|
260
|
+
v = make_valid_verdict_file_minor()
|
|
261
|
+
v["finding_counts"]["p3"] = 4
|
|
262
|
+
findings = check_a1(None, v)
|
|
263
|
+
assert any("MINOR" in f.message for f in findings)
|
|
264
|
+
|
|
265
|
+
def test_minor_with_zero_counts_fails(self):
|
|
266
|
+
# Codex round 10 P2: MINOR with p1=p2=p3=0 is malformed because the
|
|
267
|
+
# wrapper classifies zero findings as PASS, not MINOR. A hand-edited
|
|
268
|
+
# verdict file claiming zero-count MINOR would otherwise pass A1
|
|
269
|
+
# and send the orchestrator down the MINOR escalation path with
|
|
270
|
+
# no findings to act on.
|
|
271
|
+
v = make_valid_verdict_file_minor()
|
|
272
|
+
v["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
273
|
+
findings = check_a1(None, v)
|
|
274
|
+
assert any(f.rule_id == "A1" and "MINOR" in f.message for f in findings), [
|
|
275
|
+
f.render() for f in findings
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
def test_material_with_p1_gt_0_passes(self):
|
|
279
|
+
v = {**make_valid_verdict_file_pass(),
|
|
280
|
+
"verdict_status": "MATERIAL",
|
|
281
|
+
"finding_counts": {"p1": 1, "p2": 0, "p3": 0}}
|
|
282
|
+
assert check_a1(None, v) == []
|
|
283
|
+
|
|
284
|
+
def test_material_all_zero_fails(self):
|
|
285
|
+
v = {**make_valid_verdict_file_pass(), "verdict_status": "MATERIAL"}
|
|
286
|
+
findings = check_a1(None, v)
|
|
287
|
+
assert any("MATERIAL" in f.message for f in findings)
|
|
288
|
+
|
|
289
|
+
def test_audit_failed_with_failure_reason_passes(self):
|
|
290
|
+
v = make_valid_verdict_file_audit_failed()
|
|
291
|
+
assert check_a1(None, v) == []
|
|
292
|
+
|
|
293
|
+
def test_audit_failed_missing_failure_reason_fails(self):
|
|
294
|
+
v = make_valid_verdict_file_audit_failed()
|
|
295
|
+
del v["failure_reason"]
|
|
296
|
+
findings = check_a1(None, v)
|
|
297
|
+
assert any("AUDIT_FAILED" in f.message for f in findings)
|
|
298
|
+
|
|
299
|
+
def test_pass_with_failure_reason_fails(self):
|
|
300
|
+
v = make_valid_verdict_file_pass()
|
|
301
|
+
v["failure_reason"] = "should not be here"
|
|
302
|
+
findings = check_a1(None, v)
|
|
303
|
+
assert any("PASS forbids failure_reason" in f.message for f in findings)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class TestA2:
|
|
307
|
+
"""A2 — failure_reason iff status == AUDIT_FAILED."""
|
|
308
|
+
|
|
309
|
+
def test_audit_failed_with_reason_passes(self):
|
|
310
|
+
v = make_valid_verdict_file_audit_failed()
|
|
311
|
+
assert check_a2(None, v) == []
|
|
312
|
+
|
|
313
|
+
def test_audit_failed_without_reason_fails(self):
|
|
314
|
+
v = make_valid_verdict_file_audit_failed()
|
|
315
|
+
del v["failure_reason"]
|
|
316
|
+
findings = check_a2(None, v)
|
|
317
|
+
assert any(f.rule_id == "A2" for f in findings)
|
|
318
|
+
|
|
319
|
+
def test_minor_with_reason_fails(self):
|
|
320
|
+
v = make_valid_verdict_file_minor()
|
|
321
|
+
v["failure_reason"] = "leftover"
|
|
322
|
+
findings = check_a2(None, v)
|
|
323
|
+
assert any(f.rule_id == "A2" and "forbidden" in f.message for f in findings)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class TestA3:
|
|
327
|
+
"""A3 — round <= target_rounds."""
|
|
328
|
+
|
|
329
|
+
def test_round_le_target_passes(self):
|
|
330
|
+
v = make_valid_verdict_file_minor() # round=2 target=3
|
|
331
|
+
assert check_a3(None, v) == []
|
|
332
|
+
|
|
333
|
+
def test_round_gt_target_fails(self):
|
|
334
|
+
v = make_valid_verdict_file_minor()
|
|
335
|
+
v["round"] = 5
|
|
336
|
+
findings = check_a3(None, v)
|
|
337
|
+
assert any(f.rule_id == "A3" for f in findings)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class TestA4:
|
|
341
|
+
"""A4 — acknowledgement only on persisted MATERIAL."""
|
|
342
|
+
|
|
343
|
+
def test_persisted_material_with_ack_passes(self):
|
|
344
|
+
e = make_valid_persisted_entry_minor()
|
|
345
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
346
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
347
|
+
e["acknowledgement"] = {
|
|
348
|
+
"finding_ids": ["F-001"],
|
|
349
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
350
|
+
"acknowledged_by": "user",
|
|
351
|
+
}
|
|
352
|
+
assert check_a4(e, "persisted") == []
|
|
353
|
+
|
|
354
|
+
def test_proposal_with_ack_fails(self):
|
|
355
|
+
e = make_valid_proposal_entry_pass()
|
|
356
|
+
e["acknowledgement"] = {"finding_ids": ["F-001"],
|
|
357
|
+
"acknowledged_at": "2026-04-30T15:23:11.847Z",
|
|
358
|
+
"acknowledged_by": "user"}
|
|
359
|
+
findings = check_a4(e, "proposal")
|
|
360
|
+
assert any(f.rule_id == "A4" for f in findings)
|
|
361
|
+
|
|
362
|
+
def test_persisted_pass_with_ack_fails(self):
|
|
363
|
+
e = make_valid_persisted_entry_minor()
|
|
364
|
+
e["verdict"]["status"] = "PASS"
|
|
365
|
+
e["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
366
|
+
e["acknowledgement"] = {"finding_ids": ["F-001"],
|
|
367
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
368
|
+
"acknowledged_by": "user"}
|
|
369
|
+
findings = check_a4(e, "persisted")
|
|
370
|
+
assert any(f.rule_id == "A4" and "MATERIAL" in f.message for f in findings)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class TestA5:
|
|
374
|
+
"""A5 — finding_counts.pN == count(findings[severity==PN])."""
|
|
375
|
+
|
|
376
|
+
def test_counts_agree_passes(self):
|
|
377
|
+
v = make_valid_verdict_file_minor()
|
|
378
|
+
assert check_a5(v) == []
|
|
379
|
+
|
|
380
|
+
def test_count_disagrees_fails(self):
|
|
381
|
+
v = make_valid_verdict_file_minor()
|
|
382
|
+
v["finding_counts"]["p3"] = 5 # but findings only has 1 P3
|
|
383
|
+
findings = check_a5(v)
|
|
384
|
+
assert any(f.rule_id == "A5" and "p3" in f.message for f in findings)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class TestA6:
|
|
388
|
+
"""A6 — AUDIT_FAILED requires findings == []."""
|
|
389
|
+
|
|
390
|
+
def test_audit_failed_empty_findings_passes(self):
|
|
391
|
+
v = make_valid_verdict_file_audit_failed()
|
|
392
|
+
assert check_a6(v) == []
|
|
393
|
+
|
|
394
|
+
def test_audit_failed_with_findings_fails(self):
|
|
395
|
+
v = make_valid_verdict_file_audit_failed()
|
|
396
|
+
v["findings"] = [{"id": "F-001", "severity": "P3", "dimension": "3.7",
|
|
397
|
+
"file": "x.md", "line": 1, "description": "x", "suggested_fix": "y"}]
|
|
398
|
+
findings = check_a6(v)
|
|
399
|
+
assert any(f.rule_id == "A6" for f in findings)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class TestA7:
|
|
403
|
+
"""A7 — JSONL stream tool-event pairing."""
|
|
404
|
+
|
|
405
|
+
def test_no_tool_events_passes(self):
|
|
406
|
+
events = make_valid_jsonl_events_no_tool()
|
|
407
|
+
assert check_a7(events) == []
|
|
408
|
+
|
|
409
|
+
def test_tool_using_events_pass(self):
|
|
410
|
+
events = make_valid_jsonl_events_tool_using()
|
|
411
|
+
assert check_a7(events) == []
|
|
412
|
+
|
|
413
|
+
def test_orphan_tool_completion_fails(self):
|
|
414
|
+
events = [
|
|
415
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
416
|
+
{"type": "turn.started"},
|
|
417
|
+
{"type": "item.completed",
|
|
418
|
+
"item": {"id": "item_orphan", "type": "command_execution"}},
|
|
419
|
+
{"type": "turn.completed",
|
|
420
|
+
"usage": {"input_tokens": 1, "cached_input_tokens": 0,
|
|
421
|
+
"output_tokens": 1, "reasoning_output_tokens": 0}},
|
|
422
|
+
]
|
|
423
|
+
findings = check_a7(events)
|
|
424
|
+
assert any(f.rule_id == "A7" and "orphan" in f.message for f in findings)
|
|
425
|
+
|
|
426
|
+
def test_unmatched_tool_start_fails(self):
|
|
427
|
+
events = [
|
|
428
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
429
|
+
{"type": "item.started", "item": {"id": "item_unmatched", "type": "command_execution"}},
|
|
430
|
+
{"type": "turn.completed",
|
|
431
|
+
"usage": {"input_tokens": 1, "cached_input_tokens": 0,
|
|
432
|
+
"output_tokens": 1, "reasoning_output_tokens": 0}},
|
|
433
|
+
]
|
|
434
|
+
findings = check_a7(events)
|
|
435
|
+
assert any(f.rule_id == "A7" and "unmatched" in f.message for f in findings)
|
|
436
|
+
|
|
437
|
+
def test_duplicate_started_fails(self):
|
|
438
|
+
events = [
|
|
439
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
440
|
+
{"type": "item.started", "item": {"id": "item_x", "type": "command_execution"}},
|
|
441
|
+
{"type": "item.started", "item": {"id": "item_x", "type": "command_execution"}},
|
|
442
|
+
{"type": "item.completed", "item": {"id": "item_x", "type": "command_execution"}},
|
|
443
|
+
{"type": "turn.completed",
|
|
444
|
+
"usage": {"input_tokens": 1, "cached_input_tokens": 0,
|
|
445
|
+
"output_tokens": 1, "reasoning_output_tokens": 0}},
|
|
446
|
+
]
|
|
447
|
+
findings = check_a7(events)
|
|
448
|
+
assert any(f.rule_id == "A7" and "duplicate item.started" in f.message for f in findings)
|
|
449
|
+
|
|
450
|
+
def test_completed_before_started_fails(self):
|
|
451
|
+
# An item.completed appearing before its item.started would be classified
|
|
452
|
+
# as orphan because we scan top-down. Either error covers the violation.
|
|
453
|
+
events = [
|
|
454
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
455
|
+
{"type": "item.completed", "item": {"id": "item_z", "type": "command_execution"}},
|
|
456
|
+
{"type": "item.started", "item": {"id": "item_z", "type": "command_execution"}},
|
|
457
|
+
{"type": "turn.completed",
|
|
458
|
+
"usage": {"input_tokens": 1, "cached_input_tokens": 0,
|
|
459
|
+
"output_tokens": 1, "reasoning_output_tokens": 0}},
|
|
460
|
+
]
|
|
461
|
+
findings = check_a7(events)
|
|
462
|
+
# We expect at least one A7 finding (orphan or ordering)
|
|
463
|
+
assert any(f.rule_id == "A7" for f in findings)
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
# ---------------------------------------------------------------------------
|
|
467
|
+
# Family B
|
|
468
|
+
# ---------------------------------------------------------------------------
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class TestB1:
|
|
472
|
+
"""B1 — sidecar.stream.jsonl_thread_id matches JSONL.thread.started.thread_id."""
|
|
473
|
+
|
|
474
|
+
def test_matching_thread_id_passes(self):
|
|
475
|
+
s = make_valid_sidecar()
|
|
476
|
+
events = make_valid_jsonl_events_no_tool()
|
|
477
|
+
v = make_valid_verdict_file_minor()
|
|
478
|
+
assert check_b1(s, events, v) == []
|
|
479
|
+
|
|
480
|
+
def test_mismatched_thread_id_fails(self):
|
|
481
|
+
s = make_valid_sidecar()
|
|
482
|
+
s["stream"]["jsonl_thread_id"] = "11111111-1111-1111-1111-111111111111"
|
|
483
|
+
events = make_valid_jsonl_events_no_tool()
|
|
484
|
+
v = make_valid_verdict_file_minor()
|
|
485
|
+
findings = check_b1(s, events, v)
|
|
486
|
+
assert any(f.rule_id == "B1" for f in findings)
|
|
487
|
+
|
|
488
|
+
def test_audit_failed_suspends_b1(self):
|
|
489
|
+
s = make_valid_sidecar()
|
|
490
|
+
s["stream"]["jsonl_thread_id"] = "" # AUDIT_FAILED permits empty
|
|
491
|
+
v = make_valid_verdict_file_audit_failed()
|
|
492
|
+
# JSONL may be malformed/missing on AUDIT_FAILED; but B1 is suspended
|
|
493
|
+
assert check_b1(s, [], v) == []
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
class TestB2:
|
|
497
|
+
"""B2 — entry.deliverable_sha == sidecar.primary_sha == disk SHA."""
|
|
498
|
+
|
|
499
|
+
def test_matching_shas_passes(self, tmp_path: Path):
|
|
500
|
+
e = make_valid_persisted_entry_minor()
|
|
501
|
+
s = make_valid_sidecar()
|
|
502
|
+
# repo_root is tmp_path with no deliverable file — disk verify skipped
|
|
503
|
+
assert check_b2(e, s, tmp_path) == []
|
|
504
|
+
|
|
505
|
+
def test_entry_sidecar_sha_disagree_fails(self):
|
|
506
|
+
e = make_valid_persisted_entry_minor()
|
|
507
|
+
s = make_valid_sidecar()
|
|
508
|
+
s["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
|
|
509
|
+
findings = check_b2(e, s, REPO)
|
|
510
|
+
assert any(f.rule_id == "B2" for f in findings)
|
|
511
|
+
|
|
512
|
+
def test_disk_sha_mismatch_fails(self, tmp_path: Path):
|
|
513
|
+
e = make_valid_persisted_entry_minor()
|
|
514
|
+
e["deliverable_path"] = "deliverable.md"
|
|
515
|
+
s = make_valid_sidecar()
|
|
516
|
+
s["prompt"]["bundle"]["primary_deliverables"] = [
|
|
517
|
+
{"path": "deliverable.md", "sha": "a" * 64}
|
|
518
|
+
]
|
|
519
|
+
# Write a real file but with content whose SHA is NOT all-a's
|
|
520
|
+
(tmp_path / "deliverable.md").write_text("totally different content\n")
|
|
521
|
+
findings = check_b2(e, s, tmp_path)
|
|
522
|
+
assert any(f.rule_id == "B2" and "current_file" in f.message for f in findings)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class TestB3:
|
|
526
|
+
"""B3 — bundle_manifest_sha agrees with recomputed manifest."""
|
|
527
|
+
|
|
528
|
+
def test_matching_manifest_passes(self):
|
|
529
|
+
s = make_valid_sidecar()
|
|
530
|
+
# repo_root REPO doesn't have these files; but declared SHAs will recompute
|
|
531
|
+
# consistently because the helper used the same SHAs.
|
|
532
|
+
assert check_b3(s, REPO / "nonexistent_subtree") == []
|
|
533
|
+
|
|
534
|
+
def test_mismatched_manifest_fails(self):
|
|
535
|
+
s = make_valid_sidecar()
|
|
536
|
+
s["prompt"]["bundle"]["bundle_manifest_sha"] = "0" * 64
|
|
537
|
+
findings = check_b3(s, REPO / "nonexistent_subtree")
|
|
538
|
+
assert any(f.rule_id == "B3" for f in findings)
|
|
539
|
+
|
|
540
|
+
def test_missing_bundle_file_fails(self, tmp_path: Path):
|
|
541
|
+
# Codex round 2 P2: when a bundle file is missing on disk, B3 must
|
|
542
|
+
# NOT fall back to the sidecar-declared SHA — that would let a
|
|
543
|
+
# deleted/moved file silently pass. With a real .git/ marker, B3
|
|
544
|
+
# Step 2 is required to flag the missing file as stale/unverifiable.
|
|
545
|
+
# Synthesize a tmp repo with a real .git marker but missing bundle
|
|
546
|
+
# files; the sidecar's declared paths are intentionally absent.
|
|
547
|
+
(tmp_path / ".git").mkdir() # marker only; B3 doesn't run git commands
|
|
548
|
+
s = make_valid_sidecar() # references deliverable.md / bibliography.json / verification.md / template
|
|
549
|
+
findings = check_b3(s, tmp_path)
|
|
550
|
+
assert any(
|
|
551
|
+
f.rule_id == "B3" and "missing on disk" in f.message
|
|
552
|
+
for f in findings
|
|
553
|
+
), [f.render() for f in findings]
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class TestB4:
|
|
557
|
+
"""B4 — runner.git_sha resolves to a real commit."""
|
|
558
|
+
|
|
559
|
+
def test_real_git_sha_passes(self):
|
|
560
|
+
# Use the actual repo HEAD
|
|
561
|
+
result = subprocess.run(
|
|
562
|
+
["git", "rev-parse", "HEAD"],
|
|
563
|
+
capture_output=True, text=True, cwd=REPO,
|
|
564
|
+
)
|
|
565
|
+
head = result.stdout.strip()
|
|
566
|
+
s = make_valid_sidecar()
|
|
567
|
+
s["runner"]["git_sha"] = head[:12]
|
|
568
|
+
assert check_b4(s, REPO) == []
|
|
569
|
+
|
|
570
|
+
def test_fake_git_sha_fails(self):
|
|
571
|
+
s = make_valid_sidecar()
|
|
572
|
+
s["runner"]["git_sha"] = "deadbeefdeadbeef"
|
|
573
|
+
findings = check_b4(s, REPO)
|
|
574
|
+
assert any(f.rule_id == "B4" for f in findings)
|
|
575
|
+
|
|
576
|
+
def test_outside_git_repo_skipped(self, tmp_path: Path):
|
|
577
|
+
# tmp_path has no .git/, so B4 is skipped (no false positive on synthetic fixtures)
|
|
578
|
+
s = make_valid_sidecar()
|
|
579
|
+
s["runner"]["git_sha"] = "deadbeef"
|
|
580
|
+
assert check_b4(s, tmp_path) == []
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class TestB5:
|
|
584
|
+
"""B5 — ended_at - started_at == duration_seconds (±1s)."""
|
|
585
|
+
|
|
586
|
+
def test_consistent_timing_passes(self):
|
|
587
|
+
s = make_valid_sidecar()
|
|
588
|
+
assert check_b5(s) == []
|
|
589
|
+
|
|
590
|
+
def test_inconsistent_timing_fails(self):
|
|
591
|
+
s = make_valid_sidecar()
|
|
592
|
+
s["timing"]["duration_seconds"] = 999.0
|
|
593
|
+
findings = check_b5(s)
|
|
594
|
+
assert any(f.rule_id == "B5" for f in findings)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class TestB6:
|
|
598
|
+
"""B6 — exit_code == 0 for non-AUDIT_FAILED."""
|
|
599
|
+
|
|
600
|
+
def test_zero_exit_minor_passes(self):
|
|
601
|
+
s = make_valid_sidecar()
|
|
602
|
+
v = make_valid_verdict_file_minor()
|
|
603
|
+
assert check_b6(s, v) == []
|
|
604
|
+
|
|
605
|
+
def test_nonzero_exit_minor_fails(self):
|
|
606
|
+
s = make_valid_sidecar()
|
|
607
|
+
s["process"]["exit_code"] = 70
|
|
608
|
+
v = make_valid_verdict_file_minor()
|
|
609
|
+
findings = check_b6(s, v)
|
|
610
|
+
assert any(f.rule_id == "B6" for f in findings)
|
|
611
|
+
|
|
612
|
+
def test_nonzero_exit_audit_failed_passes(self):
|
|
613
|
+
s = make_valid_sidecar()
|
|
614
|
+
s["process"]["exit_code"] = 70
|
|
615
|
+
v = make_valid_verdict_file_audit_failed()
|
|
616
|
+
assert check_b6(s, v) == []
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class TestB7:
|
|
620
|
+
"""B7 — entry.run_id == sidecar.run_id == bare basename of every co-located file."""
|
|
621
|
+
|
|
622
|
+
def test_matching_run_ids_pass(self, tmp_path: Path):
|
|
623
|
+
e = make_valid_persisted_entry_minor()
|
|
624
|
+
s = make_valid_sidecar()
|
|
625
|
+
sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
|
|
626
|
+
jsonl_path = tmp_path / f"{VALID_RUN_ID}.jsonl"
|
|
627
|
+
verdict_path = tmp_path / f"{VALID_RUN_ID}.verdict.yaml"
|
|
628
|
+
# Don't actually need to write the files for B7
|
|
629
|
+
assert check_b7(e, s, None, sidecar_path, jsonl_path, verdict_path, "persisted") == []
|
|
630
|
+
|
|
631
|
+
def test_entry_sidecar_run_id_mismatch_fails(self, tmp_path: Path):
|
|
632
|
+
e = make_valid_persisted_entry_minor()
|
|
633
|
+
s = make_valid_sidecar()
|
|
634
|
+
s["run_id"] = "2026-04-30T15-22-04Z-aaaa"
|
|
635
|
+
findings = check_b7(e, s, None, tmp_path / f"{VALID_RUN_ID}.meta.json",
|
|
636
|
+
tmp_path / f"{VALID_RUN_ID}.jsonl",
|
|
637
|
+
tmp_path / f"{VALID_RUN_ID}.verdict.yaml", "persisted")
|
|
638
|
+
assert any(f.rule_id == "B7" and "entry.run_id" in f.message for f in findings)
|
|
639
|
+
|
|
640
|
+
def test_basename_mismatch_fails(self, tmp_path: Path):
|
|
641
|
+
e = make_valid_persisted_entry_minor()
|
|
642
|
+
s = make_valid_sidecar()
|
|
643
|
+
# sidecar path basename doesn't match run_id
|
|
644
|
+
wrong_path = tmp_path / "totally-wrong-stem.meta.json"
|
|
645
|
+
findings = check_b7(e, s, None, wrong_path, None, None, "persisted")
|
|
646
|
+
assert any(f.rule_id == "B7" and "basename" in f.message for f in findings)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
class TestB8:
|
|
650
|
+
"""B8 — verified_at == acknowledged_at on ack entries."""
|
|
651
|
+
|
|
652
|
+
def test_matching_timestamps_pass(self):
|
|
653
|
+
e = make_valid_persisted_entry_minor()
|
|
654
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
655
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
656
|
+
e["acknowledgement"] = {
|
|
657
|
+
"finding_ids": ["F-001"],
|
|
658
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
659
|
+
"acknowledged_by": "user",
|
|
660
|
+
}
|
|
661
|
+
assert check_b8(e, "persisted") == []
|
|
662
|
+
|
|
663
|
+
def test_mismatched_timestamps_fail(self):
|
|
664
|
+
e = make_valid_persisted_entry_minor()
|
|
665
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
666
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
667
|
+
e["acknowledgement"] = {
|
|
668
|
+
"finding_ids": ["F-001"],
|
|
669
|
+
"acknowledged_at": "2026-04-30T16:00:00.000Z",
|
|
670
|
+
"acknowledged_by": "user",
|
|
671
|
+
}
|
|
672
|
+
findings = check_b8(e, "persisted")
|
|
673
|
+
assert any(f.rule_id == "B8" for f in findings)
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
class TestB9:
|
|
677
|
+
"""B9 — entry.bundle_id == sidecar.prompt.bundle.bundle_id (when present)."""
|
|
678
|
+
|
|
679
|
+
def test_matching_bundle_id_passes(self):
|
|
680
|
+
e = make_valid_persisted_entry_minor()
|
|
681
|
+
s = make_valid_sidecar()
|
|
682
|
+
assert check_b9(e, s) == []
|
|
683
|
+
|
|
684
|
+
def test_no_bundle_id_skipped(self):
|
|
685
|
+
e = make_valid_persisted_entry_minor()
|
|
686
|
+
del e["bundle_id"]
|
|
687
|
+
s = make_valid_sidecar()
|
|
688
|
+
assert check_b9(e, s) == []
|
|
689
|
+
|
|
690
|
+
def test_mismatched_bundle_id_fails(self):
|
|
691
|
+
e = make_valid_persisted_entry_minor()
|
|
692
|
+
s = make_valid_sidecar()
|
|
693
|
+
s["prompt"]["bundle"]["bundle_id"] = "different-bundle"
|
|
694
|
+
findings = check_b9(e, s)
|
|
695
|
+
assert any(f.rule_id == "B9" for f in findings)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
class TestB10:
|
|
699
|
+
"""B10 — ack finding_ids: non-empty + all exist + full coverage."""
|
|
700
|
+
|
|
701
|
+
def test_full_coverage_passes(self):
|
|
702
|
+
e = make_valid_persisted_entry_minor()
|
|
703
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
704
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
705
|
+
e["acknowledgement"] = {
|
|
706
|
+
"finding_ids": ["F-001"],
|
|
707
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
708
|
+
"acknowledged_by": "user",
|
|
709
|
+
}
|
|
710
|
+
v = make_valid_verdict_file_minor()
|
|
711
|
+
v["verdict_status"] = "MATERIAL"
|
|
712
|
+
v["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
713
|
+
v["findings"] = [{"id": "F-001", "severity": "P1", "dimension": "3.7",
|
|
714
|
+
"file": "x.md", "line": 1, "description": "x",
|
|
715
|
+
"suggested_fix": "y"}]
|
|
716
|
+
assert check_b10(e, v, "persisted") == []
|
|
717
|
+
|
|
718
|
+
def test_unknown_finding_id_fails(self):
|
|
719
|
+
e = make_valid_persisted_entry_minor()
|
|
720
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
721
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
722
|
+
e["acknowledgement"] = {
|
|
723
|
+
"finding_ids": ["F-999"],
|
|
724
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
725
|
+
"acknowledged_by": "user",
|
|
726
|
+
}
|
|
727
|
+
v = make_valid_verdict_file_minor()
|
|
728
|
+
v["verdict_status"] = "MATERIAL"
|
|
729
|
+
v["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
730
|
+
v["findings"] = [{"id": "F-001", "severity": "P1", "dimension": "3.7",
|
|
731
|
+
"file": "x.md", "line": 1, "description": "x",
|
|
732
|
+
"suggested_fix": "y"}]
|
|
733
|
+
findings = check_b10(e, v, "persisted")
|
|
734
|
+
assert any(f.rule_id == "B10" and "unknown" in f.message for f in findings)
|
|
735
|
+
|
|
736
|
+
def test_partial_coverage_fails(self):
|
|
737
|
+
e = make_valid_persisted_entry_minor()
|
|
738
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
739
|
+
e["verdict"]["finding_counts"] = {"p1": 2, "p2": 0, "p3": 0}
|
|
740
|
+
e["acknowledgement"] = {
|
|
741
|
+
"finding_ids": ["F-001"],
|
|
742
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
743
|
+
"acknowledged_by": "user",
|
|
744
|
+
}
|
|
745
|
+
v = make_valid_verdict_file_minor()
|
|
746
|
+
v["verdict_status"] = "MATERIAL"
|
|
747
|
+
v["finding_counts"] = {"p1": 2, "p2": 0, "p3": 0}
|
|
748
|
+
v["findings"] = [
|
|
749
|
+
{"id": "F-001", "severity": "P1", "dimension": "3.7",
|
|
750
|
+
"file": "x.md", "line": 1, "description": "x", "suggested_fix": "y"},
|
|
751
|
+
{"id": "F-002", "severity": "P1", "dimension": "3.7",
|
|
752
|
+
"file": "x.md", "line": 2, "description": "y", "suggested_fix": "z"},
|
|
753
|
+
]
|
|
754
|
+
findings = check_b10(e, v, "persisted")
|
|
755
|
+
assert any(f.rule_id == "B10" and "coverage" in f.message for f in findings)
|
|
756
|
+
|
|
757
|
+
def test_empty_finding_ids_fails(self):
|
|
758
|
+
e = make_valid_persisted_entry_minor()
|
|
759
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
760
|
+
e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
761
|
+
e["acknowledgement"] = {
|
|
762
|
+
"finding_ids": [],
|
|
763
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
764
|
+
"acknowledged_by": "user",
|
|
765
|
+
}
|
|
766
|
+
v = make_valid_verdict_file_minor()
|
|
767
|
+
findings = check_b10(e, v, "persisted")
|
|
768
|
+
assert any(f.rule_id == "B10" and "non-empty" in f.message for f in findings)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
# ---------------------------------------------------------------------------
|
|
772
|
+
# Family C
|
|
773
|
+
# ---------------------------------------------------------------------------
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
class TestC1:
|
|
777
|
+
"""C1 — entry.verdict mirrors verdict file."""
|
|
778
|
+
|
|
779
|
+
def test_matching_mirror_passes(self):
|
|
780
|
+
e = make_valid_persisted_entry_minor()
|
|
781
|
+
v = make_valid_verdict_file_minor()
|
|
782
|
+
assert check_c1(e, v) == []
|
|
783
|
+
|
|
784
|
+
def test_status_drift_fails(self):
|
|
785
|
+
e = make_valid_persisted_entry_minor()
|
|
786
|
+
v = make_valid_verdict_file_minor()
|
|
787
|
+
v["verdict_status"] = "PASS"
|
|
788
|
+
findings = check_c1(e, v)
|
|
789
|
+
assert any(f.rule_id == "C1" and "status" in f.message for f in findings)
|
|
790
|
+
|
|
791
|
+
def test_finding_counts_drift_fails(self):
|
|
792
|
+
e = make_valid_persisted_entry_minor()
|
|
793
|
+
v = make_valid_verdict_file_minor()
|
|
794
|
+
v["finding_counts"] = {"p1": 0, "p2": 0, "p3": 5}
|
|
795
|
+
findings = check_c1(e, v)
|
|
796
|
+
assert any(f.rule_id == "C1" and "finding_counts" in f.message for f in findings)
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
class TestC2:
|
|
800
|
+
"""C2 — entry.bundle_manifest_sha == sidecar.bundle_manifest_sha."""
|
|
801
|
+
|
|
802
|
+
def test_matching_sha_passes(self):
|
|
803
|
+
e = make_valid_persisted_entry_minor()
|
|
804
|
+
s = make_valid_sidecar()
|
|
805
|
+
e["bundle_manifest_sha"] = s["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
806
|
+
assert check_c2(e, s) == []
|
|
807
|
+
|
|
808
|
+
def test_drift_fails(self):
|
|
809
|
+
e = make_valid_persisted_entry_minor()
|
|
810
|
+
s = make_valid_sidecar()
|
|
811
|
+
e["bundle_manifest_sha"] = "0" * 64
|
|
812
|
+
findings = check_c2(e, s)
|
|
813
|
+
assert any(f.rule_id == "C2" for f in findings)
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
class TestC3:
|
|
817
|
+
"""C3 — ack append copies prior entry byte-for-byte except verified_at/by."""
|
|
818
|
+
|
|
819
|
+
def test_byte_for_byte_copy_passes(self):
|
|
820
|
+
prior = make_valid_persisted_entry_minor()
|
|
821
|
+
prior["verdict"]["status"] = "MATERIAL"
|
|
822
|
+
prior["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
823
|
+
new = copy.deepcopy(prior)
|
|
824
|
+
new["verdict"]["verified_at"] = "2026-04-30T15:24:00.000Z" # bumped
|
|
825
|
+
new["acknowledgement"] = {
|
|
826
|
+
"finding_ids": ["F-001"],
|
|
827
|
+
"acknowledged_at": new["verdict"]["verified_at"],
|
|
828
|
+
"acknowledged_by": "user",
|
|
829
|
+
}
|
|
830
|
+
assert check_c3(new, prior) == []
|
|
831
|
+
|
|
832
|
+
def test_drift_in_copied_field_fails(self):
|
|
833
|
+
prior = make_valid_persisted_entry_minor()
|
|
834
|
+
new = copy.deepcopy(prior)
|
|
835
|
+
new["deliverable_sha"] = "f" * 64 # diverged
|
|
836
|
+
new["verdict"]["verified_at"] = "2026-04-30T15:24:00.000Z"
|
|
837
|
+
findings = check_c3(new, prior)
|
|
838
|
+
assert any(f.rule_id == "C3" and "deliverable_sha" in f.message for f in findings)
|
|
839
|
+
|
|
840
|
+
def test_same_verified_at_fails(self):
|
|
841
|
+
prior = make_valid_persisted_entry_minor()
|
|
842
|
+
new = copy.deepcopy(prior) # same verified_at
|
|
843
|
+
findings = check_c3(new, prior)
|
|
844
|
+
assert any(f.rule_id == "C3" and "freshly set" in f.message for f in findings)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
class TestC4:
|
|
848
|
+
"""C4 — entry.deliverable_sha == sidecar.primary[].sha (entry-side half of B2)."""
|
|
849
|
+
|
|
850
|
+
def test_matching_passes(self):
|
|
851
|
+
e = make_valid_persisted_entry_minor()
|
|
852
|
+
s = make_valid_sidecar()
|
|
853
|
+
assert check_c4(e, s) == []
|
|
854
|
+
|
|
855
|
+
def test_drift_fails(self):
|
|
856
|
+
e = make_valid_persisted_entry_minor()
|
|
857
|
+
s = make_valid_sidecar()
|
|
858
|
+
s["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
|
|
859
|
+
findings = check_c4(e, s)
|
|
860
|
+
assert any(f.rule_id == "C4" for f in findings)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
# ---------------------------------------------------------------------------
|
|
864
|
+
# Family D
|
|
865
|
+
# ---------------------------------------------------------------------------
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
class TestD1:
|
|
869
|
+
"""D1 — duplicate verified_at within (stage,agent,sha,run_id) group."""
|
|
870
|
+
|
|
871
|
+
def test_unique_verified_at_passes(self):
|
|
872
|
+
e1 = make_valid_persisted_entry_minor()
|
|
873
|
+
e2 = make_valid_persisted_entry_minor()
|
|
874
|
+
e2["verdict"]["verified_at"] = "2026-04-30T15:23:12.847Z"
|
|
875
|
+
e2["run_id"] = "2026-04-30T15-22-04Z-d8f4" # different run
|
|
876
|
+
assert check_d1([e1, e2]) == []
|
|
877
|
+
|
|
878
|
+
def test_duplicate_verified_at_fails(self):
|
|
879
|
+
e1 = make_valid_persisted_entry_minor()
|
|
880
|
+
e2 = copy.deepcopy(e1)
|
|
881
|
+
# same key + same verified_at → duplicate
|
|
882
|
+
findings = check_d1([e1, e2])
|
|
883
|
+
assert any(f.rule_id == "D1" for f in findings)
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
class TestD2:
|
|
887
|
+
"""D2 — proposals sharing started_at."""
|
|
888
|
+
|
|
889
|
+
def test_distinct_started_at_passes(self):
|
|
890
|
+
proposals = [
|
|
891
|
+
{"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
|
|
892
|
+
"entry": {"run_id": "2026-04-30T15-22-04Z-aaaa"}},
|
|
893
|
+
{"sidecar": {"timing": {"started_at": "2026-04-30T15:22:05.123Z"}},
|
|
894
|
+
"entry": {"run_id": "2026-04-30T15-22-05Z-bbbb"}},
|
|
895
|
+
]
|
|
896
|
+
assert check_d2(proposals) == []
|
|
897
|
+
|
|
898
|
+
def test_shared_started_at_warns(self):
|
|
899
|
+
proposals = [
|
|
900
|
+
{"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
|
|
901
|
+
"entry": {"run_id": "2026-04-30T15-22-04Z-aaaa"}},
|
|
902
|
+
{"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
|
|
903
|
+
"entry": {"run_id": "2026-04-30T15-22-04Z-bbbb"}},
|
|
904
|
+
]
|
|
905
|
+
findings = check_d2(proposals)
|
|
906
|
+
assert any(f.rule_id == "D2" for f in findings)
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
class TestD3:
|
|
910
|
+
"""D3 — verified_at strictly monotonic."""
|
|
911
|
+
|
|
912
|
+
def test_monotonic_ledger_passes(self):
|
|
913
|
+
e1 = make_valid_persisted_entry_minor()
|
|
914
|
+
e2 = copy.deepcopy(e1)
|
|
915
|
+
e2["verdict"]["verified_at"] = "2026-04-30T15:23:12.847Z"
|
|
916
|
+
e2["run_id"] = "2026-04-30T15-22-04Z-d8f4"
|
|
917
|
+
assert check_d3([e1, e2]) == []
|
|
918
|
+
|
|
919
|
+
def test_non_monotonic_fails(self):
|
|
920
|
+
e1 = make_valid_persisted_entry_minor()
|
|
921
|
+
e2 = copy.deepcopy(e1)
|
|
922
|
+
e2["verdict"]["verified_at"] = "2026-04-30T15:00:00.000Z" # earlier than e1
|
|
923
|
+
e2["run_id"] = "2026-04-30T15-22-04Z-d8f4"
|
|
924
|
+
findings = check_d3([e1, e2])
|
|
925
|
+
assert any(f.rule_id == "D3" for f in findings)
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
class TestD4:
|
|
929
|
+
"""D4 — surface the supersession requirement when proposal round is
|
|
930
|
+
higher than persisted round (spec §3.7 family D + §5.6 A1.5)."""
|
|
931
|
+
|
|
932
|
+
def test_higher_round_fires_supersession(self):
|
|
933
|
+
# The lint should SURFACE the supersession (proposal preempts
|
|
934
|
+
# persisted Path A) so the caller sees it before the orchestrator
|
|
935
|
+
# processes the artifacts.
|
|
936
|
+
findings = check_d4(persisted_round=2, proposal_round=3)
|
|
937
|
+
assert any(f.rule_id == "D4" for f in findings)
|
|
938
|
+
assert any("supersedes" in f.message for f in findings)
|
|
939
|
+
|
|
940
|
+
def test_equal_round_no_supersession(self):
|
|
941
|
+
# Same round — no supersession required; D4 is silent.
|
|
942
|
+
assert check_d4(persisted_round=3, proposal_round=3) == []
|
|
943
|
+
|
|
944
|
+
def test_lower_round_no_supersession(self):
|
|
945
|
+
# Lower-round proposal — D4 silent (orchestrator should reject the
|
|
946
|
+
# proposal at B1a as a stale leftover, but that's not D4's surface).
|
|
947
|
+
assert check_d4(persisted_round=3, proposal_round=1) == []
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
# ---------------------------------------------------------------------------
|
|
951
|
+
# Family E
|
|
952
|
+
# ---------------------------------------------------------------------------
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
class TestE1E2E6:
|
|
956
|
+
"""E1/E2/E6 — passport-shape post-hoc detection."""
|
|
957
|
+
|
|
958
|
+
def test_orchestrator_emitted_passes(self):
|
|
959
|
+
e = make_valid_persisted_entry_minor()
|
|
960
|
+
assert check_e1_e2_e6([e]) == []
|
|
961
|
+
|
|
962
|
+
def test_missing_verified_by_fails(self):
|
|
963
|
+
e = make_valid_persisted_entry_minor()
|
|
964
|
+
del e["verdict"]["verified_by"]
|
|
965
|
+
findings = check_e1_e2_e6([e])
|
|
966
|
+
assert any(f.rule_id == "E1/E2/E6" for f in findings)
|
|
967
|
+
|
|
968
|
+
def test_wrong_verified_by_fails(self):
|
|
969
|
+
e = make_valid_persisted_entry_minor()
|
|
970
|
+
e["verdict"]["verified_by"] = "totally_different_agent"
|
|
971
|
+
findings = check_e1_e2_e6([e])
|
|
972
|
+
assert any(f.rule_id == "E1/E2/E6" and "verified_by" in f.message for f in findings)
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
class TestE3E4:
|
|
976
|
+
"""E3/E4 — proposals carrying verified_at/verified_by are rejected."""
|
|
977
|
+
|
|
978
|
+
def test_clean_proposal_passes(self):
|
|
979
|
+
e = make_valid_proposal_entry_pass()
|
|
980
|
+
assert check_e3_e4(e, "proposal") == []
|
|
981
|
+
|
|
982
|
+
def test_proposal_with_verified_at_fails(self):
|
|
983
|
+
e = make_valid_proposal_entry_pass()
|
|
984
|
+
e["verdict"]["verified_at"] = "2026-04-30T15:23:11.847Z"
|
|
985
|
+
findings = check_e3_e4(e, "proposal")
|
|
986
|
+
assert any(f.rule_id == "E3/E4" and "verified_at" in f.message for f in findings)
|
|
987
|
+
|
|
988
|
+
def test_proposal_with_verified_by_fails(self):
|
|
989
|
+
e = make_valid_proposal_entry_pass()
|
|
990
|
+
e["verdict"]["verified_by"] = "pipeline_orchestrator_agent"
|
|
991
|
+
findings = check_e3_e4(e, "proposal")
|
|
992
|
+
assert any(f.rule_id == "E3/E4" and "verified_by" in f.message for f in findings)
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
class TestE5:
|
|
996
|
+
"""E5 — persisted mode rejects AUDIT_FAILED."""
|
|
997
|
+
|
|
998
|
+
def test_minor_persisted_passes(self):
|
|
999
|
+
e = make_valid_persisted_entry_minor()
|
|
1000
|
+
assert check_e5(e, "persisted") == []
|
|
1001
|
+
|
|
1002
|
+
def test_audit_failed_persisted_fails(self):
|
|
1003
|
+
e = make_valid_persisted_entry_minor()
|
|
1004
|
+
e["verdict"]["status"] = "AUDIT_FAILED"
|
|
1005
|
+
findings = check_e5(e, "persisted")
|
|
1006
|
+
assert any(f.rule_id == "E5" for f in findings)
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
class TestE7:
|
|
1010
|
+
"""E7 — ack entry's status remains MATERIAL."""
|
|
1011
|
+
|
|
1012
|
+
def test_material_with_ack_passes(self):
|
|
1013
|
+
e = make_valid_persisted_entry_minor()
|
|
1014
|
+
e["verdict"]["status"] = "MATERIAL"
|
|
1015
|
+
e["acknowledgement"] = {
|
|
1016
|
+
"finding_ids": ["F-001"],
|
|
1017
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
1018
|
+
"acknowledged_by": "user",
|
|
1019
|
+
}
|
|
1020
|
+
assert check_e7(e) == []
|
|
1021
|
+
|
|
1022
|
+
def test_synthetic_status_with_ack_fails(self):
|
|
1023
|
+
e = make_valid_persisted_entry_minor()
|
|
1024
|
+
e["verdict"]["status"] = "MATERIAL_ACKNOWLEDGED"
|
|
1025
|
+
e["acknowledgement"] = {
|
|
1026
|
+
"finding_ids": ["F-001"],
|
|
1027
|
+
"acknowledged_at": e["verdict"]["verified_at"],
|
|
1028
|
+
"acknowledged_by": "user",
|
|
1029
|
+
}
|
|
1030
|
+
findings = check_e7(e)
|
|
1031
|
+
assert any(f.rule_id == "E7" for f in findings)
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
# ---------------------------------------------------------------------------
|
|
1035
|
+
# Family F
|
|
1036
|
+
# ---------------------------------------------------------------------------
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
class TestF1:
|
|
1040
|
+
"""F1 — run_id format."""
|
|
1041
|
+
|
|
1042
|
+
def test_canonical_run_id_passes(self):
|
|
1043
|
+
assert check_f1(VALID_RUN_ID) == []
|
|
1044
|
+
|
|
1045
|
+
def test_wrong_format_fails(self):
|
|
1046
|
+
findings = check_f1("not-a-run-id")
|
|
1047
|
+
assert any(f.rule_id == "F1" for f in findings)
|
|
1048
|
+
|
|
1049
|
+
def test_missing_run_id_fails(self):
|
|
1050
|
+
findings = check_f1(None)
|
|
1051
|
+
assert any(f.rule_id == "F1" for f in findings)
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
class TestF2:
|
|
1055
|
+
"""F2 — bare-run_id basename + extensions."""
|
|
1056
|
+
|
|
1057
|
+
def test_canonical_filenames_pass(self, tmp_path: Path):
|
|
1058
|
+
jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
|
|
1059
|
+
sidecar = tmp_path / f"{VALID_RUN_ID}.meta.json"
|
|
1060
|
+
verdict = tmp_path / f"{VALID_RUN_ID}.verdict.yaml"
|
|
1061
|
+
entry = tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json"
|
|
1062
|
+
assert check_f2(jsonl, sidecar, verdict, entry, "proposal") == []
|
|
1063
|
+
|
|
1064
|
+
def test_stage_prefix_fails(self, tmp_path: Path):
|
|
1065
|
+
sidecar = tmp_path / f"stage2-synthesis-{VALID_RUN_ID}.meta.json"
|
|
1066
|
+
findings = check_f2(None, sidecar, None, None, "persisted")
|
|
1067
|
+
assert any(f.rule_id == "F2" for f in findings)
|
|
1068
|
+
|
|
1069
|
+
def test_wrong_extension_fails(self, tmp_path: Path):
|
|
1070
|
+
sidecar = tmp_path / f"{VALID_RUN_ID}.json" # missing .meta
|
|
1071
|
+
findings = check_f2(None, sidecar, None, None, "persisted")
|
|
1072
|
+
assert any(f.rule_id == "F2" for f in findings)
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
class TestF3:
|
|
1076
|
+
"""F3 — sidecar.run_id == basename stem."""
|
|
1077
|
+
|
|
1078
|
+
def test_matching_passes(self, tmp_path: Path):
|
|
1079
|
+
s = make_valid_sidecar()
|
|
1080
|
+
sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
|
|
1081
|
+
assert check_f3(s, sidecar_path) == []
|
|
1082
|
+
|
|
1083
|
+
def test_drift_fails(self, tmp_path: Path):
|
|
1084
|
+
s = make_valid_sidecar()
|
|
1085
|
+
s["run_id"] = "2026-04-30T15-22-04Z-aaaa"
|
|
1086
|
+
sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
|
|
1087
|
+
findings = check_f3(s, sidecar_path)
|
|
1088
|
+
assert any(f.rule_id == "F3" for f in findings)
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
# ---------------------------------------------------------------------------
|
|
1092
|
+
# Schema self-validation (verification gate item #1)
|
|
1093
|
+
# ---------------------------------------------------------------------------
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
class TestSchemaSelfValidation:
|
|
1097
|
+
"""Verification gate: each Phase 6.2 schema validates as Draft 2020-12."""
|
|
1098
|
+
|
|
1099
|
+
@pytest.mark.parametrize("schema_name", [
|
|
1100
|
+
"audit_artifact_entry.schema.json",
|
|
1101
|
+
"audit_jsonl.schema.json",
|
|
1102
|
+
"audit_sidecar.schema.json",
|
|
1103
|
+
"audit_verdict.schema.json",
|
|
1104
|
+
])
|
|
1105
|
+
def test_schema_meta_valid(self, schema_name):
|
|
1106
|
+
from tests.test_helpers import load_json_schema
|
|
1107
|
+
if "audit_jsonl" in schema_name or "audit_sidecar" in schema_name or "audit_verdict" in schema_name:
|
|
1108
|
+
path = REPO / "shared/contracts/audit" / schema_name
|
|
1109
|
+
else:
|
|
1110
|
+
path = REPO / "shared/contracts/passport" / schema_name
|
|
1111
|
+
schema = load_json_schema(path)
|
|
1112
|
+
assert "$schema" in schema
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
# ---------------------------------------------------------------------------
|
|
1116
|
+
# Aggregator + CLI smoke tests
|
|
1117
|
+
# ---------------------------------------------------------------------------
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
class TestAggregator:
|
|
1121
|
+
"""run_checks should emit zero findings on a fully-valid synthetic bundle."""
|
|
1122
|
+
|
|
1123
|
+
def test_clean_persisted_bundle_zero_findings(self, tmp_path: Path):
|
|
1124
|
+
sidecar = make_valid_sidecar()
|
|
1125
|
+
entry = make_valid_persisted_entry_minor()
|
|
1126
|
+
# align entry's bundle_manifest_sha with the sidecar's computed value
|
|
1127
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1128
|
+
# Codex round 8 P2 closure: B7 now resolves entry.artifact_paths
|
|
1129
|
+
# against repo_root and verifies disk existence + samefile match
|
|
1130
|
+
# against CLI-loaded paths. Aggregator test writes placeholder
|
|
1131
|
+
# files at the recorded basename so the resolution succeeds.
|
|
1132
|
+
for ext in (".jsonl", ".meta.json", ".verdict.yaml",
|
|
1133
|
+
".audit_artifact_entry.json"):
|
|
1134
|
+
(tmp_path / f"{VALID_RUN_ID}{ext}").write_text("placeholder")
|
|
1135
|
+
ctx = LintContext(
|
|
1136
|
+
mode="persisted",
|
|
1137
|
+
entry=entry,
|
|
1138
|
+
entry_path=tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json",
|
|
1139
|
+
sidecar=sidecar,
|
|
1140
|
+
sidecar_path=tmp_path / f"{VALID_RUN_ID}.meta.json",
|
|
1141
|
+
verdict=make_valid_verdict_file_minor(),
|
|
1142
|
+
verdict_path=tmp_path / f"{VALID_RUN_ID}.verdict.yaml",
|
|
1143
|
+
jsonl_events=make_valid_jsonl_events_no_tool(),
|
|
1144
|
+
jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
|
|
1145
|
+
repo_root=tmp_path, # no .git, no deliverable file: B4 skipped, B2 disk skipped
|
|
1146
|
+
)
|
|
1147
|
+
findings = run_checks(ctx)
|
|
1148
|
+
assert findings == [], "\n".join(f.render() for f in findings)
|
|
1149
|
+
|
|
1150
|
+
def test_clean_proposal_bundle_zero_findings(self, tmp_path: Path):
|
|
1151
|
+
sidecar = make_valid_sidecar()
|
|
1152
|
+
entry = make_valid_proposal_entry_pass()
|
|
1153
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1154
|
+
for ext in (".jsonl", ".meta.json", ".verdict.yaml",
|
|
1155
|
+
".audit_artifact_entry.json"):
|
|
1156
|
+
(tmp_path / f"{VALID_RUN_ID}{ext}").write_text("placeholder")
|
|
1157
|
+
ctx = LintContext(
|
|
1158
|
+
mode="proposal",
|
|
1159
|
+
entry=entry,
|
|
1160
|
+
entry_path=tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json",
|
|
1161
|
+
sidecar=sidecar,
|
|
1162
|
+
sidecar_path=tmp_path / f"{VALID_RUN_ID}.meta.json",
|
|
1163
|
+
verdict=make_valid_verdict_file_pass(),
|
|
1164
|
+
verdict_path=tmp_path / f"{VALID_RUN_ID}.verdict.yaml",
|
|
1165
|
+
jsonl_events=make_valid_jsonl_events_no_tool(),
|
|
1166
|
+
jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
|
|
1167
|
+
repo_root=tmp_path,
|
|
1168
|
+
)
|
|
1169
|
+
findings = run_checks(ctx)
|
|
1170
|
+
assert findings == [], "\n".join(f.render() for f in findings)
|
|
1171
|
+
|
|
1172
|
+
def test_jsonl_stream_mode_clean(self, tmp_path: Path):
|
|
1173
|
+
ctx = LintContext(
|
|
1174
|
+
mode="jsonl-stream",
|
|
1175
|
+
jsonl_events=make_valid_jsonl_events_tool_using(),
|
|
1176
|
+
jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
|
|
1177
|
+
repo_root=tmp_path,
|
|
1178
|
+
)
|
|
1179
|
+
assert run_checks(ctx) == []
|
|
1180
|
+
|
|
1181
|
+
def test_jsonl_stream_mode_orphan_fails(self, tmp_path: Path):
|
|
1182
|
+
events = [
|
|
1183
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1184
|
+
{"type": "item.completed",
|
|
1185
|
+
"item": {"id": "orphan", "type": "command_execution"}},
|
|
1186
|
+
]
|
|
1187
|
+
ctx = LintContext(
|
|
1188
|
+
mode="jsonl-stream",
|
|
1189
|
+
jsonl_events=events,
|
|
1190
|
+
jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
|
|
1191
|
+
repo_root=tmp_path,
|
|
1192
|
+
)
|
|
1193
|
+
findings = run_checks(ctx)
|
|
1194
|
+
assert any(f.rule_id == "A7" for f in findings)
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
class TestCLI:
|
|
1198
|
+
"""End-to-end CLI smoke tests using subprocess-style invocation through main()."""
|
|
1199
|
+
|
|
1200
|
+
def test_jsonl_stream_clean_returns_0(self, tmp_path: Path, capsys):
|
|
1201
|
+
jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
|
|
1202
|
+
events = make_valid_jsonl_events_tool_using()
|
|
1203
|
+
jsonl.write_text("\n".join(json.dumps(e) for e in events) + "\n")
|
|
1204
|
+
rc = main(["--mode", "jsonl-stream", "--jsonl", str(jsonl)])
|
|
1205
|
+
assert rc == 0
|
|
1206
|
+
|
|
1207
|
+
def test_jsonl_stream_orphan_returns_1(self, tmp_path: Path):
|
|
1208
|
+
jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
|
|
1209
|
+
events = [
|
|
1210
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1211
|
+
{"type": "item.completed",
|
|
1212
|
+
"item": {"id": "orphan", "type": "command_execution"}},
|
|
1213
|
+
]
|
|
1214
|
+
jsonl.write_text("\n".join(json.dumps(e) for e in events) + "\n")
|
|
1215
|
+
rc = main(["--mode", "jsonl-stream", "--jsonl", str(jsonl)])
|
|
1216
|
+
assert rc == 1
|
|
1217
|
+
|
|
1218
|
+
def test_persisted_clean_via_files(self, tmp_path: Path):
|
|
1219
|
+
# Write all four artifact files
|
|
1220
|
+
run_id = VALID_RUN_ID
|
|
1221
|
+
sidecar = make_valid_sidecar()
|
|
1222
|
+
entry = make_valid_persisted_entry_minor()
|
|
1223
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1224
|
+
verdict = make_valid_verdict_file_minor()
|
|
1225
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1226
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1227
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1228
|
+
# verdict as YAML
|
|
1229
|
+
import yaml as _yaml
|
|
1230
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1231
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1232
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1233
|
+
rc = main([
|
|
1234
|
+
"--mode", "persisted",
|
|
1235
|
+
"--output-dir", str(tmp_path),
|
|
1236
|
+
"--run-id", run_id,
|
|
1237
|
+
"--repo-root", str(tmp_path),
|
|
1238
|
+
])
|
|
1239
|
+
# zero findings -> rc == 0
|
|
1240
|
+
assert rc == 0
|
|
1241
|
+
|
|
1242
|
+
def test_persisted_missing_sidecar_returns_1(self, tmp_path: Path, capsys):
|
|
1243
|
+
# Codex round 1 P1: missing companion artifacts must be rejected, not
|
|
1244
|
+
# silently ignored. Write entry + verdict + jsonl but NOT sidecar.
|
|
1245
|
+
run_id = VALID_RUN_ID
|
|
1246
|
+
entry = make_valid_persisted_entry_minor()
|
|
1247
|
+
verdict = make_valid_verdict_file_minor()
|
|
1248
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1249
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1250
|
+
import yaml as _yaml
|
|
1251
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1252
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1253
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1254
|
+
rc = main([
|
|
1255
|
+
"--mode", "persisted",
|
|
1256
|
+
"--output-dir", str(tmp_path),
|
|
1257
|
+
"--run-id", run_id,
|
|
1258
|
+
"--repo-root", str(tmp_path),
|
|
1259
|
+
])
|
|
1260
|
+
captured = capsys.readouterr()
|
|
1261
|
+
assert rc == 1, captured.out
|
|
1262
|
+
assert "B7" in captured.out
|
|
1263
|
+
assert "sidecar" in captured.out
|
|
1264
|
+
|
|
1265
|
+
def test_persisted_mode_rejects_proposal_shape(self, tmp_path: Path, capsys):
|
|
1266
|
+
# Codex round 2 P1: --mode persisted must enforce persisted arm. A
|
|
1267
|
+
# proposal-shaped entry (no verified_at/verified_by) was silently
|
|
1268
|
+
# passing oneOf validation as the proposal arm.
|
|
1269
|
+
run_id = VALID_RUN_ID
|
|
1270
|
+
sidecar = make_valid_sidecar()
|
|
1271
|
+
# Build a proposal-shaped entry (no verified_at, no verified_by)
|
|
1272
|
+
entry = make_valid_persisted_entry_minor()
|
|
1273
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1274
|
+
entry["verdict"].pop("verified_at", None)
|
|
1275
|
+
entry["verdict"].pop("verified_by", None)
|
|
1276
|
+
verdict = make_valid_verdict_file_minor()
|
|
1277
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1278
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1279
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1280
|
+
import yaml as _yaml
|
|
1281
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1282
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1283
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1284
|
+
rc = main([
|
|
1285
|
+
"--mode", "persisted",
|
|
1286
|
+
"--output-dir", str(tmp_path),
|
|
1287
|
+
"--run-id", run_id,
|
|
1288
|
+
"--repo-root", str(tmp_path),
|
|
1289
|
+
])
|
|
1290
|
+
captured = capsys.readouterr()
|
|
1291
|
+
assert rc == 1, captured.out
|
|
1292
|
+
assert "E3" in captured.out
|
|
1293
|
+
assert "verified_at" in captured.out
|
|
1294
|
+
|
|
1295
|
+
def test_persisted_mode_rejects_audit_failed(self, tmp_path: Path, capsys):
|
|
1296
|
+
# AUDIT_FAILED is proposal-only per §3.2 lifecycle-conditional table.
|
|
1297
|
+
run_id = VALID_RUN_ID
|
|
1298
|
+
sidecar = make_valid_sidecar()
|
|
1299
|
+
entry = make_valid_persisted_entry_minor()
|
|
1300
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1301
|
+
entry["verdict"]["status"] = "AUDIT_FAILED"
|
|
1302
|
+
entry["verdict"]["failure_reason"] = "synthetic test"
|
|
1303
|
+
entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1304
|
+
verdict = make_valid_verdict_file_minor()
|
|
1305
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1306
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1307
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1308
|
+
import yaml as _yaml
|
|
1309
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1310
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1311
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1312
|
+
rc = main([
|
|
1313
|
+
"--mode", "persisted",
|
|
1314
|
+
"--output-dir", str(tmp_path),
|
|
1315
|
+
"--run-id", run_id,
|
|
1316
|
+
"--repo-root", str(tmp_path),
|
|
1317
|
+
])
|
|
1318
|
+
captured = capsys.readouterr()
|
|
1319
|
+
assert rc == 1, captured.out
|
|
1320
|
+
assert "E5" in captured.out
|
|
1321
|
+
assert "AUDIT_FAILED" in captured.out
|
|
1322
|
+
|
|
1323
|
+
def test_persisted_truncated_jsonl_rejected(self, tmp_path: Path, capsys):
|
|
1324
|
+
# Codex round 3 P1: JSONL with only thread.started + turn.started
|
|
1325
|
+
# has no A7 pairing violation but is incomplete evidence. Phase 6.1's
|
|
1326
|
+
# parse_audit_verdict.validate_stream_shape must run before A7 in
|
|
1327
|
+
# full mode (non-AUDIT_FAILED bundles).
|
|
1328
|
+
run_id = VALID_RUN_ID
|
|
1329
|
+
sidecar = make_valid_sidecar()
|
|
1330
|
+
entry = make_valid_persisted_entry_minor()
|
|
1331
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1332
|
+
verdict = make_valid_verdict_file_minor() # MINOR — non-AUDIT_FAILED
|
|
1333
|
+
# Truncated stream: opens correctly, never closes
|
|
1334
|
+
truncated_events = [
|
|
1335
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1336
|
+
{"type": "turn.started"},
|
|
1337
|
+
]
|
|
1338
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1339
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1340
|
+
import yaml as _yaml
|
|
1341
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1342
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1343
|
+
"\n".join(json.dumps(e) for e in truncated_events) + "\n")
|
|
1344
|
+
rc = main([
|
|
1345
|
+
"--mode", "persisted",
|
|
1346
|
+
"--output-dir", str(tmp_path),
|
|
1347
|
+
"--run-id", run_id,
|
|
1348
|
+
"--repo-root", str(tmp_path),
|
|
1349
|
+
])
|
|
1350
|
+
captured = capsys.readouterr()
|
|
1351
|
+
assert rc == 1, captured.out
|
|
1352
|
+
assert "L2-3/L2-4" in captured.out or "stream-shape" in captured.out, captured.out
|
|
1353
|
+
|
|
1354
|
+
def test_passport_scan_rejects_audit_failed(self, tmp_path: Path, capsys):
|
|
1355
|
+
# Codex round 3 P2: hand-edited passport entry with AUDIT_FAILED status
|
|
1356
|
+
# plus valid verified_at + verified_by must be flagged as forbidden.
|
|
1357
|
+
run_id = VALID_RUN_ID
|
|
1358
|
+
sidecar = make_valid_sidecar()
|
|
1359
|
+
entry = make_valid_persisted_entry_minor()
|
|
1360
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1361
|
+
verdict = make_valid_verdict_file_minor()
|
|
1362
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1363
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1364
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1365
|
+
import yaml as _yaml
|
|
1366
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1367
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1368
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1369
|
+
# Passport with hand-edited AUDIT_FAILED entry that passes E1/E2/E6
|
|
1370
|
+
# presence checks (verified_at + verified_by both set, verified_by
|
|
1371
|
+
# is the canonical orchestrator value).
|
|
1372
|
+
passport = {
|
|
1373
|
+
"audit_artifact": [
|
|
1374
|
+
{
|
|
1375
|
+
"stage": 2,
|
|
1376
|
+
"agent": "synthesis_agent",
|
|
1377
|
+
"deliverable_path": "chapter_4/synthesis.md",
|
|
1378
|
+
"deliverable_sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
|
|
1379
|
+
"run_id": "2026-04-30T15-22-04Z-aaaa",
|
|
1380
|
+
"bundle_manifest_sha": "9a8b7c6d5e4f3b2a1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9876",
|
|
1381
|
+
"artifact_paths": {
|
|
1382
|
+
"jsonl": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.jsonl",
|
|
1383
|
+
"sidecar": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.meta.json",
|
|
1384
|
+
"verdict": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.verdict.yaml",
|
|
1385
|
+
},
|
|
1386
|
+
"verdict": {
|
|
1387
|
+
"status": "AUDIT_FAILED",
|
|
1388
|
+
"round": 1,
|
|
1389
|
+
"target_rounds": 3,
|
|
1390
|
+
"finding_counts": {"p1": 0, "p2": 0, "p3": 0},
|
|
1391
|
+
"failure_reason": "hand-edited forgery",
|
|
1392
|
+
"verified_at": "2026-04-30T15:23:11.847Z",
|
|
1393
|
+
"verified_by": "pipeline_orchestrator_agent",
|
|
1394
|
+
},
|
|
1395
|
+
}
|
|
1396
|
+
]
|
|
1397
|
+
}
|
|
1398
|
+
passport_path = tmp_path / "passport.yaml"
|
|
1399
|
+
passport_path.write_text(_yaml.safe_dump(passport))
|
|
1400
|
+
rc = main([
|
|
1401
|
+
"--mode", "persisted",
|
|
1402
|
+
"--output-dir", str(tmp_path),
|
|
1403
|
+
"--run-id", run_id,
|
|
1404
|
+
"--passport-path", str(passport_path),
|
|
1405
|
+
"--repo-root", str(tmp_path),
|
|
1406
|
+
])
|
|
1407
|
+
captured = capsys.readouterr()
|
|
1408
|
+
assert rc == 1, captured.out
|
|
1409
|
+
assert "E5" in captured.out
|
|
1410
|
+
assert "AUDIT_FAILED" in captured.out
|
|
1411
|
+
|
|
1412
|
+
def test_persisted_forged_artifact_paths_in_entry(self, tmp_path: Path, capsys):
|
|
1413
|
+
# Codex round 4 P2: hand-edited entry.artifact_paths pointing at a
|
|
1414
|
+
# different run_id must be flagged as forgery seam, even when the
|
|
1415
|
+
# CLI is invoked with canonical --output-dir + --run-id and the
|
|
1416
|
+
# actual on-disk files have the canonical basename.
|
|
1417
|
+
run_id = VALID_RUN_ID
|
|
1418
|
+
sidecar = make_valid_sidecar()
|
|
1419
|
+
entry = make_valid_persisted_entry_minor()
|
|
1420
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1421
|
+
# Forged: artifact_paths point at a DIFFERENT run_id's files
|
|
1422
|
+
entry["artifact_paths"] = {
|
|
1423
|
+
"jsonl": "audit_artifacts/2026-04-30T15-22-04Z-ffff.jsonl",
|
|
1424
|
+
"sidecar": "audit_artifacts/2026-04-30T15-22-04Z-ffff.meta.json",
|
|
1425
|
+
"verdict": "audit_artifacts/2026-04-30T15-22-04Z-ffff.verdict.yaml",
|
|
1426
|
+
}
|
|
1427
|
+
verdict = make_valid_verdict_file_minor()
|
|
1428
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1429
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1430
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1431
|
+
import yaml as _yaml
|
|
1432
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1433
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1434
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1435
|
+
rc = main([
|
|
1436
|
+
"--mode", "persisted",
|
|
1437
|
+
"--output-dir", str(tmp_path),
|
|
1438
|
+
"--run-id", run_id,
|
|
1439
|
+
"--repo-root", str(tmp_path),
|
|
1440
|
+
])
|
|
1441
|
+
captured = capsys.readouterr()
|
|
1442
|
+
assert rc == 1, captured.out
|
|
1443
|
+
assert "B7" in captured.out
|
|
1444
|
+
assert "artifact-paths forgery seam" in captured.out
|
|
1445
|
+
|
|
1446
|
+
def test_argparse_invalid_mode_returns_64(self):
|
|
1447
|
+
# Codex round 4 P3: argparse-level failures must exit 64, not 2.
|
|
1448
|
+
result = subprocess.run(
|
|
1449
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
1450
|
+
"--mode", "definitely_not_a_mode"],
|
|
1451
|
+
capture_output=True, text=True,
|
|
1452
|
+
)
|
|
1453
|
+
assert result.returncode == 64, (
|
|
1454
|
+
f"argparse usage error returned {result.returncode}, expected 64\n"
|
|
1455
|
+
f"stderr: {result.stderr}"
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
def test_argparse_unknown_flag_returns_64(self):
|
|
1459
|
+
result = subprocess.run(
|
|
1460
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
1461
|
+
"--bogus-flag"],
|
|
1462
|
+
capture_output=True, text=True,
|
|
1463
|
+
)
|
|
1464
|
+
assert result.returncode == 64, (
|
|
1465
|
+
f"unknown flag returned {result.returncode}, expected 64\n"
|
|
1466
|
+
f"stderr: {result.stderr}"
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
def test_persisted_ack_without_prior_rejected(self, tmp_path: Path, capsys):
|
|
1470
|
+
# Codex round 5 P1: a persisted ack entry whose copied fields don't
|
|
1471
|
+
# match any prior MATERIAL entry must be rejected, not silently
|
|
1472
|
+
# skipped. Previously `prior is None` skipped check_c3 entirely.
|
|
1473
|
+
run_id = VALID_RUN_ID
|
|
1474
|
+
sidecar = make_valid_sidecar()
|
|
1475
|
+
# Build a MATERIAL ack entry with NO matching prior in the passport.
|
|
1476
|
+
entry = make_valid_persisted_entry_minor()
|
|
1477
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1478
|
+
entry["verdict"]["status"] = "MATERIAL"
|
|
1479
|
+
entry["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
1480
|
+
entry["acknowledgement"] = {
|
|
1481
|
+
"finding_ids": ["F-001"],
|
|
1482
|
+
"acknowledged_at": entry["verdict"]["verified_at"],
|
|
1483
|
+
"acknowledged_by": "user",
|
|
1484
|
+
}
|
|
1485
|
+
verdict = make_valid_verdict_file_minor()
|
|
1486
|
+
verdict["verdict_status"] = "MATERIAL"
|
|
1487
|
+
verdict["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
|
|
1488
|
+
verdict["findings"] = [{
|
|
1489
|
+
"id": "F-001", "severity": "P1", "dimension": "3.1",
|
|
1490
|
+
"file": "x.md", "line": 1,
|
|
1491
|
+
"description": "x", "suggested_fix": "y",
|
|
1492
|
+
}]
|
|
1493
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1494
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1495
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1496
|
+
import yaml as _yaml
|
|
1497
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1498
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1499
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1500
|
+
# Empty passport — no prior MATERIAL entry to copy from
|
|
1501
|
+
passport_path = tmp_path / "passport.yaml"
|
|
1502
|
+
passport_path.write_text(_yaml.safe_dump({"audit_artifact": []}))
|
|
1503
|
+
rc = main([
|
|
1504
|
+
"--mode", "persisted",
|
|
1505
|
+
"--output-dir", str(tmp_path),
|
|
1506
|
+
"--run-id", run_id,
|
|
1507
|
+
"--passport-path", str(passport_path),
|
|
1508
|
+
"--repo-root", str(tmp_path),
|
|
1509
|
+
])
|
|
1510
|
+
captured = capsys.readouterr()
|
|
1511
|
+
assert rc == 1, captured.out
|
|
1512
|
+
assert "C3" in captured.out
|
|
1513
|
+
assert "no prior MATERIAL entry" in captured.out
|
|
1514
|
+
|
|
1515
|
+
def test_non_dict_entry_returns_2(self, tmp_path: Path, capsys):
|
|
1516
|
+
# Codex round 5 traceback closure: a non-object entry payload (e.g., `[]`)
|
|
1517
|
+
# must surface as a clean error, not an AttributeError traceback.
|
|
1518
|
+
entry_path = tmp_path / "entry.json"
|
|
1519
|
+
entry_path.write_text("[]")
|
|
1520
|
+
rc = main([
|
|
1521
|
+
"--mode", "proposal",
|
|
1522
|
+
"--entry", str(entry_path),
|
|
1523
|
+
"--sidecar", str(tmp_path / "no.meta.json"),
|
|
1524
|
+
"--verdict", str(tmp_path / "no.verdict.yaml"),
|
|
1525
|
+
"--jsonl", str(tmp_path / "no.jsonl"),
|
|
1526
|
+
"--repo-root", str(tmp_path),
|
|
1527
|
+
])
|
|
1528
|
+
captured = capsys.readouterr()
|
|
1529
|
+
assert rc == 2
|
|
1530
|
+
assert "is not a JSON object" in captured.err
|
|
1531
|
+
|
|
1532
|
+
def test_stream_shape_runs_via_importlib(self, tmp_path: Path, capsys):
|
|
1533
|
+
# Codex round 5 P2: stream-shape gate must work via importlib, not
|
|
1534
|
+
# depend on scripts/ being on sys.path. Import this module under a
|
|
1535
|
+
# fresh `scripts` package qualifier and run main from there.
|
|
1536
|
+
run_id = VALID_RUN_ID
|
|
1537
|
+
sidecar = make_valid_sidecar()
|
|
1538
|
+
entry = make_valid_persisted_entry_minor()
|
|
1539
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1540
|
+
verdict = make_valid_verdict_file_minor() # MINOR — non-AUDIT_FAILED
|
|
1541
|
+
truncated_events = [
|
|
1542
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1543
|
+
{"type": "turn.started"},
|
|
1544
|
+
]
|
|
1545
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1546
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1547
|
+
import yaml as _yaml
|
|
1548
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(truncated_events[0])) # placeholder
|
|
1549
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1550
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1551
|
+
"\n".join(json.dumps(e) for e in truncated_events) + "\n")
|
|
1552
|
+
# Run via subprocess so we exercise the actual import path the gate
|
|
1553
|
+
# uses (importlib loads parse_audit_verdict by file path).
|
|
1554
|
+
result = subprocess.run(
|
|
1555
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
1556
|
+
"--mode", "persisted",
|
|
1557
|
+
"--output-dir", str(tmp_path),
|
|
1558
|
+
"--run-id", run_id,
|
|
1559
|
+
"--repo-root", str(tmp_path)],
|
|
1560
|
+
capture_output=True, text=True,
|
|
1561
|
+
)
|
|
1562
|
+
assert result.returncode == 1, result.stdout + result.stderr
|
|
1563
|
+
assert "L2-3/L2-4" in result.stdout or "stream-shape" in result.stdout, result.stdout
|
|
1564
|
+
|
|
1565
|
+
def test_audit_failed_skips_a7_pairing(self, tmp_path: Path, capsys):
|
|
1566
|
+
# Codex round 6 P2: AUDIT_FAILED bundles legitimately have truncated
|
|
1567
|
+
# streams. A7 pairing must be suspended for them, mirroring the
|
|
1568
|
+
# round-3 stream-shape suspension and §3.4 Layer 3 suspension.
|
|
1569
|
+
run_id = VALID_RUN_ID
|
|
1570
|
+
sidecar = make_valid_sidecar()
|
|
1571
|
+
# AUDIT_FAILED jsonl_thread_id is allowed to be empty per §3.4
|
|
1572
|
+
sidecar["stream"]["jsonl_thread_id"] = ""
|
|
1573
|
+
entry = make_valid_persisted_entry_minor()
|
|
1574
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1575
|
+
# Entry needs to be a proposal (AUDIT_FAILED is proposal-only)
|
|
1576
|
+
# — switch CLI mode to proposal for this test.
|
|
1577
|
+
entry["verdict"].pop("verified_at", None)
|
|
1578
|
+
entry["verdict"].pop("verified_by", None)
|
|
1579
|
+
entry["verdict"]["status"] = "AUDIT_FAILED"
|
|
1580
|
+
entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1581
|
+
entry["verdict"]["failure_reason"] = "synthetic — codex killed"
|
|
1582
|
+
verdict = make_valid_verdict_file_minor()
|
|
1583
|
+
verdict["verdict_status"] = "AUDIT_FAILED"
|
|
1584
|
+
verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1585
|
+
verdict["findings"] = []
|
|
1586
|
+
verdict["failure_reason"] = "synthetic — codex killed"
|
|
1587
|
+
# Truncated stream with unmatched item.started — would fail A7 if
|
|
1588
|
+
# pairing weren't suspended for AUDIT_FAILED
|
|
1589
|
+
truncated_events = [
|
|
1590
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1591
|
+
{"type": "turn.started"},
|
|
1592
|
+
{"type": "item.started",
|
|
1593
|
+
"item": {"id": "killed_tool", "type": "command_execution"}},
|
|
1594
|
+
]
|
|
1595
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1596
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1597
|
+
import yaml as _yaml
|
|
1598
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1599
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1600
|
+
"\n".join(json.dumps(e) for e in truncated_events) + "\n")
|
|
1601
|
+
rc = main([
|
|
1602
|
+
"--mode", "proposal",
|
|
1603
|
+
"--output-dir", str(tmp_path),
|
|
1604
|
+
"--run-id", run_id,
|
|
1605
|
+
"--repo-root", str(tmp_path),
|
|
1606
|
+
])
|
|
1607
|
+
captured = capsys.readouterr()
|
|
1608
|
+
# No A7 finding should appear — AUDIT_FAILED suspends pairing
|
|
1609
|
+
assert "A7" not in captured.out, (
|
|
1610
|
+
f"AUDIT_FAILED bundle should not produce A7 findings — got: {captured.out}"
|
|
1611
|
+
)
|
|
1612
|
+
|
|
1613
|
+
def test_non_object_sidecar_no_traceback(self, tmp_path: Path, capsys):
|
|
1614
|
+
# Codex round 6 P2: a non-object sidecar must surface as SCHEMA
|
|
1615
|
+
# finding without crashing cross-field checks.
|
|
1616
|
+
run_id = VALID_RUN_ID
|
|
1617
|
+
entry = make_valid_persisted_entry_minor()
|
|
1618
|
+
verdict = make_valid_verdict_file_minor()
|
|
1619
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1620
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1621
|
+
(tmp_path / f"{run_id}.meta.json").write_text("[]") # non-object
|
|
1622
|
+
import yaml as _yaml
|
|
1623
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1624
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1625
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1626
|
+
rc = main([
|
|
1627
|
+
"--mode", "persisted",
|
|
1628
|
+
"--output-dir", str(tmp_path),
|
|
1629
|
+
"--run-id", run_id,
|
|
1630
|
+
"--repo-root", str(tmp_path),
|
|
1631
|
+
])
|
|
1632
|
+
captured = capsys.readouterr()
|
|
1633
|
+
assert rc == 1, captured.out
|
|
1634
|
+
assert "SCHEMA" in captured.out, captured.out
|
|
1635
|
+
# No traceback in stderr
|
|
1636
|
+
assert "AttributeError" not in captured.err
|
|
1637
|
+
assert "Traceback" not in captured.err
|
|
1638
|
+
|
|
1639
|
+
def test_non_object_verdict_no_traceback(self, tmp_path: Path, capsys):
|
|
1640
|
+
run_id = VALID_RUN_ID
|
|
1641
|
+
sidecar = make_valid_sidecar()
|
|
1642
|
+
entry = make_valid_persisted_entry_minor()
|
|
1643
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1644
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1645
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1646
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1647
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text("[]") # non-object
|
|
1648
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1649
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1650
|
+
rc = main([
|
|
1651
|
+
"--mode", "persisted",
|
|
1652
|
+
"--output-dir", str(tmp_path),
|
|
1653
|
+
"--run-id", run_id,
|
|
1654
|
+
"--repo-root", str(tmp_path),
|
|
1655
|
+
])
|
|
1656
|
+
captured = capsys.readouterr()
|
|
1657
|
+
assert rc == 1, captured.out
|
|
1658
|
+
assert "SCHEMA" in captured.out, captured.out
|
|
1659
|
+
assert "AttributeError" not in captured.err
|
|
1660
|
+
assert "Traceback" not in captured.err
|
|
1661
|
+
|
|
1662
|
+
def test_jsonl_lacks_section6_verdict_text_rejected(self, tmp_path: Path, capsys):
|
|
1663
|
+
# Codex round 8 P1: stream-shape passes but verdict text is not a
|
|
1664
|
+
# parseable Section 6 → must be rejected as L2-4 finding.
|
|
1665
|
+
run_id = VALID_RUN_ID
|
|
1666
|
+
sidecar = make_valid_sidecar()
|
|
1667
|
+
entry = make_valid_persisted_entry_minor()
|
|
1668
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1669
|
+
verdict = make_valid_verdict_file_minor()
|
|
1670
|
+
# Stream-shape valid but agent_message has no Section 6 summary
|
|
1671
|
+
events = [
|
|
1672
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1673
|
+
{"type": "turn.started"},
|
|
1674
|
+
{"type": "item.completed",
|
|
1675
|
+
"item": {"id": "item_0", "type": "agent_message",
|
|
1676
|
+
"text": "I considered the bundle and decided everything looks fine."}},
|
|
1677
|
+
{"type": "turn.completed",
|
|
1678
|
+
"usage": {"input_tokens": 100, "cached_input_tokens": 0,
|
|
1679
|
+
"output_tokens": 50, "reasoning_output_tokens": 25}},
|
|
1680
|
+
]
|
|
1681
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1682
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1683
|
+
import yaml as _yaml
|
|
1684
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1685
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1686
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1687
|
+
rc = main([
|
|
1688
|
+
"--mode", "persisted",
|
|
1689
|
+
"--output-dir", str(tmp_path),
|
|
1690
|
+
"--run-id", run_id,
|
|
1691
|
+
"--repo-root", str(tmp_path),
|
|
1692
|
+
])
|
|
1693
|
+
captured = capsys.readouterr()
|
|
1694
|
+
assert rc == 1, captured.out
|
|
1695
|
+
assert "L2-4" in captured.out, captured.out
|
|
1696
|
+
assert "Section 6" in captured.out, captured.out
|
|
1697
|
+
|
|
1698
|
+
def test_artifact_path_resolves_to_missing_file_rejected(self, tmp_path: Path, capsys):
|
|
1699
|
+
# Codex round 8 P2: entry.artifact_paths recording a path that
|
|
1700
|
+
# resolves under repo_root to a missing file must be flagged.
|
|
1701
|
+
run_id = VALID_RUN_ID
|
|
1702
|
+
sidecar = make_valid_sidecar()
|
|
1703
|
+
entry = make_valid_persisted_entry_minor()
|
|
1704
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1705
|
+
# Recorded paths point at a sibling subdir that doesn't exist
|
|
1706
|
+
entry["artifact_paths"] = {
|
|
1707
|
+
"jsonl": f"missing_subdir/{run_id}.jsonl",
|
|
1708
|
+
"sidecar": f"missing_subdir/{run_id}.meta.json",
|
|
1709
|
+
"verdict": f"missing_subdir/{run_id}.verdict.yaml",
|
|
1710
|
+
}
|
|
1711
|
+
verdict = make_valid_verdict_file_minor()
|
|
1712
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1713
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1714
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1715
|
+
import yaml as _yaml
|
|
1716
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1717
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1718
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1719
|
+
rc = main([
|
|
1720
|
+
"--mode", "persisted",
|
|
1721
|
+
"--output-dir", str(tmp_path),
|
|
1722
|
+
"--run-id", run_id,
|
|
1723
|
+
"--repo-root", str(tmp_path),
|
|
1724
|
+
])
|
|
1725
|
+
captured = capsys.readouterr()
|
|
1726
|
+
assert rc == 1, captured.out
|
|
1727
|
+
assert "B7" in captured.out
|
|
1728
|
+
assert "does not exist on disk" in captured.out
|
|
1729
|
+
|
|
1730
|
+
def test_audit_failed_skips_layer_3_b2_b3_b4_b5_b7(self, tmp_path: Path, capsys):
|
|
1731
|
+
# Codex round 9 P2: Layer 3 cross-file rules (B2/B3/B4/B5/B7) are
|
|
1732
|
+
# suspended for AUDIT_FAILED bundles per §3.4 + §5.6 Path B5. A
|
|
1733
|
+
# failed audit caused by bundle mutation legitimately has live
|
|
1734
|
+
# deliverable SHA != recorded SHA; treating that as B2 violation
|
|
1735
|
+
# would reject the failure-signaling artifact.
|
|
1736
|
+
run_id = VALID_RUN_ID
|
|
1737
|
+
sidecar = make_valid_sidecar()
|
|
1738
|
+
sidecar["stream"]["jsonl_thread_id"] = "" # AUDIT_FAILED suspension
|
|
1739
|
+
# Forensic exit_code per §3.4 rule 6 — non-zero allowed for AUDIT_FAILED
|
|
1740
|
+
sidecar["process"]["exit_code"] = 70
|
|
1741
|
+
# Drift the bundle SHA in sidecar so live recompute would mismatch (if
|
|
1742
|
+
# B3 ran). With AUDIT_FAILED suspension, B3 must NOT fire.
|
|
1743
|
+
sidecar["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
|
|
1744
|
+
# Drift git_sha to a bogus hex that fails B4 git cat-file (if it ran).
|
|
1745
|
+
sidecar["runner"]["git_sha"] = "deadbee"
|
|
1746
|
+
# Drift duration vs end-start by >>1s so B5 would fire (if it ran).
|
|
1747
|
+
sidecar["timing"]["duration_seconds"] = 9999.0
|
|
1748
|
+
entry = make_valid_persisted_entry_minor()
|
|
1749
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1750
|
+
entry["verdict"].pop("verified_at", None)
|
|
1751
|
+
entry["verdict"].pop("verified_by", None)
|
|
1752
|
+
entry["verdict"]["status"] = "AUDIT_FAILED"
|
|
1753
|
+
entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1754
|
+
entry["verdict"]["failure_reason"] = "synthetic — codex killed"
|
|
1755
|
+
# Drift entry.deliverable_sha so B2 entry-vs-sidecar would fire (if
|
|
1756
|
+
# it ran) — entry side not suspended internally, only Layer 3 is.
|
|
1757
|
+
entry["deliverable_sha"] = "c" * 64
|
|
1758
|
+
verdict = make_valid_verdict_file_minor()
|
|
1759
|
+
verdict["verdict_status"] = "AUDIT_FAILED"
|
|
1760
|
+
verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1761
|
+
verdict["findings"] = []
|
|
1762
|
+
verdict["failure_reason"] = "synthetic — codex killed"
|
|
1763
|
+
# Truncated stream — AUDIT_FAILED expected
|
|
1764
|
+
events = [
|
|
1765
|
+
{"type": "thread.started", "thread_id": VALID_THREAD_ID},
|
|
1766
|
+
{"type": "turn.started"},
|
|
1767
|
+
# codex was killed before final agent_message + turn.completed
|
|
1768
|
+
]
|
|
1769
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1770
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1771
|
+
import yaml as _yaml
|
|
1772
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1773
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1774
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1775
|
+
rc = main([
|
|
1776
|
+
"--mode", "proposal",
|
|
1777
|
+
"--output-dir", str(tmp_path),
|
|
1778
|
+
"--run-id", run_id,
|
|
1779
|
+
"--repo-root", str(tmp_path),
|
|
1780
|
+
])
|
|
1781
|
+
captured = capsys.readouterr()
|
|
1782
|
+
# B2/B3/B4/B5/B7 must not fire — AUDIT_FAILED suspends Layer 3
|
|
1783
|
+
for rule in ("B2", "B3", "B4", "B5", "B7"):
|
|
1784
|
+
assert f"[{rule}]" not in captured.out, (
|
|
1785
|
+
f"{rule} fired on AUDIT_FAILED bundle but Layer 3 should be "
|
|
1786
|
+
f"suspended per §3.4: {captured.out}"
|
|
1787
|
+
)
|
|
1788
|
+
|
|
1789
|
+
def test_audit_failed_with_partial_jsonl_no_internal_error(self, tmp_path: Path, capsys):
|
|
1790
|
+
# Codex round 11 P2: AUDIT_FAILED bundle with partial/non-JSON last
|
|
1791
|
+
# line (codex SIGKILL'd mid-write) must NOT exit 2 internal error.
|
|
1792
|
+
# Round 9 suspended schema validation but _load_jsonl() still raised
|
|
1793
|
+
# before reaching that suspension.
|
|
1794
|
+
run_id = VALID_RUN_ID
|
|
1795
|
+
sidecar = make_valid_sidecar()
|
|
1796
|
+
sidecar["stream"]["jsonl_thread_id"] = ""
|
|
1797
|
+
sidecar["process"]["exit_code"] = 137 # 128 + SIGKILL
|
|
1798
|
+
entry = make_valid_persisted_entry_minor()
|
|
1799
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1800
|
+
entry["verdict"].pop("verified_at", None)
|
|
1801
|
+
entry["verdict"].pop("verified_by", None)
|
|
1802
|
+
entry["verdict"]["status"] = "AUDIT_FAILED"
|
|
1803
|
+
entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1804
|
+
entry["verdict"]["failure_reason"] = "synthetic — codex SIGKILL"
|
|
1805
|
+
verdict = make_valid_verdict_file_minor()
|
|
1806
|
+
verdict["verdict_status"] = "AUDIT_FAILED"
|
|
1807
|
+
verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1808
|
+
verdict["findings"] = []
|
|
1809
|
+
verdict["failure_reason"] = "synthetic — codex SIGKILL"
|
|
1810
|
+
# Partial JSON on the last line — would raise ValueError in
|
|
1811
|
+
# _load_jsonl and previously caused exit 2.
|
|
1812
|
+
partial_jsonl = (
|
|
1813
|
+
json.dumps({"type": "thread.started", "thread_id": VALID_THREAD_ID}) + "\n"
|
|
1814
|
+
+ json.dumps({"type": "turn.started"}) + "\n"
|
|
1815
|
+
+ '{"type":"item.start' # truncated mid-write, no closing quote/brace
|
|
1816
|
+
)
|
|
1817
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1818
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1819
|
+
import yaml as _yaml
|
|
1820
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1821
|
+
(tmp_path / f"{run_id}.jsonl").write_text(partial_jsonl)
|
|
1822
|
+
rc = main([
|
|
1823
|
+
"--mode", "proposal",
|
|
1824
|
+
"--output-dir", str(tmp_path),
|
|
1825
|
+
"--run-id", run_id,
|
|
1826
|
+
"--repo-root", str(tmp_path),
|
|
1827
|
+
])
|
|
1828
|
+
captured = capsys.readouterr()
|
|
1829
|
+
# Must NOT be rc=2 (internal error). Forensic-only bundle is allowed.
|
|
1830
|
+
assert rc != 2, (
|
|
1831
|
+
f"AUDIT_FAILED with partial JSONL returned rc=2 internal error; "
|
|
1832
|
+
f"should be handled as forensic-only bundle\nstdout: {captured.out}\n"
|
|
1833
|
+
f"stderr: {captured.err}"
|
|
1834
|
+
)
|
|
1835
|
+
|
|
1836
|
+
def test_schema_error_short_circuits_no_traceback(self, tmp_path: Path, capsys):
|
|
1837
|
+
# Codex round 12 P2: schema-valid-as-dict but with a nested field of
|
|
1838
|
+
# the wrong shape (timing: [1] passes top-level isinstance(dict) but
|
|
1839
|
+
# timing.get() crashes in check_b5). Short-circuit on SCHEMA error
|
|
1840
|
+
# so cross-field rules don't crash on input violating their type
|
|
1841
|
+
# presuppositions.
|
|
1842
|
+
run_id = VALID_RUN_ID
|
|
1843
|
+
sidecar = make_valid_sidecar()
|
|
1844
|
+
# Inject malformed nested field that schema rejects but isinstance
|
|
1845
|
+
# at the top level still passes:
|
|
1846
|
+
sidecar["timing"] = [1, 2, 3] # list instead of dict
|
|
1847
|
+
entry = make_valid_persisted_entry_minor()
|
|
1848
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1849
|
+
verdict = make_valid_verdict_file_minor()
|
|
1850
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1851
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1852
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1853
|
+
import yaml as _yaml
|
|
1854
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1855
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1856
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1857
|
+
rc = main([
|
|
1858
|
+
"--mode", "persisted",
|
|
1859
|
+
"--output-dir", str(tmp_path),
|
|
1860
|
+
"--run-id", run_id,
|
|
1861
|
+
"--repo-root", str(tmp_path),
|
|
1862
|
+
])
|
|
1863
|
+
captured = capsys.readouterr()
|
|
1864
|
+
assert rc == 1, captured.out
|
|
1865
|
+
assert "SCHEMA" in captured.out, captured.out
|
|
1866
|
+
# Critical: no traceback must reach stderr
|
|
1867
|
+
assert "Traceback" not in captured.err, captured.err
|
|
1868
|
+
assert "AttributeError" not in captured.err, captured.err
|
|
1869
|
+
|
|
1870
|
+
def test_persisted_with_higher_round_proposal_fires_d4(self, tmp_path: Path, capsys):
|
|
1871
|
+
# Codex round 13 P2: when --mode persisted runs and --output-dir
|
|
1872
|
+
# contains a higher-round unmerged proposal for the same (stage,
|
|
1873
|
+
# agent, deliverable_sha) tuple, D4 must fire (the persisted
|
|
1874
|
+
# entry's Path A is supposed to be preempted).
|
|
1875
|
+
run_id = VALID_RUN_ID
|
|
1876
|
+
# Persisted entry at round 1
|
|
1877
|
+
sidecar = make_valid_sidecar()
|
|
1878
|
+
entry = make_valid_persisted_entry_minor()
|
|
1879
|
+
entry["verdict"]["round"] = 1
|
|
1880
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1881
|
+
verdict = make_valid_verdict_file_minor()
|
|
1882
|
+
verdict["round"] = 1 # match entry round
|
|
1883
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1884
|
+
# Move persisted into a consumed/ subdir so the scan doesn't see
|
|
1885
|
+
# it as an unmerged proposal (matches §4.9 step 9 convention).
|
|
1886
|
+
consumed = tmp_path / "consumed"
|
|
1887
|
+
consumed.mkdir()
|
|
1888
|
+
(consumed / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1889
|
+
# Companion files in output_dir (where the lint expects them)
|
|
1890
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1891
|
+
import yaml as _yaml
|
|
1892
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1893
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1894
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1895
|
+
|
|
1896
|
+
# Higher-round proposal in output_dir (round 2 > persisted round 1)
|
|
1897
|
+
proposal_run_id = "2026-04-30T15-30-00Z-eeee"
|
|
1898
|
+
proposal_entry = make_valid_persisted_entry_minor()
|
|
1899
|
+
proposal_entry["verdict"]["round"] = 2 # higher than persisted round=1
|
|
1900
|
+
proposal_entry["verdict"]["status"] = "PASS"
|
|
1901
|
+
proposal_entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
|
|
1902
|
+
proposal_entry["verdict"].pop("verified_at", None) # proposal arm
|
|
1903
|
+
proposal_entry["verdict"].pop("verified_by", None)
|
|
1904
|
+
proposal_entry["run_id"] = proposal_run_id
|
|
1905
|
+
proposal_entry["artifact_paths"] = {
|
|
1906
|
+
"jsonl": f"{proposal_run_id}.jsonl",
|
|
1907
|
+
"sidecar": f"{proposal_run_id}.meta.json",
|
|
1908
|
+
"verdict": f"{proposal_run_id}.verdict.yaml",
|
|
1909
|
+
}
|
|
1910
|
+
(tmp_path / f"{proposal_run_id}.audit_artifact_entry.json").write_text(
|
|
1911
|
+
json.dumps(proposal_entry))
|
|
1912
|
+
|
|
1913
|
+
rc = main([
|
|
1914
|
+
"--mode", "persisted",
|
|
1915
|
+
"--entry", str(consumed / f"{run_id}.audit_artifact_entry.json"),
|
|
1916
|
+
"--output-dir", str(tmp_path),
|
|
1917
|
+
"--run-id", run_id,
|
|
1918
|
+
"--repo-root", str(tmp_path),
|
|
1919
|
+
])
|
|
1920
|
+
captured = capsys.readouterr()
|
|
1921
|
+
assert rc == 1, captured.out
|
|
1922
|
+
assert "D4" in captured.out, captured.out
|
|
1923
|
+
assert "round=2" in captured.out and "round=1" in captured.out, captured.out
|
|
1924
|
+
|
|
1925
|
+
def test_script_is_executable(self):
|
|
1926
|
+
# Codex round 14 P1: spec §5.2 L2-5 invokes the lint as
|
|
1927
|
+
# `scripts/check_audit_artifact_consistency.py --mode jsonl-stream …`
|
|
1928
|
+
# without a `python` prefix. The file must carry the executable
|
|
1929
|
+
# bit (100755) like Phase 6.1's audit_snapshot.py / parse_audit_
|
|
1930
|
+
# verdict.py / run_codex_audit.sh — direct exec failing with
|
|
1931
|
+
# permission denied (exit 126) would block the orchestrator gate.
|
|
1932
|
+
import os
|
|
1933
|
+
script_path = REPO / "scripts/check_audit_artifact_consistency.py"
|
|
1934
|
+
assert os.access(script_path, os.X_OK), (
|
|
1935
|
+
f"{script_path} must be executable for orchestrator §5.2 L2-5 "
|
|
1936
|
+
f"direct invocation. Run `chmod +x` and `git update-index --chmod=+x`."
|
|
1937
|
+
)
|
|
1938
|
+
|
|
1939
|
+
def test_script_runs_via_direct_exec(self):
|
|
1940
|
+
# End-to-end smoke: invoke the script directly (no `python` prefix)
|
|
1941
|
+
# and confirm it produces the documented harness exit code. Belt
|
|
1942
|
+
# and braces against future `chmod 644` regressions.
|
|
1943
|
+
script_path = REPO / "scripts/check_audit_artifact_consistency.py"
|
|
1944
|
+
result = subprocess.run(
|
|
1945
|
+
[str(script_path), "--example-validation-harness"],
|
|
1946
|
+
capture_output=True, text=True,
|
|
1947
|
+
)
|
|
1948
|
+
assert result.returncode == 0, (
|
|
1949
|
+
f"direct exec returned {result.returncode} (expected 0)\n"
|
|
1950
|
+
f"stdout: {result.stdout}\nstderr: {result.stderr}"
|
|
1951
|
+
)
|
|
1952
|
+
|
|
1953
|
+
def test_swap_verdict_file_with_different_run_id_rejected(self, tmp_path: Path, capsys):
|
|
1954
|
+
# Codex round 15 P1: swap-one-verdict-file forgery — a verdict.yaml
|
|
1955
|
+
# with all valid schema/cross-field but internal run_id pointing at
|
|
1956
|
+
# a different run, renamed into the canonical <run_id>.verdict.yaml
|
|
1957
|
+
# slot. C1 mirrors only the nested verdict block; B7 (pre-round-15)
|
|
1958
|
+
# checked sidecar.run_id but not verdict.run_id.
|
|
1959
|
+
run_id = VALID_RUN_ID
|
|
1960
|
+
sidecar = make_valid_sidecar()
|
|
1961
|
+
entry = make_valid_persisted_entry_minor()
|
|
1962
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1963
|
+
verdict = make_valid_verdict_file_minor()
|
|
1964
|
+
verdict["run_id"] = "2026-04-30T15-30-00Z-ffff" # different run!
|
|
1965
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1966
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1967
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1968
|
+
import yaml as _yaml
|
|
1969
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
1970
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
1971
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
1972
|
+
rc = main([
|
|
1973
|
+
"--mode", "persisted",
|
|
1974
|
+
"--output-dir", str(tmp_path),
|
|
1975
|
+
"--run-id", run_id,
|
|
1976
|
+
"--repo-root", str(tmp_path),
|
|
1977
|
+
])
|
|
1978
|
+
captured = capsys.readouterr()
|
|
1979
|
+
assert rc == 1, captured.out
|
|
1980
|
+
assert "B7" in captured.out
|
|
1981
|
+
assert "verdict.run_id" in captured.out
|
|
1982
|
+
assert "swap-one-verdict-file forgery" in captured.out
|
|
1983
|
+
|
|
1984
|
+
def test_only_entry_flag_resolves_companions_from_artifact_paths(self, tmp_path: Path, capsys):
|
|
1985
|
+
# Codex round 16 P2: caller passes only --entry + --repo-root; the
|
|
1986
|
+
# lint must resolve sidecar/verdict/jsonl from
|
|
1987
|
+
# entry.artifact_paths instead of demanding redundant flags.
|
|
1988
|
+
run_id = VALID_RUN_ID
|
|
1989
|
+
sidecar = make_valid_sidecar()
|
|
1990
|
+
entry = make_valid_persisted_entry_minor()
|
|
1991
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
1992
|
+
verdict = make_valid_verdict_file_minor()
|
|
1993
|
+
events = make_valid_jsonl_events_no_tool()
|
|
1994
|
+
# Companion files in tmp_path; entry.artifact_paths records them
|
|
1995
|
+
# as repo-relative basenames (matching the round-7+8 fixture
|
|
1996
|
+
# convention).
|
|
1997
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
1998
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
1999
|
+
import yaml as _yaml
|
|
2000
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
2001
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
2002
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
2003
|
+
# ONLY --entry + --repo-root, no --output-dir, no --sidecar/verdict/jsonl
|
|
2004
|
+
rc = main([
|
|
2005
|
+
"--mode", "persisted",
|
|
2006
|
+
"--entry", str(tmp_path / f"{run_id}.audit_artifact_entry.json"),
|
|
2007
|
+
"--repo-root", str(tmp_path),
|
|
2008
|
+
])
|
|
2009
|
+
captured = capsys.readouterr()
|
|
2010
|
+
assert rc == 0, captured.out
|
|
2011
|
+
|
|
2012
|
+
def test_persisted_schema_invalid_sidecar_returns_1(self, tmp_path: Path, capsys):
|
|
2013
|
+
# Codex round 1 P2: schema-invalid sidecar must be rejected even when
|
|
2014
|
+
# cross-field rules don't cover the malformed field.
|
|
2015
|
+
run_id = VALID_RUN_ID
|
|
2016
|
+
sidecar = make_valid_sidecar()
|
|
2017
|
+
# Inject a malformed bare extra field (additionalProperties: false on
|
|
2018
|
+
# sidecar root → schema rejects unknown top-level key).
|
|
2019
|
+
sidecar["definitely_not_in_schema"] = "drift"
|
|
2020
|
+
entry = make_valid_persisted_entry_minor()
|
|
2021
|
+
entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
|
|
2022
|
+
verdict = make_valid_verdict_file_minor()
|
|
2023
|
+
events = make_valid_jsonl_events_no_tool()
|
|
2024
|
+
(tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
|
|
2025
|
+
(tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
|
|
2026
|
+
import yaml as _yaml
|
|
2027
|
+
(tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
|
|
2028
|
+
(tmp_path / f"{run_id}.jsonl").write_text(
|
|
2029
|
+
"\n".join(json.dumps(e) for e in events) + "\n")
|
|
2030
|
+
rc = main([
|
|
2031
|
+
"--mode", "persisted",
|
|
2032
|
+
"--output-dir", str(tmp_path),
|
|
2033
|
+
"--run-id", run_id,
|
|
2034
|
+
"--repo-root", str(tmp_path),
|
|
2035
|
+
])
|
|
2036
|
+
captured = capsys.readouterr()
|
|
2037
|
+
assert rc == 1, captured.out
|
|
2038
|
+
assert "SCHEMA" in captured.out
|
|
2039
|
+
assert "definitely_not_in_schema" in captured.out
|
|
2040
|
+
|
|
2041
|
+
|
|
2042
|
+
# ---------------------------------------------------------------------------
|
|
2043
|
+
# Example validation harness (F4) — smoke test it runs without crash
|
|
2044
|
+
# ---------------------------------------------------------------------------
|
|
2045
|
+
|
|
2046
|
+
|
|
2047
|
+
class TestExampleHarness:
|
|
2048
|
+
"""F4 — example-validation-harness runs end-to-end on the real spec dir."""
|
|
2049
|
+
|
|
2050
|
+
def test_harness_runs(self):
|
|
2051
|
+
# Should not raise; may produce findings (known §3.4 hyphen drift)
|
|
2052
|
+
findings = run_example_harness(REPO)
|
|
2053
|
+
# We don't assert zero findings — the spec is known to have drift the
|
|
2054
|
+
# harness should surface. We only assert the harness ran and any
|
|
2055
|
+
# findings it produces are well-formed LintErrors.
|
|
2056
|
+
assert all(isinstance(f, LintError) for f in findings)
|
|
2057
|
+
# And every finding's rule_id is "F4" or "SCHEMA"-prefixed
|
|
2058
|
+
for f in findings:
|
|
2059
|
+
assert f.rule_id in ("F4", "SCHEMA"), f.render()
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
# ---------------------------------------------------------------------------
|
|
2063
|
+
# Fixture smoke tests — codex round 7 P2 closure
|
|
2064
|
+
# ---------------------------------------------------------------------------
|
|
2065
|
+
|
|
2066
|
+
|
|
2067
|
+
FIXTURE_ROOT = REPO / "scripts/fixtures/audit_artifact_consistency"
|
|
2068
|
+
|
|
2069
|
+
|
|
2070
|
+
class TestFixtureSmoke:
|
|
2071
|
+
"""Verify scripts/fixtures/audit_artifact_consistency/ README invocation
|
|
2072
|
+
examples actually produce the documented exit codes.
|
|
2073
|
+
|
|
2074
|
+
Codex round 7 P2: positive fixtures only pass when --repo-root points
|
|
2075
|
+
at the fixture directory itself (so B3's live-disk gate hits the
|
|
2076
|
+
synthetic-fixture safe-skip via missing .git/ marker). Without this
|
|
2077
|
+
test, README drift could leave positive fixtures silently broken
|
|
2078
|
+
against the documented invocation.
|
|
2079
|
+
"""
|
|
2080
|
+
|
|
2081
|
+
def test_positive_persisted_minor(self):
|
|
2082
|
+
bundle = FIXTURE_ROOT / "positive/persisted_minor"
|
|
2083
|
+
result = subprocess.run(
|
|
2084
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
2085
|
+
"--mode", "persisted",
|
|
2086
|
+
"--output-dir", str(bundle),
|
|
2087
|
+
"--run-id", "2026-04-30T15-22-04Z-d8f3",
|
|
2088
|
+
"--repo-root", str(bundle)],
|
|
2089
|
+
capture_output=True, text=True,
|
|
2090
|
+
)
|
|
2091
|
+
assert result.returncode == 0, (
|
|
2092
|
+
f"positive fixture persisted_minor returned {result.returncode}\n"
|
|
2093
|
+
f"stdout: {result.stdout}\nstderr: {result.stderr}"
|
|
2094
|
+
)
|
|
2095
|
+
|
|
2096
|
+
def test_positive_proposal_pass(self):
|
|
2097
|
+
bundle = FIXTURE_ROOT / "positive/proposal_pass"
|
|
2098
|
+
result = subprocess.run(
|
|
2099
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
2100
|
+
"--mode", "proposal",
|
|
2101
|
+
"--output-dir", str(bundle),
|
|
2102
|
+
"--run-id", "2026-04-30T15-22-04Z-d8f3",
|
|
2103
|
+
"--repo-root", str(bundle)],
|
|
2104
|
+
capture_output=True, text=True,
|
|
2105
|
+
)
|
|
2106
|
+
assert result.returncode == 0, (
|
|
2107
|
+
f"positive fixture proposal_pass returned {result.returncode}\n"
|
|
2108
|
+
f"stdout: {result.stdout}\nstderr: {result.stderr}"
|
|
2109
|
+
)
|
|
2110
|
+
|
|
2111
|
+
def test_negative_a1_pass_with_p1(self):
|
|
2112
|
+
bundle = FIXTURE_ROOT / "negative/a1_pass_with_p1"
|
|
2113
|
+
result = subprocess.run(
|
|
2114
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
2115
|
+
"--mode", "proposal",
|
|
2116
|
+
"--output-dir", str(bundle),
|
|
2117
|
+
"--run-id", "2026-04-30T15-22-04Z-d8f3",
|
|
2118
|
+
"--repo-root", str(bundle)],
|
|
2119
|
+
capture_output=True, text=True,
|
|
2120
|
+
)
|
|
2121
|
+
assert result.returncode == 1, result.stdout
|
|
2122
|
+
assert "A1" in result.stdout, result.stdout
|
|
2123
|
+
|
|
2124
|
+
def test_negative_a7_orphan_completion(self):
|
|
2125
|
+
jsonl = FIXTURE_ROOT / "negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl"
|
|
2126
|
+
result = subprocess.run(
|
|
2127
|
+
[sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
|
|
2128
|
+
"--mode", "jsonl-stream",
|
|
2129
|
+
"--jsonl", str(jsonl)],
|
|
2130
|
+
capture_output=True, text=True,
|
|
2131
|
+
)
|
|
2132
|
+
assert result.returncode == 1, result.stdout
|
|
2133
|
+
assert "A7" in result.stdout, result.stdout
|