@lumenflow/cli 3.1.1 → 3.1.3
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 +36 -35
- package/dist/agent-issues-query.js +13 -8
- package/dist/agent-log-issue.js +15 -4
- package/dist/agent-session-end.js +15 -4
- package/dist/agent-session.js +18 -6
- package/dist/backlog-prune.js +1 -1
- package/dist/commands/integrate.js +32 -18
- package/dist/config-get.js +27 -15
- package/dist/config-set.js +104 -37
- package/dist/delegation-list.js +1 -1
- package/dist/doctor.js +19 -13
- package/dist/file-delete.js +1 -1
- package/dist/file-edit.js +1 -1
- package/dist/file-read.js +1 -1
- package/dist/file-write.js +1 -1
- package/dist/flow-bottlenecks.js +1 -1
- package/dist/flow-report.js +10 -9
- package/dist/gates.js +3 -2
- package/dist/git-branch.js +1 -1
- package/dist/git-diff.js +1 -1
- package/dist/git-log.js +1 -1
- package/dist/init.js +244 -61
- package/dist/initiative-add-wu.js +1 -1
- package/dist/initiative-bulk-assign-wus.js +1 -1
- package/dist/initiative-create.js +2 -2
- package/dist/initiative-edit.js +1 -1
- package/dist/initiative-list.js +1 -1
- package/dist/initiative-plan.js +1 -3
- package/dist/initiative-status.js +47 -6
- package/dist/lane-edit.js +19 -10
- package/dist/lane-health.js +13 -24
- package/dist/lane-lock.js +4 -5
- package/dist/lane-setup.js +5 -5
- package/dist/lane-status.js +4 -5
- package/dist/lane-suggest.js +9 -7
- package/dist/lane-validate.js +4 -5
- package/dist/lumenflow-upgrade.js +17 -11
- package/dist/mem-checkpoint.js +1 -1
- package/dist/mem-cleanup.js +6 -23
- package/dist/mem-context.js +1 -1
- package/dist/mem-create.js +1 -1
- package/dist/mem-delete.js +1 -1
- package/dist/mem-export.js +1 -1
- package/dist/mem-inbox.js +1 -1
- package/dist/mem-init.js +1 -1
- package/dist/mem-ready.js +1 -1
- package/dist/mem-recover.js +1 -1
- package/dist/mem-signal.js +1 -1
- package/dist/mem-start.js +1 -1
- package/dist/mem-summarize.js +8 -7
- package/dist/mem-triage.js +7 -5
- package/dist/metrics-cli.js +1 -1
- package/dist/metrics-snapshot.js +1 -1
- package/dist/onboard.js +295 -120
- package/dist/orchestrate-init-status.js +12 -7
- package/dist/orchestrate-initiative.js +23 -12
- package/dist/orchestrate-monitor.js +20 -8
- package/dist/pack-scaffold.js +1 -1
- package/dist/plan-create.js +1 -3
- package/dist/plan-edit.js +1 -3
- package/dist/plan-link.js +1 -3
- package/dist/plan-promote.js +1 -3
- package/dist/release.js +1 -3
- package/dist/signal-cleanup.js +4 -18
- package/dist/state-bootstrap.js +11 -8
- package/dist/state-cleanup.js +5 -19
- package/dist/state-doctor.js +213 -9
- package/dist/task-claim.js +1 -1
- package/dist/validate.js +1 -1
- package/dist/workspace-init.js +61 -61
- package/dist/wu-block.js +1 -1
- package/dist/wu-brief.js +1 -1
- package/dist/wu-claim.js +1 -1
- package/dist/wu-cleanup.js +1 -1
- package/dist/wu-create.js +3 -3
- package/dist/wu-delegate.js +1 -1
- package/dist/wu-deps.js +1 -1
- package/dist/wu-done.js +66 -34
- package/dist/wu-edit.js +1 -1
- package/dist/wu-infer-lane.js +1 -1
- package/dist/wu-preflight.js +1 -1
- package/dist/wu-prep.js +1 -1
- package/dist/wu-proto.js +1 -1
- package/dist/wu-prune.js +1 -1
- package/dist/wu-recover.js +1 -1
- package/dist/wu-release.js +1 -1
- package/dist/wu-repair.js +1 -1
- package/dist/wu-sandbox.js +40 -27
- package/dist/wu-status.js +1 -1
- package/dist/wu-unblock.js +1 -1
- package/dist/wu-unlock-lane.js +1 -1
- package/dist/wu-validate.js +1 -1
- package/package.json +12 -8
- package/packs/software-delivery/constants.ts +10 -0
- package/packs/software-delivery/extensions.ts +140 -0
- package/packs/software-delivery/gate-policies.ts +134 -0
- package/packs/software-delivery/index.ts +8 -0
- package/packs/software-delivery/manifest-schema.ts +236 -0
- package/packs/software-delivery/manifest.ts +417 -0
- package/packs/software-delivery/manifest.yaml +711 -0
- package/packs/software-delivery/pack-registration.ts +113 -0
- package/packs/software-delivery/tool-impl/agent-tools.ts +263 -0
- package/packs/software-delivery/tool-impl/delegation-tools.ts +66 -0
- package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +219 -0
- package/packs/software-delivery/tool-impl/git-runner.ts +113 -0
- package/packs/software-delivery/tool-impl/git-tools.ts +316 -0
- package/packs/software-delivery/tool-impl/index.ts +15 -0
- package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +720 -0
- package/packs/software-delivery/tool-impl/lane-lock.ts +246 -0
- package/packs/software-delivery/tool-impl/memory-tools.ts +415 -0
- package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +21 -0
- package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +328 -0
- package/packs/software-delivery/tool-impl/runtime-native-tools.ts +687 -0
- package/packs/software-delivery/tool-impl/worker-loader.ts +52 -0
- package/packs/software-delivery/tool-impl/worktree-tools.ts +46 -0
- package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +759 -0
- package/packs/software-delivery/tools/delegation-tools.ts +23 -0
- package/packs/software-delivery/tools/git-tools.ts +55 -0
- package/packs/software-delivery/tools/index.ts +8 -0
- package/packs/software-delivery/tools/lane-lock-tool.ts +37 -0
- package/packs/software-delivery/tools/types.ts +71 -0
- package/packs/software-delivery/tools/worktree-tools.ts +49 -0
- package/templates/core/LUMENFLOW.md.template +3 -3
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
- package/templates/core/ai/onboarding/lumenflow-force-usage.md.template +1 -1
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +5 -5
- package/templates/core/ai/onboarding/starting-prompt.md.template +3 -3
- package/templates/core/ai/onboarding/vendor-support.md.template +1 -1
- package/templates/core/ai/onboarding/wu-create-checklist.md.template +1 -1
- package/dist/agent-issues-query.js.map +0 -1
- package/dist/agent-log-issue.js.map +0 -1
- package/dist/agent-session-end.js.map +0 -1
- package/dist/agent-session.js.map +0 -1
- package/dist/backlog-prune.js.map +0 -1
- package/dist/cli-entry-point.js +0 -149
- package/dist/cli-entry-point.js.map +0 -1
- package/dist/commands/integrate.js.map +0 -1
- package/dist/commands.js.map +0 -1
- package/dist/config-get.js.map +0 -1
- package/dist/config-set.js.map +0 -1
- package/dist/delegation-list.js.map +0 -1
- package/dist/deps-add.js +0 -259
- package/dist/deps-add.js.map +0 -1
- package/dist/deps-remove.js +0 -105
- package/dist/deps-remove.js.map +0 -1
- package/dist/docs-sync.js.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/file-delete.js.map +0 -1
- package/dist/file-edit.js.map +0 -1
- package/dist/file-read.js.map +0 -1
- package/dist/file-write.js.map +0 -1
- package/dist/flow-bottlenecks.js.map +0 -1
- package/dist/flow-report.js.map +0 -1
- package/dist/formatters.js +0 -151
- package/dist/formatters.js.map +0 -1
- package/dist/gate-defaults.js +0 -131
- package/dist/gate-defaults.js.map +0 -1
- package/dist/gate-registry.js +0 -73
- package/dist/gate-registry.js.map +0 -1
- package/dist/gates-graceful-degradation.js +0 -153
- package/dist/gates-graceful-degradation.js.map +0 -1
- package/dist/gates-plan-resolvers.js +0 -152
- package/dist/gates-plan-resolvers.js.map +0 -1
- package/dist/gates-runners.js +0 -509
- package/dist/gates-runners.js.map +0 -1
- package/dist/gates-types.js +0 -4
- package/dist/gates-types.js.map +0 -1
- package/dist/gates-utils.js +0 -323
- package/dist/gates-utils.js.map +0 -1
- package/dist/gates.js.map +0 -1
- package/dist/git-branch.js.map +0 -1
- package/dist/git-diff.js.map +0 -1
- package/dist/git-log.js.map +0 -1
- package/dist/git-status.js.map +0 -1
- package/dist/guard-locked.js +0 -172
- package/dist/guard-locked.js.map +0 -1
- package/dist/guard-main-branch.js +0 -217
- package/dist/guard-main-branch.js.map +0 -1
- package/dist/guard-worktree-commit.js +0 -163
- package/dist/guard-worktree-commit.js.map +0 -1
- package/dist/hooks/auto-checkpoint-utils.js +0 -54
- package/dist/hooks/auto-checkpoint-utils.js.map +0 -1
- package/dist/hooks/enforcement-checks.js +0 -399
- package/dist/hooks/enforcement-checks.js.map +0 -1
- package/dist/hooks/enforcement-generator.js +0 -139
- package/dist/hooks/enforcement-generator.js.map +0 -1
- package/dist/hooks/enforcement-sync.js +0 -385
- package/dist/hooks/enforcement-sync.js.map +0 -1
- package/dist/hooks/generators/auto-checkpoint.js +0 -125
- package/dist/hooks/generators/auto-checkpoint.js.map +0 -1
- package/dist/hooks/generators/enforce-worktree.js +0 -190
- package/dist/hooks/generators/enforce-worktree.js.map +0 -1
- package/dist/hooks/generators/index.js +0 -18
- package/dist/hooks/generators/index.js.map +0 -1
- package/dist/hooks/generators/pre-compact-checkpoint.js +0 -136
- package/dist/hooks/generators/pre-compact-checkpoint.js.map +0 -1
- package/dist/hooks/generators/require-wu.js +0 -117
- package/dist/hooks/generators/require-wu.js.map +0 -1
- package/dist/hooks/generators/session-start-recovery.js +0 -103
- package/dist/hooks/generators/session-start-recovery.js.map +0 -1
- package/dist/hooks/generators/signal-utils.js +0 -54
- package/dist/hooks/generators/signal-utils.js.map +0 -1
- package/dist/hooks/generators/warn-incomplete.js +0 -67
- package/dist/hooks/generators/warn-incomplete.js.map +0 -1
- package/dist/hooks/index.js +0 -10
- package/dist/hooks/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init-detection.js +0 -230
- package/dist/init-detection.js.map +0 -1
- package/dist/init-lane-validation.js +0 -143
- package/dist/init-lane-validation.js.map +0 -1
- package/dist/init-scaffolding.js +0 -158
- package/dist/init-scaffolding.js.map +0 -1
- package/dist/init-templates.js +0 -1964
- package/dist/init-templates.js.map +0 -1
- package/dist/init.js.map +0 -1
- package/dist/initiative-add-wu.js.map +0 -1
- package/dist/initiative-bulk-assign-wus.js.map +0 -1
- package/dist/initiative-create.js.map +0 -1
- package/dist/initiative-edit.js.map +0 -1
- package/dist/initiative-list.js.map +0 -1
- package/dist/initiative-plan.js.map +0 -1
- package/dist/initiative-remove-wu.js.map +0 -1
- package/dist/initiative-status.js.map +0 -1
- package/dist/lane-edit.js.map +0 -1
- package/dist/lane-health.js.map +0 -1
- package/dist/lane-lifecycle-process.js +0 -366
- package/dist/lane-lifecycle-process.js.map +0 -1
- package/dist/lane-lock.js.map +0 -1
- package/dist/lane-setup.js.map +0 -1
- package/dist/lane-status.js.map +0 -1
- package/dist/lane-suggest.js.map +0 -1
- package/dist/lane-validate.js.map +0 -1
- package/dist/lifecycle-regression-harness.js +0 -181
- package/dist/lifecycle-regression-harness.js.map +0 -1
- package/dist/lumenflow-upgrade.js.map +0 -1
- package/dist/mem-checkpoint.js.map +0 -1
- package/dist/mem-cleanup.js.map +0 -1
- package/dist/mem-context.js.map +0 -1
- package/dist/mem-create.js.map +0 -1
- package/dist/mem-delete.js.map +0 -1
- package/dist/mem-export.js.map +0 -1
- package/dist/mem-inbox.js.map +0 -1
- package/dist/mem-index.js +0 -214
- package/dist/mem-index.js.map +0 -1
- package/dist/mem-init.js.map +0 -1
- package/dist/mem-profile.js +0 -210
- package/dist/mem-profile.js.map +0 -1
- package/dist/mem-promote.js +0 -257
- package/dist/mem-promote.js.map +0 -1
- package/dist/mem-ready.js.map +0 -1
- package/dist/mem-recover.js.map +0 -1
- package/dist/mem-signal.js.map +0 -1
- package/dist/mem-start.js.map +0 -1
- package/dist/mem-summarize.js.map +0 -1
- package/dist/mem-triage.js.map +0 -1
- package/dist/merge-block.js +0 -225
- package/dist/merge-block.js.map +0 -1
- package/dist/metrics-cli.js.map +0 -1
- package/dist/metrics-snapshot.js.map +0 -1
- package/dist/onboard.js.map +0 -1
- package/dist/onboarding-smoke-test.js +0 -418
- package/dist/onboarding-smoke-test.js.map +0 -1
- package/dist/orchestrate-init-status.js.map +0 -1
- package/dist/orchestrate-initiative.js.map +0 -1
- package/dist/orchestrate-monitor.js.map +0 -1
- package/dist/pack-author.js.map +0 -1
- package/dist/pack-hash.js.map +0 -1
- package/dist/pack-install.js.map +0 -1
- package/dist/pack-publish.js.map +0 -1
- package/dist/pack-scaffold.js.map +0 -1
- package/dist/pack-search.js.map +0 -1
- package/dist/pack-validate.js.map +0 -1
- package/dist/plan-create.js.map +0 -1
- package/dist/plan-edit.js.map +0 -1
- package/dist/plan-link.js.map +0 -1
- package/dist/plan-promote.js.map +0 -1
- package/dist/public-manifest.js +0 -920
- package/dist/public-manifest.js.map +0 -1
- package/dist/release.js.map +0 -1
- package/dist/rotate-progress.js +0 -253
- package/dist/rotate-progress.js.map +0 -1
- package/dist/session-coordinator.js +0 -303
- package/dist/session-coordinator.js.map +0 -1
- package/dist/shared-validators.js +0 -81
- package/dist/shared-validators.js.map +0 -1
- package/dist/signal-cleanup.js.map +0 -1
- package/dist/state-bootstrap.js.map +0 -1
- package/dist/state-cleanup.js.map +0 -1
- package/dist/state-doctor-fix.js +0 -226
- package/dist/state-doctor-fix.js.map +0 -1
- package/dist/state-doctor-stamps.js +0 -23
- package/dist/state-doctor-stamps.js.map +0 -1
- package/dist/state-doctor.js.map +0 -1
- package/dist/strict-progress.js +0 -255
- package/dist/strict-progress.js.map +0 -1
- package/dist/sync-templates.js.map +0 -1
- package/dist/task-claim.js.map +0 -1
- package/dist/trace-gen.js +0 -401
- package/dist/trace-gen.js.map +0 -1
- package/dist/validate-agent-skills.js +0 -223
- package/dist/validate-agent-skills.js.map +0 -1
- package/dist/validate-agent-sync.js +0 -151
- package/dist/validate-agent-sync.js.map +0 -1
- package/dist/validate-backlog-sync.js +0 -77
- package/dist/validate-backlog-sync.js.map +0 -1
- package/dist/validate-skills-spec.js +0 -211
- package/dist/validate-skills-spec.js.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/validator-defaults.js +0 -107
- package/dist/validator-defaults.js.map +0 -1
- package/dist/validator-registry.js +0 -71
- package/dist/validator-registry.js.map +0 -1
- package/dist/workspace-init.js.map +0 -1
- package/dist/wu-block.js.map +0 -1
- package/dist/wu-brief.js.map +0 -1
- package/dist/wu-claim-branch.js +0 -123
- package/dist/wu-claim-branch.js.map +0 -1
- package/dist/wu-claim-cloud.js +0 -79
- package/dist/wu-claim-cloud.js.map +0 -1
- package/dist/wu-claim-mode.js +0 -82
- package/dist/wu-claim-mode.js.map +0 -1
- package/dist/wu-claim-output.js +0 -85
- package/dist/wu-claim-output.js.map +0 -1
- package/dist/wu-claim-repair-guidance.js +0 -12
- package/dist/wu-claim-repair-guidance.js.map +0 -1
- package/dist/wu-claim-resume-handler.js +0 -87
- package/dist/wu-claim-resume-handler.js.map +0 -1
- package/dist/wu-claim-state.js +0 -581
- package/dist/wu-claim-state.js.map +0 -1
- package/dist/wu-claim-validation.js +0 -457
- package/dist/wu-claim-validation.js.map +0 -1
- package/dist/wu-claim-worktree.js +0 -223
- package/dist/wu-claim-worktree.js.map +0 -1
- package/dist/wu-claim.js.map +0 -1
- package/dist/wu-cleanup-cloud.js +0 -78
- package/dist/wu-cleanup-cloud.js.map +0 -1
- package/dist/wu-cleanup.js.map +0 -1
- package/dist/wu-code-path-coverage.js +0 -83
- package/dist/wu-code-path-coverage.js.map +0 -1
- package/dist/wu-create-cloud.js +0 -30
- package/dist/wu-create-cloud.js.map +0 -1
- package/dist/wu-create-content.js +0 -264
- package/dist/wu-create-content.js.map +0 -1
- package/dist/wu-create-readiness.js +0 -59
- package/dist/wu-create-readiness.js.map +0 -1
- package/dist/wu-create-validation.js +0 -128
- package/dist/wu-create-validation.js.map +0 -1
- package/dist/wu-create.js.map +0 -1
- package/dist/wu-delegate.js.map +0 -1
- package/dist/wu-delete.js.map +0 -1
- package/dist/wu-deps.js.map +0 -1
- package/dist/wu-done-auto-cleanup.js +0 -203
- package/dist/wu-done-auto-cleanup.js.map +0 -1
- package/dist/wu-done-check.js +0 -38
- package/dist/wu-done-check.js.map +0 -1
- package/dist/wu-done-cloud.js +0 -48
- package/dist/wu-done-cloud.js.map +0 -1
- package/dist/wu-done-decay.js +0 -86
- package/dist/wu-done-decay.js.map +0 -1
- package/dist/wu-done.js.map +0 -1
- package/dist/wu-edit-operations.js +0 -399
- package/dist/wu-edit-operations.js.map +0 -1
- package/dist/wu-edit-validators.js +0 -282
- package/dist/wu-edit-validators.js.map +0 -1
- package/dist/wu-edit.js.map +0 -1
- package/dist/wu-infer-lane.js.map +0 -1
- package/dist/wu-preflight.js.map +0 -1
- package/dist/wu-prep.js.map +0 -1
- package/dist/wu-proto.js.map +0 -1
- package/dist/wu-prune.js.map +0 -1
- package/dist/wu-recover.js.map +0 -1
- package/dist/wu-release.js.map +0 -1
- package/dist/wu-repair.js.map +0 -1
- package/dist/wu-sandbox.js.map +0 -1
- package/dist/wu-spawn-completion.js +0 -33
- package/dist/wu-spawn-completion.js.map +0 -1
- package/dist/wu-spawn-prompt-builders.js +0 -1197
- package/dist/wu-spawn-prompt-builders.js.map +0 -1
- package/dist/wu-spawn-strategy-resolver.js +0 -322
- package/dist/wu-spawn-strategy-resolver.js.map +0 -1
- package/dist/wu-spawn.js +0 -59
- package/dist/wu-spawn.js.map +0 -1
- package/dist/wu-state-cloud.js +0 -41
- package/dist/wu-state-cloud.js.map +0 -1
- package/dist/wu-status.js.map +0 -1
- package/dist/wu-unblock.js.map +0 -1
- package/dist/wu-unlock-lane.js.map +0 -1
- package/dist/wu-validate.js.map +0 -1
package/dist/wu-done.js
CHANGED
|
@@ -126,6 +126,63 @@ const MEMORY_CHECKPOINT_NOTES = {
|
|
|
126
126
|
PRE_GATES: 'Pre-gates checkpoint for recovery if gates fail',
|
|
127
127
|
};
|
|
128
128
|
const MEMORY_SIGNAL_WINDOW_MS = 60 * 60 * 1000; // 1 hour for recent signals
|
|
129
|
+
export const CHECKPOINT_GATE_MODES = {
|
|
130
|
+
OFF: 'off',
|
|
131
|
+
WARN: 'warn',
|
|
132
|
+
BLOCK: 'block',
|
|
133
|
+
};
|
|
134
|
+
const CHECKPOINT_GATE_CONFIG = {
|
|
135
|
+
PATH: 'memory.enforcement.require_checkpoint_for_done',
|
|
136
|
+
COMMAND_PREFIX: 'pnpm mem:checkpoint --wu',
|
|
137
|
+
WARN_TAG: 'WU-1998',
|
|
138
|
+
};
|
|
139
|
+
function buildCheckpointGateBlockMessage(id) {
|
|
140
|
+
return (`${STRING_LITERALS.NEWLINE}${LOG_PREFIX.DONE} ${EMOJI.FAILURE} No checkpoints found for ${id} session.${STRING_LITERALS.NEWLINE}` +
|
|
141
|
+
`${LOG_PREFIX.DONE} ${CHECKPOINT_GATE_CONFIG.PATH} is set to '${CHECKPOINT_GATE_MODES.BLOCK}'.${STRING_LITERALS.NEWLINE}` +
|
|
142
|
+
`${LOG_PREFIX.DONE} Create a checkpoint before completing: ${CHECKPOINT_GATE_CONFIG.COMMAND_PREFIX} ${id}${STRING_LITERALS.NEWLINE}`);
|
|
143
|
+
}
|
|
144
|
+
function buildCheckpointGateWarnMessages(id) {
|
|
145
|
+
return [
|
|
146
|
+
`${STRING_LITERALS.NEWLINE}${LOG_PREFIX.DONE} ${EMOJI.INFO} ${CHECKPOINT_GATE_CONFIG.WARN_TAG}: No prior checkpoints recorded for ${id} in this session.`,
|
|
147
|
+
`${LOG_PREFIX.DONE} A pre-gates checkpoint will be created automatically by wu:done.`,
|
|
148
|
+
`${LOG_PREFIX.DONE} For earlier crash recovery, run '${CHECKPOINT_GATE_CONFIG.COMMAND_PREFIX} ${id}' after each acceptance criterion, before gates, or every 30 tool calls.${STRING_LITERALS.NEWLINE}`,
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
export function resolveCheckpointGateMode(mode) {
|
|
152
|
+
if (mode === CHECKPOINT_GATE_MODES.OFF) {
|
|
153
|
+
return CHECKPOINT_GATE_MODES.OFF;
|
|
154
|
+
}
|
|
155
|
+
if (mode === CHECKPOINT_GATE_MODES.BLOCK) {
|
|
156
|
+
return CHECKPOINT_GATE_MODES.BLOCK;
|
|
157
|
+
}
|
|
158
|
+
return CHECKPOINT_GATE_MODES.WARN;
|
|
159
|
+
}
|
|
160
|
+
export async function enforceCheckpointGateForDone({ id, workspacePath, mode, queryByWuFn = queryByWu, hasSessionCheckpointsFn = hasSessionCheckpoints, log = console.log, blocker = (message) => {
|
|
161
|
+
die(message);
|
|
162
|
+
}, }) {
|
|
163
|
+
if (mode === CHECKPOINT_GATE_MODES.OFF) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
let wuNodes;
|
|
167
|
+
try {
|
|
168
|
+
wuNodes = await queryByWuFn(workspacePath, id);
|
|
169
|
+
if (hasSessionCheckpointsFn(id, wuNodes)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Fail-open: checkpoint discovery issues should not block wu:done.
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (mode === CHECKPOINT_GATE_MODES.BLOCK) {
|
|
178
|
+
blocker(buildCheckpointGateBlockMessage(id));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const warnMessages = buildCheckpointGateWarnMessages(id);
|
|
182
|
+
for (const message of warnMessages) {
|
|
183
|
+
log(message);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
129
186
|
function normalizeWUDocLike(doc) {
|
|
130
187
|
if (!doc || typeof doc !== 'object') {
|
|
131
188
|
return {};
|
|
@@ -2078,7 +2135,7 @@ function printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree,
|
|
|
2078
2135
|
console.log(`\n${LOG_PREFIX.DONE} HUD: WU=${id} status=${yamlStatus} stamp=${stampExists} locked=${yamlLocked} mode=${mode} branch=${branch} worktree=${worktreeDisplay}`);
|
|
2079
2136
|
}
|
|
2080
2137
|
// eslint-disable-next-line sonarjs/cognitive-complexity -- Pre-existing complexity, refactor tracked separately
|
|
2081
|
-
async function main() {
|
|
2138
|
+
export async function main() {
|
|
2082
2139
|
// Allow pre-push hook to recognize wu:done automation (WU-1030)
|
|
2083
2140
|
process.env.LUMENFLOW_WU_TOOL = 'wu-done';
|
|
2084
2141
|
// Validate CLI arguments and WU ID format (extracted to wu-done-validators.ts)
|
|
@@ -2198,39 +2255,14 @@ async function main() {
|
|
|
2198
2255
|
});
|
|
2199
2256
|
// Step 0: Run gates (WU-1215: extracted to executeGates function)
|
|
2200
2257
|
const worktreePath = effectiveWorktreePath;
|
|
2201
|
-
// WU-1471 AC3: Config-driven checkpoint gate
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
if (requireCheckpoint !== 'off') {
|
|
2210
|
-
const wuNodes = await queryByWu(worktreePath || mainCheckoutPath, id);
|
|
2211
|
-
if (!hasSessionCheckpoints(id, wuNodes)) {
|
|
2212
|
-
if (requireCheckpoint === 'block') {
|
|
2213
|
-
die(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} No checkpoints found for ${id} session.\n` +
|
|
2214
|
-
`${LOG_PREFIX.DONE} memory.enforcement.require_checkpoint_for_done is set to 'block'.\n` +
|
|
2215
|
-
`${LOG_PREFIX.DONE} Create a checkpoint before completing: pnpm mem:checkpoint --wu ${id}\n`);
|
|
2216
|
-
}
|
|
2217
|
-
else {
|
|
2218
|
-
// 'warn' mode (default)
|
|
2219
|
-
console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1943: No checkpoints found for ${id} session.`);
|
|
2220
|
-
console.log(`${LOG_PREFIX.DONE} Consider using 'pnpm mem:checkpoint --wu ${id}' periodically for crash recovery.`);
|
|
2221
|
-
console.log(`${LOG_PREFIX.DONE} Checkpoint triggers: after each acceptance criterion, before gates, every 30 tool calls.\n`);
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
catch (checkpointErr) {
|
|
2227
|
-
// Non-blocking in 'warn' mode: checkpoint check failure should not block wu:done
|
|
2228
|
-
// In 'block' mode, die() already exited, so this only catches non-die errors
|
|
2229
|
-
if (checkpointErr instanceof Error && checkpointErr.message?.includes('No checkpoints found')) {
|
|
2230
|
-
throw checkpointErr; // Re-throw die() errors
|
|
2231
|
-
}
|
|
2232
|
-
// Otherwise silently allow - fail-open
|
|
2233
|
-
}
|
|
2258
|
+
// WU-1471 AC3 + WU-1998: Config-driven checkpoint gate with accurate warn-mode messaging.
|
|
2259
|
+
const checkpointGateConfig = getConfig();
|
|
2260
|
+
const requireCheckpoint = resolveCheckpointGateMode(checkpointGateConfig.memory?.enforcement?.require_checkpoint_for_done);
|
|
2261
|
+
await enforceCheckpointGateForDone({
|
|
2262
|
+
id,
|
|
2263
|
+
workspacePath: worktreePath || mainCheckoutPath,
|
|
2264
|
+
mode: requireCheckpoint,
|
|
2265
|
+
});
|
|
2234
2266
|
// WU-1663: Preparation complete - transition to gating state
|
|
2235
2267
|
pipelineActor.send({ type: WU_DONE_EVENTS.PREPARATION_COMPLETE });
|
|
2236
2268
|
// WU-1663: Wrap gates in try/catch to send pipeline failure event
|
package/dist/wu-edit.js
CHANGED
|
@@ -247,7 +247,7 @@ async function ensureCleanWorkingTree() {
|
|
|
247
247
|
* Main entry point
|
|
248
248
|
*/
|
|
249
249
|
// eslint-disable-next-line sonarjs/cognitive-complexity -- Pre-existing complexity, refactor tracked separately
|
|
250
|
-
async function main() {
|
|
250
|
+
export async function main() {
|
|
251
251
|
const opts = parseArgs();
|
|
252
252
|
const { id } = opts;
|
|
253
253
|
console.log(`${PREFIX} Starting WU edit for ${id}`);
|
package/dist/wu-infer-lane.js
CHANGED
package/dist/wu-preflight.js
CHANGED
|
@@ -54,7 +54,7 @@ function detectWorktreePath(id) {
|
|
|
54
54
|
/**
|
|
55
55
|
* Main entry point
|
|
56
56
|
*/
|
|
57
|
-
async function main() {
|
|
57
|
+
export async function main() {
|
|
58
58
|
const PREFIX = LOG_PREFIX.PREFLIGHT;
|
|
59
59
|
// WU-1180: Use createWUParser for proper Commander help output
|
|
60
60
|
const args = createWUParser({
|
package/dist/wu-prep.js
CHANGED
package/dist/wu-proto.js
CHANGED
|
@@ -220,7 +220,7 @@ function claimWU(wuId, lane) {
|
|
|
220
220
|
die(`Failed to claim WU: ${error.message}`);
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
-
async function main() {
|
|
223
|
+
export async function main() {
|
|
224
224
|
const args = createWUParser({
|
|
225
225
|
name: 'wu-proto',
|
|
226
226
|
description: 'Create and claim a prototype WU with relaxed validation (rapid prototyping)',
|
package/dist/wu-prune.js
CHANGED
|
@@ -123,7 +123,7 @@ async function validateWorktree(wt) {
|
|
|
123
123
|
}
|
|
124
124
|
return { valid: true, warnings, errors };
|
|
125
125
|
}
|
|
126
|
-
async function main() {
|
|
126
|
+
export async function main() {
|
|
127
127
|
const args = parseArgs(process.argv);
|
|
128
128
|
const PREFIX = LOG_PREFIX.PRUNE;
|
|
129
129
|
if (args.help) {
|
package/dist/wu-recover.js
CHANGED
|
@@ -473,7 +473,7 @@ export async function executeRecoveryAction(action, wuId) {
|
|
|
473
473
|
/**
|
|
474
474
|
* Main entry point
|
|
475
475
|
*/
|
|
476
|
-
async function main() {
|
|
476
|
+
export async function main() {
|
|
477
477
|
const args = createWUParser({
|
|
478
478
|
name: 'wu-recover',
|
|
479
479
|
description: 'Analyze and fix WU state inconsistencies (WU-1090)',
|
package/dist/wu-release.js
CHANGED
|
@@ -47,7 +47,7 @@ export function clearClaimMetadataOnRelease(doc) {
|
|
|
47
47
|
export function shouldUseBranchPrReleasePath(doc) {
|
|
48
48
|
return shouldUseBranchPrStatePath(doc);
|
|
49
49
|
}
|
|
50
|
-
async function main() {
|
|
50
|
+
export async function main() {
|
|
51
51
|
const args = createWUParser({
|
|
52
52
|
name: 'wu-release',
|
|
53
53
|
description: 'Release an orphaned WU from in_progress back to ready state for reclaiming',
|
package/dist/wu-repair.js
CHANGED
|
@@ -212,7 +212,7 @@ async function routeToRepairMode(options) {
|
|
|
212
212
|
}
|
|
213
213
|
return runConsistencyRepairMode(options);
|
|
214
214
|
}
|
|
215
|
-
async function main() {
|
|
215
|
+
export async function main() {
|
|
216
216
|
const options = createProgram();
|
|
217
217
|
validateOptions(options);
|
|
218
218
|
validateModeRequirements(options);
|
package/dist/wu-sandbox.js
CHANGED
|
@@ -11,14 +11,14 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { spawn } from 'node:child_process';
|
|
13
13
|
import { parse as parseYaml } from 'yaml';
|
|
14
|
-
import { createWUParser, WU_OPTIONS, resolveLocation, readWURaw, buildSandboxProfile, resolveSandboxBackendForPlatform, SANDBOX_BACKEND_IDS, createLinuxSandboxBackend, createMacosSandboxBackend, createWindowsSandboxBackend, } from '@lumenflow/core';
|
|
14
|
+
import { createWUParser, WU_OPTIONS, resolveLocation, readWURaw, buildSandboxProfile, resolveSandboxBackendForPlatform, SANDBOX_BACKEND_IDS, createLinuxSandboxBackend, createMacosSandboxBackend, createWindowsSandboxBackend, WORKSPACE_CONFIG_FILE_NAME, WORKSPACE_V2_KEYS, } from '@lumenflow/core';
|
|
15
15
|
import { WU_PATHS, defaultWorktreeFrom } from '@lumenflow/core/wu-paths';
|
|
16
16
|
import { die } from '@lumenflow/core/error-handler';
|
|
17
17
|
import { LOG_PREFIX, EXIT_CODES } from '@lumenflow/core/wu-constants';
|
|
18
18
|
import { runCLI } from './cli-entry-point.js';
|
|
19
19
|
const PREFIX = LOG_PREFIX.CLAIM.replace('wu-claim', 'wu:sandbox');
|
|
20
|
-
const CONFIG_FILE = '.lumenflow.config.yaml';
|
|
21
20
|
const DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR = 'LUMENFLOW_SANDBOX_ALLOW_UNSANDBOXED';
|
|
21
|
+
const SOFTWARE_DELIVERY_KEY = WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY;
|
|
22
22
|
function toNormalizedAbsolute(targetPath) {
|
|
23
23
|
const normalized = path.resolve(targetPath);
|
|
24
24
|
return normalized.length > 1 && normalized.endsWith(path.sep)
|
|
@@ -36,41 +36,54 @@ function parsePolicyConfig(value) {
|
|
|
36
36
|
}
|
|
37
37
|
return value;
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
const configPath = path.join(projectRoot,
|
|
39
|
+
function readWorkspaceSoftwareDelivery(projectRoot) {
|
|
40
|
+
const configPath = path.join(projectRoot, WORKSPACE_CONFIG_FILE_NAME);
|
|
41
41
|
if (!existsSync(configPath)) {
|
|
42
|
-
return
|
|
43
|
-
allowUnsandboxedEnvVar: DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR,
|
|
44
|
-
extraWritableRoots: [],
|
|
45
|
-
denyWritableRoots: [],
|
|
46
|
-
};
|
|
42
|
+
return null;
|
|
47
43
|
}
|
|
48
44
|
try {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
allowUnsandboxedEnvVar,
|
|
63
|
-
extraWritableRoots,
|
|
64
|
-
denyWritableRoots,
|
|
65
|
-
};
|
|
45
|
+
const parsed = parseYaml(readFileSync(configPath, 'utf8'));
|
|
46
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const workspace = parsed;
|
|
50
|
+
const softwareDelivery = workspace[SOFTWARE_DELIVERY_KEY];
|
|
51
|
+
if (!softwareDelivery ||
|
|
52
|
+
typeof softwareDelivery !== 'object' ||
|
|
53
|
+
Array.isArray(softwareDelivery)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return softwareDelivery;
|
|
66
57
|
}
|
|
67
58
|
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function readSandboxPolicy(projectRoot) {
|
|
63
|
+
const softwareDelivery = readWorkspaceSoftwareDelivery(projectRoot);
|
|
64
|
+
if (!softwareDelivery) {
|
|
68
65
|
return {
|
|
69
66
|
allowUnsandboxedEnvVar: DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR,
|
|
70
67
|
extraWritableRoots: [],
|
|
71
68
|
denyWritableRoots: [],
|
|
72
69
|
};
|
|
73
70
|
}
|
|
71
|
+
const sandbox = parsePolicyConfig(softwareDelivery.sandbox);
|
|
72
|
+
const allowUnsandboxedEnvVar = typeof sandbox.allow_unsandboxed_fallback_env === 'string' &&
|
|
73
|
+
sandbox.allow_unsandboxed_fallback_env.trim() !== ''
|
|
74
|
+
? sandbox.allow_unsandboxed_fallback_env.trim()
|
|
75
|
+
: DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR;
|
|
76
|
+
const extraWritableRoots = Array.isArray(sandbox.extra_writable_roots)
|
|
77
|
+
? sandbox.extra_writable_roots.filter((value) => typeof value === 'string')
|
|
78
|
+
: [];
|
|
79
|
+
const denyWritableRoots = Array.isArray(sandbox.deny_writable_roots)
|
|
80
|
+
? sandbox.deny_writable_roots.filter((value) => typeof value === 'string')
|
|
81
|
+
: [];
|
|
82
|
+
return {
|
|
83
|
+
allowUnsandboxedEnvVar,
|
|
84
|
+
extraWritableRoots,
|
|
85
|
+
denyWritableRoots,
|
|
86
|
+
};
|
|
74
87
|
}
|
|
75
88
|
export function extractSandboxCommandFromArgv(argv) {
|
|
76
89
|
const separator = argv.indexOf('--');
|
|
@@ -244,7 +257,7 @@ export async function runWuSandbox(input) {
|
|
|
244
257
|
}
|
|
245
258
|
return runCommand(commandToRun, commandArgs, worktreePath);
|
|
246
259
|
}
|
|
247
|
-
async function main() {
|
|
260
|
+
export async function main() {
|
|
248
261
|
const options = parseWuSandboxOptions(process.argv);
|
|
249
262
|
const exitCode = await runWuSandbox(options);
|
|
250
263
|
process.exit(exitCode);
|
package/dist/wu-status.js
CHANGED
|
@@ -152,7 +152,7 @@ export function getStatusExitCode(context) {
|
|
|
152
152
|
/**
|
|
153
153
|
* Main entry point
|
|
154
154
|
*/
|
|
155
|
-
async function main() {
|
|
155
|
+
export async function main() {
|
|
156
156
|
const args = createWUParser({
|
|
157
157
|
name: 'wu-status',
|
|
158
158
|
description: 'Show WU status, location, and valid commands (WU-1090)',
|
package/dist/wu-unblock.js
CHANGED
|
@@ -114,7 +114,7 @@ function handleWorktreeCreation(args, doc) {
|
|
|
114
114
|
}
|
|
115
115
|
createWorktree(doc, worktreePath, branchName);
|
|
116
116
|
}
|
|
117
|
-
async function main() {
|
|
117
|
+
export async function main() {
|
|
118
118
|
const args = createWUParser({
|
|
119
119
|
name: 'wu-unblock',
|
|
120
120
|
description: 'Unblock a work unit and move it from blocked to in-progress status',
|
package/dist/wu-unlock-lane.js
CHANGED
|
@@ -96,7 +96,7 @@ function showLaneStatus(lane) {
|
|
|
96
96
|
console.log(` pnpm wu:unlock-lane --lane "${lane}" --reason "<explanation>" --force`);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
async function main() {
|
|
99
|
+
export async function main() {
|
|
100
100
|
const args = createWUParser({
|
|
101
101
|
name: 'wu-unlock-lane',
|
|
102
102
|
description: 'Safely unlock a lane lock with audit logging',
|
package/dist/wu-validate.js
CHANGED
|
@@ -139,7 +139,7 @@ function validateAllWUs({ strict = true } = {}) {
|
|
|
139
139
|
/**
|
|
140
140
|
* Main entry point
|
|
141
141
|
*/
|
|
142
|
-
async function main() {
|
|
142
|
+
export async function main() {
|
|
143
143
|
const args = createWUParser({
|
|
144
144
|
name: 'wu-validate',
|
|
145
145
|
description: 'Validate WU YAML files against schema (strict mode by default, WU-1329)',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -142,11 +142,13 @@
|
|
|
142
142
|
"workspace-init": "./dist/workspace-init.js",
|
|
143
143
|
"config-set": "./dist/config-set.js",
|
|
144
144
|
"config-get": "./dist/config-get.js",
|
|
145
|
+
"cloud-connect": "./dist/init.js",
|
|
145
146
|
"lumenflow-onboard": "./dist/onboard.js",
|
|
146
147
|
"onboard": "./dist/onboard.js"
|
|
147
148
|
},
|
|
148
149
|
"files": [
|
|
149
150
|
"dist",
|
|
151
|
+
"packs",
|
|
150
152
|
"templates",
|
|
151
153
|
"LICENSE",
|
|
152
154
|
"README.md"
|
|
@@ -176,12 +178,12 @@
|
|
|
176
178
|
"xstate": "^5.28.0",
|
|
177
179
|
"yaml": "^2.8.2",
|
|
178
180
|
"zod": "^4.3.6",
|
|
179
|
-
"@lumenflow/agent": "3.1.
|
|
180
|
-
"@lumenflow/
|
|
181
|
-
"@lumenflow/
|
|
182
|
-
"@lumenflow/
|
|
183
|
-
"@lumenflow/
|
|
184
|
-
"@lumenflow/
|
|
181
|
+
"@lumenflow/agent": "3.1.3",
|
|
182
|
+
"@lumenflow/core": "3.1.3",
|
|
183
|
+
"@lumenflow/initiatives": "3.1.3",
|
|
184
|
+
"@lumenflow/metrics": "3.1.3",
|
|
185
|
+
"@lumenflow/memory": "3.1.3",
|
|
186
|
+
"@lumenflow/kernel": "3.1.3"
|
|
185
187
|
},
|
|
186
188
|
"devDependencies": {
|
|
187
189
|
"@vitest/coverage-v8": "^4.0.18",
|
|
@@ -197,8 +199,10 @@
|
|
|
197
199
|
"access": "public"
|
|
198
200
|
},
|
|
199
201
|
"scripts": {
|
|
202
|
+
"sync:bundled-packs": "node scripts/sync-bundled-packs.mjs",
|
|
200
203
|
"build": "tsc && node scripts/check-shebangs.mjs",
|
|
201
|
-
"build:dist": "
|
|
204
|
+
"build:dist:deps": "pnpm --filter @lumenflow/metrics build && pnpm --filter @lumenflow/kernel build && pnpm --filter @lumenflow/core build && pnpm --filter @lumenflow/memory build && pnpm --filter @lumenflow/agent build && pnpm --filter @lumenflow/initiatives build",
|
|
205
|
+
"build:dist": "pnpm run clean && pnpm run build:dist:deps && tsup && node scripts/fix-entry-points.mjs && node scripts/check-shebangs.mjs",
|
|
202
206
|
"pack:dist": "pnpm pack",
|
|
203
207
|
"clean": "rm -rf dist *.tgz",
|
|
204
208
|
"test": "vitest run",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
export const SOFTWARE_DELIVERY_PACK_ID = 'software-delivery' as const;
|
|
5
|
+
export const SOFTWARE_DELIVERY_PACK_VERSION = '0.1.0' as const;
|
|
6
|
+
export const SOFTWARE_DELIVERY_DOMAIN = SOFTWARE_DELIVERY_PACK_ID;
|
|
7
|
+
export const SOFTWARE_DELIVERY_POLICY_ID_PREFIX = `${SOFTWARE_DELIVERY_PACK_ID}.gate` as const;
|
|
8
|
+
export const SOFTWARE_DELIVERY_MANIFEST_FILE_NAME = 'manifest.yaml' as const;
|
|
9
|
+
export const SHA256_ALGORITHM = 'sha256' as const;
|
|
10
|
+
export const UTF8_ENCODING = 'utf8' as const;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
import { SOFTWARE_DELIVERY_DOMAIN } from './constants.js';
|
|
5
|
+
|
|
6
|
+
export const SOFTWARE_DELIVERY_EXTENSION_KEY = 'software_delivery';
|
|
7
|
+
export const SOFTWARE_DELIVERY_EXPOSURES = ['ui', 'api', 'backend-only', 'documentation'] as const;
|
|
8
|
+
|
|
9
|
+
interface Parser<T> {
|
|
10
|
+
parse(input: unknown): T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SoftwareDeliveryTests {
|
|
14
|
+
unit: string[];
|
|
15
|
+
e2e: string[];
|
|
16
|
+
manual: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SoftwareDeliveryTaskExtensions {
|
|
20
|
+
code_paths: string[];
|
|
21
|
+
tests: SoftwareDeliveryTests;
|
|
22
|
+
exposure: (typeof SOFTWARE_DELIVERY_EXPOSURES)[number];
|
|
23
|
+
worktree: string;
|
|
24
|
+
branch: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SoftwareDeliveryTask {
|
|
28
|
+
domain: typeof SOFTWARE_DELIVERY_DOMAIN;
|
|
29
|
+
extensions: Record<string, unknown> & {
|
|
30
|
+
[SOFTWARE_DELIVERY_EXTENSION_KEY]: SoftwareDeliveryTaskExtensions;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function asRecord(input: unknown, label: string): Record<string, unknown> {
|
|
35
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
36
|
+
throw new Error(`${label} must be an object.`);
|
|
37
|
+
}
|
|
38
|
+
return input as Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function assertOnlyKeys(
|
|
42
|
+
record: Record<string, unknown>,
|
|
43
|
+
allowedKeys: readonly string[],
|
|
44
|
+
label: string,
|
|
45
|
+
): void {
|
|
46
|
+
const allowed = new Set(allowedKeys);
|
|
47
|
+
for (const key of Object.keys(record)) {
|
|
48
|
+
if (!allowed.has(key)) {
|
|
49
|
+
throw new Error(`${label} has unrecognized key "${key}".`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parseNonEmptyString(value: unknown, label: string): string {
|
|
55
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
56
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseStringArray(value: unknown, label: string): string[] {
|
|
62
|
+
if (!Array.isArray(value)) {
|
|
63
|
+
throw new Error(`${label} must be an array.`);
|
|
64
|
+
}
|
|
65
|
+
return value.map((entry, index) => parseNonEmptyString(entry, `${label}[${index}]`));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseTests(input: unknown): SoftwareDeliveryTests {
|
|
69
|
+
const tests = asRecord(input, 'tests');
|
|
70
|
+
assertOnlyKeys(tests, ['unit', 'e2e', 'manual'], 'tests');
|
|
71
|
+
return {
|
|
72
|
+
unit: parseStringArray(tests.unit ?? [], 'tests.unit'),
|
|
73
|
+
e2e: parseStringArray(tests.e2e ?? [], 'tests.e2e'),
|
|
74
|
+
manual: parseStringArray(tests.manual ?? [], 'tests.manual'),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const ExposureSet = new Set<string>(SOFTWARE_DELIVERY_EXPOSURES);
|
|
79
|
+
|
|
80
|
+
export const SoftwareDeliveryTaskExtensionsSchema: Parser<SoftwareDeliveryTaskExtensions> = {
|
|
81
|
+
parse(input: unknown): SoftwareDeliveryTaskExtensions {
|
|
82
|
+
const extension = asRecord(input, 'software delivery extension');
|
|
83
|
+
assertOnlyKeys(
|
|
84
|
+
extension,
|
|
85
|
+
['code_paths', 'tests', 'exposure', 'worktree', 'branch'],
|
|
86
|
+
'software delivery extension',
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const exposure = parseNonEmptyString(extension.exposure, 'exposure');
|
|
90
|
+
if (!ExposureSet.has(exposure)) {
|
|
91
|
+
throw new Error(`exposure must be one of: ${SOFTWARE_DELIVERY_EXPOSURES.join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
code_paths: parseStringArray(extension.code_paths, 'code_paths'),
|
|
96
|
+
tests: parseTests(extension.tests),
|
|
97
|
+
exposure: exposure as (typeof SOFTWARE_DELIVERY_EXPOSURES)[number],
|
|
98
|
+
worktree: parseNonEmptyString(extension.worktree, 'worktree'),
|
|
99
|
+
branch: parseNonEmptyString(extension.branch, 'branch'),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const TaskExtensionsOpaqueRecordSchema: Parser<Record<string, unknown>> = {
|
|
105
|
+
parse(input: unknown): Record<string, unknown> {
|
|
106
|
+
return asRecord(input, 'extensions');
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export function extractSoftwareDeliveryExtensions(
|
|
111
|
+
extensions: Record<string, unknown> | undefined,
|
|
112
|
+
): SoftwareDeliveryTaskExtensions {
|
|
113
|
+
const parsedExtensions = TaskExtensionsOpaqueRecordSchema.parse(extensions ?? {});
|
|
114
|
+
return SoftwareDeliveryTaskExtensionsSchema.parse(
|
|
115
|
+
parsedExtensions[SOFTWARE_DELIVERY_EXTENSION_KEY],
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const SoftwareDeliveryTaskSchema: Parser<SoftwareDeliveryTask> = {
|
|
120
|
+
parse(input: unknown): SoftwareDeliveryTask {
|
|
121
|
+
const task = asRecord(input, 'task');
|
|
122
|
+
|
|
123
|
+
if (task.domain !== SOFTWARE_DELIVERY_DOMAIN) {
|
|
124
|
+
throw new Error(`task.domain must be "${SOFTWARE_DELIVERY_DOMAIN}".`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parsedExtensions = TaskExtensionsOpaqueRecordSchema.parse(task.extensions);
|
|
128
|
+
const parsedSoftwareDeliveryExtensions = SoftwareDeliveryTaskExtensionsSchema.parse(
|
|
129
|
+
parsedExtensions[SOFTWARE_DELIVERY_EXTENSION_KEY],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
domain: SOFTWARE_DELIVERY_DOMAIN,
|
|
134
|
+
extensions: {
|
|
135
|
+
...parsedExtensions,
|
|
136
|
+
[SOFTWARE_DELIVERY_EXTENSION_KEY]: parsedSoftwareDeliveryExtensions,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
};
|