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,1462 @@
|
|
|
1
|
+
"""Unit tests for check_version_consistency.py."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
import textwrap
|
|
6
|
+
import unittest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from tempfile import TemporaryDirectory
|
|
9
|
+
|
|
10
|
+
from tests.test_helpers import run_script
|
|
11
|
+
|
|
12
|
+
SCRIPT = Path(__file__).resolve().parent / "check_version_consistency.py"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _run(root: Path) -> subprocess.CompletedProcess:
|
|
16
|
+
return run_script(SCRIPT, "--path", str(root))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _write_skill(root: Path, name: str, version: str) -> None:
|
|
20
|
+
skill_dir = root / name
|
|
21
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
(skill_dir / "SKILL.md").write_text(
|
|
23
|
+
textwrap.dedent(
|
|
24
|
+
f"""\
|
|
25
|
+
---
|
|
26
|
+
name: {name}
|
|
27
|
+
description: "fixture"
|
|
28
|
+
metadata:
|
|
29
|
+
version: "{version}"
|
|
30
|
+
last_updated: "2026-04-22"
|
|
31
|
+
status: active
|
|
32
|
+
data_access_level: raw
|
|
33
|
+
task_type: open-ended
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
# {name}
|
|
37
|
+
"""
|
|
38
|
+
),
|
|
39
|
+
encoding="utf-8",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _write_claude_md(
|
|
44
|
+
root: Path,
|
|
45
|
+
suite_version: str,
|
|
46
|
+
table_rows: list[tuple[str, str]],
|
|
47
|
+
last_updated: str | None = "2026-04-22",
|
|
48
|
+
key_additions: str | None = "derive",
|
|
49
|
+
) -> None:
|
|
50
|
+
"""`last_updated` / `key_additions` default to values aligned with the
|
|
51
|
+
fixture CHANGELOG (invariants 10 + 11). `key_additions="derive"` writes a
|
|
52
|
+
`## v<major>.<minor> Key Additions` heading derived from `suite_version`;
|
|
53
|
+
pass an explicit token (e.g. "v3.4") to drift it, or None to omit."""
|
|
54
|
+
claude_dir = root / ".claude"
|
|
55
|
+
claude_dir.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
rows = "\n".join(
|
|
57
|
+
f"| `{name}` v{ver} | purpose | modes |" for name, ver in table_rows
|
|
58
|
+
)
|
|
59
|
+
if key_additions == "derive":
|
|
60
|
+
parts = suite_version.split(".")
|
|
61
|
+
if len(parts) >= 2 and all(p.isdigit() for p in parts[:2]):
|
|
62
|
+
key_additions = f"v{parts[0]}.{parts[1]}"
|
|
63
|
+
else:
|
|
64
|
+
key_additions = None
|
|
65
|
+
key_additions_block = (
|
|
66
|
+
f"## {key_additions} Key Additions (fixture)\n\n- fixture addition\n\n"
|
|
67
|
+
if key_additions is not None
|
|
68
|
+
else ""
|
|
69
|
+
)
|
|
70
|
+
last_updated_line = (
|
|
71
|
+
f"- **Last Updated**: {last_updated}\n" if last_updated is not None else ""
|
|
72
|
+
)
|
|
73
|
+
text = (
|
|
74
|
+
"# Academic Research Skills\n"
|
|
75
|
+
"\n"
|
|
76
|
+
"## Skills Overview\n"
|
|
77
|
+
"\n"
|
|
78
|
+
"| Skill | Purpose | Key Modes |\n"
|
|
79
|
+
"|-------|---------|-----------|\n"
|
|
80
|
+
f"{rows}\n"
|
|
81
|
+
"\n"
|
|
82
|
+
f"{key_additions_block}"
|
|
83
|
+
"## Version Info\n"
|
|
84
|
+
f"- **Suite version**: {suite_version} (per CHANGELOG.md)\n"
|
|
85
|
+
f"{last_updated_line}"
|
|
86
|
+
)
|
|
87
|
+
(claude_dir / "CLAUDE.md").write_text(text, encoding="utf-8")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _write_changelog(
|
|
91
|
+
root: Path,
|
|
92
|
+
latest_version: str,
|
|
93
|
+
prior_versions: list[str] | None = None,
|
|
94
|
+
latest_body: str | None = None,
|
|
95
|
+
latest_date: str = "2026-04-22",
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Write fixture CHANGELOG with `latest_version` first, then any `prior_versions`.
|
|
98
|
+
|
|
99
|
+
The default latest-entry body is long enough to satisfy the >=100-char
|
|
100
|
+
release-notes invariant (9); pass a short `latest_body` to drift it."""
|
|
101
|
+
if latest_body is None:
|
|
102
|
+
latest_body = (
|
|
103
|
+
"### Added\n"
|
|
104
|
+
"- fixture entry with enough substantive body text that the latest "
|
|
105
|
+
"release entry clears the one-hundred-character release-notes "
|
|
106
|
+
"minimum enforced by invariant 9\n"
|
|
107
|
+
)
|
|
108
|
+
entries = [f"## [{latest_version}] - {latest_date}\n\n{latest_body}"]
|
|
109
|
+
for prev in prior_versions or []:
|
|
110
|
+
entries.append(f"## [{prev}] - 2026-04-15\n\n### Added\n- prior fixture entry\n")
|
|
111
|
+
(root / "CHANGELOG.md").write_text(
|
|
112
|
+
"# Changelog\n\n" + "\n".join(entries),
|
|
113
|
+
encoding="utf-8",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _write_plugin_manifests(
|
|
118
|
+
root: Path, version: str, description: str | None = None
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Fixture .claude-plugin/{plugin,marketplace}.json at `version` (invariant 4).
|
|
121
|
+
`description` (when given) lands in plugin.json for the invariant-8
|
|
122
|
+
"N-agent" claim tests."""
|
|
123
|
+
import json
|
|
124
|
+
plugin_dir = root / ".claude-plugin"
|
|
125
|
+
plugin_dir.mkdir(parents=True, exist_ok=True)
|
|
126
|
+
plugin_obj: dict[str, str] = {"name": "fixture", "version": version}
|
|
127
|
+
if description is not None:
|
|
128
|
+
plugin_obj["description"] = description
|
|
129
|
+
(plugin_dir / "plugin.json").write_text(
|
|
130
|
+
json.dumps(plugin_obj), encoding="utf-8"
|
|
131
|
+
)
|
|
132
|
+
(plugin_dir / "marketplace.json").write_text(
|
|
133
|
+
json.dumps({"name": "fixture", "plugins": [{"name": "fixture", "version": version}]}),
|
|
134
|
+
encoding="utf-8",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _write_readme(root: Path, badge_version: str) -> None:
|
|
139
|
+
"""Fixture README.md with a shields.io version badge (invariant 5).
|
|
140
|
+
|
|
141
|
+
The badge encodes the version twice (label + release-tag URL); the lint
|
|
142
|
+
keys off the `badge/version-vX.Y.Z` label, the canonical surface a user
|
|
143
|
+
sees. Both are written aligned here so drift tests mutate one explicitly.
|
|
144
|
+
"""
|
|
145
|
+
(root / "README.md").write_text(
|
|
146
|
+
"# Academic Research Skills for Claude Code\n"
|
|
147
|
+
"\n"
|
|
148
|
+
f"[]"
|
|
149
|
+
f"(https://github.com/x/y/releases/tag/v{badge_version})\n",
|
|
150
|
+
encoding="utf-8",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _write_docs(
|
|
155
|
+
root: Path,
|
|
156
|
+
en_h2: list[str],
|
|
157
|
+
zh_h2: list[str] | None = None,
|
|
158
|
+
extra_version_strings: list[str] | None = None,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Fixture docs/ tree (invariants 6 + 7).
|
|
161
|
+
|
|
162
|
+
`en_h2` / `zh_h2` are H2 heading texts (without the leading '## ').
|
|
163
|
+
Version-bearing headings like 'Foo (v3.6.4+)' exercise the en<->zh-TW
|
|
164
|
+
heading-pairing invariant; plain headings are ignored by it. Any
|
|
165
|
+
`extra_version_strings` are dropped into PERFORMANCE.md body to exercise
|
|
166
|
+
the 'docs must not cite a future/unknown version' invariant.
|
|
167
|
+
"""
|
|
168
|
+
docs = root / "docs"
|
|
169
|
+
docs.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
en_body = "\n".join(f"## {h}\n\nbody\n" for h in en_h2)
|
|
171
|
+
if extra_version_strings:
|
|
172
|
+
en_body += "\n" + "\n".join(f"See v{v} for details.\n" for v in extra_version_strings)
|
|
173
|
+
(docs / "PERFORMANCE.md").write_text("# Performance\n\n" + en_body, encoding="utf-8")
|
|
174
|
+
if zh_h2 is not None:
|
|
175
|
+
zh_body = "\n".join(f"## {h}\n\n內文\n" for h in zh_h2)
|
|
176
|
+
(docs / "PERFORMANCE.zh-TW.md").write_text("# 效能\n\n" + zh_body, encoding="utf-8")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _write_cff(
|
|
180
|
+
root: Path, version: str, date_released: str = "2026-04-22"
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Fixture CITATION.cff at `version` (invariant 12). The default
|
|
183
|
+
date-released matches the fixture CHANGELOG's latest-entry date so the
|
|
184
|
+
±7-day window passes unless a test drifts it."""
|
|
185
|
+
(root / "CITATION.cff").write_text(
|
|
186
|
+
textwrap.dedent(
|
|
187
|
+
f"""\
|
|
188
|
+
cff-version: 1.2.0
|
|
189
|
+
title: fixture
|
|
190
|
+
type: software
|
|
191
|
+
version: {version}
|
|
192
|
+
date-released: {date_released}
|
|
193
|
+
"""
|
|
194
|
+
),
|
|
195
|
+
encoding="utf-8",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _write_positioning(root: Path, version: str) -> None:
|
|
200
|
+
"""Fixture POSITIONING.md citation prose at `version` (invariant 12)."""
|
|
201
|
+
(root / "POSITIONING.md").write_text(
|
|
202
|
+
"# Positioning\n\n"
|
|
203
|
+
"Wu, C.-I. (2026). Fixture Suite "
|
|
204
|
+
f"(Version {version}) [Computer software]. Zenodo.\n",
|
|
205
|
+
encoding="utf-8",
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _write_aligned_fixture(
|
|
210
|
+
root: Path,
|
|
211
|
+
last_updated: str | None = "2026-04-22",
|
|
212
|
+
key_additions: str | None = "derive",
|
|
213
|
+
) -> None:
|
|
214
|
+
"""Everything lines up — baseline for PASS cases and drift mutations.
|
|
215
|
+
|
|
216
|
+
`last_updated` / `key_additions` pass straight through to `_write_claude_md`
|
|
217
|
+
so invariant-10 / invariant-11 tests can drift a single field without
|
|
218
|
+
re-specifying the aligned skill table (which would duplicate the very
|
|
219
|
+
drift this lint exists to catch)."""
|
|
220
|
+
skills = [
|
|
221
|
+
("deep-research", "2.9.0"),
|
|
222
|
+
("academic-paper", "3.1.0"),
|
|
223
|
+
("academic-paper-reviewer", "1.8.1"),
|
|
224
|
+
("academic-pipeline", "3.5.0"),
|
|
225
|
+
]
|
|
226
|
+
for name, ver in skills:
|
|
227
|
+
_write_skill(root, name, ver)
|
|
228
|
+
_write_claude_md(
|
|
229
|
+
root,
|
|
230
|
+
suite_version="3.5.0",
|
|
231
|
+
table_rows=skills,
|
|
232
|
+
last_updated=last_updated,
|
|
233
|
+
key_additions=key_additions,
|
|
234
|
+
)
|
|
235
|
+
_write_changelog(root, latest_version="3.5.0")
|
|
236
|
+
_write_plugin_manifests(root, "3.5.0")
|
|
237
|
+
_write_readme(root, "3.5.0")
|
|
238
|
+
_write_cff(root, "3.5.0")
|
|
239
|
+
_write_positioning(root, "3.5.0")
|
|
240
|
+
# en has an extra plain H2 (translation asymmetry is allowed); the
|
|
241
|
+
# version-bearing heading is present in both and at a past version.
|
|
242
|
+
_write_docs(
|
|
243
|
+
root,
|
|
244
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)", "Extra EN-only section"],
|
|
245
|
+
zh_h2=["Token 用量", "語料庫導入 (v3.4.0+)"],
|
|
246
|
+
extra_version_strings=["3.4.0"],
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _write_aligned_fixture_v351(root: Path) -> None:
|
|
251
|
+
"""v3.5.1 suite: deep-research 2.9.1, academic-pipeline 3.5.1."""
|
|
252
|
+
skills = [
|
|
253
|
+
("deep-research", "2.9.1"),
|
|
254
|
+
("academic-paper", "3.1.0"),
|
|
255
|
+
("academic-paper-reviewer", "1.8.1"),
|
|
256
|
+
("academic-pipeline", "3.5.1"),
|
|
257
|
+
]
|
|
258
|
+
for name, ver in skills:
|
|
259
|
+
_write_skill(root, name, ver)
|
|
260
|
+
_write_claude_md(root, suite_version="3.5.1", table_rows=skills)
|
|
261
|
+
_write_changelog(root, latest_version="3.5.1")
|
|
262
|
+
_write_plugin_manifests(root, "3.5.1")
|
|
263
|
+
_write_readme(root, "3.5.1")
|
|
264
|
+
_write_cff(root, "3.5.1")
|
|
265
|
+
_write_positioning(root, "3.5.1")
|
|
266
|
+
_write_docs(
|
|
267
|
+
root,
|
|
268
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)"],
|
|
269
|
+
zh_h2=["Token 用量", "語料庫導入 (v3.4.0+)"],
|
|
270
|
+
extra_version_strings=["3.4.0", "3.5.1"],
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class TestVersionConsistency(unittest.TestCase):
|
|
275
|
+
def test_all_aligned_passes(self) -> None:
|
|
276
|
+
with TemporaryDirectory() as tmp:
|
|
277
|
+
root = Path(tmp)
|
|
278
|
+
_write_aligned_fixture(root)
|
|
279
|
+
result = _run(root)
|
|
280
|
+
self.assertEqual(
|
|
281
|
+
result.returncode, 0,
|
|
282
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def test_all_aligned_v351_passes(self) -> None:
|
|
286
|
+
"""v3.5.1 suite (deep-research 2.9.1, academic-pipeline 3.5.1) must pass."""
|
|
287
|
+
with TemporaryDirectory() as tmp:
|
|
288
|
+
root = Path(tmp)
|
|
289
|
+
_write_aligned_fixture_v351(root)
|
|
290
|
+
result = _run(root)
|
|
291
|
+
self.assertEqual(
|
|
292
|
+
result.returncode, 0,
|
|
293
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def test_table_version_drift_fails(self) -> None:
|
|
297
|
+
"""Table lists deep-research v2.9.0 but SKILL.md says 2.8.0 — must fail."""
|
|
298
|
+
with TemporaryDirectory() as tmp:
|
|
299
|
+
root = Path(tmp)
|
|
300
|
+
_write_aligned_fixture(root)
|
|
301
|
+
_write_skill(root, "deep-research", "2.8.0")
|
|
302
|
+
result = _run(root)
|
|
303
|
+
self.assertEqual(result.returncode, 1)
|
|
304
|
+
self.assertIn("deep-research", result.stdout)
|
|
305
|
+
self.assertIn("2.9.0", result.stdout)
|
|
306
|
+
self.assertIn("2.8.0", result.stdout)
|
|
307
|
+
|
|
308
|
+
def test_suite_version_vs_changelog_drift_fails(self) -> None:
|
|
309
|
+
"""CLAUDE.md suite version != CHANGELOG latest entry — must fail."""
|
|
310
|
+
with TemporaryDirectory() as tmp:
|
|
311
|
+
root = Path(tmp)
|
|
312
|
+
_write_aligned_fixture(root)
|
|
313
|
+
_write_changelog(root, latest_version="3.4.0")
|
|
314
|
+
result = _run(root)
|
|
315
|
+
self.assertEqual(result.returncode, 1)
|
|
316
|
+
self.assertIn("3.5.0", result.stdout)
|
|
317
|
+
self.assertIn("3.4.0", result.stdout)
|
|
318
|
+
self.assertIn("CHANGELOG", result.stdout)
|
|
319
|
+
|
|
320
|
+
def test_plugin_json_version_drift_fails(self) -> None:
|
|
321
|
+
"""Invariant 4: .claude-plugin/plugin.json version != suite — must fail.
|
|
322
|
+
(Regression for the v3.10.0 release miss: plugin.json sat at the prior
|
|
323
|
+
version because no lint covered it.)"""
|
|
324
|
+
with TemporaryDirectory() as tmp:
|
|
325
|
+
root = Path(tmp)
|
|
326
|
+
_write_aligned_fixture(root) # suite 3.5.0
|
|
327
|
+
_write_plugin_manifests(root, "3.4.0") # drift both manifests down
|
|
328
|
+
result = _run(root)
|
|
329
|
+
self.assertEqual(result.returncode, 1)
|
|
330
|
+
self.assertIn("plugin.json", result.stdout)
|
|
331
|
+
self.assertIn("3.4.0", result.stdout)
|
|
332
|
+
self.assertIn("3.5.0", result.stdout)
|
|
333
|
+
|
|
334
|
+
def test_marketplace_version_drift_fails(self) -> None:
|
|
335
|
+
"""Invariant 4: marketplace.json plugins[].version != suite — must fail.
|
|
336
|
+
(Regression for marketplace.json silently sitting at 3.7.0 for releases.)"""
|
|
337
|
+
import json
|
|
338
|
+
with TemporaryDirectory() as tmp:
|
|
339
|
+
root = Path(tmp)
|
|
340
|
+
_write_aligned_fixture(root) # suite 3.5.0; plugin.json aligned at 3.5.0
|
|
341
|
+
# Drift ONLY marketplace, leaving plugin.json correct, to isolate it.
|
|
342
|
+
(root / ".claude-plugin" / "marketplace.json").write_text(
|
|
343
|
+
json.dumps({"name": "fixture",
|
|
344
|
+
"plugins": [{"name": "fixture", "version": "3.7.0"}]}),
|
|
345
|
+
encoding="utf-8",
|
|
346
|
+
)
|
|
347
|
+
result = _run(root)
|
|
348
|
+
self.assertEqual(result.returncode, 1)
|
|
349
|
+
self.assertIn("marketplace.json", result.stdout)
|
|
350
|
+
self.assertIn("3.7.0", result.stdout)
|
|
351
|
+
|
|
352
|
+
def test_pipeline_version_vs_suite_drift_fails(self) -> None:
|
|
353
|
+
"""academic-pipeline version in table must equal suite version."""
|
|
354
|
+
with TemporaryDirectory() as tmp:
|
|
355
|
+
root = Path(tmp)
|
|
356
|
+
_write_aligned_fixture(root)
|
|
357
|
+
# Drop pipeline to 3.4.0 in both table and SKILL.md, keep suite at 3.5.0.
|
|
358
|
+
# This isolates invariant 3 (pipeline tracks suite) from invariant 1
|
|
359
|
+
# (SKILL.md == table).
|
|
360
|
+
_write_skill(root, "academic-pipeline", "3.4.0")
|
|
361
|
+
_write_claude_md(
|
|
362
|
+
root,
|
|
363
|
+
suite_version="3.5.0",
|
|
364
|
+
table_rows=[
|
|
365
|
+
("deep-research", "2.9.0"),
|
|
366
|
+
("academic-paper", "3.1.0"),
|
|
367
|
+
("academic-paper-reviewer", "1.8.1"),
|
|
368
|
+
("academic-pipeline", "3.4.0"),
|
|
369
|
+
],
|
|
370
|
+
)
|
|
371
|
+
result = _run(root)
|
|
372
|
+
self.assertEqual(result.returncode, 1)
|
|
373
|
+
self.assertIn("academic-pipeline", result.stdout)
|
|
374
|
+
|
|
375
|
+
def test_skill_missing_frontmatter_fails(self) -> None:
|
|
376
|
+
with TemporaryDirectory() as tmp:
|
|
377
|
+
root = Path(tmp)
|
|
378
|
+
_write_aligned_fixture(root)
|
|
379
|
+
(root / "deep-research" / "SKILL.md").write_text(
|
|
380
|
+
"# deep-research\n\nNo frontmatter here.\n",
|
|
381
|
+
encoding="utf-8",
|
|
382
|
+
)
|
|
383
|
+
result = _run(root)
|
|
384
|
+
self.assertEqual(result.returncode, 1)
|
|
385
|
+
self.assertIn("deep-research", result.stdout)
|
|
386
|
+
|
|
387
|
+
def test_skill_listed_in_table_but_missing_on_disk_fails(self) -> None:
|
|
388
|
+
with TemporaryDirectory() as tmp:
|
|
389
|
+
root = Path(tmp)
|
|
390
|
+
_write_aligned_fixture(root)
|
|
391
|
+
import shutil
|
|
392
|
+
shutil.rmtree(root / "academic-paper-reviewer")
|
|
393
|
+
result = _run(root)
|
|
394
|
+
self.assertEqual(result.returncode, 1)
|
|
395
|
+
self.assertIn("academic-paper-reviewer", result.stdout)
|
|
396
|
+
|
|
397
|
+
def test_missing_suite_version_in_claude_md_fails(self) -> None:
|
|
398
|
+
with TemporaryDirectory() as tmp:
|
|
399
|
+
root = Path(tmp)
|
|
400
|
+
_write_aligned_fixture(root)
|
|
401
|
+
(root / ".claude" / "CLAUDE.md").write_text(
|
|
402
|
+
textwrap.dedent(
|
|
403
|
+
"""\
|
|
404
|
+
# Academic Research Skills
|
|
405
|
+
|
|
406
|
+
## Skills Overview
|
|
407
|
+
|
|
408
|
+
| Skill | Purpose | Key Modes |
|
|
409
|
+
|-------|---------|-----------|
|
|
410
|
+
| `deep-research` v2.9.0 | x | y |
|
|
411
|
+
| `academic-paper` v3.1.0 | x | y |
|
|
412
|
+
| `academic-paper-reviewer` v1.8.1 | x | y |
|
|
413
|
+
| `academic-pipeline` v3.5.0 | x | y |
|
|
414
|
+
|
|
415
|
+
## Version Info
|
|
416
|
+
- No suite version line here.
|
|
417
|
+
"""
|
|
418
|
+
),
|
|
419
|
+
encoding="utf-8",
|
|
420
|
+
)
|
|
421
|
+
result = _run(root)
|
|
422
|
+
self.assertEqual(result.returncode, 1)
|
|
423
|
+
self.assertIn("Suite version", result.stdout)
|
|
424
|
+
|
|
425
|
+
def test_four_segment_suite_vs_changelog_drift_fails(self) -> None:
|
|
426
|
+
"""Regression for #169: 4-segment hotfix versions (e.g. 3.9.4.1 vs 3.9.4.2)
|
|
427
|
+
must not be silently parsed as a shared 3-segment prefix.
|
|
428
|
+
|
|
429
|
+
Scenario: suite claims v3.9.4.2 but CHANGELOG's latest entry is v3.9.4.1.
|
|
430
|
+
Pre-fix behavior: both got truncated to "3.9.4" and the lint passed silently.
|
|
431
|
+
"""
|
|
432
|
+
with TemporaryDirectory() as tmp:
|
|
433
|
+
root = Path(tmp)
|
|
434
|
+
skills = [
|
|
435
|
+
("deep-research", "2.9.4"),
|
|
436
|
+
("academic-paper", "3.1.2"),
|
|
437
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
438
|
+
("academic-pipeline", "3.9.4.2"),
|
|
439
|
+
]
|
|
440
|
+
for name, ver in skills:
|
|
441
|
+
_write_skill(root, name, ver)
|
|
442
|
+
_write_claude_md(root, suite_version="3.9.4.2", table_rows=skills)
|
|
443
|
+
# Include the 3-segment ancestor so the pre-fix regex would still find
|
|
444
|
+
# *something* and silently report a passing 3.9.4 == 3.9.4 comparison.
|
|
445
|
+
_write_changelog(
|
|
446
|
+
root, latest_version="3.9.4.1", prior_versions=["3.9.4"],
|
|
447
|
+
)
|
|
448
|
+
result = _run(root)
|
|
449
|
+
self.assertEqual(
|
|
450
|
+
result.returncode, 1,
|
|
451
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
452
|
+
)
|
|
453
|
+
self.assertIn("3.9.4.2", result.stdout)
|
|
454
|
+
self.assertIn("3.9.4.1", result.stdout)
|
|
455
|
+
self.assertIn("CHANGELOG", result.stdout)
|
|
456
|
+
|
|
457
|
+
def test_five_segment_changelog_does_not_silently_fall_through(self) -> None:
|
|
458
|
+
"""Regression for dual-track review of #169: an N+1 segment latest
|
|
459
|
+
entry (e.g. 3.9.4.2.1) must not silently skip to the next valid
|
|
460
|
+
3-or-4 segment predecessor. Pre-fix CHANGELOG_ENTRY_RE failed on
|
|
461
|
+
the 5-segment heading and `re.search` fell through to a predecessor;
|
|
462
|
+
if that predecessor happened to equal the suite version, the lint
|
|
463
|
+
reported PASS even though the actual latest release was a different
|
|
464
|
+
version."""
|
|
465
|
+
with TemporaryDirectory() as tmp:
|
|
466
|
+
root = Path(tmp)
|
|
467
|
+
skills = [
|
|
468
|
+
("deep-research", "2.9.4"),
|
|
469
|
+
("academic-paper", "3.1.2"),
|
|
470
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
471
|
+
("academic-pipeline", "3.9.4.2"),
|
|
472
|
+
]
|
|
473
|
+
for name, ver in skills:
|
|
474
|
+
_write_skill(root, name, ver)
|
|
475
|
+
_write_claude_md(root, suite_version="3.9.4.2", table_rows=skills)
|
|
476
|
+
_write_changelog(
|
|
477
|
+
root, latest_version="3.9.4.2.1", prior_versions=["3.9.4.2"],
|
|
478
|
+
)
|
|
479
|
+
result = _run(root)
|
|
480
|
+
self.assertEqual(
|
|
481
|
+
result.returncode, 1,
|
|
482
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
483
|
+
)
|
|
484
|
+
self.assertIn("3.9.4.2.1", result.stdout)
|
|
485
|
+
# Either path is acceptable surfacing: either the suite-vs-CHANGELOG
|
|
486
|
+
# mismatch (if the new 5-seg token is itself accepted as canonical
|
|
487
|
+
# under the broadened validator) or an invalid-token report.
|
|
488
|
+
self.assertTrue(
|
|
489
|
+
"does not match CHANGELOG latest entry" in result.stdout
|
|
490
|
+
or "canonical" in result.stdout,
|
|
491
|
+
msg=f"expected either drift or invalid-token surface: {result.stdout!r}",
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
def test_invalid_table_row_token_is_reported(self) -> None:
|
|
495
|
+
"""Regression for dual-track review of #169: a table row carrying a
|
|
496
|
+
non-canonical version token (e.g. v3.9.4.2-alpha or v3.9.4.2.1) must
|
|
497
|
+
surface as an error. Pre-fix: TABLE_ROW_RE failed and the row silently
|
|
498
|
+
vanished from table_versions, so invariant 3 (pipeline tracks suite)
|
|
499
|
+
was skipped because `pipeline_in_table` ended up None."""
|
|
500
|
+
with TemporaryDirectory() as tmp:
|
|
501
|
+
root = Path(tmp)
|
|
502
|
+
skills_on_disk = [
|
|
503
|
+
("deep-research", "2.9.4"),
|
|
504
|
+
("academic-paper", "3.1.2"),
|
|
505
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
506
|
+
("academic-pipeline", "3.9.4.2"),
|
|
507
|
+
]
|
|
508
|
+
for name, ver in skills_on_disk:
|
|
509
|
+
_write_skill(root, name, ver)
|
|
510
|
+
table_rows_with_junk = [
|
|
511
|
+
("deep-research", "2.9.4"),
|
|
512
|
+
("academic-paper", "3.1.2"),
|
|
513
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
514
|
+
("academic-pipeline", "3.9.4.2-alpha"), # invalid token
|
|
515
|
+
]
|
|
516
|
+
_write_claude_md(
|
|
517
|
+
root, suite_version="3.9.4.2", table_rows=table_rows_with_junk
|
|
518
|
+
)
|
|
519
|
+
_write_changelog(
|
|
520
|
+
root, latest_version="3.9.4.2", prior_versions=["3.9.4"],
|
|
521
|
+
)
|
|
522
|
+
result = _run(root)
|
|
523
|
+
self.assertEqual(
|
|
524
|
+
result.returncode, 1,
|
|
525
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
526
|
+
)
|
|
527
|
+
self.assertIn("academic-pipeline", result.stdout)
|
|
528
|
+
self.assertIn("3.9.4.2-alpha", result.stdout)
|
|
529
|
+
self.assertIn("canonical", result.stdout)
|
|
530
|
+
|
|
531
|
+
def test_invalid_suite_token_is_reported(self) -> None:
|
|
532
|
+
"""Regression for dual-track review of #169: a non-canonical suite
|
|
533
|
+
version token (e.g. 3.9.4.2-alpha) must surface as an error rather
|
|
534
|
+
than being partially captured as 3.9.4.2 via prefix match."""
|
|
535
|
+
with TemporaryDirectory() as tmp:
|
|
536
|
+
root = Path(tmp)
|
|
537
|
+
skills = [
|
|
538
|
+
("deep-research", "2.9.4"),
|
|
539
|
+
("academic-paper", "3.1.2"),
|
|
540
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
541
|
+
("academic-pipeline", "3.9.4.2"),
|
|
542
|
+
]
|
|
543
|
+
for name, ver in skills:
|
|
544
|
+
_write_skill(root, name, ver)
|
|
545
|
+
_write_claude_md(
|
|
546
|
+
root, suite_version="3.9.4.2-alpha", table_rows=skills
|
|
547
|
+
)
|
|
548
|
+
_write_changelog(
|
|
549
|
+
root, latest_version="3.9.4.2", prior_versions=["3.9.4"],
|
|
550
|
+
)
|
|
551
|
+
result = _run(root)
|
|
552
|
+
self.assertEqual(
|
|
553
|
+
result.returncode, 1,
|
|
554
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
555
|
+
)
|
|
556
|
+
self.assertIn("3.9.4.2-alpha", result.stdout)
|
|
557
|
+
self.assertIn("canonical", result.stdout)
|
|
558
|
+
|
|
559
|
+
def test_four_segment_table_row_drift_fails(self) -> None:
|
|
560
|
+
"""Regression for #169: the Skills table row regex must also see the 4th
|
|
561
|
+
segment, so a pipeline row v3.9.4.1 vs suite version 3.9.4.2 is caught."""
|
|
562
|
+
with TemporaryDirectory() as tmp:
|
|
563
|
+
root = Path(tmp)
|
|
564
|
+
# Pipeline SKILL.md and CHANGELOG both at 3.9.4.2, but the table row
|
|
565
|
+
# in CLAUDE.md still says v3.9.4.1 (forgot to bump one place).
|
|
566
|
+
skills_on_disk = [
|
|
567
|
+
("deep-research", "2.9.4"),
|
|
568
|
+
("academic-paper", "3.1.2"),
|
|
569
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
570
|
+
("academic-pipeline", "3.9.4.2"),
|
|
571
|
+
]
|
|
572
|
+
for name, ver in skills_on_disk:
|
|
573
|
+
_write_skill(root, name, ver)
|
|
574
|
+
table_rows_drifted = [
|
|
575
|
+
("deep-research", "2.9.4"),
|
|
576
|
+
("academic-paper", "3.1.2"),
|
|
577
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
578
|
+
("academic-pipeline", "3.9.4.1"), # drift inside the 4th segment
|
|
579
|
+
]
|
|
580
|
+
_write_claude_md(
|
|
581
|
+
root, suite_version="3.9.4.2", table_rows=table_rows_drifted
|
|
582
|
+
)
|
|
583
|
+
_write_changelog(
|
|
584
|
+
root, latest_version="3.9.4.2", prior_versions=["3.9.4"],
|
|
585
|
+
)
|
|
586
|
+
result = _run(root)
|
|
587
|
+
self.assertEqual(
|
|
588
|
+
result.returncode, 1,
|
|
589
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
590
|
+
)
|
|
591
|
+
self.assertIn("academic-pipeline", result.stdout)
|
|
592
|
+
self.assertIn("3.9.4.1", result.stdout)
|
|
593
|
+
self.assertIn("3.9.4.2", result.stdout)
|
|
594
|
+
|
|
595
|
+
def test_five_segment_token_rejected_as_non_canonical(self) -> None:
|
|
596
|
+
"""Regression for #178: post-ship codex review of PR #173 flagged that
|
|
597
|
+
SEMVER_STRICT_RE used `{2,}` (3 or more dot segments) with no upper
|
|
598
|
+
bound, so a 5-segment token like 3.9.4.2.1 passed the canonical check
|
|
599
|
+
even though the file-level docstrings and #173 design defined canonical
|
|
600
|
+
as N.N.N or N.N.N.N. If a 5-segment typo were copied consistently to
|
|
601
|
+
CLAUDE.md, CHANGELOG.md, and the pipeline table, the lint would have
|
|
602
|
+
passed despite the malformed shape. Cap to {2,3}: 4 segments max."""
|
|
603
|
+
with TemporaryDirectory() as tmp:
|
|
604
|
+
root = Path(tmp)
|
|
605
|
+
# All four sources self-consistent on a 5-segment typo. Pre-fix:
|
|
606
|
+
# all three regexes captured "3.9.4.2.1" verbatim, _is_strict_semver
|
|
607
|
+
# returned True (matched {2,} = 3 or more segments), and every
|
|
608
|
+
# invariant compared two identical strings — lint reported PASS
|
|
609
|
+
# even though the shape was malformed. Post-fix: SEMVER_STRICT_RE
|
|
610
|
+
# rejects 5+ segments, so the suite, table row, and CHANGELOG
|
|
611
|
+
# entry all surface as "canonical" violations.
|
|
612
|
+
skills_5seg = [
|
|
613
|
+
("deep-research", "2.9.4"),
|
|
614
|
+
("academic-paper", "3.1.2"),
|
|
615
|
+
("academic-paper-reviewer", "1.9.1"),
|
|
616
|
+
("academic-pipeline", "3.9.4.2.1"),
|
|
617
|
+
]
|
|
618
|
+
for name, ver in skills_5seg:
|
|
619
|
+
_write_skill(root, name, ver)
|
|
620
|
+
_write_claude_md(
|
|
621
|
+
root, suite_version="3.9.4.2.1", table_rows=skills_5seg
|
|
622
|
+
)
|
|
623
|
+
_write_changelog(
|
|
624
|
+
root, latest_version="3.9.4.2.1", prior_versions=["3.9.4.2"],
|
|
625
|
+
)
|
|
626
|
+
result = _run(root)
|
|
627
|
+
self.assertEqual(
|
|
628
|
+
result.returncode, 1,
|
|
629
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
630
|
+
)
|
|
631
|
+
self.assertIn("3.9.4.2.1", result.stdout)
|
|
632
|
+
self.assertIn("canonical", result.stdout)
|
|
633
|
+
|
|
634
|
+
# ── Invariant 5: README version badge tracks the suite version ──────────
|
|
635
|
+
def test_readme_badge_drift_fails(self) -> None:
|
|
636
|
+
"""README shields.io version badge drifts below suite version — must fail."""
|
|
637
|
+
with TemporaryDirectory() as tmp:
|
|
638
|
+
root = Path(tmp)
|
|
639
|
+
_write_aligned_fixture(root)
|
|
640
|
+
_write_readme(root, "3.4.0") # badge stale vs suite 3.5.0
|
|
641
|
+
result = _run(root)
|
|
642
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
643
|
+
self.assertIn("README", result.stdout)
|
|
644
|
+
self.assertIn("3.4.0", result.stdout)
|
|
645
|
+
|
|
646
|
+
def test_readme_missing_fails(self) -> None:
|
|
647
|
+
with TemporaryDirectory() as tmp:
|
|
648
|
+
root = Path(tmp)
|
|
649
|
+
_write_aligned_fixture(root)
|
|
650
|
+
(root / "README.md").unlink()
|
|
651
|
+
result = _run(root)
|
|
652
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
653
|
+
self.assertIn("README", result.stdout)
|
|
654
|
+
|
|
655
|
+
# ── Invariant 6: docs/ must not cite a version above the suite version ──
|
|
656
|
+
def test_docs_future_version_fails(self) -> None:
|
|
657
|
+
"""docs/ references v9.9.9 (above suite 3.5.0) — must fail."""
|
|
658
|
+
with TemporaryDirectory() as tmp:
|
|
659
|
+
root = Path(tmp)
|
|
660
|
+
_write_aligned_fixture(root)
|
|
661
|
+
_write_docs(
|
|
662
|
+
root,
|
|
663
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)"],
|
|
664
|
+
zh_h2=["Token 用量", "語料庫導入 (v3.4.0+)"],
|
|
665
|
+
extra_version_strings=["9.9.9"], # future / nonexistent
|
|
666
|
+
)
|
|
667
|
+
result = _run(root)
|
|
668
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
669
|
+
self.assertIn("9.9.9", result.stdout)
|
|
670
|
+
|
|
671
|
+
def test_docs_at_suite_version_passes_inv6(self) -> None:
|
|
672
|
+
"""A docs version string EQUAL to the suite version is allowed (<=)."""
|
|
673
|
+
with TemporaryDirectory() as tmp:
|
|
674
|
+
root = Path(tmp)
|
|
675
|
+
_write_aligned_fixture(root)
|
|
676
|
+
_write_docs(
|
|
677
|
+
root,
|
|
678
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)"],
|
|
679
|
+
zh_h2=["Token 用量", "語料庫導入 (v3.4.0+)"],
|
|
680
|
+
extra_version_strings=["3.5.0"], # == suite, must be OK
|
|
681
|
+
)
|
|
682
|
+
result = _run(root)
|
|
683
|
+
self.assertEqual(result.returncode, 0, msg=f"stdout={result.stdout!r}")
|
|
684
|
+
|
|
685
|
+
def test_docs_superpowers_future_version_exempt_inv6(self) -> None:
|
|
686
|
+
"""docs/superpowers/ holds skill specs/plans that intentionally plan the
|
|
687
|
+
NEXT release; a future version there is exempt (must PASS). A non-aligned
|
|
688
|
+
future version anywhere ELSE in docs/ still fails — proves the carve-out
|
|
689
|
+
is scoped to superpowers/, not a blanket disable."""
|
|
690
|
+
with TemporaryDirectory() as tmp:
|
|
691
|
+
root = Path(tmp)
|
|
692
|
+
_write_aligned_fixture(root) # suite 3.5.0
|
|
693
|
+
sp = root / "docs" / "superpowers" / "plans"
|
|
694
|
+
sp.mkdir(parents=True, exist_ok=True)
|
|
695
|
+
(sp / "next-release-plan.md").write_text(
|
|
696
|
+
"# Plan\n\nBump suite to v9.9.9 in this release.\n", encoding="utf-8"
|
|
697
|
+
)
|
|
698
|
+
result = _run(root)
|
|
699
|
+
self.assertEqual(
|
|
700
|
+
result.returncode, 0,
|
|
701
|
+
msg=f"superpowers/ future ref should be exempt; stdout={result.stdout!r}",
|
|
702
|
+
)
|
|
703
|
+
# control: same future token under a published doc path still fails
|
|
704
|
+
(root / "docs" / "OTHER.md").write_text(
|
|
705
|
+
"# Other\n\nSee v9.9.9 here.\n", encoding="utf-8"
|
|
706
|
+
)
|
|
707
|
+
result2 = _run(root)
|
|
708
|
+
self.assertEqual(result2.returncode, 1, msg=f"stdout={result2.stdout!r}")
|
|
709
|
+
self.assertIn("9.9.9", result2.stdout)
|
|
710
|
+
|
|
711
|
+
# ── Invariant 7: en<->zh-TW version-bearing H2 + version-string parity ──
|
|
712
|
+
def test_zhtw_version_bearing_heading_missing_fails(self) -> None:
|
|
713
|
+
"""en has '(v3.4.0+)' heading; zh-TW drops it — must fail."""
|
|
714
|
+
with TemporaryDirectory() as tmp:
|
|
715
|
+
root = Path(tmp)
|
|
716
|
+
_write_aligned_fixture(root)
|
|
717
|
+
_write_docs(
|
|
718
|
+
root,
|
|
719
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)"],
|
|
720
|
+
zh_h2=["Token 用量", "語料庫導入"], # version tag dropped
|
|
721
|
+
extra_version_strings=["3.4.0"],
|
|
722
|
+
)
|
|
723
|
+
result = _run(root)
|
|
724
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
725
|
+
self.assertIn("zh-TW", result.stdout)
|
|
726
|
+
|
|
727
|
+
def test_zhtw_version_bearing_heading_drift_fails(self) -> None:
|
|
728
|
+
"""en heading says v3.4.0, zh-TW says v3.3.0 — version drift, must fail."""
|
|
729
|
+
with TemporaryDirectory() as tmp:
|
|
730
|
+
root = Path(tmp)
|
|
731
|
+
_write_aligned_fixture(root)
|
|
732
|
+
_write_docs(
|
|
733
|
+
root,
|
|
734
|
+
en_h2=["Token usage", "Corpus ingestion (v3.4.0+)"],
|
|
735
|
+
zh_h2=["Token 用量", "語料庫導入 (v3.3.0+)"], # drift
|
|
736
|
+
extra_version_strings=["3.4.0"],
|
|
737
|
+
)
|
|
738
|
+
result = _run(root)
|
|
739
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
740
|
+
|
|
741
|
+
def test_zhtw_plain_heading_asymmetry_allowed(self) -> None:
|
|
742
|
+
"""en may have extra PLAIN (no-version) H2 the zh-TW lacks — must pass."""
|
|
743
|
+
with TemporaryDirectory() as tmp:
|
|
744
|
+
root = Path(tmp)
|
|
745
|
+
_write_aligned_fixture(root)
|
|
746
|
+
# aligned fixture already has en-only 'Extra EN-only section'; assert it passes
|
|
747
|
+
result = _run(root)
|
|
748
|
+
self.assertEqual(result.returncode, 0, msg=f"stdout={result.stdout!r}")
|
|
749
|
+
|
|
750
|
+
def test_docs_two_segment_version_is_skipped(self) -> None:
|
|
751
|
+
"""inv 6 gates only on full N.N.N tokens; a 2-segment `v9.9` in docs is
|
|
752
|
+
not a release token this lint adjudicates, so it must NOT fail even
|
|
753
|
+
though 9.9 > suite 3.5.0 (review test-gap)."""
|
|
754
|
+
with TemporaryDirectory() as tmp:
|
|
755
|
+
root = Path(tmp)
|
|
756
|
+
_write_aligned_fixture(root)
|
|
757
|
+
docs = root / "docs"
|
|
758
|
+
(docs / "PERFORMANCE.md").write_text(
|
|
759
|
+
"# Performance\n\n## Token usage\n\nSee v9.9 milestone.\n"
|
|
760
|
+
"## Corpus ingestion (v3.4.0+)\n\nbody\n",
|
|
761
|
+
encoding="utf-8",
|
|
762
|
+
)
|
|
763
|
+
(docs / "PERFORMANCE.zh-TW.md").write_text(
|
|
764
|
+
"# 效能\n\n## Token 用量\n\n看 v9.9 里程碑。\n"
|
|
765
|
+
"## 語料庫導入 (v3.4.0+)\n\n內文\n",
|
|
766
|
+
encoding="utf-8",
|
|
767
|
+
)
|
|
768
|
+
result = _run(root)
|
|
769
|
+
self.assertEqual(result.returncode, 0, msg=f"stdout={result.stdout!r}")
|
|
770
|
+
|
|
771
|
+
def test_docs_prerelease_token_is_skipped(self) -> None:
|
|
772
|
+
"""`v3.12.0-alpha` (above suite) must NOT partial-match to 3.12.0 and
|
|
773
|
+
fail — non-canonical tokens are dropped entirely (review finding)."""
|
|
774
|
+
with TemporaryDirectory() as tmp:
|
|
775
|
+
root = Path(tmp)
|
|
776
|
+
_write_aligned_fixture(root)
|
|
777
|
+
(root / "docs" / "PERFORMANCE.md").write_text(
|
|
778
|
+
"# Performance\n\n## Token usage\n\nWork toward v3.12.0-alpha.\n"
|
|
779
|
+
"## Corpus ingestion (v3.4.0+)\n\nbody\n",
|
|
780
|
+
encoding="utf-8",
|
|
781
|
+
)
|
|
782
|
+
(root / "docs" / "PERFORMANCE.zh-TW.md").write_text(
|
|
783
|
+
"# 效能\n\n## Token 用量\n\n邁向 v3.12.0-alpha。\n"
|
|
784
|
+
"## 語料庫導入 (v3.4.0+)\n\n內文\n",
|
|
785
|
+
encoding="utf-8",
|
|
786
|
+
)
|
|
787
|
+
result = _run(root)
|
|
788
|
+
self.assertEqual(result.returncode, 0, msg=f"stdout={result.stdout!r}")
|
|
789
|
+
|
|
790
|
+
def test_zhtw_no_english_sibling_fails(self) -> None:
|
|
791
|
+
"""A standalone docs/*.zh-TW.md with no .md sibling — must fail (review test-gap)."""
|
|
792
|
+
with TemporaryDirectory() as tmp:
|
|
793
|
+
root = Path(tmp)
|
|
794
|
+
_write_aligned_fixture(root)
|
|
795
|
+
(root / "docs" / "ORPHAN.zh-TW.md").write_text(
|
|
796
|
+
"# 孤兒\n\n## 一節 (v3.4.0+)\n\n內文\n", encoding="utf-8"
|
|
797
|
+
)
|
|
798
|
+
result = _run(root)
|
|
799
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
800
|
+
self.assertIn("sibling", result.stdout)
|
|
801
|
+
|
|
802
|
+
def test_zhtw_duplicate_same_version_heading_drop_fails(self) -> None:
|
|
803
|
+
"""en has TWO H2s tagged v3.4.0; zh-TW translates only one. Multiset
|
|
804
|
+
comparison must catch the dropped duplicate (review finding; a
|
|
805
|
+
{version: heading} dict silently collapsed this and passed)."""
|
|
806
|
+
with TemporaryDirectory() as tmp:
|
|
807
|
+
root = Path(tmp)
|
|
808
|
+
_write_aligned_fixture(root)
|
|
809
|
+
(root / "docs" / "PERFORMANCE.md").write_text(
|
|
810
|
+
"# Performance\n\n## Feature A (v3.4.0+)\n\nbody\n"
|
|
811
|
+
"## Feature B (v3.4.0+)\n\nbody\n",
|
|
812
|
+
encoding="utf-8",
|
|
813
|
+
)
|
|
814
|
+
(root / "docs" / "PERFORMANCE.zh-TW.md").write_text(
|
|
815
|
+
"# 效能\n\n## 功能 A (v3.4.0+)\n\n內文\n", # only ONE translated
|
|
816
|
+
encoding="utf-8",
|
|
817
|
+
)
|
|
818
|
+
result = _run(root)
|
|
819
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
820
|
+
self.assertIn("3.4.0", result.stdout)
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
class TestAgentCountClaim(unittest.TestCase):
|
|
824
|
+
"""Invariant 8 (#414): plugin.json description "N-agent" claim equals the
|
|
825
|
+
tree's unique *_agent.md count (symlinks resolved, not double-counted)."""
|
|
826
|
+
|
|
827
|
+
@staticmethod
|
|
828
|
+
def _write_agents(root: Path, names: list[str]) -> None:
|
|
829
|
+
agents_dir = root / "deep-research" / "agents"
|
|
830
|
+
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
831
|
+
for name in names:
|
|
832
|
+
(agents_dir / f"{name}_agent.md").write_text(
|
|
833
|
+
f"# {name}\n", encoding="utf-8"
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
def _claim_case(self, description: str) -> "subprocess.CompletedProcess":
|
|
837
|
+
"""Run the lint against a two-agent fixture tree whose plugin.json
|
|
838
|
+
carries the given description."""
|
|
839
|
+
with TemporaryDirectory() as tmp:
|
|
840
|
+
root = Path(tmp)
|
|
841
|
+
_write_aligned_fixture(root)
|
|
842
|
+
_write_plugin_manifests(root, "3.5.0", description=description)
|
|
843
|
+
self._write_agents(root, ["alpha", "beta"])
|
|
844
|
+
return _run(root)
|
|
845
|
+
|
|
846
|
+
def test_agent_claim_drift_fails(self) -> None:
|
|
847
|
+
result = self._claim_case("fixture, 3-agent ensemble, more")
|
|
848
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
849
|
+
self.assertIn("agent count of 3", result.stdout)
|
|
850
|
+
self.assertIn("2", result.stdout)
|
|
851
|
+
|
|
852
|
+
def test_prompt_roles_claim_drift_fails(self) -> None:
|
|
853
|
+
"""#753: the "N prompt roles" spelling binds to the same tree count
|
|
854
|
+
as the legacy "N-agent" spelling."""
|
|
855
|
+
result = self._claim_case("fixture, 3 prompt roles, more")
|
|
856
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
857
|
+
self.assertIn("agent count of 3", result.stdout)
|
|
858
|
+
|
|
859
|
+
def test_prompt_roles_claim_matching_passes(self) -> None:
|
|
860
|
+
result = self._claim_case("fixture, 2 prompt roles, more")
|
|
861
|
+
self.assertEqual(
|
|
862
|
+
result.returncode, 0,
|
|
863
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
def test_agent_claim_matching_passes(self) -> None:
|
|
867
|
+
result = self._claim_case("fixture, 2-agent ensemble, more")
|
|
868
|
+
self.assertEqual(
|
|
869
|
+
result.returncode, 0,
|
|
870
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
def test_every_count_token_checked_not_just_first(self) -> None:
|
|
874
|
+
"""#753: finditer semantics — a correct first token cannot shadow a
|
|
875
|
+
drifted second one."""
|
|
876
|
+
result = self._claim_case("fixture, 2-agent core, 5 prompt roles")
|
|
877
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
878
|
+
self.assertIn("agent count of 5", result.stdout)
|
|
879
|
+
|
|
880
|
+
def test_agent_claim_symlink_alias_not_double_counted(self) -> None:
|
|
881
|
+
"""Legacy/transition pin: a symlink alias in the plugin-root agents/
|
|
882
|
+
dir (the pre-#413 pattern) still counts once — the whole root
|
|
883
|
+
agents/ mirror is excluded from the count regardless of file kind."""
|
|
884
|
+
with TemporaryDirectory() as tmp:
|
|
885
|
+
root = Path(tmp)
|
|
886
|
+
_write_aligned_fixture(root)
|
|
887
|
+
_write_plugin_manifests(
|
|
888
|
+
root, "3.5.0", description="fixture, 2-agent ensemble, more"
|
|
889
|
+
)
|
|
890
|
+
self._write_agents(root, ["alpha", "beta"])
|
|
891
|
+
link_dir = root / "agents"
|
|
892
|
+
link_dir.mkdir()
|
|
893
|
+
(link_dir / "alpha_agent.md").symlink_to(
|
|
894
|
+
root / "deep-research" / "agents" / "alpha_agent.md"
|
|
895
|
+
)
|
|
896
|
+
result = _run(root)
|
|
897
|
+
self.assertEqual(
|
|
898
|
+
result.returncode, 0,
|
|
899
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
def test_agent_claim_materialized_mirror_not_double_counted(self) -> None:
|
|
903
|
+
"""#413: the plugin-root agents/ mirror holds REAL byte-identical
|
|
904
|
+
copies (symlinks broke Windows checkouts / zip installs), so resolve()
|
|
905
|
+
no longer dedups them. The mirror dir is excluded from the count —
|
|
906
|
+
it is an alias surface pinned byte-identical to its deep-research
|
|
907
|
+
sources by check_agents_mirror_sync.py, never a source of new
|
|
908
|
+
agents."""
|
|
909
|
+
with TemporaryDirectory() as tmp:
|
|
910
|
+
root = Path(tmp)
|
|
911
|
+
_write_aligned_fixture(root)
|
|
912
|
+
_write_plugin_manifests(
|
|
913
|
+
root, "3.5.0", description="fixture, 2-agent ensemble, more"
|
|
914
|
+
)
|
|
915
|
+
self._write_agents(root, ["alpha", "beta"])
|
|
916
|
+
mirror_dir = root / "agents"
|
|
917
|
+
mirror_dir.mkdir()
|
|
918
|
+
src = root / "deep-research" / "agents" / "alpha_agent.md"
|
|
919
|
+
(mirror_dir / "alpha_agent.md").write_bytes(src.read_bytes())
|
|
920
|
+
result = _run(root)
|
|
921
|
+
self.assertEqual(
|
|
922
|
+
result.returncode, 0,
|
|
923
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
def test_no_agent_claim_skips(self) -> None:
|
|
927
|
+
"""A description without an N-agent token is not gated (the claim is
|
|
928
|
+
optional; only a stated number must be true)."""
|
|
929
|
+
with TemporaryDirectory() as tmp:
|
|
930
|
+
root = Path(tmp)
|
|
931
|
+
_write_aligned_fixture(root)
|
|
932
|
+
_write_plugin_manifests(
|
|
933
|
+
root, "3.5.0", description="fixture without a count claim"
|
|
934
|
+
)
|
|
935
|
+
self._write_agents(root, ["alpha", "beta"])
|
|
936
|
+
result = _run(root)
|
|
937
|
+
self.assertEqual(
|
|
938
|
+
result.returncode, 0,
|
|
939
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
class TestChangelogBodyLength(unittest.TestCase):
|
|
944
|
+
"""Invariant 9 (#487): the latest CHANGELOG entry's body must be >= 100
|
|
945
|
+
characters — a bare heading (or a stub line) is not release notes."""
|
|
946
|
+
|
|
947
|
+
def test_short_body_fails(self) -> None:
|
|
948
|
+
with TemporaryDirectory() as tmp:
|
|
949
|
+
root = Path(tmp)
|
|
950
|
+
_write_aligned_fixture(root)
|
|
951
|
+
_write_changelog(root, latest_version="3.5.0", latest_body="- stub\n")
|
|
952
|
+
result = _run(root)
|
|
953
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
954
|
+
self.assertIn("body", result.stdout)
|
|
955
|
+
self.assertIn("100", result.stdout)
|
|
956
|
+
|
|
957
|
+
def test_empty_body_fails(self) -> None:
|
|
958
|
+
with TemporaryDirectory() as tmp:
|
|
959
|
+
root = Path(tmp)
|
|
960
|
+
_write_aligned_fixture(root)
|
|
961
|
+
_write_changelog(root, latest_version="3.5.0", latest_body="\n")
|
|
962
|
+
result = _run(root)
|
|
963
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
964
|
+
self.assertIn("body", result.stdout)
|
|
965
|
+
|
|
966
|
+
def test_prior_entry_body_not_gated(self) -> None:
|
|
967
|
+
"""Only the LATEST entry is gated — historical entries may be terse."""
|
|
968
|
+
with TemporaryDirectory() as tmp:
|
|
969
|
+
root = Path(tmp)
|
|
970
|
+
_write_aligned_fixture(root)
|
|
971
|
+
_write_changelog(root, latest_version="3.5.0", prior_versions=["3.4.0"])
|
|
972
|
+
result = _run(root)
|
|
973
|
+
self.assertEqual(
|
|
974
|
+
result.returncode, 0,
|
|
975
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
976
|
+
)
|
|
977
|
+
|
|
978
|
+
def test_fenced_h2_inside_body_not_a_terminator(self) -> None:
|
|
979
|
+
"""A '## ' line inside a fenced code block must NOT truncate the body
|
|
980
|
+
(codex P2-1): the entry body ends at the next RELEASE heading, not any
|
|
981
|
+
markdown H2. Otherwise a code sample in the release notes false-fails
|
|
982
|
+
invariant 9."""
|
|
983
|
+
with TemporaryDirectory() as tmp:
|
|
984
|
+
root = Path(tmp)
|
|
985
|
+
_write_aligned_fixture(root)
|
|
986
|
+
body = (
|
|
987
|
+
"### Added\n\n"
|
|
988
|
+
"```md\n"
|
|
989
|
+
"## not a release heading — just a code sample\n"
|
|
990
|
+
"```\n\n"
|
|
991
|
+
"Real release notes continue here with more than one hundred "
|
|
992
|
+
"characters of substantive text so invariant 9 is satisfied by "
|
|
993
|
+
"the full body, not the truncated fence prefix.\n"
|
|
994
|
+
)
|
|
995
|
+
_write_changelog(
|
|
996
|
+
root, latest_version="3.5.0", latest_body=body,
|
|
997
|
+
prior_versions=["3.4.0"],
|
|
998
|
+
)
|
|
999
|
+
result = _run(root)
|
|
1000
|
+
self.assertEqual(
|
|
1001
|
+
result.returncode, 0,
|
|
1002
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
def test_fenced_bracketed_h2_not_a_terminator(self) -> None:
|
|
1006
|
+
"""A fenced '## [example]' — which even LOOKS like a release heading —
|
|
1007
|
+
must not truncate the body either (codex re-review): the body ends at
|
|
1008
|
+
the next real release heading, past any fenced content."""
|
|
1009
|
+
with TemporaryDirectory() as tmp:
|
|
1010
|
+
root = Path(tmp)
|
|
1011
|
+
_write_aligned_fixture(root)
|
|
1012
|
+
body = (
|
|
1013
|
+
"### Added\n\n"
|
|
1014
|
+
"```md\n"
|
|
1015
|
+
"## [example] - 2020-01-01 — a code sample, not a real entry\n"
|
|
1016
|
+
"```\n\n"
|
|
1017
|
+
"Real release notes continue here with more than one hundred "
|
|
1018
|
+
"characters of substantive text so invariant 9 is satisfied by "
|
|
1019
|
+
"the full body, not the truncated fence prefix.\n"
|
|
1020
|
+
)
|
|
1021
|
+
_write_changelog(
|
|
1022
|
+
root, latest_version="3.5.0", latest_body=body,
|
|
1023
|
+
prior_versions=["3.4.0"],
|
|
1024
|
+
)
|
|
1025
|
+
result = _run(root)
|
|
1026
|
+
self.assertEqual(
|
|
1027
|
+
result.returncode, 0,
|
|
1028
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
class TestLastUpdatedFreshness(unittest.TestCase):
|
|
1033
|
+
"""Invariant 10 (#487): .claude/CLAUDE.md "Last Updated" must lie within
|
|
1034
|
+
±7 days of the latest CHANGELOG entry's date (deterministic baseline —
|
|
1035
|
+
re-running the lint later cannot flip the result)."""
|
|
1036
|
+
|
|
1037
|
+
def test_stale_last_updated_fails(self) -> None:
|
|
1038
|
+
"""8 days after the CHANGELOG date (2026-04-22) is out of the window."""
|
|
1039
|
+
with TemporaryDirectory() as tmp:
|
|
1040
|
+
root = Path(tmp)
|
|
1041
|
+
_write_aligned_fixture(root, last_updated="2026-04-30")
|
|
1042
|
+
result = _run(root)
|
|
1043
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1044
|
+
self.assertIn("Last Updated", result.stdout)
|
|
1045
|
+
|
|
1046
|
+
def test_boundary_seven_days_passes(self) -> None:
|
|
1047
|
+
with TemporaryDirectory() as tmp:
|
|
1048
|
+
root = Path(tmp)
|
|
1049
|
+
_write_aligned_fixture(root, last_updated="2026-04-29")
|
|
1050
|
+
result = _run(root)
|
|
1051
|
+
self.assertEqual(
|
|
1052
|
+
result.returncode, 0,
|
|
1053
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
def test_missing_last_updated_fails(self) -> None:
|
|
1057
|
+
with TemporaryDirectory() as tmp:
|
|
1058
|
+
root = Path(tmp)
|
|
1059
|
+
_write_aligned_fixture(root, last_updated=None)
|
|
1060
|
+
result = _run(root)
|
|
1061
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1062
|
+
self.assertIn("Last Updated", result.stdout)
|
|
1063
|
+
|
|
1064
|
+
def test_changelog_missing_date_fails(self) -> None:
|
|
1065
|
+
"""The latest entry carrying no date breaks the freshness baseline."""
|
|
1066
|
+
with TemporaryDirectory() as tmp:
|
|
1067
|
+
root = Path(tmp)
|
|
1068
|
+
_write_aligned_fixture(root)
|
|
1069
|
+
# Strip only the date off the aligned CHANGELOG heading, keeping
|
|
1070
|
+
# the compliant body so invariant 9 doesn't also fire.
|
|
1071
|
+
changelog = root / "CHANGELOG.md"
|
|
1072
|
+
changelog.write_text(
|
|
1073
|
+
changelog.read_text(encoding="utf-8").replace(
|
|
1074
|
+
"## [3.5.0] - 2026-04-22", "## [3.5.0]"
|
|
1075
|
+
),
|
|
1076
|
+
encoding="utf-8",
|
|
1077
|
+
)
|
|
1078
|
+
result = _run(root)
|
|
1079
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1080
|
+
self.assertIn("date", result.stdout)
|
|
1081
|
+
|
|
1082
|
+
def test_impossible_changelog_date_reports_not_crashes(self) -> None:
|
|
1083
|
+
"""A syntactically-shaped but impossible CHANGELOG date (2026-02-30)
|
|
1084
|
+
must produce a lint error, never a traceback (codex P2-2)."""
|
|
1085
|
+
with TemporaryDirectory() as tmp:
|
|
1086
|
+
root = Path(tmp)
|
|
1087
|
+
_write_aligned_fixture(root)
|
|
1088
|
+
_write_changelog(root, latest_version="3.5.0", latest_date="2026-02-30")
|
|
1089
|
+
result = _run(root)
|
|
1090
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1091
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
1092
|
+
self.assertIn("2026-02-30", result.stdout)
|
|
1093
|
+
|
|
1094
|
+
def test_overlong_changelog_date_not_accepted_as_prefix(self) -> None:
|
|
1095
|
+
"""A trailing-digit date like 2026-04-222 must NOT be prefix-captured
|
|
1096
|
+
as 2026-04-22 and silently pass freshness (codex re-review P3): it is
|
|
1097
|
+
malformed and must be flagged."""
|
|
1098
|
+
with TemporaryDirectory() as tmp:
|
|
1099
|
+
root = Path(tmp)
|
|
1100
|
+
_write_aligned_fixture(root, last_updated="2026-04-22")
|
|
1101
|
+
_write_changelog(root, latest_version="3.5.0", latest_date="2026-04-222")
|
|
1102
|
+
result = _run(root)
|
|
1103
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1104
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
1105
|
+
|
|
1106
|
+
def test_non_iso_last_updated_reports_not_crashes(self) -> None:
|
|
1107
|
+
"""A non-YYYY-MM-DD Last Updated that date.fromisoformat happens to
|
|
1108
|
+
accept (e.g. compact 20260422) must still be flagged as malformed
|
|
1109
|
+
rather than silently passing (codex P2-2)."""
|
|
1110
|
+
with TemporaryDirectory() as tmp:
|
|
1111
|
+
root = Path(tmp)
|
|
1112
|
+
_write_aligned_fixture(root, last_updated="20260422")
|
|
1113
|
+
result = _run(root)
|
|
1114
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1115
|
+
self.assertIn("Last Updated", result.stdout)
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
class TestKeyAdditionsAlignment(unittest.TestCase):
|
|
1119
|
+
"""Invariant 11 (#487): the newest "## vX.Y… Key Additions" heading in
|
|
1120
|
+
.claude/CLAUDE.md must match the suite version (compared at the heading's
|
|
1121
|
+
own precision, so `## v3.5 Key Additions` matches suite 3.5.0)."""
|
|
1122
|
+
|
|
1123
|
+
def test_key_additions_drift_fails(self) -> None:
|
|
1124
|
+
with TemporaryDirectory() as tmp:
|
|
1125
|
+
root = Path(tmp)
|
|
1126
|
+
_write_aligned_fixture(root, key_additions="v3.4")
|
|
1127
|
+
result = _run(root)
|
|
1128
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1129
|
+
self.assertIn("Key Additions", result.stdout)
|
|
1130
|
+
|
|
1131
|
+
def test_key_additions_missing_fails(self) -> None:
|
|
1132
|
+
with TemporaryDirectory() as tmp:
|
|
1133
|
+
root = Path(tmp)
|
|
1134
|
+
_write_aligned_fixture(root, key_additions=None)
|
|
1135
|
+
result = _run(root)
|
|
1136
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1137
|
+
self.assertIn("Key Additions", result.stdout)
|
|
1138
|
+
|
|
1139
|
+
def test_three_segment_heading_match_passes(self) -> None:
|
|
1140
|
+
with TemporaryDirectory() as tmp:
|
|
1141
|
+
root = Path(tmp)
|
|
1142
|
+
_write_aligned_fixture(root, key_additions="v3.5.0")
|
|
1143
|
+
result = _run(root)
|
|
1144
|
+
self.assertEqual(
|
|
1145
|
+
result.returncode, 0,
|
|
1146
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
def test_older_headings_below_newest_allowed(self) -> None:
|
|
1150
|
+
"""Historical Key Additions sections stay put; only the NEWEST (max
|
|
1151
|
+
version) heading is compared against the suite version."""
|
|
1152
|
+
with TemporaryDirectory() as tmp:
|
|
1153
|
+
root = Path(tmp)
|
|
1154
|
+
_write_aligned_fixture(root)
|
|
1155
|
+
claude_md = root / ".claude" / "CLAUDE.md"
|
|
1156
|
+
text = claude_md.read_text(encoding="utf-8")
|
|
1157
|
+
text = text.replace(
|
|
1158
|
+
"## Version Info\n",
|
|
1159
|
+
"## v3.4 Key Additions (older, allowed)\n\n- old\n\n## Version Info\n",
|
|
1160
|
+
)
|
|
1161
|
+
claude_md.write_text(text, encoding="utf-8")
|
|
1162
|
+
result = _run(root)
|
|
1163
|
+
self.assertEqual(
|
|
1164
|
+
result.returncode, 0,
|
|
1165
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
class TestTagMatch(unittest.TestCase):
|
|
1170
|
+
"""Tag gate (#487, invariant 1c): `--tag <ref>` must equal the suite
|
|
1171
|
+
version — the one comparison nothing else performs at tag time."""
|
|
1172
|
+
|
|
1173
|
+
def test_matching_tag_passes(self) -> None:
|
|
1174
|
+
with TemporaryDirectory() as tmp:
|
|
1175
|
+
root = Path(tmp)
|
|
1176
|
+
_write_aligned_fixture(root)
|
|
1177
|
+
result = run_script(SCRIPT, "--path", str(root), "--tag", "v3.5.0")
|
|
1178
|
+
self.assertEqual(
|
|
1179
|
+
result.returncode, 0,
|
|
1180
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1181
|
+
)
|
|
1182
|
+
|
|
1183
|
+
def test_mismatched_tag_fails(self) -> None:
|
|
1184
|
+
with TemporaryDirectory() as tmp:
|
|
1185
|
+
root = Path(tmp)
|
|
1186
|
+
_write_aligned_fixture(root)
|
|
1187
|
+
result = run_script(SCRIPT, "--path", str(root), "--tag", "v3.6.0")
|
|
1188
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1189
|
+
self.assertIn("v3.6.0", result.stdout)
|
|
1190
|
+
self.assertIn("3.5.0", result.stdout)
|
|
1191
|
+
|
|
1192
|
+
def test_tag_without_v_prefix_matches(self) -> None:
|
|
1193
|
+
"""A bare `3.5.0` ref compares equal — the leading `v` is cosmetic."""
|
|
1194
|
+
with TemporaryDirectory() as tmp:
|
|
1195
|
+
root = Path(tmp)
|
|
1196
|
+
_write_aligned_fixture(root)
|
|
1197
|
+
result = run_script(SCRIPT, "--path", str(root), "--tag", "3.5.0")
|
|
1198
|
+
self.assertEqual(
|
|
1199
|
+
result.returncode, 0,
|
|
1200
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1201
|
+
)
|
|
1202
|
+
|
|
1203
|
+
def test_tag_supplied_but_suite_version_missing_fails(self) -> None:
|
|
1204
|
+
"""The tag gate must NOT silently no-op when .claude/CLAUDE.md has no
|
|
1205
|
+
Suite-version line: the whole point of `--tag` is to guarantee the tag
|
|
1206
|
+
is right at tag time, so a garbage tag co-occurring with a broken
|
|
1207
|
+
CLAUDE.md has to be a non-zero exit (both the suite-missing error AND
|
|
1208
|
+
the tag-uncheckable error surface), never a pass."""
|
|
1209
|
+
with TemporaryDirectory() as tmp:
|
|
1210
|
+
root = Path(tmp)
|
|
1211
|
+
_write_aligned_fixture(root)
|
|
1212
|
+
claude_md = root / ".claude" / "CLAUDE.md"
|
|
1213
|
+
text = claude_md.read_text(encoding="utf-8")
|
|
1214
|
+
text = text.replace(
|
|
1215
|
+
"- **Suite version**: 3.5.0 (per CHANGELOG.md)\n", ""
|
|
1216
|
+
)
|
|
1217
|
+
claude_md.write_text(text, encoding="utf-8")
|
|
1218
|
+
result = run_script(SCRIPT, "--path", str(root), "--tag", "v9.9.9")
|
|
1219
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1220
|
+
# The tag itself must be named as uncheckable — not just the
|
|
1221
|
+
# generic suite-missing error that would fire even without --tag.
|
|
1222
|
+
self.assertIn("v9.9.9", result.stdout)
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
class TestCitationSurfaces(unittest.TestCase):
|
|
1226
|
+
"""Invariant 12: CITATION.cff (YAML-parsed, absence errors) and
|
|
1227
|
+
POSITIONING.md citation prose (optional) track the release. The aligned
|
|
1228
|
+
baseline is exercised by every pass-case test via _write_aligned_fixture,
|
|
1229
|
+
which writes both surfaces."""
|
|
1230
|
+
|
|
1231
|
+
def test_cff_drift_fails(self) -> None:
|
|
1232
|
+
"""CITATION.cff stuck below the suite version — the #754 drift class."""
|
|
1233
|
+
with TemporaryDirectory() as tmp:
|
|
1234
|
+
root = Path(tmp)
|
|
1235
|
+
_write_aligned_fixture(root)
|
|
1236
|
+
_write_cff(root, "3.4.0")
|
|
1237
|
+
result = _run(root)
|
|
1238
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1239
|
+
self.assertIn("CITATION.cff", result.stdout)
|
|
1240
|
+
self.assertIn("3.4.0", result.stdout)
|
|
1241
|
+
|
|
1242
|
+
def test_cff_quoted_version_passes(self) -> None:
|
|
1243
|
+
"""`version: "3.5.0"` is a legitimate CFF spelling — YAML parsing must
|
|
1244
|
+
not report the quotes as drift (regression: the first regex-based
|
|
1245
|
+
draft of this invariant did exactly that)."""
|
|
1246
|
+
with TemporaryDirectory() as tmp:
|
|
1247
|
+
root = Path(tmp)
|
|
1248
|
+
_write_aligned_fixture(root)
|
|
1249
|
+
(root / "CITATION.cff").write_text(
|
|
1250
|
+
'cff-version: 1.2.0\ntitle: fixture\ntype: software\n'
|
|
1251
|
+
'version: "3.5.0"\ndate-released: 2026-04-22\n',
|
|
1252
|
+
encoding="utf-8",
|
|
1253
|
+
)
|
|
1254
|
+
result = _run(root)
|
|
1255
|
+
self.assertEqual(
|
|
1256
|
+
result.returncode, 0,
|
|
1257
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1258
|
+
)
|
|
1259
|
+
|
|
1260
|
+
def test_cff_noncanonical_version_fails_as_noncanonical(self) -> None:
|
|
1261
|
+
"""A 2-segment `version: 3.5` must be reported as non-canonical, not
|
|
1262
|
+
as a drift mismatch a maintainer would chase with a version bump."""
|
|
1263
|
+
with TemporaryDirectory() as tmp:
|
|
1264
|
+
root = Path(tmp)
|
|
1265
|
+
_write_aligned_fixture(root)
|
|
1266
|
+
(root / "CITATION.cff").write_text(
|
|
1267
|
+
"cff-version: 1.2.0\ntitle: fixture\ntype: software\n"
|
|
1268
|
+
"version: 3.5\ndate-released: 2026-04-22\n",
|
|
1269
|
+
encoding="utf-8",
|
|
1270
|
+
)
|
|
1271
|
+
result = _run(root)
|
|
1272
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1273
|
+
self.assertIn("canonical", result.stdout)
|
|
1274
|
+
|
|
1275
|
+
def test_cff_missing_version_key_fails(self) -> None:
|
|
1276
|
+
"""A present CITATION.cff with no version key is malformed — error,
|
|
1277
|
+
never a silent skip. date-released stays valid so this test fails
|
|
1278
|
+
for the missing-version diagnostic specifically, not the
|
|
1279
|
+
missing-date one (codex round-3 P2: conflated fixtures let a
|
|
1280
|
+
missing-version regression hide behind the date error)."""
|
|
1281
|
+
with TemporaryDirectory() as tmp:
|
|
1282
|
+
root = Path(tmp)
|
|
1283
|
+
_write_aligned_fixture(root)
|
|
1284
|
+
(root / "CITATION.cff").write_text(
|
|
1285
|
+
"cff-version: 1.2.0\ntitle: fixture\ntype: software\n"
|
|
1286
|
+
"date-released: 2026-04-22\n",
|
|
1287
|
+
encoding="utf-8",
|
|
1288
|
+
)
|
|
1289
|
+
result = _run(root)
|
|
1290
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1291
|
+
self.assertIn("no 'version' key found", result.stdout)
|
|
1292
|
+
|
|
1293
|
+
def test_cff_absent_fails(self) -> None:
|
|
1294
|
+
"""CITATION.cff is outward-facing release metadata like README.md —
|
|
1295
|
+
deleting it must not silently disable the invariant."""
|
|
1296
|
+
with TemporaryDirectory() as tmp:
|
|
1297
|
+
root = Path(tmp)
|
|
1298
|
+
_write_aligned_fixture(root)
|
|
1299
|
+
(root / "CITATION.cff").unlink()
|
|
1300
|
+
result = _run(root)
|
|
1301
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1302
|
+
self.assertIn("CITATION.cff", result.stdout)
|
|
1303
|
+
|
|
1304
|
+
def test_cff_stale_date_released_fails(self) -> None:
|
|
1305
|
+
"""date-released more than 7 days from the CHANGELOG latest-entry date
|
|
1306
|
+
— the second half of the #754 drift (version was bumped by hand while
|
|
1307
|
+
the date sat 6 weeks stale)."""
|
|
1308
|
+
with TemporaryDirectory() as tmp:
|
|
1309
|
+
root = Path(tmp)
|
|
1310
|
+
_write_aligned_fixture(root)
|
|
1311
|
+
_write_cff(root, "3.5.0", date_released="2026-01-01")
|
|
1312
|
+
result = _run(root)
|
|
1313
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1314
|
+
self.assertIn("date-released", result.stdout)
|
|
1315
|
+
|
|
1316
|
+
def test_cff_missing_date_released_fails(self) -> None:
|
|
1317
|
+
"""Omitting (or nulling) date-released must error, not silently skip
|
|
1318
|
+
the freshness check — deleting the field must not disable the
|
|
1319
|
+
invariant (codex review round-2 P2; same posture as file absence)."""
|
|
1320
|
+
with TemporaryDirectory() as tmp:
|
|
1321
|
+
root = Path(tmp)
|
|
1322
|
+
_write_aligned_fixture(root)
|
|
1323
|
+
(root / "CITATION.cff").write_text(
|
|
1324
|
+
"cff-version: 1.2.0\ntitle: fixture\ntype: software\n"
|
|
1325
|
+
"version: 3.5.0\n",
|
|
1326
|
+
encoding="utf-8",
|
|
1327
|
+
)
|
|
1328
|
+
result = _run(root)
|
|
1329
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1330
|
+
self.assertIn("date-released", result.stdout)
|
|
1331
|
+
|
|
1332
|
+
def test_cff_impossible_date_fails_cleanly(self) -> None:
|
|
1333
|
+
"""`date-released: 2026-02-30` — PyYAML's timestamp constructor raises
|
|
1334
|
+
ValueError, not YAMLError; the lint must emit an error, never crash
|
|
1335
|
+
with a traceback (codex review P2)."""
|
|
1336
|
+
with TemporaryDirectory() as tmp:
|
|
1337
|
+
root = Path(tmp)
|
|
1338
|
+
_write_aligned_fixture(root)
|
|
1339
|
+
_write_cff(root, "3.5.0", date_released="2026-02-30")
|
|
1340
|
+
result = _run(root)
|
|
1341
|
+
self.assertEqual(
|
|
1342
|
+
result.returncode, 1,
|
|
1343
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1344
|
+
)
|
|
1345
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
1346
|
+
self.assertIn("CITATION.cff", result.stdout)
|
|
1347
|
+
|
|
1348
|
+
def test_cff_timestamp_date_fails_as_nonstrict(self) -> None:
|
|
1349
|
+
"""An unquoted `2026-04-22T00:00:00Z` parses as datetime — a date
|
|
1350
|
+
SUBCLASS that would TypeError against the date baseline. It must be
|
|
1351
|
+
rejected as not a strict YYYY-MM-DD, never crash (codex review P2)."""
|
|
1352
|
+
with TemporaryDirectory() as tmp:
|
|
1353
|
+
root = Path(tmp)
|
|
1354
|
+
_write_aligned_fixture(root)
|
|
1355
|
+
_write_cff(root, "3.5.0", date_released="2026-04-22T00:00:00Z")
|
|
1356
|
+
result = _run(root)
|
|
1357
|
+
self.assertEqual(
|
|
1358
|
+
result.returncode, 1,
|
|
1359
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1360
|
+
)
|
|
1361
|
+
self.assertNotIn("Traceback", result.stderr)
|
|
1362
|
+
self.assertIn("strict YYYY-MM-DD", result.stdout)
|
|
1363
|
+
|
|
1364
|
+
def test_cff_date_within_window_passes(self) -> None:
|
|
1365
|
+
"""A date-released within the ±7-day window of the CHANGELOG date
|
|
1366
|
+
(fixture CHANGELOG: 2026-04-22) passes."""
|
|
1367
|
+
with TemporaryDirectory() as tmp:
|
|
1368
|
+
root = Path(tmp)
|
|
1369
|
+
_write_aligned_fixture(root)
|
|
1370
|
+
_write_cff(root, "3.5.0", date_released="2026-04-25")
|
|
1371
|
+
result = _run(root)
|
|
1372
|
+
self.assertEqual(
|
|
1373
|
+
result.returncode, 0,
|
|
1374
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
def test_positioning_drift_fails(self) -> None:
|
|
1378
|
+
"""POSITIONING.md citation prose stuck below the suite version."""
|
|
1379
|
+
with TemporaryDirectory() as tmp:
|
|
1380
|
+
root = Path(tmp)
|
|
1381
|
+
_write_aligned_fixture(root)
|
|
1382
|
+
_write_positioning(root, "3.4.0")
|
|
1383
|
+
result = _run(root)
|
|
1384
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1385
|
+
self.assertIn("POSITIONING.md", result.stdout)
|
|
1386
|
+
self.assertIn("3.4.0", result.stdout)
|
|
1387
|
+
|
|
1388
|
+
def test_positioning_vprefixed_token_fails_as_noncanonical(self) -> None:
|
|
1389
|
+
"""`(Version v3.5.0)` — the likeliest human edit of the citation line —
|
|
1390
|
+
must error as non-canonical, not slip through a filtering regex (the
|
|
1391
|
+
pre-#169 pattern this file's header warns about)."""
|
|
1392
|
+
with TemporaryDirectory() as tmp:
|
|
1393
|
+
root = Path(tmp)
|
|
1394
|
+
_write_aligned_fixture(root)
|
|
1395
|
+
(root / "POSITIONING.md").write_text(
|
|
1396
|
+
"# Positioning\n\nCite: Fixture Suite (Version v3.5.0).\n",
|
|
1397
|
+
encoding="utf-8",
|
|
1398
|
+
)
|
|
1399
|
+
result = _run(root)
|
|
1400
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1401
|
+
self.assertIn("canonical", result.stdout)
|
|
1402
|
+
|
|
1403
|
+
def test_positioning_malformed_clause_still_validated(self) -> None:
|
|
1404
|
+
"""`(Version 3.4.0 )` — a stray space must not drop the clause out of
|
|
1405
|
+
the capture and silently pass a stale citation (codex round-3 P2):
|
|
1406
|
+
the payload is captured whole, stripped, then validated."""
|
|
1407
|
+
with TemporaryDirectory() as tmp:
|
|
1408
|
+
root = Path(tmp)
|
|
1409
|
+
_write_aligned_fixture(root)
|
|
1410
|
+
(root / "POSITIONING.md").write_text(
|
|
1411
|
+
"# Positioning\n\nCite: Fixture Suite (Version 3.4.0 ).\n",
|
|
1412
|
+
encoding="utf-8",
|
|
1413
|
+
)
|
|
1414
|
+
result = _run(root)
|
|
1415
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1416
|
+
self.assertIn("3.4.0", result.stdout)
|
|
1417
|
+
|
|
1418
|
+
def test_positioning_empty_payload_fails(self) -> None:
|
|
1419
|
+
"""`(Version )` — deleting the version mid-edit must reach strict
|
|
1420
|
+
validation and error, not fall out of the capture (codex round-4
|
|
1421
|
+
P2)."""
|
|
1422
|
+
with TemporaryDirectory() as tmp:
|
|
1423
|
+
root = Path(tmp)
|
|
1424
|
+
_write_aligned_fixture(root)
|
|
1425
|
+
(root / "POSITIONING.md").write_text(
|
|
1426
|
+
"# Positioning\n\nCite: Fixture Suite (Version ).\n",
|
|
1427
|
+
encoding="utf-8",
|
|
1428
|
+
)
|
|
1429
|
+
result = _run(root)
|
|
1430
|
+
self.assertEqual(result.returncode, 1, msg=f"stdout={result.stdout!r}")
|
|
1431
|
+
self.assertIn("canonical", result.stdout)
|
|
1432
|
+
|
|
1433
|
+
def test_positioning_absent_passes(self) -> None:
|
|
1434
|
+
"""POSITIONING.md is repo-specific prose — absence is a skip."""
|
|
1435
|
+
with TemporaryDirectory() as tmp:
|
|
1436
|
+
root = Path(tmp)
|
|
1437
|
+
_write_aligned_fixture(root)
|
|
1438
|
+
(root / "POSITIONING.md").unlink()
|
|
1439
|
+
result = _run(root)
|
|
1440
|
+
self.assertEqual(
|
|
1441
|
+
result.returncode, 0,
|
|
1442
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1443
|
+
)
|
|
1444
|
+
|
|
1445
|
+
def test_positioning_without_version_token_passes(self) -> None:
|
|
1446
|
+
"""A POSITIONING.md with no `(Version X.Y.Z)` token has nothing to
|
|
1447
|
+
check — the token is the claim; no claim, no drift."""
|
|
1448
|
+
with TemporaryDirectory() as tmp:
|
|
1449
|
+
root = Path(tmp)
|
|
1450
|
+
_write_aligned_fixture(root)
|
|
1451
|
+
(root / "POSITIONING.md").write_text(
|
|
1452
|
+
"# Positioning\n\nNo citation prose here.\n", encoding="utf-8"
|
|
1453
|
+
)
|
|
1454
|
+
result = _run(root)
|
|
1455
|
+
self.assertEqual(
|
|
1456
|
+
result.returncode, 0,
|
|
1457
|
+
msg=f"stdout={result.stdout!r} stderr={result.stderr!r}",
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
if __name__ == "__main__":
|
|
1462
|
+
unittest.main()
|