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,3505 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Offline-only execution envelope for #659.
|
|
3
|
+
|
|
4
|
+
The command set creates and validates files. It has no provider transport,
|
|
5
|
+
dispatch, probe, actor, subject, judge, or adjudicator execution path.
|
|
6
|
+
Externally recorded transcripts may be ingested only after a separately held
|
|
7
|
+
fresh authorization; this envelope never grants that authorization.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import base64
|
|
13
|
+
import copy
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import re
|
|
20
|
+
import secrets
|
|
21
|
+
import stat
|
|
22
|
+
import sys
|
|
23
|
+
import unicodedata
|
|
24
|
+
from typing import Any, NoReturn
|
|
25
|
+
|
|
26
|
+
from jsonschema import Draft202012Validator, FormatChecker
|
|
27
|
+
|
|
28
|
+
import validate_ideation_diversity_assets as phase1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
32
|
+
SUITE = "within_session_ideation_diversity"
|
|
33
|
+
SUITE_ROOT = REPO_ROOT / "evals" / "heldout" / SUITE
|
|
34
|
+
RUN_PLAN_SCHEMA = SUITE_ROOT / "run_plan.schema.json"
|
|
35
|
+
AUTHORIZATION_SCHEMA = SUITE_ROOT / "authorization_record.schema.json"
|
|
36
|
+
TRANSCRIPT_SCHEMA = SUITE_ROOT / "transcript.schema.json"
|
|
37
|
+
INGESTION_SCHEMA = SUITE_ROOT / "ingestion_manifest.schema.json"
|
|
38
|
+
STOP_INTENT_SCHEMA = SUITE_ROOT / "stop_intent.schema.json"
|
|
39
|
+
BLIND_PACKET_SCHEMA = SUITE_ROOT / "blind_packet.schema.json"
|
|
40
|
+
BLIND_INVENTORY_SCHEMA = SUITE_ROOT / "blind_inventory.schema.json"
|
|
41
|
+
BLIND_MANIFEST_SCHEMA = SUITE_ROOT / "blind_manifest.schema.json"
|
|
42
|
+
PRIVATE_ARM_MAP_SCHEMA = SUITE_ROOT / "private_arm_map.schema.json"
|
|
43
|
+
BLIND_INTENT_SCHEMA = SUITE_ROOT / "blind_intent.schema.json"
|
|
44
|
+
PRODUCTION_PROMPT = REPO_ROOT / "deep-research" / "agents" / "socratic_mentor_agent.md"
|
|
45
|
+
CODEBOOK = SUITE_ROOT / "codebook.md"
|
|
46
|
+
MAX_INPUT_BYTES = 4 * 1024 * 1024
|
|
47
|
+
MAX_STOP_INTENT_BYTES = 8 * 1024 * 1024
|
|
48
|
+
STOP_INTENT_REF = "stop-intent.json"
|
|
49
|
+
STOP_INTENT_FALLBACK_REF = "stop-intent.fallback.json"
|
|
50
|
+
BLIND_INTENT_REF = "blind-intent.json"
|
|
51
|
+
BLIND_STAGING_REF = "blind-staging"
|
|
52
|
+
BLIND_ALIAS_ROOT = "blind-atomic-aliases"
|
|
53
|
+
ATTEMPT_JOURNAL_ROOT = "terminal-journal"
|
|
54
|
+
COMMIT_RE = re.compile(r"^[0-9a-f]{40}$")
|
|
55
|
+
RUN_ID_RE = re.compile(r"^[a-z0-9][a-z0-9._-]{2,79}$")
|
|
56
|
+
EXTERNAL_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,255}$")
|
|
57
|
+
STOP_RULES = [
|
|
58
|
+
"prompt_or_role_card_hash_mismatch",
|
|
59
|
+
"arm_leakage",
|
|
60
|
+
"ineligible_session",
|
|
61
|
+
"authorization_record_missing_or_mismatch",
|
|
62
|
+
"unplanned_tool_action",
|
|
63
|
+
"unplanned_network_action",
|
|
64
|
+
"evidence_write_failure",
|
|
65
|
+
"partial_subject_output",
|
|
66
|
+
"actor_protocol_deviation",
|
|
67
|
+
"out_of_order_ingestion",
|
|
68
|
+
"transcript_contract_failure",
|
|
69
|
+
]
|
|
70
|
+
BLINDED_TO = [
|
|
71
|
+
"mechanism",
|
|
72
|
+
"arm",
|
|
73
|
+
"pair_mapping",
|
|
74
|
+
"replicate",
|
|
75
|
+
"expected_direction",
|
|
76
|
+
"other_transcripts",
|
|
77
|
+
"aggregate",
|
|
78
|
+
"prior_labels",
|
|
79
|
+
]
|
|
80
|
+
ARM_LEAK_TOKENS = (
|
|
81
|
+
"adjacent_probe",
|
|
82
|
+
"exploratory_guardrails",
|
|
83
|
+
"ars_socratic_adjacent_probe",
|
|
84
|
+
"paired transcript",
|
|
85
|
+
"expected metric direction",
|
|
86
|
+
)
|
|
87
|
+
MESSAGE_EVENT_KINDS = {"actor_message", "subject_message"}
|
|
88
|
+
TERMINAL_EVENT_KINDS = {
|
|
89
|
+
"session_started",
|
|
90
|
+
"session_completed",
|
|
91
|
+
"session_blocked",
|
|
92
|
+
"session_partial",
|
|
93
|
+
"tool_action",
|
|
94
|
+
"network_action",
|
|
95
|
+
"evidence_write_failure",
|
|
96
|
+
}
|
|
97
|
+
RAW_EVENT_KINDS = MESSAGE_EVENT_KINDS | TERMINAL_EVENT_KINDS
|
|
98
|
+
HUMAN_EVIDENCE_PATTERNS = (
|
|
99
|
+
re.compile(
|
|
100
|
+
r"\b(?:prior|human|judge|annotator|reviewer)\s+"
|
|
101
|
+
r"(?:label|rating|decision|evidence)s?\b",
|
|
102
|
+
re.IGNORECASE,
|
|
103
|
+
),
|
|
104
|
+
re.compile(r"\badjudicat(?:e|ed|es|ing|ion|or|ors)\b", re.IGNORECASE),
|
|
105
|
+
re.compile(
|
|
106
|
+
r"\b(?:A[12]|M[1-5])\s*(?:=|:)?\s*"
|
|
107
|
+
r"(?:yes|no|not_applicable)\b",
|
|
108
|
+
re.IGNORECASE,
|
|
109
|
+
),
|
|
110
|
+
re.compile(r"(?:真人|人類)(?:專家|評審|標註|裁決|證據)"),
|
|
111
|
+
re.compile(r"(?:先前|既有|前輪)(?:標註|評分|裁決)"),
|
|
112
|
+
re.compile(r"\b(?:gold[- ]standard\s+)?(?:coder|annotator|rater)\b", re.IGNORECASE),
|
|
113
|
+
re.compile(r"\btie[- ]breaker\b", re.IGNORECASE),
|
|
114
|
+
re.compile(r"(?:黃金標準|標註者|編碼者|評分者|裁決者)"),
|
|
115
|
+
)
|
|
116
|
+
HUMAN_EVIDENCE_COMPACT_PHRASES = (
|
|
117
|
+
"adjudicate",
|
|
118
|
+
"adjudicated",
|
|
119
|
+
"adjudicates",
|
|
120
|
+
"adjudicating",
|
|
121
|
+
"adjudication",
|
|
122
|
+
"adjudicator",
|
|
123
|
+
"adjudicators",
|
|
124
|
+
"tie breaker",
|
|
125
|
+
) + tuple(
|
|
126
|
+
f"{source} {evidence}"
|
|
127
|
+
for source in ("prior", "human", "judge", "annotator", "reviewer")
|
|
128
|
+
for evidence in (
|
|
129
|
+
"label", "labels", "rating", "ratings", "decision", "decisions",
|
|
130
|
+
"evidence", "evidences",
|
|
131
|
+
)
|
|
132
|
+
) + tuple(
|
|
133
|
+
f"{prefix} {role}"
|
|
134
|
+
for prefix in ("", "gold standard")
|
|
135
|
+
for role in ("coder", "annotator", "rater")
|
|
136
|
+
) + tuple(
|
|
137
|
+
f"{label} {value}"
|
|
138
|
+
for label in ("a1", "a2", "m1", "m2", "m3", "m4", "m5")
|
|
139
|
+
for value in ("yes", "no", "not applicable")
|
|
140
|
+
) + tuple(
|
|
141
|
+
f"{source}{evidence}"
|
|
142
|
+
for source in ("真人", "人類")
|
|
143
|
+
for evidence in ("專家", "評審", "標註", "裁決", "證據")
|
|
144
|
+
) + tuple(
|
|
145
|
+
f"{source}{evidence}"
|
|
146
|
+
for source in ("先前", "既有", "前輪")
|
|
147
|
+
for evidence in ("標註", "評分", "裁決")
|
|
148
|
+
) + ("黃金標準", "標註者", "編碼者", "評分者", "裁決者")
|
|
149
|
+
MAPPING_LEAK_PATTERNS = (
|
|
150
|
+
re.compile(r"\breplicate(?:\s+id)?\s*(?:=|:|#)?\s*[12]\b", re.IGNORECASE),
|
|
151
|
+
re.compile(r"\b(?:cell|pair|arm|scenario|experiment)\s+id\s*(?:=|:)", re.IGNORECASE),
|
|
152
|
+
re.compile(r"\bother transcripts?\b", re.IGNORECASE),
|
|
153
|
+
re.compile(r"\b(?:adjacent[- ]probe|exploratory[- ]guardrails?)\b", re.IGNORECASE),
|
|
154
|
+
re.compile(
|
|
155
|
+
r"\b(?:treatment|control|experimental)\s+"
|
|
156
|
+
r"(?:arm|condition|group)\b",
|
|
157
|
+
re.IGNORECASE,
|
|
158
|
+
),
|
|
159
|
+
re.compile(
|
|
160
|
+
r"\b(?:mechanism|feature|probe|guardrails?)\b.{0,24}"
|
|
161
|
+
r"\b(?:enabled|disabled|ablated|on|off)\b",
|
|
162
|
+
re.IGNORECASE,
|
|
163
|
+
),
|
|
164
|
+
re.compile(
|
|
165
|
+
r"\b(?:preceding|previous|prior|other|paired)\s+"
|
|
166
|
+
r"(?:session|transcript|response)\b",
|
|
167
|
+
re.IGNORECASE,
|
|
168
|
+
),
|
|
169
|
+
re.compile(r"(?:處置|控制|實驗)(?:組|條件|臂)"),
|
|
170
|
+
re.compile(r"(?:前一|先前|其他|配對)(?:場次|對話|逐字稿|回應)"),
|
|
171
|
+
)
|
|
172
|
+
MAPPING_LEAK_COMPACT_PHRASES = (
|
|
173
|
+
"adjacent probe",
|
|
174
|
+
"exploratory guardrail",
|
|
175
|
+
"exploratory guardrails",
|
|
176
|
+
) + tuple(
|
|
177
|
+
f"replicate {middle}{value}"
|
|
178
|
+
for middle in ("", "id ")
|
|
179
|
+
for value in ("1", "2")
|
|
180
|
+
) + tuple(
|
|
181
|
+
f"{entity} id"
|
|
182
|
+
for entity in ("cell", "pair", "arm", "scenario", "experiment")
|
|
183
|
+
) + tuple(
|
|
184
|
+
f"{arm} {kind}"
|
|
185
|
+
for arm in ("treatment", "control", "experimental")
|
|
186
|
+
for kind in ("arm", "condition", "group")
|
|
187
|
+
) + tuple(
|
|
188
|
+
f"{relation} {artifact}"
|
|
189
|
+
for relation in ("preceding", "previous", "prior", "other", "paired")
|
|
190
|
+
for artifact in ("session", "transcript", "response")
|
|
191
|
+
) + ("other transcripts",) + tuple(
|
|
192
|
+
f"{arm}{kind}"
|
|
193
|
+
for arm in ("處置", "控制", "實驗")
|
|
194
|
+
for kind in ("組", "條件", "臂")
|
|
195
|
+
) + tuple(
|
|
196
|
+
f"{relation}{artifact}"
|
|
197
|
+
for relation in ("前一", "先前", "其他", "配對")
|
|
198
|
+
for artifact in ("場次", "對話", "逐字稿", "回應")
|
|
199
|
+
)
|
|
200
|
+
EXPERIMENTS = (
|
|
201
|
+
(
|
|
202
|
+
"adjacent_probe",
|
|
203
|
+
("adjacent_probe_off", "adjacent_probe_on"),
|
|
204
|
+
),
|
|
205
|
+
(
|
|
206
|
+
"exploratory_guardrails",
|
|
207
|
+
("exploratory_guardrails_on", "exploratory_guardrails_ablated"),
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
ASSET_PATHS = (
|
|
211
|
+
"docs/design/2026-08-13-659-within-session-ideation-diversity-design.md",
|
|
212
|
+
"evals/heldout/within_session_ideation_diversity/README.md",
|
|
213
|
+
"evals/heldout/within_session_ideation_diversity/heldout_set.json",
|
|
214
|
+
"evals/heldout/within_session_ideation_diversity/heldout_set.schema.json",
|
|
215
|
+
"evals/heldout/within_session_ideation_diversity/codebook.md",
|
|
216
|
+
"evals/heldout/within_session_ideation_diversity/nonproduction_variant.json",
|
|
217
|
+
"evals/heldout/within_session_ideation_diversity/run_plan.schema.json",
|
|
218
|
+
"evals/heldout/within_session_ideation_diversity/authorization_record.schema.json",
|
|
219
|
+
"evals/heldout/within_session_ideation_diversity/transcript.schema.json",
|
|
220
|
+
"evals/heldout/within_session_ideation_diversity/ingestion_manifest.schema.json",
|
|
221
|
+
"evals/heldout/within_session_ideation_diversity/stop_intent.schema.json",
|
|
222
|
+
"evals/heldout/within_session_ideation_diversity/blind_packet.schema.json",
|
|
223
|
+
"evals/heldout/within_session_ideation_diversity/blind_inventory.schema.json",
|
|
224
|
+
"evals/heldout/within_session_ideation_diversity/blind_manifest.schema.json",
|
|
225
|
+
"evals/heldout/within_session_ideation_diversity/private_arm_map.schema.json",
|
|
226
|
+
"evals/heldout/within_session_ideation_diversity/blind_intent.schema.json",
|
|
227
|
+
"deep-research/agents/socratic_mentor_agent.md",
|
|
228
|
+
"scripts/run_ideation_diversity_no_call.py",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class EnvelopeError(RuntimeError):
|
|
233
|
+
"""Closed no-call envelope failure."""
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class StopViolation(EnvelopeError):
|
|
237
|
+
"""A transcript event that permanently stops the run."""
|
|
238
|
+
|
|
239
|
+
def __init__(self, code: str, detail: str):
|
|
240
|
+
super().__init__(detail)
|
|
241
|
+
self.code = code
|
|
242
|
+
self.detail = detail
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _fail(message: str) -> NoReturn:
|
|
246
|
+
raise EnvelopeError(message)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _reject_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
250
|
+
value: dict[str, Any] = {}
|
|
251
|
+
for key, item in pairs:
|
|
252
|
+
if key in value:
|
|
253
|
+
raise ValueError(f"duplicate JSON key {key!r}")
|
|
254
|
+
value[key] = item
|
|
255
|
+
return value
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _strict_loads(raw: bytes) -> dict[str, Any]:
|
|
259
|
+
try:
|
|
260
|
+
value = json.loads(
|
|
261
|
+
raw.decode("utf-8"),
|
|
262
|
+
object_pairs_hook=_reject_pairs,
|
|
263
|
+
parse_constant=lambda token: (_ for _ in ()).throw(
|
|
264
|
+
ValueError(f"non-finite JSON value {token!r}")
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
except (UnicodeError, ValueError, json.JSONDecodeError) as exc:
|
|
268
|
+
raise EnvelopeError(f"invalid strict JSON: {exc}") from exc
|
|
269
|
+
if not isinstance(value, dict):
|
|
270
|
+
_fail("JSON root must be an object")
|
|
271
|
+
return value
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _canonical(value: Any) -> bytes:
|
|
275
|
+
try:
|
|
276
|
+
return json.dumps(
|
|
277
|
+
value,
|
|
278
|
+
ensure_ascii=False,
|
|
279
|
+
allow_nan=False,
|
|
280
|
+
sort_keys=True,
|
|
281
|
+
separators=(",", ":"),
|
|
282
|
+
).encode("utf-8")
|
|
283
|
+
except (TypeError, ValueError, UnicodeError) as exc:
|
|
284
|
+
raise EnvelopeError(f"value is not canonical JSON: {exc}") from exc
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _json_bytes(value: Any) -> bytes:
|
|
288
|
+
return _canonical(value) + b"\n"
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _sha(raw: bytes) -> str:
|
|
292
|
+
return hashlib.sha256(raw).hexdigest()
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _has_semantic_text(
|
|
296
|
+
value: Any, *, allowed_categories: frozenset[str] = frozenset({"L", "N", "S"})
|
|
297
|
+
) -> bool:
|
|
298
|
+
"""Require an NFKC-stable letter, number, or explicitly allowed symbol."""
|
|
299
|
+
if not isinstance(value, str):
|
|
300
|
+
return False
|
|
301
|
+
normalized = unicodedata.normalize("NFKC", value)
|
|
302
|
+
return any(
|
|
303
|
+
unicodedata.category(character)[0] in allowed_categories
|
|
304
|
+
for character in normalized
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _require_semantic_text(value: Any, label: str) -> None:
|
|
309
|
+
if not _has_semantic_text(value):
|
|
310
|
+
_fail(f"{label} must contain semantic text")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _require_identity_text(value: Any, label: str) -> None:
|
|
314
|
+
if not _has_semantic_text(value, allowed_categories=frozenset({"L", "N"})):
|
|
315
|
+
_fail(f"{label} must contain semantic text with a letter or number")
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _hash_file_stable(path: Path) -> tuple[str, int]:
|
|
319
|
+
"""Stream-hash an inventory file without imposing the transcript input cap."""
|
|
320
|
+
try:
|
|
321
|
+
if path.is_symlink() or not path.is_file():
|
|
322
|
+
_fail(f"inventory entry must be a regular non-symlink file: {path}")
|
|
323
|
+
before = path.stat()
|
|
324
|
+
digest = hashlib.sha256()
|
|
325
|
+
size = 0
|
|
326
|
+
with path.open("rb") as stream:
|
|
327
|
+
while True:
|
|
328
|
+
chunk = stream.read(1024 * 1024)
|
|
329
|
+
if not chunk:
|
|
330
|
+
break
|
|
331
|
+
digest.update(chunk)
|
|
332
|
+
size += len(chunk)
|
|
333
|
+
after = path.stat()
|
|
334
|
+
except OSError as exc:
|
|
335
|
+
raise EnvelopeError(f"cannot hash inventory file {path}: {exc}") from exc
|
|
336
|
+
if size != after.st_size or (
|
|
337
|
+
before.st_dev,
|
|
338
|
+
before.st_ino,
|
|
339
|
+
before.st_size,
|
|
340
|
+
before.st_mtime_ns,
|
|
341
|
+
) != (
|
|
342
|
+
after.st_dev,
|
|
343
|
+
after.st_ino,
|
|
344
|
+
after.st_size,
|
|
345
|
+
after.st_mtime_ns,
|
|
346
|
+
):
|
|
347
|
+
_fail(f"inventory file changed while being hashed: {path}")
|
|
348
|
+
return digest.hexdigest(), size
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _read_file(path: Path, *, limit: int = MAX_INPUT_BYTES) -> bytes:
|
|
352
|
+
try:
|
|
353
|
+
if path.is_symlink() or not path.is_file():
|
|
354
|
+
_fail(f"input must be a regular non-symlink file: {path}")
|
|
355
|
+
before = path.stat()
|
|
356
|
+
if before.st_size > limit:
|
|
357
|
+
_fail(f"input exceeds {limit} bytes: {path}")
|
|
358
|
+
raw = path.read_bytes()
|
|
359
|
+
after = path.stat()
|
|
360
|
+
except OSError as exc:
|
|
361
|
+
raise EnvelopeError(f"cannot read {path}: {exc}") from exc
|
|
362
|
+
if len(raw) != after.st_size or (
|
|
363
|
+
before.st_dev,
|
|
364
|
+
before.st_ino,
|
|
365
|
+
before.st_size,
|
|
366
|
+
before.st_mtime_ns,
|
|
367
|
+
) != (
|
|
368
|
+
after.st_dev,
|
|
369
|
+
after.st_ino,
|
|
370
|
+
after.st_size,
|
|
371
|
+
after.st_mtime_ns,
|
|
372
|
+
):
|
|
373
|
+
_fail(f"input changed while being read: {path}")
|
|
374
|
+
return raw
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _write_new(path: Path, raw: bytes, *, mode: int = 0o644) -> None:
|
|
378
|
+
"""Publish complete bytes exclusively without ever exposing a partial target."""
|
|
379
|
+
temporary: Path | None = None
|
|
380
|
+
descriptor: int | None = None
|
|
381
|
+
try:
|
|
382
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
383
|
+
temporary = path.with_name(
|
|
384
|
+
f".{path.name}.atomic-write-{secrets.token_hex(12)}.tmp"
|
|
385
|
+
)
|
|
386
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
387
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
388
|
+
flags |= os.O_NOFOLLOW
|
|
389
|
+
descriptor = os.open(temporary, flags, mode)
|
|
390
|
+
view = memoryview(raw)
|
|
391
|
+
written = 0
|
|
392
|
+
while written < len(view):
|
|
393
|
+
count = os.write(descriptor, view[written:])
|
|
394
|
+
if count <= 0:
|
|
395
|
+
raise OSError("atomic staging write made no progress")
|
|
396
|
+
written += count
|
|
397
|
+
os.fsync(descriptor)
|
|
398
|
+
os.close(descriptor)
|
|
399
|
+
descriptor = None
|
|
400
|
+
# A hard link is an atomic, exclusive publication: an existing target
|
|
401
|
+
# is never replaced and readers can only observe complete staged bytes.
|
|
402
|
+
os.link(temporary, path, follow_symlinks=False)
|
|
403
|
+
_fsync_directory(path.parent)
|
|
404
|
+
except FileExistsError as exc:
|
|
405
|
+
raise EnvelopeError(f"refusing to overwrite {path}") from exc
|
|
406
|
+
except OSError as exc:
|
|
407
|
+
raise EnvelopeError(f"cannot create {path}: {exc}") from exc
|
|
408
|
+
finally:
|
|
409
|
+
if descriptor is not None:
|
|
410
|
+
try:
|
|
411
|
+
os.close(descriptor)
|
|
412
|
+
except OSError:
|
|
413
|
+
pass
|
|
414
|
+
if temporary is not None and temporary.exists():
|
|
415
|
+
try:
|
|
416
|
+
temporary.unlink()
|
|
417
|
+
_fsync_directory(temporary.parent)
|
|
418
|
+
except OSError as exc:
|
|
419
|
+
# A complete target may already be published, but an orphaned
|
|
420
|
+
# staging name would violate the exact inventory contract.
|
|
421
|
+
raise EnvelopeError(
|
|
422
|
+
f"cannot remove unpublished atomic staging file {temporary}: {exc}"
|
|
423
|
+
) from exc
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _publish_stop_intent(path: Path, raw: bytes, staging_ref: str) -> Path:
|
|
427
|
+
"""Publish to the primary or fallback slot from one durable hardlink alias."""
|
|
428
|
+
staging = path.parent / staging_ref
|
|
429
|
+
if staging.exists():
|
|
430
|
+
if _read_file(staging, limit=MAX_STOP_INTENT_BYTES) != raw:
|
|
431
|
+
_fail(f"write-once evidence collision at {staging}")
|
|
432
|
+
else:
|
|
433
|
+
descriptor: int | None = None
|
|
434
|
+
try:
|
|
435
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
436
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
437
|
+
flags |= os.O_NOFOLLOW
|
|
438
|
+
descriptor = os.open(staging, flags, 0o644)
|
|
439
|
+
view = memoryview(raw)
|
|
440
|
+
written = 0
|
|
441
|
+
while written < len(view):
|
|
442
|
+
count = os.write(descriptor, view[written:])
|
|
443
|
+
if count <= 0:
|
|
444
|
+
raise OSError("stop-intent staging write made no progress")
|
|
445
|
+
written += count
|
|
446
|
+
os.fsync(descriptor)
|
|
447
|
+
os.close(descriptor)
|
|
448
|
+
descriptor = None
|
|
449
|
+
_fsync_directory(staging.parent)
|
|
450
|
+
except OSError as exc:
|
|
451
|
+
raise EnvelopeError(
|
|
452
|
+
f"cannot stage durable stop intent {staging}: {exc}"
|
|
453
|
+
) from exc
|
|
454
|
+
finally:
|
|
455
|
+
if descriptor is not None:
|
|
456
|
+
try:
|
|
457
|
+
os.close(descriptor)
|
|
458
|
+
except OSError:
|
|
459
|
+
pass
|
|
460
|
+
published: Path | None = None
|
|
461
|
+
errors: list[str] = []
|
|
462
|
+
for target in (path, path.parent / STOP_INTENT_FALLBACK_REF):
|
|
463
|
+
try:
|
|
464
|
+
if target.exists():
|
|
465
|
+
if _read_file(target, limit=MAX_STOP_INTENT_BYTES) != raw:
|
|
466
|
+
_fail(f"write-once evidence collision at {target}")
|
|
467
|
+
else:
|
|
468
|
+
os.link(staging, target, follow_symlinks=False)
|
|
469
|
+
_fsync_directory(target.parent)
|
|
470
|
+
if target.stat().st_ino != staging.stat().st_ino:
|
|
471
|
+
_fail(f"durable stop-intent slot is not the staged hardlink: {target}")
|
|
472
|
+
published = target
|
|
473
|
+
break
|
|
474
|
+
except OSError as exc:
|
|
475
|
+
# If directory fsync reported after a successful link, that linked
|
|
476
|
+
# slot is already the durable publication boundary.
|
|
477
|
+
if target.exists():
|
|
478
|
+
try:
|
|
479
|
+
if (
|
|
480
|
+
_read_file(target, limit=MAX_STOP_INTENT_BYTES) == raw
|
|
481
|
+
and target.stat().st_ino == staging.stat().st_ino
|
|
482
|
+
):
|
|
483
|
+
published = target
|
|
484
|
+
break
|
|
485
|
+
except EnvelopeError:
|
|
486
|
+
pass
|
|
487
|
+
errors.append(f"{target.name}: {exc}")
|
|
488
|
+
if published is None:
|
|
489
|
+
raise EnvelopeError(
|
|
490
|
+
"cannot publish durable stop intent in either predeclared slot: "
|
|
491
|
+
+ "; ".join(errors)
|
|
492
|
+
)
|
|
493
|
+
try:
|
|
494
|
+
staging.unlink()
|
|
495
|
+
_fsync_directory(staging.parent)
|
|
496
|
+
except OSError:
|
|
497
|
+
# The marker is already durable. The exact alias path and bytes are
|
|
498
|
+
# bound inside the marker and validated on every replay.
|
|
499
|
+
pass
|
|
500
|
+
return published
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _fsync_directory(path: Path) -> None:
|
|
504
|
+
flags = os.O_RDONLY
|
|
505
|
+
if hasattr(os, "O_DIRECTORY"):
|
|
506
|
+
flags |= os.O_DIRECTORY
|
|
507
|
+
descriptor = os.open(path, flags)
|
|
508
|
+
try:
|
|
509
|
+
os.fsync(descriptor)
|
|
510
|
+
finally:
|
|
511
|
+
os.close(descriptor)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _validate_run_tree(run_dir: Path) -> None:
|
|
515
|
+
if run_dir.is_symlink() or not run_dir.is_dir():
|
|
516
|
+
_fail(f"run directory must be a real non-symlink directory: {run_dir}")
|
|
517
|
+
try:
|
|
518
|
+
links = [path for path in run_dir.rglob("*") if path.is_symlink()]
|
|
519
|
+
except OSError as exc:
|
|
520
|
+
raise EnvelopeError(f"cannot inspect run directory {run_dir}: {exc}") from exc
|
|
521
|
+
if links:
|
|
522
|
+
_fail(f"run directory must not contain symlinks: {links[0]}")
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _replace_json(
|
|
526
|
+
path: Path,
|
|
527
|
+
value: dict[str, Any],
|
|
528
|
+
*,
|
|
529
|
+
staging_ref: str | None = None,
|
|
530
|
+
) -> None:
|
|
531
|
+
raw = _json_bytes(value)
|
|
532
|
+
temporary = (
|
|
533
|
+
path.parent / staging_ref
|
|
534
|
+
if staging_ref is not None
|
|
535
|
+
else path.with_name(f".{path.name}.replace-{_sha(raw)[:24]}.json")
|
|
536
|
+
)
|
|
537
|
+
_ensure_exact_new(temporary, raw)
|
|
538
|
+
try:
|
|
539
|
+
os.replace(temporary, path)
|
|
540
|
+
_fsync_directory(path.parent)
|
|
541
|
+
except OSError as exc:
|
|
542
|
+
# Keep the complete content-addressed staging bytes. A stopped-state
|
|
543
|
+
# marker can replay this exact replacement; normal ingestion registers
|
|
544
|
+
# the surviving file as pre-stop evidence.
|
|
545
|
+
raise EnvelopeError(
|
|
546
|
+
f"cannot atomically replace {path}; complete staging preserved at "
|
|
547
|
+
f"{temporary}: {exc}"
|
|
548
|
+
) from exc
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _relative_files_and_directories(run_dir: Path) -> tuple[set[str], set[str]]:
|
|
552
|
+
files: set[str] = set()
|
|
553
|
+
directories: set[str] = set()
|
|
554
|
+
try:
|
|
555
|
+
for path in run_dir.rglob("*"):
|
|
556
|
+
relative = str(path.relative_to(run_dir))
|
|
557
|
+
if path.is_symlink():
|
|
558
|
+
_fail(f"run directory must not contain symlinks: {path}")
|
|
559
|
+
if path.is_file():
|
|
560
|
+
files.add(relative)
|
|
561
|
+
elif path.is_dir():
|
|
562
|
+
directories.add(relative)
|
|
563
|
+
else:
|
|
564
|
+
_fail(f"run directory contains a non-file entry: {path}")
|
|
565
|
+
except OSError as exc:
|
|
566
|
+
raise EnvelopeError(f"cannot inventory run directory {run_dir}: {exc}") from exc
|
|
567
|
+
return files, directories
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _inventory_binding(
|
|
571
|
+
run_dir: Path,
|
|
572
|
+
file_refs: set[str],
|
|
573
|
+
directory_refs: set[str],
|
|
574
|
+
) -> dict[str, Any]:
|
|
575
|
+
rows: list[dict[str, Any]] = []
|
|
576
|
+
for ref in sorted(directory_refs):
|
|
577
|
+
rows.append({"entry_type": "directory", "ref": ref})
|
|
578
|
+
for ref in sorted(file_refs):
|
|
579
|
+
digest, size = _hash_file_stable(run_dir / ref)
|
|
580
|
+
rows.append(
|
|
581
|
+
{
|
|
582
|
+
"entry_type": "file",
|
|
583
|
+
"ref": ref,
|
|
584
|
+
"sha256": digest,
|
|
585
|
+
"size_bytes": size,
|
|
586
|
+
}
|
|
587
|
+
)
|
|
588
|
+
return {"entry_count": len(rows), "sha256": _sha(_canonical(rows))}
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _parent_directories(refs: set[str]) -> set[str]:
|
|
592
|
+
directories: set[str] = set()
|
|
593
|
+
for ref in refs:
|
|
594
|
+
parent = Path(ref).parent
|
|
595
|
+
while str(parent) != ".":
|
|
596
|
+
directories.add(str(parent))
|
|
597
|
+
parent = parent.parent
|
|
598
|
+
return directories
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _journal_ref(state: str, cell_id: str) -> str:
|
|
602
|
+
return f"{ATTEMPT_JOURNAL_ROOT}/{state}/{cell_id}.json"
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _journal_value(plan_sha: str, cell: dict[str, Any]) -> dict[str, Any]:
|
|
606
|
+
return {
|
|
607
|
+
"schema_version": "ideation-diversity-attempt-guard/1.0",
|
|
608
|
+
"suite": SUITE,
|
|
609
|
+
"run_plan_sha256": plan_sha,
|
|
610
|
+
"cell_id": cell["cell_id"],
|
|
611
|
+
"sequence_index": cell["sequence_index"],
|
|
612
|
+
"purpose": "precommitted_single_ingestion_attempt_retry_guard",
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _journal_raw(plan_sha: str, cell: dict[str, Any]) -> bytes:
|
|
617
|
+
return _json_bytes(_journal_value(plan_sha, cell))
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _journal_observed_state(run_dir: Path, cell_id: str) -> str | None:
|
|
621
|
+
states = [
|
|
622
|
+
state
|
|
623
|
+
for state in ("ready", "active", "completed")
|
|
624
|
+
if (run_dir / _journal_ref(state, cell_id)).exists()
|
|
625
|
+
]
|
|
626
|
+
if len(states) > 1:
|
|
627
|
+
_fail(f"terminal journal has multiple states for {cell_id}")
|
|
628
|
+
return states[0] if states else None
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def _validate_terminal_journal(
|
|
632
|
+
run_dir: Path,
|
|
633
|
+
plan: dict[str, Any],
|
|
634
|
+
plan_sha: str,
|
|
635
|
+
manifest: dict[str, Any],
|
|
636
|
+
*,
|
|
637
|
+
durable_stop_present: bool,
|
|
638
|
+
) -> None:
|
|
639
|
+
if manifest["status"] == "initialized":
|
|
640
|
+
if (run_dir / ATTEMPT_JOURNAL_ROOT).exists():
|
|
641
|
+
_fail("initialized run must not contain a terminal attempt journal")
|
|
642
|
+
return
|
|
643
|
+
ingested = manifest["next_sequence_index"] - 1
|
|
644
|
+
for index, cell in enumerate(plan["cells"]):
|
|
645
|
+
state = _journal_observed_state(run_dir, cell["cell_id"])
|
|
646
|
+
if state is None:
|
|
647
|
+
_fail(f"terminal journal guard is missing for {cell['cell_id']}")
|
|
648
|
+
raw = _read_file(run_dir / _journal_ref(state, cell["cell_id"]))
|
|
649
|
+
if raw != _journal_raw(plan_sha, cell):
|
|
650
|
+
_fail(f"terminal journal guard bytes drifted for {cell['cell_id']}")
|
|
651
|
+
if index < ingested:
|
|
652
|
+
if state not in {"active", "completed"}:
|
|
653
|
+
_fail(f"ingested cell terminal journal did not advance: {cell['cell_id']}")
|
|
654
|
+
elif manifest["stopped"] and index == ingested:
|
|
655
|
+
if state != "active":
|
|
656
|
+
_fail("blocked cell must retain its active retry guard")
|
|
657
|
+
elif index == ingested and state == "active" and not durable_stop_present:
|
|
658
|
+
_fail(
|
|
659
|
+
"active terminal journal permanently forbids retry after an "
|
|
660
|
+
"unrecoverable first-stop publication failure"
|
|
661
|
+
)
|
|
662
|
+
elif state != "ready":
|
|
663
|
+
_fail(f"future cell terminal journal advanced early: {cell['cell_id']}")
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _recover_completed_attempts(
|
|
667
|
+
run_dir: Path, plan: dict[str, Any], manifest: dict[str, Any]
|
|
668
|
+
) -> None:
|
|
669
|
+
ingested = manifest["next_sequence_index"] - 1
|
|
670
|
+
for cell in plan["cells"][:ingested]:
|
|
671
|
+
active = run_dir / _journal_ref("active", cell["cell_id"])
|
|
672
|
+
completed = run_dir / _journal_ref("completed", cell["cell_id"])
|
|
673
|
+
if active.exists():
|
|
674
|
+
try:
|
|
675
|
+
os.rename(active, completed)
|
|
676
|
+
_fsync_directory(active.parent)
|
|
677
|
+
_fsync_directory(completed.parent)
|
|
678
|
+
except OSError as exc:
|
|
679
|
+
raise EnvelopeError(
|
|
680
|
+
f"cannot recover accepted terminal journal for {cell['cell_id']}: {exc}"
|
|
681
|
+
) from exc
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _begin_ingestion_attempt(
|
|
685
|
+
run_dir: Path, plan: dict[str, Any], plan_sha: str, manifest: dict[str, Any]
|
|
686
|
+
) -> None:
|
|
687
|
+
# Recover only already-accepted cells. No new external bytes are read until
|
|
688
|
+
# every prior journal transition is durable.
|
|
689
|
+
_recover_completed_attempts(run_dir, plan, manifest)
|
|
690
|
+
ingested = manifest["next_sequence_index"] - 1
|
|
691
|
+
cell = plan["cells"][ingested]
|
|
692
|
+
ready = run_dir / _journal_ref("ready", cell["cell_id"])
|
|
693
|
+
active = run_dir / _journal_ref("active", cell["cell_id"])
|
|
694
|
+
try:
|
|
695
|
+
os.rename(ready, active)
|
|
696
|
+
_fsync_directory(ready.parent)
|
|
697
|
+
_fsync_directory(active.parent)
|
|
698
|
+
except OSError as exc:
|
|
699
|
+
raise EnvelopeError(
|
|
700
|
+
"cannot durably activate the precommitted terminal attempt guard; "
|
|
701
|
+
"no transcript bytes were acquired"
|
|
702
|
+
) from exc
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def _complete_ingestion_attempt(run_dir: Path, cell_id: str) -> None:
|
|
706
|
+
active = run_dir / _journal_ref("active", cell_id)
|
|
707
|
+
completed = run_dir / _journal_ref("completed", cell_id)
|
|
708
|
+
try:
|
|
709
|
+
os.rename(active, completed)
|
|
710
|
+
_fsync_directory(active.parent)
|
|
711
|
+
_fsync_directory(completed.parent)
|
|
712
|
+
except OSError as exc:
|
|
713
|
+
raise EnvelopeError(
|
|
714
|
+
f"accepted state is durable but terminal journal recovery remains pending: {exc}"
|
|
715
|
+
) from exc
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _schema(path: Path) -> dict[str, Any]:
|
|
719
|
+
schema = _strict_loads(_read_file(path))
|
|
720
|
+
try:
|
|
721
|
+
Draft202012Validator.check_schema(schema)
|
|
722
|
+
except Exception as exc:
|
|
723
|
+
raise EnvelopeError(f"invalid Draft 2020-12 schema {path}: {exc}") from exc
|
|
724
|
+
return schema
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def _validate_schema(path: Path, value: dict[str, Any], label: str) -> None:
|
|
728
|
+
errors = sorted(
|
|
729
|
+
Draft202012Validator(
|
|
730
|
+
_schema(path), format_checker=FormatChecker()
|
|
731
|
+
).iter_errors(value),
|
|
732
|
+
key=lambda error: list(error.absolute_path),
|
|
733
|
+
)
|
|
734
|
+
if errors:
|
|
735
|
+
first = errors[0]
|
|
736
|
+
location = "$" + "".join(f"[{part!r}]" for part in first.absolute_path)
|
|
737
|
+
_fail(f"{label} schema failure at {location}: {first.message}")
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _timestamp(value: str, label: str) -> datetime:
|
|
741
|
+
if not isinstance(value, str):
|
|
742
|
+
_fail(f"invalid {label} date-time")
|
|
743
|
+
try:
|
|
744
|
+
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
745
|
+
except ValueError as exc:
|
|
746
|
+
raise EnvelopeError(f"invalid {label} date-time") from exc
|
|
747
|
+
if parsed.tzinfo is None:
|
|
748
|
+
_fail(f"{label} must include a UTC offset")
|
|
749
|
+
return parsed
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def _repo_path(ref: str) -> Path:
|
|
753
|
+
path = Path(ref)
|
|
754
|
+
if path.is_absolute() or ".." in path.parts or not ref:
|
|
755
|
+
_fail(f"unsafe repository asset reference {ref!r}")
|
|
756
|
+
return REPO_ROOT / path
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
def _asset_bindings() -> list[dict[str, str]]:
|
|
760
|
+
return [
|
|
761
|
+
{"path": ref, "sha256": _sha(_read_file(_repo_path(ref)))}
|
|
762
|
+
for ref in ASSET_PATHS
|
|
763
|
+
]
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def _scenario_index(heldout: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
|
767
|
+
return {row["scenario_id"]: row for row in heldout["scenarios"]}
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def _actor_packet(cell_id: str, scenario: dict[str, Any], blind_fields: list[str]) -> dict[str, Any]:
|
|
771
|
+
return {
|
|
772
|
+
"schema_version": "ideation-diversity-actor-packet/1.0",
|
|
773
|
+
"suite": SUITE,
|
|
774
|
+
"actor_session_id": cell_id,
|
|
775
|
+
"content_class": "repository-owned synthetic scholar role card",
|
|
776
|
+
"blind_fields": blind_fields,
|
|
777
|
+
"role_card": {
|
|
778
|
+
key: scenario[key]
|
|
779
|
+
for key in (
|
|
780
|
+
"language",
|
|
781
|
+
"domain",
|
|
782
|
+
"role_brief",
|
|
783
|
+
"initial_message",
|
|
784
|
+
"owned_framings",
|
|
785
|
+
"response_policy",
|
|
786
|
+
"forbidden_out_of_role",
|
|
787
|
+
)
|
|
788
|
+
},
|
|
789
|
+
"model_generated_actor": False,
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def _session_envelope(cell: dict[str, Any]) -> dict[str, Any]:
|
|
794
|
+
return {
|
|
795
|
+
"schema_version": "ideation-diversity-session-envelope/1.0",
|
|
796
|
+
"suite": SUITE,
|
|
797
|
+
"cell_id": cell["cell_id"],
|
|
798
|
+
"sequence_index": cell["sequence_index"],
|
|
799
|
+
"prompt_path": cell["prompt"]["path"],
|
|
800
|
+
"prompt_sha256": cell["prompt"]["sha256"],
|
|
801
|
+
"environment": cell["prompt"]["environment"],
|
|
802
|
+
"actor_packet_path": cell["actor_packet_path"],
|
|
803
|
+
"actor_packet_sha256": cell["actor_packet_sha256"],
|
|
804
|
+
"fresh_context_required": True,
|
|
805
|
+
"tools": [],
|
|
806
|
+
"web_enabled": False,
|
|
807
|
+
"runner_transport": "none",
|
|
808
|
+
"dispatch_available": False,
|
|
809
|
+
"api_spend_ceiling_usd": 0,
|
|
810
|
+
"api_fallback": False,
|
|
811
|
+
"envelope_grants_consent": False,
|
|
812
|
+
"fresh_external_authorization_required": True,
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def _prompt_binding(arm_id: str, production_sha: str, variant_sha: str) -> dict[str, Any]:
|
|
817
|
+
if arm_id == "adjacent_probe_on":
|
|
818
|
+
return {
|
|
819
|
+
"kind": "production",
|
|
820
|
+
"path": "materials/prompts/production-socratic-mentor.md",
|
|
821
|
+
"sha256": production_sha,
|
|
822
|
+
"environment": {"ARS_SOCRATIC_ADJACENT_PROBE": "1"},
|
|
823
|
+
}
|
|
824
|
+
if arm_id == "exploratory_guardrails_ablated":
|
|
825
|
+
return {
|
|
826
|
+
"kind": "nonproduction_ablation",
|
|
827
|
+
"path": "materials/prompts/exploratory-guardrails-ablated.md",
|
|
828
|
+
"sha256": variant_sha,
|
|
829
|
+
"environment": {},
|
|
830
|
+
}
|
|
831
|
+
return {
|
|
832
|
+
"kind": "production",
|
|
833
|
+
"path": "materials/prompts/production-socratic-mentor.md",
|
|
834
|
+
"sha256": production_sha,
|
|
835
|
+
"environment": {},
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def _ordered_assignments(seed: str, scenarios: list[dict[str, Any]]) -> list[tuple[str, str, dict[str, Any], int, int]]:
|
|
840
|
+
blocks: list[tuple[str, tuple[str, str], dict[str, Any]]] = []
|
|
841
|
+
for experiment_id, arms in EXPERIMENTS:
|
|
842
|
+
for scenario in scenarios:
|
|
843
|
+
blocks.append((experiment_id, arms, scenario))
|
|
844
|
+
blocks.sort(
|
|
845
|
+
key=lambda block: _sha(
|
|
846
|
+
f"{seed}\0{block[0]}\0{block[2]['scenario_id']}".encode("utf-8")
|
|
847
|
+
)
|
|
848
|
+
)
|
|
849
|
+
assignments: list[tuple[str, str, dict[str, Any], int, int]] = []
|
|
850
|
+
for block_index, (experiment_id, arms, scenario) in enumerate(blocks, 1):
|
|
851
|
+
orientation = int(
|
|
852
|
+
_sha(f"{seed}\0orientation\0{experiment_id}\0{scenario['scenario_id']}".encode()),
|
|
853
|
+
16,
|
|
854
|
+
) % 2
|
|
855
|
+
first = arms if orientation == 0 else tuple(reversed(arms))
|
|
856
|
+
for replicate, arm_order in ((1, first), (2, tuple(reversed(first)))):
|
|
857
|
+
for arm_id in arm_order:
|
|
858
|
+
assignments.append(
|
|
859
|
+
(experiment_id, arm_id, scenario, replicate, block_index)
|
|
860
|
+
)
|
|
861
|
+
return assignments
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def _build_plan(config: dict[str, Any]) -> dict[str, Any]:
|
|
865
|
+
heldout, variant = phase1.load_assets()
|
|
866
|
+
production_raw = _read_file(PRODUCTION_PROMPT)
|
|
867
|
+
rendered_raw = phase1.render_variant(variant).encode("utf-8")
|
|
868
|
+
production_sha = _sha(production_raw)
|
|
869
|
+
variant_sha = _sha(rendered_raw)
|
|
870
|
+
cells: list[dict[str, Any]] = []
|
|
871
|
+
for sequence_index, assignment in enumerate(
|
|
872
|
+
_ordered_assignments(config["order_seed"], heldout["scenarios"]), 1
|
|
873
|
+
):
|
|
874
|
+
experiment_id, arm_id, scenario, replicate, block_index = assignment
|
|
875
|
+
cell_id = f"cell-{sequence_index:03d}"
|
|
876
|
+
scenario_sha = _sha(_canonical(scenario))
|
|
877
|
+
actor_packet_path = f"materials/actor-packets/{cell_id}.json"
|
|
878
|
+
actor_packet = _actor_packet(
|
|
879
|
+
cell_id, scenario, heldout["actor_blind_fields"]
|
|
880
|
+
)
|
|
881
|
+
cell: dict[str, Any] = {
|
|
882
|
+
"cell_id": cell_id,
|
|
883
|
+
"sequence_index": sequence_index,
|
|
884
|
+
"experiment_id": experiment_id,
|
|
885
|
+
"arm_id": arm_id,
|
|
886
|
+
"scenario_id": scenario["scenario_id"],
|
|
887
|
+
"pair_id": scenario["pair_id"],
|
|
888
|
+
"language": scenario["language"],
|
|
889
|
+
"replicate": replicate,
|
|
890
|
+
"actor_block_id": f"actor-block-{block_index:02d}-r{replicate}",
|
|
891
|
+
"prompt": _prompt_binding(arm_id, production_sha, variant_sha),
|
|
892
|
+
"scenario_sha256": scenario_sha,
|
|
893
|
+
"initial_message_sha256": _sha(scenario["initial_message"].encode("utf-8")),
|
|
894
|
+
"actor_packet_path": actor_packet_path,
|
|
895
|
+
"actor_packet_sha256": _sha(_json_bytes(actor_packet)),
|
|
896
|
+
"session_envelope_path": f"materials/session-envelopes/{cell_id}.json",
|
|
897
|
+
"session_envelope_sha256": "0" * 64,
|
|
898
|
+
"fresh_context_required": True,
|
|
899
|
+
}
|
|
900
|
+
cell["session_envelope_sha256"] = _sha(_json_bytes(_session_envelope(cell)))
|
|
901
|
+
cells.append(cell)
|
|
902
|
+
cell_ids = [cell["cell_id"] for cell in cells]
|
|
903
|
+
order_projection = [
|
|
904
|
+
{
|
|
905
|
+
"cell_id": cell["cell_id"],
|
|
906
|
+
"sequence_index": cell["sequence_index"],
|
|
907
|
+
"experiment_id": cell["experiment_id"],
|
|
908
|
+
"scenario_id": cell["scenario_id"],
|
|
909
|
+
"arm_id": cell["arm_id"],
|
|
910
|
+
"replicate": cell["replicate"],
|
|
911
|
+
"actor_block_id": cell["actor_block_id"],
|
|
912
|
+
}
|
|
913
|
+
for cell in cells
|
|
914
|
+
]
|
|
915
|
+
return {
|
|
916
|
+
"schema_version": "ideation-diversity-run-plan/1.0",
|
|
917
|
+
"suite": SUITE,
|
|
918
|
+
"phase": "phase_2_no_call_execution_envelope",
|
|
919
|
+
"status": "frozen_no_call",
|
|
920
|
+
"run_id": config["run_id"],
|
|
921
|
+
"suite_commit": config["suite_commit"],
|
|
922
|
+
"suite_commit_provenance": {
|
|
923
|
+
"status": "operator_declared_unverified",
|
|
924
|
+
"verified_by_no_call_runner": False,
|
|
925
|
+
"verifiable_authority": "run_plan_sha256_and_asset_bindings",
|
|
926
|
+
},
|
|
927
|
+
"content_class": "repository-owned synthetic scholar role cards",
|
|
928
|
+
"created_without_dispatch": True,
|
|
929
|
+
"asset_bindings": _asset_bindings(),
|
|
930
|
+
"rendered_variant_sha256": variant_sha,
|
|
931
|
+
"execution": {
|
|
932
|
+
"subject_provider": config["subject_provider"],
|
|
933
|
+
"subject_model": config["subject_model"],
|
|
934
|
+
"subject_runtime": config["subject_runtime"],
|
|
935
|
+
"subject_runtime_version": config["subject_runtime_version"],
|
|
936
|
+
"auth_mode": config["auth_mode"],
|
|
937
|
+
"reasoning_effort": config["reasoning_effort"],
|
|
938
|
+
"input_token_cap": config["input_token_cap"],
|
|
939
|
+
"output_token_cap": config["output_token_cap"],
|
|
940
|
+
"token_cap_verification": {
|
|
941
|
+
"status": "operator_declared_unverified",
|
|
942
|
+
"enforced_by_no_call_runner": False,
|
|
943
|
+
"observed_usage_recorded": False,
|
|
944
|
+
"provider_tokenizer_verified": False,
|
|
945
|
+
},
|
|
946
|
+
"tools": [],
|
|
947
|
+
"web_enabled": False,
|
|
948
|
+
"runner_transport": "none",
|
|
949
|
+
"dispatch_available": False,
|
|
950
|
+
"api_spend_ceiling_usd": 0,
|
|
951
|
+
"api_fallback": False,
|
|
952
|
+
"envelope_grants_consent": False,
|
|
953
|
+
"fresh_external_authorization_required": True,
|
|
954
|
+
},
|
|
955
|
+
"design": {
|
|
956
|
+
"experiments": 2,
|
|
957
|
+
"scenarios": 6,
|
|
958
|
+
"arms_per_experiment": 2,
|
|
959
|
+
"replicates_per_scenario_arm": 2,
|
|
960
|
+
"subject_session_cells": 48,
|
|
961
|
+
"fresh_context_per_cell": True,
|
|
962
|
+
"adaptive_actor_forbidden": True,
|
|
963
|
+
"model_generated_actor_forbidden": True,
|
|
964
|
+
},
|
|
965
|
+
"order": {
|
|
966
|
+
"algorithm": "sha256-balanced-block-v1",
|
|
967
|
+
"seed": config["order_seed"],
|
|
968
|
+
"block_count": 12,
|
|
969
|
+
"cell_ids": cell_ids,
|
|
970
|
+
"order_sha256": _sha(_canonical(order_projection)),
|
|
971
|
+
},
|
|
972
|
+
"cells": cells,
|
|
973
|
+
"stop_rules": STOP_RULES,
|
|
974
|
+
"judge_requirements": {
|
|
975
|
+
"minimum_independent_judges": 2,
|
|
976
|
+
"separate_arm_blind_adjudicator_required": True,
|
|
977
|
+
"raw_labels_must_be_retained": True,
|
|
978
|
+
"human_evidence_may_not_be_fabricated": True,
|
|
979
|
+
"packet_contains_no_labels": True,
|
|
980
|
+
"first_round_assignment_ledger_required_before_delivery": True,
|
|
981
|
+
"same_role_card_cross_arm_or_replicate_exposure_forbidden": True,
|
|
982
|
+
"bundle_alone_proves_judge_exposure_blindness": False,
|
|
983
|
+
},
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _config_from_plan(plan: dict[str, Any]) -> dict[str, Any]:
|
|
988
|
+
execution = plan["execution"]
|
|
989
|
+
return {
|
|
990
|
+
"run_id": plan["run_id"],
|
|
991
|
+
"suite_commit": plan["suite_commit"],
|
|
992
|
+
"order_seed": plan["order"]["seed"],
|
|
993
|
+
"subject_provider": execution["subject_provider"],
|
|
994
|
+
"subject_model": execution["subject_model"],
|
|
995
|
+
"subject_runtime": execution["subject_runtime"],
|
|
996
|
+
"subject_runtime_version": execution["subject_runtime_version"],
|
|
997
|
+
"auth_mode": execution["auth_mode"],
|
|
998
|
+
"reasoning_effort": execution["reasoning_effort"],
|
|
999
|
+
"input_token_cap": execution["input_token_cap"],
|
|
1000
|
+
"output_token_cap": execution["output_token_cap"],
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
def _validate_execution_text(config: dict[str, Any]) -> None:
|
|
1005
|
+
for key in (
|
|
1006
|
+
"subject_provider",
|
|
1007
|
+
"subject_model",
|
|
1008
|
+
"subject_runtime",
|
|
1009
|
+
"subject_runtime_version",
|
|
1010
|
+
"auth_mode",
|
|
1011
|
+
"reasoning_effort",
|
|
1012
|
+
):
|
|
1013
|
+
_require_identity_text(config[key], f"execution {key}")
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
def _validate_authorization_record(
|
|
1017
|
+
record: dict[str, Any],
|
|
1018
|
+
plan: dict[str, Any],
|
|
1019
|
+
plan_sha: str,
|
|
1020
|
+
) -> None:
|
|
1021
|
+
_validate_schema(AUTHORIZATION_SCHEMA, record, "authorization record")
|
|
1022
|
+
expected = {
|
|
1023
|
+
"run_plan_sha256": plan_sha,
|
|
1024
|
+
"run_id": plan["run_id"],
|
|
1025
|
+
"suite_commit": plan["suite_commit"],
|
|
1026
|
+
"suite_commit_provenance": plan["suite_commit_provenance"],
|
|
1027
|
+
"execution": plan["execution"],
|
|
1028
|
+
}
|
|
1029
|
+
for key, value in expected.items():
|
|
1030
|
+
if record[key] != value:
|
|
1031
|
+
_fail(f"authorization record {key} differs from frozen run plan")
|
|
1032
|
+
scope = record["scope"]
|
|
1033
|
+
if scope["cell_ids"] != plan["order"]["cell_ids"]:
|
|
1034
|
+
_fail("authorization record is not scoped to the exact ordered 48 cells")
|
|
1035
|
+
if scope["order_sha256"] != plan["order"]["order_sha256"]:
|
|
1036
|
+
_fail("authorization record order hash differs from frozen run plan")
|
|
1037
|
+
if record["decision"]["status"] != "authorized":
|
|
1038
|
+
_fail("authorization record decision is not authorized")
|
|
1039
|
+
_require_identity_text(
|
|
1040
|
+
record["decision"]["operator_reference"],
|
|
1041
|
+
"authorization operator_reference",
|
|
1042
|
+
)
|
|
1043
|
+
_require_identity_text(
|
|
1044
|
+
record["decision"]["statement"],
|
|
1045
|
+
"authorization statement",
|
|
1046
|
+
)
|
|
1047
|
+
_timestamp(record["decision"]["decided_at"], "authorization decided_at")
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
def _validate_plan(plan: dict[str, Any]) -> None:
|
|
1051
|
+
_validate_schema(RUN_PLAN_SCHEMA, plan, "run plan")
|
|
1052
|
+
config = _config_from_plan(plan)
|
|
1053
|
+
_validate_execution_text(config)
|
|
1054
|
+
expected = _build_plan(config)
|
|
1055
|
+
if _canonical(plan) != _canonical(expected):
|
|
1056
|
+
_fail("run plan differs from the exact current asset/order/hash expansion")
|
|
1057
|
+
cells = plan["cells"]
|
|
1058
|
+
if len({cell["cell_id"] for cell in cells}) != 48:
|
|
1059
|
+
_fail("run plan cell ids must be unique")
|
|
1060
|
+
observed = {
|
|
1061
|
+
(
|
|
1062
|
+
cell["experiment_id"],
|
|
1063
|
+
cell["scenario_id"],
|
|
1064
|
+
cell["arm_id"],
|
|
1065
|
+
cell["replicate"],
|
|
1066
|
+
)
|
|
1067
|
+
for cell in cells
|
|
1068
|
+
}
|
|
1069
|
+
expected_cross_product = {
|
|
1070
|
+
(experiment, scenario["scenario_id"], arm, replicate)
|
|
1071
|
+
for experiment, arms in EXPERIMENTS
|
|
1072
|
+
for scenario in phase1.load_assets()[0]["scenarios"]
|
|
1073
|
+
for arm in arms
|
|
1074
|
+
for replicate in (1, 2)
|
|
1075
|
+
}
|
|
1076
|
+
if observed != expected_cross_product:
|
|
1077
|
+
_fail("run plan is not the exact 2 x 6 x 2 x 2 cross-product")
|
|
1078
|
+
for experiment, arms in EXPERIMENTS:
|
|
1079
|
+
for scenario in phase1.load_assets()[0]["scenarios"]:
|
|
1080
|
+
rows = [
|
|
1081
|
+
cell
|
|
1082
|
+
for cell in cells
|
|
1083
|
+
if cell["experiment_id"] == experiment
|
|
1084
|
+
and cell["scenario_id"] == scenario["scenario_id"]
|
|
1085
|
+
]
|
|
1086
|
+
orders = [
|
|
1087
|
+
[cell["arm_id"] for cell in rows if cell["replicate"] == replicate]
|
|
1088
|
+
for replicate in (1, 2)
|
|
1089
|
+
]
|
|
1090
|
+
if len(orders[0]) != 2 or orders[1] != list(reversed(orders[0])):
|
|
1091
|
+
_fail(f"arm order is not counterbalanced for {experiment}/{scenario['scenario_id']}")
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def _initial_manifest(plan: dict[str, Any], plan_sha: str) -> dict[str, Any]:
|
|
1095
|
+
return {
|
|
1096
|
+
"schema_version": "ideation-diversity-ingestion-manifest/1.0",
|
|
1097
|
+
"suite": SUITE,
|
|
1098
|
+
"run_plan_sha256": plan_sha,
|
|
1099
|
+
"authorization_record_path": None,
|
|
1100
|
+
"authorization_record_sha256": None,
|
|
1101
|
+
"blind_manifest_ref": None,
|
|
1102
|
+
"blind_manifest_sha256": None,
|
|
1103
|
+
"status": "initialized",
|
|
1104
|
+
"next_sequence_index": 1,
|
|
1105
|
+
"stopped": False,
|
|
1106
|
+
"stop_receipt": None,
|
|
1107
|
+
"cells": [
|
|
1108
|
+
{
|
|
1109
|
+
"cell_id": cell["cell_id"],
|
|
1110
|
+
"sequence_index": cell["sequence_index"],
|
|
1111
|
+
"status": "pending",
|
|
1112
|
+
"transcript_ref": None,
|
|
1113
|
+
"transcript_sha256": None,
|
|
1114
|
+
"ingestion_receipt_ref": None,
|
|
1115
|
+
}
|
|
1116
|
+
for cell in plan["cells"]
|
|
1117
|
+
],
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def _validate_manifest(manifest: dict[str, Any], plan: dict[str, Any], plan_sha: str) -> None:
|
|
1122
|
+
_validate_schema(INGESTION_SCHEMA, manifest, "ingestion manifest")
|
|
1123
|
+
if manifest["run_plan_sha256"] != plan_sha:
|
|
1124
|
+
_fail("ingestion manifest is bound to a different run plan")
|
|
1125
|
+
authorization_bound = (
|
|
1126
|
+
manifest["authorization_record_path"] is not None,
|
|
1127
|
+
manifest["authorization_record_sha256"] is not None,
|
|
1128
|
+
)
|
|
1129
|
+
if authorization_bound not in {(False, False), (True, True)}:
|
|
1130
|
+
_fail("authorization-record manifest fields must be both null or both bound")
|
|
1131
|
+
blind_bound = (
|
|
1132
|
+
manifest["blind_manifest_ref"] is not None,
|
|
1133
|
+
manifest["blind_manifest_sha256"] is not None,
|
|
1134
|
+
)
|
|
1135
|
+
if blind_bound not in {(False, False), (True, True)}:
|
|
1136
|
+
_fail("blind-manifest fields must be both null or both bound")
|
|
1137
|
+
pairs = [(row["cell_id"], row["sequence_index"]) for row in manifest["cells"]]
|
|
1138
|
+
expected = [(row["cell_id"], row["sequence_index"]) for row in plan["cells"]]
|
|
1139
|
+
if pairs != expected:
|
|
1140
|
+
_fail("ingestion manifest cell order differs from run plan")
|
|
1141
|
+
rows = manifest["cells"]
|
|
1142
|
+
ingested = sum(row["status"] == "ingested" for row in rows)
|
|
1143
|
+
if ingested and authorization_bound != (True, True):
|
|
1144
|
+
_fail("ingested transcripts require a preserved authorization record")
|
|
1145
|
+
if manifest["next_sequence_index"] != ingested + 1:
|
|
1146
|
+
_fail("ingestion manifest next_sequence_index is inconsistent")
|
|
1147
|
+
if manifest["stopped"] != (manifest["status"] == "stopped"):
|
|
1148
|
+
_fail("ingestion stopped/status fields disagree")
|
|
1149
|
+
for index, row in enumerate(rows):
|
|
1150
|
+
if index < ingested:
|
|
1151
|
+
expected_status = "ingested"
|
|
1152
|
+
elif manifest["stopped"] and index == ingested:
|
|
1153
|
+
expected_status = "blocked"
|
|
1154
|
+
else:
|
|
1155
|
+
expected_status = "pending"
|
|
1156
|
+
if row["status"] != expected_status:
|
|
1157
|
+
_fail("ingestion manifest states must form an exact append-only prefix")
|
|
1158
|
+
populated = (
|
|
1159
|
+
row["transcript_ref"] is not None,
|
|
1160
|
+
row["transcript_sha256"] is not None,
|
|
1161
|
+
row["ingestion_receipt_ref"] is not None,
|
|
1162
|
+
)
|
|
1163
|
+
expected_populated = {
|
|
1164
|
+
"pending": (False, False, False),
|
|
1165
|
+
"ingested": (True, True, True),
|
|
1166
|
+
"blocked": (True, True, False),
|
|
1167
|
+
}[row["status"]]
|
|
1168
|
+
if populated != expected_populated:
|
|
1169
|
+
_fail(f"ingestion manifest references disagree for {row['cell_id']}")
|
|
1170
|
+
if row["status"] == "ingested" and (
|
|
1171
|
+
row["transcript_ref"], row["ingestion_receipt_ref"]
|
|
1172
|
+
) != (
|
|
1173
|
+
f"transcripts/{row['cell_id']}.json",
|
|
1174
|
+
f"receipts/{row['cell_id']}.json",
|
|
1175
|
+
):
|
|
1176
|
+
_fail(f"ingested artifact paths drifted for {row['cell_id']}")
|
|
1177
|
+
if row["status"] == "blocked":
|
|
1178
|
+
base_ref = (
|
|
1179
|
+
f"blocked/{row['cell_id']}.transcript."
|
|
1180
|
+
f"{row['transcript_sha256']}.raw"
|
|
1181
|
+
)
|
|
1182
|
+
if row["transcript_ref"] != base_ref and not row[
|
|
1183
|
+
"transcript_ref"
|
|
1184
|
+
].startswith(base_ref + ".collision-"):
|
|
1185
|
+
_fail(f"blocked evidence path drifted for {row['cell_id']}")
|
|
1186
|
+
state = manifest["status"]
|
|
1187
|
+
if state in {"initialized", "materialized"} and ingested != 0:
|
|
1188
|
+
_fail(f"{state} manifest cannot contain ingested cells")
|
|
1189
|
+
if state == "ingesting" and not 1 <= ingested <= 47:
|
|
1190
|
+
_fail("ingesting manifest must contain 1 through 47 ingested cells")
|
|
1191
|
+
if state in {"complete", "blind_finalized"} and ingested != 48:
|
|
1192
|
+
_fail(f"{state} manifest must contain 48 ingested cells")
|
|
1193
|
+
if state == "stopped":
|
|
1194
|
+
if ingested >= 48:
|
|
1195
|
+
_fail("stopped manifest requires one remaining blocked cell")
|
|
1196
|
+
if manifest["stop_receipt"] is None:
|
|
1197
|
+
_fail("stopped manifest requires one stop receipt")
|
|
1198
|
+
blocked = rows[ingested]
|
|
1199
|
+
receipt = manifest["stop_receipt"]
|
|
1200
|
+
if (
|
|
1201
|
+
receipt["cell_id"],
|
|
1202
|
+
receipt["sequence_index"],
|
|
1203
|
+
receipt["raw_ref"],
|
|
1204
|
+
receipt["raw_sha256"],
|
|
1205
|
+
) != (
|
|
1206
|
+
blocked["cell_id"],
|
|
1207
|
+
blocked["sequence_index"],
|
|
1208
|
+
blocked["transcript_ref"],
|
|
1209
|
+
blocked["transcript_sha256"],
|
|
1210
|
+
):
|
|
1211
|
+
_fail("stop receipt is not bound to the single blocked cell")
|
|
1212
|
+
if receipt["stop_intent_ref"] != STOP_INTENT_REF:
|
|
1213
|
+
_fail("stopped manifest is not bound to the durable stop intent")
|
|
1214
|
+
elif manifest["stop_receipt"] is not None:
|
|
1215
|
+
_fail("unstopped manifest must not contain a stop receipt")
|
|
1216
|
+
if state == "blind_finalized":
|
|
1217
|
+
if blind_bound != (True, True):
|
|
1218
|
+
_fail("blind_finalized manifest requires a bound blind manifest")
|
|
1219
|
+
if manifest["blind_manifest_ref"] != "blind/manifest.json":
|
|
1220
|
+
_fail("blind manifest path drifted")
|
|
1221
|
+
elif blind_bound != (False, False):
|
|
1222
|
+
_fail("only blind_finalized state may bind blind artifacts")
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
def _expected_run_inventory(
|
|
1226
|
+
run_dir: Path, plan: dict[str, Any], manifest: dict[str, Any]
|
|
1227
|
+
) -> tuple[set[str], set[str]]:
|
|
1228
|
+
files = {"run-plan.json", "ingestion-manifest.json"}
|
|
1229
|
+
journal_directories: set[str] = set()
|
|
1230
|
+
if manifest["status"] != "initialized":
|
|
1231
|
+
files.update(_material_map(plan))
|
|
1232
|
+
journal_directories = {
|
|
1233
|
+
ATTEMPT_JOURNAL_ROOT,
|
|
1234
|
+
f"{ATTEMPT_JOURNAL_ROOT}/ready",
|
|
1235
|
+
f"{ATTEMPT_JOURNAL_ROOT}/active",
|
|
1236
|
+
f"{ATTEMPT_JOURNAL_ROOT}/completed",
|
|
1237
|
+
}
|
|
1238
|
+
for cell in plan["cells"]:
|
|
1239
|
+
state = _journal_observed_state(run_dir, cell["cell_id"])
|
|
1240
|
+
if state is not None:
|
|
1241
|
+
files.add(_journal_ref(state, cell["cell_id"]))
|
|
1242
|
+
if manifest["authorization_record_path"] is not None:
|
|
1243
|
+
files.add(manifest["authorization_record_path"])
|
|
1244
|
+
for row in manifest["cells"]:
|
|
1245
|
+
if row["transcript_ref"] is not None:
|
|
1246
|
+
files.add(row["transcript_ref"])
|
|
1247
|
+
if row["ingestion_receipt_ref"] is not None:
|
|
1248
|
+
files.add(row["ingestion_receipt_ref"])
|
|
1249
|
+
if manifest["stop_receipt"] is not None:
|
|
1250
|
+
for ref in (STOP_INTENT_REF, STOP_INTENT_FALLBACK_REF):
|
|
1251
|
+
if (run_dir / ref).exists():
|
|
1252
|
+
files.add(ref)
|
|
1253
|
+
files.update(
|
|
1254
|
+
row["ref"]
|
|
1255
|
+
for row in manifest["stop_receipt"]["preserved_auxiliary_artifacts"]
|
|
1256
|
+
)
|
|
1257
|
+
receipt = manifest["stop_receipt"]
|
|
1258
|
+
for ref in (
|
|
1259
|
+
receipt["stop_intent_publication_staging_ref"],
|
|
1260
|
+
receipt["manifest_replacement_staging_ref"],
|
|
1261
|
+
):
|
|
1262
|
+
if (run_dir / ref).exists():
|
|
1263
|
+
files.add(ref)
|
|
1264
|
+
if (run_dir / BLIND_INTENT_REF).exists():
|
|
1265
|
+
files.add(BLIND_INTENT_REF)
|
|
1266
|
+
alias_root = run_dir / BLIND_ALIAS_ROOT
|
|
1267
|
+
alias_directories = {BLIND_ALIAS_ROOT} if alias_root.exists() else set()
|
|
1268
|
+
if alias_root.exists():
|
|
1269
|
+
observed_alias_files, observed_alias_directories = (
|
|
1270
|
+
_relative_files_and_directories(alias_root)
|
|
1271
|
+
)
|
|
1272
|
+
files.update(f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_alias_files)
|
|
1273
|
+
alias_directories.update(
|
|
1274
|
+
f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_alias_directories
|
|
1275
|
+
)
|
|
1276
|
+
staging_root = run_dir / BLIND_STAGING_REF
|
|
1277
|
+
if staging_root.exists():
|
|
1278
|
+
observed_staging_files, observed_staging_directories = (
|
|
1279
|
+
_relative_files_and_directories(staging_root)
|
|
1280
|
+
)
|
|
1281
|
+
files.update(
|
|
1282
|
+
f"{BLIND_STAGING_REF}/{ref}" for ref in observed_staging_files
|
|
1283
|
+
)
|
|
1284
|
+
staging_directories = {
|
|
1285
|
+
BLIND_STAGING_REF,
|
|
1286
|
+
*(
|
|
1287
|
+
f"{BLIND_STAGING_REF}/{ref}"
|
|
1288
|
+
for ref in observed_staging_directories
|
|
1289
|
+
),
|
|
1290
|
+
}
|
|
1291
|
+
else:
|
|
1292
|
+
staging_directories = set()
|
|
1293
|
+
else:
|
|
1294
|
+
alias_directories = set()
|
|
1295
|
+
staging_directories = set()
|
|
1296
|
+
if manifest["status"] == "blind_finalized":
|
|
1297
|
+
blind_manifest_raw = _read_file(run_dir / "blind/manifest.json")
|
|
1298
|
+
blind_manifest = _strict_loads(blind_manifest_raw)
|
|
1299
|
+
files.update(
|
|
1300
|
+
{
|
|
1301
|
+
"blind/manifest.json",
|
|
1302
|
+
blind_manifest["inventory_ref"],
|
|
1303
|
+
blind_manifest["private_map_ref"],
|
|
1304
|
+
}
|
|
1305
|
+
)
|
|
1306
|
+
files.update(row["packet_ref"] for row in blind_manifest["packets"])
|
|
1307
|
+
return (
|
|
1308
|
+
files,
|
|
1309
|
+
_parent_directories(files)
|
|
1310
|
+
| staging_directories
|
|
1311
|
+
| alias_directories
|
|
1312
|
+
| journal_directories,
|
|
1313
|
+
)
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
def _validate_run_inventory(
|
|
1317
|
+
run_dir: Path,
|
|
1318
|
+
plan: dict[str, Any],
|
|
1319
|
+
manifest: dict[str, Any],
|
|
1320
|
+
) -> None:
|
|
1321
|
+
expected_files, expected_directories = _expected_run_inventory(
|
|
1322
|
+
run_dir, plan, manifest
|
|
1323
|
+
)
|
|
1324
|
+
observed_files, observed_directories = _relative_files_and_directories(run_dir)
|
|
1325
|
+
missing_files = observed_files ^ expected_files
|
|
1326
|
+
missing_directories = observed_directories ^ expected_directories
|
|
1327
|
+
if manifest["status"] == "stopped":
|
|
1328
|
+
missing_files = expected_files - observed_files
|
|
1329
|
+
missing_directories = expected_directories - observed_directories
|
|
1330
|
+
extra_files = observed_files - expected_files
|
|
1331
|
+
extra_directories = observed_directories - expected_directories
|
|
1332
|
+
observed_binding = _inventory_binding(
|
|
1333
|
+
run_dir, extra_files, extra_directories
|
|
1334
|
+
)
|
|
1335
|
+
if observed_binding != manifest["stop_receipt"]["pre_stop_inventory"]:
|
|
1336
|
+
_fail("pre-stop compact inventory digest/count drifted")
|
|
1337
|
+
if missing_files:
|
|
1338
|
+
unexpected = sorted(observed_files - expected_files)
|
|
1339
|
+
missing = sorted(expected_files - observed_files)
|
|
1340
|
+
_fail(
|
|
1341
|
+
"run file inventory drifted; "
|
|
1342
|
+
f"unexpected={unexpected[:3]!r}, missing={missing[:3]!r}"
|
|
1343
|
+
)
|
|
1344
|
+
if missing_directories:
|
|
1345
|
+
unexpected = sorted(observed_directories - expected_directories)
|
|
1346
|
+
missing = sorted(expected_directories - observed_directories)
|
|
1347
|
+
_fail(
|
|
1348
|
+
"run directory inventory drifted; "
|
|
1349
|
+
f"unexpected={unexpected[:3]!r}, missing={missing[:3]!r}"
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def _decode_base64(value: str, label: str) -> bytes:
|
|
1354
|
+
try:
|
|
1355
|
+
decoded = base64.b64decode(value.encode("ascii"), validate=True)
|
|
1356
|
+
except (UnicodeError, ValueError) as exc:
|
|
1357
|
+
raise EnvelopeError(f"invalid {label} base64") from exc
|
|
1358
|
+
if base64.b64encode(decoded).decode("ascii") != value:
|
|
1359
|
+
_fail(f"non-canonical {label} base64")
|
|
1360
|
+
return decoded
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
def _validate_stop_intent(
|
|
1364
|
+
raw: bytes,
|
|
1365
|
+
plan: dict[str, Any],
|
|
1366
|
+
plan_sha: str,
|
|
1367
|
+
) -> tuple[dict[str, Any], dict[str, Any], bytes, bytes]:
|
|
1368
|
+
marker = _strict_loads(raw)
|
|
1369
|
+
if raw != _json_bytes(marker):
|
|
1370
|
+
_fail("stop intent is not canonical write-once replay bytes")
|
|
1371
|
+
_validate_schema(STOP_INTENT_SCHEMA, marker, "stop intent")
|
|
1372
|
+
if marker["run_plan_sha256"] != plan_sha:
|
|
1373
|
+
_fail("stop intent is bound to a different run plan")
|
|
1374
|
+
stopped_raw = _decode_base64(
|
|
1375
|
+
marker["stopped_manifest_base64"], "stopped manifest"
|
|
1376
|
+
)
|
|
1377
|
+
if _sha(stopped_raw) != marker["stopped_manifest_sha256"]:
|
|
1378
|
+
_fail("stop intent stopped-manifest hash drift")
|
|
1379
|
+
stopped = _strict_loads(stopped_raw)
|
|
1380
|
+
if stopped_raw != _json_bytes(stopped):
|
|
1381
|
+
_fail("stop intent stopped manifest is not canonical replay bytes")
|
|
1382
|
+
_validate_manifest(stopped, plan, plan_sha)
|
|
1383
|
+
raw_evidence = _decode_base64(marker["raw_base64"], "blocked raw evidence")
|
|
1384
|
+
if _sha(raw_evidence) != marker["raw_sha256"]:
|
|
1385
|
+
_fail("stop intent blocked-raw hash drift")
|
|
1386
|
+
receipt = stopped["stop_receipt"]
|
|
1387
|
+
bound = (
|
|
1388
|
+
marker["cell_id"],
|
|
1389
|
+
marker["sequence_index"],
|
|
1390
|
+
marker["reason_code"],
|
|
1391
|
+
marker["detail"],
|
|
1392
|
+
marker["raw_ref"],
|
|
1393
|
+
marker["raw_sha256"],
|
|
1394
|
+
marker["raw_capture_kind"],
|
|
1395
|
+
marker["pre_stop_inventory"],
|
|
1396
|
+
marker["stop_intent_publication_staging_ref"],
|
|
1397
|
+
marker["manifest_replacement_staging_ref"],
|
|
1398
|
+
)
|
|
1399
|
+
replayed = (
|
|
1400
|
+
receipt["cell_id"],
|
|
1401
|
+
receipt["sequence_index"],
|
|
1402
|
+
receipt["reason_code"],
|
|
1403
|
+
receipt["detail"],
|
|
1404
|
+
receipt["raw_ref"],
|
|
1405
|
+
receipt["raw_sha256"],
|
|
1406
|
+
receipt["raw_capture_kind"],
|
|
1407
|
+
receipt["pre_stop_inventory"],
|
|
1408
|
+
receipt["stop_intent_publication_staging_ref"],
|
|
1409
|
+
receipt["manifest_replacement_staging_ref"],
|
|
1410
|
+
)
|
|
1411
|
+
if bound != replayed:
|
|
1412
|
+
_fail("stop intent fields drifted from the stopped-manifest replay")
|
|
1413
|
+
return marker, stopped, stopped_raw, raw_evidence
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def _ensure_exact_new(path: Path, raw: bytes, *, mode: int = 0o644) -> None:
|
|
1417
|
+
if path.exists():
|
|
1418
|
+
if _read_file(path, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
|
|
1419
|
+
_fail(f"write-once evidence collision at {path}")
|
|
1420
|
+
return
|
|
1421
|
+
_write_new(path, raw, mode=mode)
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
def _recover_stop_intent(
|
|
1425
|
+
run_dir: Path,
|
|
1426
|
+
plan: dict[str, Any],
|
|
1427
|
+
plan_sha: str,
|
|
1428
|
+
current_manifest_raw: bytes,
|
|
1429
|
+
marker_path: Path,
|
|
1430
|
+
) -> tuple[dict[str, Any], bytes]:
|
|
1431
|
+
marker_raw = _read_file(
|
|
1432
|
+
marker_path, limit=MAX_STOP_INTENT_BYTES
|
|
1433
|
+
)
|
|
1434
|
+
marker, stopped, stopped_raw, raw_evidence = _validate_stop_intent(
|
|
1435
|
+
marker_raw, plan, plan_sha
|
|
1436
|
+
)
|
|
1437
|
+
publication_alias = run_dir / marker["stop_intent_publication_staging_ref"]
|
|
1438
|
+
if publication_alias.exists():
|
|
1439
|
+
if _read_file(publication_alias, limit=MAX_STOP_INTENT_BYTES) != marker_raw:
|
|
1440
|
+
_fail("registered stop-intent publication alias bytes drifted")
|
|
1441
|
+
if publication_alias.stat().st_ino != marker_path.stat().st_ino:
|
|
1442
|
+
_fail("registered stop-intent publication alias is not the marker hardlink")
|
|
1443
|
+
replacement_staging = run_dir / marker["manifest_replacement_staging_ref"]
|
|
1444
|
+
if replacement_staging.exists() and _read_file(
|
|
1445
|
+
replacement_staging, limit=max(MAX_INPUT_BYTES, len(stopped_raw))
|
|
1446
|
+
) != stopped_raw:
|
|
1447
|
+
_fail("registered stopped-manifest replacement staging bytes drifted")
|
|
1448
|
+
current_sha = _sha(current_manifest_raw)
|
|
1449
|
+
if current_manifest_raw == stopped_raw:
|
|
1450
|
+
pass
|
|
1451
|
+
elif current_sha == marker["prior_manifest_sha256"]:
|
|
1452
|
+
try:
|
|
1453
|
+
_ensure_exact_new(run_dir / marker["raw_ref"], raw_evidence)
|
|
1454
|
+
_replace_json(
|
|
1455
|
+
run_dir / "ingestion-manifest.json",
|
|
1456
|
+
stopped,
|
|
1457
|
+
staging_ref=marker["manifest_replacement_staging_ref"],
|
|
1458
|
+
)
|
|
1459
|
+
except EnvelopeError as exc:
|
|
1460
|
+
raise EnvelopeError(
|
|
1461
|
+
"durable stop intent forbids retry; stopped-state recovery is "
|
|
1462
|
+
f"still pending: {exc}"
|
|
1463
|
+
) from exc
|
|
1464
|
+
current_manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
|
|
1465
|
+
if current_manifest_raw != stopped_raw:
|
|
1466
|
+
_fail("durable stop recovery did not publish exact stopped state")
|
|
1467
|
+
else:
|
|
1468
|
+
_fail("manifest drifted from both pre-stop and durable stopped state")
|
|
1469
|
+
try:
|
|
1470
|
+
_ensure_exact_new(run_dir / marker["raw_ref"], raw_evidence)
|
|
1471
|
+
except EnvelopeError as exc:
|
|
1472
|
+
raise EnvelopeError(
|
|
1473
|
+
"durable stop intent forbids retry; blocked transcript hash drift or "
|
|
1474
|
+
f"evidence recovery failure: {exc}"
|
|
1475
|
+
) from exc
|
|
1476
|
+
return stopped, stopped_raw
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
def _load_run(run_dir: Path, expected_sha: str | None = None) -> tuple[dict[str, Any], bytes, str, dict[str, Any]]:
|
|
1480
|
+
_validate_run_tree(run_dir)
|
|
1481
|
+
plan_raw = _read_file(run_dir / "run-plan.json")
|
|
1482
|
+
plan_sha = _sha(plan_raw)
|
|
1483
|
+
if expected_sha is not None and expected_sha != plan_sha:
|
|
1484
|
+
_fail("--plan-sha256 does not match frozen run-plan bytes")
|
|
1485
|
+
plan = _strict_loads(plan_raw)
|
|
1486
|
+
_validate_plan(plan)
|
|
1487
|
+
manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
|
|
1488
|
+
marker_paths = [
|
|
1489
|
+
run_dir / ref
|
|
1490
|
+
for ref in (STOP_INTENT_REF, STOP_INTENT_FALLBACK_REF)
|
|
1491
|
+
if (run_dir / ref).exists()
|
|
1492
|
+
]
|
|
1493
|
+
if len(marker_paths) > 1:
|
|
1494
|
+
_fail("multiple durable stop-intent publication slots are present")
|
|
1495
|
+
if marker_paths:
|
|
1496
|
+
manifest, manifest_raw = _recover_stop_intent(
|
|
1497
|
+
run_dir, plan, plan_sha, manifest_raw, marker_paths[0]
|
|
1498
|
+
)
|
|
1499
|
+
else:
|
|
1500
|
+
manifest = _strict_loads(manifest_raw)
|
|
1501
|
+
_validate_manifest(manifest, plan, plan_sha)
|
|
1502
|
+
if manifest["stopped"] and not marker_paths:
|
|
1503
|
+
_fail("stopped state is missing its durable write-once stop intent")
|
|
1504
|
+
_validate_terminal_journal(
|
|
1505
|
+
run_dir,
|
|
1506
|
+
plan,
|
|
1507
|
+
plan_sha,
|
|
1508
|
+
manifest,
|
|
1509
|
+
durable_stop_present=bool(marker_paths),
|
|
1510
|
+
)
|
|
1511
|
+
return plan, plan_raw, plan_sha, manifest
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
def init_run(args: argparse.Namespace) -> dict[str, Any]:
|
|
1515
|
+
if args.run_dir.exists():
|
|
1516
|
+
_fail(f"run directory already exists: {args.run_dir}")
|
|
1517
|
+
config = {
|
|
1518
|
+
"run_id": args.run_id,
|
|
1519
|
+
"suite_commit": args.suite_commit,
|
|
1520
|
+
"order_seed": args.order_seed,
|
|
1521
|
+
"subject_provider": args.subject_provider,
|
|
1522
|
+
"subject_model": args.subject_model,
|
|
1523
|
+
"subject_runtime": args.subject_runtime,
|
|
1524
|
+
"subject_runtime_version": args.subject_runtime_version,
|
|
1525
|
+
"auth_mode": args.auth_mode,
|
|
1526
|
+
"reasoning_effort": args.reasoning_effort,
|
|
1527
|
+
"input_token_cap": args.input_token_cap,
|
|
1528
|
+
"output_token_cap": args.output_token_cap,
|
|
1529
|
+
}
|
|
1530
|
+
if RUN_ID_RE.fullmatch(args.run_id) is None:
|
|
1531
|
+
_fail("--run-id has invalid syntax")
|
|
1532
|
+
if COMMIT_RE.fullmatch(args.suite_commit) is None:
|
|
1533
|
+
_fail("--suite-commit must be 40 lowercase hex characters")
|
|
1534
|
+
_validate_execution_text(config)
|
|
1535
|
+
plan = _build_plan(config)
|
|
1536
|
+
_validate_plan(plan)
|
|
1537
|
+
plan_raw = _json_bytes(plan)
|
|
1538
|
+
plan_sha = _sha(plan_raw)
|
|
1539
|
+
args.run_dir.mkdir(parents=True)
|
|
1540
|
+
_validate_run_tree(args.run_dir)
|
|
1541
|
+
_write_new(args.run_dir / "run-plan.json", plan_raw)
|
|
1542
|
+
manifest = _initial_manifest(plan, plan_sha)
|
|
1543
|
+
_validate_manifest(manifest, plan, plan_sha)
|
|
1544
|
+
_write_new(args.run_dir / "ingestion-manifest.json", _json_bytes(manifest))
|
|
1545
|
+
return {"run_plan_sha256": plan_sha, "cells": 48, "dispatch_available": False}
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
def _material_map(plan: dict[str, Any]) -> dict[str, bytes]:
|
|
1549
|
+
heldout, variant = phase1.load_assets()
|
|
1550
|
+
scenarios = _scenario_index(heldout)
|
|
1551
|
+
values: dict[str, bytes] = {
|
|
1552
|
+
"materials/prompts/production-socratic-mentor.md": _read_file(PRODUCTION_PROMPT),
|
|
1553
|
+
"materials/prompts/exploratory-guardrails-ablated.md": phase1.render_variant(variant).encode("utf-8"),
|
|
1554
|
+
}
|
|
1555
|
+
for cell in plan["cells"]:
|
|
1556
|
+
scenario = scenarios[cell["scenario_id"]]
|
|
1557
|
+
actor_packet = _actor_packet(
|
|
1558
|
+
cell["cell_id"], scenario, heldout["actor_blind_fields"]
|
|
1559
|
+
)
|
|
1560
|
+
values[cell["actor_packet_path"]] = _json_bytes(actor_packet)
|
|
1561
|
+
values[cell["session_envelope_path"]] = _json_bytes(_session_envelope(cell))
|
|
1562
|
+
return values
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
def _validate_materials(run_dir: Path, plan: dict[str, Any], required: bool) -> None:
|
|
1566
|
+
expected = _material_map(plan)
|
|
1567
|
+
material_root = run_dir / "materials"
|
|
1568
|
+
if not material_root.exists():
|
|
1569
|
+
if required:
|
|
1570
|
+
_fail("materials have not been materialized")
|
|
1571
|
+
return
|
|
1572
|
+
for ref, raw in expected.items():
|
|
1573
|
+
actual = _read_file(run_dir / ref)
|
|
1574
|
+
if actual != raw:
|
|
1575
|
+
_fail(f"material bytes drifted: {ref}")
|
|
1576
|
+
expected_refs = set(expected)
|
|
1577
|
+
observed_refs = {
|
|
1578
|
+
str(path.relative_to(run_dir))
|
|
1579
|
+
for path in material_root.rglob("*")
|
|
1580
|
+
if path.is_file()
|
|
1581
|
+
}
|
|
1582
|
+
if observed_refs != expected_refs:
|
|
1583
|
+
_fail("material file inventory is not the exact frozen set")
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
def materialize(args: argparse.Namespace) -> dict[str, Any]:
|
|
1587
|
+
plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
|
|
1588
|
+
_validate_run_inventory(args.run_dir, plan, manifest)
|
|
1589
|
+
if manifest["status"] != "initialized":
|
|
1590
|
+
_fail("materialize is allowed exactly once from initialized state")
|
|
1591
|
+
if (args.run_dir / "materials").exists():
|
|
1592
|
+
_fail("refusing to overwrite existing materials")
|
|
1593
|
+
for ref, raw in _material_map(plan).items():
|
|
1594
|
+
_write_new(args.run_dir / ref, raw)
|
|
1595
|
+
(args.run_dir / ATTEMPT_JOURNAL_ROOT).mkdir()
|
|
1596
|
+
for state in ("ready", "active", "completed"):
|
|
1597
|
+
(args.run_dir / ATTEMPT_JOURNAL_ROOT / state).mkdir()
|
|
1598
|
+
for cell in plan["cells"]:
|
|
1599
|
+
_write_new(
|
|
1600
|
+
args.run_dir / _journal_ref("ready", cell["cell_id"]),
|
|
1601
|
+
_journal_raw(plan_sha, cell),
|
|
1602
|
+
)
|
|
1603
|
+
_validate_materials(args.run_dir, plan, required=True)
|
|
1604
|
+
manifest["status"] = "materialized"
|
|
1605
|
+
_validate_manifest(manifest, plan, plan_sha)
|
|
1606
|
+
_replace_json(args.run_dir / "ingestion-manifest.json", manifest)
|
|
1607
|
+
return {"materialized_files": 146, "cells": 48, "dispatch_available": False}
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
def _closed_embedded_object(
|
|
1611
|
+
raw_utf8: str,
|
|
1612
|
+
declared_sha256: str,
|
|
1613
|
+
label: str,
|
|
1614
|
+
) -> dict[str, Any]:
|
|
1615
|
+
raw = raw_utf8.encode("utf-8")
|
|
1616
|
+
if _sha(raw) != declared_sha256:
|
|
1617
|
+
_fail(f"{label} hash mismatch")
|
|
1618
|
+
value = _strict_loads(raw)
|
|
1619
|
+
if raw != _canonical(value):
|
|
1620
|
+
_fail(f"{label} must use exact canonical JSON bytes")
|
|
1621
|
+
return value
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
def _normalize_raw_event(event: dict[str, Any]) -> dict[str, Any]:
|
|
1625
|
+
try:
|
|
1626
|
+
normalized = _closed_embedded_object(
|
|
1627
|
+
event["raw_event_utf8"],
|
|
1628
|
+
event["raw_event_sha256"],
|
|
1629
|
+
f"raw event {event['event_index']}",
|
|
1630
|
+
)
|
|
1631
|
+
except EnvelopeError as exc:
|
|
1632
|
+
raise StopViolation("transcript_contract_failure", str(exc)) from exc
|
|
1633
|
+
event_kind = normalized.get("event_kind")
|
|
1634
|
+
if not isinstance(event_kind, str) or event_kind not in RAW_EVENT_KINDS:
|
|
1635
|
+
raise StopViolation(
|
|
1636
|
+
"transcript_contract_failure",
|
|
1637
|
+
f"raw event {event['event_index']} has an unknown action classification",
|
|
1638
|
+
)
|
|
1639
|
+
expected_keys = (
|
|
1640
|
+
{"event_kind", "turn_index", "text"}
|
|
1641
|
+
if event_kind in MESSAGE_EVENT_KINDS
|
|
1642
|
+
else {"event_kind", "turn_index"}
|
|
1643
|
+
)
|
|
1644
|
+
if set(normalized) != expected_keys:
|
|
1645
|
+
raise StopViolation(
|
|
1646
|
+
"transcript_contract_failure",
|
|
1647
|
+
f"raw event {event['event_index']} is not a closed normalized event",
|
|
1648
|
+
)
|
|
1649
|
+
turn_index = normalized["turn_index"]
|
|
1650
|
+
if turn_index is not None and type(turn_index) is not int:
|
|
1651
|
+
raise StopViolation(
|
|
1652
|
+
"transcript_contract_failure",
|
|
1653
|
+
f"raw event {event['event_index']} has an invalid turn index type",
|
|
1654
|
+
)
|
|
1655
|
+
raw_stop_codes = {
|
|
1656
|
+
"tool_action": "unplanned_tool_action",
|
|
1657
|
+
"network_action": "unplanned_network_action",
|
|
1658
|
+
"evidence_write_failure": "evidence_write_failure",
|
|
1659
|
+
"session_blocked": "partial_subject_output",
|
|
1660
|
+
"session_partial": "partial_subject_output",
|
|
1661
|
+
}
|
|
1662
|
+
if event_kind in raw_stop_codes:
|
|
1663
|
+
raise StopViolation(
|
|
1664
|
+
raw_stop_codes[event_kind],
|
|
1665
|
+
f"raw event stream reports stop condition {event_kind}",
|
|
1666
|
+
)
|
|
1667
|
+
if normalized["event_kind"] != event["event_kind"] or normalized[
|
|
1668
|
+
"turn_index"
|
|
1669
|
+
] != event["turn_index"]:
|
|
1670
|
+
raise StopViolation(
|
|
1671
|
+
"transcript_contract_failure",
|
|
1672
|
+
f"raw event {event['event_index']} disagrees with normalized fields",
|
|
1673
|
+
)
|
|
1674
|
+
if event_kind in MESSAGE_EVENT_KINDS:
|
|
1675
|
+
if not _has_semantic_text(normalized["text"]):
|
|
1676
|
+
code = (
|
|
1677
|
+
"partial_subject_output"
|
|
1678
|
+
if event_kind == "subject_message"
|
|
1679
|
+
else "actor_protocol_deviation"
|
|
1680
|
+
)
|
|
1681
|
+
raise StopViolation(
|
|
1682
|
+
code,
|
|
1683
|
+
f"raw message event {event['event_index']} has no semantic text",
|
|
1684
|
+
)
|
|
1685
|
+
elif normalized["turn_index"] is not None:
|
|
1686
|
+
raise StopViolation(
|
|
1687
|
+
"transcript_contract_failure",
|
|
1688
|
+
f"non-message raw event {event['event_index']} claims a turn",
|
|
1689
|
+
)
|
|
1690
|
+
return normalized
|
|
1691
|
+
|
|
1692
|
+
|
|
1693
|
+
def _external_session_receipt(
|
|
1694
|
+
transcript: dict[str, Any], expected: dict[str, Any]
|
|
1695
|
+
) -> dict[str, Any]:
|
|
1696
|
+
source = transcript["source"]
|
|
1697
|
+
try:
|
|
1698
|
+
receipt = _closed_embedded_object(
|
|
1699
|
+
source["external_session_receipt_utf8"],
|
|
1700
|
+
source["external_session_receipt_sha256"],
|
|
1701
|
+
"external session receipt",
|
|
1702
|
+
)
|
|
1703
|
+
except EnvelopeError as exc:
|
|
1704
|
+
raise StopViolation("transcript_contract_failure", str(exc)) from exc
|
|
1705
|
+
required = {
|
|
1706
|
+
"receipt_id",
|
|
1707
|
+
"external_session_id",
|
|
1708
|
+
"cell_id",
|
|
1709
|
+
"sequence_index",
|
|
1710
|
+
"started_at",
|
|
1711
|
+
"completed_at",
|
|
1712
|
+
"fresh_context",
|
|
1713
|
+
}
|
|
1714
|
+
if set(receipt) != required:
|
|
1715
|
+
raise StopViolation(
|
|
1716
|
+
"transcript_contract_failure",
|
|
1717
|
+
"external session receipt is not a closed exact record",
|
|
1718
|
+
)
|
|
1719
|
+
if not (
|
|
1720
|
+
isinstance(receipt["cell_id"], str)
|
|
1721
|
+
and type(receipt["sequence_index"]) is int
|
|
1722
|
+
and type(receipt["fresh_context"]) is bool
|
|
1723
|
+
and isinstance(receipt["started_at"], str)
|
|
1724
|
+
and isinstance(receipt["completed_at"], str)
|
|
1725
|
+
):
|
|
1726
|
+
raise StopViolation(
|
|
1727
|
+
"transcript_contract_failure",
|
|
1728
|
+
"external session receipt field types are invalid",
|
|
1729
|
+
)
|
|
1730
|
+
if (
|
|
1731
|
+
receipt["cell_id"],
|
|
1732
|
+
receipt["sequence_index"],
|
|
1733
|
+
receipt["fresh_context"],
|
|
1734
|
+
) != (expected["cell_id"], expected["sequence_index"], True):
|
|
1735
|
+
raise StopViolation(
|
|
1736
|
+
"out_of_order_ingestion",
|
|
1737
|
+
"external session receipt is not bound to the next fresh-context cell",
|
|
1738
|
+
)
|
|
1739
|
+
if not all(
|
|
1740
|
+
isinstance(receipt[key], str)
|
|
1741
|
+
and EXTERNAL_ID_RE.fullmatch(receipt[key]) is not None
|
|
1742
|
+
for key in ("receipt_id", "external_session_id")
|
|
1743
|
+
):
|
|
1744
|
+
raise StopViolation(
|
|
1745
|
+
"transcript_contract_failure",
|
|
1746
|
+
"external session receipt identifiers are invalid",
|
|
1747
|
+
)
|
|
1748
|
+
started = _timestamp(receipt["started_at"], "external session started_at")
|
|
1749
|
+
completed = _timestamp(receipt["completed_at"], "external session completed_at")
|
|
1750
|
+
if started > completed:
|
|
1751
|
+
raise StopViolation(
|
|
1752
|
+
"transcript_contract_failure",
|
|
1753
|
+
"external session receipt starts after it completes",
|
|
1754
|
+
)
|
|
1755
|
+
if transcript["session"]["fresh_context"] is not True:
|
|
1756
|
+
raise StopViolation(
|
|
1757
|
+
"partial_subject_output",
|
|
1758
|
+
"transcript and external receipt do not attest a fresh context",
|
|
1759
|
+
)
|
|
1760
|
+
return receipt
|
|
1761
|
+
|
|
1762
|
+
|
|
1763
|
+
def _receipt_value(
|
|
1764
|
+
plan_sha: str,
|
|
1765
|
+
cell: dict[str, Any],
|
|
1766
|
+
transcript_ref: str,
|
|
1767
|
+
transcript_raw: bytes,
|
|
1768
|
+
transcript: dict[str, Any],
|
|
1769
|
+
) -> dict[str, Any]:
|
|
1770
|
+
return {
|
|
1771
|
+
"schema_version": "ideation-diversity-ingestion-receipt/1.0",
|
|
1772
|
+
"suite": SUITE,
|
|
1773
|
+
"run_plan_sha256": plan_sha,
|
|
1774
|
+
"cell_id": cell["cell_id"],
|
|
1775
|
+
"sequence_index": cell["sequence_index"],
|
|
1776
|
+
"transcript_ref": transcript_ref,
|
|
1777
|
+
"transcript_sha256": _sha(transcript_raw),
|
|
1778
|
+
"authorization_record_ref": transcript["source"]["authorization_record_ref"],
|
|
1779
|
+
"authorization_record_sha256": transcript["source"][
|
|
1780
|
+
"authorization_record_sha256"
|
|
1781
|
+
],
|
|
1782
|
+
"source_artifact_id": transcript["source"]["source_artifact_id"],
|
|
1783
|
+
"external_session_receipt_sha256": transcript["source"][
|
|
1784
|
+
"external_session_receipt_sha256"
|
|
1785
|
+
],
|
|
1786
|
+
"accepted": True,
|
|
1787
|
+
"generated_evidence": False,
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
|
|
1791
|
+
def _validate_ingested_artifacts(
|
|
1792
|
+
run_dir: Path,
|
|
1793
|
+
plan: dict[str, Any],
|
|
1794
|
+
plan_sha: str,
|
|
1795
|
+
manifest: dict[str, Any],
|
|
1796
|
+
*,
|
|
1797
|
+
semantic: bool = True,
|
|
1798
|
+
) -> dict[str, Any]:
|
|
1799
|
+
scenarios = _scenario_index(phase1.load_assets()[0]) if semantic else {}
|
|
1800
|
+
authorization: dict[str, Any] | None = None
|
|
1801
|
+
if manifest["authorization_record_path"] is not None:
|
|
1802
|
+
authorization_raw = _read_file(
|
|
1803
|
+
run_dir / manifest["authorization_record_path"]
|
|
1804
|
+
)
|
|
1805
|
+
if _sha(authorization_raw) != manifest["authorization_record_sha256"]:
|
|
1806
|
+
_fail("preserved authorization record hash drift")
|
|
1807
|
+
authorization = _strict_loads(authorization_raw)
|
|
1808
|
+
_validate_authorization_record(authorization, plan, plan_sha)
|
|
1809
|
+
seen_source_artifact_ids: set[str] = set()
|
|
1810
|
+
seen_external_receipt_ids: set[str] = set()
|
|
1811
|
+
seen_external_session_ids: set[str] = set()
|
|
1812
|
+
last_completed_at: datetime | None = None
|
|
1813
|
+
for cell, row in zip(plan["cells"], manifest["cells"]):
|
|
1814
|
+
if row["status"] == "blocked":
|
|
1815
|
+
raw = _read_file(run_dir / row["transcript_ref"])
|
|
1816
|
+
if _sha(raw) != row["transcript_sha256"]:
|
|
1817
|
+
_fail(f"blocked transcript hash drift: {row['cell_id']}")
|
|
1818
|
+
receipt = manifest["stop_receipt"]
|
|
1819
|
+
if receipt is None or (
|
|
1820
|
+
receipt["raw_ref"], receipt["raw_sha256"]
|
|
1821
|
+
) != (row["transcript_ref"], _sha(raw)):
|
|
1822
|
+
_fail(f"blocked evidence receipt drift: {row['cell_id']}")
|
|
1823
|
+
for artifact in receipt["preserved_auxiliary_artifacts"]:
|
|
1824
|
+
auxiliary_raw = _read_file(run_dir / artifact["ref"])
|
|
1825
|
+
if _sha(auxiliary_raw) != artifact["sha256"]:
|
|
1826
|
+
_fail(
|
|
1827
|
+
"preserved pre-stop artifact hash drift: "
|
|
1828
|
+
f"{artifact['ref']}"
|
|
1829
|
+
)
|
|
1830
|
+
if artifact["role"] == "authorization_record":
|
|
1831
|
+
_validate_authorization_record(
|
|
1832
|
+
_strict_loads(auxiliary_raw), plan, plan_sha
|
|
1833
|
+
)
|
|
1834
|
+
continue
|
|
1835
|
+
if row["status"] != "ingested":
|
|
1836
|
+
continue
|
|
1837
|
+
raw = _read_file(run_dir / row["transcript_ref"])
|
|
1838
|
+
if _sha(raw) != row["transcript_sha256"]:
|
|
1839
|
+
_fail(f"ingested transcript hash drift: {row['cell_id']}")
|
|
1840
|
+
transcript = _strict_loads(raw)
|
|
1841
|
+
if authorization is None or manifest["authorization_record_sha256"] != transcript["source"][
|
|
1842
|
+
"authorization_record_sha256"
|
|
1843
|
+
]:
|
|
1844
|
+
_fail(f"transcript authorization binding drift: {row['cell_id']}")
|
|
1845
|
+
external_receipt = _external_session_receipt(transcript, cell)
|
|
1846
|
+
started_at = _timestamp(
|
|
1847
|
+
external_receipt["started_at"], "external session started_at"
|
|
1848
|
+
)
|
|
1849
|
+
completed_at = _timestamp(
|
|
1850
|
+
external_receipt["completed_at"], "external session completed_at"
|
|
1851
|
+
)
|
|
1852
|
+
if started_at < _timestamp(
|
|
1853
|
+
authorization["decision"]["decided_at"],
|
|
1854
|
+
"authorization decided_at",
|
|
1855
|
+
):
|
|
1856
|
+
_fail(f"transcript predates authorization: {row['cell_id']}")
|
|
1857
|
+
identifiers = (
|
|
1858
|
+
(transcript["source"]["source_artifact_id"], seen_source_artifact_ids),
|
|
1859
|
+
(external_receipt["receipt_id"], seen_external_receipt_ids),
|
|
1860
|
+
(external_receipt["external_session_id"], seen_external_session_ids),
|
|
1861
|
+
)
|
|
1862
|
+
for identifier, seen in identifiers:
|
|
1863
|
+
if identifier in seen:
|
|
1864
|
+
_fail(f"external session identity was reused: {identifier}")
|
|
1865
|
+
seen.add(identifier)
|
|
1866
|
+
if last_completed_at is not None and started_at < last_completed_at:
|
|
1867
|
+
_fail(f"external session time/order regressed: {row['cell_id']}")
|
|
1868
|
+
last_completed_at = completed_at
|
|
1869
|
+
if semantic:
|
|
1870
|
+
_validate_transcript(
|
|
1871
|
+
transcript,
|
|
1872
|
+
cell,
|
|
1873
|
+
plan,
|
|
1874
|
+
plan_sha,
|
|
1875
|
+
scenarios[cell["scenario_id"]],
|
|
1876
|
+
)
|
|
1877
|
+
expected_receipt = _json_bytes(
|
|
1878
|
+
_receipt_value(
|
|
1879
|
+
plan_sha,
|
|
1880
|
+
cell,
|
|
1881
|
+
row["transcript_ref"],
|
|
1882
|
+
raw,
|
|
1883
|
+
transcript,
|
|
1884
|
+
)
|
|
1885
|
+
)
|
|
1886
|
+
actual_receipt = _read_file(run_dir / row["ingestion_receipt_ref"])
|
|
1887
|
+
if actual_receipt != expected_receipt:
|
|
1888
|
+
_fail(f"ingestion receipt drift: {row['cell_id']}")
|
|
1889
|
+
return {
|
|
1890
|
+
"source_artifact_ids": seen_source_artifact_ids,
|
|
1891
|
+
"external_receipt_ids": seen_external_receipt_ids,
|
|
1892
|
+
"external_session_ids": seen_external_session_ids,
|
|
1893
|
+
"last_completed_at": last_completed_at,
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
|
|
1897
|
+
def validate_run(args: argparse.Namespace) -> dict[str, Any]:
|
|
1898
|
+
plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
|
|
1899
|
+
_validate_materials(
|
|
1900
|
+
args.run_dir, plan, required=manifest["status"] != "initialized"
|
|
1901
|
+
)
|
|
1902
|
+
_validate_ingested_artifacts(args.run_dir, plan, plan_sha, manifest)
|
|
1903
|
+
blind_transaction = _validate_blind_transaction(
|
|
1904
|
+
args.run_dir, plan, plan_sha, manifest
|
|
1905
|
+
)
|
|
1906
|
+
if manifest["status"] == "blind_finalized":
|
|
1907
|
+
if not blind_transaction["present"]:
|
|
1908
|
+
_fail("blind_finalized state is missing its durable blind intent")
|
|
1909
|
+
_validate_blind_bundle(args.run_dir, plan, plan_sha, manifest)
|
|
1910
|
+
elif (args.run_dir / "blind").exists():
|
|
1911
|
+
if not blind_transaction["present"]:
|
|
1912
|
+
_fail("atomic blind bundle exists without its durable blind intent")
|
|
1913
|
+
_validate_blind_bundle(args.run_dir, plan, plan_sha, manifest)
|
|
1914
|
+
_validate_run_inventory(args.run_dir, plan, manifest)
|
|
1915
|
+
return {
|
|
1916
|
+
"run_plan_sha256": plan_sha,
|
|
1917
|
+
"status": manifest["status"],
|
|
1918
|
+
"ingested": sum(row["status"] == "ingested" for row in manifest["cells"]),
|
|
1919
|
+
"cells": 48,
|
|
1920
|
+
"blind_transaction_pending": bool(
|
|
1921
|
+
blind_transaction["present"] and manifest["status"] != "blind_finalized"
|
|
1922
|
+
),
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
def _validate_transcript(
|
|
1927
|
+
value: dict[str, Any],
|
|
1928
|
+
expected: dict[str, Any],
|
|
1929
|
+
plan: dict[str, Any],
|
|
1930
|
+
plan_sha: str,
|
|
1931
|
+
scenario: dict[str, Any],
|
|
1932
|
+
) -> None:
|
|
1933
|
+
try:
|
|
1934
|
+
_validate_schema(TRANSCRIPT_SCHEMA, value, "transcript")
|
|
1935
|
+
except EnvelopeError as exc:
|
|
1936
|
+
raise StopViolation("transcript_contract_failure", str(exc)) from exc
|
|
1937
|
+
if value["cell_id"] != expected["cell_id"] or value["sequence_index"] != expected["sequence_index"]:
|
|
1938
|
+
raise StopViolation("out_of_order_ingestion", "transcript is not the next frozen cell")
|
|
1939
|
+
if value["run_plan_sha256"] != plan_sha:
|
|
1940
|
+
raise StopViolation("prompt_or_role_card_hash_mismatch", "transcript run-plan binding mismatch")
|
|
1941
|
+
binding_pairs = {
|
|
1942
|
+
"prompt_sha256": expected["prompt"]["sha256"],
|
|
1943
|
+
"scenario_sha256": expected["scenario_sha256"],
|
|
1944
|
+
"initial_message_sha256": expected["initial_message_sha256"],
|
|
1945
|
+
"actor_packet_sha256": expected["actor_packet_sha256"],
|
|
1946
|
+
"session_envelope_sha256": expected["session_envelope_sha256"],
|
|
1947
|
+
}
|
|
1948
|
+
if value["bindings"] != binding_pairs:
|
|
1949
|
+
raise StopViolation("prompt_or_role_card_hash_mismatch", "transcript prompt/role/envelope binding mismatch")
|
|
1950
|
+
_external_session_receipt(value, expected)
|
|
1951
|
+
observations = value["observations"]
|
|
1952
|
+
turns = value["turns"]
|
|
1953
|
+
events = value["events"]
|
|
1954
|
+
normalized_events = [_normalize_raw_event(event) for event in events]
|
|
1955
|
+
event_kinds = [event["event_kind"] for event in normalized_events]
|
|
1956
|
+
try:
|
|
1957
|
+
_assert_blindable_transcript(plan, value)
|
|
1958
|
+
except EnvelopeError as exc:
|
|
1959
|
+
raise StopViolation(
|
|
1960
|
+
"arm_leakage",
|
|
1961
|
+
f"transcript semantic blinding gate failed: {exc}",
|
|
1962
|
+
) from exc
|
|
1963
|
+
transcript_text = "\n".join(turn["text"] for turn in turns).casefold()
|
|
1964
|
+
checks = (
|
|
1965
|
+
(
|
|
1966
|
+
not observations["prompt_hash_match"]
|
|
1967
|
+
or not observations["role_card_hash_match"],
|
|
1968
|
+
"prompt_or_role_card_hash_mismatch",
|
|
1969
|
+
),
|
|
1970
|
+
(
|
|
1971
|
+
observations["arm_leakage"]
|
|
1972
|
+
or any(token in transcript_text for token in ARM_LEAK_TOKENS),
|
|
1973
|
+
"arm_leakage",
|
|
1974
|
+
),
|
|
1975
|
+
(not observations["eligible"], "ineligible_session"),
|
|
1976
|
+
(
|
|
1977
|
+
bool(observations["unplanned_tool_actions"])
|
|
1978
|
+
or "tool_action" in event_kinds,
|
|
1979
|
+
"unplanned_tool_action",
|
|
1980
|
+
),
|
|
1981
|
+
(
|
|
1982
|
+
bool(observations["unplanned_network_actions"])
|
|
1983
|
+
or "network_action" in event_kinds,
|
|
1984
|
+
"unplanned_network_action",
|
|
1985
|
+
),
|
|
1986
|
+
(
|
|
1987
|
+
observations["evidence_write_failure"]
|
|
1988
|
+
or "evidence_write_failure" in event_kinds,
|
|
1989
|
+
"evidence_write_failure",
|
|
1990
|
+
),
|
|
1991
|
+
(
|
|
1992
|
+
observations["partial_subject_output"]
|
|
1993
|
+
or "session_blocked" in event_kinds
|
|
1994
|
+
or "session_partial" in event_kinds,
|
|
1995
|
+
"partial_subject_output",
|
|
1996
|
+
),
|
|
1997
|
+
(observations["actor_protocol_deviation"], "actor_protocol_deviation"),
|
|
1998
|
+
)
|
|
1999
|
+
for failed, code in checks:
|
|
2000
|
+
if failed:
|
|
2001
|
+
raise StopViolation(code, f"transcript reports stop condition {code}")
|
|
2002
|
+
session = value["session"]
|
|
2003
|
+
if not session["fresh_context"] or not session["complete"] or session["blocked"] or session["partial"]:
|
|
2004
|
+
raise StopViolation("partial_subject_output", "session is not a complete fresh-context record")
|
|
2005
|
+
if session["stop_reason"] not in {"freeze_point_reached", "actor_decided_early"}:
|
|
2006
|
+
raise StopViolation("actor_protocol_deviation", "session has a non-eligible stop reason")
|
|
2007
|
+
if [turn["turn_index"] for turn in turns] != list(range(1, len(turns) + 1)):
|
|
2008
|
+
raise StopViolation("transcript_contract_failure", "turn indices are not contiguous")
|
|
2009
|
+
if any(turn["role"] != ("actor" if index % 2 == 0 else "subject") for index, turn in enumerate(turns)):
|
|
2010
|
+
raise StopViolation("actor_protocol_deviation", "turn roles do not strictly alternate from actor")
|
|
2011
|
+
if turns[-1]["role"] != "actor" or turns[0]["text"] != scenario["initial_message"]:
|
|
2012
|
+
raise StopViolation("actor_protocol_deviation", "transcript first/final actor-turn contract failed")
|
|
2013
|
+
actor_turns = sum(turn["role"] == "actor" for turn in turns)
|
|
2014
|
+
subject_turns = len(turns) - actor_turns
|
|
2015
|
+
if (actor_turns, subject_turns) != (session["actor_turns"], session["subject_turns"]):
|
|
2016
|
+
raise StopViolation("transcript_contract_failure", "declared turn counts do not match transcript")
|
|
2017
|
+
if session["stop_reason"] == "freeze_point_reached" and actor_turns != 6:
|
|
2018
|
+
raise StopViolation("actor_protocol_deviation", "freeze-point session must end on actor turn 6")
|
|
2019
|
+
if [event["event_index"] for event in events] != list(range(1, len(events) + 1)):
|
|
2020
|
+
raise StopViolation("transcript_contract_failure", "event indices are not contiguous")
|
|
2021
|
+
if (
|
|
2022
|
+
event_kinds[0] != "session_started"
|
|
2023
|
+
or event_kinds[-1] != "session_completed"
|
|
2024
|
+
or event_kinds.count("session_started") != 1
|
|
2025
|
+
or event_kinds.count("session_completed") != 1
|
|
2026
|
+
):
|
|
2027
|
+
raise StopViolation(
|
|
2028
|
+
"transcript_contract_failure",
|
|
2029
|
+
"eligible event stream requires exactly one first start and one last completion",
|
|
2030
|
+
)
|
|
2031
|
+
message_events = [
|
|
2032
|
+
event
|
|
2033
|
+
for event in normalized_events
|
|
2034
|
+
if event["event_kind"] in MESSAGE_EVENT_KINDS
|
|
2035
|
+
]
|
|
2036
|
+
expected_messages = [
|
|
2037
|
+
{
|
|
2038
|
+
"event_kind": f"{turn['role']}_message",
|
|
2039
|
+
"turn_index": turn["turn_index"],
|
|
2040
|
+
"text": turn["text"],
|
|
2041
|
+
}
|
|
2042
|
+
for turn in turns
|
|
2043
|
+
]
|
|
2044
|
+
if message_events != expected_messages:
|
|
2045
|
+
raise StopViolation(
|
|
2046
|
+
"transcript_contract_failure",
|
|
2047
|
+
"transcript turns are not derived byte-exactly from raw message events",
|
|
2048
|
+
)
|
|
2049
|
+
|
|
2050
|
+
|
|
2051
|
+
def _record_stop(
|
|
2052
|
+
run_dir: Path,
|
|
2053
|
+
plan: dict[str, Any],
|
|
2054
|
+
plan_sha: str,
|
|
2055
|
+
manifest: dict[str, Any],
|
|
2056
|
+
raw: bytes,
|
|
2057
|
+
violation: StopViolation,
|
|
2058
|
+
preserved_auxiliary_artifacts: list[dict[str, str]] | None = None,
|
|
2059
|
+
*,
|
|
2060
|
+
raw_capture_kind: str = "exact_input_bytes",
|
|
2061
|
+
) -> None:
|
|
2062
|
+
index = manifest["next_sequence_index"] - 1
|
|
2063
|
+
expected = plan["cells"][index]
|
|
2064
|
+
raw_sha = _sha(raw)
|
|
2065
|
+
raw_ref = f"blocked/{expected['cell_id']}.transcript.{raw_sha}.raw"
|
|
2066
|
+
raw_path = run_dir / raw_ref
|
|
2067
|
+
if raw_path.exists():
|
|
2068
|
+
try:
|
|
2069
|
+
collision = _read_file(raw_path, limit=max(MAX_INPUT_BYTES, len(raw)))
|
|
2070
|
+
except EnvelopeError:
|
|
2071
|
+
collision = None
|
|
2072
|
+
if collision != raw:
|
|
2073
|
+
while True:
|
|
2074
|
+
candidate = raw_ref + f".collision-{secrets.token_hex(8)}"
|
|
2075
|
+
if not (run_dir / candidate).exists():
|
|
2076
|
+
raw_ref = candidate
|
|
2077
|
+
raw_path = run_dir / raw_ref
|
|
2078
|
+
break
|
|
2079
|
+
# Attempt complete-byte publication before freezing the marker so any
|
|
2080
|
+
# surviving atomic staging evidence can be hash-registered in its replay.
|
|
2081
|
+
raw_write_error: EnvelopeError | None = None
|
|
2082
|
+
try:
|
|
2083
|
+
_ensure_exact_new(raw_path, raw)
|
|
2084
|
+
except EnvelopeError as exc:
|
|
2085
|
+
raw_write_error = exc
|
|
2086
|
+
supplied = list(preserved_auxiliary_artifacts or [])
|
|
2087
|
+
if len(supplied) > 8:
|
|
2088
|
+
_fail("too many bounded ingestion artifacts to register")
|
|
2089
|
+
publication_staging_ref = (
|
|
2090
|
+
f".stop-intent.json.publish-"
|
|
2091
|
+
f"{_sha((plan_sha + expected['cell_id'] + raw_sha).encode('utf-8'))[:24]}.tmp"
|
|
2092
|
+
)
|
|
2093
|
+
manifest_replacement_staging_ref = (
|
|
2094
|
+
f".ingestion-manifest.json.stop-"
|
|
2095
|
+
f"{_sha((raw_sha + plan_sha + expected['cell_id']).encode('utf-8'))[:24]}.json"
|
|
2096
|
+
)
|
|
2097
|
+
stopped_manifest = copy.deepcopy(manifest)
|
|
2098
|
+
stopped_manifest["cells"][index].update(
|
|
2099
|
+
status="blocked",
|
|
2100
|
+
transcript_ref=raw_ref,
|
|
2101
|
+
transcript_sha256=raw_sha,
|
|
2102
|
+
ingestion_receipt_ref=None,
|
|
2103
|
+
)
|
|
2104
|
+
stopped_manifest["status"] = "stopped"
|
|
2105
|
+
stopped_manifest["stopped"] = True
|
|
2106
|
+
stopped_manifest["stop_receipt"] = {
|
|
2107
|
+
"cell_id": expected["cell_id"],
|
|
2108
|
+
"sequence_index": expected["sequence_index"],
|
|
2109
|
+
"reason_code": violation.code,
|
|
2110
|
+
"detail": violation.detail[:2000],
|
|
2111
|
+
"raw_ref": raw_ref,
|
|
2112
|
+
"raw_sha256": raw_sha,
|
|
2113
|
+
"raw_capture_kind": raw_capture_kind,
|
|
2114
|
+
"retry_forbidden": True,
|
|
2115
|
+
"stop_intent_ref": STOP_INTENT_REF,
|
|
2116
|
+
"stop_intent_committed_before_state_replacement": True,
|
|
2117
|
+
"stop_intent_publication_staging_ref": publication_staging_ref,
|
|
2118
|
+
"manifest_replacement_staging_ref": manifest_replacement_staging_ref,
|
|
2119
|
+
"pre_stop_inventory": {"entry_count": 0, "sha256": _sha(_canonical([]))},
|
|
2120
|
+
"preserved_auxiliary_artifacts": supplied,
|
|
2121
|
+
}
|
|
2122
|
+
expected_before, expected_directories = _expected_run_inventory(
|
|
2123
|
+
run_dir, plan, stopped_manifest
|
|
2124
|
+
)
|
|
2125
|
+
observed_before, observed_directories = _relative_files_and_directories(run_dir)
|
|
2126
|
+
stopped_manifest["stop_receipt"]["pre_stop_inventory"] = _inventory_binding(
|
|
2127
|
+
run_dir,
|
|
2128
|
+
observed_before - expected_before,
|
|
2129
|
+
observed_directories - expected_directories,
|
|
2130
|
+
)
|
|
2131
|
+
_validate_manifest(stopped_manifest, plan, plan_sha)
|
|
2132
|
+
prior_manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
|
|
2133
|
+
if _strict_loads(prior_manifest_raw) != manifest:
|
|
2134
|
+
_fail("ingestion manifest changed while preparing durable stop intent")
|
|
2135
|
+
stopped_raw = _json_bytes(stopped_manifest)
|
|
2136
|
+
marker = {
|
|
2137
|
+
"schema_version": "ideation-diversity-stop-intent/1.0",
|
|
2138
|
+
"suite": SUITE,
|
|
2139
|
+
"run_plan_sha256": plan_sha,
|
|
2140
|
+
"prior_manifest_sha256": _sha(prior_manifest_raw),
|
|
2141
|
+
"cell_id": expected["cell_id"],
|
|
2142
|
+
"sequence_index": expected["sequence_index"],
|
|
2143
|
+
"reason_code": violation.code,
|
|
2144
|
+
"detail": violation.detail[:2000],
|
|
2145
|
+
"raw_ref": raw_ref,
|
|
2146
|
+
"raw_sha256": raw_sha,
|
|
2147
|
+
"raw_capture_kind": raw_capture_kind,
|
|
2148
|
+
"raw_base64": base64.b64encode(raw).decode("ascii"),
|
|
2149
|
+
"stopped_manifest_sha256": _sha(stopped_raw),
|
|
2150
|
+
"stopped_manifest_base64": base64.b64encode(stopped_raw).decode("ascii"),
|
|
2151
|
+
"pre_stop_inventory": stopped_manifest["stop_receipt"]["pre_stop_inventory"],
|
|
2152
|
+
"stop_intent_publication_staging_ref": publication_staging_ref,
|
|
2153
|
+
"manifest_replacement_staging_ref": manifest_replacement_staging_ref,
|
|
2154
|
+
"retry_forbidden": True,
|
|
2155
|
+
}
|
|
2156
|
+
marker_raw = _json_bytes(marker)
|
|
2157
|
+
_validate_stop_intent(marker_raw, plan, plan_sha)
|
|
2158
|
+
_publish_stop_intent(
|
|
2159
|
+
run_dir / STOP_INTENT_REF,
|
|
2160
|
+
marker_raw,
|
|
2161
|
+
publication_staging_ref,
|
|
2162
|
+
)
|
|
2163
|
+
try:
|
|
2164
|
+
_ensure_exact_new(raw_path, raw)
|
|
2165
|
+
raw_write_error = None
|
|
2166
|
+
except EnvelopeError as exc:
|
|
2167
|
+
raw_write_error = exc
|
|
2168
|
+
# The marker is the irreversible boundary. If this replacement fails,
|
|
2169
|
+
# every later load rejects retry and can replay the exact stopped state.
|
|
2170
|
+
_replace_json(
|
|
2171
|
+
run_dir / "ingestion-manifest.json",
|
|
2172
|
+
stopped_manifest,
|
|
2173
|
+
staging_ref=manifest_replacement_staging_ref,
|
|
2174
|
+
)
|
|
2175
|
+
if raw_write_error is not None:
|
|
2176
|
+
raise EnvelopeError(
|
|
2177
|
+
"run is permanently stopped; blocked raw recovery remains pending: "
|
|
2178
|
+
f"{raw_write_error}"
|
|
2179
|
+
) from raw_write_error
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
def _preserved_ingestion_artifacts(
|
|
2183
|
+
run_dir: Path,
|
|
2184
|
+
manifest_before: dict[str, Any],
|
|
2185
|
+
authorization_raw: bytes,
|
|
2186
|
+
transcript_ref: str,
|
|
2187
|
+
transcript_raw: bytes,
|
|
2188
|
+
receipt_ref: str,
|
|
2189
|
+
receipt_raw: bytes,
|
|
2190
|
+
) -> list[dict[str, str]]:
|
|
2191
|
+
candidates: list[tuple[str, str, bytes]] = []
|
|
2192
|
+
if manifest_before["authorization_record_sha256"] is None:
|
|
2193
|
+
candidates.append(
|
|
2194
|
+
("authorization_record", "authorization/record.json", authorization_raw)
|
|
2195
|
+
)
|
|
2196
|
+
candidates.extend(
|
|
2197
|
+
(
|
|
2198
|
+
(
|
|
2199
|
+
"accepted_transcript_before_state_commit",
|
|
2200
|
+
transcript_ref,
|
|
2201
|
+
transcript_raw,
|
|
2202
|
+
),
|
|
2203
|
+
(
|
|
2204
|
+
"ingestion_receipt_before_state_commit",
|
|
2205
|
+
receipt_ref,
|
|
2206
|
+
receipt_raw,
|
|
2207
|
+
),
|
|
2208
|
+
)
|
|
2209
|
+
)
|
|
2210
|
+
preserved: list[dict[str, str]] = []
|
|
2211
|
+
for role, ref, expected_raw in candidates:
|
|
2212
|
+
try:
|
|
2213
|
+
actual_raw = _read_file(run_dir / ref)
|
|
2214
|
+
except EnvelopeError:
|
|
2215
|
+
continue
|
|
2216
|
+
if actual_raw == expected_raw:
|
|
2217
|
+
preserved.append({"role": role, "ref": ref, "sha256": _sha(actual_raw)})
|
|
2218
|
+
return preserved
|
|
2219
|
+
|
|
2220
|
+
|
|
2221
|
+
def _bounded_acquisition_failure_evidence(
|
|
2222
|
+
path: Path,
|
|
2223
|
+
error: EnvelopeError,
|
|
2224
|
+
) -> bytes:
|
|
2225
|
+
observed: dict[str, Any] = {
|
|
2226
|
+
"schema_version": "ideation-diversity-transcript-acquisition-failure/1.0",
|
|
2227
|
+
"kind": "bounded_acquisition_failure_record",
|
|
2228
|
+
"error": str(error)[:2000],
|
|
2229
|
+
"path_name": path.name[:255],
|
|
2230
|
+
"stat": None,
|
|
2231
|
+
"prefix_sha256": None,
|
|
2232
|
+
"prefix_bytes": 0,
|
|
2233
|
+
}
|
|
2234
|
+
try:
|
|
2235
|
+
metadata = path.lstat()
|
|
2236
|
+
observed["stat"] = {
|
|
2237
|
+
"device": metadata.st_dev,
|
|
2238
|
+
"inode": metadata.st_ino,
|
|
2239
|
+
"mode": stat.S_IFMT(metadata.st_mode),
|
|
2240
|
+
"size_bytes": metadata.st_size,
|
|
2241
|
+
"mtime_ns": metadata.st_mtime_ns,
|
|
2242
|
+
}
|
|
2243
|
+
if stat.S_ISREG(metadata.st_mode) and not path.is_symlink():
|
|
2244
|
+
with path.open("rb") as stream:
|
|
2245
|
+
prefix = stream.read(64 * 1024)
|
|
2246
|
+
observed["prefix_sha256"] = _sha(prefix)
|
|
2247
|
+
observed["prefix_bytes"] = len(prefix)
|
|
2248
|
+
except OSError as exc:
|
|
2249
|
+
observed["stat_error"] = str(exc)[:1000]
|
|
2250
|
+
return _json_bytes(observed)
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
def ingest(args: argparse.Namespace) -> dict[str, Any]:
|
|
2254
|
+
plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
|
|
2255
|
+
if manifest["stopped"]:
|
|
2256
|
+
_fail("run is stopped; the frozen protocol forbids retry")
|
|
2257
|
+
if manifest["status"] in {"complete", "blind_finalized"}:
|
|
2258
|
+
_fail("all 48 transcripts are already ingested")
|
|
2259
|
+
if manifest["status"] not in {"materialized", "ingesting"}:
|
|
2260
|
+
_fail("transcripts may be ingested only after materialization")
|
|
2261
|
+
expected = plan["cells"][manifest["next_sequence_index"] - 1]
|
|
2262
|
+
scenario = _scenario_index(phase1.load_assets()[0])[expected["scenario_id"]]
|
|
2263
|
+
_begin_ingestion_attempt(args.run_dir, plan, plan_sha, manifest)
|
|
2264
|
+
try:
|
|
2265
|
+
raw = _read_file(args.transcript)
|
|
2266
|
+
except EnvelopeError as exc:
|
|
2267
|
+
violation = StopViolation(
|
|
2268
|
+
"transcript_contract_failure",
|
|
2269
|
+
f"cannot acquire bounded external transcript bytes: {exc}",
|
|
2270
|
+
)
|
|
2271
|
+
evidence = _bounded_acquisition_failure_evidence(args.transcript, exc)
|
|
2272
|
+
_record_stop(
|
|
2273
|
+
args.run_dir,
|
|
2274
|
+
plan,
|
|
2275
|
+
plan_sha,
|
|
2276
|
+
manifest,
|
|
2277
|
+
evidence,
|
|
2278
|
+
violation,
|
|
2279
|
+
raw_capture_kind="bounded_acquisition_failure_record",
|
|
2280
|
+
)
|
|
2281
|
+
raise violation from exc
|
|
2282
|
+
try:
|
|
2283
|
+
_validate_materials(args.run_dir, plan, required=True)
|
|
2284
|
+
sequence_state = _validate_ingested_artifacts(
|
|
2285
|
+
args.run_dir,
|
|
2286
|
+
plan,
|
|
2287
|
+
plan_sha,
|
|
2288
|
+
manifest,
|
|
2289
|
+
semantic=False,
|
|
2290
|
+
)
|
|
2291
|
+
_validate_run_inventory(args.run_dir, plan, manifest)
|
|
2292
|
+
except EnvelopeError as exc:
|
|
2293
|
+
violation = StopViolation(
|
|
2294
|
+
"evidence_write_failure",
|
|
2295
|
+
f"pre-ingestion run evidence/inventory validation failed: {exc}",
|
|
2296
|
+
)
|
|
2297
|
+
_record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
|
|
2298
|
+
raise violation from exc
|
|
2299
|
+
try:
|
|
2300
|
+
value = _strict_loads(raw)
|
|
2301
|
+
_validate_transcript(value, expected, plan, plan_sha, scenario)
|
|
2302
|
+
try:
|
|
2303
|
+
authorization_raw = _read_file(args.authorization_record)
|
|
2304
|
+
authorization = _strict_loads(authorization_raw)
|
|
2305
|
+
_validate_authorization_record(authorization, plan, plan_sha)
|
|
2306
|
+
except EnvelopeError as exc:
|
|
2307
|
+
raise StopViolation(
|
|
2308
|
+
"authorization_record_missing_or_mismatch",
|
|
2309
|
+
f"cannot verify external authorization record: {exc}",
|
|
2310
|
+
) from exc
|
|
2311
|
+
authorization_sha = _sha(authorization_raw)
|
|
2312
|
+
if authorization_sha != value["source"]["authorization_record_sha256"]:
|
|
2313
|
+
raise StopViolation(
|
|
2314
|
+
"authorization_record_missing_or_mismatch",
|
|
2315
|
+
"external authorization record hash does not match transcript binding",
|
|
2316
|
+
)
|
|
2317
|
+
if value["source"]["authorization_record_ref"] != authorization["record_id"]:
|
|
2318
|
+
raise StopViolation(
|
|
2319
|
+
"authorization_record_missing_or_mismatch",
|
|
2320
|
+
"transcript authorization reference differs from record id",
|
|
2321
|
+
)
|
|
2322
|
+
external_receipt = _external_session_receipt(value, expected)
|
|
2323
|
+
started_at = _timestamp(
|
|
2324
|
+
external_receipt["started_at"], "external session started_at"
|
|
2325
|
+
)
|
|
2326
|
+
if started_at < _timestamp(
|
|
2327
|
+
authorization["decision"]["decided_at"],
|
|
2328
|
+
"authorization decided_at",
|
|
2329
|
+
):
|
|
2330
|
+
raise StopViolation(
|
|
2331
|
+
"authorization_record_missing_or_mismatch",
|
|
2332
|
+
"external session starts before the authorization decision",
|
|
2333
|
+
)
|
|
2334
|
+
uniqueness = (
|
|
2335
|
+
(
|
|
2336
|
+
value["source"]["source_artifact_id"],
|
|
2337
|
+
sequence_state["source_artifact_ids"],
|
|
2338
|
+
),
|
|
2339
|
+
(
|
|
2340
|
+
external_receipt["receipt_id"],
|
|
2341
|
+
sequence_state["external_receipt_ids"],
|
|
2342
|
+
),
|
|
2343
|
+
(
|
|
2344
|
+
external_receipt["external_session_id"],
|
|
2345
|
+
sequence_state["external_session_ids"],
|
|
2346
|
+
),
|
|
2347
|
+
)
|
|
2348
|
+
if any(identifier in seen for identifier, seen in uniqueness):
|
|
2349
|
+
raise StopViolation(
|
|
2350
|
+
"out_of_order_ingestion",
|
|
2351
|
+
"external artifact, receipt, or fresh-context session id was reused",
|
|
2352
|
+
)
|
|
2353
|
+
last_completed_at = sequence_state["last_completed_at"]
|
|
2354
|
+
if last_completed_at is not None and started_at < last_completed_at:
|
|
2355
|
+
raise StopViolation(
|
|
2356
|
+
"out_of_order_ingestion",
|
|
2357
|
+
"external session time/order regressed across frozen cells",
|
|
2358
|
+
)
|
|
2359
|
+
if manifest["authorization_record_sha256"] not in {None, authorization_sha}:
|
|
2360
|
+
raise StopViolation(
|
|
2361
|
+
"authorization_record_missing_or_mismatch",
|
|
2362
|
+
"run is already bound to a different authorization record",
|
|
2363
|
+
)
|
|
2364
|
+
except StopViolation as violation:
|
|
2365
|
+
_record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
|
|
2366
|
+
raise
|
|
2367
|
+
except EnvelopeError as exc:
|
|
2368
|
+
violation = StopViolation("transcript_contract_failure", str(exc))
|
|
2369
|
+
_record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
|
|
2370
|
+
raise violation from exc
|
|
2371
|
+
cell_id = expected["cell_id"]
|
|
2372
|
+
transcript_ref = f"transcripts/{cell_id}.json"
|
|
2373
|
+
receipt_ref = f"receipts/{cell_id}.json"
|
|
2374
|
+
receipt = _receipt_value(plan_sha, expected, transcript_ref, raw, value)
|
|
2375
|
+
receipt_raw = _json_bytes(receipt)
|
|
2376
|
+
manifest_before_ingestion = copy.deepcopy(manifest)
|
|
2377
|
+
try:
|
|
2378
|
+
if manifest["authorization_record_sha256"] is None:
|
|
2379
|
+
_write_new(
|
|
2380
|
+
args.run_dir / "authorization/record.json",
|
|
2381
|
+
authorization_raw,
|
|
2382
|
+
)
|
|
2383
|
+
manifest["authorization_record_path"] = "authorization/record.json"
|
|
2384
|
+
manifest["authorization_record_sha256"] = authorization_sha
|
|
2385
|
+
_write_new(args.run_dir / transcript_ref, raw)
|
|
2386
|
+
_write_new(args.run_dir / receipt_ref, receipt_raw)
|
|
2387
|
+
except EnvelopeError as exc:
|
|
2388
|
+
violation = StopViolation(
|
|
2389
|
+
"evidence_write_failure",
|
|
2390
|
+
f"cannot preserve accepted transcript/receipt: {exc}",
|
|
2391
|
+
)
|
|
2392
|
+
try:
|
|
2393
|
+
_record_stop(
|
|
2394
|
+
args.run_dir,
|
|
2395
|
+
plan,
|
|
2396
|
+
plan_sha,
|
|
2397
|
+
manifest_before_ingestion,
|
|
2398
|
+
raw,
|
|
2399
|
+
violation,
|
|
2400
|
+
_preserved_ingestion_artifacts(
|
|
2401
|
+
args.run_dir,
|
|
2402
|
+
manifest_before_ingestion,
|
|
2403
|
+
authorization_raw,
|
|
2404
|
+
transcript_ref,
|
|
2405
|
+
raw,
|
|
2406
|
+
receipt_ref,
|
|
2407
|
+
receipt_raw,
|
|
2408
|
+
),
|
|
2409
|
+
)
|
|
2410
|
+
except EnvelopeError as stop_exc:
|
|
2411
|
+
raise EnvelopeError(
|
|
2412
|
+
"evidence write failed and the stop receipt could not be persisted; "
|
|
2413
|
+
"do not retry this run"
|
|
2414
|
+
) from stop_exc
|
|
2415
|
+
raise violation from exc
|
|
2416
|
+
manifest["cells"][expected["sequence_index"] - 1].update(
|
|
2417
|
+
status="ingested",
|
|
2418
|
+
transcript_ref=transcript_ref,
|
|
2419
|
+
transcript_sha256=_sha(raw),
|
|
2420
|
+
ingestion_receipt_ref=receipt_ref,
|
|
2421
|
+
)
|
|
2422
|
+
manifest["next_sequence_index"] += 1
|
|
2423
|
+
ingested = manifest["next_sequence_index"] - 1
|
|
2424
|
+
manifest["status"] = "complete" if ingested == 48 else "ingesting"
|
|
2425
|
+
_validate_manifest(manifest, plan, plan_sha)
|
|
2426
|
+
try:
|
|
2427
|
+
_replace_json(args.run_dir / "ingestion-manifest.json", manifest)
|
|
2428
|
+
except EnvelopeError as exc:
|
|
2429
|
+
violation = StopViolation(
|
|
2430
|
+
"evidence_write_failure",
|
|
2431
|
+
f"cannot persist ingestion manifest: {exc}",
|
|
2432
|
+
)
|
|
2433
|
+
try:
|
|
2434
|
+
_record_stop(
|
|
2435
|
+
args.run_dir,
|
|
2436
|
+
plan,
|
|
2437
|
+
plan_sha,
|
|
2438
|
+
manifest_before_ingestion,
|
|
2439
|
+
raw,
|
|
2440
|
+
violation,
|
|
2441
|
+
_preserved_ingestion_artifacts(
|
|
2442
|
+
args.run_dir,
|
|
2443
|
+
manifest_before_ingestion,
|
|
2444
|
+
authorization_raw,
|
|
2445
|
+
transcript_ref,
|
|
2446
|
+
raw,
|
|
2447
|
+
receipt_ref,
|
|
2448
|
+
receipt_raw,
|
|
2449
|
+
),
|
|
2450
|
+
)
|
|
2451
|
+
except EnvelopeError as stop_exc:
|
|
2452
|
+
raise EnvelopeError(
|
|
2453
|
+
"ingestion manifest write failed and the stop receipt could not be "
|
|
2454
|
+
"persisted; do not retry this run"
|
|
2455
|
+
) from stop_exc
|
|
2456
|
+
raise violation from exc
|
|
2457
|
+
_complete_ingestion_attempt(args.run_dir, expected["cell_id"])
|
|
2458
|
+
return {"ingested": ingested, "next_sequence_index": manifest["next_sequence_index"]}
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
def _artifact_presence() -> dict[str, bool]:
|
|
2462
|
+
return {
|
|
2463
|
+
"structured_judge_labels_attached": False,
|
|
2464
|
+
"adjudication_artifact_attached": False,
|
|
2465
|
+
"human_evidence_artifact_attached": False,
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
def _assignment_boundary() -> dict[str, bool]:
|
|
2470
|
+
return {
|
|
2471
|
+
"future_closed_assignment_ledger_gate_required": True,
|
|
2472
|
+
"assignment_ledger_implemented_by_no_call_runner": False,
|
|
2473
|
+
"same_judge_equivalent_scholar_context_cross_condition_exposure_forbidden": True,
|
|
2474
|
+
"bundle_alone_proves_judge_exposure_blindness": False,
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
def _blind_identifiers(plan: dict[str, Any]) -> set[str]:
|
|
2479
|
+
identifiers: set[str] = set()
|
|
2480
|
+
for cell in plan["cells"]:
|
|
2481
|
+
identifiers.update(
|
|
2482
|
+
_normalized_blind_text(str(cell[key]))
|
|
2483
|
+
for key in (
|
|
2484
|
+
"cell_id",
|
|
2485
|
+
"experiment_id",
|
|
2486
|
+
"arm_id",
|
|
2487
|
+
"scenario_id",
|
|
2488
|
+
"pair_id",
|
|
2489
|
+
"actor_block_id",
|
|
2490
|
+
)
|
|
2491
|
+
)
|
|
2492
|
+
return identifiers
|
|
2493
|
+
|
|
2494
|
+
|
|
2495
|
+
def _normalized_blind_text(text: str) -> str:
|
|
2496
|
+
"""Normalize compatibility forms and punctuation separators before matching."""
|
|
2497
|
+
normalized = unicodedata.normalize("NFKC", text).casefold()
|
|
2498
|
+
separated = "".join(
|
|
2499
|
+
" " if unicodedata.category(character)[0] in {"C", "P", "Z"} else character
|
|
2500
|
+
for character in normalized
|
|
2501
|
+
)
|
|
2502
|
+
return " ".join(separated.split())
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
def _compact_blind_text(text: str) -> str:
|
|
2506
|
+
"""Remove spaces from the mark-safe screening projection."""
|
|
2507
|
+
return "".join(_blind_screen_text(text).split())
|
|
2508
|
+
|
|
2509
|
+
|
|
2510
|
+
def _fold_blind_alphanumeric(source_character: str) -> str:
|
|
2511
|
+
visible = "".join(
|
|
2512
|
+
folded
|
|
2513
|
+
for folded in unicodedata.normalize("NFKD", source_character.casefold())
|
|
2514
|
+
if unicodedata.category(folded)[0] not in {"M", "C", "P", "S", "Z"}
|
|
2515
|
+
)
|
|
2516
|
+
if visible:
|
|
2517
|
+
return visible
|
|
2518
|
+
# Some compatibility letters decompose entirely to a separator plus a
|
|
2519
|
+
# mark. Retain the original letter as an alphanumeric boundary sentinel.
|
|
2520
|
+
return source_character.casefold()
|
|
2521
|
+
|
|
2522
|
+
|
|
2523
|
+
def _blind_screen_text(text: str) -> str:
|
|
2524
|
+
"""Project text without allowing a mark to casefold into a letter.
|
|
2525
|
+
|
|
2526
|
+
Marks and format/control characters disappear. Punctuation and separators
|
|
2527
|
+
become spaces so real word boundaries survive, while a matcher can still
|
|
2528
|
+
allow those spaces between the characters of an obfuscated identifier.
|
|
2529
|
+
"""
|
|
2530
|
+
projected: list[str] = []
|
|
2531
|
+
for source_character in text:
|
|
2532
|
+
source_category = unicodedata.category(source_character)[0]
|
|
2533
|
+
if source_category in {"M", "C"}:
|
|
2534
|
+
continue
|
|
2535
|
+
if source_category in {"P", "S", "Z"}:
|
|
2536
|
+
projected.append(" ")
|
|
2537
|
+
continue
|
|
2538
|
+
# Compatibility punctuation created by one source L/N code point is
|
|
2539
|
+
# not a real boundary. Keeping the source character atomic makes
|
|
2540
|
+
# `ĿCell` and parenthesized-number forms remain adjacent to `Cell`.
|
|
2541
|
+
projected.append(_fold_blind_alphanumeric(source_character))
|
|
2542
|
+
return " ".join("".join(projected).split())
|
|
2543
|
+
|
|
2544
|
+
|
|
2545
|
+
def _joined_blind_text(text: str) -> str:
|
|
2546
|
+
"""Repair mark/format/punctuation splits while preserving real spacing."""
|
|
2547
|
+
projected: list[str] = []
|
|
2548
|
+
for source_character in text:
|
|
2549
|
+
source_category = unicodedata.category(source_character)[0]
|
|
2550
|
+
if source_category in {"M", "C", "P", "S"}:
|
|
2551
|
+
continue
|
|
2552
|
+
if source_category == "Z":
|
|
2553
|
+
projected.append(" ")
|
|
2554
|
+
continue
|
|
2555
|
+
projected.append(_fold_blind_alphanumeric(source_character))
|
|
2556
|
+
return " ".join("".join(projected).split())
|
|
2557
|
+
|
|
2558
|
+
|
|
2559
|
+
def _contains_complete_normalized_identifier(text: str, identifier: str) -> bool:
|
|
2560
|
+
if len(identifier) < 4:
|
|
2561
|
+
return False
|
|
2562
|
+
return re.search(
|
|
2563
|
+
r"(?<![^\W_])" + re.escape(identifier) + r"(?![^\W_])",
|
|
2564
|
+
text,
|
|
2565
|
+
) is not None
|
|
2566
|
+
|
|
2567
|
+
|
|
2568
|
+
def _contains_complete_compact_identifier(text: str, identifier: str) -> bool:
|
|
2569
|
+
"""Match an entire identifier despite inserted marks or separators.
|
|
2570
|
+
|
|
2571
|
+
Letter/number boundaries keep the compact comparison from turning ordinary
|
|
2572
|
+
surrounding prose into a match. The separator expression operates on the
|
|
2573
|
+
NFKD form so combining marks, format characters, punctuation, and spacing
|
|
2574
|
+
cannot split the characters of a frozen identifier.
|
|
2575
|
+
"""
|
|
2576
|
+
compact = _compact_blind_text(identifier)
|
|
2577
|
+
return _contains_complete_compact_projection(_blind_screen_text(text), compact)
|
|
2578
|
+
|
|
2579
|
+
|
|
2580
|
+
def _contains_complete_compact_projection(screen: str, compact: str) -> bool:
|
|
2581
|
+
if len(compact) < 4:
|
|
2582
|
+
return False
|
|
2583
|
+
separator = r"\s*"
|
|
2584
|
+
return re.search(
|
|
2585
|
+
r"(?<![^\W_])"
|
|
2586
|
+
+ separator.join(re.escape(character) for character in compact)
|
|
2587
|
+
+ r"(?![^\W_])",
|
|
2588
|
+
screen,
|
|
2589
|
+
) is not None
|
|
2590
|
+
|
|
2591
|
+
|
|
2592
|
+
def _contains_compact_phrase_projection(
|
|
2593
|
+
screen: str, compact_text: str, compact: str
|
|
2594
|
+
) -> bool:
|
|
2595
|
+
if any(ord(character) > 127 for character in compact):
|
|
2596
|
+
return compact in compact_text
|
|
2597
|
+
return _contains_complete_compact_projection(screen, compact)
|
|
2598
|
+
|
|
2599
|
+
|
|
2600
|
+
def _obfuscated_token_pattern(token: str) -> str:
|
|
2601
|
+
return r"\s*".join(re.escape(character) for character in token)
|
|
2602
|
+
|
|
2603
|
+
|
|
2604
|
+
_COMPACT_MAPPING_RELATION_PATTERN = re.compile(
|
|
2605
|
+
r"(?<![^\W_])(?:"
|
|
2606
|
+
+ "|".join(
|
|
2607
|
+
_obfuscated_token_pattern(token)
|
|
2608
|
+
for token in ("mechanism", "feature", "probe", "guardrail", "guardrails")
|
|
2609
|
+
)
|
|
2610
|
+
+ r")(?:[^\W_]|\s){0,32}(?<![^\W_])(?:"
|
|
2611
|
+
+ "|".join(
|
|
2612
|
+
_obfuscated_token_pattern(token)
|
|
2613
|
+
for token in ("enabled", "disabled", "ablated", "on", "off")
|
|
2614
|
+
)
|
|
2615
|
+
+ r")(?![^\W_])",
|
|
2616
|
+
re.IGNORECASE,
|
|
2617
|
+
)
|
|
2618
|
+
|
|
2619
|
+
|
|
2620
|
+
def _assert_blindable_transcript(
|
|
2621
|
+
plan: dict[str, Any], transcript: dict[str, Any]
|
|
2622
|
+
) -> None:
|
|
2623
|
+
_assert_blind_texts(plan, [turn["text"] for turn in transcript["turns"]])
|
|
2624
|
+
|
|
2625
|
+
|
|
2626
|
+
def _assert_blind_texts(plan: dict[str, Any], texts: list[str]) -> None:
|
|
2627
|
+
blind_identifiers = _blind_identifiers(plan)
|
|
2628
|
+
blind_identifier_projections = tuple(
|
|
2629
|
+
(identifier, _compact_blind_text(identifier))
|
|
2630
|
+
for identifier in blind_identifiers
|
|
2631
|
+
)
|
|
2632
|
+
mapping_phrase_projections = tuple(
|
|
2633
|
+
_compact_blind_text(phrase) for phrase in MAPPING_LEAK_COMPACT_PHRASES
|
|
2634
|
+
)
|
|
2635
|
+
human_phrase_projections = tuple(
|
|
2636
|
+
_compact_blind_text(phrase) for phrase in HUMAN_EVIDENCE_COMPACT_PHRASES
|
|
2637
|
+
)
|
|
2638
|
+
for text in texts:
|
|
2639
|
+
folded = _normalized_blind_text(text)
|
|
2640
|
+
joined = _joined_blind_text(text)
|
|
2641
|
+
screen = _blind_screen_text(text)
|
|
2642
|
+
compact_text = "".join(screen.split())
|
|
2643
|
+
leaked = next(
|
|
2644
|
+
(
|
|
2645
|
+
identifier
|
|
2646
|
+
for identifier, compact in blind_identifier_projections
|
|
2647
|
+
if _contains_complete_compact_projection(screen, compact)
|
|
2648
|
+
),
|
|
2649
|
+
None,
|
|
2650
|
+
)
|
|
2651
|
+
if leaked is not None:
|
|
2652
|
+
_fail(f"transcript free text leaks frozen blind identifier {leaked!r}")
|
|
2653
|
+
if any(
|
|
2654
|
+
pattern.search(candidate)
|
|
2655
|
+
for pattern in MAPPING_LEAK_PATTERNS
|
|
2656
|
+
for candidate in (folded, joined)
|
|
2657
|
+
):
|
|
2658
|
+
_fail("transcript free text leaks pair/arm/replicate mapping")
|
|
2659
|
+
if any(
|
|
2660
|
+
_contains_compact_phrase_projection(screen, compact_text, compact)
|
|
2661
|
+
for compact in mapping_phrase_projections
|
|
2662
|
+
) or _COMPACT_MAPPING_RELATION_PATTERN.search(screen):
|
|
2663
|
+
_fail("transcript free text leaks pair/arm/replicate mapping")
|
|
2664
|
+
if any(
|
|
2665
|
+
pattern.search(candidate)
|
|
2666
|
+
for pattern in HUMAN_EVIDENCE_PATTERNS
|
|
2667
|
+
for candidate in (folded, joined)
|
|
2668
|
+
):
|
|
2669
|
+
_fail("transcript free text contains prior label/adjudication/human evidence")
|
|
2670
|
+
if any(
|
|
2671
|
+
_contains_compact_phrase_projection(screen, compact_text, compact)
|
|
2672
|
+
for compact in human_phrase_projections
|
|
2673
|
+
):
|
|
2674
|
+
_fail("transcript free text contains prior label/adjudication/human evidence")
|
|
2675
|
+
|
|
2676
|
+
|
|
2677
|
+
def _assert_blind_packet_structure(
|
|
2678
|
+
plan: dict[str, Any], packet: dict[str, Any]
|
|
2679
|
+
) -> None:
|
|
2680
|
+
forbidden_keys = {
|
|
2681
|
+
"cell_id",
|
|
2682
|
+
"sequence_index",
|
|
2683
|
+
"experiment_id",
|
|
2684
|
+
"arm_id",
|
|
2685
|
+
"scenario_id",
|
|
2686
|
+
"pair_id",
|
|
2687
|
+
"replicate",
|
|
2688
|
+
"actor_block_id",
|
|
2689
|
+
"source",
|
|
2690
|
+
"events",
|
|
2691
|
+
"observations",
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
def visit(value: Any) -> None:
|
|
2695
|
+
if isinstance(value, dict):
|
|
2696
|
+
leaked_keys = forbidden_keys.intersection(value)
|
|
2697
|
+
if leaked_keys:
|
|
2698
|
+
_fail(f"blind packet contains assignment/source keys {sorted(leaked_keys)!r}")
|
|
2699
|
+
for child in value.values():
|
|
2700
|
+
visit(child)
|
|
2701
|
+
elif isinstance(value, list):
|
|
2702
|
+
for child in value:
|
|
2703
|
+
visit(child)
|
|
2704
|
+
|
|
2705
|
+
visit(packet)
|
|
2706
|
+
role_card = packet["role_card"]
|
|
2707
|
+
role_texts = [
|
|
2708
|
+
role_card["role_brief"],
|
|
2709
|
+
role_card["initial_message"],
|
|
2710
|
+
*[row["statement"] for row in role_card["owned_framings"]],
|
|
2711
|
+
*role_card["forbidden_out_of_role"],
|
|
2712
|
+
packet["domain"],
|
|
2713
|
+
]
|
|
2714
|
+
_assert_blind_texts(plan, role_texts)
|
|
2715
|
+
|
|
2716
|
+
|
|
2717
|
+
def _blind_packet_value(
|
|
2718
|
+
blind_id: str,
|
|
2719
|
+
scenario: dict[str, Any],
|
|
2720
|
+
transcript: dict[str, Any],
|
|
2721
|
+
codebook_raw: bytes,
|
|
2722
|
+
) -> dict[str, Any]:
|
|
2723
|
+
return {
|
|
2724
|
+
"schema_version": "ideation-diversity-isolated-blind-session/1.0",
|
|
2725
|
+
"suite": SUITE,
|
|
2726
|
+
"blind_session_id": blind_id,
|
|
2727
|
+
"content_class": "repository-owned synthetic scholar role cards",
|
|
2728
|
+
"blinded_to": BLINDED_TO,
|
|
2729
|
+
"delivery": {
|
|
2730
|
+
"isolated_single_session": True,
|
|
2731
|
+
"deliver_other_sessions_together": False,
|
|
2732
|
+
"assignment_ledger_gate_required_before_delivery": True,
|
|
2733
|
+
},
|
|
2734
|
+
"assignment_boundary": _assignment_boundary(),
|
|
2735
|
+
"artifact_presence": _artifact_presence(),
|
|
2736
|
+
"free_text_screening": {
|
|
2737
|
+
"frozen_identifiers_rejected": True,
|
|
2738
|
+
"mapping_markers_rejected": True,
|
|
2739
|
+
"prior_label_adjudication_human_markers_rejected": True,
|
|
2740
|
+
},
|
|
2741
|
+
"codebook": {
|
|
2742
|
+
"sha256": _sha(codebook_raw),
|
|
2743
|
+
"text": codebook_raw.decode("utf-8"),
|
|
2744
|
+
},
|
|
2745
|
+
"language": scenario["language"],
|
|
2746
|
+
"domain": scenario["domain"],
|
|
2747
|
+
"role_card": {
|
|
2748
|
+
key: scenario[key]
|
|
2749
|
+
for key in (
|
|
2750
|
+
"role_brief",
|
|
2751
|
+
"initial_message",
|
|
2752
|
+
"owned_framings",
|
|
2753
|
+
"response_policy",
|
|
2754
|
+
"forbidden_out_of_role",
|
|
2755
|
+
)
|
|
2756
|
+
},
|
|
2757
|
+
"transcript": {
|
|
2758
|
+
"stop_reason": transcript["session"]["stop_reason"],
|
|
2759
|
+
"turns": transcript["turns"],
|
|
2760
|
+
},
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
|
|
2764
|
+
def _blind_inventory_value(
|
|
2765
|
+
plan_sha: str, inventory_rows: list[dict[str, str]]
|
|
2766
|
+
) -> dict[str, Any]:
|
|
2767
|
+
return {
|
|
2768
|
+
"schema_version": "ideation-diversity-blind-inventory/1.0",
|
|
2769
|
+
"suite": SUITE,
|
|
2770
|
+
"run_plan_sha256": plan_sha,
|
|
2771
|
+
"packet_count": 48,
|
|
2772
|
+
"delivery_rule": (
|
|
2773
|
+
"deliver_exactly_one_isolated_packet_per_first_round_judge_assignment"
|
|
2774
|
+
),
|
|
2775
|
+
"assignment_boundary": _assignment_boundary(),
|
|
2776
|
+
"artifact_presence": _artifact_presence(),
|
|
2777
|
+
"packets": inventory_rows,
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
|
|
2781
|
+
def _private_map_value(
|
|
2782
|
+
plan_sha: str,
|
|
2783
|
+
inventory_raw: bytes,
|
|
2784
|
+
mapping: list[dict[str, Any]],
|
|
2785
|
+
) -> dict[str, Any]:
|
|
2786
|
+
return {
|
|
2787
|
+
"schema_version": "ideation-diversity-private-arm-map/1.0",
|
|
2788
|
+
"suite": SUITE,
|
|
2789
|
+
"run_plan_sha256": plan_sha,
|
|
2790
|
+
"inventory_sha256": _sha(inventory_raw),
|
|
2791
|
+
"private": True,
|
|
2792
|
+
"protection": {
|
|
2793
|
+
"kind": "procedural_nondisclosure_only",
|
|
2794
|
+
"encrypted": False,
|
|
2795
|
+
"directory_mode": "0700",
|
|
2796
|
+
"file_mode": "0600",
|
|
2797
|
+
"unblind_only_after_raw_labels_and_adjudication_sealed": True,
|
|
2798
|
+
},
|
|
2799
|
+
"mapping": mapping,
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
|
|
2803
|
+
def _blind_manifest_value(
|
|
2804
|
+
plan_sha: str,
|
|
2805
|
+
source_ingestion_manifest_sha: str,
|
|
2806
|
+
inventory_raw: bytes,
|
|
2807
|
+
packets: list[tuple[str, bytes]],
|
|
2808
|
+
private_map_raw: bytes,
|
|
2809
|
+
) -> dict[str, Any]:
|
|
2810
|
+
return {
|
|
2811
|
+
"schema_version": "ideation-diversity-blind-manifest/1.0",
|
|
2812
|
+
"suite": SUITE,
|
|
2813
|
+
"run_plan_sha256": plan_sha,
|
|
2814
|
+
"source_ingestion_manifest_sha256": source_ingestion_manifest_sha,
|
|
2815
|
+
"inventory_ref": "blind/inventory.json",
|
|
2816
|
+
"inventory_sha256": _sha(inventory_raw),
|
|
2817
|
+
"private_map_ref": "blind/private/arm-map.json",
|
|
2818
|
+
"private_map_sha256": _sha(private_map_raw),
|
|
2819
|
+
"packet_count": 48,
|
|
2820
|
+
"packets": [
|
|
2821
|
+
{
|
|
2822
|
+
"blind_session_id": blind_id,
|
|
2823
|
+
"packet_ref": f"blind/sessions/{blind_id}.json",
|
|
2824
|
+
"packet_sha256": _sha(packet_raw),
|
|
2825
|
+
}
|
|
2826
|
+
for blind_id, packet_raw in packets
|
|
2827
|
+
],
|
|
2828
|
+
"finalized_artifacts": True,
|
|
2829
|
+
"exact_inventory_required": True,
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
|
|
2833
|
+
def _blind_intent_value(
|
|
2834
|
+
plan_sha: str,
|
|
2835
|
+
source_ingestion_manifest_sha: str,
|
|
2836
|
+
private_id_nonce: str,
|
|
2837
|
+
inventory_raw: bytes,
|
|
2838
|
+
packets: list[tuple[str, bytes]],
|
|
2839
|
+
private_map_raw: bytes,
|
|
2840
|
+
blind_manifest_raw: bytes,
|
|
2841
|
+
) -> dict[str, Any]:
|
|
2842
|
+
return {
|
|
2843
|
+
"schema_version": "ideation-diversity-blind-intent/1.0",
|
|
2844
|
+
"suite": SUITE,
|
|
2845
|
+
"run_plan_sha256": plan_sha,
|
|
2846
|
+
"source_ingestion_manifest_sha256": source_ingestion_manifest_sha,
|
|
2847
|
+
"private_id_nonce": private_id_nonce,
|
|
2848
|
+
"protection": {
|
|
2849
|
+
"procedural_nondisclosure_only": True,
|
|
2850
|
+
"file_mode": "0600",
|
|
2851
|
+
"deliver_to_judges": False,
|
|
2852
|
+
},
|
|
2853
|
+
"staging_ref": BLIND_STAGING_REF,
|
|
2854
|
+
"final_ref": "blind",
|
|
2855
|
+
"atomic_alias_root": BLIND_ALIAS_ROOT,
|
|
2856
|
+
"atomic_alias_policy": "deterministic_target_hash_hardlink_exact_replay",
|
|
2857
|
+
"blind_manifest_sha256": _sha(blind_manifest_raw),
|
|
2858
|
+
"inventory_sha256": _sha(inventory_raw),
|
|
2859
|
+
"private_map_sha256": _sha(private_map_raw),
|
|
2860
|
+
"packets": [
|
|
2861
|
+
{
|
|
2862
|
+
"blind_session_id": blind_id,
|
|
2863
|
+
"packet_sha256": _sha(packet_raw),
|
|
2864
|
+
}
|
|
2865
|
+
for blind_id, packet_raw in packets
|
|
2866
|
+
],
|
|
2867
|
+
"recovery_policy": "exact_recovery_only_no_second_bundle",
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
|
|
2871
|
+
def _blind_alias_ref(target_ref: str, raw: bytes) -> str:
|
|
2872
|
+
digest = _sha(f"{target_ref}\0{_sha(raw)}".encode("utf-8"))
|
|
2873
|
+
return f"{BLIND_ALIAS_ROOT}/{digest[:32]}.tmp"
|
|
2874
|
+
|
|
2875
|
+
|
|
2876
|
+
def _blind_alias_contract(expected_files: dict[str, bytes]) -> dict[str, tuple[str, bytes]]:
|
|
2877
|
+
return {
|
|
2878
|
+
_blind_alias_ref(target_ref, raw): (target_ref, raw)
|
|
2879
|
+
for target_ref, raw in expected_files.items()
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
|
|
2883
|
+
def _ensure_blind_exact(
|
|
2884
|
+
run_dir: Path, target_ref: str, raw: bytes
|
|
2885
|
+
) -> None:
|
|
2886
|
+
target = run_dir / BLIND_STAGING_REF / target_ref
|
|
2887
|
+
alias = run_dir / _blind_alias_ref(target_ref, raw)
|
|
2888
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
2889
|
+
if target.exists():
|
|
2890
|
+
if _read_file(target, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
|
|
2891
|
+
_fail(f"blind staging artifact collision at {target_ref}")
|
|
2892
|
+
if alias.exists():
|
|
2893
|
+
if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
|
|
2894
|
+
_fail(f"blind atomic alias collision at {alias.name}")
|
|
2895
|
+
if alias.stat().st_ino != target.stat().st_ino:
|
|
2896
|
+
_fail(f"blind atomic alias is not the target hardlink: {alias.name}")
|
|
2897
|
+
return
|
|
2898
|
+
if alias.exists():
|
|
2899
|
+
if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
|
|
2900
|
+
_fail(f"blind atomic alias collision at {alias.name}")
|
|
2901
|
+
else:
|
|
2902
|
+
descriptor: int | None = None
|
|
2903
|
+
try:
|
|
2904
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
2905
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
2906
|
+
flags |= os.O_NOFOLLOW
|
|
2907
|
+
descriptor = os.open(alias, flags, 0o600)
|
|
2908
|
+
view = memoryview(raw)
|
|
2909
|
+
written = 0
|
|
2910
|
+
while written < len(view):
|
|
2911
|
+
count = os.write(descriptor, view[written:])
|
|
2912
|
+
if count <= 0:
|
|
2913
|
+
raise OSError("blind atomic alias write made no progress")
|
|
2914
|
+
written += count
|
|
2915
|
+
os.fsync(descriptor)
|
|
2916
|
+
os.close(descriptor)
|
|
2917
|
+
descriptor = None
|
|
2918
|
+
_fsync_directory(alias.parent)
|
|
2919
|
+
except OSError as exc:
|
|
2920
|
+
raise EnvelopeError(
|
|
2921
|
+
f"cannot stage intent-bound blind alias {alias}: {exc}"
|
|
2922
|
+
) from exc
|
|
2923
|
+
finally:
|
|
2924
|
+
if descriptor is not None:
|
|
2925
|
+
try:
|
|
2926
|
+
os.close(descriptor)
|
|
2927
|
+
except OSError:
|
|
2928
|
+
pass
|
|
2929
|
+
try:
|
|
2930
|
+
os.link(alias, target, follow_symlinks=False)
|
|
2931
|
+
_fsync_directory(target.parent)
|
|
2932
|
+
except OSError as exc:
|
|
2933
|
+
raise EnvelopeError(
|
|
2934
|
+
f"cannot publish intent-bound blind artifact {target}: {exc}"
|
|
2935
|
+
) from exc
|
|
2936
|
+
try:
|
|
2937
|
+
alias.unlink()
|
|
2938
|
+
_fsync_directory(alias.parent)
|
|
2939
|
+
except OSError:
|
|
2940
|
+
# This complete alias is deterministic and hash-bound by blind-intent.
|
|
2941
|
+
# It remains durable evidence and is exact-replayed on every load.
|
|
2942
|
+
pass
|
|
2943
|
+
|
|
2944
|
+
|
|
2945
|
+
def _validate_blind_aliases(
|
|
2946
|
+
run_dir: Path,
|
|
2947
|
+
expected_files: dict[str, bytes],
|
|
2948
|
+
*,
|
|
2949
|
+
final_published: bool,
|
|
2950
|
+
) -> None:
|
|
2951
|
+
root = run_dir / BLIND_ALIAS_ROOT
|
|
2952
|
+
if not root.exists():
|
|
2953
|
+
return
|
|
2954
|
+
if root.is_symlink() or not root.is_dir() or stat.S_IMODE(root.stat().st_mode) != 0o700:
|
|
2955
|
+
_fail("blind atomic alias root must remain a real 0700 directory")
|
|
2956
|
+
observed_files, observed_directories = _relative_files_and_directories(root)
|
|
2957
|
+
if observed_directories:
|
|
2958
|
+
_fail("blind atomic alias root may not contain subdirectories")
|
|
2959
|
+
contract = _blind_alias_contract(expected_files)
|
|
2960
|
+
observed_refs = {f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_files}
|
|
2961
|
+
if not observed_refs <= set(contract):
|
|
2962
|
+
_fail("blind atomic alias root contains an unbound artifact")
|
|
2963
|
+
target_root = run_dir / ("blind" if final_published else BLIND_STAGING_REF)
|
|
2964
|
+
for alias_ref in observed_refs:
|
|
2965
|
+
target_ref, raw = contract[alias_ref]
|
|
2966
|
+
alias = run_dir / alias_ref
|
|
2967
|
+
if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
|
|
2968
|
+
_fail(f"blind atomic alias bytes drifted: {alias_ref}")
|
|
2969
|
+
if stat.S_IMODE(alias.stat().st_mode) != 0o600:
|
|
2970
|
+
_fail(f"blind atomic alias mode must remain 0600: {alias_ref}")
|
|
2971
|
+
target = target_root / target_ref
|
|
2972
|
+
if target.exists() and alias.stat().st_ino != target.stat().st_ino:
|
|
2973
|
+
_fail(f"blind atomic alias is not target hardlink: {alias_ref}")
|
|
2974
|
+
|
|
2975
|
+
|
|
2976
|
+
def _deterministic_blind_id(
|
|
2977
|
+
plan_sha: str,
|
|
2978
|
+
source_ingestion_manifest_sha: str,
|
|
2979
|
+
cell_id: str,
|
|
2980
|
+
private_id_nonce: str,
|
|
2981
|
+
) -> str:
|
|
2982
|
+
digest = _sha(
|
|
2983
|
+
(
|
|
2984
|
+
f"{private_id_nonce}\0{plan_sha}\0"
|
|
2985
|
+
f"{source_ingestion_manifest_sha}\0{cell_id}"
|
|
2986
|
+
).encode("utf-8")
|
|
2987
|
+
)
|
|
2988
|
+
return f"blind-{digest[:24]}"
|
|
2989
|
+
|
|
2990
|
+
|
|
2991
|
+
def _blind_transaction_values(
|
|
2992
|
+
run_dir: Path,
|
|
2993
|
+
plan: dict[str, Any],
|
|
2994
|
+
plan_sha: str,
|
|
2995
|
+
manifest: dict[str, Any],
|
|
2996
|
+
private_id_nonce: str,
|
|
2997
|
+
) -> dict[str, Any]:
|
|
2998
|
+
scenarios = _scenario_index(phase1.load_assets()[0])
|
|
2999
|
+
codebook_raw = _read_file(CODEBOOK)
|
|
3000
|
+
source_sha = _sha(_json_bytes(_source_ingestion_manifest(manifest)))
|
|
3001
|
+
packets: list[tuple[str, bytes]] = []
|
|
3002
|
+
inventory_rows: list[dict[str, str]] = []
|
|
3003
|
+
mapping: list[dict[str, Any]] = []
|
|
3004
|
+
for cell, state in zip(plan["cells"], manifest["cells"]):
|
|
3005
|
+
raw = _read_file(run_dir / state["transcript_ref"])
|
|
3006
|
+
if _sha(raw) != state["transcript_sha256"]:
|
|
3007
|
+
_fail(f"transcript drift before blinding: {cell['cell_id']}")
|
|
3008
|
+
transcript = _strict_loads(raw)
|
|
3009
|
+
_assert_blindable_transcript(plan, transcript)
|
|
3010
|
+
blind_id = _deterministic_blind_id(
|
|
3011
|
+
plan_sha,
|
|
3012
|
+
source_sha,
|
|
3013
|
+
cell["cell_id"],
|
|
3014
|
+
private_id_nonce,
|
|
3015
|
+
)
|
|
3016
|
+
isolated_packet = _blind_packet_value(
|
|
3017
|
+
blind_id, scenarios[cell["scenario_id"]], transcript, codebook_raw
|
|
3018
|
+
)
|
|
3019
|
+
_assert_blind_packet_structure(plan, isolated_packet)
|
|
3020
|
+
_validate_schema(BLIND_PACKET_SCHEMA, isolated_packet, "isolated blind packet")
|
|
3021
|
+
packet_raw = _json_bytes(isolated_packet)
|
|
3022
|
+
packets.append((blind_id, packet_raw))
|
|
3023
|
+
inventory_rows.append(
|
|
3024
|
+
{"blind_session_id": blind_id, "packet_sha256": _sha(packet_raw)}
|
|
3025
|
+
)
|
|
3026
|
+
mapping.append(
|
|
3027
|
+
{
|
|
3028
|
+
"blind_session_id": blind_id,
|
|
3029
|
+
"cell_id": cell["cell_id"],
|
|
3030
|
+
"experiment_id": cell["experiment_id"],
|
|
3031
|
+
"arm_id": cell["arm_id"],
|
|
3032
|
+
"scenario_id": cell["scenario_id"],
|
|
3033
|
+
"pair_id": cell["pair_id"],
|
|
3034
|
+
"replicate": cell["replicate"],
|
|
3035
|
+
}
|
|
3036
|
+
)
|
|
3037
|
+
packets.sort(key=lambda row: row[0])
|
|
3038
|
+
inventory_rows.sort(key=lambda row: row["blind_session_id"])
|
|
3039
|
+
mapping.sort(key=lambda row: row["blind_session_id"])
|
|
3040
|
+
if len({blind_id for blind_id, _ in packets}) != 48:
|
|
3041
|
+
_fail("deterministic blind ids collided")
|
|
3042
|
+
inventory = _blind_inventory_value(plan_sha, inventory_rows)
|
|
3043
|
+
_validate_schema(BLIND_INVENTORY_SCHEMA, inventory, "blind inventory")
|
|
3044
|
+
inventory_raw = _json_bytes(inventory)
|
|
3045
|
+
private_map = _private_map_value(plan_sha, inventory_raw, mapping)
|
|
3046
|
+
_validate_schema(PRIVATE_ARM_MAP_SCHEMA, private_map, "private arm map")
|
|
3047
|
+
private_map_raw = _json_bytes(private_map)
|
|
3048
|
+
blind_manifest = _blind_manifest_value(
|
|
3049
|
+
plan_sha,
|
|
3050
|
+
source_sha,
|
|
3051
|
+
inventory_raw,
|
|
3052
|
+
packets,
|
|
3053
|
+
private_map_raw,
|
|
3054
|
+
)
|
|
3055
|
+
_validate_schema(BLIND_MANIFEST_SCHEMA, blind_manifest, "blind manifest")
|
|
3056
|
+
blind_manifest_raw = _json_bytes(blind_manifest)
|
|
3057
|
+
intent = _blind_intent_value(
|
|
3058
|
+
plan_sha,
|
|
3059
|
+
source_sha,
|
|
3060
|
+
private_id_nonce,
|
|
3061
|
+
inventory_raw,
|
|
3062
|
+
packets,
|
|
3063
|
+
private_map_raw,
|
|
3064
|
+
blind_manifest_raw,
|
|
3065
|
+
)
|
|
3066
|
+
_validate_schema(BLIND_INTENT_SCHEMA, intent, "blind intent")
|
|
3067
|
+
expected_files = {
|
|
3068
|
+
**{
|
|
3069
|
+
f"sessions/{blind_id}.json": packet_raw
|
|
3070
|
+
for blind_id, packet_raw in packets
|
|
3071
|
+
},
|
|
3072
|
+
"inventory.json": inventory_raw,
|
|
3073
|
+
"private/arm-map.json": private_map_raw,
|
|
3074
|
+
"manifest.json": blind_manifest_raw,
|
|
3075
|
+
}
|
|
3076
|
+
return {
|
|
3077
|
+
"intent": intent,
|
|
3078
|
+
"intent_raw": _json_bytes(intent),
|
|
3079
|
+
"expected_staging_files": expected_files,
|
|
3080
|
+
"blind_manifest_sha256": _sha(blind_manifest_raw),
|
|
3081
|
+
"inventory_sha256": _sha(inventory_raw),
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
|
|
3085
|
+
def _validate_blind_staging(
|
|
3086
|
+
staging: Path,
|
|
3087
|
+
expected_files: dict[str, bytes],
|
|
3088
|
+
*,
|
|
3089
|
+
require_complete: bool,
|
|
3090
|
+
) -> None:
|
|
3091
|
+
if staging.is_symlink() or not staging.is_dir():
|
|
3092
|
+
_fail("blind staging must be a real non-symlink directory")
|
|
3093
|
+
observed_files, observed_directories = _relative_files_and_directories(staging)
|
|
3094
|
+
allowed_files = set(expected_files)
|
|
3095
|
+
allowed_directories = _parent_directories(allowed_files)
|
|
3096
|
+
if not observed_files <= allowed_files or not observed_directories <= allowed_directories:
|
|
3097
|
+
_fail("blind staging contains an unbound artifact or directory")
|
|
3098
|
+
if require_complete and (
|
|
3099
|
+
observed_files != allowed_files or observed_directories != allowed_directories
|
|
3100
|
+
):
|
|
3101
|
+
_fail("blind staging transaction is incomplete")
|
|
3102
|
+
for ref in observed_files:
|
|
3103
|
+
if _read_file(staging / ref) != expected_files[ref]:
|
|
3104
|
+
_fail(f"blind staging artifact collision at {ref}")
|
|
3105
|
+
private_directory = staging / "private"
|
|
3106
|
+
private_map_path = private_directory / "arm-map.json"
|
|
3107
|
+
if private_directory.exists() and stat.S_IMODE(private_directory.stat().st_mode) != 0o700:
|
|
3108
|
+
_fail("blind staging private directory mode must be 0700")
|
|
3109
|
+
if private_map_path.exists() and stat.S_IMODE(private_map_path.stat().st_mode) != 0o600:
|
|
3110
|
+
_fail("blind staging private map mode must be 0600")
|
|
3111
|
+
|
|
3112
|
+
|
|
3113
|
+
def _validate_blind_transaction(
|
|
3114
|
+
run_dir: Path,
|
|
3115
|
+
plan: dict[str, Any],
|
|
3116
|
+
plan_sha: str,
|
|
3117
|
+
manifest: dict[str, Any],
|
|
3118
|
+
) -> dict[str, Any]:
|
|
3119
|
+
intent_path = run_dir / BLIND_INTENT_REF
|
|
3120
|
+
if not intent_path.exists():
|
|
3121
|
+
if (run_dir / BLIND_STAGING_REF).exists():
|
|
3122
|
+
_fail("blind staging exists without its durable transaction intent")
|
|
3123
|
+
return {"present": False}
|
|
3124
|
+
if manifest["stopped"] or manifest["status"] not in {
|
|
3125
|
+
"complete",
|
|
3126
|
+
"blind_finalized",
|
|
3127
|
+
}:
|
|
3128
|
+
_fail("blind transaction intent is forbidden before complete ingestion")
|
|
3129
|
+
intent_raw = _read_file(intent_path)
|
|
3130
|
+
intent = _strict_loads(intent_raw)
|
|
3131
|
+
_validate_schema(BLIND_INTENT_SCHEMA, intent, "blind intent")
|
|
3132
|
+
if stat.S_IMODE(intent_path.stat().st_mode) != 0o600:
|
|
3133
|
+
_fail("blind transaction intent mode must remain 0600")
|
|
3134
|
+
values = _blind_transaction_values(
|
|
3135
|
+
run_dir,
|
|
3136
|
+
plan,
|
|
3137
|
+
plan_sha,
|
|
3138
|
+
manifest,
|
|
3139
|
+
intent["private_id_nonce"],
|
|
3140
|
+
)
|
|
3141
|
+
if intent_raw != values["intent_raw"]:
|
|
3142
|
+
_fail("blind transaction intent differs from deterministic exact replay")
|
|
3143
|
+
staging = run_dir / BLIND_STAGING_REF
|
|
3144
|
+
final = run_dir / "blind"
|
|
3145
|
+
if staging.exists() and final.exists():
|
|
3146
|
+
_fail("blind transaction has both staging and finalized directories")
|
|
3147
|
+
if staging.exists():
|
|
3148
|
+
_validate_blind_staging(
|
|
3149
|
+
staging,
|
|
3150
|
+
values["expected_staging_files"],
|
|
3151
|
+
require_complete=False,
|
|
3152
|
+
)
|
|
3153
|
+
if final.exists():
|
|
3154
|
+
final_manifest_raw = _read_file(final / "manifest.json")
|
|
3155
|
+
if _sha(final_manifest_raw) != values["blind_manifest_sha256"]:
|
|
3156
|
+
_fail("published blind bundle differs from its durable transaction intent")
|
|
3157
|
+
final_inventory_raw = _read_file(final / "inventory.json")
|
|
3158
|
+
if _sha(final_inventory_raw) != values["inventory_sha256"]:
|
|
3159
|
+
_fail("published blind inventory differs from its durable transaction intent")
|
|
3160
|
+
_validate_blind_aliases(
|
|
3161
|
+
run_dir,
|
|
3162
|
+
values["expected_staging_files"],
|
|
3163
|
+
final_published=final.exists(),
|
|
3164
|
+
)
|
|
3165
|
+
return {
|
|
3166
|
+
**values,
|
|
3167
|
+
"present": True,
|
|
3168
|
+
"staging_present": staging.exists(),
|
|
3169
|
+
"final_present": final.exists(),
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
|
|
3173
|
+
def _source_ingestion_manifest(manifest: dict[str, Any]) -> dict[str, Any]:
|
|
3174
|
+
source = copy.deepcopy(manifest)
|
|
3175
|
+
source["status"] = "complete"
|
|
3176
|
+
source["blind_manifest_ref"] = None
|
|
3177
|
+
source["blind_manifest_sha256"] = None
|
|
3178
|
+
return source
|
|
3179
|
+
|
|
3180
|
+
|
|
3181
|
+
def _validate_blind_bundle(
|
|
3182
|
+
run_dir: Path,
|
|
3183
|
+
plan: dict[str, Any],
|
|
3184
|
+
plan_sha: str,
|
|
3185
|
+
manifest: dict[str, Any],
|
|
3186
|
+
) -> tuple[str, str]:
|
|
3187
|
+
blind_manifest_raw = _read_file(run_dir / "blind/manifest.json")
|
|
3188
|
+
blind_manifest_sha = _sha(blind_manifest_raw)
|
|
3189
|
+
blind_manifest = _strict_loads(blind_manifest_raw)
|
|
3190
|
+
_validate_schema(BLIND_MANIFEST_SCHEMA, blind_manifest, "blind manifest")
|
|
3191
|
+
if blind_manifest["run_plan_sha256"] != plan_sha:
|
|
3192
|
+
_fail("blind manifest is bound to a different run plan")
|
|
3193
|
+
source_sha = _sha(_json_bytes(_source_ingestion_manifest(manifest)))
|
|
3194
|
+
if blind_manifest["source_ingestion_manifest_sha256"] != source_sha:
|
|
3195
|
+
_fail("blind manifest source ingestion state drifted")
|
|
3196
|
+
if manifest["status"] == "blind_finalized" and (
|
|
3197
|
+
manifest["blind_manifest_ref"], manifest["blind_manifest_sha256"]
|
|
3198
|
+
) != ("blind/manifest.json", blind_manifest_sha):
|
|
3199
|
+
_fail("finalized ingestion state disagrees with blind manifest bytes")
|
|
3200
|
+
|
|
3201
|
+
expected_bundle_files = {
|
|
3202
|
+
"manifest.json",
|
|
3203
|
+
str(Path(blind_manifest["inventory_ref"]).relative_to("blind")),
|
|
3204
|
+
str(Path(blind_manifest["private_map_ref"]).relative_to("blind")),
|
|
3205
|
+
}
|
|
3206
|
+
expected_bundle_files.update(
|
|
3207
|
+
str(Path(row["packet_ref"]).relative_to("blind"))
|
|
3208
|
+
for row in blind_manifest["packets"]
|
|
3209
|
+
)
|
|
3210
|
+
observed_files, observed_directories = _relative_files_and_directories(
|
|
3211
|
+
run_dir / "blind"
|
|
3212
|
+
)
|
|
3213
|
+
if observed_files != expected_bundle_files or observed_directories != _parent_directories(
|
|
3214
|
+
expected_bundle_files
|
|
3215
|
+
):
|
|
3216
|
+
_fail("blind bundle file/directory inventory is not exact")
|
|
3217
|
+
|
|
3218
|
+
inventory_raw = _read_file(run_dir / blind_manifest["inventory_ref"])
|
|
3219
|
+
if _sha(inventory_raw) != blind_manifest["inventory_sha256"]:
|
|
3220
|
+
_fail("blind inventory hash drift")
|
|
3221
|
+
inventory = _strict_loads(inventory_raw)
|
|
3222
|
+
_validate_schema(BLIND_INVENTORY_SCHEMA, inventory, "blind inventory")
|
|
3223
|
+
|
|
3224
|
+
private_map_path = run_dir / blind_manifest["private_map_ref"]
|
|
3225
|
+
private_map_raw = _read_file(private_map_path)
|
|
3226
|
+
if _sha(private_map_raw) != blind_manifest["private_map_sha256"]:
|
|
3227
|
+
_fail("private arm-map hash drift")
|
|
3228
|
+
if stat.S_IMODE(private_map_path.parent.stat().st_mode) != 0o700:
|
|
3229
|
+
_fail("private arm-map directory mode must remain 0700")
|
|
3230
|
+
if stat.S_IMODE(private_map_path.stat().st_mode) != 0o600:
|
|
3231
|
+
_fail("private arm-map file mode must remain 0600")
|
|
3232
|
+
private_map = _strict_loads(private_map_raw)
|
|
3233
|
+
_validate_schema(PRIVATE_ARM_MAP_SCHEMA, private_map, "private arm map")
|
|
3234
|
+
mapping = private_map.get("mapping")
|
|
3235
|
+
if not isinstance(mapping, list) or len(mapping) != 48:
|
|
3236
|
+
_fail("private arm map must contain exactly 48 mappings")
|
|
3237
|
+
mapping_by_id = {row.get("blind_session_id"): row for row in mapping}
|
|
3238
|
+
if None in mapping_by_id or len(mapping_by_id) != 48:
|
|
3239
|
+
_fail("private arm map blind ids must be unique")
|
|
3240
|
+
|
|
3241
|
+
scenarios = _scenario_index(phase1.load_assets()[0])
|
|
3242
|
+
codebook_raw = _read_file(CODEBOOK)
|
|
3243
|
+
packet_pairs: list[tuple[str, bytes]] = []
|
|
3244
|
+
inventory_rows: list[dict[str, str]] = []
|
|
3245
|
+
expected_mapping: list[dict[str, Any]] = []
|
|
3246
|
+
plan_by_cell = {cell["cell_id"]: cell for cell in plan["cells"]}
|
|
3247
|
+
state_by_cell = {row["cell_id"]: row for row in manifest["cells"]}
|
|
3248
|
+
for packet_row in blind_manifest["packets"]:
|
|
3249
|
+
blind_id = packet_row["blind_session_id"]
|
|
3250
|
+
mapped = mapping_by_id.get(blind_id)
|
|
3251
|
+
if mapped is None or mapped.get("cell_id") not in plan_by_cell:
|
|
3252
|
+
_fail(f"blind mapping is missing or invalid for {blind_id}")
|
|
3253
|
+
cell = plan_by_cell[mapped["cell_id"]]
|
|
3254
|
+
expected_map_row = {
|
|
3255
|
+
"blind_session_id": blind_id,
|
|
3256
|
+
"cell_id": cell["cell_id"],
|
|
3257
|
+
"experiment_id": cell["experiment_id"],
|
|
3258
|
+
"arm_id": cell["arm_id"],
|
|
3259
|
+
"scenario_id": cell["scenario_id"],
|
|
3260
|
+
"pair_id": cell["pair_id"],
|
|
3261
|
+
"replicate": cell["replicate"],
|
|
3262
|
+
}
|
|
3263
|
+
if mapped != expected_map_row:
|
|
3264
|
+
_fail(f"private arm mapping drift for {blind_id}")
|
|
3265
|
+
transcript_raw = _read_file(
|
|
3266
|
+
run_dir / state_by_cell[cell["cell_id"]]["transcript_ref"]
|
|
3267
|
+
)
|
|
3268
|
+
transcript = _strict_loads(transcript_raw)
|
|
3269
|
+
_assert_blindable_transcript(plan, transcript)
|
|
3270
|
+
expected_packet = _blind_packet_value(
|
|
3271
|
+
blind_id,
|
|
3272
|
+
scenarios[cell["scenario_id"]],
|
|
3273
|
+
transcript,
|
|
3274
|
+
codebook_raw,
|
|
3275
|
+
)
|
|
3276
|
+
_assert_blind_packet_structure(plan, expected_packet)
|
|
3277
|
+
expected_packet_raw = _json_bytes(expected_packet)
|
|
3278
|
+
packet_raw = _read_file(run_dir / packet_row["packet_ref"])
|
|
3279
|
+
if packet_raw != expected_packet_raw or _sha(packet_raw) != packet_row[
|
|
3280
|
+
"packet_sha256"
|
|
3281
|
+
]:
|
|
3282
|
+
_fail(f"blind packet replay/hash drift for {blind_id}")
|
|
3283
|
+
packet_pairs.append((blind_id, packet_raw))
|
|
3284
|
+
inventory_rows.append(
|
|
3285
|
+
{"blind_session_id": blind_id, "packet_sha256": _sha(packet_raw)}
|
|
3286
|
+
)
|
|
3287
|
+
expected_mapping.append(expected_map_row)
|
|
3288
|
+
|
|
3289
|
+
packet_pairs.sort(key=lambda row: row[0])
|
|
3290
|
+
inventory_rows.sort(key=lambda row: row["blind_session_id"])
|
|
3291
|
+
expected_mapping.sort(key=lambda row: row["blind_session_id"])
|
|
3292
|
+
if inventory != _blind_inventory_value(plan_sha, inventory_rows):
|
|
3293
|
+
_fail("blind inventory replay drift")
|
|
3294
|
+
expected_private_map = _private_map_value(
|
|
3295
|
+
plan_sha, inventory_raw, expected_mapping
|
|
3296
|
+
)
|
|
3297
|
+
if private_map != expected_private_map:
|
|
3298
|
+
_fail("private arm-map replay drift")
|
|
3299
|
+
expected_blind_manifest = _blind_manifest_value(
|
|
3300
|
+
plan_sha,
|
|
3301
|
+
source_sha,
|
|
3302
|
+
inventory_raw,
|
|
3303
|
+
packet_pairs,
|
|
3304
|
+
private_map_raw,
|
|
3305
|
+
)
|
|
3306
|
+
if blind_manifest != expected_blind_manifest:
|
|
3307
|
+
_fail("blind manifest replay drift")
|
|
3308
|
+
return blind_manifest_sha, _sha(inventory_raw)
|
|
3309
|
+
|
|
3310
|
+
|
|
3311
|
+
def _finalize_blind_state(
|
|
3312
|
+
run_dir: Path,
|
|
3313
|
+
plan: dict[str, Any],
|
|
3314
|
+
plan_sha: str,
|
|
3315
|
+
manifest: dict[str, Any],
|
|
3316
|
+
blind_manifest_sha: str,
|
|
3317
|
+
) -> dict[str, Any]:
|
|
3318
|
+
finalized = copy.deepcopy(manifest)
|
|
3319
|
+
finalized["status"] = "blind_finalized"
|
|
3320
|
+
finalized["blind_manifest_ref"] = "blind/manifest.json"
|
|
3321
|
+
finalized["blind_manifest_sha256"] = blind_manifest_sha
|
|
3322
|
+
_validate_manifest(finalized, plan, plan_sha)
|
|
3323
|
+
_replace_json(run_dir / "ingestion-manifest.json", finalized)
|
|
3324
|
+
return finalized
|
|
3325
|
+
|
|
3326
|
+
|
|
3327
|
+
def prepare_blind_packet(args: argparse.Namespace) -> dict[str, Any]:
|
|
3328
|
+
plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
|
|
3329
|
+
_recover_completed_attempts(args.run_dir, plan, manifest)
|
|
3330
|
+
_validate_materials(args.run_dir, plan, required=True)
|
|
3331
|
+
_validate_ingested_artifacts(args.run_dir, plan, plan_sha, manifest)
|
|
3332
|
+
if manifest["status"] == "blind_finalized":
|
|
3333
|
+
_fail("blind packet is already finalized and write-once")
|
|
3334
|
+
if manifest["status"] != "complete" or manifest["stopped"]:
|
|
3335
|
+
_fail("blind packet requires 48 complete, unstopped transcript ingestions")
|
|
3336
|
+
|
|
3337
|
+
blind_root = args.run_dir / "blind"
|
|
3338
|
+
intent_path = args.run_dir / BLIND_INTENT_REF
|
|
3339
|
+
intent_preexisting = intent_path.exists()
|
|
3340
|
+
if not intent_preexisting:
|
|
3341
|
+
_validate_run_inventory(args.run_dir, plan, manifest)
|
|
3342
|
+
private_id_nonce = secrets.token_hex(32)
|
|
3343
|
+
else:
|
|
3344
|
+
existing_intent = _strict_loads(_read_file(intent_path))
|
|
3345
|
+
_validate_schema(BLIND_INTENT_SCHEMA, existing_intent, "blind intent")
|
|
3346
|
+
private_id_nonce = existing_intent["private_id_nonce"]
|
|
3347
|
+
values = _blind_transaction_values(
|
|
3348
|
+
args.run_dir,
|
|
3349
|
+
plan,
|
|
3350
|
+
plan_sha,
|
|
3351
|
+
manifest,
|
|
3352
|
+
private_id_nonce,
|
|
3353
|
+
)
|
|
3354
|
+
_ensure_exact_new(intent_path, values["intent_raw"], mode=0o600)
|
|
3355
|
+
if intent_preexisting:
|
|
3356
|
+
if stat.S_IMODE(intent_path.stat().st_mode) != 0o600:
|
|
3357
|
+
_fail("blind transaction intent mode must remain 0600")
|
|
3358
|
+
else:
|
|
3359
|
+
os.chmod(intent_path, 0o600)
|
|
3360
|
+
alias_root = args.run_dir / BLIND_ALIAS_ROOT
|
|
3361
|
+
if not alias_root.exists():
|
|
3362
|
+
alias_root.mkdir(mode=0o700)
|
|
3363
|
+
_fsync_directory(args.run_dir)
|
|
3364
|
+
os.chmod(alias_root, 0o700)
|
|
3365
|
+
transaction = _validate_blind_transaction(
|
|
3366
|
+
args.run_dir, plan, plan_sha, manifest
|
|
3367
|
+
)
|
|
3368
|
+
staging = args.run_dir / BLIND_STAGING_REF
|
|
3369
|
+
if not blind_root.exists():
|
|
3370
|
+
_validate_run_inventory(args.run_dir, plan, manifest)
|
|
3371
|
+
recovered = bool(
|
|
3372
|
+
intent_preexisting or transaction["staging_present"] or blind_root.exists()
|
|
3373
|
+
)
|
|
3374
|
+
if not blind_root.exists():
|
|
3375
|
+
if not staging.exists():
|
|
3376
|
+
staging.mkdir(mode=0o755)
|
|
3377
|
+
_fsync_directory(args.run_dir)
|
|
3378
|
+
_validate_blind_staging(
|
|
3379
|
+
staging,
|
|
3380
|
+
values["expected_staging_files"],
|
|
3381
|
+
require_complete=False,
|
|
3382
|
+
)
|
|
3383
|
+
expected_staging_files = values["expected_staging_files"]
|
|
3384
|
+
ordered_refs = [
|
|
3385
|
+
*sorted(ref for ref in expected_staging_files if ref.startswith("sessions/")),
|
|
3386
|
+
"inventory.json",
|
|
3387
|
+
"private/arm-map.json",
|
|
3388
|
+
"manifest.json",
|
|
3389
|
+
]
|
|
3390
|
+
for ref in ordered_refs:
|
|
3391
|
+
raw = expected_staging_files[ref]
|
|
3392
|
+
target = staging / ref
|
|
3393
|
+
if ref == "private/arm-map.json":
|
|
3394
|
+
target.parent.mkdir(mode=0o700, exist_ok=True)
|
|
3395
|
+
os.chmod(target.parent, 0o700)
|
|
3396
|
+
_ensure_blind_exact(args.run_dir, ref, raw)
|
|
3397
|
+
os.chmod(target, 0o600)
|
|
3398
|
+
else:
|
|
3399
|
+
_ensure_blind_exact(args.run_dir, ref, raw)
|
|
3400
|
+
_validate_blind_staging(
|
|
3401
|
+
staging,
|
|
3402
|
+
values["expected_staging_files"],
|
|
3403
|
+
require_complete=True,
|
|
3404
|
+
)
|
|
3405
|
+
try:
|
|
3406
|
+
os.rename(staging, blind_root)
|
|
3407
|
+
_fsync_directory(args.run_dir)
|
|
3408
|
+
except OSError as exc:
|
|
3409
|
+
raise EnvelopeError(
|
|
3410
|
+
f"cannot atomically finalize deterministic blind bundle: {exc}"
|
|
3411
|
+
) from exc
|
|
3412
|
+
|
|
3413
|
+
blind_manifest_sha, inventory_sha = _validate_blind_bundle(
|
|
3414
|
+
args.run_dir, plan, plan_sha, manifest
|
|
3415
|
+
)
|
|
3416
|
+
finalized = _finalize_blind_state(
|
|
3417
|
+
args.run_dir, plan, plan_sha, manifest, blind_manifest_sha
|
|
3418
|
+
)
|
|
3419
|
+
_validate_run_inventory(args.run_dir, plan, finalized)
|
|
3420
|
+
return {
|
|
3421
|
+
"inventory_sha256": inventory_sha,
|
|
3422
|
+
"sessions": 48,
|
|
3423
|
+
"artifact_presence": _artifact_presence(),
|
|
3424
|
+
"state": "blind_finalized",
|
|
3425
|
+
"recovered_existing_atomic_bundle": recovered,
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
|
|
3429
|
+
def _parser() -> argparse.ArgumentParser:
|
|
3430
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
3431
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
3432
|
+
init = sub.add_parser("init-run", help="freeze a 48-cell no-call run plan")
|
|
3433
|
+
init.add_argument("--run-dir", type=Path, required=True)
|
|
3434
|
+
init.add_argument("--run-id", required=True)
|
|
3435
|
+
init.add_argument(
|
|
3436
|
+
"--suite-commit",
|
|
3437
|
+
required=True,
|
|
3438
|
+
help="operator-declared 40-hex provenance; existence is not verified",
|
|
3439
|
+
)
|
|
3440
|
+
init.add_argument("--order-seed", required=True)
|
|
3441
|
+
init.add_argument("--subject-provider", required=True)
|
|
3442
|
+
init.add_argument("--subject-model", required=True)
|
|
3443
|
+
init.add_argument("--subject-runtime", required=True)
|
|
3444
|
+
init.add_argument("--subject-runtime-version", required=True)
|
|
3445
|
+
init.add_argument("--auth-mode", required=True)
|
|
3446
|
+
init.add_argument("--reasoning-effort", required=True)
|
|
3447
|
+
init.add_argument(
|
|
3448
|
+
"--input-token-cap",
|
|
3449
|
+
type=int,
|
|
3450
|
+
required=True,
|
|
3451
|
+
help="operator-declared cap; this no-call runner does not verify usage",
|
|
3452
|
+
)
|
|
3453
|
+
init.add_argument(
|
|
3454
|
+
"--output-token-cap",
|
|
3455
|
+
type=int,
|
|
3456
|
+
required=True,
|
|
3457
|
+
help="operator-declared cap; this no-call runner does not verify usage",
|
|
3458
|
+
)
|
|
3459
|
+
materialize_parser = sub.add_parser(
|
|
3460
|
+
"materialize",
|
|
3461
|
+
help="materialize prompt, actor, and operator packets only",
|
|
3462
|
+
)
|
|
3463
|
+
validate_parser = sub.add_parser(
|
|
3464
|
+
"validate",
|
|
3465
|
+
help="validate frozen plan, materials, and ingested hashes",
|
|
3466
|
+
)
|
|
3467
|
+
blind_parser = sub.add_parser(
|
|
3468
|
+
"prepare-blind-packet",
|
|
3469
|
+
help="prepare isolated unlabeled arm-blind packets",
|
|
3470
|
+
)
|
|
3471
|
+
for command_parser in (materialize_parser, validate_parser, blind_parser):
|
|
3472
|
+
command_parser.add_argument("--run-dir", type=Path, required=True)
|
|
3473
|
+
command_parser.add_argument("--plan-sha256", required=True)
|
|
3474
|
+
ingest_parser = sub.add_parser("ingest", help="ingest one external transcript in frozen order")
|
|
3475
|
+
ingest_parser.add_argument("--run-dir", type=Path, required=True)
|
|
3476
|
+
ingest_parser.add_argument("--plan-sha256", required=True)
|
|
3477
|
+
ingest_parser.add_argument("--transcript", type=Path, required=True)
|
|
3478
|
+
ingest_parser.add_argument("--authorization-record", type=Path, required=True)
|
|
3479
|
+
return parser
|
|
3480
|
+
|
|
3481
|
+
|
|
3482
|
+
def main(argv: list[str] | None = None) -> int:
|
|
3483
|
+
args = _parser().parse_args(argv)
|
|
3484
|
+
try:
|
|
3485
|
+
if args.command == "init-run":
|
|
3486
|
+
result = init_run(args)
|
|
3487
|
+
elif args.command == "materialize":
|
|
3488
|
+
result = materialize(args)
|
|
3489
|
+
elif args.command == "validate":
|
|
3490
|
+
result = validate_run(args)
|
|
3491
|
+
elif args.command == "ingest":
|
|
3492
|
+
result = ingest(args)
|
|
3493
|
+
elif args.command == "prepare-blind-packet":
|
|
3494
|
+
result = prepare_blind_packet(args)
|
|
3495
|
+
else:
|
|
3496
|
+
raise AssertionError(f"unhandled command {args.command!r}")
|
|
3497
|
+
except EnvelopeError as exc:
|
|
3498
|
+
print(f"{SUITE}: FAIL: {exc}", file=sys.stderr)
|
|
3499
|
+
return 2
|
|
3500
|
+
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
|
|
3501
|
+
return 0
|
|
3502
|
+
|
|
3503
|
+
|
|
3504
|
+
if __name__ == "__main__":
|
|
3505
|
+
raise SystemExit(main())
|