oh-my-opencode 4.19.0 → 4.19.2
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/.agents/command/omomomo.md +1 -1
- package/.agents/skills/omomomo/SKILL.md +1 -1
- package/.opencode/command/omomomo.md +1 -1
- package/README.ja.md +6 -6
- package/README.ko.md +6 -6
- package/README.md +6 -6
- package/README.ru.md +6 -6
- package/README.zh-cn.md +6 -6
- package/dist/agents/atlas/agent.d.ts +5 -3
- package/dist/agents/explore.d.ts +4 -2
- package/dist/agents/gpt-prompt-identity.d.ts +3 -0
- package/dist/agents/hephaestus/agent.d.ts +4 -2
- package/dist/agents/librarian.d.ts +4 -2
- package/dist/agents/metis.d.ts +4 -2
- package/dist/agents/momus.d.ts +3 -2
- package/dist/agents/multimodal-looker.d.ts +4 -2
- package/dist/agents/oracle.d.ts +4 -2
- package/dist/agents/sisyphus/claude-opus-5.d.ts +33 -0
- package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
- package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
- package/dist/agents/sisyphus/index.d.ts +2 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +5 -2
- package/dist/agents/sisyphus-junior/agent.d.ts +4 -1
- package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +23577 -19269
- package/dist/cli-node/index.js +23577 -19269
- package/dist/config/schema/agent-names.d.ts +26 -26
- package/dist/config/schema/agent-overrides.d.ts +400 -400
- package/dist/config/schema/browser-automation.d.ts +2 -2
- package/dist/config/schema/categories.d.ts +36 -36
- package/dist/config/schema/codegraph.d.ts +2 -0
- package/dist/config/schema/commands.d.ts +2 -2
- package/dist/config/schema/dynamic-context-pruning.d.ts +1 -1
- package/dist/config/schema/experimental.d.ts +1 -1
- package/dist/config/schema/fallback-models.d.ts +25 -25
- package/dist/config/schema/hooks.d.ts +43 -43
- package/dist/config/schema/internal/permission.d.ts +9 -9
- package/dist/config/schema/keyword-detector.d.ts +6 -6
- package/dist/config/schema/oh-my-opencode-config.d.ts +404 -402
- package/dist/config/schema/tmux.d.ts +6 -6
- package/dist/create-hooks.d.ts +35 -35
- package/dist/features/background-agent/spawner/tmux-callback-invoker.d.ts +11 -0
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/features/claude-tasks/types.d.ts +4 -4
- package/dist/features/team-mode/team-runtime/shutdown-helpers.d.ts +1 -1
- package/dist/features/team-mode/team-runtime/shutdown-test-fixtures.d.ts +4 -4
- package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +42 -42
- package/dist/features/tmux-subagent/environment-eligibility.d.ts +2 -0
- package/dist/features/tmux-subagent/index.d.ts +1 -0
- package/dist/features/tmux-subagent/tracked-session-state.d.ts +1 -0
- package/dist/features/tui-sidebar/snapshot-schema.d.ts +6 -6
- package/dist/help/schema/acp.d.ts +3 -3
- package/dist/help/schema/doctor.d.ts +4 -4
- package/dist/help/schema/status.d.ts +3 -3
- package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
- package/dist/hooks/goal/types.d.ts +5 -5
- package/dist/hooks/non-interactive-env/constants.d.ts +4 -1
- package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
- package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
- package/dist/hooks/start-work/parse-user-request.d.ts +2 -0
- package/dist/hooks/start-work/worktree-block.d.ts +5 -0
- package/dist/index.js +27370 -21301
- package/dist/mcp/types.d.ts +3 -3
- package/dist/oh-my-opencode.schema.json +11 -0
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -0
- package/dist/plugin/command-execute-before.d.ts +3 -3
- package/dist/plugin/hooks/create-core-hooks.d.ts +25 -25
- package/dist/shared/omo-process-sweep.d.ts +18 -0
- package/dist/shared/tmux/tmux-utils/environment.d.ts +1 -1
- package/dist/shared/tmux/tmux-utils.d.ts +1 -1
- package/dist/shared/typescript-native-source-parser.d.ts +7 -0
- package/dist/skills/coding-agent-sessions/SKILL.md +5 -2
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/dist/skills/coding-agent-sessions/references/senpi.md +15 -5
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
- package/dist/skills/debugging/SKILL.md +2 -1
- package/dist/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
- package/dist/skills/programming/SKILL.md +11 -1
- package/dist/skills/programming/references/logging.md +6 -1
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
- package/dist/skills/start-work/SKILL.md +12 -2
- package/dist/skills/ulw-plan/SKILL.md +21 -2
- package/dist/skills/ulw-plan/references/full-workflow.md +12 -1
- package/dist/skills/ulw-plan/references/intent-clear.md +1 -1
- package/dist/testing/create-plugin-module.d.ts +1 -0
- package/dist/tools/task/types.d.ts +10 -10
- package/dist/tui.js +23442 -19182
- package/package.json +14 -14
- package/packages/git-bash-mcp/dist/cli.js +54 -8
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +62 -2
- package/packages/lsp-core/src/lsp/client.ts +14 -1
- package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
- package/packages/lsp-core/src/mcp.ts +9 -0
- package/packages/lsp-daemon/dist/cli.js +319 -51
- package/packages/lsp-daemon/dist/client.js +136 -48
- package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
- package/packages/lsp-daemon/dist/daemon-client.js +4 -34
- package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
- package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
- package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
- package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
- package/packages/lsp-daemon/dist/daemon-server.js +5 -1
- package/packages/lsp-daemon/dist/index.js +137 -48
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +1 -0
- package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
- package/packages/lsp-daemon/dist/version-reap.js +182 -0
- package/packages/lsp-tools-mcp/dist/cli.js +61 -10
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
- package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
- package/packages/lsp-tools-mcp/dist/tools.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +72 -12
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +779 -385
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +302 -173
- package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +23 -42
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +17 -9
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +18 -25
- package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +5 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +176 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +6 -1
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
- package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +6 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +2 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +42 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +122 -10
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +101 -64
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
- package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +12 -7
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +12 -7
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +21 -2
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +12 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +12 -7
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +41 -41
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +44 -0
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +5 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/senpi.md +15 -5
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
- package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +11 -1
- package/packages/omo-codex/plugin/skills/programming/references/logging.md +6 -1
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +12 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +12 -7
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +21 -2
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +12 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +1 -1
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +9 -9
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
- package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +22 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +49 -5
- package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +5 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/packages/shared-skills/skills/coding-agent-sessions/references/senpi.md +15 -5
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
- package/packages/shared-skills/skills/debugging/SKILL.md +2 -1
- package/packages/shared-skills/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
- package/packages/shared-skills/skills/programming/SKILL.md +11 -1
- package/packages/shared-skills/skills/programming/references/logging.md +6 -1
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
- package/packages/shared-skills/skills/start-work/SKILL.md +12 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +21 -2
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +12 -1
- package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-ulw-loop",
|
|
3
|
-
"version": "4.19.
|
|
3
|
+
"version": "4.19.2",
|
|
4
4
|
"description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -20,6 +20,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
|
|
|
20
20
|
## Non-Negotiables
|
|
21
21
|
|
|
22
22
|
- Use the ulw-loop CLI state under `.omo/ulw-loop`; do not hand-edit goal state.
|
|
23
|
+
- Register goals up front (`omo ulw-loop create-goals`, then `create_goal` from the printed handoff) and mirror every atomic step into the live `update_plan` checklist: one ultra-granular step per action, exactly one in_progress, transitions marked the instant they happen.
|
|
23
24
|
- After any compaction or context loss, re-read brief + goals + ledger FIRST plus `omo ulw-loop status --json`, then resume; never re-plan from scratch.
|
|
24
25
|
- If `omo ulw-loop create-goals` says the existing aggregate is already complete, start unrelated new work with a fresh `--session-id <new-id>` instead of steering or forcing the completed default state. Use `--force` only to intentionally overwrite completed evidence.
|
|
25
26
|
- Every success criterion needs observable evidence from a real surface: a channel (terminal/TUI via the xterm.js web terminal, HTTP, browser, computer-use) or, for CLI- or data-shaped criteria, an auxiliary surface (CLI stdout, DB diff, parsed config dump).
|
|
@@ -31,7 +32,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
|
|
|
31
32
|
- Track spawned agent names locally. Use `wait_agent` for mailbox signals, not proof of completion. A timeout only means no new mailbox update arrived. Treat a running child as alive.
|
|
32
33
|
- While children run, surface the active subagent count, agent names, and latest `WORKING:` phase.
|
|
33
34
|
- Fallback only when the child is completed without the deliverable, ack-only after `followup_task`, explicitly `BLOCKED:`, or no longer running. Then record inconclusive and respawn a smaller `fork_turns: "none"` task with the missing deliverable.
|
|
34
|
-
- Use `git-master` for git-tracked edits: inspect recent and touched-path commit history, then commit each verified work unit atomically in the repository's observed language, scope, and message style with only that unit's files staged.
|
|
35
|
+
- Use `git-master` for git-tracked edits: inspect recent and touched-path commit history, then commit each verified work unit atomically in the repository's observed language, scope, and message style with only that unit's files staged. Never carry verified units into a later omnibus commit.
|
|
35
36
|
|
|
36
37
|
## Codex Tool Mapping
|
|
37
38
|
|
package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md
CHANGED
|
@@ -113,9 +113,9 @@ If `ULW_LOOP_CLI` is empty, open the durable notepad first, record the missing C
|
|
|
113
113
|
|
|
114
114
|
Run one form:
|
|
115
115
|
```sh
|
|
116
|
-
omo ulw-loop create-goals --brief "<brief>" --json
|
|
117
|
-
omo ulw-loop create-goals --brief-file <path> --json
|
|
118
|
-
cat <brief> | omo ulw-loop create-goals --from-stdin --json
|
|
116
|
+
omo ulw-loop create-goals --brief "<brief>" [--validation-batch-json <json-or-path>] --json
|
|
117
|
+
omo ulw-loop create-goals --brief-file <path> [--validation-batch-json <json-or-path>] --json
|
|
118
|
+
cat <brief> | omo ulw-loop create-goals --from-stdin [--validation-batch-json <json-or-path>] --json
|
|
119
119
|
```
|
|
120
120
|
If the existing aggregate is already complete, do not steer or force the
|
|
121
121
|
completed default state for unrelated new work. Start a fresh run with
|
|
@@ -145,7 +145,7 @@ Read pending goals, criteria IDs, current ledger head, blockers, and aggregate C
|
|
|
145
145
|
Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures at 3.
|
|
146
146
|
|
|
147
147
|
### Acquire Next Goal
|
|
148
|
-
1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria.
|
|
148
|
+
1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria. After the first goal starts, a successful complete checkpoint normally prints the next goal instruction directly; use `complete-goals` as the manual fallback/resume path.
|
|
149
149
|
2. Call `get_goal` and inspect active Codex state.
|
|
150
150
|
3. Apply this table exactly:
|
|
151
151
|
|
|
@@ -177,7 +177,7 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
|
|
|
177
177
|
### Goal Completion
|
|
178
178
|
1. Non-final aggregate goal: confirm every `essential` criterion is `pass`; non-essential criteria may remain pending. Final aggregate goal: confirm every criterion across the whole plan is `pass`.
|
|
179
179
|
2. Call `get_goal` for a fresh snapshot.
|
|
180
|
-
3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json
|
|
180
|
+
3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json`; on success it auto-starts and prints the next eligible goal unless `--no-advance` is passed.
|
|
181
181
|
4. If blocked or failed, checkpoint with `--status blocked` or `--status failed` and include diagnosis evidence.
|
|
182
182
|
5. If this is the final goal, run the final quality gate first and pass `--quality-gate-json`.
|
|
183
183
|
|
|
@@ -219,7 +219,9 @@ Use steering only for structured evidence-backed mutation. Reject natural-langua
|
|
|
219
219
|
| annotate_ledger | Audit-only note | `--evidence`, `--rationale` |
|
|
220
220
|
| mark_blocked_superseded | Old story replaced by new evidence | `--goal-id`, `--replacements?`, `--evidence`, `--rationale` |
|
|
221
221
|
|
|
222
|
-
Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`.
|
|
222
|
+
Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`. For multiple evidence-backed plan-shape changes discovered together, pass `--proposals-json <json-or-path>` with an array of proposals; the batch applies atomically or rejects without partial plan mutation.
|
|
223
|
+
|
|
224
|
+
Validation batches are optional aggregate-mode review boundaries declared at create time with `--validation-batch-json`. A batch-final member requires all other members resolved, all member criteria pass, and a member-spanning quality gate; split/supersede steering keeps batch membership updated.
|
|
223
225
|
Structured prompt directives accepted: `OMO_ULW_LOOP_STEER: { ... }`, `omo.ulw-loop.steer: {...}`, `omo ulw-loop steer: {...}`.
|
|
224
226
|
|
|
225
227
|
## Constraints
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type CheckpointUlwLoopArgs, type CheckpointUlwLoopResult, checkpointUlwLoop } from "./checkpoint.js";
|
|
2
|
+
import { hasFlag, parseCodexGoalJson, readValue } from "./cli-arg-parser.js";
|
|
3
|
+
import { blockedDecisionHandoff, printJson } from "./cli-output.js";
|
|
4
|
+
import { buildCodexGoalInstruction, type UlwLoopGoalInstruction } from "./codex-goal-instruction.js";
|
|
5
|
+
import type { UlwLoopScope } from "./paths.js";
|
|
6
|
+
import { startNextUlwLoop, summarizeUlwLoopPlan } from "./plan-crud.js";
|
|
7
|
+
import type { UlwLoopItem, UlwLoopPlan } from "./types.js";
|
|
8
|
+
import { UlwLoopError } from "./types.js";
|
|
9
|
+
|
|
10
|
+
type ContinuationNext =
|
|
11
|
+
| { readonly resumed: boolean; readonly goal: UlwLoopItem; readonly instruction: UlwLoopGoalInstruction }
|
|
12
|
+
| { readonly done: true; readonly blocked: boolean; readonly handoff: string };
|
|
13
|
+
|
|
14
|
+
export type CheckpointAndContinueResult = CheckpointUlwLoopResult & { readonly next?: ContinuationNext };
|
|
15
|
+
|
|
16
|
+
export async function checkpointAndContinue(
|
|
17
|
+
repoRoot: string,
|
|
18
|
+
args: CheckpointUlwLoopArgs & { readonly advance: boolean },
|
|
19
|
+
scope?: UlwLoopScope,
|
|
20
|
+
): Promise<CheckpointAndContinueResult> {
|
|
21
|
+
const result = await checkpointUlwLoop(repoRoot, args, scope);
|
|
22
|
+
if (args.status !== "complete" || result.aggregateCompletion !== undefined || !args.advance) return result;
|
|
23
|
+
const next = await startNextUlwLoop(repoRoot, {}, scope);
|
|
24
|
+
if ("done" in next) return { ...result, plan: next.plan, next: doneNext(next.plan) };
|
|
25
|
+
const instruction = buildCodexGoalInstruction({ plan: next.plan, goal: next.goal });
|
|
26
|
+
return { ...result, plan: next.plan, next: { resumed: next.resumed, goal: next.goal, instruction } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function checkpoint(
|
|
30
|
+
repoRoot: string,
|
|
31
|
+
argv: readonly string[],
|
|
32
|
+
json: boolean,
|
|
33
|
+
scope?: UlwLoopScope,
|
|
34
|
+
): Promise<number> {
|
|
35
|
+
const goalId = required(argv, "--goal-id");
|
|
36
|
+
const statusValue = checkpointStatus(required(argv, "--status"));
|
|
37
|
+
const evidence = required(argv, "--evidence");
|
|
38
|
+
const codexGoalJson = await parseCodexGoalJson(
|
|
39
|
+
statusValue === "complete" ? required(argv, "--codex-goal-json") : readValue(argv, "--codex-goal-json"),
|
|
40
|
+
);
|
|
41
|
+
if (statusValue === "complete" && codexGoalJson === undefined) {
|
|
42
|
+
throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
|
|
43
|
+
}
|
|
44
|
+
const qualityGateJson = readValue(argv, "--quality-gate-json");
|
|
45
|
+
const args: CheckpointUlwLoopArgs & { readonly advance: boolean } = {
|
|
46
|
+
goalId,
|
|
47
|
+
status: statusValue,
|
|
48
|
+
evidence,
|
|
49
|
+
advance: !hasFlag(argv, "--no-advance"),
|
|
50
|
+
...(codexGoalJson === undefined ? {} : { codexGoalJson }),
|
|
51
|
+
...(qualityGateJson === undefined ? {} : { qualityGateJson }),
|
|
52
|
+
};
|
|
53
|
+
const result = await checkpointAndContinue(repoRoot, args, scope);
|
|
54
|
+
if (json) printJson({ ok: true, ...result, summary: summarizeUlwLoopPlan(result.plan) });
|
|
55
|
+
else printCheckpointText(result);
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function printCheckpointText(result: CheckpointAndContinueResult): void {
|
|
60
|
+
process.stdout.write(`ulw-loop checkpoint: ${result.goal.id} -> ${result.goal.status}\n`);
|
|
61
|
+
if (result.next === undefined) return;
|
|
62
|
+
if ("instruction" in result.next) process.stdout.write(`${result.next.instruction.text}\n`);
|
|
63
|
+
else process.stdout.write(`${result.next.handoff || "ulw-loop: all goals complete"}\n`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function doneNext(plan: UlwLoopPlan): ContinuationNext {
|
|
67
|
+
const handoff = blockedDecisionHandoff(plan);
|
|
68
|
+
return { done: true, blocked: handoff.length > 0, handoff };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function required(argv: readonly string[], flag: string): string {
|
|
72
|
+
const value = readValue(argv, flag)?.trim();
|
|
73
|
+
if (value) return value;
|
|
74
|
+
throw new UlwLoopError(`Missing ${flag}.`, "ULW_LOOP_ARGUMENT_MISSING", { details: { flag } });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function checkpointStatus(value: string): CheckpointUlwLoopArgs["status"] {
|
|
78
|
+
if (value === "complete" || value === "failed" || value === "blocked") return value;
|
|
79
|
+
throw new UlwLoopError(
|
|
80
|
+
"Missing or invalid --status; expected complete, failed, or blocked.",
|
|
81
|
+
"ULW_LOOP_STATUS_INVALID",
|
|
82
|
+
{ details: { status: value } },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// biome-ignore-all format: checkpoint stays under the pure LOC ceiling.
|
|
2
2
|
import { existsSync, statSync } from "node:fs";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { resolve } from "node:path";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isFinalRunCompletionCandidate,
|
|
21
21
|
} from "./goal-status.js";
|
|
22
22
|
import type { UlwLoopScope } from "./paths.js";
|
|
23
|
+
import { ulwLoopAttemptEvidenceDir } from "./paths.js";
|
|
23
24
|
import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
|
|
24
25
|
import {
|
|
25
26
|
classifyExternalAuthorizationBlocker,
|
|
@@ -35,6 +36,7 @@ import type {
|
|
|
35
36
|
UlwLoopQualityGate,
|
|
36
37
|
} from "./types.js";
|
|
37
38
|
import { iso, UlwLoopError } from "./types.js";
|
|
39
|
+
import { batchClosedBy, requireAllValidationBatchesClosed, requireBatchFinalReady, requireBatchGate } from "./validation-batch.js";
|
|
38
40
|
|
|
39
41
|
export interface CheckpointUlwLoopArgs {
|
|
40
42
|
readonly goalId: string;
|
|
@@ -169,9 +171,11 @@ export async function checkpointUlwLoop(
|
|
|
169
171
|
if (args.status === "complete") {
|
|
170
172
|
const aggregate = codexGoalMode(plan) === "aggregate";
|
|
171
173
|
const final = isFinalRunCompletionCandidate(plan, goal);
|
|
174
|
+
const closesBatch = batchClosedBy(plan, goal.id) !== undefined;
|
|
172
175
|
if (final) {
|
|
173
176
|
requireAllCriteriaPass(goal);
|
|
174
177
|
requireAllPlanCriteriaPass(plan);
|
|
178
|
+
requireAllValidationBatchesClosed(plan, goal.id);
|
|
175
179
|
} else if (aggregate) requireEssentialCriteriaPass(goal);
|
|
176
180
|
else requireAllCriteriaPass(goal);
|
|
177
181
|
const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
|
|
@@ -218,8 +222,11 @@ export async function checkpointUlwLoop(
|
|
|
218
222
|
"ulw_loop_codex_snapshot_mismatch",
|
|
219
223
|
);
|
|
220
224
|
}
|
|
225
|
+
if (closesBatch) requireBatchFinalReady(plan, goal);
|
|
226
|
+
if (closesBatch && args.qualityGateJson === undefined)
|
|
227
|
+
throw new UlwLoopError("Validation batch final checkpoint requires --quality-gate-json.", "ULW_LOOP_VALIDATION_BATCH_GATE_REQUIRED");
|
|
221
228
|
if (final) aggregateCompletion = makeAggregateCompletion(now, evidence, codexGoal);
|
|
222
|
-
if (final || aggregateCompletion !== undefined)
|
|
229
|
+
if (final || aggregateCompletion !== undefined || closesBatch) {
|
|
223
230
|
qualityGate = validateQualityGate(await readJsonInput(args.qualityGateJson, repoRoot), {
|
|
224
231
|
repoRoot,
|
|
225
232
|
fs: QUALITY_GATE_FS,
|
|
@@ -227,6 +234,8 @@ export async function checkpointUlwLoop(
|
|
|
227
234
|
? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) }
|
|
228
235
|
: {}),
|
|
229
236
|
});
|
|
237
|
+
requireBatchGate(plan, goal, qualityGate);
|
|
238
|
+
}
|
|
230
239
|
goal.status = "complete";
|
|
231
240
|
goal.completedAt = now;
|
|
232
241
|
goal.evidence = evidence;
|
|
@@ -241,6 +250,8 @@ export async function checkpointUlwLoop(
|
|
|
241
250
|
await writePlan(repoRoot, plan, scope);
|
|
242
251
|
const ledgerEntry = buildLedger(now, args, goal, qualityGate, codexGoal, aggregateCompletion);
|
|
243
252
|
await appendLedger(repoRoot, ledgerEntry, scope);
|
|
253
|
+
const closedBatch = args.status === "complete" ? batchClosedBy(plan, goal.id) : undefined;
|
|
254
|
+
if (closedBatch !== undefined) await appendLedger(repoRoot, { at: now, kind: "batch_closed", goalId: goal.id, message: closedBatch.batchId }, scope);
|
|
244
255
|
return aggregateCompletion === undefined
|
|
245
256
|
? { plan, goal, ledgerEntry }
|
|
246
257
|
: { plan, goal, ledgerEntry, aggregateCompletion };
|
|
@@ -5,7 +5,7 @@ import { UlwLoopError } from "./types.js";
|
|
|
5
5
|
|
|
6
6
|
type RecordEvidenceCliArgs = { readonly goalId: string; readonly criterionId: string; readonly status: "pass" | "fail" | "blocked"; readonly evidence: string; readonly notes?: string };
|
|
7
7
|
|
|
8
|
-
const VALUE_FLAGS = new Set("--brief --brief-file --session-id --codex-goal-mode --goal --goal-id --criterion-id --status --evidence --notes --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key".split(" "));
|
|
8
|
+
const VALUE_FLAGS = new Set("--brief --brief-file --session-id --codex-goal-mode --validation-batch-json --goal --goal-id --criterion-id --status --evidence --notes --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key --proposals-json".split(" "));
|
|
9
9
|
const SUBCOMMANDS = new Set("create-goals status complete-goals criteria record-evidence checkpoint steer add-goal record-review-blockers".split(" "));
|
|
10
10
|
|
|
11
11
|
export function hasFlag(argv: readonly string[], flag: string): boolean { return argv.includes(flag); }
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { checkpoint } from "./checkpoint-continuation.js";
|
|
1
2
|
import { hasFlag, readValue } from "./cli-arg-parser.js";
|
|
2
3
|
import { printJsonError, ULW_LOOP_HELP } from "./cli-output.js";
|
|
3
4
|
import {
|
|
4
5
|
addGoal,
|
|
5
6
|
captureEvidence,
|
|
6
|
-
checkpoint,
|
|
7
7
|
completeGoals,
|
|
8
8
|
createGoals,
|
|
9
9
|
criteria,
|
|
@@ -2,13 +2,13 @@ import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan } from "./types.js"
|
|
|
2
2
|
import { UlwLoopError } from "./types.js";
|
|
3
3
|
|
|
4
4
|
export const ULW_LOOP_HELP = `Usage:
|
|
5
|
-
omo ulw-loop create-goals --brief "..." [--brief-file <path>] [--from-stdin] [--codex-goal-mode aggregate|per_story] [--force] [--json]
|
|
5
|
+
omo ulw-loop create-goals --brief "..." [--brief-file <path>] [--from-stdin] [--codex-goal-mode aggregate|per_story] [--validation-batch-json <json-or-path>] [--force] [--json]
|
|
6
6
|
omo ulw-loop status [--json]
|
|
7
7
|
omo ulw-loop complete-goals [--retry-failed] [--json]
|
|
8
8
|
omo ulw-loop criteria --goal-id <id> [--json]
|
|
9
9
|
omo ulw-loop record-evidence --goal-id <id> --criterion-id <id> --status pass|fail|blocked --evidence "..." [--notes "..."] [--json]
|
|
10
|
-
omo ulw-loop checkpoint --goal-id <id> --status complete|failed|blocked --evidence "..." --codex-goal-json <...> [--quality-gate-json <...>] [--json]
|
|
11
|
-
omo ulw-loop steer --kind <kind> ... --evidence "..." --rationale "..." [--json]
|
|
10
|
+
omo ulw-loop checkpoint --goal-id <id> --status complete|failed|blocked --evidence "..." --codex-goal-json <...> [--quality-gate-json <...>] [--no-advance] [--json]
|
|
11
|
+
omo ulw-loop steer --kind <kind> ... --evidence "..." --rationale "..." [--proposals-json <json-or-path>] [--json]
|
|
12
12
|
omo ulw-loop add-goal --title "..." --objective "..." [--json]
|
|
13
13
|
omo ulw-loop record-review-blockers --goal-id <id> --title "..." --objective "..." --evidence "..." --codex-goal-json <...> [--json]
|
|
14
14
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// biome-ignore-all format: keep this module under the mandated pure LOC budget.
|
|
2
2
|
import { parseGoalArg, readJsonInput, readValue } from "./cli-arg-parser.js";
|
|
3
3
|
import { printJson, printStatus } from "./cli-output.js";
|
|
4
|
+
import type { SteerUlwLoopBatchResult } from "./steering-batch.js";
|
|
4
5
|
import type { SteerUlwLoopResult, UlwLoopSteeringChildGoal, UlwLoopSteeringMutationKind, UlwLoopSteeringProposal, UlwLoopSteeringSource, UlwLoopSuccessCriterionUserModel } from "./types.js";
|
|
5
6
|
import { ULW_LOOP_STEERING_MUTATION_KINDS, ULW_LOOP_SUCCESS_CRITERION_USER_MODELS, UlwLoopError } from "./types.js";
|
|
6
7
|
|
|
@@ -31,6 +32,10 @@ function requiredGoal(argv: readonly string[]): string { const value = text(pars
|
|
|
31
32
|
function readObject(value: object, key: string): unknown { return Object.entries(value).find(([name]) => name === key)?.[1]; }
|
|
32
33
|
function isPlain(value: unknown): value is object { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
33
34
|
function objectText(value: object, key: string): string | undefined { const candidate = readObject(value, key); return typeof candidate === "string" ? candidate : undefined; }
|
|
35
|
+
function objectStrings(value: object, key: string): string[] | undefined { const candidate = readObject(value, key); return Array.isArray(candidate) && candidate.every((item) => typeof item === "string") ? candidate : undefined; }
|
|
36
|
+
function objectChildren(value: object, key: string): UlwLoopSteeringChildGoal[] | undefined { const candidate = readObject(value, key); if (!Array.isArray(candidate)) return undefined; const parsed: UlwLoopSteeringChildGoal[] = []; for (const item of candidate) { const next = child(item); if (next === null) return fail(`${key} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { key }); parsed.push(next); } return parsed; }
|
|
37
|
+
function isProposalKind(value: unknown): value is UlwLoopSteeringMutationKind { return typeof value === "string" && ULW_LOOP_STEERING_MUTATION_KINDS.some((kind) => kind === value); }
|
|
38
|
+
function isProposalSource(value: unknown): value is UlwLoopSteeringSource { return typeof value === "string" && SOURCES.some((source) => source === value); }
|
|
34
39
|
|
|
35
40
|
export function parseSteeringKind(argv: readonly string[]): UlwLoopSteeringMutationKind {
|
|
36
41
|
const value = readValue(argv, "--kind");
|
|
@@ -98,6 +103,43 @@ export function normalizeSteeringProposal(proposal: CliSteeringProposal): CliSte
|
|
|
98
103
|
return { kind: proposal.kind, source: proposal.source, evidence, rationale, ...(goalId === undefined ? {} : { goalId }), ...(targetGoalId === undefined ? {} : { targetGoalId }), ...(targetGoalIds === undefined ? {} : { targetGoalIds }), ...(criterionId === undefined ? {} : { criterionId }), ...(title === undefined ? {} : { title }), ...(objective === undefined ? {} : { objective }), ...(childGoals === undefined ? {} : { childGoals }), ...(revisedTitle === undefined ? {} : { revisedTitle }), ...(revisedObjective === undefined ? {} : { revisedObjective }), ...(pendingOrder === undefined ? {} : { pendingOrder }), ...(blockedReason === undefined ? {} : { blockedReason }), ...(proposal.after === undefined ? {} : { after: proposal.after }), ...(directiveText === undefined ? {} : { directiveText }), ...(promptSignature === undefined ? {} : { promptSignature }), ...(idempotencyKey === undefined ? {} : { idempotencyKey }), ...(proposal.now === undefined ? {} : { now: proposal.now }), ...(scenario === undefined ? {} : { scenario }), ...(expectedEvidence === undefined ? {} : { expectedEvidence }), ...(proposal.userModel === undefined ? {} : { userModel: proposal.userModel }) };
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
export async function parseSteeringProposals(argv: readonly string[]): Promise<readonly CliSteeringProposal[]> {
|
|
107
|
+
const input = text(readValue(argv, "--proposals-json"), "--proposals-json");
|
|
108
|
+
if (input === undefined) return [await parseSteeringProposal(argv)];
|
|
109
|
+
if (readValue(argv, "--kind") !== undefined) return fail("--kind and --proposals-json are mutually exclusive.", "ULW_LOOP_STEERING_BATCH_CONFLICT", { flags: ["--kind", "--proposals-json"] });
|
|
110
|
+
const raw = await readJsonInput(input);
|
|
111
|
+
if (!Array.isArray(raw) || raw.length === 0) return fail("--proposals-json must be a non-empty JSON array.", "ULW_LOOP_STEERING_BATCH_ARRAY_REQUIRED", { flag: "--proposals-json" });
|
|
112
|
+
const proposals: CliSteeringProposal[] = [];
|
|
113
|
+
for (const item of raw) proposals.push(normalizeSteeringProposal(proposalFromObject(item)));
|
|
114
|
+
return proposals;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function proposalFromObject(value: unknown): CliSteeringProposal {
|
|
118
|
+
if (!isPlain(value)) return fail("--proposals-json entries must be objects.", "ULW_LOOP_STEERING_BATCH_ITEM_INVALID", { flag: "--proposals-json" });
|
|
119
|
+
const kind = readObject(value, "kind"); const source = readObject(value, "source") ?? "cli";
|
|
120
|
+
if (!isProposalKind(kind)) return fail(`Invalid batch steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_INVALID", { value: kind });
|
|
121
|
+
if (!isProposalSource(source)) return fail(`Invalid batch steering source: ${String(source)}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value: source });
|
|
122
|
+
let proposal: CliSteeringProposal = { kind, source, evidence: objectText(value, "evidence") ?? "", rationale: objectText(value, "rationale") ?? "" };
|
|
123
|
+
const goalId = objectText(value, "goalId"); const targetGoalId = objectText(value, "targetGoalId"); const criterionId = objectText(value, "criterionId");
|
|
124
|
+
const title = objectText(value, "title"); const objective = objectText(value, "objective"); const revisedTitle = objectText(value, "revisedTitle"); const revisedObjective = objectText(value, "revisedObjective");
|
|
125
|
+
const scenario = objectText(value, "scenario"); const expectedEvidence = objectText(value, "expectedEvidence"); const idempotencyKey = objectText(value, "idempotencyKey");
|
|
126
|
+
const targetGoalIds = objectStrings(value, "targetGoalIds"); const pendingOrder = objectStrings(value, "pendingOrder"); const childGoals = objectChildren(value, "childGoals");
|
|
127
|
+
if (goalId !== undefined) proposal = { ...proposal, goalId };
|
|
128
|
+
if (targetGoalId !== undefined) proposal = { ...proposal, targetGoalId };
|
|
129
|
+
if (criterionId !== undefined) proposal = { ...proposal, criterionId };
|
|
130
|
+
if (title !== undefined) proposal = { ...proposal, title };
|
|
131
|
+
if (objective !== undefined) proposal = { ...proposal, objective };
|
|
132
|
+
if (revisedTitle !== undefined) proposal = { ...proposal, revisedTitle };
|
|
133
|
+
if (revisedObjective !== undefined) proposal = { ...proposal, revisedObjective };
|
|
134
|
+
if (scenario !== undefined) proposal = { ...proposal, scenario };
|
|
135
|
+
if (expectedEvidence !== undefined) proposal = { ...proposal, expectedEvidence };
|
|
136
|
+
if (idempotencyKey !== undefined) proposal = { ...proposal, idempotencyKey };
|
|
137
|
+
if (targetGoalIds !== undefined) proposal = { ...proposal, targetGoalIds };
|
|
138
|
+
if (pendingOrder !== undefined) proposal = { ...proposal, pendingOrder };
|
|
139
|
+
if (childGoals !== undefined) proposal = { ...proposal, childGoals };
|
|
140
|
+
return proposal;
|
|
141
|
+
}
|
|
142
|
+
|
|
101
143
|
export function printSteerResult(result: SteerUlwLoopResult, json: boolean): void {
|
|
102
144
|
if (json) { printJson({ ok: result.accepted, accepted: result.accepted, rejectedReasons: result.rejectedReasons, deduped: result.deduped, audit: result.audit, plan: result.plan }); return; }
|
|
103
145
|
const outcome = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
|
|
@@ -106,3 +148,10 @@ export function printSteerResult(result: SteerUlwLoopResult, json: boolean): voi
|
|
|
106
148
|
if (result.audit.idempotencyKey !== undefined) process.stdout.write(`idempotency-key: ${result.audit.idempotencyKey}\n`);
|
|
107
149
|
printStatus(result.plan);
|
|
108
150
|
}
|
|
151
|
+
|
|
152
|
+
export function printSteerBatchResult(result: SteerUlwLoopBatchResult, json: boolean): void {
|
|
153
|
+
if (json) { printJson({ ok: result.accepted, accepted: result.accepted, rejectedReasons: result.rejectedReasons, results: result.results, plan: result.plan }); return; }
|
|
154
|
+
process.stdout.write(`ulw-loop steer batch: ${result.accepted ? "accepted" : "rejected"} ${result.results.length} proposal(s)\n`);
|
|
155
|
+
if (result.rejectedReasons.length > 0) process.stdout.write(`rejected: ${result.rejectedReasons.join("; ")}\n`);
|
|
156
|
+
printStatus(result.plan);
|
|
157
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
|
-
import { type CheckpointUlwLoopArgs, checkpointUlwLoop } from "./checkpoint.js";
|
|
4
3
|
import {
|
|
5
4
|
hasFlag,
|
|
6
5
|
parseCodexGoalJson,
|
|
@@ -10,7 +9,7 @@ import {
|
|
|
10
9
|
readValue,
|
|
11
10
|
} from "./cli-arg-parser.js";
|
|
12
11
|
import { blockedDecisionHandoff, normalizeCodexGoalMode, printJson, printStatus } from "./cli-output.js";
|
|
13
|
-
import {
|
|
12
|
+
import { parseSteeringProposals, printSteerBatchResult, printSteerResult } from "./cli-steering.js";
|
|
14
13
|
import { buildCodexGoalInstruction } from "./codex-goal-instruction.js";
|
|
15
14
|
import { recordEvidence } from "./evidence.js";
|
|
16
15
|
import { isEssentialCriterion } from "./goal-status.js";
|
|
@@ -19,11 +18,10 @@ import { addUlwLoopGoal, createUlwLoopPlan, startNextUlwLoop, summarizeUlwLoopPl
|
|
|
19
18
|
import { readUlwLoopPlan } from "./plan-io.js";
|
|
20
19
|
import { recordFinalReviewBlockers } from "./review-blockers.js";
|
|
21
20
|
import { steerUlwLoop } from "./steering.js";
|
|
21
|
+
import { steerUlwLoopBatch } from "./steering-batch.js";
|
|
22
22
|
import type { UlwLoopItem } from "./types.js";
|
|
23
23
|
import { UlwLoopError } from "./types.js";
|
|
24
24
|
|
|
25
|
-
type CheckpointStatus = "complete" | "failed" | "blocked";
|
|
26
|
-
|
|
27
25
|
export async function createGoals(
|
|
28
26
|
repoRoot: string,
|
|
29
27
|
argv: readonly string[],
|
|
@@ -42,12 +40,14 @@ export async function createGoals(
|
|
|
42
40
|
"ULW_LOOP_BRIEF_REQUIRED",
|
|
43
41
|
);
|
|
44
42
|
}
|
|
43
|
+
const validationBatchesJson = readValue(argv, "--validation-batch-json");
|
|
45
44
|
const plan = await createUlwLoopPlan(
|
|
46
45
|
repoRoot,
|
|
47
46
|
{
|
|
48
47
|
brief,
|
|
49
48
|
codexGoalMode: normalizeCodexGoalMode(readValue(argv, "--codex-goal-mode")),
|
|
50
49
|
force: hasFlag(argv, "--force"),
|
|
50
|
+
...(validationBatchesJson === undefined ? {} : { validationBatchesJson }),
|
|
51
51
|
},
|
|
52
52
|
scope,
|
|
53
53
|
);
|
|
@@ -105,44 +105,21 @@ export async function completeGoals(
|
|
|
105
105
|
return 0;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
export async function checkpoint(
|
|
109
|
-
repoRoot: string,
|
|
110
|
-
argv: readonly string[],
|
|
111
|
-
json: boolean,
|
|
112
|
-
scope?: UlwLoopScope,
|
|
113
|
-
): Promise<number> {
|
|
114
|
-
const goalId = required(argv, "--goal-id");
|
|
115
|
-
const statusValue = checkpointStatus(required(argv, "--status"));
|
|
116
|
-
const evidence = required(argv, "--evidence");
|
|
117
|
-
const codexGoalJson = await parseCodexGoalJson(
|
|
118
|
-
statusValue === "complete" ? required(argv, "--codex-goal-json") : readValue(argv, "--codex-goal-json"),
|
|
119
|
-
);
|
|
120
|
-
if (statusValue === "complete" && codexGoalJson === undefined) {
|
|
121
|
-
throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
|
|
122
|
-
}
|
|
123
|
-
const qualityGateJson = readValue(argv, "--quality-gate-json");
|
|
124
|
-
const args: CheckpointUlwLoopArgs = {
|
|
125
|
-
goalId,
|
|
126
|
-
status: statusValue,
|
|
127
|
-
evidence,
|
|
128
|
-
...(codexGoalJson === undefined ? {} : { codexGoalJson }),
|
|
129
|
-
...(qualityGateJson === undefined ? {} : { qualityGateJson }),
|
|
130
|
-
};
|
|
131
|
-
const result = await checkpointUlwLoop(repoRoot, args, scope);
|
|
132
|
-
if (json) printJson({ ok: true, ...result, summary: summarizeUlwLoopPlan(result.plan) });
|
|
133
|
-
else process.stdout.write(`ulw-loop checkpoint: ${result.goal.id} -> ${result.goal.status}\n`);
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
108
|
export async function steer(
|
|
138
109
|
repoRoot: string,
|
|
139
110
|
argv: readonly string[],
|
|
140
111
|
json: boolean,
|
|
141
112
|
scope?: UlwLoopScope,
|
|
142
113
|
): Promise<number> {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
114
|
+
const proposals = await parseSteeringProposals(argv);
|
|
115
|
+
const single = proposals[0];
|
|
116
|
+
if (single !== undefined && proposals.length === 1 && readValue(argv, "--proposals-json") === undefined) {
|
|
117
|
+
const result = await steerUlwLoop(repoRoot, single, scope);
|
|
118
|
+
printSteerResult(result, json);
|
|
119
|
+
return result.accepted ? 0 : 1;
|
|
120
|
+
}
|
|
121
|
+
const result = await steerUlwLoopBatch(repoRoot, proposals, scope);
|
|
122
|
+
printSteerBatchResult(result, json);
|
|
146
123
|
return result.accepted ? 0 : 1;
|
|
147
124
|
}
|
|
148
125
|
|
|
@@ -245,15 +222,6 @@ function required(argv: readonly string[], flag: string): string {
|
|
|
245
222
|
throw new UlwLoopError(`Missing ${flag}.`, "ULW_LOOP_ARGUMENT_MISSING", { details: { flag } });
|
|
246
223
|
}
|
|
247
224
|
|
|
248
|
-
function checkpointStatus(value: string): CheckpointStatus {
|
|
249
|
-
if (value === "complete" || value === "failed" || value === "blocked") return value;
|
|
250
|
-
throw new UlwLoopError(
|
|
251
|
-
"Missing or invalid --status; expected complete, failed, or blocked.",
|
|
252
|
-
"ULW_LOOP_STATUS_INVALID",
|
|
253
|
-
{ details: { status: value } },
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
225
|
function findGoal(plan: { readonly goals: readonly UlwLoopItem[] }, goalId: string): UlwLoopItem {
|
|
258
226
|
const goal = plan.goals.find((candidate) => candidate.id === goalId);
|
|
259
227
|
if (goal !== undefined) return goal;
|
|
@@ -60,5 +60,7 @@ export const ULW_LOOP_LEDGER_EVENT_KINDS = [
|
|
|
60
60
|
"criterion_failed",
|
|
61
61
|
"criterion_blocked",
|
|
62
62
|
"criteria_revised",
|
|
63
|
+
"batch_closed",
|
|
64
|
+
"batch_updated",
|
|
63
65
|
] as const satisfies readonly string[];
|
|
64
66
|
export type UlwLoopLedgerEventKind = (typeof ULW_LOOP_LEDGER_EVENT_KINDS)[number];
|
|
@@ -55,6 +55,12 @@ export interface UlwLoopAggregateCompletion {
|
|
|
55
55
|
codexGoal?: unknown;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export interface UlwLoopValidationBatch {
|
|
59
|
+
readonly batchId: string;
|
|
60
|
+
readonly memberIds: readonly string[];
|
|
61
|
+
readonly finalGoalId: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
export interface UlwLoopPlan {
|
|
59
65
|
version: 1;
|
|
60
66
|
evidenceLayoutVersion?: 2;
|
|
@@ -68,6 +74,7 @@ export interface UlwLoopPlan {
|
|
|
68
74
|
codexObjectiveAliases?: string[];
|
|
69
75
|
aggregateCompletion?: UlwLoopAggregateCompletion;
|
|
70
76
|
activeGoalId?: string;
|
|
77
|
+
validationBatches?: readonly UlwLoopValidationBatch[];
|
|
71
78
|
goals: UlwLoopItem[];
|
|
72
79
|
}
|
|
73
80
|
|
|
@@ -21,6 +21,10 @@ function isResolvedStatus(status: UlwLoopStatus): boolean {
|
|
|
21
21
|
return status === "complete";
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export function isMemberResolved(goal: UlwLoopItem, plan: UlwLoopPlan): boolean {
|
|
25
|
+
return isResolvedStatus(goal.status) || isSupersededResolved(goal, plan);
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
function isSupersededResolved(goal: UlwLoopItem, plan: UlwLoopPlan): boolean {
|
|
25
29
|
if (goal.steeringStatus !== "superseded") return false;
|
|
26
30
|
const replacements = goal.supersededBy ?? [];
|
|
@@ -16,6 +16,7 @@ import { appendGoalToPlan, deriveGoalCandidates, makeGoal } from "./plan-goal-fa
|
|
|
16
16
|
import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
|
|
17
17
|
import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "./types.js";
|
|
18
18
|
import { iso, UlwLoopError } from "./types.js";
|
|
19
|
+
import { parseValidationBatches } from "./validation-batch.js";
|
|
19
20
|
|
|
20
21
|
export { deriveGoalCandidates, seedDefaultSuccessCriteria } from "./plan-goal-factory.js";
|
|
21
22
|
|
|
@@ -57,7 +58,7 @@ function clearGoalBlockerFields(goal: UlwLoopItem): void {
|
|
|
57
58
|
|
|
58
59
|
export async function createUlwLoopPlan(
|
|
59
60
|
repoRoot: string,
|
|
60
|
-
args: { brief: string; codexGoalMode?: UlwLoopCodexGoalMode; force?: boolean },
|
|
61
|
+
args: { brief: string; codexGoalMode?: UlwLoopCodexGoalMode; force?: boolean; validationBatchesJson?: string },
|
|
61
62
|
scope?: UlwLoopScope,
|
|
62
63
|
): Promise<UlwLoopPlan> {
|
|
63
64
|
return withUlwLoopMutationLock(repoRoot, scope, async () => {
|
|
@@ -84,6 +85,8 @@ export async function createUlwLoopPlan(
|
|
|
84
85
|
codexGoalMode: args.codexGoalMode ?? "aggregate",
|
|
85
86
|
goals,
|
|
86
87
|
};
|
|
88
|
+
const validationBatches = await parseValidationBatches(args.validationBatchesJson, goals);
|
|
89
|
+
if (validationBatches !== undefined) plan.validationBatches = validationBatches;
|
|
87
90
|
if (plan.codexGoalMode === "aggregate") plan.codexObjective = aggregateCodexObjectiveForScope(scope);
|
|
88
91
|
await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
|
|
89
92
|
await writeFile(
|
|
@@ -27,7 +27,12 @@ function isLegacyEnumeratedAggregateObjective(objective: string | undefined): ob
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function isSteeringKind(value: unknown): value is UlwLoopLedgerEntry["kind"] {
|
|
30
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
value === "steering_accepted" ||
|
|
32
|
+
value === "steering_rejected" ||
|
|
33
|
+
value === "criteria_revised" ||
|
|
34
|
+
value === "batch_updated"
|
|
35
|
+
);
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
export async function withUlwLoopMutationLock<T>(repoRoot: string, fn: () => Promise<T>): Promise<T>;
|
|
@@ -112,8 +117,21 @@ export async function writePlan(repoRoot: string, plan: UlwLoopPlan, scope?: Ulw
|
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
export async function appendLedger(repoRoot: string, entry: UlwLoopLedgerEntry, scope?: UlwLoopScope): Promise<void> {
|
|
120
|
+
await appendLedgerEntries(repoRoot, [entry], scope);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function appendLedgerEntries(
|
|
124
|
+
repoRoot: string,
|
|
125
|
+
entries: readonly UlwLoopLedgerEntry[],
|
|
126
|
+
scope?: UlwLoopScope,
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
if (entries.length === 0) return;
|
|
115
129
|
await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
|
|
116
|
-
await appendFile(
|
|
130
|
+
await appendFile(
|
|
131
|
+
ulwLoopLedgerPath(repoRoot, scope),
|
|
132
|
+
`${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`,
|
|
133
|
+
"utf8",
|
|
134
|
+
);
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { invalid, literal, textField } from "./quality-gate-fields.js";
|
|
2
2
|
|
|
3
3
|
export function passedVerdict(value: unknown, field: string): "passed" {
|
|
4
4
|
if (value === "not_applicable") invalid(`${field} must not be not_applicable.`, field);
|