@khoavu882/doflow 2.0.0-beta.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -11
- package/bin/doflow.js +21 -11
- package/core/.antigravity-plugin/plugin.json +1 -3
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/claude/settings/settings.json +18 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
- package/core/harnesses/kiro/hooks/session-start.sh +12 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
- package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
- package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +72 -80
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +60 -33
- package/core/registry/contribution-points.json +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1542 -277
- package/core/registry/lifecycle.json +2837 -37
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +276 -0
- package/core/registry/storage.json +701 -30
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +154 -35
- package/core/shared/skills/do/SKILL.md +12 -10
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +41 -144
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +24 -23
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +7 -9
- package/core/shared/skills/do-plan/SKILL.md +29 -19
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +226 -113
- package/src/adapters/claude/index.js +39 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +40 -5
- package/src/adapters/codex/index.js +100 -22
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +294 -48
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +38 -13
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +120 -0
- package/src/adapters/pi/index.js +48 -16
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +3 -2
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +3 -2
- package/src/cli/index.js +338 -33
- package/src/cli/runtime-commands.js +243 -52
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +74 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +434 -86
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +686 -32
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +492 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +514 -0
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +27 -12
- package/src/runtime/cli-result.js +11 -4
- package/src/runtime/cli.js +221 -277
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +100 -0
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +1106 -245
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/evidence-ledger.js +41 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +77 -10
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +3 -3
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +477 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +127 -16
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +402 -73
- package/src/runtime/retrieval-record.js +293 -0
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +63 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +290 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +47 -11
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +202 -56
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +440 -65
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +288 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +578 -63
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -5
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -210
- package/src/adapters/gemini/index.js +0 -390
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// 119 IC-001: the review/debate protocol registry (core/registry/protocols.json) and its validator.
|
|
4
|
+
// Presets are data behind `validateProtocols`, a pure function over a parsed object, so a package
|
|
5
|
+
// could contribute presets later without a second validator. Read from the runtime install root
|
|
6
|
+
// (REPO_ROOT), never from a project tree. Content errors are returned, never thrown.
|
|
7
|
+
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const { REPO_ROOT } = require('../../helper/repo-root.js');
|
|
11
|
+
const { dominates } = require('./dominance.js');
|
|
12
|
+
const { isLocalId } = require('../../helper/package-ids.js');
|
|
13
|
+
|
|
14
|
+
const PROTOCOLS_FILE = 'core/registry/protocols.json';
|
|
15
|
+
const MODELS_FILE = 'core/registry/models.json';
|
|
16
|
+
const KINDS = ['review', 'debate'];
|
|
17
|
+
const FAMILIES = ['any', 'different-from-author'];
|
|
18
|
+
const SYNTHESIZERS = ['separate', 'seat', 'none', 'verifier'];
|
|
19
|
+
const DECIDERS = ['gate', 'orchestrator'];
|
|
20
|
+
const PRESET_ID = /^[a-z][a-z0-9-]*$/;
|
|
21
|
+
const INVARIANT_ID = /^INV-\d+$/;
|
|
22
|
+
|
|
23
|
+
const TOP_KEYS = ['schema', 'description', 'hardRoundCap', 'lenses', 'synthesizerStrength', 'presets', 'order', 'invariants'];
|
|
24
|
+
const LENS_KEYS = ['id', 'description'];
|
|
25
|
+
const PRESET_KEYS = ['kind', 'depth', 'seats', 'rounds', 'crossAnswer', 'synthesizer', 'decider'];
|
|
26
|
+
const SEAT_KEYS = ['id', 'agent', 'lenses', 'model'];
|
|
27
|
+
const MODEL_KEYS = ['tier', 'family'];
|
|
28
|
+
const ROUNDS_KEYS = ['default', 'min'];
|
|
29
|
+
const INVARIANT_KEYS = ['id', 'statement', 'enforcedBy', 'kinds', 'authorKinds'];
|
|
30
|
+
|
|
31
|
+
function isObject(value ) {
|
|
32
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isPositiveInt = (value ) => Number.isInteger(value) && value > 0;
|
|
36
|
+
const isNonEmptyString = (value ) => typeof value === 'string' && value.length > 0;
|
|
37
|
+
|
|
38
|
+
/** Closed shape: every key of `value` must be in `allowed`. */
|
|
39
|
+
function checkKeys(errors , where , value , allowed ) {
|
|
40
|
+
for (const key of Object.keys(value)) {
|
|
41
|
+
if (!allowed.includes(key)) errors.push(`${where} has unknown key '${key}'`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The ordered core model tiers (models.json `dispatchTiers.tiers`, loosest first) of the runtime install; null when unreadable. */
|
|
46
|
+
let coreTierCache ;
|
|
47
|
+
function coreTiers() {
|
|
48
|
+
if (coreTierCache === undefined) {
|
|
49
|
+
try {
|
|
50
|
+
const tiers = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, MODELS_FILE), 'utf8'))?.dispatchTiers?.tiers;
|
|
51
|
+
coreTierCache = Array.isArray(tiers) && tiers.length > 0 && tiers.every(isNonEmptyString) ? tiers : null;
|
|
52
|
+
} catch {
|
|
53
|
+
coreTierCache = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return coreTierCache;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The entry shape of one preset (122 IC-014): the one validator for core presets and, later, for a
|
|
61
|
+
* package's contributed ones. `context` carries what the check reads beside the preset: `lensIds` (the
|
|
62
|
+
* lens catalogue), `hardRoundCap` and `tiers` (default: the core tiers of models.json). With
|
|
63
|
+
* `context.mode === 'package'` (a contributed preset, IC-014) `depth` is refused rather than required, each
|
|
64
|
+
* seat id must be a local id, and each seat agent must be in `context.agents` (the core agent specs a package
|
|
65
|
+
* seat may name). The kind match and comparability belong to the caller. Cross-preset rules (unique depth, `order`) stay in
|
|
66
|
+
* `validateProtocols`. Returns the errors; never throws.
|
|
67
|
+
*/
|
|
68
|
+
function validatePreset(id , preset , context = {}) {
|
|
69
|
+
const errors = [];
|
|
70
|
+
const lensIdSet = new Set (context.lensIds ?? []);
|
|
71
|
+
const cap = context.hardRoundCap;
|
|
72
|
+
const tiers = Object.hasOwn(context, 'tiers') ? context.tiers : coreTiers();
|
|
73
|
+
const where = `presets.${id}`;
|
|
74
|
+
if (!PRESET_ID.test(id)) errors.push(`${where} id must match ${PRESET_ID}`);
|
|
75
|
+
if (!isObject(preset)) { errors.push(`${where} must be an object`); return errors; }
|
|
76
|
+
checkKeys(errors, where, preset, PRESET_KEYS);
|
|
77
|
+
|
|
78
|
+
if (!KINDS.includes(preset.kind)) errors.push(`${where}.kind must be one of ${KINDS.join(', ')}`);
|
|
79
|
+
const packageMode = context.mode === 'package';
|
|
80
|
+
const agents = Array.isArray(context.agents) ? context.agents : [];
|
|
81
|
+
if (packageMode) {
|
|
82
|
+
if ('depth' in preset) errors.push(`${where}.depth is not allowed in a package preset (the depth vocabulary stays core)`);
|
|
83
|
+
} else if (preset.kind === 'review') {
|
|
84
|
+
if (!isNonEmptyString(preset.depth)) errors.push(`${where}.depth is required on a review preset`);
|
|
85
|
+
} else if (preset.kind === 'debate' && 'depth' in preset) {
|
|
86
|
+
errors.push(`${where}.depth is not allowed on a debate preset`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const seatIds = new Set ();
|
|
90
|
+
if (!Array.isArray(preset.seats) || preset.seats.length === 0) {
|
|
91
|
+
errors.push(`${where}.seats must be a non-empty array`);
|
|
92
|
+
} else {
|
|
93
|
+
preset.seats.forEach((seat , i ) => {
|
|
94
|
+
const sw = `${where}.seats[${i}]`;
|
|
95
|
+
if (!isObject(seat)) { errors.push(`${sw} must be an object`); return; }
|
|
96
|
+
checkKeys(errors, sw, seat, SEAT_KEYS);
|
|
97
|
+
if (!isNonEmptyString(seat.id)) errors.push(`${sw}.id must be a non-empty string`);
|
|
98
|
+
else if (seatIds.has(seat.id)) errors.push(`${where} seat id '${seat.id}' is duplicated`);
|
|
99
|
+
else seatIds.add(seat.id);
|
|
100
|
+
if (packageMode && isNonEmptyString(seat.id) && !isLocalId(seat.id)) errors.push(`${sw}.id must be a local id (2 to 15 characters of a-z, 0-9 and single hyphens)`);
|
|
101
|
+
if (!isNonEmptyString(seat.agent)) errors.push(`${sw}.agent must be a non-empty string`);
|
|
102
|
+
else if (packageMode && !agents.includes(seat.agent)) errors.push(`${sw}.agent must be one of the core agents allowed for this kind: ${agents.join(', ') || '(none readable)'}`);
|
|
103
|
+
if ('lenses' in seat) {
|
|
104
|
+
if (!Array.isArray(seat.lenses)) errors.push(`${sw}.lenses must be an array`);
|
|
105
|
+
else for (const lens of seat.lenses) {
|
|
106
|
+
if (!lensIdSet.has(lens)) errors.push(`${sw}.lenses names unknown lens '${lens}'`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!isObject(seat.model)) errors.push(`${sw}.model must be an object`);
|
|
110
|
+
else {
|
|
111
|
+
checkKeys(errors, `${sw}.model`, seat.model, MODEL_KEYS);
|
|
112
|
+
if (!isNonEmptyString(seat.model.tier)) errors.push(`${sw}.model.tier must be a non-empty string`);
|
|
113
|
+
else if (!Array.isArray(tiers)) errors.push(`${sw}.model.tier cannot be checked: ${MODELS_FILE} dispatchTiers.tiers is unreadable`);
|
|
114
|
+
else if (!tiers.includes(seat.model.tier)) errors.push(`${sw}.model.tier '${seat.model.tier}' must be one of ${tiers.join(', ')}`);
|
|
115
|
+
if ('family' in seat.model && !FAMILIES.includes(seat.model.family)) {
|
|
116
|
+
errors.push(`${sw}.model.family must be one of ${FAMILIES.join(', ')}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const rounds = preset.rounds;
|
|
123
|
+
if (!isObject(rounds)) errors.push(`${where}.rounds must be an object`);
|
|
124
|
+
else {
|
|
125
|
+
checkKeys(errors, `${where}.rounds`, rounds, ROUNDS_KEYS);
|
|
126
|
+
for (const field of ROUNDS_KEYS) {
|
|
127
|
+
if (!isPositiveInt(rounds[field])) errors.push(`${where}.rounds.${field} must be a positive integer`);
|
|
128
|
+
}
|
|
129
|
+
if (isPositiveInt(rounds.default) && isPositiveInt(rounds.min)) {
|
|
130
|
+
if (rounds.min > rounds.default) errors.push(`${where}.rounds.min must not exceed rounds.default`);
|
|
131
|
+
if (isPositiveInt(cap) && rounds.default > cap) errors.push(`${where}.rounds.default must not exceed hardRoundCap`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof preset.crossAnswer !== 'boolean') errors.push(`${where}.crossAnswer must be a boolean`);
|
|
136
|
+
if (!SYNTHESIZERS.includes(preset.synthesizer)) errors.push(`${where}.synthesizer must be one of ${SYNTHESIZERS.join(', ')}`);
|
|
137
|
+
else if (preset.synthesizer === 'seat' && !(Array.isArray(preset.seats) && preset.seats.length === 1)) {
|
|
138
|
+
errors.push(`${where}.synthesizer 'seat' is only valid on a one-seat preset`);
|
|
139
|
+
}
|
|
140
|
+
if (!DECIDERS.includes(preset.decider)) errors.push(`${where}.decider must be one of ${DECIDERS.join(', ')}`);
|
|
141
|
+
return errors;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The preset ids a first-party (bundled) package contributes, by kind, for an `order` entry that names an id
|
|
146
|
+
* `presets` does not hold (122 IC-014: `order` stays core-authored and may name first-party ids). `opaque` is
|
|
147
|
+
* true when a bundled package's ids cannot be known (point registry or manifest unreadable): such an id is then
|
|
148
|
+
* skipped by the loader, never a validation error (FR-009). Lazy, so the packages modules load only when needed.
|
|
149
|
+
*/
|
|
150
|
+
function bundledPresetIds({ repoRoot, fsImpl } = {}) {
|
|
151
|
+
try {
|
|
152
|
+
return require('../packages/contributions.js').firstPartyPresets({ repoRoot, fsImpl });
|
|
153
|
+
} catch {
|
|
154
|
+
return { review: [], debate: [], opaque: false };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Validate a parsed registry. `options.firstParty` (default: read from `options.repoRoot`, REPO_ROOT, only when an
|
|
160
|
+
* `order` id has no preset) names the first-party ids an `order` may carry without holding their preset.
|
|
161
|
+
*/
|
|
162
|
+
function validateProtocols(data , options = {}) {
|
|
163
|
+
const errors = [];
|
|
164
|
+
if (!isObject(data)) return { ok: false, errors: ['registry must be an object'] };
|
|
165
|
+
checkKeys(errors, 'registry', data, TOP_KEYS);
|
|
166
|
+
|
|
167
|
+
if (data.schema !== 1) errors.push('schema must be 1');
|
|
168
|
+
if (!isNonEmptyString(data.description)) errors.push('description must be a non-empty string');
|
|
169
|
+
const cap = data.hardRoundCap;
|
|
170
|
+
if (!isPositiveInt(cap)) errors.push('hardRoundCap must be a positive integer');
|
|
171
|
+
|
|
172
|
+
const lensIdSet = new Set ();
|
|
173
|
+
if (!Array.isArray(data.lenses) || data.lenses.length === 0) {
|
|
174
|
+
errors.push('lenses must be a non-empty array');
|
|
175
|
+
} else {
|
|
176
|
+
data.lenses.forEach((lens , i ) => {
|
|
177
|
+
const where = `lenses[${i}]`;
|
|
178
|
+
if (!isObject(lens)) { errors.push(`${where} must be an object`); return; }
|
|
179
|
+
checkKeys(errors, where, lens, LENS_KEYS);
|
|
180
|
+
if (!isNonEmptyString(lens.id)) errors.push(`${where}.id must be a non-empty string`);
|
|
181
|
+
else if (lensIdSet.has(lens.id)) errors.push(`lens id '${lens.id}' is duplicated`);
|
|
182
|
+
else lensIdSet.add(lens.id);
|
|
183
|
+
if (!isNonEmptyString(lens.description)) errors.push(`${where}.description must be a non-empty string`);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// The synthesizer lattice `dominates` reads: one non-negative integer per synthesizer, higher is stronger.
|
|
188
|
+
const strength = data.synthesizerStrength;
|
|
189
|
+
let strengthOk = isObject(strength);
|
|
190
|
+
if (!strengthOk) errors.push('synthesizerStrength must be an object');
|
|
191
|
+
else {
|
|
192
|
+
const before = errors.length;
|
|
193
|
+
checkKeys(errors, 'synthesizerStrength', strength, SYNTHESIZERS);
|
|
194
|
+
for (const name of SYNTHESIZERS) {
|
|
195
|
+
if (!Object.hasOwn(strength, name)) errors.push(`synthesizerStrength omits synthesizer '${name}'`);
|
|
196
|
+
else if (!Number.isInteger(strength[name]) || strength[name] < 0) errors.push(`synthesizerStrength.${name} must be a non-negative integer`);
|
|
197
|
+
}
|
|
198
|
+
strengthOk = errors.length === before;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let firstParty ;
|
|
202
|
+
const kindOf = {};
|
|
203
|
+
const validIds = new Set (); // presets with no entry error: the only ones the order's dominance check compares
|
|
204
|
+
const depths = new Map ();
|
|
205
|
+
if (!isObject(data.presets) || Object.keys(data.presets).length === 0) {
|
|
206
|
+
errors.push('presets must be a non-empty object');
|
|
207
|
+
} else {
|
|
208
|
+
const context = { lensIds: lensIdSet, hardRoundCap: cap, tiers: coreTiers() };
|
|
209
|
+
for (const [id, preset] of Object.entries (data.presets)) {
|
|
210
|
+
const presetErrors = validatePreset(id, preset, context);
|
|
211
|
+
errors.push(...presetErrors);
|
|
212
|
+
if (presetErrors.length === 0) validIds.add(id);
|
|
213
|
+
if (!isObject(preset) || !KINDS.includes(preset.kind)) continue;
|
|
214
|
+
kindOf[id] = preset.kind;
|
|
215
|
+
if (preset.kind === 'review' && isNonEmptyString(preset.depth)) {
|
|
216
|
+
if (depths.has(preset.depth)) errors.push(`presets.${id}.depth '${preset.depth}' duplicates presets.${depths.get(preset.depth)}.depth`);
|
|
217
|
+
else depths.set(preset.depth, id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!isObject(data.order)) errors.push('order must be an object');
|
|
223
|
+
else {
|
|
224
|
+
checkKeys(errors, 'order', data.order, KINDS);
|
|
225
|
+
for (const kind of KINDS) {
|
|
226
|
+
const list = data.order[kind];
|
|
227
|
+
if (!Array.isArray(list)) { errors.push(`order.${kind} must be an array`); continue; }
|
|
228
|
+
const expected = Object.keys(kindOf).filter((id) => kindOf[id] === kind);
|
|
229
|
+
for (const id of list) {
|
|
230
|
+
if (expected.includes(id)) continue;
|
|
231
|
+
// An id with no preset here may be one a bundled package supplies (kept out of this file, merged by loadProtocols).
|
|
232
|
+
if (!Object.hasOwn(data.presets ?? {}, id)) {
|
|
233
|
+
firstParty ??= options.firstParty ?? bundledPresetIds(options);
|
|
234
|
+
if ((firstParty )[kind].includes(id) || firstParty .opaque) continue;
|
|
235
|
+
}
|
|
236
|
+
errors.push(`order.${kind} lists '${id}', which is not a ${kind} preset`);
|
|
237
|
+
}
|
|
238
|
+
for (const id of expected) {
|
|
239
|
+
const count = list.filter((entry ) => entry === id).length;
|
|
240
|
+
if (count === 0) errors.push(`order.${kind} omits preset '${id}'`);
|
|
241
|
+
else if (count > 1) errors.push(`order.${kind} lists preset '${id}' more than once`);
|
|
242
|
+
}
|
|
243
|
+
// 122 IC-014: the authored order is strictest first, so each preset dominates the next one. A pair
|
|
244
|
+
// with an invalid preset or lattice is not compared: its own error is already reported.
|
|
245
|
+
const tiers = coreTiers();
|
|
246
|
+
if (!strengthOk || !tiers) continue;
|
|
247
|
+
for (let i = 0; i + 1 < list.length; i++) {
|
|
248
|
+
const [stricter, looser] = [list[i], list[i + 1]];
|
|
249
|
+
if (kindOf[stricter] !== kind || kindOf[looser] !== kind || !validIds.has(stricter) || !validIds.has(looser)) continue;
|
|
250
|
+
if (!dominates(data.presets[stricter], data.presets[looser], { tiers, synthesizerStrength: strength })) {
|
|
251
|
+
errors.push(`order.${kind}: '${stricter}' does not dominate '${looser}'`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!Array.isArray(data.invariants) || data.invariants.length === 0) {
|
|
258
|
+
errors.push('invariants must be a non-empty array');
|
|
259
|
+
} else {
|
|
260
|
+
const seen = new Set ();
|
|
261
|
+
data.invariants.forEach((inv , i ) => {
|
|
262
|
+
const where = `invariants[${i}]`;
|
|
263
|
+
if (!isObject(inv)) { errors.push(`${where} must be an object`); return; }
|
|
264
|
+
checkKeys(errors, where, inv, INVARIANT_KEYS);
|
|
265
|
+
if (typeof inv.id !== 'string' || !INVARIANT_ID.test(inv.id)) errors.push(`${where}.id must match ${INVARIANT_ID}`);
|
|
266
|
+
else if (seen.has(inv.id)) errors.push(`invariant id '${inv.id}' is duplicated`);
|
|
267
|
+
else seen.add(inv.id);
|
|
268
|
+
if (!isNonEmptyString(inv.statement)) errors.push(`${where}.statement must be a non-empty string`);
|
|
269
|
+
if (!isNonEmptyString(inv.enforcedBy)) errors.push(`${where}.enforcedBy must be a non-empty string`);
|
|
270
|
+
if (Object.hasOwn(inv, 'kinds')) { // the protocol kinds the invariant applies to; absent = ['review']
|
|
271
|
+
if (!Array.isArray(inv.kinds) || inv.kinds.length === 0) errors.push(`${where}.kinds must be a non-empty list of ${KINDS.join(', ')}`);
|
|
272
|
+
else {
|
|
273
|
+
for (const kind of inv.kinds) if (!KINDS.includes(kind)) errors.push(`${where}.kinds has unknown kind '${kind}'`);
|
|
274
|
+
for (const kind of new Set(inv.kinds)) {
|
|
275
|
+
if (inv.kinds.filter((k ) => k === kind).length > 1) errors.push(`${where}.kinds lists '${kind}' more than once`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (Object.hasOwn(inv, 'authorKinds')) {
|
|
280
|
+
const VALID_AUTHOR_KINDS = ['record', 'subagent', 'sequential', 'primary'];
|
|
281
|
+
if (!Array.isArray(inv.authorKinds) || inv.authorKinds.length === 0) {
|
|
282
|
+
errors.push(`${where}.authorKinds must be a non-empty list of ${VALID_AUTHOR_KINDS.join(', ')}`);
|
|
283
|
+
} else {
|
|
284
|
+
for (const kind of inv.authorKinds) {
|
|
285
|
+
if (!VALID_AUTHOR_KINDS.includes(kind)) errors.push(`${where}.authorKinds has unknown kind '${kind}'`);
|
|
286
|
+
}
|
|
287
|
+
for (const kind of new Set(inv.authorKinds)) {
|
|
288
|
+
if (inv.authorKinds.filter((k ) => k === kind).length > 1) {
|
|
289
|
+
errors.push(`${where}.authorKinds lists '${kind}' more than once`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!inv.authorKinds.includes('subagent')) {
|
|
293
|
+
errors.push(`${where}.authorKinds must contain 'subagent'`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return { ok: errors.length === 0, errors };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Keys that appear twice in one object of `text` (JSON.parse keeps only the last, so a parsed
|
|
305
|
+
* object cannot show it). A single pass over the text: a stack of per-object key sets.
|
|
306
|
+
*/
|
|
307
|
+
function duplicateKeys(text ) {
|
|
308
|
+
const found = [];
|
|
309
|
+
const stack = [];
|
|
310
|
+
for (let i = 0; i < text.length; i++) {
|
|
311
|
+
const ch = text[i];
|
|
312
|
+
if (ch === '{' || ch === '[') stack.push({ obj: ch === '{', keys: new Set(), last: null });
|
|
313
|
+
else if (ch === '}' || ch === ']') stack.pop();
|
|
314
|
+
else if (ch === '"') {
|
|
315
|
+
let end = i + 1;
|
|
316
|
+
while (end < text.length && text[end] !== '"') end += text[end] === '\\' ? 2 : 1;
|
|
317
|
+
const top = stack[stack.length - 1];
|
|
318
|
+
let next = end + 1;
|
|
319
|
+
while (next < text.length && /\s/.test(text[next])) next++;
|
|
320
|
+
if (top && top.obj && text[next] === ':') {
|
|
321
|
+
const key = JSON.parse(text.slice(i, end + 1));
|
|
322
|
+
if (top.keys.has(key)) found.push(`duplicate key '${key}' in ${stack.map((e) => e.last ?? '[]').slice(0, -1).join('.') || 'registry'}`);
|
|
323
|
+
top.keys.add(key);
|
|
324
|
+
top.last = key;
|
|
325
|
+
}
|
|
326
|
+
i = end;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return found;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** The core registry file alone: `core/registry/protocols.json` (or `file`) read, held to no duplicate key and `validateProtocols`. */
|
|
333
|
+
function loadCoreProtocols({ repoRoot = REPO_ROOT, file, fsImpl = fs } = {}) {
|
|
334
|
+
const target = file || path.join(path.resolve(repoRoot), PROTOCOLS_FILE);
|
|
335
|
+
let text ;
|
|
336
|
+
let registry ;
|
|
337
|
+
try {
|
|
338
|
+
text = fsImpl.readFileSync(target, 'utf8');
|
|
339
|
+
registry = JSON.parse(text);
|
|
340
|
+
} catch (error ) {
|
|
341
|
+
return { ok: false, errors: [`${target}: ${error.message}`] };
|
|
342
|
+
}
|
|
343
|
+
const errors = [...duplicateKeys(text), ...validateProtocols(registry, { repoRoot, fsImpl }).errors];
|
|
344
|
+
return errors.length === 0 ? { ok: true, registry, errors } : { ok: false, errors };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const CONTRIBUTION_POINT = { review: 'review-preset', debate: 'debate-preset' };
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* The effective registry (122 IC-013, IC-014): the core file plus the presets of every active package, with
|
|
351
|
+
* `provenance` (`{ [presetId]: { package, contentDigest } }`, contributed presets only) and `notices`. A
|
|
352
|
+
* contributed id the core `order` names joins the validated registry, so dominance is checked on the merged order;
|
|
353
|
+
* any other contributed id is added to `presets` after validation and never to `order`. An `order` id whose
|
|
354
|
+
* package is not active is skipped from the effective order and noticed (FR-009), never a validation error.
|
|
355
|
+
*/
|
|
356
|
+
function loadProtocols(options = {}) {
|
|
357
|
+
const { repoRoot = REPO_ROOT, fsImpl = fs } = options;
|
|
358
|
+
const core = loadCoreProtocols(options);
|
|
359
|
+
if (!core.ok) return core;
|
|
360
|
+
const { contributionsFor } = require('../packages/contributions.js');
|
|
361
|
+
const registry = { ...core.registry, presets: { ...core.registry.presets }, order: { ...core.registry.order } };
|
|
362
|
+
const provenance = {};
|
|
363
|
+
const notices = [];
|
|
364
|
+
const extra = {};
|
|
365
|
+
for (const kind of KINDS) {
|
|
366
|
+
const problems = [];
|
|
367
|
+
const contributed = contributionsFor(CONTRIBUTION_POINT[kind], 1, { repoRoot, fsImpl, protocols: core.registry, problems });
|
|
368
|
+
notices.push(...problems);
|
|
369
|
+
for (const item of contributed) {
|
|
370
|
+
if (Object.hasOwn(registry.presets, item.qualifiedId) || Object.hasOwn(extra, item.qualifiedId)) {
|
|
371
|
+
notices.push(`${kind} preset '${item.qualifiedId}' from package ${item.packageId} is skipped: the id is already taken`);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
provenance[item.qualifiedId] = { package: item.packageId, contentDigest: item.provenance.contentDigest };
|
|
375
|
+
if (registry.order[kind].includes(item.qualifiedId)) registry.presets[item.qualifiedId] = item.data;
|
|
376
|
+
else extra[item.qualifiedId] = item.data;
|
|
377
|
+
}
|
|
378
|
+
const effective = registry.order[kind].filter((id ) => Object.hasOwn(registry.presets, id));
|
|
379
|
+
for (const id of registry.order[kind]) {
|
|
380
|
+
if (!effective.includes(id)) notices.push(`order.${kind} names '${id}', which no active package provides; it is skipped from the effective order`);
|
|
381
|
+
}
|
|
382
|
+
registry.order[kind] = effective;
|
|
383
|
+
}
|
|
384
|
+
const errors = validateProtocols(registry, { repoRoot, fsImpl }).errors;
|
|
385
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
386
|
+
Object.assign(registry.presets, extra);
|
|
387
|
+
return { ok: true, registry, errors: [], provenance, notices };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Preset ids of one kind, strictest first (the registry's own `order`). */
|
|
391
|
+
function presetIds(registry , kind ) {
|
|
392
|
+
return [...(registry.order?.[kind] ?? [])];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Presets of one kind that `order` does not name: the contributed ones that are selectable by name only. A first-party id the core `order` names counts as core, not contributed. */
|
|
396
|
+
function contributedPresetIds(registry , kind ) {
|
|
397
|
+
const ordered = new Set (registry.order?.[kind] ?? []);
|
|
398
|
+
return Object.keys(registry.presets ?? {}).filter((id) => registry.presets[id]?.kind === kind && !ordered.has(id)).sort();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function lensIds(registry ) {
|
|
402
|
+
return (registry.lenses ?? []).map((lens ) => lens.id);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = { loadProtocols, loadCoreProtocols, validateProtocols, validatePreset, duplicateKeys, coreTiers, presetIds, contributedPresetIds, lensIds, PROTOCOLS_FILE };
|