eduevidence 5.2.0 → 6.2.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/CONTRIBUTING.md +105 -0
- package/README.md +142 -75
- package/README.zh-CN.md +73 -30
- package/SKILL.md +397 -131
- package/agents/openai.yaml +4 -0
- package/assets/readme/controlled-execution.svg +34 -0
- package/assets/readme/landing-tour.gif +0 -0
- package/assets/readme/logo.png +0 -0
- package/assets/readme/research-workflow.svg +56 -0
- package/assets/readme/studio-graph.png +0 -0
- package/assets/readme/studio-overview.png +0 -0
- package/assets/readme/studio-reports.png +0 -0
- package/assets/readme/studio-tour.gif +0 -0
- package/autoevolve/config.yaml +17 -0
- package/autoevolve/program.md +25 -0
- package/autoevolve/protected.manifest.yaml +34 -0
- package/benchmarks/adversarial/cases.jsonl +7 -0
- package/benchmarks/evidence-library.json +5268 -0
- package/benchmarks/partitions.json +8 -0
- package/bin/eduevidence.js +2 -1
- package/docs/architecture.md +496 -0
- package/docs/autoresearch-evolution-plan.md +2903 -0
- package/docs/autoresearch-implementation-status.md +101 -0
- package/docs/demo-storyboard.md +20 -0
- package/docs/demo-workplace-ai.md +92 -0
- package/docs/demo.md +32 -0
- package/docs/install-guide.md +150 -0
- package/docs/orchestration-role-model.md +1254 -0
- package/docs/release-closeout/README.md +17 -0
- package/docs/release-closeout/frontend-acceptance.md +23 -0
- package/docs/release-closeout/issues.md +19 -0
- package/docs/release-closeout/verification.md +28 -0
- package/docs/release-contract.md +108 -0
- package/docs/research-studio-guide.zh-CN.md +166 -0
- package/docs/sciverse-api.md +125 -0
- package/eduevidence_cli.py +29 -13
- package/engine/_resources.py +13 -0
- package/engine/autoevolve/__init__.py +3 -0
- package/engine/autoevolve/agent_view.py +167 -0
- package/engine/autoevolve/core.py +357 -0
- package/engine/autoevolve/events.py +11 -0
- package/engine/autoevolve/git_workspace.py +77 -0
- package/engine/autoevolve/projection.py +23 -0
- package/engine/autoevolve/runner.py +413 -0
- package/engine/autoevolve/trust.py +146 -0
- package/engine/autoresearch/__init__.py +6 -0
- package/engine/autoresearch/commit.py +132 -0
- package/engine/autoresearch/contracts.py +126 -0
- package/engine/autoresearch/controller.py +207 -0
- package/engine/autoresearch/events.py +12 -0
- package/engine/autoresearch/gap_priority.py +168 -0
- package/engine/autoresearch/projection.py +30 -0
- package/engine/autoresearch/research_memory.py +59 -0
- package/engine/autoresearch/saturation.py +91 -0
- package/engine/briefs.py +2 -1
- package/engine/capabilities.py +1 -0
- package/engine/contracts.py +3 -1
- package/engine/decision_policy.py +96 -0
- package/engine/evidence_graph.py +14 -10
- package/engine/evidencecore.py +7 -5
- package/engine/gaps.py +132 -73
- package/engine/ids.py +2 -0
- package/engine/judge_pack.py +65 -0
- package/engine/library.py +6 -2
- package/engine/library_builtin.py +3 -1
- package/engine/living.py +36 -5
- package/engine/meta_synthesis.py +3 -1
- package/engine/migration.py +88 -3
- package/engine/orchestration.py +460 -0
- package/engine/paths.py +2 -0
- package/engine/pilot.py +36 -33
- package/engine/project.py +2 -2
- package/engine/research_service.py +113 -0
- package/engine/studio_read_model.py +400 -0
- package/engine/taxonomy.py +211 -0
- package/engine/tribunal.py +44 -33
- package/engine/update.py +1 -0
- package/engine/versions.py +1 -1
- package/engine/worker_result.py +109 -0
- package/engine/workflows.py +70 -0
- package/examples/ai-coding-assistant-evidence/EduEvidence_Report.html +2934 -0
- package/examples/ai-coding-assistant-evidence/artifact_manifest.json +15 -0
- package/examples/ai-coding-assistant-evidence/citation_check.json +79 -0
- package/examples/ai-coding-assistant-evidence/claims.jsonl +12 -0
- package/examples/ai-coding-assistant-evidence/evaluation.json +35 -0
- package/examples/ai-coding-assistant-evidence/evidence.jsonl +12 -0
- package/examples/ai-coding-assistant-evidence/final_verdict.json +107 -0
- package/examples/ai-coding-assistant-evidence/frame.json +48 -0
- package/examples/ai-coding-assistant-evidence/gate_report.json +101 -0
- package/examples/ai-coding-assistant-evidence/intervention.json +51 -0
- package/examples/ai-coding-assistant-evidence/methodology.json +36 -0
- package/examples/ai-coding-assistant-evidence/raw_verdict.json +86 -0
- package/examples/ai-coding-assistant-evidence/report_spec.json +230 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_academic.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_claude.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_datalab-dark.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_datalab.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/EduEvidence_Report_presentation.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/report_academic.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/report_claude.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/report_datalab-dark.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/report_datalab.html +2934 -0
- package/examples/ai-coding-assistant-evidence/reports-5themes/report_presentation.html +2934 -0
- package/examples/ai-coding-assistant-evidence/result.json +1457 -0
- package/examples/ai-coding-assistant-evidence/result.zh.json +1457 -0
- package/examples/ai-coding-assistant-evidence/skeptic.json +72 -0
- package/examples/ai-coding-assistant-evidence/sources.jsonl +8 -0
- package/examples/ai-coding-assistant-evidence/verdict.json +107 -0
- package/examples/spaced-retrieval-practice/applicability.json +14 -0
- package/examples/spaced-retrieval-practice/artifact_manifest.json +15 -0
- package/examples/spaced-retrieval-practice/claims.jsonl +3 -0
- package/examples/spaced-retrieval-practice/evidence.jsonl +6 -0
- package/examples/spaced-retrieval-practice/final_verdict.json +93 -0
- package/examples/spaced-retrieval-practice/frame.json +58 -0
- package/examples/spaced-retrieval-practice/gate_report.json +101 -0
- package/examples/spaced-retrieval-practice/methodology.json +78 -0
- package/examples/spaced-retrieval-practice/report_spec.json +212 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_academic.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_claude.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_datalab-dark.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_datalab.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/EduEvidence_Report_presentation.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/report_academic.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/report_claude.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/report_datalab-dark.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/report_datalab.html +2728 -0
- package/examples/spaced-retrieval-practice/reports-5themes/report_presentation.html +2728 -0
- package/examples/spaced-retrieval-practice/result.json +942 -0
- package/examples/spaced-retrieval-practice/result.zh.json +942 -0
- package/examples/spaced-retrieval-practice/skeptic.json +70 -0
- package/examples/spaced-retrieval-practice/sources.jsonl +7 -0
- package/examples/spaced-retrieval-practice/verdict.json +93 -0
- package/examples/workplace-ai-assistant/artifact_manifest.json +15 -0
- package/examples/workplace-ai-assistant/claims.jsonl +4 -0
- package/examples/workplace-ai-assistant/evaluation.json +19 -0
- package/examples/workplace-ai-assistant/evidence.jsonl +4 -0
- package/examples/workplace-ai-assistant/evidence_graph.json +444 -0
- package/examples/workplace-ai-assistant/final_verdict.json +78 -0
- package/examples/workplace-ai-assistant/frame.json +41 -0
- package/examples/workplace-ai-assistant/gate_report.json +101 -0
- package/examples/workplace-ai-assistant/intervention.json +27 -0
- package/examples/workplace-ai-assistant/legacy-link-check.json +16 -0
- package/examples/workplace-ai-assistant/methodology.json +60 -0
- package/examples/workplace-ai-assistant/report_spec.json +224 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_academic.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_claude.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_datalab-dark.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_datalab.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/EduEvidence_Report_presentation.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/report_academic.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/report_claude.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/report_datalab-dark.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/report_datalab.html +2814 -0
- package/examples/workplace-ai-assistant/reports-5themes/report_presentation.html +2814 -0
- package/examples/workplace-ai-assistant/result.json +615 -0
- package/examples/workplace-ai-assistant/result.zh.json +615 -0
- package/examples/workplace-ai-assistant/search_log.json +19 -0
- package/examples/workplace-ai-assistant/skeptic.json +72 -0
- package/examples/workplace-ai-assistant/sources.jsonl +3 -0
- package/examples/workplace-ai-assistant/validation_result.json +9 -0
- package/examples/workplace-ai-assistant/verdict.json +78 -0
- package/install.sh +7 -7
- package/integrations/agent_mcp.py +2 -2
- package/integrations/orchestration_dispatch.py +146 -0
- package/package.json +46 -3
- package/pyproject.toml +14 -22
- package/references/autoresearch.md +30 -0
- package/references/evaluation-policy.md +24 -0
- package/references/orchestration.md +22 -0
- package/references/report-copy-style.md +67 -0
- package/references/retrieval-compliance.md +75 -0
- package/references/retrieval-protocol.md +20 -0
- package/references/scientific-invariants.md +19 -0
- package/retrieval/audit.py +178 -0
- package/retrieval/fetch.py +96 -0
- package/retrieval/sciverse.py +398 -0
- package/retrieval/search.py +47 -7
- package/schemas/applicability.schema.json +94 -0
- package/schemas/chart-spec.schema.json +10 -3
- package/schemas/evidence.schema.json +316 -43
- package/schemas/fetch-result.schema.json +2 -1
- package/schemas/intervention.schema.json +106 -21
- package/schemas/report-result.schema.json +12 -4
- package/schemas/report-spec.schema.json +98 -100
- package/schemas/skeptic.schema.json +86 -0
- package/schemas/source.schema.json +21 -2
- package/schemas/v2/finding.schema.json +5 -1
- package/schemas/v2/methodology-audit.schema.json +5 -1
- package/schemas/v2/outcome.schema.json +28 -5
- package/schemas/v2/project.schema.json +2 -2
- package/schemas/v2/run.schema.json +1 -1
- package/schemas/v2/study.schema.json +5 -1
- package/schemas/vNext/autoevolve-session.schema.json +34 -0
- package/schemas/vNext/eval-snapshot.schema.json +77 -0
- package/schemas/vNext/execution-plan.schema.json +50 -0
- package/schemas/vNext/gap-priority.schema.json +54 -0
- package/schemas/vNext/negative-search-record.schema.json +68 -0
- package/schemas/vNext/research-iteration.schema.json +87 -0
- package/schemas/vNext/research-strategy.schema.json +62 -0
- package/schemas/vNext/skill-experiment.schema.json +90 -0
- package/schemas/vNext/task-spec.schema.json +156 -0
- package/schemas/vNext/worker-result.schema.json +60 -0
- package/schemas/verdict.schema.json +164 -28
- package/scripts/benchmark_judge.py +2 -2
- package/scripts/benchmark_v3.py +26 -43
- package/scripts/build_esl_artifacts.py +4 -4
- package/scripts/build_evidence_library.py +2 -2
- package/scripts/build_gh_pages.py +98 -0
- package/scripts/build_readme_diagrams.py +72 -0
- package/scripts/build_report_variants.py +101 -0
- package/scripts/build_result.py +74 -9
- package/scripts/check_autoresearch_invariants.py +95 -0
- package/scripts/check_package_parity.py +85 -0
- package/scripts/check_protocol_alignment.py +375 -0
- package/scripts/check_versioned_schemas.py +254 -0
- package/scripts/claim_audit.py +13 -8
- package/scripts/compute_confidence.py +10 -0
- package/scripts/daily_evolve.py +30 -0
- package/scripts/dashboard_server.py +130 -101
- package/scripts/did_regression.py +17 -32
- package/scripts/enrich_projects_human_and_lieflat.py +1 -1
- package/scripts/evidence_score.py +5 -2
- package/scripts/generate_metrics.py +4 -3
- package/scripts/generate_new_projects.py +5 -5
- package/scripts/orchestrator.py +286 -36
- package/scripts/pre_verdict_gate.py +224 -26
- package/scripts/quickstart.py +18 -2
- package/scripts/rebake_all_5themes.py +1 -2
- package/scripts/research_auto_cli.py +475 -0
- package/scripts/run_workspace.py +24 -8
- package/scripts/search_provenance.py +64 -0
- package/scripts/serve_web.py +9 -10
- package/scripts/skill_lint.py +1 -1
- package/scripts/skill_payload.py +81 -0
- package/scripts/test_adversarial_empirical.py +26 -19
- package/scripts/validate_schema.py +46 -2
- package/scripts/vnext_cli.py +133 -0
- package/setup.py +12 -0
- package/skill/agents/evaluation-designer.md +20 -4
- package/skill/agents/evidence-analyst.md +19 -3
- package/skill/agents/evidence-judge.md +50 -2
- package/skill/agents/evidence-retriever.md +20 -3
- package/skill/agents/intervention-designer.md +20 -4
- package/skill/agents/method-reviewer.md +18 -2
- package/skill/agents/{education-planner.md → research-planner.md} +19 -3
- package/skill/agents/skeptic.md +18 -2
- package/skill/roles/registry.yaml +45 -0
- package/skill/sub-skills/aihot-trend-analysis/SKILL.md +28 -9
- package/skill/sub-skills/contradiction-analysis/SKILL.md +31 -11
- package/skill/sub-skills/data-analysis/SKILL.md +34 -15
- package/skill/sub-skills/ethics-review/SKILL.md +33 -10
- package/skill/sub-skills/evidence-extraction/SKILL.md +29 -11
- package/skill/sub-skills/evidence-review/SKILL.md +31 -12
- package/skill/sub-skills/gap-analysis/SKILL.md +31 -9
- package/skill/sub-skills/literature-review/SKILL.md +35 -14
- package/skill/sub-skills/methodology-audit/SKILL.md +29 -12
- package/skill/sub-skills/report-generation/SKILL.md +40 -6
- package/skill/sub-skills/research-planning/SKILL.md +41 -14
- package/skill/sub-skills/study-design/SKILL.md +30 -9
- package/skill/task-briefs/adjudicate.md +32 -7
- package/skill/task-briefs/applicability.md +38 -0
- package/skill/task-briefs/audit.md +32 -7
- package/skill/task-briefs/challenge.md +34 -5
- package/skill/task-briefs/evaluate.md +30 -5
- package/skill/task-briefs/extract.md +31 -8
- package/skill/task-briefs/frame.md +39 -10
- package/skill/task-briefs/intervene.md +32 -6
- package/skill/task-briefs/present.md +32 -8
- package/skill/task-briefs/projection.md +37 -0
- package/skill/task-briefs/retrieve.md +36 -6
- package/skill/workflows/decision-and-pilot.md +85 -0
- package/skill/workflows/evaluate-and-update.md +93 -0
- package/skill/workflows/evidence-review.md +117 -0
- package/visualization/eduevidence-report/assets/base.css +2 -2
- package/visualization/eduevidence-report/assets/reader.css +752 -0
- package/visualization/eduevidence-report/assets/reader.js +132 -0
- package/visualization/eduevidence-report/references/chart-selection-catalog.md +109 -0
- package/visualization/eduevidence-report/references/lieflat-composition.md +3 -1
- package/visualization/eduevidence-report/scripts/build_figures.py +25 -3
- package/visualization/eduevidence-report/scripts/build_infographics.py +5 -1
- package/visualization/eduevidence-report/scripts/build_report.py +561 -121
- package/visualization/eduevidence-report/scripts/charts_data.py +2 -0
- package/visualization/eduevidence-report/scripts/lieflat_engine.py +371 -136
- package/visualization/eduevidence-report/scripts/zh_labels.py +80 -1
- package/visualization/eduevidence-report/themes/academic.css +1 -1
- package/visualization/eduevidence-report/themes/claude.css +1 -1
- package/visualization/eduevidence-report/themes/datalab-dark.css +2 -2
- package/visualization/eduevidence-report/themes/datalab.css +2 -2
- package/visualization/eduevidence-report/themes/presentation.css +2 -2
- package/web/README.md +18 -0
- package/web/architecture.html +14885 -0
- package/web/index.html +53 -0
- package/web/studio/THIRD_PARTY_LICENSES.txt +146 -0
- package/web/studio/assets/index-B8tkF44Q.css +1 -0
- package/web/studio/assets/index-CQ6Keoyc.js +230 -0
- package/web/studio/config.json +1 -0
- package/web/studio/index.html +14 -0
- package/engine/__pycache__/__init__.cpython-312.pyc +0 -0
- package/engine/__pycache__/analysis.cpython-312.pyc +0 -0
- package/engine/__pycache__/bias.cpython-312.pyc +0 -0
- package/engine/__pycache__/briefs.cpython-312.pyc +0 -0
- package/engine/__pycache__/capabilities.cpython-312.pyc +0 -0
- package/engine/__pycache__/citation_check.cpython-312.pyc +0 -0
- package/engine/__pycache__/contracts.cpython-312.pyc +0 -0
- package/engine/__pycache__/datasets.cpython-312.pyc +0 -0
- package/engine/__pycache__/events.cpython-312.pyc +0 -0
- package/engine/__pycache__/evidence_graph.cpython-312.pyc +0 -0
- package/engine/__pycache__/evidence_review.cpython-312.pyc +0 -0
- package/engine/__pycache__/evidencecore.cpython-312.pyc +0 -0
- package/engine/__pycache__/gap_lens.cpython-312.pyc +0 -0
- package/engine/__pycache__/gaps.cpython-312.pyc +0 -0
- package/engine/__pycache__/graph_store.cpython-312.pyc +0 -0
- package/engine/__pycache__/graph_validate.cpython-312.pyc +0 -0
- package/engine/__pycache__/ids.cpython-312.pyc +0 -0
- package/engine/__pycache__/library.cpython-312.pyc +0 -0
- package/engine/__pycache__/library_builtin.cpython-312.pyc +0 -0
- package/engine/__pycache__/living.cpython-312.pyc +0 -0
- package/engine/__pycache__/log.cpython-312.pyc +0 -0
- package/engine/__pycache__/meta_analysis.cpython-312.pyc +0 -0
- package/engine/__pycache__/meta_synthesis.cpython-312.pyc +0 -0
- package/engine/__pycache__/migration.cpython-312.pyc +0 -0
- package/engine/__pycache__/mode_router.cpython-312.pyc +0 -0
- package/engine/__pycache__/paths.cpython-312.pyc +0 -0
- package/engine/__pycache__/pilot.cpython-312.pyc +0 -0
- package/engine/__pycache__/planner.cpython-312.pyc +0 -0
- package/engine/__pycache__/project.cpython-312.pyc +0 -0
- package/engine/__pycache__/projections.cpython-312.pyc +0 -0
- package/engine/__pycache__/robustness.cpython-312.pyc +0 -0
- package/engine/__pycache__/run.cpython-312.pyc +0 -0
- package/engine/__pycache__/semantics.cpython-312.pyc +0 -0
- package/engine/__pycache__/study_design.cpython-312.pyc +0 -0
- package/engine/__pycache__/synthesis.cpython-312.pyc +0 -0
- package/engine/__pycache__/tribunal.cpython-312.pyc +0 -0
- package/engine/__pycache__/update.cpython-312.pyc +0 -0
- package/engine/__pycache__/versions.cpython-312.pyc +0 -0
- package/integrations/__pycache__/__init__.cpython-312.pyc +0 -0
- package/integrations/__pycache__/agent_mcp.cpython-312.pyc +0 -0
- package/integrations/__pycache__/smart_web_fetch.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/__init__.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/corpus_store.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/dedupe.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/failures.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/fetch.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/search.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/source.cpython-312.pyc +0 -0
- package/retrieval/__pycache__/validate.cpython-312.pyc +0 -0
- package/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark_evaluator.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark_judge.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark_routing.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark_v2.cpython-312.pyc +0 -0
- package/scripts/__pycache__/benchmark_v3.cpython-312.pyc +0 -0
- package/scripts/__pycache__/build_result.cpython-312.pyc +0 -0
- package/scripts/__pycache__/claim_audit.cpython-312.pyc +0 -0
- package/scripts/__pycache__/complexity_gate.cpython-312.pyc +0 -0
- package/scripts/__pycache__/compute_confidence.cpython-312.pyc +0 -0
- package/scripts/__pycache__/dashboard_server.cpython-312.pyc +0 -0
- package/scripts/__pycache__/did_regression.cpython-312.pyc +0 -0
- package/scripts/__pycache__/effect_calculator.cpython-312.pyc +0 -0
- package/scripts/__pycache__/evidence_matrix.cpython-312.pyc +0 -0
- package/scripts/__pycache__/evidence_score.cpython-312.pyc +0 -0
- package/scripts/__pycache__/evidence_semantics.cpython-312.pyc +0 -0
- package/scripts/__pycache__/fetch_benchmark.cpython-312.pyc +0 -0
- package/scripts/__pycache__/lint_report_layout.cpython-312.pyc +0 -0
- package/scripts/__pycache__/orchestrator.cpython-312.pyc +0 -0
- package/scripts/__pycache__/pre_verdict_gate.cpython-312.pyc +0 -0
- package/scripts/__pycache__/recompute_demo_quality.cpython-312.pyc +0 -0
- package/scripts/__pycache__/render_report.cpython-312.pyc +0 -0
- package/scripts/__pycache__/render_report_html.cpython-312.pyc +0 -0
- package/scripts/__pycache__/run_workspace.cpython-312.pyc +0 -0
- package/scripts/__pycache__/skill_lint.cpython-312.pyc +0 -0
- package/scripts/__pycache__/startup_probe.cpython-312.pyc +0 -0
- package/scripts/__pycache__/sync_killer_demo_report.cpython-312.pyc +0 -0
- package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.0.2.pyc +0 -0
- package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.1.1.pyc +0 -0
- package/scripts/__pycache__/validate_schema.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/adapter_contract.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/build_artifact_manifest.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/build_charts.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/build_figures.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/build_infographics.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/build_report.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/charts_data.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/lieflat_engine.cpython-312.pyc +0 -0
- package/visualization/eduevidence-report/scripts/__pycache__/zh_labels.cpython-312.pyc +0 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import shlex
|
|
7
|
+
import subprocess
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _read_json(path: str | Path | None, default=None):
|
|
15
|
+
if not path:
|
|
16
|
+
return default
|
|
17
|
+
return json.loads(Path(path).read_text(encoding="utf-8"))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _write_json(path: Path, value: dict[str, Any]) -> None:
|
|
21
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
tmp = path.with_name(path.name + ".tmp")
|
|
23
|
+
tmp.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
24
|
+
os.replace(tmp, path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _home(value):
|
|
28
|
+
if value:
|
|
29
|
+
return Path(value).expanduser().resolve()
|
|
30
|
+
from engine.paths import resolve_home
|
|
31
|
+
return resolve_home()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _project(home, project_id):
|
|
35
|
+
from engine.project import ProjectWorkspace
|
|
36
|
+
return ProjectWorkspace.open(home, project_id)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _load_gaps(workspace, explicit=None):
|
|
40
|
+
if explicit:
|
|
41
|
+
path = Path(explicit)
|
|
42
|
+
rows = [
|
|
43
|
+
json.loads(line)
|
|
44
|
+
for line in path.read_text(encoding="utf-8").splitlines()
|
|
45
|
+
if line.strip()
|
|
46
|
+
]
|
|
47
|
+
else:
|
|
48
|
+
gap_dir = workspace.path / "gaps"
|
|
49
|
+
files = sorted(gap_dir.glob("gaps-rev-*.jsonl")) if gap_dir.exists() else []
|
|
50
|
+
if not files:
|
|
51
|
+
raise ValueError("no persisted KnowledgeGap file; derive gaps first or pass --gaps")
|
|
52
|
+
rows = [
|
|
53
|
+
json.loads(line)
|
|
54
|
+
for line in files[-1].read_text(encoding="utf-8").splitlines()
|
|
55
|
+
if line.strip()
|
|
56
|
+
]
|
|
57
|
+
return rows
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _apply_gap_state(gaps: list[dict[str, Any]], state: dict[str, str]) -> None:
|
|
61
|
+
for gap in gaps:
|
|
62
|
+
gap_id = str(gap.get("gap_id", ""))
|
|
63
|
+
if gap_id in state:
|
|
64
|
+
gap["status"] = state[gap_id]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _save_gap_state(path: Path, gaps: list[dict[str, Any]]) -> None:
|
|
68
|
+
_write_json(
|
|
69
|
+
path,
|
|
70
|
+
{
|
|
71
|
+
str(gap.get("gap_id")): str(gap.get("status", "open"))
|
|
72
|
+
for gap in gaps
|
|
73
|
+
if gap.get("gap_id")
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _require_fresh_research_state(
|
|
79
|
+
*,
|
|
80
|
+
active_revision: int,
|
|
81
|
+
gaps: list[dict[str, Any]],
|
|
82
|
+
decision: dict[str, Any],
|
|
83
|
+
previous_state: dict[str, Any],
|
|
84
|
+
) -> None:
|
|
85
|
+
if previous_state.get("next_action") == "re_adjudicate":
|
|
86
|
+
if int(decision.get("graph_revision", -1)) != active_revision:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"re-adjudication barrier: provide a DecisionSnapshot bound to the current GraphRevision"
|
|
89
|
+
)
|
|
90
|
+
for gap in gaps:
|
|
91
|
+
derived = gap.get("derived_from_graph_revision")
|
|
92
|
+
if derived is not None and int(derived) != active_revision:
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"stale KnowledgeGap {gap.get('gap_id')}: derived from revision {derived}, "
|
|
95
|
+
f"active revision is {active_revision}; re-derive gaps before continuing"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _iteration_id(iteration_number: int) -> str:
|
|
100
|
+
return f"RIT-{iteration_number:04d}"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _request(priority, strategy, project_id, revision, iteration_number, gap):
|
|
104
|
+
iteration_id = _iteration_id(iteration_number)
|
|
105
|
+
return {
|
|
106
|
+
"project_id": project_id,
|
|
107
|
+
"iteration_id": iteration_id,
|
|
108
|
+
"iteration_number": iteration_number,
|
|
109
|
+
"base_graph_revision": revision,
|
|
110
|
+
"gap_id": priority.gap_id,
|
|
111
|
+
"gap": gap,
|
|
112
|
+
"gap_priority": priority.as_dict(),
|
|
113
|
+
"strategy": {
|
|
114
|
+
"strategy_id": strategy.strategy_id,
|
|
115
|
+
"experiment_type": strategy.experiment_type.value,
|
|
116
|
+
"hypothesis": strategy.hypothesis,
|
|
117
|
+
"expected_gain": strategy.expected_gain,
|
|
118
|
+
"budget": strategy.budget.__dict__,
|
|
119
|
+
},
|
|
120
|
+
"contract": {
|
|
121
|
+
"canonical_state_write": "FORBIDDEN",
|
|
122
|
+
"output": "validated staging result JSON only",
|
|
123
|
+
"required_measurements": ["query_count", "candidate_count", "fetched_count"],
|
|
124
|
+
"negative_search_research_iteration_id": iteration_id,
|
|
125
|
+
"negative_search_gap_id": priority.gap_id,
|
|
126
|
+
"search_snippets_are_evidence": False,
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _validate_outcome_budget(outcome: dict[str, Any], strategy) -> None:
|
|
132
|
+
if not isinstance(outcome, dict):
|
|
133
|
+
raise ValueError("executor output must be a JSON object")
|
|
134
|
+
budget = strategy.budget
|
|
135
|
+
checks = (
|
|
136
|
+
("query_count", budget.max_queries),
|
|
137
|
+
("candidate_count", budget.max_candidates),
|
|
138
|
+
("fetched_count", budget.max_fulltext_fetches),
|
|
139
|
+
)
|
|
140
|
+
measured: dict[str, int] = {}
|
|
141
|
+
for key, limit in checks:
|
|
142
|
+
if key not in outcome:
|
|
143
|
+
raise ValueError(f"executor output must report {key} to enforce bounded research")
|
|
144
|
+
try:
|
|
145
|
+
value = int(outcome[key])
|
|
146
|
+
except (TypeError, ValueError) as exc:
|
|
147
|
+
raise ValueError(f"executor measurement {key} must be an integer") from exc
|
|
148
|
+
if value < 0:
|
|
149
|
+
raise ValueError(f"executor measurement {key} cannot be negative")
|
|
150
|
+
if value > limit:
|
|
151
|
+
raise ValueError(f"research budget exceeded: {key}={value} > {limit}")
|
|
152
|
+
measured[key] = value
|
|
153
|
+
if measured["fetched_count"] > measured["candidate_count"]:
|
|
154
|
+
raise ValueError("fetched_count cannot exceed candidate_count")
|
|
155
|
+
candidates = outcome.get("candidate_sources")
|
|
156
|
+
if isinstance(candidates, list) and len(candidates) > measured["candidate_count"]:
|
|
157
|
+
raise ValueError("candidate_sources length exceeds reported candidate_count")
|
|
158
|
+
attempts = outcome.get("search_attempts")
|
|
159
|
+
if isinstance(attempts, list) and len(attempts) > measured["query_count"]:
|
|
160
|
+
raise ValueError("search_attempts length exceeds reported query_count")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _validate_outcome_identity(
|
|
164
|
+
outcome: dict[str, Any], *, expected_iteration_id: str, expected_gap_id: str
|
|
165
|
+
) -> None:
|
|
166
|
+
supplied_iteration = outcome.get("iteration_id")
|
|
167
|
+
supplied_gap = outcome.get("gap_id")
|
|
168
|
+
if supplied_iteration is not None and supplied_iteration != expected_iteration_id:
|
|
169
|
+
raise ValueError("executor iteration_id does not match ResearchRequest")
|
|
170
|
+
if supplied_gap is not None and supplied_gap != expected_gap_id:
|
|
171
|
+
raise ValueError("executor gap_id does not match ResearchRequest")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _external_executor(command, request, request_path, *, timeout_seconds: int):
|
|
175
|
+
_write_json(request_path, request)
|
|
176
|
+
argv = shlex.split(command)
|
|
177
|
+
if not argv:
|
|
178
|
+
raise ValueError("empty executor command")
|
|
179
|
+
env = os.environ.copy()
|
|
180
|
+
env["EDUEVIDENCE_RESEARCH_REQUEST"] = str(request_path)
|
|
181
|
+
completed = subprocess.run(
|
|
182
|
+
argv,
|
|
183
|
+
check=True,
|
|
184
|
+
text=True,
|
|
185
|
+
capture_output=True,
|
|
186
|
+
env=env,
|
|
187
|
+
timeout=timeout_seconds,
|
|
188
|
+
)
|
|
189
|
+
value = json.loads(completed.stdout)
|
|
190
|
+
if not isinstance(value, dict):
|
|
191
|
+
raise ValueError("executor stdout must be one JSON object")
|
|
192
|
+
return value
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _commit_payload(workspace, run_id, payload, *, expected_base_revision: int):
|
|
196
|
+
from engine.autoresearch import commit_staging_bundle
|
|
197
|
+
from engine.graph_store import GraphStore
|
|
198
|
+
|
|
199
|
+
return commit_staging_bundle(
|
|
200
|
+
GraphStore.create(workspace),
|
|
201
|
+
run_id=run_id,
|
|
202
|
+
expected_base_revision=expected_base_revision,
|
|
203
|
+
payload=payload,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@contextmanager
|
|
208
|
+
def _writer_lock(path: Path):
|
|
209
|
+
"""One research-auto writer per Project; stale locks fail closed."""
|
|
210
|
+
payload = {
|
|
211
|
+
"pid": os.getpid(),
|
|
212
|
+
"acquired_at": datetime.now(timezone.utc).isoformat(),
|
|
213
|
+
}
|
|
214
|
+
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
|
|
215
|
+
try:
|
|
216
|
+
fd = os.open(path, flags, 0o600)
|
|
217
|
+
except FileExistsError as exc:
|
|
218
|
+
detail = ""
|
|
219
|
+
try:
|
|
220
|
+
detail = path.read_text(encoding="utf-8").strip()
|
|
221
|
+
except OSError:
|
|
222
|
+
pass
|
|
223
|
+
raise RuntimeError(
|
|
224
|
+
f"research auto writer already active or a stale lock exists at {path}. "
|
|
225
|
+
"Do not delete it while another run is active; after confirming no writer is running, "
|
|
226
|
+
f"remove it manually. lock={detail or 'unreadable'}"
|
|
227
|
+
) from exc
|
|
228
|
+
try:
|
|
229
|
+
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
230
|
+
json.dump(payload, handle)
|
|
231
|
+
handle.write("\n")
|
|
232
|
+
yield
|
|
233
|
+
finally:
|
|
234
|
+
try:
|
|
235
|
+
path.unlink()
|
|
236
|
+
except FileNotFoundError:
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _mutating_research_auto(args, workspace, root, state_path, gap_state_path, stop_path):
|
|
241
|
+
from engine.autoresearch import EvidenceAutoresearchController, NegativeSearchRecord, ResearchMemory
|
|
242
|
+
from engine.graph_store import GraphStore
|
|
243
|
+
|
|
244
|
+
memory = ResearchMemory(root)
|
|
245
|
+
gaps = _load_gaps(workspace, args.gaps)
|
|
246
|
+
_apply_gap_state(gaps, _read_json(gap_state_path, {}) or {})
|
|
247
|
+
decision = _read_json(args.decision, {}) or {}
|
|
248
|
+
controller = EvidenceAutoresearchController(max_iterations=args.max_iterations)
|
|
249
|
+
store = GraphStore.create(workspace)
|
|
250
|
+
previous_state = _read_json(state_path, {}) or {}
|
|
251
|
+
_require_fresh_research_state(
|
|
252
|
+
active_revision=store.active_revision(),
|
|
253
|
+
gaps=gaps,
|
|
254
|
+
decision=decision,
|
|
255
|
+
previous_state=previous_state,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def process(outcome, history, priority, strategy):
|
|
259
|
+
base_revision = GraphStore.create(workspace).active_revision()
|
|
260
|
+
expected_iteration_id = _iteration_id(len(history) + 1)
|
|
261
|
+
_validate_outcome_identity(
|
|
262
|
+
outcome,
|
|
263
|
+
expected_iteration_id=expected_iteration_id,
|
|
264
|
+
expected_gap_id=priority.gap_id,
|
|
265
|
+
)
|
|
266
|
+
_validate_outcome_budget(outcome, strategy)
|
|
267
|
+
evidence_ids = outcome.get("validated_evidence_ids") or [
|
|
268
|
+
item.get("finding_id")
|
|
269
|
+
for item in outcome.get("findings", [])
|
|
270
|
+
if isinstance(item, dict) and item.get("finding_id")
|
|
271
|
+
]
|
|
272
|
+
outcome["validated_evidence_ids"] = [item for item in evidence_ids if item]
|
|
273
|
+
|
|
274
|
+
def executor(actual_strategy, gap):
|
|
275
|
+
if actual_strategy.strategy_id != strategy.strategy_id:
|
|
276
|
+
raise RuntimeError("controller strategy changed between request and execution")
|
|
277
|
+
return outcome
|
|
278
|
+
|
|
279
|
+
def commit(_ids):
|
|
280
|
+
return _commit_payload(
|
|
281
|
+
workspace,
|
|
282
|
+
f"autoresearch-{len(history) + 1:04d}",
|
|
283
|
+
outcome,
|
|
284
|
+
expected_base_revision=base_revision,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
result = controller.step(
|
|
288
|
+
project_id=args.project,
|
|
289
|
+
base_graph_revision=base_revision,
|
|
290
|
+
gaps=gaps,
|
|
291
|
+
decision=decision,
|
|
292
|
+
history=history,
|
|
293
|
+
executor=executor,
|
|
294
|
+
graph_commit=commit if outcome["validated_evidence_ids"] else None,
|
|
295
|
+
ethics_feasible=args.ethics_feasible,
|
|
296
|
+
)
|
|
297
|
+
for raw in outcome.get("negative_searches", []):
|
|
298
|
+
record = NegativeSearchRecord(**raw)
|
|
299
|
+
if record.research_iteration_id != result.iteration.iteration_id:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"NegativeSearchRecord research_iteration_id does not match current iteration"
|
|
302
|
+
)
|
|
303
|
+
if record.gap_id != result.iteration.gap_id:
|
|
304
|
+
raise ValueError("NegativeSearchRecord gap_id does not match current gap")
|
|
305
|
+
memory.append_negative_search(record)
|
|
306
|
+
if record.negative_search_id not in result.iteration.negative_search_ids:
|
|
307
|
+
result.iteration.negative_search_ids.append(record.negative_search_id)
|
|
308
|
+
memory.append_iteration(result.iteration)
|
|
309
|
+
return result
|
|
310
|
+
|
|
311
|
+
def persist_result(result, completed=None):
|
|
312
|
+
selected = next(g for g in gaps if g.get("gap_id") == result.iteration.gap_id)
|
|
313
|
+
if result.next_action == "stop_search_saturated":
|
|
314
|
+
selected["status"] = "search_saturated"
|
|
315
|
+
elif result.next_action == "empirical_evidence_needed":
|
|
316
|
+
selected["status"] = "empirical_needed"
|
|
317
|
+
state = {
|
|
318
|
+
"status": result.iteration.status.value if result.iteration.status else "unknown",
|
|
319
|
+
"next_action": result.next_action,
|
|
320
|
+
"gap_priority": result.priority.as_dict(),
|
|
321
|
+
"iteration": result.iteration.as_dict(),
|
|
322
|
+
"rationale": list(result.rationale),
|
|
323
|
+
}
|
|
324
|
+
if completed is not None:
|
|
325
|
+
state["completed_iterations"] = list(completed)
|
|
326
|
+
_save_gap_state(gap_state_path, gaps)
|
|
327
|
+
_write_json(state_path, state)
|
|
328
|
+
return state
|
|
329
|
+
|
|
330
|
+
if args.action == "step":
|
|
331
|
+
history = memory.load_iterations()
|
|
332
|
+
priority = controller.select_gap(gaps, decision)
|
|
333
|
+
gap = next(g for g in gaps if g.get("gap_id") == priority.gap_id)
|
|
334
|
+
strategy = controller.build_strategy(priority, gap, history)
|
|
335
|
+
outcome = _read_json(args.outcome_file) if args.outcome_file else None
|
|
336
|
+
if outcome is None:
|
|
337
|
+
pending = {
|
|
338
|
+
"status": "awaiting_execution",
|
|
339
|
+
**_request(
|
|
340
|
+
priority,
|
|
341
|
+
strategy,
|
|
342
|
+
args.project,
|
|
343
|
+
GraphStore.create(workspace).active_revision(),
|
|
344
|
+
len(history) + 1,
|
|
345
|
+
gap,
|
|
346
|
+
),
|
|
347
|
+
}
|
|
348
|
+
_write_json(state_path, pending)
|
|
349
|
+
print(json.dumps(pending, ensure_ascii=False, indent=2))
|
|
350
|
+
return 0
|
|
351
|
+
result = process(outcome, history, priority, strategy)
|
|
352
|
+
state = persist_result(result)
|
|
353
|
+
print(json.dumps(state, ensure_ascii=False, indent=2))
|
|
354
|
+
return 0
|
|
355
|
+
|
|
356
|
+
if args.outcome_file and args.max_iterations != 1:
|
|
357
|
+
raise ValueError(
|
|
358
|
+
"--outcome-file with start requires --max-iterations 1; use --executor-command for a loop"
|
|
359
|
+
)
|
|
360
|
+
if not args.executor_command and not args.outcome_file:
|
|
361
|
+
raise ValueError(
|
|
362
|
+
"start requires --executor-command or one --outcome-file with --max-iterations 1"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
completed = []
|
|
366
|
+
for _ in range(args.max_iterations):
|
|
367
|
+
if stop_path.exists():
|
|
368
|
+
stop_path.unlink(missing_ok=True)
|
|
369
|
+
latest = _read_json(state_path, {}) or {}
|
|
370
|
+
latest["status"] = "stopped_by_user"
|
|
371
|
+
latest["completed_iterations"] = list(completed)
|
|
372
|
+
_write_json(state_path, latest)
|
|
373
|
+
break
|
|
374
|
+
history = memory.load_iterations()
|
|
375
|
+
priority = controller.select_gap(gaps, decision)
|
|
376
|
+
gap = next(g for g in gaps if g.get("gap_id") == priority.gap_id)
|
|
377
|
+
strategy = controller.build_strategy(priority, gap, history)
|
|
378
|
+
request = _request(
|
|
379
|
+
priority,
|
|
380
|
+
strategy,
|
|
381
|
+
args.project,
|
|
382
|
+
GraphStore.create(workspace).active_revision(),
|
|
383
|
+
len(history) + 1,
|
|
384
|
+
gap,
|
|
385
|
+
)
|
|
386
|
+
outcome = (
|
|
387
|
+
_read_json(args.outcome_file)
|
|
388
|
+
if args.outcome_file
|
|
389
|
+
else _external_executor(
|
|
390
|
+
args.executor_command,
|
|
391
|
+
request,
|
|
392
|
+
root / f"request-{len(history) + 1:04d}.json",
|
|
393
|
+
timeout_seconds=args.executor_timeout_seconds,
|
|
394
|
+
)
|
|
395
|
+
)
|
|
396
|
+
result = process(outcome, history, priority, strategy)
|
|
397
|
+
completed.append(result.iteration.iteration_id)
|
|
398
|
+
persist_result(result, completed)
|
|
399
|
+
|
|
400
|
+
if result.next_action in {
|
|
401
|
+
"re_adjudicate",
|
|
402
|
+
"empirical_evidence_needed",
|
|
403
|
+
"stop_search_saturated",
|
|
404
|
+
}:
|
|
405
|
+
break
|
|
406
|
+
if all(str(row.get("status", "")).lower() == "resolved" for row in gaps):
|
|
407
|
+
break
|
|
408
|
+
|
|
409
|
+
final = _read_json(state_path, {"status": "completed_no_iterations"})
|
|
410
|
+
print(json.dumps(final, ensure_ascii=False, indent=2))
|
|
411
|
+
return 0
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def research_auto(argv):
|
|
415
|
+
parser = argparse.ArgumentParser(prog="eduevidence research auto")
|
|
416
|
+
sub = parser.add_subparsers(dest="action", required=True)
|
|
417
|
+
for name in ("step", "start"):
|
|
418
|
+
cmd = sub.add_parser(name)
|
|
419
|
+
cmd.add_argument("--project", required=True)
|
|
420
|
+
cmd.add_argument("--home")
|
|
421
|
+
cmd.add_argument("--gaps")
|
|
422
|
+
cmd.add_argument("--decision")
|
|
423
|
+
cmd.add_argument("--outcome-file")
|
|
424
|
+
cmd.add_argument("--max-iterations", type=int, default=5)
|
|
425
|
+
cmd.add_argument("--ethics-feasible", action="store_true")
|
|
426
|
+
cmd.add_argument("--executor-timeout-seconds", type=int, default=1800)
|
|
427
|
+
if name == "start":
|
|
428
|
+
cmd.add_argument("--executor-command")
|
|
429
|
+
for name in ("status", "report", "stop"):
|
|
430
|
+
cmd = sub.add_parser(name)
|
|
431
|
+
cmd.add_argument("--project", required=True)
|
|
432
|
+
cmd.add_argument("--home")
|
|
433
|
+
args = parser.parse_args(argv)
|
|
434
|
+
if getattr(args, "executor_timeout_seconds", 1) <= 0:
|
|
435
|
+
raise ValueError("executor timeout must be positive")
|
|
436
|
+
|
|
437
|
+
workspace = _project(_home(args.home), args.project)
|
|
438
|
+
root = workspace.path / "autoresearch"
|
|
439
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
440
|
+
state_path = root / "state.json"
|
|
441
|
+
gap_state_path = root / "gap-state.json"
|
|
442
|
+
stop_path = root / "stop.requested"
|
|
443
|
+
lock_path = root / ".writer.lock"
|
|
444
|
+
|
|
445
|
+
if args.action in {"status", "report"}:
|
|
446
|
+
state = _read_json(state_path, {"status": "not_started"})
|
|
447
|
+
if isinstance(state, dict):
|
|
448
|
+
state = dict(state)
|
|
449
|
+
state["stop_requested"] = stop_path.exists()
|
|
450
|
+
state["writer_active"] = lock_path.exists()
|
|
451
|
+
print(json.dumps(state, ensure_ascii=False, indent=2))
|
|
452
|
+
return 0
|
|
453
|
+
if args.action == "stop":
|
|
454
|
+
stop_path.write_text(
|
|
455
|
+
json.dumps(
|
|
456
|
+
{
|
|
457
|
+
"requested_at": datetime.now(timezone.utc).isoformat(),
|
|
458
|
+
"requested_by_pid": os.getpid(),
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
+ "\n",
|
|
462
|
+
encoding="utf-8",
|
|
463
|
+
)
|
|
464
|
+
print("stop requested")
|
|
465
|
+
return 0
|
|
466
|
+
|
|
467
|
+
with _writer_lock(lock_path):
|
|
468
|
+
return _mutating_research_auto(
|
|
469
|
+
args,
|
|
470
|
+
workspace,
|
|
471
|
+
root,
|
|
472
|
+
state_path,
|
|
473
|
+
gap_state_path,
|
|
474
|
+
stop_path,
|
|
475
|
+
)
|
package/scripts/run_workspace.py
CHANGED
|
@@ -46,14 +46,16 @@ from datetime import datetime, timezone
|
|
|
46
46
|
from pathlib import Path
|
|
47
47
|
from typing import Any
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
from engine.versions import CONFIDENCE_POLICY_VERSION, ENGINE_VERSION
|
|
50
|
+
from engine.workflows import execution_stages
|
|
51
|
+
|
|
52
|
+
# Kept as an import-compatible name for existing integrations. Engine version
|
|
53
|
+
# is the sole source of truth for new manifests.
|
|
54
|
+
SKILL_VERSION = ENGINE_VERSION
|
|
50
55
|
RESOURCE_POLICY_VERSION = "2026-08-12.v1"
|
|
51
56
|
|
|
52
57
|
#: Ordered EvidenceFlow stages the orchestrator routes through.
|
|
53
|
-
STAGES =
|
|
54
|
-
"frame", "retrieve", "extract", "challenge", "audit",
|
|
55
|
-
"adjudicate", "intervene", "evaluate", "present",
|
|
56
|
-
]
|
|
58
|
+
STAGES = list(execution_stages())
|
|
57
59
|
|
|
58
60
|
#: Every artifact a run workspace owns (dirs end with '/').
|
|
59
61
|
WORKSPACE_FILES: list[str] = [
|
|
@@ -61,7 +63,7 @@ WORKSPACE_FILES: list[str] = [
|
|
|
61
63
|
"capability_plan.json", "resource_plan.json", "execution_plan.json",
|
|
62
64
|
"model_inventory.json", "agent_mcp_approval.json",
|
|
63
65
|
"frame.json", "sources.jsonl", "fetch/", "evidence.jsonl", "skeptic.json",
|
|
64
|
-
"methodology.json", "raw_verdict.json", "final_verdict.json",
|
|
66
|
+
"methodology.json", "raw_verdict.json", "final_verdict.json", "applicability.json",
|
|
65
67
|
"intervention.json", "evaluation.json", "result.json", "result.zh.json",
|
|
66
68
|
"report_spec.json", "report.html", "trace.jsonl",
|
|
67
69
|
]
|
|
@@ -77,6 +79,7 @@ EMPTY_SEED: dict[str, str] = {
|
|
|
77
79
|
"evidence.jsonl": "",
|
|
78
80
|
"skeptic.json": "{}",
|
|
79
81
|
"methodology.json": "{}",
|
|
82
|
+
"applicability.json": "{}",
|
|
80
83
|
"raw_verdict.json": "{}",
|
|
81
84
|
"final_verdict.json": "{}",
|
|
82
85
|
"intervention.json": "{}",
|
|
@@ -176,20 +179,26 @@ def build_manifest(
|
|
|
176
179
|
scp_available: bool | None = None,
|
|
177
180
|
root: Path | None = None,
|
|
178
181
|
started_at: str | None = None,
|
|
182
|
+
domain: str = "education",
|
|
179
183
|
) -> dict[str, Any]:
|
|
180
|
-
"""Phase 13 run manifest with every contract field.
|
|
184
|
+
"""Phase 13 run manifest with every contract field.
|
|
185
|
+
|
|
186
|
+
``domain`` selects which registered frame contract this run must satisfy;
|
|
187
|
+
it defaults to education so existing callers and manifests stay valid.
|
|
188
|
+
"""
|
|
181
189
|
return {
|
|
182
190
|
"run_id": run_id,
|
|
183
191
|
"skill_version": SKILL_VERSION,
|
|
184
192
|
"git_commit": git_commit(root),
|
|
185
193
|
"started_at": started_at or utc_now(),
|
|
186
194
|
"question": question,
|
|
195
|
+
"domain": domain,
|
|
187
196
|
"execution_mode": execution_mode,
|
|
188
197
|
"scp_available": detect_scp() if scp_available is None else scp_available,
|
|
189
198
|
"agent_mcp_available": agent_mcp_available,
|
|
190
199
|
"agent_mcp_approved": agent_mcp_approved,
|
|
191
200
|
"resource_policy_version": RESOURCE_POLICY_VERSION,
|
|
192
|
-
"confidence_policy_version":
|
|
201
|
+
"confidence_policy_version": CONFIDENCE_POLICY_VERSION,
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
|
|
@@ -249,7 +258,14 @@ class RunWorkspace:
|
|
|
249
258
|
|
|
250
259
|
def load_state(self) -> dict[str, Any]:
|
|
251
260
|
state = load_json(self.state_path)
|
|
261
|
+
# Read old workspaces without treating a presentation record as a
|
|
262
|
+
# scientific stage. New writes only use the canonical projection name.
|
|
263
|
+
legacy = (state.get("stages") or {}).pop("present", None)
|
|
264
|
+
if legacy and "projection" not in state.get("stages", {}):
|
|
265
|
+
state.setdefault("stages", {})["projection"] = legacy
|
|
252
266
|
state.setdefault("stages", {s: {"status": "pending"} for s in STAGES})
|
|
267
|
+
for stage in STAGES:
|
|
268
|
+
state["stages"].setdefault(stage, {"status": "pending"})
|
|
253
269
|
return state
|
|
254
270
|
|
|
255
271
|
def save_state(self, updates: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Execute an audited, bounded search plan and persist provenance.
|
|
3
|
+
|
|
4
|
+
For every explicit query (core / expansion / counter_evidence) this records
|
|
5
|
+
each provider attempt, then exports search-provenance.json,
|
|
6
|
+
search-attempts.jsonl, source-screening.csv and exclusion-log.csv into the
|
|
7
|
+
output directory. A search result without this provenance file is a candidate,
|
|
8
|
+
never evidence.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
18
|
+
for _p in (str(ROOT), str(ROOT / "scripts")):
|
|
19
|
+
if _p not in sys.path:
|
|
20
|
+
sys.path.insert(0, _p)
|
|
21
|
+
|
|
22
|
+
from retrieval.audit import AuditedSearchExecutor, SearchPlan # noqa: E402
|
|
23
|
+
from retrieval.search import search_router # noqa: E402
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
CHANNELS = {
|
|
27
|
+
"academic": lambda router: router.zero_config_academic,
|
|
28
|
+
"web": lambda router: router.zero_config_web + router.configured_providers,
|
|
29
|
+
"all": lambda router: (router.zero_config_academic + router.zero_config_web
|
|
30
|
+
+ [p for p in router.configured_providers if p.is_available()]),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main(argv: list[str] | None = None) -> int:
|
|
35
|
+
parser = argparse.ArgumentParser(description="audited external search with provenance")
|
|
36
|
+
parser.add_argument("question", help="research question")
|
|
37
|
+
parser.add_argument("--out", required=True, type=Path, help="provenance output directory")
|
|
38
|
+
parser.add_argument("--domain", default="education", choices=["education", "policy"])
|
|
39
|
+
parser.add_argument("--concept", action="append", default=[], help="concept term (repeatable)")
|
|
40
|
+
parser.add_argument("--synonym", action="append", default=[], help="synonym term (repeatable)")
|
|
41
|
+
parser.add_argument("--limit", type=int, default=10)
|
|
42
|
+
parser.add_argument("--channel", default="all", choices=list(CHANNELS))
|
|
43
|
+
parser.add_argument("--policy", default="2026.09", help="search policy version")
|
|
44
|
+
args = parser.parse_args(argv)
|
|
45
|
+
plan = SearchPlan.from_question(args.question, domain=args.domain,
|
|
46
|
+
concepts=args.concept, synonyms=args.synonym)
|
|
47
|
+
plan = SearchPlan(plan.question, plan.domain, plan.concepts, plan.synonyms,
|
|
48
|
+
plan.inclusion_criteria, plan.exclusion_criteria, plan.queries,
|
|
49
|
+
provider_budget=args.limit, policy_version=args.policy)
|
|
50
|
+
providers = CHANNELS[args.channel](search_router)
|
|
51
|
+
hits = AuditedSearchExecutor(providers).execute(plan, args.out, limit=args.limit)
|
|
52
|
+
print(json.dumps({
|
|
53
|
+
"output": str(args.out),
|
|
54
|
+
"sources": len(hits),
|
|
55
|
+
"query_count": len(plan.queries),
|
|
56
|
+
"counter_evidence_queries": sum(q.purpose == "counter_evidence" for q in plan.queries),
|
|
57
|
+
"provenance_files": ["search-provenance.json", "search-attempts.jsonl",
|
|
58
|
+
"source-screening.csv", "exclusion-log.csv"],
|
|
59
|
+
}, ensure_ascii=False, indent=2))
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
sys.exit(main())
|
package/scripts/serve_web.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""scripts/serve_web.py — EduEvidence
|
|
2
|
+
"""scripts/serve_web.py — EduEvidence 介绍页/静态资源预览服务器(CORS 开启)。
|
|
3
3
|
|
|
4
|
-
web/
|
|
5
|
-
dashboard_server
|
|
6
|
-
|
|
7
|
-
使落地页与不同端口的 Web Studio 控制台之间能做跨源探测与互相跳转。
|
|
4
|
+
`web/landing.html` 是独立的公开介绍页;正式研究控制台由
|
|
5
|
+
`scripts/dashboard_server.py` 提供 `web/studio/` 中的 Research Studio。
|
|
6
|
+
本脚本只用于只读预览介绍页和静态展示资源,不承载研究状态或控制台 API。
|
|
8
7
|
|
|
9
8
|
python3 scripts/serve_web.py --host 127.0.0.1 --port 8877
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
介绍页:http://127.0.0.1:8877/landing.html
|
|
11
|
+
Research Studio:http://127.0.0.1:8765/studio/
|
|
13
12
|
"""
|
|
14
13
|
from __future__ import annotations
|
|
15
14
|
|
|
@@ -40,9 +39,9 @@ def main() -> None:
|
|
|
40
39
|
server = http.server.ThreadingHTTPServer(
|
|
41
40
|
(args.host, args.port), CORSStaticHandler)
|
|
42
41
|
print("=" * 60)
|
|
43
|
-
print(f"🌐 EduEvidence
|
|
44
|
-
print(
|
|
45
|
-
print(f"
|
|
42
|
+
print(f"🌐 EduEvidence 介绍页预览 running at http://{args.host}:{args.port}/")
|
|
43
|
+
print(" 介绍页 /landing.html")
|
|
44
|
+
print(f" Research Studio http://{args.host}:8765/studio/ (scripts/dashboard_server.py)")
|
|
46
45
|
print("=" * 60)
|
|
47
46
|
try:
|
|
48
47
|
server.serve_forever()
|
package/scripts/skill_lint.py
CHANGED
|
@@ -102,7 +102,7 @@ def lint_skill() -> list[str]:
|
|
|
102
102
|
# 7. task-briefs 模板(编排链补全)
|
|
103
103
|
brief_dir = ROOT / "skill" / "task-briefs"
|
|
104
104
|
stage_briefs = ["frame", "retrieve", "extract", "challenge", "audit",
|
|
105
|
-
"adjudicate", "intervene", "evaluate", "
|
|
105
|
+
"adjudicate", "applicability", "intervene", "evaluate", "projection"]
|
|
106
106
|
if not brief_dir.exists():
|
|
107
107
|
errors.append("skill/task-briefs/ is missing (orchestration chain)")
|
|
108
108
|
else:
|