@khoavu882/doflow 2.0.0-beta.5 → 2.0.1
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 +16 -16
- package/bin/doflow.js +22 -12
- package/core/.antigravity-plugin/plugin.json +1 -2
- 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 +4 -1
- 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-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +26 -9
- 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/hooks.json +0 -10
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-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 +51 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -11
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
- 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 +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- package/core/registry/contribution-points.json +35 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1445 -366
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +599 -996
- 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/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/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 +272 -0
- package/core/registry/storage.json +687 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -6
- 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 +3 -2
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- 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 +49 -5
- package/core/shared/scripts/doflow/bin/doflow-run +152 -34
- package/core/shared/skills/do/SKILL.md +14 -12
- 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 +45 -97
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -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 +48 -148
- 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 +23 -20
- package/core/shared/skills/do-diagnose/SKILL.md +18 -6
- package/core/shared/skills/do-document/SKILL.md +12 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +19 -7
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
- 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 +26 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
- 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 +10 -11
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +13 -14
- package/core/shared/skills/do-plan/SKILL.md +34 -20
- 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/shared/workflows/do-flow-chain.md +2 -0
- 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 +143 -66
- package/src/adapters/claude/index.js +46 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +61 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +291 -46
- 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 +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- 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 +22 -3
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +337 -32
- package/src/cli/runtime-commands.js +220 -60
- 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 +4 -16
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +472 -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 +20 -11
- 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 +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/pointer.js +67 -12
- 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 +158 -79
- 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-ledger.js +52 -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 +33 -3
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +624 -18
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +281 -26
- package/src/runtime/intake/engine.js +677 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- 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 +88 -10
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +38 -7
- package/src/runtime/lifecycle/overview.js +26 -2
- 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 +2 -2
- 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 +487 -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 +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- 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/attendance.js +137 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +273 -0
- package/src/runtime/roadmap/loop.js +1719 -0
- package/src/runtime/roadmap/merge.js +390 -0
- package/src/runtime/roadmap/rows.js +1122 -0
- package/src/runtime/roadmap/seed.js +1063 -0
- package/src/runtime/roadmap/worktree-cleanup.js +147 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +60 -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 +1 -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 +85 -14
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +373 -64
- 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/verb-view.js +267 -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 -54
- 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 +256 -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 +610 -67
- 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/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- 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 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- 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 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
- 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 -251
- package/src/adapters/gemini/index.js +0 -429
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `doflow-run attendance --task-id <slug> [--run <id>]` (autonomy plan section 2): a read-only answer
|
|
5
|
+
* to "is a person watching this stage?", printed as `unattended <source>` or `attended <reason>`, exit
|
|
6
|
+
* 0. A skill reads it once per stage and resolves its own questions to recorded defaults when the
|
|
7
|
+
* answer is `unattended`; no hook reads it.
|
|
8
|
+
*
|
|
9
|
+
* `unattended` when either holds:
|
|
10
|
+
* (a) `DOFLOW_SESSION_ROLE` is `worker` (the launcher sets it), matched case-insensitively and
|
|
11
|
+
* literally: an unrecognised value is NOT a worker here, although the hook identity policy
|
|
12
|
+
* treats it as one, because uncertainty must read as `attended`; or
|
|
13
|
+
* (b) all of: the run record `.doflow/state/roadmap/runs/<run>.json` is open (no `closedAt`); its
|
|
14
|
+
* slot for the seed row with this slug holds a `perform` or the loop's `postMerge` marker (the stage is
|
|
15
|
+
* still pending; the loop clears the marker on its next merge step) and no `held` or
|
|
16
|
+
* `paused` mark; that row is not on `hold`; and the autopilot kill switch is enabled (the reader the policy decisions use).
|
|
17
|
+
* Anything else, including a missing or unreadable record, is `attended`. The signal is derived from
|
|
18
|
+
* state the runtime owns; a forged value only reaches the verbs the forging session itself calls.
|
|
19
|
+
*
|
|
20
|
+
* This module is imported only by the CLI. The person-only code (seed, autopilot settings, config
|
|
21
|
+
* write, gates, decision register, package trust, merge) never imports it nor reads
|
|
22
|
+
* `DOFLOW_SESSION_ROLE` (test/guards/attendance-scope.test.ts).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('node:fs');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
const { listCheckouts } = require('../checkouts.js');
|
|
28
|
+
|
|
29
|
+
const RUN_ID = /^rl-[0-9]{8}-[0-9a-hjkmnp-tv-z]{4}$/;
|
|
30
|
+
const ALLOWED_FLAGS = new Set(['--task-id', '--run', '--json']);
|
|
31
|
+
|
|
32
|
+
function mainRootOf(cwd ) {
|
|
33
|
+
try {
|
|
34
|
+
const info = listCheckouts({ cwd });
|
|
35
|
+
if (info && info.ok && info.main) return info.main;
|
|
36
|
+
} catch { /* fall back to the working directory */ }
|
|
37
|
+
return path.resolve(cwd);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The validation loop.ts readRunFile applies (steps, n and log included). loop.ts does not export it
|
|
42
|
+
* and is edited in parallel by other packages, so the check is repeated here; a record that fails it
|
|
43
|
+
* is unreadable to the loop and therefore reads as `attended`, never as a signal.
|
|
44
|
+
*/
|
|
45
|
+
function validRecord(value ) {
|
|
46
|
+
return Boolean(value) && value.schema === 1 && typeof value.runId === 'string' && RUN_ID.test(value.runId)
|
|
47
|
+
&& Number.isSafeInteger(value.steps) && Number.isSafeInteger(value.n)
|
|
48
|
+
&& Boolean(value.rows) && typeof value.rows === 'object' && Array.isArray(value.log)
|
|
49
|
+
&& typeof value.createdAt === 'string';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readRecord(file , fsImpl ) {
|
|
53
|
+
const value = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
54
|
+
if (!validRecord(value)) throw new Error('not a schema 1 run record');
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The named run's record, else the newest open one; null when there is none. Throws when unreadable. */
|
|
59
|
+
function loadRun(runsDir , requested , fsImpl ) {
|
|
60
|
+
if (requested) {
|
|
61
|
+
try { return readRecord(path.join(runsDir, `${requested}.json`), fsImpl); }
|
|
62
|
+
catch (error ) { if (error.code === 'ENOENT') return null; throw error; }
|
|
63
|
+
}
|
|
64
|
+
let names ;
|
|
65
|
+
try { names = fsImpl.readdirSync(runsDir); }
|
|
66
|
+
catch (error ) { if (error.code === 'ENOENT') return null; throw error; }
|
|
67
|
+
const open = names.filter((name) => /^rl-.*\.json$/.test(name)).sort()
|
|
68
|
+
.map((name) => readRecord(path.join(runsDir, name), fsImpl))
|
|
69
|
+
.filter((record) => !record.closedAt)
|
|
70
|
+
.sort((a, b) => (a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : a.runId < b.runId ? -1 : 1));
|
|
71
|
+
return open.at(-1) || null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {{taskId: string, run?: string, env?: object, cwd?: string, root?: string, fsImpl?: object, readKillSwitch?: Function}} options
|
|
76
|
+
* @returns {{attendance: 'unattended'|'attended', source?: string, reason?: string, run?: string}}
|
|
77
|
+
*/
|
|
78
|
+
function resolveAttendance(options = {}) {
|
|
79
|
+
const env = options.env || process.env;
|
|
80
|
+
const fsImpl = options.fsImpl || fs;
|
|
81
|
+
const attended = (reason ) => ({ attendance: 'attended', reason });
|
|
82
|
+
if (String(env.DOFLOW_SESSION_ROLE || '').toLowerCase() === 'worker') return { attendance: 'unattended', source: 'worker-role' };
|
|
83
|
+
|
|
84
|
+
const root = options.root || mainRootOf(options.cwd || process.cwd());
|
|
85
|
+
let record ;
|
|
86
|
+
try { record = loadRun(path.join(root, '.doflow', 'state', 'roadmap', 'runs'), options.run, fsImpl); }
|
|
87
|
+
catch { return attended('run-unreadable'); }
|
|
88
|
+
if (!record) return attended('no-run');
|
|
89
|
+
if (record.closedAt) return attended('run-closed');
|
|
90
|
+
|
|
91
|
+
let rows ;
|
|
92
|
+
try { rows = require('./rows.js').parseSeed(fsImpl.readFileSync(path.join(root, 'agent-docs', 'roadmap', 'rows.json'), 'utf8')).rows; }
|
|
93
|
+
catch { return attended('seed-unreadable'); }
|
|
94
|
+
const row = rows.find((entry ) => entry && entry.slug === options.taskId);
|
|
95
|
+
if (!row) return attended('row-unknown');
|
|
96
|
+
if (row.hold) return attended('row-held');
|
|
97
|
+
const slot = record.rows?.[row.id];
|
|
98
|
+
// A held or paused slot is machine state the loop stopped on for a person (hold, gate, dispatch, budget).
|
|
99
|
+
if (slot?.held) return attended('slot-held');
|
|
100
|
+
if (slot?.paused) return attended('slot-paused');
|
|
101
|
+
// A finished row has no stage left, whatever marker an interrupted step left behind.
|
|
102
|
+
if (slot?.done) return attended('slot-done');
|
|
103
|
+
// A pending stage is the slot's `perform`, or the marker the loop leaves when it issues the post-merge
|
|
104
|
+
// verification perform (that perform is returned without being stored as slot.perform).
|
|
105
|
+
const perform = slot?.perform;
|
|
106
|
+
const postMerge = slot?.postMerge;
|
|
107
|
+
const performPending = Boolean(perform) && typeof perform === 'object' && typeof perform.stage === 'string';
|
|
108
|
+
const postMergePending = Boolean(postMerge) && typeof postMerge === 'object' && postMerge.purpose === 'post-merge' && typeof postMerge.stage === 'string';
|
|
109
|
+
if (!performPending && !postMergePending) return attended('no-pending-perform');
|
|
110
|
+
|
|
111
|
+
let enabled = false;
|
|
112
|
+
try { enabled = (options.readKillSwitch || require('../settings/autopilot.js').readKillSwitch)(env).enabled === true; } catch { enabled = false; }
|
|
113
|
+
if (!enabled) return attended('switch-off');
|
|
114
|
+
return { attendance: 'unattended', source: 'run-record', run: record.runId };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The CLI handler: exit 0 with the answer, 2 for a usage fault. */
|
|
118
|
+
function handleAttendanceCommand(options = {}) {
|
|
119
|
+
const json = Boolean(options.json);
|
|
120
|
+
const usage = (detail ) => {
|
|
121
|
+
if (json) console.log(JSON.stringify({ schema: 1, error: 'usage', detail }));
|
|
122
|
+
else console.error(`doflow attendance: error: ${detail}`);
|
|
123
|
+
process.exitCode = 2;
|
|
124
|
+
return 2;
|
|
125
|
+
};
|
|
126
|
+
const extra = ((options.flags || []) ).map((flag) => flag.split('=')[0]).find((flag) => !ALLOWED_FLAGS.has(flag));
|
|
127
|
+
if (extra !== undefined) return usage(`unsupported flag ${extra}; only --task-id, --run and --json apply`);
|
|
128
|
+
if (typeof options.taskId !== 'string' || !options.taskId) return usage('--task-id <row slug> is required');
|
|
129
|
+
if (options.run !== undefined && !RUN_ID.test(String(options.run))) return usage('--run must be rl-<yyyymmdd>-<4 crockford characters>');
|
|
130
|
+
const answer = resolveAttendance({ taskId: options.taskId, run: options.run, env: options.env, cwd: options.cwd });
|
|
131
|
+
if (json) console.log(JSON.stringify({ schema: 1, ...answer }));
|
|
132
|
+
else console.log(answer.attendance === 'unattended' ? `unattended ${answer.source}${answer.run ? ` ${answer.run}` : ''}` : `attended ${answer.reason}`);
|
|
133
|
+
process.exitCode = 0;
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { resolveAttendance, handleAttendanceCommand };
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Git facts gathering for roadmap loop (feature 108, IC-003, IC-004; FR-002).
|
|
5
|
+
* Reads only — executes allowed git read verbs via execFileSync, without side effects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const { execFileSync } = require('node:child_process');
|
|
11
|
+
const { createHash } = require('node:crypto');
|
|
12
|
+
const { parsePlan } = require('../scaffold/artifacts.js');
|
|
13
|
+
const { planVerdict } = require('../task-graph.js');
|
|
14
|
+
const { CapabilityRouter } = require('../capability-router.js');
|
|
15
|
+
const { deriveStatus, check } = require('./rows.js');
|
|
16
|
+
const { validateFindings } = require('./gates.js');
|
|
17
|
+
const defaultExecFileSync = execFileSync;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
function sha256Digest(content ) {
|
|
33
|
+
return `sha256:${createHash('sha256').update(content).digest('hex')}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function qualifiedBranch(name ) {
|
|
37
|
+
return name.startsWith('refs/') ? name : `refs/heads/${name}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gathers git facts for roadmap status derivation and checking.
|
|
42
|
+
* Uses only allowed read verbs: rev-list, rev-parse, for-each-ref.
|
|
43
|
+
*/
|
|
44
|
+
function gatherGitFacts({
|
|
45
|
+
root,
|
|
46
|
+
developBranch = 'develop',
|
|
47
|
+
developHead: knownDevelopHead,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
) {
|
|
54
|
+
const execOptions = {
|
|
55
|
+
cwd: root,
|
|
56
|
+
encoding: 'utf8',
|
|
57
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const unreadable = [];
|
|
61
|
+
let developHead = '';
|
|
62
|
+
let developBranchReadable = false;
|
|
63
|
+
|
|
64
|
+
// Always the branch: an unqualified name resolves a tag of the same name first.
|
|
65
|
+
const developRef = qualifiedBranch(developBranch);
|
|
66
|
+
try {
|
|
67
|
+
developHead = knownDevelopHead && /^[0-9a-f]{40}$/.test(knownDevelopHead) ? knownDevelopHead
|
|
68
|
+
: execFileSync('git', ['-C', root, 'rev-parse', '--verify', developRef], execOptions).trim();
|
|
69
|
+
developBranchReadable = true;
|
|
70
|
+
} catch {
|
|
71
|
+
unreadable.push('develop-branch');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 1. Read first-parent merges on developBranch
|
|
75
|
+
const merges = [];
|
|
76
|
+
if (developBranchReadable) {
|
|
77
|
+
try {
|
|
78
|
+
const revListOut = execFileSync('git', [
|
|
79
|
+
'-C',
|
|
80
|
+
root,
|
|
81
|
+
'rev-list',
|
|
82
|
+
'--no-commit-header',
|
|
83
|
+
'--first-parent',
|
|
84
|
+
'--merges',
|
|
85
|
+
'--format=%H %P%x00%(trailers:key=DoFlow-Row,valueonly)',
|
|
86
|
+
developRef,
|
|
87
|
+
], execOptions);
|
|
88
|
+
|
|
89
|
+
const lines = revListOut.split('\n');
|
|
90
|
+
let currentRecord = null;
|
|
91
|
+
|
|
92
|
+
for (const rawLine of lines) {
|
|
93
|
+
if (/^[0-9a-f]{40}( |\0|$)/.test(rawLine)) {
|
|
94
|
+
const nulIdx = rawLine.indexOf('\0');
|
|
95
|
+
let commitPart = rawLine;
|
|
96
|
+
let trailerPart = '';
|
|
97
|
+
if (nulIdx !== -1) {
|
|
98
|
+
commitPart = rawLine.slice(0, nulIdx);
|
|
99
|
+
trailerPart = rawLine.slice(nulIdx + 1);
|
|
100
|
+
}
|
|
101
|
+
const parts = commitPart.trim().split(/\s+/);
|
|
102
|
+
const sha = parts[0];
|
|
103
|
+
const parents = parts.slice(1);
|
|
104
|
+
currentRecord = {
|
|
105
|
+
sha,
|
|
106
|
+
parents,
|
|
107
|
+
rowTrailer: trailerPart.trim() || null,
|
|
108
|
+
};
|
|
109
|
+
merges.push(currentRecord);
|
|
110
|
+
} else if (currentRecord) {
|
|
111
|
+
const continuation = rawLine.trim();
|
|
112
|
+
if (continuation) {
|
|
113
|
+
if (currentRecord.rowTrailer) {
|
|
114
|
+
currentRecord.rowTrailer = `${currentRecord.rowTrailer}\n${continuation}`;
|
|
115
|
+
} else {
|
|
116
|
+
currentRecord.rowTrailer = continuation;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
unreadable.push('merges');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 2. Read local branch names
|
|
127
|
+
const branches = [];
|
|
128
|
+
try {
|
|
129
|
+
const branchesOut = execFileSync('git', [
|
|
130
|
+
'-C',
|
|
131
|
+
root,
|
|
132
|
+
'for-each-ref',
|
|
133
|
+
'--format=%(refname:short)',
|
|
134
|
+
'refs/heads/',
|
|
135
|
+
], execOptions);
|
|
136
|
+
for (const rawBranch of branchesOut.split('\n')) {
|
|
137
|
+
const b = rawBranch.trim();
|
|
138
|
+
if (b) branches.push(b);
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
unreadable.push('branches');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
developHead,
|
|
146
|
+
merges,
|
|
147
|
+
branches,
|
|
148
|
+
unreadable,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** CommonMark fences and exact-length backtick spans; unmatched spans remain prose. */
|
|
153
|
+
function countMarkers(text ) {
|
|
154
|
+
const prose = [];
|
|
155
|
+
let fence = null;
|
|
156
|
+
for (const line of text.split('\n')) {
|
|
157
|
+
const match = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
158
|
+
if (fence) {
|
|
159
|
+
if (match && match[1][0] === fence.char && match[1].length >= fence.length && /^\s*$/.test(match[2])) fence = null;
|
|
160
|
+
// Keep a block boundary: inline spans cannot cross a fenced block.
|
|
161
|
+
prose.push('\0');
|
|
162
|
+
} else if (match && (match[1][0] !== '`' || !match[2].includes('`'))) {
|
|
163
|
+
fence = { char: match[1][0], length: match[1].length };
|
|
164
|
+
prose.push('\0');
|
|
165
|
+
} else prose.push(line);
|
|
166
|
+
}
|
|
167
|
+
const visible = prose.join('\n');
|
|
168
|
+
let count = 0;
|
|
169
|
+
for (let i = 0; i < visible.length;) {
|
|
170
|
+
if (visible[i] === '\\' && visible[i + 1] === '`') { i += 2; continue; }
|
|
171
|
+
if (visible[i] === '`') {
|
|
172
|
+
let end = i;
|
|
173
|
+
while (visible[end] === '`') end++;
|
|
174
|
+
const length = end - i;
|
|
175
|
+
let cursor = end;
|
|
176
|
+
let closing = -1;
|
|
177
|
+
while (cursor < visible.length && visible[cursor] !== '\0') {
|
|
178
|
+
if (visible[cursor] !== '`') { cursor++; continue; }
|
|
179
|
+
let after = cursor;
|
|
180
|
+
while (visible[after] === '`') after++;
|
|
181
|
+
if (after - cursor === length) { closing = after; break; }
|
|
182
|
+
cursor = after;
|
|
183
|
+
}
|
|
184
|
+
i = closing < 0 ? end : closing;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (visible.startsWith('[NEEDS CLARIFICATION', i)) count++;
|
|
188
|
+
i++;
|
|
189
|
+
}
|
|
190
|
+
return count;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Read only the selected gate's sources. Every derived fact and raw source binds its bytes.
|
|
203
|
+
* root is the feature checkout; mainRoot and registryRoot may point at the main checkout/install.
|
|
204
|
+
* execFileSync/fsImpl are test seams; production uses only literal, allow-listed git vectors.
|
|
205
|
+
*/
|
|
206
|
+
function gatherGateFacts(options ) {
|
|
207
|
+
const { root, artifactRoot, gateId, policy, row } = options;
|
|
208
|
+
const fsImpl = options.fsImpl || fs;
|
|
209
|
+
const execFileSync = options.execFileSync || defaultExecFileSync;
|
|
210
|
+
const execOptions = { cwd: root, encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] };
|
|
211
|
+
const result = [];
|
|
212
|
+
const predicates = policy?.classes?.[row?.class || 'feature']?.[gateId]?.predicates || [];
|
|
213
|
+
const uses = (predicate ) => predicates.includes(predicate);
|
|
214
|
+
const add = (name , value , locator , bytes , unreadable ) => {
|
|
215
|
+
const contentDigest = sha256Digest(bytes === undefined ? JSON.stringify(value) : bytes);
|
|
216
|
+
const fact = { name, value, locator,
|
|
217
|
+
sha256: unreadable ? sha256Digest(JSON.stringify({ unreadable, contentDigest })) : contentDigest };
|
|
218
|
+
if (unreadable) fact.unreadable = unreadable;
|
|
219
|
+
result.push(fact);
|
|
220
|
+
return fact;
|
|
221
|
+
};
|
|
222
|
+
const failure = (error ) => String(error?.code || 'unreadable');
|
|
223
|
+
const read = (candidates ) => {
|
|
224
|
+
for (const file of candidates) {
|
|
225
|
+
try { const bytes = fsImpl.readFileSync(file); return { locator: file, bytes, text: String(bytes) }; }
|
|
226
|
+
catch (error ) {
|
|
227
|
+
if (error?.code !== 'ENOENT') return { locator: file, bytes: '', unreadable: failure(error) };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { locator: candidates[0], bytes: '', unreadable: 'ENOENT' };
|
|
231
|
+
};
|
|
232
|
+
const source = (name , input ) => add(`source:${name}`, null, input.locator, input.bytes, input.unreadable);
|
|
233
|
+
add('row', row || null, `${options.mainRoot || root}/agent-docs/roadmap/rows.json#${row?.id || 'unknown'}`);
|
|
234
|
+
if (!row) return result;
|
|
235
|
+
|
|
236
|
+
let plan = null;
|
|
237
|
+
if (uses('plan-parses') || uses('plan-within-write-set') || (uses('protected-paths') && gateId === 'gate-a')) {
|
|
238
|
+
const input = read([path.join(artifactRoot, 'plan.md'), path.join(artifactRoot, 'planning/plan.md')]);
|
|
239
|
+
let value = { ok: false, files: [] };
|
|
240
|
+
if (!input.unreadable) {
|
|
241
|
+
try {
|
|
242
|
+
const parsed = parsePlan(input.text);
|
|
243
|
+
value = { ok: planVerdict(input.text).ok, files: parsed.tasks.flatMap((task ) => task.files) };
|
|
244
|
+
} catch { /* The plan-parses predicate denies; no guessed tasks. */ }
|
|
245
|
+
}
|
|
246
|
+
plan = add('plan', value, input.locator, input.bytes, input.unreadable);
|
|
247
|
+
}
|
|
248
|
+
if (uses('plan-verification-absent')) {
|
|
249
|
+
// C6: a `doflow-verification` block replaces the verify commands, so its presence (even a
|
|
250
|
+
// malformed one) is the fact. No plan at all is absent; any other read failure is unreadable.
|
|
251
|
+
const input = read([path.join(artifactRoot, 'plan.md'), path.join(artifactRoot, 'planning/plan.md')]);
|
|
252
|
+
const absent = input.unreadable === 'ENOENT';
|
|
253
|
+
let present = absent ? false : null;
|
|
254
|
+
if (!input.unreadable) {
|
|
255
|
+
const { parsePlanOverride } = require('../command-detect.js');
|
|
256
|
+
present = ((r ) => r.override !== null || r.errors.length > 0)(parsePlanOverride(input.text));
|
|
257
|
+
}
|
|
258
|
+
add('plan-verification-block', { present }, input.locator, input.bytes, absent ? undefined : input.unreadable);
|
|
259
|
+
}
|
|
260
|
+
for (const artifact of ['requirement', 'design', 'plan']) {
|
|
261
|
+
if (!uses(`markers-zero:${artifact}`)) continue;
|
|
262
|
+
const candidates = artifact === 'requirement' ? ['intention/requirement.md', 'requirement.md']
|
|
263
|
+
: artifact === 'design' ? ['design/design.md', 'design.md'] : ['plan.md', 'planning/plan.md'];
|
|
264
|
+
const input = read(candidates.map((entry) => path.join(artifactRoot, entry)));
|
|
265
|
+
add(`markers:${artifact}`, input.unreadable ? null : countMarkers(input.text), input.locator, input.bytes, input.unreadable);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let developHead = options.developHead;
|
|
269
|
+
let featureHead = options.featureHead;
|
|
270
|
+
if (uses('protected-paths') || uses('verify-pass-on-head') || uses('findings-valid') || uses('dispositions-backed')) {
|
|
271
|
+
let unreadable ;
|
|
272
|
+
try {
|
|
273
|
+
if (!developHead) developHead = execFileSync('git', ['rev-parse', '--verify', qualifiedBranch(options.developBranch || 'develop')], execOptions).trim();
|
|
274
|
+
if (!featureHead) {
|
|
275
|
+
// The calling checkout's HEAD may be develop itself (a diff of develop against develop is
|
|
276
|
+
// empty), so the head is only ever the feature branch's tip.
|
|
277
|
+
const branch = options.featureBranch || (row.slug ? `feat/${row.slug}` : null);
|
|
278
|
+
try {
|
|
279
|
+
if (!branch) throw new Error('no feature branch');
|
|
280
|
+
featureHead = execFileSync('git', ['rev-parse', '--verify', `refs/heads/${branch}`], execOptions).trim();
|
|
281
|
+
} catch { throw Object.assign(new Error('no feature branch'), { code: 'no-feature-branch' }); }
|
|
282
|
+
}
|
|
283
|
+
if (!/^[0-9a-f]{40}$/.test(developHead) || !/^[0-9a-f]{40}$/.test(featureHead)) unreadable = 'invalid-head';
|
|
284
|
+
} catch (error ) { unreadable = failure(error); }
|
|
285
|
+
add('heads', { developHead: developHead || null, featureHead: featureHead || null }, `${root}#heads`, undefined, unreadable);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (uses('protected-paths')) {
|
|
289
|
+
const locator = `${root}#diff:${developHead}...${featureHead}`;
|
|
290
|
+
const changes = [];
|
|
291
|
+
const packages = {};
|
|
292
|
+
let unreadable ;
|
|
293
|
+
try {
|
|
294
|
+
const text = execFileSync('git', ['diff', '--name-status', '-z', `${developHead}...${featureHead}`, '--'], execOptions);
|
|
295
|
+
add('source:diff', null, locator, text);
|
|
296
|
+
const fields = text.split('\0');
|
|
297
|
+
if (fields[fields.length - 1] === '') fields.pop();
|
|
298
|
+
for (let i = 0; i < fields.length;) {
|
|
299
|
+
const status = fields[i++];
|
|
300
|
+
if (!/^(?:[ADMTUXB]|[RC][0-9]{1,3})$/.test(status) || !fields[i]) throw new Error('invalid diff');
|
|
301
|
+
if (/^[RC]/.test(status)) {
|
|
302
|
+
const oldPath = fields[i++];
|
|
303
|
+
const file = fields[i++];
|
|
304
|
+
if (!file) throw new Error('invalid rename');
|
|
305
|
+
changes.push({ status, oldPath, path: file });
|
|
306
|
+
} else changes.push({ status, path: fields[i++] });
|
|
307
|
+
}
|
|
308
|
+
const base = execFileSync('git', ['show', `${developHead}:package.json`], execOptions);
|
|
309
|
+
add('source:package-base', null, `${root}#${developHead}:package.json`, base);
|
|
310
|
+
const head = execFileSync('git', ['show', `${featureHead}:package.json`], execOptions);
|
|
311
|
+
add('source:package-head', null, `${root}#${featureHead}:package.json`, head);
|
|
312
|
+
packages.base = JSON.parse(base);
|
|
313
|
+
packages.head = JSON.parse(head);
|
|
314
|
+
} catch (error ) { unreadable = failure(error); }
|
|
315
|
+
const addedGuards = [];
|
|
316
|
+
if (gateId === 'gate-a' && !unreadable) {
|
|
317
|
+
for (const file of [...(row.writeSet || []), ...(plan?.value.files || [])]) {
|
|
318
|
+
if (!file.startsWith(policy.protected.guardDir) || file.endsWith('/')) continue;
|
|
319
|
+
if (changes.some((change) => change.path === file)) continue;
|
|
320
|
+
// A missing guard at the base is a planned addition; status 1/128 is only accepted after
|
|
321
|
+
// a readable tree listing proves absence, never by treating a failed command as absence.
|
|
322
|
+
try {
|
|
323
|
+
const listed = execFileSync('git', ['ls-files', '--with-tree', developHead, '-z', '--', file], execOptions);
|
|
324
|
+
add(`source:guard:${file}`, null, `${root}#${developHead}:${file}`, listed);
|
|
325
|
+
if (!listed.split('\0').includes(file)) addedGuards.push(file);
|
|
326
|
+
} catch (error ) { unreadable = failure(error); }
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
add('protected-paths', { changes, packages, writeSet: row.writeSet || [], planFiles: plan?.value.files || [], addedGuards }, locator, undefined, unreadable);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (uses('needs-merged')) {
|
|
333
|
+
const gathered = options.gitFacts || gatherGitFacts({ root: options.mainRoot || root, developBranch: options.developBranch || 'develop' });
|
|
334
|
+
const unreadable = gathered.unreadable || [];
|
|
335
|
+
const declaredRows = options.rows || [row];
|
|
336
|
+
const statuses = deriveStatus(declaredRows, gathered);
|
|
337
|
+
const integrity = check({ rows: declaredRows, gitFacts: gathered });
|
|
338
|
+
add('needs', (row.needs || []).map((id ) => ({ id, status: integrity.offendingRowIds.includes(id) ? 'not-started' : statuses.rowStatus[id] || 'not-started' })),
|
|
339
|
+
`${options.mainRoot || root}#develop:${unreadable.length ? unreadable.join(',') : 'merges'}`,
|
|
340
|
+
JSON.stringify(gathered), unreadable.length ? `unreadable:${unreadable.join(',')}` : undefined);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (uses('requires-reachable')) {
|
|
344
|
+
const registryRoot = options.registryRoot || path.resolve(__dirname, '../../..');
|
|
345
|
+
const input = read([path.join(registryRoot, 'core/registry/capabilities.json')]);
|
|
346
|
+
const values = [];
|
|
347
|
+
let unreadable = input.unreadable;
|
|
348
|
+
const env = options.env || process.env;
|
|
349
|
+
const binaryChecker = (binary ) => {
|
|
350
|
+
const candidates = binary.includes('/') || binary.includes('\\') ? [path.resolve(root, binary)]
|
|
351
|
+
: String(env.PATH || '').split(path.delimiter).filter(Boolean).flatMap((dir ) => {
|
|
352
|
+
const suffixes = process.platform === 'win32' ? String(env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';') : [''];
|
|
353
|
+
return suffixes.map((suffix ) => path.join(dir, `${binary}${suffix}`));
|
|
354
|
+
});
|
|
355
|
+
return candidates.some((file ) => {
|
|
356
|
+
try { fsImpl.accessSync(file, fs.constants.X_OK); return fsImpl.statSync(file).isFile(); } catch { return false; }
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
if (!unreadable) {
|
|
360
|
+
try {
|
|
361
|
+
const capabilities = JSON.parse(input.text).capabilities;
|
|
362
|
+
if (!capabilities || typeof capabilities !== 'object' || Array.isArray(capabilities)) throw new Error('invalid capabilities');
|
|
363
|
+
const router = new CapabilityRouter({ repoRoot: registryRoot, projectRoot: root, fsImpl, capabilities, routes: {}, binaryChecker });
|
|
364
|
+
for (const id of row.requires || []) {
|
|
365
|
+
const declared = Object.hasOwn(capabilities, id);
|
|
366
|
+
const resolved = declared ? router.resolveCapability(id, { deepCheck: false }) : null;
|
|
367
|
+
values.push({ id, declared, reachable: Boolean(resolved?.provider && resolved.status === 'HEALTHY'), provider: resolved?.provider?.id || null });
|
|
368
|
+
}
|
|
369
|
+
} catch (error ) { unreadable = failure(error); }
|
|
370
|
+
}
|
|
371
|
+
source('capabilities', input);
|
|
372
|
+
add('requires', values, `${input.locator}#requires`, undefined, unreadable);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (uses('verify-pass-on-head')) {
|
|
376
|
+
// The record lives where `verify --action report` appends it (IC-007), not in the feature checkout.
|
|
377
|
+
const { verificationRunsRoot, verificationRunRecordFile } = require('../verification/engine.js');
|
|
378
|
+
const input = read([verificationRunRecordFile(verificationRunsRoot(root), row.slug)]);
|
|
379
|
+
let records = [];
|
|
380
|
+
let unreadable = input.unreadable === 'ENOENT' ? undefined : input.unreadable;
|
|
381
|
+
if (!input.unreadable) {
|
|
382
|
+
try {
|
|
383
|
+
records = input.text.split('\n').filter((line ) => line.trim()).map((line ) => JSON.parse(line));
|
|
384
|
+
if (records.some((record ) => !record || typeof record !== 'object' || Array.isArray(record))) throw new Error('invalid run');
|
|
385
|
+
} catch { unreadable = 'invalid-jsonl'; }
|
|
386
|
+
}
|
|
387
|
+
add('verification', records, input.locator, input.bytes, unreadable);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
let findings = null;
|
|
391
|
+
let sameFamily = 'warn';
|
|
392
|
+
let preset = 'auto';
|
|
393
|
+
let authors = [];
|
|
394
|
+
if (predicates.some((predicate ) => ['findings-valid', 'no-dropped-findings', 'dispositions-backed', 'blockers-zero'].includes(predicate))) {
|
|
395
|
+
// The project's `review.sameFamily` (119 L-D3a) and `review.preset` (C-2), read once; the gate judges the fact, never the settings.
|
|
396
|
+
let settingsLocator = `${root}/.doflow/settings.json#review.sameFamily`;
|
|
397
|
+
let settingsUnreadable ;
|
|
398
|
+
try {
|
|
399
|
+
const settings = require('../settings/resolve.js').loadSettings({ projectRoot: root, env: options.env || process.env, fsImpl });
|
|
400
|
+
const resolved = settings.get('review.sameFamily');
|
|
401
|
+
if (resolved.value === 'refuse') sameFamily = 'refuse';
|
|
402
|
+
// C-2: the project's `review.preset` is the floor gate-b holds the recorded protocol to (`auto` needs the diff).
|
|
403
|
+
if (typeof settings.get('review.preset').value === 'string') preset = settings.get('review.preset').value;
|
|
404
|
+
if (resolved.origin?.layer !== 'default' && typeof resolved.origin?.source === 'string') settingsLocator = resolved.origin.source;
|
|
405
|
+
// C-6: an invalid project or local candidate silently falls back to the default, so the fact is unreadable.
|
|
406
|
+
const findingsLib = require('../review/findings.js');
|
|
407
|
+
const invalid = findingsLib.invalidProjectCandidate(settings.get('review.sameFamily')) ?? findingsLib.invalidPresetCandidate(settings.get('review.preset'));
|
|
408
|
+
if (invalid) { settingsLocator = invalid.source; settingsUnreadable = 'settings-unavailable'; }
|
|
409
|
+
} catch { settingsUnreadable = 'settings-unavailable'; }
|
|
410
|
+
add('review-settings', { sameFamily, preset }, settingsLocator, undefined, settingsUnreadable);
|
|
411
|
+
|
|
412
|
+
// 120 D.4: authoring-identities fact
|
|
413
|
+
let authorsLocator = `${root}/.doflow/state/dispatch/${row.slug}.json`;
|
|
414
|
+
let authorsUnreadable ;
|
|
415
|
+
try {
|
|
416
|
+
const { authoringIdentities } = require('../review/identity.js');
|
|
417
|
+
const { storeFileFor, readDispatch } = require('../dispatch/store.js');
|
|
418
|
+
const { projectRoot } = require('../lifecycle/root.js');
|
|
419
|
+
const pRoot = projectRoot(root);
|
|
420
|
+
authorsLocator = storeFileFor(pRoot, row.slug);
|
|
421
|
+
if (fsImpl.existsSync(authorsLocator)) {
|
|
422
|
+
readDispatch(authorsLocator, fsImpl);
|
|
423
|
+
}
|
|
424
|
+
authors = authoringIdentities({
|
|
425
|
+
slug: row.slug,
|
|
426
|
+
cwd: root,
|
|
427
|
+
env: options.env || process.env,
|
|
428
|
+
fsImpl,
|
|
429
|
+
});
|
|
430
|
+
} catch {
|
|
431
|
+
authorsUnreadable = 'dispatch-unreadable';
|
|
432
|
+
}
|
|
433
|
+
add('authoring-identities', authors, authorsLocator, undefined, authorsUnreadable);
|
|
434
|
+
|
|
435
|
+
const input = read([path.join(artifactRoot, 'review/findings.json')]);
|
|
436
|
+
if (!input.unreadable) {
|
|
437
|
+
try { findings = JSON.parse(input.text); } catch { findings = input.text; }
|
|
438
|
+
}
|
|
439
|
+
add('findings', findings, input.locator, input.bytes, input.unreadable === 'ENOENT' ? undefined : input.unreadable);
|
|
440
|
+
}
|
|
441
|
+
if (uses('dispositions-backed')) {
|
|
442
|
+
const ancestors = {};
|
|
443
|
+
let decisions = [];
|
|
444
|
+
let unreadable ;
|
|
445
|
+
const converged = Array.isArray(findings?.converged) ? findings.converged : [];
|
|
446
|
+
for (const finding of converged) if (finding?.status === 'fixed' && /^[0-9a-f]{40}$/.test(finding.fixedIn)) {
|
|
447
|
+
try { execFileSync('git', ['merge-base', '--is-ancestor', finding.fixedIn, featureHead], execOptions); ancestors[finding.fixedIn] = true; }
|
|
448
|
+
catch (error ) {
|
|
449
|
+
ancestors[finding.fixedIn] = false;
|
|
450
|
+
if (error?.status !== 1) unreadable = `${failure(error)}:ancestor:${finding.fixedIn}`;
|
|
451
|
+
}
|
|
452
|
+
add(`source:ancestor:${finding.fixedIn}`, ancestors[finding.fixedIn], `${root}#ancestor:${finding.fixedIn}:${featureHead}`);
|
|
453
|
+
}
|
|
454
|
+
if (converged.some((finding ) => finding?.status === 'user-accepted')) {
|
|
455
|
+
const input = read([path.join(artifactRoot, 'decisions/register.json')]);
|
|
456
|
+
source('decisions', input);
|
|
457
|
+
// A register not created yet holds no decisions; any other read or parse failure is unreadable.
|
|
458
|
+
if (input.unreadable === 'ENOENT') decisions = [];
|
|
459
|
+
else if (input.unreadable) unreadable = `${input.unreadable}:${input.locator}`;
|
|
460
|
+
else {
|
|
461
|
+
try {
|
|
462
|
+
const register = JSON.parse(input.text);
|
|
463
|
+
if (register.version !== 1 || !Array.isArray(register.decisions) || !Number.isInteger(register.nextId)) throw new Error('invalid register');
|
|
464
|
+
decisions = register.decisions;
|
|
465
|
+
} catch { unreadable = `invalid-register:${input.locator}`; }
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// The schema validator is deliberately pure and shared by gathering and gate evaluation.
|
|
469
|
+
add('dispositions', { ancestors, decisions, schemaValid: validateFindings(findings, { sameFamily, preset, authors }).ok }, `${artifactRoot}/review/findings.json#dispositions`, undefined, unreadable);
|
|
470
|
+
}
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
module.exports = {
|
|
475
|
+
gatherGitFacts,
|
|
476
|
+
sha256Digest,
|
|
477
|
+
countMarkers,
|
|
478
|
+
gatherGateFacts,
|
|
479
|
+
};
|