@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,617 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Impacted test selector (feature 089, IC-009, DD-7, DD-8; feature 090, IC-001, IC-002).
|
|
7
|
+
*
|
|
8
|
+
* Statically discovers tests impacted by changed files through relative import/require graph
|
|
9
|
+
* traversal, non-test helper graph inclusion, CLI hub cutting and verb scoping, wildcard
|
|
10
|
+
* spawn-test reachability, guard directory inclusion, and unknown-file fallback to the full
|
|
11
|
+
* test suite.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require('node:path');
|
|
15
|
+
const nodeFs = require('node:fs');
|
|
16
|
+
const { conventionFor, testFilesFor, GUARD_DIR } = require('./test-selection.js');
|
|
17
|
+
|
|
18
|
+
const DISPATCHER_RE = /bin\/doflow|doflow-run(?![a-z0-9-])|(['"])bin\1\s*,\s*(['"])doflow(?:\.js)?\2/;
|
|
19
|
+
const CP_FUNCS = /\b(?:spawn|spawnSync|execSync|execFile|execFileSync|fork)\s*\(/;
|
|
20
|
+
const CP_TOKEN = ['child', 'process'].join('_');
|
|
21
|
+
const CP_RE = new RegExp(CP_TOKEN);
|
|
22
|
+
|
|
23
|
+
/** Normalises a repository-relative path: forward slashes, no leading `./`, no trailing `/`. */
|
|
24
|
+
function normalize(file ) {
|
|
25
|
+
return String(file).replace(/\\/g, '/').replace(/^(?:\.\/)+/, '').replace(/\/+$/, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function statOf(fsImpl , file ) {
|
|
29
|
+
try {
|
|
30
|
+
return fsImpl.statSync(file);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Determines whether a test or helper file spawns the CLI / dispatcher. */
|
|
37
|
+
function isSpawnTest(text ) {
|
|
38
|
+
if (!DISPATCHER_RE.test(text)) return false;
|
|
39
|
+
if (CP_FUNCS.test(text)) return true;
|
|
40
|
+
if (CP_RE.test(text) && /\bexec\s*\(/.test(text)) return true;
|
|
41
|
+
if (/(?:^|[^.\w$])exec\s*\(/.test(text)) return true;
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Extracts static relative specifiers starting with `.` from file text. */
|
|
46
|
+
function extractRelativeSpecifiers(text ) {
|
|
47
|
+
const specs = new Set ();
|
|
48
|
+
// call forms: require and dynamic import with a literal relative specifier
|
|
49
|
+
const callPattern = /\b(?:require|import)\s*\(\s*(['"`])(\.[^'"`]*)\1\s*\)/g;
|
|
50
|
+
for (const match of text.matchAll(callPattern)) {
|
|
51
|
+
specs.add(match[2]);
|
|
52
|
+
}
|
|
53
|
+
// static import and re-export forms with a from clause
|
|
54
|
+
const fromPattern = /\b(?:import|export)\b(?:(?!import|export)[^;])*?\bfrom\s*(['"`])(\.[^'"`]*)\1/g;
|
|
55
|
+
for (const match of text.matchAll(fromPattern)) {
|
|
56
|
+
specs.add(match[2]);
|
|
57
|
+
}
|
|
58
|
+
// side-effect import with a literal specifier
|
|
59
|
+
const sideImportPattern = /\bimport\s+(['"`])(\.[^'"`]*)\1/g;
|
|
60
|
+
for (const match of text.matchAll(sideImportPattern)) {
|
|
61
|
+
specs.add(match[2]);
|
|
62
|
+
}
|
|
63
|
+
return [...specs];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Resolves a relative specifier against `fromFile` checking exact, .ts, .js, /index.ts, /index.js. */
|
|
67
|
+
function resolveRelativeSpecifier(fromFile , spec , root , fsImpl ) {
|
|
68
|
+
if (!spec.startsWith('.')) return null;
|
|
69
|
+
const fromDir = path.dirname(fromFile);
|
|
70
|
+
const targetBase = normalize(path.join(fromDir, spec));
|
|
71
|
+
|
|
72
|
+
const candidates = [
|
|
73
|
+
targetBase,
|
|
74
|
+
`${targetBase}.ts`,
|
|
75
|
+
`${targetBase}.js`,
|
|
76
|
+
`${targetBase}/index.ts`,
|
|
77
|
+
`${targetBase}/index.js`,
|
|
78
|
+
];
|
|
79
|
+
if (targetBase.endsWith('.js')) {
|
|
80
|
+
candidates.push(`${targetBase.slice(0, -3)}.ts`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const candidate of candidates) {
|
|
84
|
+
const absPath = path.join(root, candidate);
|
|
85
|
+
try {
|
|
86
|
+
const stat = fsImpl.statSync(absPath);
|
|
87
|
+
if (stat && stat.isFile()) {
|
|
88
|
+
return normalize(candidate);
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// not found
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Checks whether a path belongs to the monitored module graph: src/**, bin/**, bench/**, or test/** non-test helper. */
|
|
98
|
+
function isGraphNode(file , convention ) {
|
|
99
|
+
return (
|
|
100
|
+
file.startsWith('src/') ||
|
|
101
|
+
file.startsWith('bin/') ||
|
|
102
|
+
file.startsWith('bench/') ||
|
|
103
|
+
(file.startsWith('test/') && !convention.isTest(file))
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Checks whether a non-code file is guard-covered and should not trigger unknown-file expansion. */
|
|
108
|
+
function isGuardCoveredPath(file , hasGuardDir ) {
|
|
109
|
+
if (!hasGuardDir) return false;
|
|
110
|
+
return (
|
|
111
|
+
file === 'CHANGELOG.md' ||
|
|
112
|
+
file === 'AGENTS.md' ||
|
|
113
|
+
file === 'CLAUDE.md' ||
|
|
114
|
+
file.startsWith('docs/') ||
|
|
115
|
+
file.startsWith('agent-docs/') ||
|
|
116
|
+
file.startsWith('.doflow/')
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Parses CLI dispatch hubs and lazy handler functions to map CLI verbs to their target implementation modules. */
|
|
121
|
+
function parseCliVerbMap(root , fsImpl ) {
|
|
122
|
+
const verbToModules = new Map ();
|
|
123
|
+
const addVerbMod = (v , m ) => {
|
|
124
|
+
if (!m) return;
|
|
125
|
+
if (!verbToModules.has(v)) verbToModules.set(v, new Set());
|
|
126
|
+
verbToModules.get(v) .add(m);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
for (const hub of ['src/cli/runtime-commands.js', 'src/cli/index.js']) {
|
|
130
|
+
let text = '';
|
|
131
|
+
try {
|
|
132
|
+
text = fsImpl.readFileSync(path.join(root, hub), 'utf8');
|
|
133
|
+
} catch {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const symToFile = new Map ();
|
|
138
|
+
const reqRe = /(?:const|let|var)\s+(\{[^}]+\}|\w+)\s*=\s*require\(\s*(['"])(\.[^'"]+)\2\s*\)/g;
|
|
139
|
+
for (const m of text.matchAll(reqRe)) {
|
|
140
|
+
const target = resolveRelativeSpecifier(hub, m[3], root, fsImpl);
|
|
141
|
+
if (!target) continue;
|
|
142
|
+
const lhs = m[1].trim();
|
|
143
|
+
if (lhs.startsWith('{')) {
|
|
144
|
+
for (const part of lhs.slice(1, -1).split(',')) {
|
|
145
|
+
const piece = part.trim();
|
|
146
|
+
if (!piece) continue;
|
|
147
|
+
const [orig, alias] = piece.split(':').map((s ) => s.trim());
|
|
148
|
+
symToFile.set(alias || orig, target);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
symToFile.set(lhs, target);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const fnRe = /function\s+(\w+)\s*\([^)]*\)[^{]*\{\s*return\s+require\(\s*(['"])(\.[^'"]+)\2\s*\)/g;
|
|
156
|
+
for (const m of text.matchAll(fnRe)) {
|
|
157
|
+
const target = resolveRelativeSpecifier(hub, m[3], root, fsImpl);
|
|
158
|
+
if (target) symToFile.set(m[1], target);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const caseRe = /case\s+(['"])([a-z0-9-]+)\1\s*:\s*return\s+(\w+)\s*\(/g;
|
|
162
|
+
for (const m of text.matchAll(caseRe)) {
|
|
163
|
+
const verb = m[2];
|
|
164
|
+
const fn = m[3];
|
|
165
|
+
if (symToFile.has(fn)) addVerbMod(verb, symToFile.get(fn) );
|
|
166
|
+
const helperRe = new RegExp('function\\s+' + fn + '\\b[\\s\\S]*?\\n}', 'm');
|
|
167
|
+
const hm = text.match(helperRe);
|
|
168
|
+
if (hm) {
|
|
169
|
+
for (const [sym, f] of symToFile.entries()) {
|
|
170
|
+
if (new RegExp('\\b' + sym + '\\b').test(hm[0])) addVerbMod(verb, f);
|
|
171
|
+
}
|
|
172
|
+
for (const rm of hm[0].matchAll(/require\(\s*(['"])(\.[^'"]+)\1\s*\)/g)) {
|
|
173
|
+
addVerbMod(verb, resolveRelativeSpecifier(hub, rm[2], root, fsImpl));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const cmdBlock = text.match(/\bconst\s+COMMANDS\b[\s\S]*?=\s*\{([\s\S]*?)\};/);
|
|
179
|
+
if (cmdBlock) {
|
|
180
|
+
for (const m of cmdBlock[1].matchAll(/(?:['"]([a-z0-9-]+)['"]|([a-z0-9-]+))\s*:\s*(\w+)/g)) {
|
|
181
|
+
const verb = m[1] || m[2];
|
|
182
|
+
const sym = m[3];
|
|
183
|
+
if (symToFile.has(sym)) addVerbMod(verb, symToFile.get(sym) );
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return verbToModules;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Strips handle*Command function bodies when extracting general library dependencies of a module that also exports library functions. */
|
|
192
|
+
function stripCommandHandlerBodies(text ) {
|
|
193
|
+
return text.replace(/function\s+handle\w*Command\b[\s\S]*?\n\}/gm, '');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Extracts statically invoked CLI verbs from test or helper text. */
|
|
197
|
+
function extractInvokedVerbs(text , knownVerbsSet ) {
|
|
198
|
+
const verbs = new Set ();
|
|
199
|
+
const arrRe = /\[\s*(?:[A-Za-z0-9_$.]+\s*,\s*)?['"`]([a-z][a-z0-9-]*)['"`]/g;
|
|
200
|
+
for (const m of text.matchAll(arrRe)) {
|
|
201
|
+
if (knownVerbsSet.has(m[1])) verbs.add(m[1]);
|
|
202
|
+
}
|
|
203
|
+
const runCallRe = /\b(?:run\w*|cli\w*|doflow\w*|exec\w*|spawn\w*)\s*\(\s*(?:[^,()\n]+\s*,\s*)?['"`]([a-z][a-z0-9-]*)['"`]/gi;
|
|
204
|
+
for (const m of text.matchAll(runCallRe)) {
|
|
205
|
+
if (knownVerbsSet.has(m[1])) verbs.add(m[1]);
|
|
206
|
+
}
|
|
207
|
+
const cmdRe = /\bdoflow(?:-run)?(?:\.js)?['"`\s,]+\s*['"`]?([a-z][a-z0-9-]*)/g;
|
|
208
|
+
for (const m of text.matchAll(cmdRe)) {
|
|
209
|
+
if (knownVerbsSet.has(m[1])) verbs.add(m[1]);
|
|
210
|
+
}
|
|
211
|
+
return verbs;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolves all .test.* files that directly or transitively import any of the specified non-test helper files (IC-002).
|
|
216
|
+
*
|
|
217
|
+
* @param {Object} input
|
|
218
|
+
* @param {string} input.root project root directory
|
|
219
|
+
* @param {Object} input.found detected testTargeted entry (from detectCommands)
|
|
220
|
+
* @param {Array<string>} [input.helperFiles] repo-relative non-test helper paths under test/**
|
|
221
|
+
* @param {Object} [input.fsImpl] filesystem implementation (defaults to node:fs)
|
|
222
|
+
* @returns {Array<string>} sorted list of repo-relative test paths
|
|
223
|
+
*/
|
|
224
|
+
function resolveTestsImportingHelper({
|
|
225
|
+
root,
|
|
226
|
+
found,
|
|
227
|
+
helperFiles = [],
|
|
228
|
+
fsImpl = nodeFs,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
) {
|
|
235
|
+
const convention = conventionFor(found);
|
|
236
|
+
if (!convention) return [];
|
|
237
|
+
const allTests = testFilesFor(found, root, fsImpl);
|
|
238
|
+
if (!allTests || allTests.length === 0 || helperFiles.length === 0) return [];
|
|
239
|
+
|
|
240
|
+
const normalizedHelpers = new Set(helperFiles.map(normalize));
|
|
241
|
+
const testFilesSet = new Set (allTests);
|
|
242
|
+
|
|
243
|
+
const directImporters = new Map ();
|
|
244
|
+
const helperQueue = [];
|
|
245
|
+
const scanned = new Set ();
|
|
246
|
+
|
|
247
|
+
for (const testFile of allTests) {
|
|
248
|
+
let text = '';
|
|
249
|
+
try {
|
|
250
|
+
text = fsImpl.readFileSync(path.join(root, testFile), 'utf8');
|
|
251
|
+
} catch {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
for (const spec of extractRelativeSpecifiers(text)) {
|
|
255
|
+
const resolved = resolveRelativeSpecifier(testFile, spec, root, fsImpl);
|
|
256
|
+
if (resolved && (resolved.startsWith('test/') || resolved.startsWith('bench/') || resolved.startsWith('src/'))) {
|
|
257
|
+
if (!directImporters.has(resolved)) directImporters.set(resolved, new Set());
|
|
258
|
+
directImporters.get(resolved) .add(testFile);
|
|
259
|
+
if (resolved.startsWith('test/') && !convention.isTest(resolved) && !scanned.has(resolved)) {
|
|
260
|
+
helperQueue.push(resolved);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
while (helperQueue.length > 0) {
|
|
267
|
+
const helper = helperQueue.shift() ;
|
|
268
|
+
if (scanned.has(helper)) continue;
|
|
269
|
+
scanned.add(helper);
|
|
270
|
+
|
|
271
|
+
let text = '';
|
|
272
|
+
try {
|
|
273
|
+
text = fsImpl.readFileSync(path.join(root, helper), 'utf8');
|
|
274
|
+
} catch {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
for (const spec of extractRelativeSpecifiers(text)) {
|
|
279
|
+
const resolved = resolveRelativeSpecifier(helper, spec, root, fsImpl);
|
|
280
|
+
if (resolved && (resolved.startsWith('test/') || resolved.startsWith('bench/') || resolved.startsWith('src/'))) {
|
|
281
|
+
if (!directImporters.has(resolved)) directImporters.set(resolved, new Set());
|
|
282
|
+
directImporters.get(resolved) .add(helper);
|
|
283
|
+
if (resolved.startsWith('test/') && !convention.isTest(resolved) && !scanned.has(resolved)) {
|
|
284
|
+
helperQueue.push(resolved);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const importingTests = new Set ();
|
|
291
|
+
const visited = new Set ();
|
|
292
|
+
const queue = [...normalizedHelpers];
|
|
293
|
+
|
|
294
|
+
while (queue.length > 0) {
|
|
295
|
+
const current = queue.shift() ;
|
|
296
|
+
if (visited.has(current)) continue;
|
|
297
|
+
visited.add(current);
|
|
298
|
+
|
|
299
|
+
const callers = directImporters.get(current);
|
|
300
|
+
if (!callers) continue;
|
|
301
|
+
|
|
302
|
+
for (const caller of callers) {
|
|
303
|
+
if (testFilesSet.has(caller)) {
|
|
304
|
+
importingTests.add(caller);
|
|
305
|
+
}
|
|
306
|
+
if (!visited.has(caller)) {
|
|
307
|
+
queue.push(caller);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return [...importingTests].sort();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Selects impacted tests for a set of changed files based on static dependency analysis (IC-001, IC-009).
|
|
317
|
+
*
|
|
318
|
+
* @param {Object} input
|
|
319
|
+
* @param {string} input.root project root directory
|
|
320
|
+
* @param {Object} input.found detected testTargeted entry (from detectCommands)
|
|
321
|
+
* @param {Array<string>} [input.changedFiles] changed repo-relative file paths
|
|
322
|
+
* @param {Array<string>} [input.deletedFiles] deleted repo-relative file paths
|
|
323
|
+
* @param {Object} [input.fsImpl] filesystem implementation (defaults to node:fs)
|
|
324
|
+
* @returns {null|{ mode: "selected" | "full", tests: Array<string>, totalCount: number, unknownFiles: Array<string>, spawnTests: number, graphMs: number }}
|
|
325
|
+
*/
|
|
326
|
+
function selectImpactedTests({
|
|
327
|
+
root,
|
|
328
|
+
found,
|
|
329
|
+
changedFiles = [],
|
|
330
|
+
deletedFiles = [],
|
|
331
|
+
fsImpl = nodeFs,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
) {
|
|
339
|
+
const convention = conventionFor(found);
|
|
340
|
+
if (!convention) return null;
|
|
341
|
+
|
|
342
|
+
const t0 = process.hrtime.bigint();
|
|
343
|
+
|
|
344
|
+
const allTests = testFilesFor(found, root, fsImpl);
|
|
345
|
+
const totalCount = allTests.length;
|
|
346
|
+
const allTestFiles = new Set (allTests);
|
|
347
|
+
|
|
348
|
+
const guardDir = convention.guardDir && statOf(fsImpl, path.join(root, convention.guardDir))?.isDirectory()
|
|
349
|
+
? convention.guardDir
|
|
350
|
+
: null;
|
|
351
|
+
const guardTests = guardDir
|
|
352
|
+
? allTests.filter((file ) => file.startsWith(`${guardDir}/`) || file === guardDir)
|
|
353
|
+
: [];
|
|
354
|
+
|
|
355
|
+
const isGraphNodePath = (file ) => isGraphNode(file, convention);
|
|
356
|
+
|
|
357
|
+
const verbToModules = parseCliVerbMap(root, fsImpl);
|
|
358
|
+
const knownVerbsSet = new Set (verbToModules.keys());
|
|
359
|
+
|
|
360
|
+
const testTexts = new Map ();
|
|
361
|
+
const testToModules = new Map ();
|
|
362
|
+
const moduleQueue = [];
|
|
363
|
+
|
|
364
|
+
for (const testFile of allTests) {
|
|
365
|
+
testToModules.set(testFile, new Set());
|
|
366
|
+
let text = '';
|
|
367
|
+
try {
|
|
368
|
+
text = fsImpl.readFileSync(path.join(root, testFile), 'utf8');
|
|
369
|
+
testTexts.set(testFile, text);
|
|
370
|
+
} catch {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
for (const spec of extractRelativeSpecifiers(text)) {
|
|
374
|
+
const resolved = resolveRelativeSpecifier(testFile, spec, root, fsImpl);
|
|
375
|
+
if (resolved && isGraphNodePath(resolved)) {
|
|
376
|
+
testToModules.get(testFile) .add(resolved);
|
|
377
|
+
moduleQueue.push(resolved);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Queue all modules from hubs and verbToModules
|
|
383
|
+
const hubs = new Set (['bin/doflow.js', 'src/cli/index.js', 'src/cli/runtime-commands.js']);
|
|
384
|
+
for (const hub of hubs) {
|
|
385
|
+
if (statOf(fsImpl, path.join(root, hub))) {
|
|
386
|
+
moduleQueue.push(hub);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
for (const mods of verbToModules.values()) {
|
|
390
|
+
for (const m of mods) {
|
|
391
|
+
moduleQueue.push(m);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const moduleDependencies = new Map ();
|
|
396
|
+
const scannedModules = new Set ();
|
|
397
|
+
const moduleTexts = new Map ();
|
|
398
|
+
|
|
399
|
+
function drainModuleQueue() {
|
|
400
|
+
while (moduleQueue.length > 0) {
|
|
401
|
+
const mod = moduleQueue.shift() ;
|
|
402
|
+
if (scannedModules.has(mod)) continue;
|
|
403
|
+
scannedModules.add(mod);
|
|
404
|
+
|
|
405
|
+
let text = '';
|
|
406
|
+
try {
|
|
407
|
+
text = fsImpl.readFileSync(path.join(root, mod), 'utf8');
|
|
408
|
+
moduleTexts.set(mod, text);
|
|
409
|
+
} catch {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (!moduleDependencies.has(mod)) {
|
|
414
|
+
moduleDependencies.set(mod, new Set());
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (const spec of extractRelativeSpecifiers(text)) {
|
|
418
|
+
const resolved = resolveRelativeSpecifier(mod, spec, root, fsImpl);
|
|
419
|
+
if (resolved && isGraphNodePath(resolved)) {
|
|
420
|
+
if (!scannedModules.has(resolved)) {
|
|
421
|
+
moduleQueue.push(resolved);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (hubs.has(mod)) continue;
|
|
427
|
+
|
|
428
|
+
const libText = stripCommandHandlerBodies(text);
|
|
429
|
+
for (const spec of extractRelativeSpecifiers(libText)) {
|
|
430
|
+
const resolved = resolveRelativeSpecifier(mod, spec, root, fsImpl);
|
|
431
|
+
if (resolved && isGraphNodePath(resolved)) {
|
|
432
|
+
moduleDependencies.get(mod) .add(resolved);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
drainModuleQueue();
|
|
439
|
+
|
|
440
|
+
// Scan lazy requires in handle*Command functions for verbToModules expansion
|
|
441
|
+
for (const [verb, mods] of [...verbToModules.entries()]) {
|
|
442
|
+
for (const mod of [...mods]) {
|
|
443
|
+
const txt = moduleTexts.get(mod) || '';
|
|
444
|
+
for (const fnMatch of txt.matchAll(/function\s+handle\w*Command\b[\s\S]*?\n\}/gm)) {
|
|
445
|
+
for (const s of extractRelativeSpecifiers(fnMatch[0])) {
|
|
446
|
+
const resolved = resolveRelativeSpecifier(mod, s, root, fsImpl);
|
|
447
|
+
if (resolved) {
|
|
448
|
+
mods.add(resolved);
|
|
449
|
+
if (!scannedModules.has(resolved)) {
|
|
450
|
+
moduleQueue.push(resolved);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
drainModuleQueue();
|
|
459
|
+
|
|
460
|
+
const allSpawnTests = new Set ();
|
|
461
|
+
const spawnTests = new Set (); // wildcard spawn tests
|
|
462
|
+
|
|
463
|
+
for (const tf of allTests) {
|
|
464
|
+
const tText = testTexts.get(tf) || '';
|
|
465
|
+
let spawns = isSpawnTest(tText);
|
|
466
|
+
let invokedVerbs = extractInvokedVerbs(tText, knownVerbsSet);
|
|
467
|
+
|
|
468
|
+
// Transitive helper reachability from tf to test/** helpers
|
|
469
|
+
const helperQueue = [...testToModules.get(tf) ].filter((m) => m.startsWith('test/'));
|
|
470
|
+
const seenHelpers = new Set (helperQueue);
|
|
471
|
+
while (helperQueue.length > 0) {
|
|
472
|
+
const h = helperQueue.shift() ;
|
|
473
|
+
const hText = moduleTexts.get(h) || '';
|
|
474
|
+
if (isSpawnTest(hText)) spawns = true;
|
|
475
|
+
for (const dep of (moduleDependencies.get(h) || [])) {
|
|
476
|
+
if (dep.startsWith('test/') && !seenHelpers.has(dep)) {
|
|
477
|
+
seenHelpers.add(dep);
|
|
478
|
+
helperQueue.push(dep);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (spawns && invokedVerbs.size === 0) {
|
|
484
|
+
for (const h of seenHelpers) {
|
|
485
|
+
for (const v of extractInvokedVerbs(moduleTexts.get(h) || '', knownVerbsSet)) {
|
|
486
|
+
invokedVerbs.add(v);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (spawns) {
|
|
492
|
+
allSpawnTests.add(tf);
|
|
493
|
+
|
|
494
|
+
for (const hub of hubs) {
|
|
495
|
+
if (scannedModules.has(hub)) {
|
|
496
|
+
testToModules.get(tf) .add(hub);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (invokedVerbs.size > 0) {
|
|
501
|
+
for (const v of invokedVerbs) {
|
|
502
|
+
const mods = verbToModules.get(v);
|
|
503
|
+
if (mods) {
|
|
504
|
+
for (const m of mods) {
|
|
505
|
+
testToModules.get(tf) .add(m);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
spawnTests.add(tf);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const reverseModuleDeps = new Map ();
|
|
516
|
+
for (const [mod, targets] of moduleDependencies.entries()) {
|
|
517
|
+
for (const target of targets) {
|
|
518
|
+
if (!reverseModuleDeps.has(target)) reverseModuleDeps.set(target, new Set());
|
|
519
|
+
reverseModuleDeps.get(target) .add(mod);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const moduleDirectTests = new Map ();
|
|
524
|
+
for (const [t, targets] of testToModules.entries()) {
|
|
525
|
+
for (const target of targets) {
|
|
526
|
+
if (!moduleDirectTests.has(target)) moduleDirectTests.set(target, new Set());
|
|
527
|
+
moduleDirectTests.get(target) .add(t);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function testsReachingModule(targetModule ) {
|
|
532
|
+
const reachingTests = new Set ();
|
|
533
|
+
const visitedModules = new Set ([targetModule]);
|
|
534
|
+
const queue = [targetModule];
|
|
535
|
+
while (queue.length > 0) {
|
|
536
|
+
const current = queue.shift() ;
|
|
537
|
+
const direct = moduleDirectTests.get(current);
|
|
538
|
+
if (direct) {
|
|
539
|
+
for (const t of direct) reachingTests.add(t);
|
|
540
|
+
}
|
|
541
|
+
const callers = reverseModuleDeps.get(current);
|
|
542
|
+
if (callers) {
|
|
543
|
+
for (const caller of callers) {
|
|
544
|
+
if (!visitedModules.has(caller)) {
|
|
545
|
+
visitedModules.add(caller);
|
|
546
|
+
queue.push(caller);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return reachingTests;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const normalizedChanged = (changedFiles || []).map(normalize);
|
|
555
|
+
const normalizedDeleted = (deletedFiles || []).map(normalize);
|
|
556
|
+
const candidateFiles = [...new Set([...normalizedChanged, ...normalizedDeleted])];
|
|
557
|
+
|
|
558
|
+
const unknownFiles = new Set ();
|
|
559
|
+
const selectedTests = new Set ();
|
|
560
|
+
let hasReachedModule = false;
|
|
561
|
+
|
|
562
|
+
for (const file of candidateFiles) {
|
|
563
|
+
if (scannedModules.has(file)) {
|
|
564
|
+
if (!file.startsWith('test/')) {
|
|
565
|
+
hasReachedModule = true;
|
|
566
|
+
}
|
|
567
|
+
for (const t of testsReachingModule(file)) {
|
|
568
|
+
selectedTests.add(t);
|
|
569
|
+
}
|
|
570
|
+
} else if (convention.isTest(file)) {
|
|
571
|
+
if (allTestFiles.has(file)) {
|
|
572
|
+
selectedTests.add(file);
|
|
573
|
+
}
|
|
574
|
+
} else if (isGuardCoveredPath(file, guardDir !== null)) {
|
|
575
|
+
// guard-covered: verified by guardTests, does not enter unknownFiles
|
|
576
|
+
} else {
|
|
577
|
+
unknownFiles.add(file);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const fullSuite = () => ({
|
|
582
|
+
mode: 'full' ,
|
|
583
|
+
tests: [...allTests].sort(),
|
|
584
|
+
totalCount,
|
|
585
|
+
unknownFiles: [...unknownFiles].sort(),
|
|
586
|
+
spawnTests: allSpawnTests.size,
|
|
587
|
+
graphMs: Number(process.hrtime.bigint() - t0) / 1e6,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
/* UNKNOWN_EXPANSION */ if (unknownFiles.size > 0) return fullSuite();
|
|
591
|
+
|
|
592
|
+
if (hasReachedModule) {
|
|
593
|
+
/* SPAWN_WILDCARD */ for (const spawnTest of spawnTests) selectedTests.add(spawnTest);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
for (const guardTest of guardTests) {
|
|
597
|
+
selectedTests.add(guardTest);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return {
|
|
601
|
+
mode: 'selected' ,
|
|
602
|
+
tests: [...selectedTests].sort(),
|
|
603
|
+
totalCount,
|
|
604
|
+
unknownFiles: [],
|
|
605
|
+
spawnTests: allSpawnTests.size,
|
|
606
|
+
graphMs: Number(process.hrtime.bigint() - t0) / 1e6,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
module.exports = {
|
|
611
|
+
selectImpactedTests,
|
|
612
|
+
isSpawnTest,
|
|
613
|
+
resolveRelativeSpecifier,
|
|
614
|
+
extractRelativeSpecifiers,
|
|
615
|
+
resolveTestsImportingHelper,
|
|
616
|
+
GUARD_DIR,
|
|
617
|
+
};
|