@citeark/agent 0.3.20
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/LICENSE +202 -0
- package/README.md +128 -0
- package/data/dataset-source-registry.v1.json +300 -0
- package/dist/arkgraph/boot.js +6 -0
- package/dist/arkgraph/index.html +1 -0
- package/dist/arkgraph/viewer.css +1 -0
- package/dist/arkgraph/viewer.en.css +1 -0
- package/dist/arkgraph/viewer.en.js +49 -0
- package/dist/arkgraph/viewer.en.js.LEGAL.txt +56 -0
- package/dist/arkgraph/viewer.js +49 -0
- package/dist/arkgraph/viewer.js.LEGAL.txt +56 -0
- package/docker/claude-code/Dockerfile +97 -0
- package/docker/claude-code/codex-pro-relay.mjs +466 -0
- package/docker/claude-code/runtime-contract-check.mjs +79 -0
- package/docs/arkgraph-reading.md +79 -0
- package/docs/configuration.md +100 -0
- package/docs/integration.md +92 -0
- package/docs/maturity-plan.md +27 -0
- package/docs/npm-release.md +44 -0
- package/docs/paper-reading.md +40 -0
- package/docs/research-plan-granularity.md +27 -0
- package/docs/terminal.md +49 -0
- package/examples/toy-evaluation/compile-task.json +27 -0
- package/examples/toy-evaluation/paper.md +5 -0
- package/examples/toy-evaluation/repository/README.md +9 -0
- package/examples/toy-evaluation/repository/checkpoint.json +4 -0
- package/examples/toy-evaluation/repository/evaluate.py +17 -0
- package/examples/toy-evaluation/task.json +81 -0
- package/package.json +59 -0
- package/prompts/compile-research.md +58 -0
- package/prompts/execute-contract.md +72 -0
- package/prompts/execute-workspace-simple.md +51 -0
- package/prompts/execute-workspace.md +34 -0
- package/prompts/prepare-reproduction.md +82 -0
- package/prompts/repair-research.md +45 -0
- package/protocol/CAP.md +129 -0
- package/protocol/LICENSE +12 -0
- package/protocol/MAPPINGS.md +72 -0
- package/protocol/README.md +38 -0
- package/protocol/conformance-v2.0-alpha.1.json +36 -0
- package/protocol/examples/arkgraph/checkpoint-evaluation.json +309 -0
- package/protocol/examples/arkgraph/fixtures.mjs +49 -0
- package/protocol/examples/arkgraph/paper-free.json +291 -0
- package/protocol/examples/arkgraph/partial-failure.json +344 -0
- package/protocol/examples/arkgraph/training-evaluation.json +443 -0
- package/protocol/profiles/agent-trace.md +16 -0
- package/protocol/profiles/computational-run.md +16 -0
- package/protocol/profiles/core.md +15 -0
- package/protocol/profiles/public-bundle.md +18 -0
- package/protocol/profiles/reproduction.md +29 -0
- package/protocol/profiles/research-compilation.md +44 -0
- package/protocol/profiles/research-plan.md +39 -0
- package/protocol/profiles/restricted-evidence.md +15 -0
- package/runtime/bootstrap-autodl-runtime.sh +314 -0
- package/runtime/create-runtime-venv.sh +41 -0
- package/runtime/install-local-cpu-runtime.sh +23 -0
- package/runtime/install-scientific-runtime.sh +153 -0
- package/runtime/mineru/parse.py +62 -0
- package/runtime/mineru/requirements.txt +4 -0
- package/runtime/requirements-baseline.txt +38 -0
- package/schemas/cap/v2/activity.schema.json +47 -0
- package/schemas/cap/v2/agent.schema.json +32 -0
- package/schemas/cap/v2/assertion.schema.json +110 -0
- package/schemas/cap/v2/descriptor.schema.json +243 -0
- package/schemas/cap/v2/entity.schema.json +64 -0
- package/schemas/cap/v2/manifest.schema.json +67 -0
- package/schemas/cap/v2/relation.schema.json +82 -0
- package/schemas/compute-catalog.schema.json +63 -0
- package/schemas/compute-decision.schema.json +27 -0
- package/schemas/execution-contract.schema.json +1024 -0
- package/schemas/research-card.schema.json +30 -0
- package/schemas/research-inventory-draft.schema.json +366 -0
- package/schemas/research.schema.json +1044 -0
- package/schemas/result.schema.json +173 -0
- package/schemas/verification-policy.schema.json +47 -0
- package/schemas/verified-conclusion.schema.json +58 -0
- package/schemas/workspace-summary.schema.json +24 -0
- package/scripts/build-arkgraph-view.mjs +12 -0
- package/scripts/check-execution-feasibility.mjs +24 -0
- package/scripts/check-syntax.mjs +15 -0
- package/scripts/deterministic-asset-preparation.py +438 -0
- package/scripts/package-cap.mjs +23 -0
- package/scripts/package-local-agent.mjs +23 -0
- package/scripts/preview-arkgraph.mjs +25 -0
- package/scripts/replay-research-compiler-candidate.mjs +134 -0
- package/scripts/review-compiler-sources.mjs +44 -0
- package/scripts/run-asset-preparation.sh +17 -0
- package/scripts/run-research-plan.mjs +98 -0
- package/scripts/validate-asset-preparation.py +290 -0
- package/scripts/verify-local-runtime.mjs +57 -0
- package/scripts/verify-npm-package.mjs +57 -0
- package/src/adapters/paper2agent.mjs +107 -0
- package/src/assets/cache.mjs +159 -0
- package/src/assets/compute.mjs +98 -0
- package/src/assets/executor.mjs +145 -0
- package/src/assets/lifecycle.mjs +213 -0
- package/src/assets/manifest.mjs +242 -0
- package/src/assets/opportunistic-preparation.mjs +81 -0
- package/src/assets/plan.mjs +411 -0
- package/src/assets/prompts.mjs +29 -0
- package/src/assets/public-asset-probe.mjs +525 -0
- package/src/assets/qualification.mjs +119 -0
- package/src/assets/readiness.mjs +130 -0
- package/src/assets/reproduction-admission.mjs +355 -0
- package/src/assets/requirements.mjs +152 -0
- package/src/assets/source-grounding.mjs +341 -0
- package/src/assets/source-policy.mjs +118 -0
- package/src/autodl/client.mjs +260 -0
- package/src/autodl/ssh.mjs +380 -0
- package/src/autodl/tools.mjs +129 -0
- package/src/cap/redaction.mjs +38 -0
- package/src/cap/v2/archive.mjs +152 -0
- package/src/cap/v2/attestation.mjs +204 -0
- package/src/cap/v2/canonical-json.mjs +114 -0
- package/src/cap/v2/compilation-artifact.mjs +240 -0
- package/src/cap/v2/core.mjs +282 -0
- package/src/cap/v2/measurement-assessment-records.mjs +23 -0
- package/src/cap/v2/pipeline-artifact.mjs +922 -0
- package/src/cap/v2/read.mjs +41 -0
- package/src/cap/v2/reassessment-artifact.mjs +383 -0
- package/src/cap/v2/research-artifact.mjs +231 -0
- package/src/cap/v2/research-map-records.mjs +46 -0
- package/src/cap/v2/research-object-records.mjs +163 -0
- package/src/cap/v2/research-records.mjs +187 -0
- package/src/cap/v2/verify.mjs +642 -0
- package/src/cli.mjs +1146 -0
- package/src/compute/autodl-pro-compiler.mjs +347 -0
- package/src/compute/autodl-pro-executor.mjs +459 -0
- package/src/compute/autodl-pro-job.mjs +843 -0
- package/src/compute/autodl-pro-network.mjs +295 -0
- package/src/compute/autodl-pro-remote.mjs +810 -0
- package/src/compute/autodl-pro-staging.mjs +117 -0
- package/src/compute/campaign.mjs +110 -0
- package/src/compute/catalog.mjs +123 -0
- package/src/compute/checkpoint-protocol.mjs +154 -0
- package/src/compute/codex-account-lock.mjs +111 -0
- package/src/compute/codex-account-session.mjs +107 -0
- package/src/compute/compiler-profile.mjs +38 -0
- package/src/compute/compiler-router.mjs +23 -0
- package/src/compute/coordinator-recovery.mjs +210 -0
- package/src/compute/executor-router.mjs +29 -0
- package/src/compute/gcp-batch-compiler.mjs +685 -0
- package/src/compute/gcp-batch-executor.mjs +1215 -0
- package/src/compute/gcp-batch-failure.mjs +92 -0
- package/src/compute/gcp-batch-job.mjs +527 -0
- package/src/compute/gcp-batch-lifecycle.mjs +81 -0
- package/src/compute/gcp-checkpoint-worker.mjs +1633 -0
- package/src/compute/local-codex-compiler.mjs +52 -0
- package/src/compute/measurement-hardware.mjs +128 -0
- package/src/compute/remote-attempt.mjs +226 -0
- package/src/compute/requirements.mjs +124 -0
- package/src/compute/research-phases.mjs +48 -0
- package/src/compute/scheduler.mjs +452 -0
- package/src/compute/shared-workloads.mjs +26 -0
- package/src/compute/stage-archive.mjs +79 -0
- package/src/contracts/campaign-contract.mjs +52 -0
- package/src/contracts/execution-contract.mjs +819 -0
- package/src/contracts/execution-mode.mjs +19 -0
- package/src/contracts/execution-timeouts.mjs +45 -0
- package/src/contracts/execution-workload.mjs +68 -0
- package/src/contracts/preflight-schema.mjs +25 -0
- package/src/contracts/public-contract.mjs +63 -0
- package/src/contracts/subject-tags.mjs +31 -0
- package/src/dashboard/data.mjs +898 -0
- package/src/dashboard/server.mjs +79 -0
- package/src/dashboard/static/dashboard.css +366 -0
- package/src/dashboard/static/dashboard.js +560 -0
- package/src/dashboard/static/index.html +85 -0
- package/src/deployment/community-policy.mjs +9 -0
- package/src/deployment/environment.mjs +112 -0
- package/src/deployment/guided.mjs +98 -0
- package/src/deployment/handoff.mjs +102 -0
- package/src/deployment/local-contract.mjs +31 -0
- package/src/deployment/local.mjs +100 -0
- package/src/deployment/prepare.mjs +46 -0
- package/src/deployment/recipe.mjs +108 -0
- package/src/deployment/supplement.mjs +51 -0
- package/src/deployment/terminal.mjs +43 -0
- package/src/diagnosis/renderer.mjs +75 -0
- package/src/diagnosis/target-failure.mjs +46 -0
- package/src/evidence/parser-registry.mjs +54 -0
- package/src/evidence/parsers/fasttext-classification.mjs +82 -0
- package/src/evidence/parsers/json-scalar.mjs +96 -0
- package/src/evidence/parsers/simcse-senteval.mjs +104 -0
- package/src/evidence/parsers/starspace-classification.mjs +78 -0
- package/src/evidence/registry.mjs +147 -0
- package/src/execution/runner-audit.mjs +473 -0
- package/src/gcp/auth.mjs +106 -0
- package/src/gcp/batch-client.mjs +120 -0
- package/src/gcp/resource-discovery.mjs +177 -0
- package/src/gcp/rest.mjs +82 -0
- package/src/gcp/secret-manager.mjs +34 -0
- package/src/gcp/signed-url.mjs +133 -0
- package/src/gcp/storage.mjs +220 -0
- package/src/graph/command.mjs +41 -0
- package/src/graph/execution.mjs +97 -0
- package/src/graph/model.mjs +37 -0
- package/src/graph/presentation.mjs +110 -0
- package/src/graph/query.mjs +159 -0
- package/src/graph/research-relations.mjs +69 -0
- package/src/graph/source-page.mjs +12 -0
- package/src/graph/source-preview.mjs +34 -0
- package/src/graph/validate.mjs +76 -0
- package/src/job.mjs +496 -0
- package/src/network/autodl-routing-proxy.mjs +462 -0
- package/src/network/egress-proxy.mjs +158 -0
- package/src/observability/event-contract.mjs +230 -0
- package/src/observability/pipeline-monitor.mjs +166 -0
- package/src/pipeline/orchestrator.mjs +1281 -0
- package/src/pipeline/recovery-error.mjs +11 -0
- package/src/pipeline/replay.mjs +304 -0
- package/src/pipeline/shared-execution.mjs +115 -0
- package/src/pipeline/stage-checkpoint.mjs +86 -0
- package/src/pipeline/stage-recovery.mjs +101 -0
- package/src/pipeline/targets.mjs +110 -0
- package/src/process.mjs +143 -0
- package/src/protocol.mjs +312 -0
- package/src/provider/codex-account.mjs +44 -0
- package/src/provider/codex-completion.mjs +49 -0
- package/src/provider/completion.mjs +292 -0
- package/src/provider/model-client.mjs +44 -0
- package/src/provider/model-route.mjs +29 -0
- package/src/provider/openrouter-readiness.mjs +189 -0
- package/src/provider/reader-bridge.mjs +35 -0
- package/src/provider/relay.mjs +263 -0
- package/src/provider/runtime-auth.mjs +40 -0
- package/src/public/cap.d.mts +90 -0
- package/src/public/cap.mjs +12 -0
- package/src/public/contracts.d.mts +2 -0
- package/src/public/host.mjs +171 -0
- package/src/public/operations.d.mts +11 -0
- package/src/public/presentation.d.mts +4 -0
- package/src/records/views.mjs +26 -0
- package/src/remote/command.mjs +178 -0
- package/src/remote/ssh.mjs +59 -0
- package/src/repository-origin.mjs +81 -0
- package/src/reproduction/evidence-feedback.mjs +96 -0
- package/src/reproduction/incomplete-initialization.mjs +25 -0
- package/src/reproduction/lifecycle.mjs +253 -0
- package/src/reproduction/plan.mjs +132 -0
- package/src/reproduction/prompts.mjs +70 -0
- package/src/reproduction/runner.mjs +188 -0
- package/src/reproduction/summary.mjs +130 -0
- package/src/reproduction/workspace-mode.mjs +7 -0
- package/src/research/automatic-admission.mjs +156 -0
- package/src/research/compiler-coverage.mjs +85 -0
- package/src/research/compiler-failure.mjs +24 -0
- package/src/research/compiler-normalization-guards.mjs +112 -0
- package/src/research/compiler-repair.mjs +3 -0
- package/src/research/compiler.mjs +853 -0
- package/src/research/continuation-selection.mjs +26 -0
- package/src/research/execution-graph-context.mjs +43 -0
- package/src/research/experiment-importance.mjs +15 -0
- package/src/research/inventory-handoff.mjs +104 -0
- package/src/research/inventory-revisions.mjs +32 -0
- package/src/research/mineru-local.mjs +73 -0
- package/src/research/paper-command.mjs +19 -0
- package/src/research/paper-markdown.mjs +180 -0
- package/src/research/paper-source-map.mjs +69 -0
- package/src/research/planning-policy.mjs +88 -0
- package/src/research/reference-materials.mjs +11 -0
- package/src/research/reproduction-scope.mjs +30 -0
- package/src/research/research-map.mjs +94 -0
- package/src/research/research-objects.mjs +88 -0
- package/src/research/source-discovery.mjs +646 -0
- package/src/research/source-observations.mjs +75 -0
- package/src/research/source-review-cli-mcp.mjs +26 -0
- package/src/research/source-review-input.mjs +209 -0
- package/src/research/source-review-local-codex.mjs +36 -0
- package/src/research/source-review-model.mjs +70 -0
- package/src/research/source-review.mjs +173 -0
- package/src/research/structure.mjs +3163 -0
- package/src/research-card/renderer.mjs +277 -0
- package/src/research-card/verified-conclusion.mjs +143 -0
- package/src/results/output-registry.mjs +183 -0
- package/src/runtime/claude-code.mjs +52 -0
- package/src/runtime/codex-capacity-retry.mjs +87 -0
- package/src/runtime/codex.mjs +64 -0
- package/src/runtime/config.mjs +157 -0
- package/src/runtime/final-output.mjs +40 -0
- package/src/runtime/index.mjs +21 -0
- package/src/runtime/local-codex.mjs +74 -0
- package/src/runtime/opencode.mjs +95 -0
- package/src/runtime/prompt.mjs +13 -0
- package/src/sandbox/docker.mjs +363 -0
- package/src/settings/command.mjs +297 -0
- package/src/settings/store.mjs +119 -0
- package/src/telemetry/pricing.mjs +68 -0
- package/src/telemetry/usage.mjs +265 -0
- package/src/terminal/events.mjs +97 -0
- package/src/terminal/input.mjs +40 -0
- package/src/terminal/plain.mjs +40 -0
- package/src/terminal/remote-stream.mjs +22 -0
- package/src/terminal/screen.mjs +214 -0
- package/src/terminal/transcript.mjs +69 -0
- package/src/util.mjs +107 -0
- package/src/verification/ai-assessor.mjs +534 -0
- package/src/verification/claim-evaluator.mjs +242 -0
- package/src/verification/evidence-context.mjs +165 -0
- package/src/verification/evidence-reader.mjs +95 -0
- package/src/verification/integrity.mjs +570 -0
- package/src/verification/tolerance.mjs +32 -0
- package/src/workloads/cpu-research-preparation.mjs +56 -0
- package/src/workloads/definition.mjs +74 -0
- package/src/workloads/phase-aware-reproduction.mjs +46 -0
- package/src/workloads/reproduction.mjs +85 -0
- package/src/workspace/command.mjs +242 -0
- package/src/workspace/control.mjs +49 -0
- package/src/workspace/entry.mjs +28 -0
- package/src/workspace/input.mjs +93 -0
- package/src/workspace/interactive.mjs +94 -0
- package/src/workspace/jobs.mjs +418 -0
- package/src/workspace/session.mjs +97 -0
- package/src/workspace/worker.mjs +137 -0
- package/ui/arkgraph/ambient-motion.mjs +10 -0
- package/ui/arkgraph/app.jsx +153 -0
- package/ui/arkgraph/boot.js +6 -0
- package/ui/arkgraph/camera-motion.mjs +20 -0
- package/ui/arkgraph/context-reveal.mjs +39 -0
- package/ui/arkgraph/details.css +3 -0
- package/ui/arkgraph/entry.jsx +28 -0
- package/ui/arkgraph/experiment-curves.mjs +17 -0
- package/ui/arkgraph/experiment-selection.mjs +15 -0
- package/ui/arkgraph/experiment-style.css +26 -0
- package/ui/arkgraph/experiment-ui.jsx +32 -0
- package/ui/arkgraph/frame.html +1 -0
- package/ui/arkgraph/graph-gestures.mjs +62 -0
- package/ui/arkgraph/label-layout.mjs +57 -0
- package/ui/arkgraph/locales/en.json +229 -0
- package/ui/arkgraph/locales/source-types.json +15 -0
- package/ui/arkgraph/localization-build.mjs +27 -0
- package/ui/arkgraph/material-build.mjs +23 -0
- package/ui/arkgraph/material-colors.mjs +39 -0
- package/ui/arkgraph/material-style.css +15 -0
- package/ui/arkgraph/open-graph.jsx +326 -0
- package/ui/arkgraph/outline.jsx +49 -0
- package/ui/arkgraph/package-lock.json +888 -0
- package/ui/arkgraph/package.json +17 -0
- package/ui/arkgraph/reading-layout.mjs +130 -0
- package/ui/arkgraph/reading-presentation.mjs +73 -0
- package/ui/arkgraph/record-detail.css +51 -0
- package/ui/arkgraph/record-details.jsx +29 -0
- package/ui/arkgraph/research-types.mjs +31 -0
- package/ui/arkgraph/selection-mark.jsx +6 -0
- package/ui/arkgraph/soft-spine.mjs +26 -0
- package/ui/arkgraph/steering-style.css +187 -0
- package/ui/arkgraph/style.css +272 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { REPRODUCTION_SCOPES } from "./reproduction-scope.mjs";
|
|
2
|
+
|
|
3
|
+
export const SCIENTIFIC_OPERATION_SCOPES = {
|
|
4
|
+
evaluate_existing_state: "low",
|
|
5
|
+
regenerate_selected_state: "medium",
|
|
6
|
+
rebuild_research_workflow: "high",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const LEGACY_SCIENTIFIC_OPERATION_SCOPES = {
|
|
10
|
+
observe_existing: "low",
|
|
11
|
+
generate_inputs: "medium",
|
|
12
|
+
rebuild_workflow: "high",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function scientificOperationScope(kind) {
|
|
16
|
+
return SCIENTIFIC_OPERATION_SCOPES[kind]
|
|
17
|
+
?? LEGACY_SCIENTIFIC_OPERATION_SCOPES[kind]
|
|
18
|
+
?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// One task definition for the compiler and the independent plan reviewer.
|
|
22
|
+
// The three user-facing values are recommendation presets, while operation
|
|
23
|
+
// scope remains descriptive metadata for each experiment.
|
|
24
|
+
export const SCIENTIFIC_PLANNING_POLICY = {
|
|
25
|
+
version: "scientific-planning-v8",
|
|
26
|
+
resourceDiscovery: "Read the task's timestamped resource discovery before planning. Distinguish provider inventory, quota and permissions, supported runtime adapters, exact prices and unknown live capacity. Derive CPU/RAM/VRAM minima from scientific workload and cited or explicitly assumed evidence, never by copying the selected machine's size. Explain compatible alternatives without weakening source-bound measurement hardware. Only charge preparation dependencies needed by each experiment; unknown or failed discovery is not proof of resource absence.",
|
|
27
|
+
objective: "Produce a source-grounded catalog of independently selectable experiment packages for the paper's reported questions. Planning is not execution or scientific support.",
|
|
28
|
+
compilationBoundary: {
|
|
29
|
+
responsibility: "Define the reported empirical questions, necessary comparisons and conditions, allowed verification routes, and evidence needed by execution and independent assessment. Preserve the complete empirical result inventory, including negative results and local gaps. Organize independently meaningful assertions separately from source observation collections, methods, conditions and proposed hypotheses; never create a claim per cell or point.",
|
|
30
|
+
context: "Theory, proofs, architecture descriptions and implementation exposition explain the empirical questions; they do not each require a separate claim or experiment. Include a distinct testable result when it is needed to interpret or verify the paper's empirical conclusions. Numerical examples do not prove a theorem.",
|
|
31
|
+
handoff: "Execution resolves commands, loaders, asset locations, environment details and operational probes. Assessment judges comparability and scientific support from actual evidence. Compilation does not certify either runtime success or the paper's truth.",
|
|
32
|
+
review: "Block changes to scientific meaning, material empirical omissions, invalid comparisons or verification routes, and fabricated evidence. Do not reject the whole catalog because one package is unavailable; preserve its requirements and surface the reason to the user. Keep editorial details, redundant coverage, optional theoretical extensions and resolvable operational uncertainty as non-blocking advice.",
|
|
33
|
+
},
|
|
34
|
+
scopes: {
|
|
35
|
+
low: "Descriptive operation class: evaluate existing scientific state and produce fresh measurements without regenerating upstream scientific state.",
|
|
36
|
+
medium: "Descriptive operation class: regenerate selected scientific state for one or a few key experiments with matched controls, required seeds and analysis.",
|
|
37
|
+
high: "Descriptive operation class: rebuild a major research workflow from preparation and scientific-state generation through experiments, controls and analysis.",
|
|
38
|
+
},
|
|
39
|
+
stateBoundary: {
|
|
40
|
+
scientificState: "Research outputs whose generation is itself part of the scientific question, such as trained weights, collected datasets, simulation trajectories, generated samples, attack traces, or state produced by an experimental system.",
|
|
41
|
+
operationalScaffolding: "Means needed to evaluate the question without recreating its upstream science, such as dependencies, compiled binaries, deterministic fixed-workload construction, temporary runtime tensors, caches, evaluators, analysis code, and logs.",
|
|
42
|
+
},
|
|
43
|
+
decisions: {
|
|
44
|
+
planned: "An allowed, source-grounded verification route exists. Unverified operational details can be resolved by bounded execution probes.",
|
|
45
|
+
blocked: "A specific necessary scientific input, decision rule, access or resource is demonstrably unavailable. Name the missing requirement and affected measurements; uncertainty alone does not establish impossibility.",
|
|
46
|
+
notSelected: "The experiment remains in the catalog but is not in the current default checkbox recommendation. This is not a blocker or scientific verdict.",
|
|
47
|
+
},
|
|
48
|
+
operationScopes: SCIENTIFIC_OPERATION_SCOPES,
|
|
49
|
+
planGranularity: {
|
|
50
|
+
steps: "Describe method stages, not Cartesian-product execution instances. Keep checkpoints, datasets, seeds, layers and metrics as complete parameter axes inside a stage and its expected collection; split only for materially different operations, scientific state, decision rules or reusable consumer scope. Preserve all explicit source object and measurement identities. No graph-size cap or scope reduction is allowed.",
|
|
51
|
+
products: "Prospective objects specify coherent result tables, matrices, curve families or trained-state families with their complete dimensions and row/member identity rules. Do not create a future object per loop coordinate or scalar. Source identities remain separate and actual produced states retain their own identities during execution. Declare direct inputs and derivation, not repeated transitive ancestry.",
|
|
52
|
+
observationTargets: "Retain qualitative methods as explicit source observation comparisons with claimId, observationId, comparison and an unresolved decision_rule limitation. Never fabricate scalar IDs or parsers for collections. Observation-only proposals remain in the catalog but are not automatically executable or verifiable. Mixed proposals preserve valid numeric bindings separately.",
|
|
53
|
+
accounting: "A compute workload can be an exact recipe/seed/parameter batch. Reuse identical batches across consumers. Partition partially shared batches into exact shared subsets and disjoint remainders only where independently selectable consumers require it. Accounting partitions do not force separate steps or output objects. Count every member in duration arithmetic; different complete batch definitions cannot share an ID.",
|
|
54
|
+
},
|
|
55
|
+
sharedExecution: "All selected experiments share one machine sized to the maximum resource requirements and one Agent workspace. Declare compute.workloads for each experiment, separating reusable scientific-state generation and setup from incremental measurements. Compare the whole catalog before submitting: shared prerequisites must have identical IDs and definitions across consumers, never be buried in differently named experiment-sized bundles. Preserve independent seeds and different recipes within explicitly scoped batch identities; do not enumerate one work item per seed by default. Execution chooses order, batching and safe parallelism; workload items are not separate jobs or a forced serial schedule. Every selectable experiment and workload item must have a finite positive time estimate for its eligible execution path. Explicit conservative runtime assumptions are acceptable and must report their basis; unknown or null quotes are not acceptable, and hard limits remain separate.",
|
|
56
|
+
principles: [
|
|
57
|
+
"Preserve all reported empirical questions, their conditions, controls and source locations, including findings without a reliable numeric reference. Do not replace an unresolved source value with an invented target.",
|
|
58
|
+
"For each question identify the necessary inputs, verification operation, observable, aggregation and decision rule. A relation can be encoded numerically only when that computation and comparison are defined.",
|
|
59
|
+
"Classify the necessary operations, not the table, topic, runtime cost, accelerator count or artifact's production history. Ask whether matching existing scientific state would let an independent observation or analysis answer the question.",
|
|
60
|
+
"Fresh measurements do not make a task medium scope. Measuring released software on a fixed workload, including compilation and deterministic construction of temporary runtime inputs, is low scope when it does not regenerate upstream scientific state.",
|
|
61
|
+
"If evaluating matching pretrained artifacts would answer a result question, missing those artifacts is an input gap, not a higher-scope question merely because training could replace them. Distinguish an unverified locator from demonstrated unavailability; preserve an allowed discovery probe when plausible.",
|
|
62
|
+
"Apply planned or blocked decisions to the exact measurements concerned. Preserve feasible work and localize uncertainty; one missing input does not block unrelated measurements.",
|
|
63
|
+
"Treat default selection, operation complexity, monetary budget, available resources and fidelity as separate dimensions. Preserve source ambiguity explicitly and distinguish a qualified verification question from an unsupported claim of exact reproduction.",
|
|
64
|
+
"Use low, medium and high only to recommend initial checkboxes. Never remove a valid experiment, fabricate a blocker, or reject a package because its operation scope is above the requested preset.",
|
|
65
|
+
"Execution owns operational refinement; source identity, the scientific question and its evidence requirements remain fixed. A bounded probe is a way to investigate uncertainty, not proof that a workload is feasible.",
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export function scientificPlanningPolicy(requestedScope, stage = "inventory") {
|
|
70
|
+
if (requestedScope !== undefined && !REPRODUCTION_SCOPES.includes(requestedScope)) {
|
|
71
|
+
throw new Error("Invalid task-owned reproduction scope");
|
|
72
|
+
}
|
|
73
|
+
const policy = stage === "execution_plan" ? SCIENTIFIC_PLANNING_POLICY : {
|
|
74
|
+
version: "research-inventory-v2",
|
|
75
|
+
objective: "Record the paper faithfully for the reproduction Agent. Recorded inventory is neither an executable plan nor scientific support.",
|
|
76
|
+
compilationBoundary: {
|
|
77
|
+
responsibility: "Preserve empirical results including negative findings, reported numbers, comparison objects, necessary source conditions, locations, relationships and ambiguities. Optional verification hints are provisional. Preserve scientifically meaningful propositions as claims, coherent matrices/tables/curve families as observation collections with observationId-linked measurements, and proposed mechanisms as hypotheses. Completeness does not require one claim per value; there is no claim-count cap.",
|
|
78
|
+
context: "Theory and implementation exposition supply context; do not expand each passage into a separate verification obligation.",
|
|
79
|
+
handoff: "Reproduction reads the inventory and original materials, investigates inputs, defines comparisons and decision rules, estimates each package, and recommends initial selections. Execution refines interpretations with source-grounded reasons and retained history. Assessment judges actual evidence. Original source statements and reported values remain preserved.",
|
|
80
|
+
review: "Check material empirical omissions, incorrect numbers or comparison objects, lost necessary reported conditions and inferences presented as source facts. Do not demand executable routes, asset availability checks, decision rules, scope decisions or cost estimates from the inventory. Preserve ambiguity rather than rejecting already disclosed uncertainty.",
|
|
81
|
+
},
|
|
82
|
+
scopes: SCIENTIFIC_PLANNING_POLICY.scopes,
|
|
83
|
+
principles: ["Keep the complete empirical result inventory, including qualitative and negative findings without numeric targets.", "Distinguish reported facts, unknown source conditions and investigator interpretations. Never invent reference values.", "Source ambiguity and unresolved verification methods are handoff context, not proof of impossibility."],
|
|
84
|
+
};
|
|
85
|
+
return { ...structuredClone(policy), requestedPreset: requestedScope ?? null,
|
|
86
|
+
requestedScope: requestedScope ?? null,
|
|
87
|
+
legacyScope: requestedScope === undefined ? "Preserve the existing task's scientific objectives; do not invent a scope restriction." : null };
|
|
88
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { writeJson } from '../util.mjs';
|
|
4
|
+
import { validateCodeFiles } from '../deployment/handoff.mjs';
|
|
5
|
+
|
|
6
|
+
export async function stageReferenceMaterials(filename, directory) {
|
|
7
|
+
if (!filename) return;
|
|
8
|
+
const materials = JSON.parse(await readFile(filename, 'utf8'));
|
|
9
|
+
validateCodeFiles(materials.files);
|
|
10
|
+
await writeJson(path.join(directory, 'reference-materials.json'), materials);
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Task-owned scope. Old artifacts without this field retain their original contract. */
|
|
2
|
+
export const REPRODUCTION_SCOPES = ["low", "medium", "high"];
|
|
3
|
+
export const REPRODUCTION_ROUTES = ["directional", "official-checkpoint", "full-training", "artifact-evaluation", "experiment-rerun", "workflow-rebuild"];
|
|
4
|
+
export const isReproductionScope = (value) => REPRODUCTION_SCOPES.includes(value);
|
|
5
|
+
export const exceedsReproductionScope = (required, requested) =>
|
|
6
|
+
isReproductionScope(required) && isReproductionScope(requested)
|
|
7
|
+
&& REPRODUCTION_SCOPES.indexOf(required) > REPRODUCTION_SCOPES.indexOf(requested);
|
|
8
|
+
|
|
9
|
+
export function scopeExclusionFor(claim, measurementId, requested) {
|
|
10
|
+
void claim;
|
|
11
|
+
void measurementId;
|
|
12
|
+
void requested;
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function reproductionScopeIssues(research, requested = research?.reproductionScope) {
|
|
17
|
+
if (requested === undefined) return [];
|
|
18
|
+
if (!isReproductionScope(requested)) return ["Invalid requested reproduction scope"];
|
|
19
|
+
const issues = [];
|
|
20
|
+
if (research.reproductionScope !== requested) issues.push("Research Plan must preserve the task-owned reproductionScope");
|
|
21
|
+
for (const experiment of Array.isArray(research.experiments) ? research.experiments : []) {
|
|
22
|
+
if (!experiment || typeof experiment !== "object") continue;
|
|
23
|
+
if (experiment.reproductionScope !== requested) issues.push(`Experiment ${experiment.id} must retain the requested scope identity`);
|
|
24
|
+
if (experiment.requiredReproductionScope !== undefined
|
|
25
|
+
&& !isReproductionScope(experiment.requiredReproductionScope)) {
|
|
26
|
+
issues.push(`Experiment ${experiment.id} has an invalid requiredReproductionScope annotation`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return issues;
|
|
30
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { CAP_PREDICATES } from '../graph/model.mjs';
|
|
2
|
+
|
|
3
|
+
// Editorial references are typed local identities, never a match on a display title.
|
|
4
|
+
export const CONTEXT_ROLES = Object.freeze({
|
|
5
|
+
question: ['entity', 'question'], concept: ['entity', 'concept'],
|
|
6
|
+
premise: ['assertion', 'hypothesis'], argument: ['entity', 'argument'],
|
|
7
|
+
'research-plan': ['entity', 'plan'], 'research-activity': ['activity', 'activity'],
|
|
8
|
+
'source-assessment': ['assertion', 'assessment'],
|
|
9
|
+
person: ['agent', 'agent'], organization: ['agent', 'agent'], instrument: ['entity', 'resource'],
|
|
10
|
+
});
|
|
11
|
+
export function inventoryReferences(research) {
|
|
12
|
+
return new Map([
|
|
13
|
+
...(research.sources ?? []).map(n => [`source:${n.id}`, { value: n, primitive: 'entity' }]),
|
|
14
|
+
...(research.researchObjects ?? []).map(n => [`object:${n.id}`, { value: n, primitive: CONTEXT_ROLES[n.role]?.[0] ?? 'entity' }]),
|
|
15
|
+
...(research.claims ?? []).map(n => [`claim:${n.id}`, { value: n, primitive: 'assertion' }]),
|
|
16
|
+
...(research.hypotheses ?? []).map(n => [`hypothesis:${n.id}`, { value: n, primitive: 'assertion' }]),
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
19
|
+
export function validateResearchMap(research, issues) {
|
|
20
|
+
const refs = inventoryReferences(research), sources = new Set((research.sources ?? []).map(n => n.id));
|
|
21
|
+
const nonempty = value => typeof value === 'string' && value.trim().length > 0;
|
|
22
|
+
const reference = (value, label) => { if (!refs.has(value)) issues.push(`${label}: unknown research reference ${String(value)}`); };
|
|
23
|
+
const list = (value, label) => { if (!Array.isArray(value)) { issues.push(`${label} must be an array`); return []; } return value; };
|
|
24
|
+
const locators = (value, label) => {
|
|
25
|
+
const entries = list(value, label);
|
|
26
|
+
if (!entries.length) issues.push(`${label} requires a source locator`);
|
|
27
|
+
for (const entry of entries) if (!sources.has(entry?.sourceId) || !nonempty(entry?.locator)) issues.push(`${label} requires a fixed source and location`);
|
|
28
|
+
};
|
|
29
|
+
for (const object of research.researchObjects ?? []) if (CONTEXT_ROLES[object.role]) {
|
|
30
|
+
locators(object.sourceLocators, `object ${object.id}.sourceLocators`);
|
|
31
|
+
if (['research-activity', 'person', 'organization'].includes(object.role) && object.prospective === true)
|
|
32
|
+
issues.push(`object ${object.id}: prospective work must use research-plan, not an actual activity or participant`);
|
|
33
|
+
}
|
|
34
|
+
const entityInputs=(ids,label)=>{for(const id of Array.isArray(ids)?ids:[])if(refs.has(`object:${id}`)&&refs.get(`object:${id}`).primitive!=='entity')issues.push(`${label}: ${id} must be an Entity; use an explicit researchRelations predicate for assertions, activities or participants`);};
|
|
35
|
+
for(const item of [...(research.claims??[]),...(research.hypotheses??[])]){
|
|
36
|
+
entityInputs(item.objectIds,`${item.id}.objectIds`);
|
|
37
|
+
for(const row of item.reportedMeasurements??[])entityInputs(row.objectIds,`${row.id}.objectIds`);
|
|
38
|
+
}
|
|
39
|
+
for(const object of research.researchObjects??[]){entityInputs(object.derivedFromIds,`${object.id}.derivedFromIds`);if(object.derivedFromIds?.length&&refs.get(`object:${object.id}`)?.primitive!=='entity')issues.push(`${object.id}: derivedFromIds requires an Entity subject`);}
|
|
40
|
+
for(const experiment of research.experiments??[]){entityInputs(experiment.inputIds,`${experiment.id}.inputIds`);for(const step of experiment.steps??[]){entityInputs(step.inputIds,`${step.id}.inputIds`);entityInputs(step.outputIds,`${step.id}.outputIds`);}}
|
|
41
|
+
if (research.researchRelations !== undefined) {
|
|
42
|
+
const ids = new Set();
|
|
43
|
+
for (const relation of list(research.researchRelations, 'researchRelations')) {
|
|
44
|
+
if (!relation || !nonempty(relation.id) || ids.has(relation.id)) { issues.push('researchRelations requires unique nonempty ids'); continue; }
|
|
45
|
+
ids.add(relation.id);
|
|
46
|
+
reference(relation.from, `relation ${relation.id}.from`); reference(relation.to, `relation ${relation.id}.to`);
|
|
47
|
+
const types = CAP_PREDICATES[relation.predicate];
|
|
48
|
+
if (!types || ['revises', 'supersedes', 'hasStep', 'expects'].includes(relation.predicate)) issues.push(`relation ${relation.id}: unsupported source predicate`);
|
|
49
|
+
else for (const [i, endpoint] of [relation.from, relation.to].entries())
|
|
50
|
+
if (types[i] && refs.has(endpoint) && refs.get(endpoint).primitive !== types[i]) issues.push(`relation ${relation.id}: ${endpoint} must be ${types[i]}`);
|
|
51
|
+
if (!['declared', 'inferred'].includes(relation.basis)) issues.push(`relation ${relation.id}: source basis must be declared or inferred`);
|
|
52
|
+
locators(relation.sourceLocators, `relation ${relation.id}.sourceLocators`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const view = research.reading;
|
|
56
|
+
if (view !== undefined) {
|
|
57
|
+
if (!view || view.schemaVersion !== '1.0') { issues.push('reading.schemaVersion must be 1.0'); return; }
|
|
58
|
+
if (!nonempty(view.overview)) issues.push('reading.overview must be nonempty');
|
|
59
|
+
const main = list(view.mainline, 'reading.mainline');
|
|
60
|
+
if (!main.length) issues.push('reading.mainline must contain source-grounded objects');
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const id of main) { reference(id, 'reading.mainline'); if (seen.has(id)) issues.push('reading.mainline contains duplicates'); seen.add(id); }
|
|
63
|
+
const parents = new Map();
|
|
64
|
+
for (const branch of list(view.branches, 'reading.branches')) {
|
|
65
|
+
reference(branch?.parent, 'reading.branch.parent');
|
|
66
|
+
for (const child of list(branch?.children, 'reading.branch.children')) {
|
|
67
|
+
reference(child, 'reading.branch.child');
|
|
68
|
+
if (main.includes(child) || parents.has(child) || child === branch.parent) issues.push(`reading branch ${child} has duplicate or cyclic placement`);
|
|
69
|
+
parents.set(child, branch.parent);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const start of parents.keys()) {
|
|
73
|
+
let id = start; const visited = new Set();
|
|
74
|
+
while (parents.has(id)) { if (visited.has(id)) { issues.push(`reading branch cycle at ${start}`); break; } visited.add(id); id = parents.get(id); }
|
|
75
|
+
if (!main.includes(id)) issues.push(`reading branch ${start} must connect to a mainline object`);
|
|
76
|
+
}
|
|
77
|
+
const labelled = new Set();
|
|
78
|
+
for (const entry of list(view.labels ?? [], 'reading.labels')) {
|
|
79
|
+
reference(entry?.target, 'reading.label');
|
|
80
|
+
if(entry?.ref !== undefined) issues.push('reading.labels uses target for local identity; ref is reserved for bound CAP references');
|
|
81
|
+
if (!nonempty(entry?.title) || labelled.has(entry?.target)) issues.push('reading.labels needs unique references and nonempty titles');
|
|
82
|
+
labelled.add(entry?.target);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const experiment of research.experiments ?? []) {
|
|
86
|
+
const intent = experiment.researchIntent;
|
|
87
|
+
if (intent === undefined) continue;
|
|
88
|
+
for (const field of ['question', 'comparison', 'criterion']) if (!nonempty(intent?.[field])) issues.push(`experiment ${experiment.id}.researchIntent.${field} must be nonempty`);
|
|
89
|
+
reference(intent?.from, `experiment ${experiment.id}.researchIntent.from`);
|
|
90
|
+
reference(intent?.to, `experiment ${experiment.id}.researchIntent.to`);
|
|
91
|
+
if (!experiment.claimIds?.some(id => intent?.to === `claim:${id}`)) issues.push(`experiment ${experiment.id}: researchIntent.to must be an explicitly targeted claim`);
|
|
92
|
+
for (const id of list(intent?.context ?? [], `experiment ${experiment.id}.researchIntent.context`)) reference(id, `experiment ${experiment.id}.researchIntent.context`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CONTEXT_ROLES } from './research-map.mjs';
|
|
2
|
+
import { isRecord, sha256Value } from '../util.mjs';
|
|
3
|
+
import { validateSourceObservations } from './source-observations.mjs';
|
|
4
|
+
|
|
5
|
+
export const RESEARCH_OBJECT_ROLES = new Set(['dataset', 'sample', 'model', 'checkpoint', 'code', 'method', 'observation', 'material', ...Object.keys(CONTEXT_ROLES)]);
|
|
6
|
+
export const RESEARCH_STEP_KINDS = new Set(['preparation', 'collection', 'preprocessing', 'training', 'evaluation', 'analysis']);
|
|
7
|
+
|
|
8
|
+
/** IDs are explicit scientific identities. Names never merge objects or steps. */
|
|
9
|
+
export function validateResearchObjects(research, issues) {
|
|
10
|
+
const objects = research.researchObjects ?? [];
|
|
11
|
+
if (!Array.isArray(objects)) { issues.push('researchObjects must be an array'); return []; }
|
|
12
|
+
const boundSources = new Set();
|
|
13
|
+
const ids = new Set(), sourceIds = new Set((research.sources ?? []).map(item => item.id));
|
|
14
|
+
const claimIds = new Set((research.claims ?? []).map(item => item.id));
|
|
15
|
+
const steps = new Map();
|
|
16
|
+
const checkIds = (values, allowed, label) => {
|
|
17
|
+
if (values === undefined) return;
|
|
18
|
+
if (!Array.isArray(values)) { issues.push(`${label} must be an array`); return; }
|
|
19
|
+
for (const value of values) if (typeof value !== 'string' || !allowed.has(value)) issues.push(`${label} references an unknown identity: ${String(value)}`);
|
|
20
|
+
if (new Set(values).size !== values.length) issues.push(`${label} contains duplicate identities`);
|
|
21
|
+
};
|
|
22
|
+
for (const [index, object] of objects.entries()) {
|
|
23
|
+
const label = `researchObjects[${index}]`;
|
|
24
|
+
if (!isRecord(object)) { issues.push(`${label} must be an object`); continue; }
|
|
25
|
+
for (const field of ['id', 'title']) if (typeof object[field] !== 'string' || !object[field].trim()) issues.push(`${label}.${field} must be nonempty`);
|
|
26
|
+
if (ids.has(object.id)) issues.push(`Duplicate research object id: ${object.id}`);
|
|
27
|
+
ids.add(object.id);
|
|
28
|
+
if (!RESEARCH_OBJECT_ROLES.has(object.role)) issues.push(`${label}.role is unsupported`);
|
|
29
|
+
if (object.observationKind !== undefined && (object.role !== 'observation'
|
|
30
|
+
|| !['scalar', 'table', 'matrix', 'series', 'collection'].includes(object.observationKind))) issues.push(`${label}.observationKind must describe an observation`);
|
|
31
|
+
if (object.basis !== undefined && !['declared', 'inferred'].includes(object.basis)) issues.push(`${label}.basis cannot claim actual observation during planning`);
|
|
32
|
+
if (object.prospective !== undefined && typeof object.prospective !== 'boolean') issues.push(`${label}.prospective must be boolean`);
|
|
33
|
+
if (object.sourceId !== undefined) {
|
|
34
|
+
const source = research.sources.find(source => source.id === object.sourceId);
|
|
35
|
+
if (!source || source.kind === 'paper') issues.push(`${label}.sourceId must identify an existing scientific material, not a paper mention`);
|
|
36
|
+
if (boundSources.has(object.sourceId)) issues.push(`${label}.sourceId is already represented by another object`);
|
|
37
|
+
boundSources.add(object.sourceId);
|
|
38
|
+
if (object.prospective === true) issues.push(`${label} cannot replace an existing source with a prospective object`);
|
|
39
|
+
for (const [key, expected] of [['uri', source?.uri], ['version', source?.version], ['revision', source?.kind === 'repository' ? source.version : undefined], ['digest', source?.digest]]) {
|
|
40
|
+
if (expected && object.identity?.[key] !== undefined && object.identity[key] !== expected) issues.push(`${label}.identity.${key} conflicts with its fixed source`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (object.sourceLocators !== undefined && !Array.isArray(object.sourceLocators)) { issues.push(`${label}.sourceLocators must be an array`); continue; }
|
|
44
|
+
for (const locator of object.sourceLocators ?? []) if (!sourceIds.has(locator?.sourceId) || typeof locator?.locator !== 'string' || !locator.locator.trim()) issues.push(`${label}.sourceLocators requires fixed source identities and locations`);
|
|
45
|
+
if (object.identity !== undefined && !isRecord(object.identity)) issues.push(`${label}.identity must be an object`);
|
|
46
|
+
}
|
|
47
|
+
for (const [index, object] of objects.entries()) if (isRecord(object)) checkIds(object.derivedFromIds, ids, `researchObjects[${index}].derivedFromIds`);
|
|
48
|
+
for (const claim of research.claims ?? []) {
|
|
49
|
+
checkIds(claim.objectIds, ids, `claim ${claim.id}.objectIds`);
|
|
50
|
+
for (const measurement of claim.reportedMeasurements ?? []) checkIds(measurement.objectIds, ids, `measurement ${measurement.id}.objectIds`);
|
|
51
|
+
}
|
|
52
|
+
const prospective = new Set(objects.filter(object => object?.prospective === true).map(object => object.id));
|
|
53
|
+
for (const experiment of research.experiments ?? []) {
|
|
54
|
+
checkIds(experiment.inputIds, ids, `experiment ${experiment.id}.inputIds`);
|
|
55
|
+
if (experiment.steps !== undefined && !Array.isArray(experiment.steps)) { issues.push(`experiment ${experiment.id}.steps must be an array`); continue; }
|
|
56
|
+
for (const step of experiment.steps ?? []) {
|
|
57
|
+
const label = `experiment ${experiment.id}.step ${step?.id}`;
|
|
58
|
+
if (!isRecord(step)) { issues.push(`${label} must be an object`); continue; }
|
|
59
|
+
for (const field of ['id', 'title']) if (typeof step[field] !== 'string' || !step[field].trim()) issues.push(`${label}.${field} must be nonempty`);
|
|
60
|
+
if (!RESEARCH_STEP_KINDS.has(step.kind)) issues.push(`${label}.kind is unsupported`);
|
|
61
|
+
checkIds(step.inputIds, ids, `${label}.inputIds`);
|
|
62
|
+
checkIds(step.outputIds, prospective, `${label}.outputIds (prospective objects only)`);
|
|
63
|
+
checkIds(step.claimIds, claimIds, `${label}.claimIds`);
|
|
64
|
+
const content = { ...step }; delete content.claimIds;
|
|
65
|
+
const prior = steps.get(step.id);
|
|
66
|
+
if (prior && sha256Value(prior) !== sha256Value(content)) issues.push(`Shared scientific step ${step.id} has conflicting definitions`);
|
|
67
|
+
steps.set(step.id, content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const [id, step] of steps) {
|
|
71
|
+
checkIds(step.dependsOn, new Set(steps.keys()), `step ${id}.dependsOn`);
|
|
72
|
+
if (step.dependsOn?.includes(id)) issues.push(`step ${id} cannot depend on itself`);
|
|
73
|
+
}
|
|
74
|
+
validateSourceObservations(research, issues);
|
|
75
|
+
return structuredClone(objects);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Preserve source objects; preparation adds new specifications without rewriting source identity. */
|
|
79
|
+
export function mergeResearchObjects(source = [], additions = []) {
|
|
80
|
+
const merged = new Map(source.map(object => [object.id, structuredClone(object)]));
|
|
81
|
+
if (!Array.isArray(additions)) throw Error('Preparation researchObjects must be an array');
|
|
82
|
+
for (const object of additions) {
|
|
83
|
+
const original = merged.get(object?.id);
|
|
84
|
+
if (original && sha256Value(original) !== sha256Value(object)) throw Error(`Preparation cannot overwrite source research object ${object.id}; create a distinct derived object`);
|
|
85
|
+
merged.set(object.id, structuredClone(object));
|
|
86
|
+
}
|
|
87
|
+
return [...merged.values()];
|
|
88
|
+
}
|