@intentius/chant 0.91.0 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +6 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +2 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +12 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/lexicon-module.d.ts +22 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lifecycle/converge-ledger.d.ts +19 -0
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.d.ts +2 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/symptoms.d.ts +51 -0
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/rules/index.d.ts +4 -2
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +14 -3
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -1
- package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts +29 -0
- package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts.map +1 -0
- package/dist/lint/rules/sys001-no-literal-key.d.ts +3 -0
- package/dist/lint/rules/sys001-no-literal-key.d.ts.map +1 -0
- package/dist/op/__fixtures__/decide-stub-backend.d.ts +53 -0
- package/dist/op/__fixtures__/decide-stub-backend.d.ts.map +1 -0
- package/dist/op/activities/activity-contracts.d.ts +201 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.d.ts +44 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +24 -1
- package/dist/op/activities/converge.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts +107 -0
- package/dist/op/activities/decide.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +10 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/shell.d.ts +32 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/source-release.d.ts +86 -0
- package/dist/op/activities/source-release.d.ts.map +1 -0
- package/dist/op/activities/source-rollback.d.ts +100 -0
- package/dist/op/activities/source-rollback.d.ts.map +1 -0
- package/dist/op/activities/work-evidence.d.ts +42 -0
- package/dist/op/activities/work-evidence.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +1 -0
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts +16 -0
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -2
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/converge-op.d.ts +31 -4
- package/dist/op/composites/converge-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts +116 -0
- package/dist/op/decide-backend.d.ts.map +1 -0
- package/dist/op/decide-config.d.ts +120 -0
- package/dist/op/decide-config.d.ts.map +1 -0
- package/dist/op/decide-read-inputs.d.ts +40 -0
- package/dist/op/decide-read-inputs.d.ts.map +1 -0
- package/dist/op/gate-wait.d.ts +43 -0
- package/dist/op/gate-wait.d.ts.map +1 -0
- package/dist/op/gate.d.ts +5 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +11 -4
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +29 -4
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +2 -0
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +2 -0
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +97 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +37 -5
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts +63 -0
- package/dist/op/source-archive.d.ts.map +1 -0
- package/dist/op/steward-points.d.ts +136 -0
- package/dist/op/steward-points.d.ts.map +1 -0
- package/dist/op/steward-turn.d.ts +53 -0
- package/dist/op/steward-turn.d.ts.map +1 -0
- package/dist/op/steward.d.ts +22 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +7 -0
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +1 -1
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +100 -0
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -0
- package/dist/workspace/answers-ledger.d.ts +76 -0
- package/dist/workspace/answers-ledger.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts +16 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts +200 -0
- package/dist/workspace/changes.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts +59 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts +51 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts +26 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts +59 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts +22 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts +71 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -0
- package/dist/workspace/chant-migrations.d.ts +99 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -0
- package/dist/workspace/checks/boxes.d.ts +34 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- package/dist/workspace/checks/diagrams.d.ts +31 -0
- package/dist/workspace/checks/diagrams.d.ts.map +1 -0
- package/dist/workspace/checks/records.d.ts +7 -0
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +3 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +2 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/decide.d.ts +49 -4
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +52 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +114 -1
- package/dist/workspace/declared-kinds.d.ts +20 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/generated-files.d.ts +7 -0
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/lineage-check.d.ts +3 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +6 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/ls.d.ts +39 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/point-answer.schema.json +95 -0
- package/dist/workspace/points-cli.d.ts.map +1 -1
- package/dist/workspace/points.d.ts +1 -1
- package/dist/workspace/reason-codes.d.ts +14 -2
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +11 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +11 -5
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +34 -4
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +7 -2
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-stewards.d.ts +59 -1
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +16 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +113 -2
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +74 -0
- package/dist/workspace/work-evidence.d.ts.map +1 -0
- package/dist/workspace/work.d.ts +43 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/audit.test.ts +9 -1
- package/src/cli/commands/build.ts +3 -2
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +16 -2
- package/src/cli/handlers/operator.ts +34 -2
- package/src/cli/handlers/run.test.ts +154 -2
- package/src/cli/handlers/run.ts +121 -2
- package/src/cli/main.test.ts +10 -0
- package/src/cli/main.ts +19 -0
- package/src/cli/mcp/workspace-tools.test.ts +35 -19
- package/src/cli/mcp/workspace-tools.ts +62 -4
- package/src/cli/plugins.ts +11 -3
- package/src/cli/registry.ts +12 -2
- package/src/codegen/pinned-upgrade.ts +2 -1
- package/src/codegen/release-wiring.test.ts +49 -2
- package/src/components/capability-plugin-loader.ts +3 -3
- package/src/components/cli-support.ts +2 -2
- package/src/config.ts +11 -0
- package/src/lexicon-module.ts +56 -1
- package/src/lexicon-package.test.ts +96 -0
- package/src/lifecycle/converge-ledger.ts +20 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/lifecycle/symptoms.ts +95 -0
- package/src/lint/rules/index.ts +6 -2
- package/src/lint/rules/op/index.ts +17 -4
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +23 -1
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +10 -6
- package/src/lint/rules/op/sys010-key-over-plain-http.test.ts +52 -0
- package/src/lint/rules/op/sys010-key-over-plain-http.ts +100 -0
- package/src/lint/rules/sys001-no-literal-key.test.ts +31 -0
- package/src/lint/rules/sys001-no-literal-key.ts +67 -0
- package/src/op/__fixtures__/decide-stub-backend.ts +143 -0
- package/src/op/activities/activity-contracts.ts +190 -1
- package/src/op/activities/apply.ts +7 -6
- package/src/op/activities/change-coverage.ts +60 -0
- package/src/op/activities/converge.test.ts +55 -1
- package/src/op/activities/converge.ts +208 -29
- package/src/op/activities/decide.live.test.ts +78 -0
- package/src/op/activities/decide.test.ts +300 -0
- package/src/op/activities/decide.ts +267 -0
- package/src/op/activities/index.ts +21 -0
- package/src/op/activities/shell.test.ts +91 -1
- package/src/op/activities/shell.ts +91 -2
- package/src/op/activities/source-release.test.ts +118 -0
- package/src/op/activities/source-release.ts +168 -0
- package/src/op/activities/source-rollback.test.ts +131 -0
- package/src/op/activities/source-rollback.ts +239 -0
- package/src/op/activities/work-evidence.ts +62 -0
- package/src/op/activity-contract-registry.test.ts +2 -0
- package/src/op/activity-contract-registry.ts +2 -2
- package/src/op/activity-contract.test.ts +12 -0
- package/src/op/activity-contract.ts +6 -1
- package/src/op/activity-profiles.ts +29 -0
- package/src/op/activity-registry.test.ts +1 -1
- package/src/op/activity-registry.ts +2 -2
- package/src/op/builders.ts +119 -5
- package/src/op/command-gate.test.ts +192 -0
- package/src/op/composites/converge-op.test.ts +27 -1
- package/src/op/composites/converge-op.ts +69 -15
- package/src/op/decide-backend.test.ts +44 -0
- package/src/op/decide-backend.ts +212 -0
- package/src/op/decide-config.test.ts +52 -0
- package/src/op/decide-config.ts +80 -0
- package/src/op/decide-read-inputs.ts +117 -0
- package/src/op/gate-approval.ts +2 -2
- package/src/op/gate-wait.ts +57 -0
- package/src/op/gate.ts +8 -3
- package/src/op/generate-pipeline.ts +2 -2
- package/src/op/index.ts +18 -3
- package/src/op/local-executor.ts +151 -15
- package/src/op/local-output.test.ts +33 -0
- package/src/op/local-output.ts +20 -2
- package/src/op/op-ir.test.ts +10 -0
- package/src/op/op-ir.ts +4 -0
- package/src/op/op-verb-class.ts +1 -0
- package/src/op/operator.ts +290 -11
- package/src/op/runtime.ts +31 -8
- package/src/op/runtimes/local.ts +3 -1
- package/src/op/source-archive.ts +161 -0
- package/src/op/steward-points.test.ts +313 -0
- package/src/op/steward-points.ts +194 -0
- package/src/op/steward-turn.ts +79 -0
- package/src/op/steward.test.ts +87 -3
- package/src/op/steward.ts +25 -1
- package/src/op/types.ts +7 -0
- package/src/op/work-lease-run.test.ts +20 -1
- package/src/op/work-lease-run.ts +2 -2
- package/src/workspace/__fixtures__/decide-workspace.ts +129 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +67 -0
- package/src/workspace/answers-ledger.ts +118 -0
- package/src/workspace/changes-cli.ts +56 -0
- package/src/workspace/changes.schema.json +472 -0
- package/src/workspace/changes.test.ts +315 -0
- package/src/workspace/changes.ts +512 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-fly-site.ts +234 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-live-names.ts +164 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-rollback.ts +223 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.ts +195 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-ship-policy.ts +108 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit.ts +882 -0
- package/src/workspace/chant-migrations.e2e.test.ts +1089 -0
- package/src/workspace/chant-migrations.ts +164 -0
- package/src/workspace/check-contract.test.ts +4 -2
- package/src/workspace/check.schema.json +15 -5
- package/src/workspace/checks/boxes.test.ts +80 -1
- package/src/workspace/checks/boxes.ts +136 -1
- package/src/workspace/checks/diagrams.test.ts +134 -0
- package/src/workspace/checks/diagrams.ts +108 -0
- package/src/workspace/checks/records.ts +47 -0
- package/src/workspace/checks.ts +8 -1
- package/src/workspace/compose-graph.ts +2 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +10 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +26 -0
- package/src/workspace/conformance/index.ts +5 -0
- package/src/workspace/decide.test.ts +1 -1
- package/src/workspace/decide.ts +156 -23
- package/src/workspace/declaration.schema.json +114 -1
- package/src/workspace/declaration.test.ts +58 -0
- package/src/workspace/declaration.ts +104 -0
- package/src/workspace/declared-kinds.test.ts +5 -5
- package/src/workspace/declared-kinds.ts +46 -1
- package/src/workspace/generated-files.ts +14 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/graph.schema.json +9 -1
- package/src/workspace/intent.schema.json +8 -2
- package/src/workspace/intent.ts +3 -10
- package/src/workspace/lineage-check.ts +16 -1
- package/src/workspace/lineage-upgrade-cli.ts +1 -1
- package/src/workspace/lineage-upgrade.test.ts +70 -0
- package/src/workspace/lineage-upgrade.ts +81 -10
- package/src/workspace/ls-contract.test.ts +57 -0
- package/src/workspace/ls.schema.json +46 -1
- package/src/workspace/ls.ts +62 -3
- package/src/workspace/points-cli.ts +14 -3
- package/src/workspace/points-write.schema.json +11 -1
- package/src/workspace/points.schema.json +33 -0
- package/src/workspace/points.test.ts +1 -1
- package/src/workspace/points.ts +1 -1
- package/src/workspace/read-contract.test.ts +1 -1
- package/src/workspace/reason-codes.test.ts +11 -1
- package/src/workspace/reason-codes.ts +18 -2
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-cli.ts +13 -3
- package/src/workspace/records-close.schema.json +2 -0
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-sessions-write.e2e.test.ts +77 -0
- package/src/workspace/records-sessions-write.test.ts +2 -34
- package/src/workspace/records-write.e2e.test.ts +108 -0
- package/src/workspace/records-write.test.ts +32 -41
- package/src/workspace/records-write.ts +33 -15
- package/src/workspace/records.schema.json +36 -2
- package/src/workspace/records.test.ts +48 -3
- package/src/workspace/records.ts +109 -3
- package/src/workspace/status-contract.test.ts +13 -8
- package/src/workspace/status-gates.ts +9 -4
- package/src/workspace/status-stewards.ts +108 -4
- package/src/workspace/status.schema.json +111 -7
- package/src/workspace/status.ts +36 -1
- package/src/workspace/work-acceptance.test.ts +274 -0
- package/src/workspace/{work-cli.test.ts → work-cli.e2e.test.ts} +1 -1
- package/src/workspace/work-cli.ts +193 -10
- package/src/workspace/work-evidence.schema.json +212 -0
- package/src/workspace/work-evidence.ts +169 -0
- package/src/workspace/work-history.schema.json +349 -0
- package/src/workspace/work-history.test.ts +156 -0
- package/src/workspace/work.test.ts +3 -2
- package/src/workspace/work.ts +100 -1
- /package/src/cli/{discovery-skip.test.ts → discovery-skip.e2e.test.ts} +0 -0
- /package/src/workspace/conformance/{conformance.test.ts → conformance.e2e.test.ts} +0 -0
- /package/src/workspace/{which-chant.test.ts → which-chant.e2e.test.ts} +0 -0
|
@@ -103,9 +103,14 @@ export const defaultGateLedgerReader: GateLedgerReader = async (dir, commit, cwd
|
|
|
103
103
|
return files;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
/**
|
|
107
|
+
* The exact `chant approve` line for a gate. A gate bound to a plan (#2300)
|
|
108
|
+
* gets `--plan <digest>` too (#2832), so running the line back approves the
|
|
109
|
+
* plan this read showed and not whatever happens to be pending by the time
|
|
110
|
+
* someone runs it.
|
|
111
|
+
*/
|
|
112
|
+
export function approveCommand(component: string, gate: string, env: string | null, planDigest?: string | null): string {
|
|
113
|
+
return `chant approve ${component} ${gate}${env !== null ? ` --env ${env}` : ""}${planDigest ? ` --plan ${planDigest}` : ""}`;
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
const approvalOf = (r: GateResolutionRecord): StatusGateApproval => ({ principal: r.resolvedBy, channel: r.origin ?? null, at: r.timestamp });
|
|
@@ -150,7 +155,7 @@ function decide(component: string, standing: PendingGateRecord, resolutions: Gat
|
|
|
150
155
|
expiresAt: standing.expiresAt,
|
|
151
156
|
approvals: approvals.map(approvalOf),
|
|
152
157
|
needed,
|
|
153
|
-
approve: approveCommand(component, gate, env),
|
|
158
|
+
approve: approveCommand(component, gate, env, planDigest),
|
|
154
159
|
};
|
|
155
160
|
}
|
|
156
161
|
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* - each Op's last run is the newest record in its run ledger,
|
|
15
15
|
* `<env>/runs__<op>.jsonl` under the member's ledger prefix, where `<env>`
|
|
16
16
|
* is the Op's own `labels.Env` or `local`;
|
|
17
|
+
* - `waiting` lists the open decision points the steward waits on (#2749):
|
|
18
|
+
* each Op whose newest run is the steward's own and stopped on a question,
|
|
19
|
+
* as the run ledger records it. The question's state now is `points`'s;
|
|
17
20
|
* - `lease` is the local steward's own lease ref
|
|
18
21
|
* (`refs/chant/lease/[<prefix>]_stewards/<name>`), present while a
|
|
19
22
|
* `chant operator --steward` holds it or until it expires;
|
|
@@ -27,6 +30,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
27
30
|
import { discoverStewards } from "../op/discover";
|
|
28
31
|
import { stewardFormFor, stewardLeaseName, type StewardForm } from "../op/steward";
|
|
29
32
|
import { readRunLedger, runEnvOf } from "../lifecycle/run-ledger";
|
|
33
|
+
import { readConvergeLedger, type ConvergeTickRecord } from "../lifecycle/converge-ledger";
|
|
30
34
|
import { leaseRef } from "../lifecycle/lease";
|
|
31
35
|
import { readBlobBySha, readRefSha } from "../lifecycle/git";
|
|
32
36
|
import { resolveMemberLedger } from "../lifecycle/member-ledger";
|
|
@@ -34,6 +38,7 @@ import { listWorkLeases } from "../lifecycle/work-lease";
|
|
|
34
38
|
import { stewardWorkHolder } from "../op/work-lease-run";
|
|
35
39
|
import type { OpConfig } from "../op/types";
|
|
36
40
|
import type { OpRunRecord } from "../op/runtime";
|
|
41
|
+
import { approveCommand } from "./status-gates";
|
|
37
42
|
import type { ReasonCode } from "./reason-codes";
|
|
38
43
|
|
|
39
44
|
/** Why a member's stewards can't be fully listed. Closed: a new code is a contract change. */
|
|
@@ -42,7 +47,7 @@ export const STEWARD_REASON_CODES = [
|
|
|
42
47
|
"stewards-unreadable",
|
|
43
48
|
/** A steward was dropped: its name, or an Op it lists, belongs to another steward. */
|
|
44
49
|
"stewards-conflict",
|
|
45
|
-
/** Reading an Op's run ledger failed, so its last run is null. */
|
|
50
|
+
/** Reading an Op's run ledger, or a ConvergeOp's converge ledger, failed, so its last run or last tick is null. */
|
|
46
51
|
"steward-runs-unreadable",
|
|
47
52
|
] as const satisfies readonly ReasonCode[];
|
|
48
53
|
export type StewardReasonCode = (typeof STEWARD_REASON_CODES)[number];
|
|
@@ -52,8 +57,48 @@ export interface StatusStewardRun {
|
|
|
52
57
|
status: OpRunRecord["status"];
|
|
53
58
|
started: string;
|
|
54
59
|
ended: string;
|
|
55
|
-
/**
|
|
56
|
-
|
|
60
|
+
/**
|
|
61
|
+
* The gate the run stopped at, for a `gated` run: its name, when the run
|
|
62
|
+
* stopped there, the op it is recorded under (the Op's own, or the
|
|
63
|
+
* command's for a step whose command stopped at its own gate, #2779, such
|
|
64
|
+
* as `workspace-upgrade`), and the command that approves it.
|
|
65
|
+
*/
|
|
66
|
+
gate: { name: string; since: string; op: string; approve: string } | null;
|
|
67
|
+
/** The open decision point the run stopped on, for a `waiting` run (#2749). */
|
|
68
|
+
point: StatusStewardWait | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** An open decision point a steward's run stopped on (#2749), as the run ledger records it. */
|
|
72
|
+
export interface StatusStewardWait {
|
|
73
|
+
/** The answer record's id: what `points --open` lists and `points answer` names. */
|
|
74
|
+
id: string;
|
|
75
|
+
point: string;
|
|
76
|
+
/** The question's state when the run stopped. */
|
|
77
|
+
state: "escalated" | "proposed";
|
|
78
|
+
path: string;
|
|
79
|
+
subject: string | null;
|
|
80
|
+
since: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A ConvergeOp's newest tick on the converge ledger (#2778). */
|
|
84
|
+
export interface StatusStewardTick {
|
|
85
|
+
/** The tick's id, or null for a tick recorded before ticks had ids. */
|
|
86
|
+
id: string | null;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
/** The tick's one log line. */
|
|
89
|
+
log: string;
|
|
90
|
+
/** Every rule that fired. */
|
|
91
|
+
firedRuleIds: string[];
|
|
92
|
+
/** What each fired rule did, and for which resource on a ConvergeOp with an observer step. */
|
|
93
|
+
outcomes: {
|
|
94
|
+
ruleId: string;
|
|
95
|
+
action: ConvergeTickRecord["outcomes"][number]["action"];
|
|
96
|
+
op: string | null;
|
|
97
|
+
resource: string | null;
|
|
98
|
+
reason: string | null;
|
|
99
|
+
}[];
|
|
100
|
+
/** What the observer step reported, or null for a ConvergeOp that observes a lexicon environment. */
|
|
101
|
+
resources: { name: string; status: "in-sync" | "drifted" | "unknown"; detail: string | null }[] | null;
|
|
57
102
|
}
|
|
58
103
|
|
|
59
104
|
export interface StatusStewardOp {
|
|
@@ -64,6 +109,8 @@ export interface StatusStewardOp {
|
|
|
64
109
|
env: string;
|
|
65
110
|
/** The newest run in its run ledger, or null when it has none. */
|
|
66
111
|
lastRun: StatusStewardRun | null;
|
|
112
|
+
/** For a ConvergeOp, its newest tick on the converge ledger (#2778); null for any other Op, or before its first tick. */
|
|
113
|
+
lastTick: StatusStewardTick | null;
|
|
67
114
|
/** Whether the Op changes the checkout, and so runs under a work lease on a branch of its own (#2748). */
|
|
68
115
|
changesCheckout: boolean;
|
|
69
116
|
/**
|
|
@@ -103,6 +150,11 @@ export interface StatusSteward {
|
|
|
103
150
|
/** The local steward's own lease, or null when no local operator has held it. */
|
|
104
151
|
lease: { holder: string; acquiredAt: string; expiresAt: string; live: boolean } | null;
|
|
105
152
|
ops: StatusStewardOp[];
|
|
153
|
+
/**
|
|
154
|
+
* The open decision points the steward waits on (#2749): each Op whose
|
|
155
|
+
* newest run is the steward's own and stopped on a question.
|
|
156
|
+
*/
|
|
157
|
+
waiting: (StatusStewardWait & { op: string; run: string })[];
|
|
106
158
|
}
|
|
107
159
|
|
|
108
160
|
export interface MemberStewards {
|
|
@@ -110,6 +162,39 @@ export interface MemberStewards {
|
|
|
110
162
|
reasons: { code: StewardReasonCode; message: string }[];
|
|
111
163
|
}
|
|
112
164
|
|
|
165
|
+
function waitOf(p: NonNullable<OpRunRecord["point"]>): StatusStewardWait {
|
|
166
|
+
return {
|
|
167
|
+
id: p.id,
|
|
168
|
+
point: p.point,
|
|
169
|
+
state: p.state === "proposed" ? "proposed" : "escalated",
|
|
170
|
+
path: p.path,
|
|
171
|
+
subject: p.subject ?? null,
|
|
172
|
+
since: p.since,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function gateOf(g: NonNullable<OpRunRecord["gate"]>, opName: string): NonNullable<StatusStewardRun["gate"]> {
|
|
177
|
+
const op = g.op ?? opName;
|
|
178
|
+
return { name: g.name, since: g.since, op, approve: approveCommand(op, g.name, null) };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function tickOf(t: ConvergeTickRecord): StatusStewardTick {
|
|
182
|
+
return {
|
|
183
|
+
id: t.id ?? null,
|
|
184
|
+
timestamp: t.timestamp,
|
|
185
|
+
log: t.log,
|
|
186
|
+
firedRuleIds: [...t.firedRuleIds],
|
|
187
|
+
outcomes: t.outcomes.map((o) => ({
|
|
188
|
+
ruleId: o.ruleId,
|
|
189
|
+
action: o.action,
|
|
190
|
+
op: o.op ?? null,
|
|
191
|
+
resource: o.resource ?? null,
|
|
192
|
+
reason: o.reason ?? null,
|
|
193
|
+
})),
|
|
194
|
+
resources: t.resources ? t.resources.map((r) => ({ name: r.name, status: r.status, detail: r.detail ?? null })) : null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
113
198
|
/** Whether a directory is a chant project of its own. */
|
|
114
199
|
function isChantProject(dir: string): boolean {
|
|
115
200
|
return existsSync(join(dir, "chant.config.ts")) || existsSync(join(dir, "chant.config.json"));
|
|
@@ -176,6 +261,7 @@ export async function readMemberStewards(
|
|
|
176
261
|
const stewards: StatusSteward[] = [];
|
|
177
262
|
for (const { declaration, filePath } of [...found.values()].sort((a, b) => a.declaration.name.localeCompare(b.declaration.name))) {
|
|
178
263
|
const ops: StatusStewardOp[] = [];
|
|
264
|
+
const waiting: StatusSteward["waiting"] = [];
|
|
179
265
|
for (const op of declaration.ops) {
|
|
180
266
|
const opEnv = runEnvOf(op);
|
|
181
267
|
let lastRun: StatusStewardRun | null = null;
|
|
@@ -187,8 +273,12 @@ export async function readMemberStewards(
|
|
|
187
273
|
status: newest.status,
|
|
188
274
|
started: newest.started,
|
|
189
275
|
ended: newest.ended,
|
|
190
|
-
gate: newest.gate ?
|
|
276
|
+
gate: newest.gate ? gateOf(newest.gate, op.name) : null,
|
|
277
|
+
point: newest.point ? waitOf(newest.point) : null,
|
|
191
278
|
};
|
|
279
|
+
if (newest.status === "waiting" && lastRun.point && newest.steward === declaration.name) {
|
|
280
|
+
waiting.push({ op: op.name, run: newest.id, ...lastRun.point });
|
|
281
|
+
}
|
|
192
282
|
}
|
|
193
283
|
} catch (err) {
|
|
194
284
|
reasons.push({
|
|
@@ -196,11 +286,24 @@ export async function readMemberStewards(
|
|
|
196
286
|
message: `${opEnv}/runs__${op.name}.jsonl: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`,
|
|
197
287
|
});
|
|
198
288
|
}
|
|
289
|
+
let lastTick: StatusStewardTick | null = null;
|
|
290
|
+
if (op.labels?.Converge === "true") {
|
|
291
|
+
try {
|
|
292
|
+
const newest = (await readConvergeLedger(opEnv, { cwd: memberDir })).records.filter((r) => r.op === op.name).at(-1);
|
|
293
|
+
if (newest) lastTick = tickOf(newest);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
reasons.push({
|
|
296
|
+
code: "steward-runs-unreadable",
|
|
297
|
+
message: `${opEnv}/converge.jsonl: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
199
301
|
ops.push({
|
|
200
302
|
name: op.name,
|
|
201
303
|
schedule: op.schedule ? { cron: op.schedule.cron, overlap: "skip" } : null,
|
|
202
304
|
env: opEnv,
|
|
203
305
|
lastRun,
|
|
306
|
+
lastTick,
|
|
204
307
|
changesCheckout: op.changesCheckout === true,
|
|
205
308
|
workLease: op.workLease
|
|
206
309
|
? { kind: op.workLease.kind ?? null, held: await readHeldWorkLeases(declaration.name, op, memberDir, now) }
|
|
@@ -219,6 +322,7 @@ export async function readMemberStewards(
|
|
|
219
322
|
}),
|
|
220
323
|
lease: await readStewardLease(declaration.name, memberDir, now),
|
|
221
324
|
ops,
|
|
325
|
+
waiting,
|
|
222
326
|
});
|
|
223
327
|
}
|
|
224
328
|
return { stewards, reasons };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://intentius.io/chant/schemas/workspace/status/v1/status.schema.json",
|
|
4
4
|
"title": "chant workspace status output",
|
|
5
|
-
"description": "What `chant workspace status <env> --json` prints, version 1 of the read contract for releases across members (#2524 D15, D19, #2544). Readers ignore fields they do not know; a field is only ever added within a version. The reason and error codes are closed lists: a new code is a new contract version. Contract version 1 is written by chant 0.81.0 and newer; a reader that needs it refuses output whose `contract` it doesn't know. Every code is in the one closed list of `reason-codes.ts`. Each member's gates, read from its gate ledger on the same branch, are in the JSON only (#2674), and so is each member's box block, from the declaration (#2726), with its isolation resolved (#2727). So are its stewards, their Ops and each Op's last run (#2731),
|
|
5
|
+
"description": "What `chant workspace status <env> --json` prints, version 1 of the read contract for releases across members (#2524 D15, D19, #2544). Readers ignore fields they do not know; a field is only ever added within a version. The reason and error codes are closed lists: a new code is a new contract version. Contract version 1 is written by chant 0.81.0 and newer; a reader that needs it refuses output whose `contract` it doesn't know. Every code is in the one closed list of `reason-codes.ts`. Each member's gates, read from its gate ledger on the same branch, are in the JSON only (#2674), and so is each member's box block, from the declaration (#2726), with its isolation resolved (#2727). So are its stewards, their Ops and each Op's last run (#2731), the work lease each Op's turn holds (#2748), and the open decision points a steward waits on (#2749).",
|
|
6
6
|
"oneOf": [{ "$ref": "#/$defs/result" }, { "$ref": "#/$defs/failure" }],
|
|
7
7
|
"$defs": {
|
|
8
8
|
"result": {
|
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
"type": "array",
|
|
51
51
|
"items": { "$ref": "#/$defs/lease" }
|
|
52
52
|
},
|
|
53
|
+
"acceptance": {
|
|
54
|
+
"description": "Added in contract 1 by #2772. Each current work item of a declared work kind whose work block names acceptance criteria, when the item lists some, by declared kind and then item, read in the working tree: how many of its criteria passing evidence meets. Empty when no declared work kind has criteria. A done item with a criterion unmet is what chant workspace check fails on (WSP117).",
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": { "$ref": "#/$defs/acceptance" }
|
|
57
|
+
},
|
|
53
58
|
"summary": {
|
|
54
59
|
"type": "object",
|
|
55
60
|
"required": ["members", "released", "unreadable", "differing"],
|
|
@@ -66,6 +71,18 @@
|
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
},
|
|
74
|
+
"acceptance": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["member", "kind", "item", "state", "met", "total"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"member": { "type": ["string", "null"], "description": "The member that declares the work kind, or null for the workspace's own." },
|
|
79
|
+
"kind": { "type": "string", "description": "The work kind file, from the workspace root." },
|
|
80
|
+
"item": { "type": "string", "description": "The work item's id." },
|
|
81
|
+
"state": { "type": ["string", "null"], "description": "The item's state." },
|
|
82
|
+
"met": { "type": "integer", "minimum": 0, "description": "Criteria met by evidence naming them, with result pass and the criterion's verification; a manual verdict counts only from someone other than the implementer." },
|
|
83
|
+
"total": { "type": "integer", "minimum": 0, "description": "Criteria the item lists." }
|
|
84
|
+
}
|
|
85
|
+
},
|
|
69
86
|
"lease": {
|
|
70
87
|
"type": "object",
|
|
71
88
|
"required": ["item", "member", "ref", "holder", "token", "acquiredAt", "expiresAt", "state"],
|
|
@@ -135,7 +152,7 @@
|
|
|
135
152
|
"stewardForm": { "enum": ["local", "fountain"] },
|
|
136
153
|
"steward": {
|
|
137
154
|
"type": "object",
|
|
138
|
-
"required": ["name", "file", "form", "forms", "vault", "capabilities", "lease", "ops"],
|
|
155
|
+
"required": ["name", "file", "form", "forms", "vault", "capabilities", "lease", "ops", "waiting"],
|
|
139
156
|
"properties": {
|
|
140
157
|
"name": { "type": "string", "description": "The steward's name; on Fountain, its Agent's and Teammate's." },
|
|
141
158
|
"file": { "type": "string", "description": "The *.op.ts file that declares it, relative to the member's directory." },
|
|
@@ -187,7 +204,7 @@
|
|
|
187
204
|
"type": "array",
|
|
188
205
|
"items": {
|
|
189
206
|
"type": "object",
|
|
190
|
-
"required": ["name", "schedule", "env", "lastRun", "changesCheckout", "workLease"],
|
|
207
|
+
"required": ["name", "schedule", "env", "lastRun", "lastTick", "changesCheckout", "workLease"],
|
|
191
208
|
"properties": {
|
|
192
209
|
"name": { "type": "string" },
|
|
193
210
|
"schedule": {
|
|
@@ -208,16 +225,77 @@
|
|
|
208
225
|
{ "type": "null" },
|
|
209
226
|
{
|
|
210
227
|
"type": "object",
|
|
211
|
-
"required": ["id", "status", "started", "ended", "gate"],
|
|
228
|
+
"required": ["id", "status", "started", "ended", "gate", "point"],
|
|
212
229
|
"properties": {
|
|
213
230
|
"id": { "type": "string" },
|
|
214
|
-
"status": { "enum": ["ok", "fail", "gated"] },
|
|
231
|
+
"status": { "enum": ["ok", "fail", "gated", "waiting"], "description": "waiting: an activity asked a decision point that is still open (#2749), and the run stopped there." },
|
|
215
232
|
"started": { "type": "string" },
|
|
216
233
|
"ended": { "type": "string" },
|
|
217
234
|
"gate": {
|
|
218
235
|
"oneOf": [
|
|
219
236
|
{ "type": "null" },
|
|
220
|
-
{
|
|
237
|
+
{
|
|
238
|
+
"type": "object",
|
|
239
|
+
"required": ["name", "since"],
|
|
240
|
+
"properties": {
|
|
241
|
+
"name": { "type": "string" },
|
|
242
|
+
"since": { "type": "string" },
|
|
243
|
+
"op": { "type": "string", "description": "The op the gate is recorded under: the Op's own, or the command's when a step's command stopped at a gate of its own (#2779), such as workspace-upgrade." },
|
|
244
|
+
"approve": { "type": "string", "description": "The command that approves the gate, run in the member's directory." }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
"point": {
|
|
250
|
+
"description": "The open decision point a waiting run stopped on (#2749), as the run ledger records it, or null.",
|
|
251
|
+
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/stewardWait" }]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
"lastTick": {
|
|
258
|
+
"description": "For a ConvergeOp, its newest tick on the converge ledger (#2778): the rules that fired, what each did and for which resource, and what its observer step reported. null for any other Op, or before its first tick.",
|
|
259
|
+
"oneOf": [
|
|
260
|
+
{ "type": "null" },
|
|
261
|
+
{
|
|
262
|
+
"type": "object",
|
|
263
|
+
"required": ["id", "timestamp", "log", "firedRuleIds", "outcomes", "resources"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"id": { "type": ["string", "null"] },
|
|
266
|
+
"timestamp": { "type": "string" },
|
|
267
|
+
"log": { "type": "string" },
|
|
268
|
+
"firedRuleIds": { "type": "array", "items": { "type": "string" } },
|
|
269
|
+
"outcomes": {
|
|
270
|
+
"type": "array",
|
|
271
|
+
"items": {
|
|
272
|
+
"type": "object",
|
|
273
|
+
"required": ["ruleId", "action", "op", "resource", "reason"],
|
|
274
|
+
"properties": {
|
|
275
|
+
"ruleId": { "type": "string" },
|
|
276
|
+
"action": { "enum": ["ran", "reported", "skipped-budget", "skipped-flap", "gated"] },
|
|
277
|
+
"op": { "type": ["string", "null"] },
|
|
278
|
+
"resource": { "type": ["string", "null"], "description": "The resource the rule fired for, on a ConvergeOp with an observer step." },
|
|
279
|
+
"reason": { "type": ["string", "null"] }
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"resources": {
|
|
284
|
+
"description": "What the observer step reported, or null for a ConvergeOp that observes a lexicon environment.",
|
|
285
|
+
"oneOf": [
|
|
286
|
+
{ "type": "null" },
|
|
287
|
+
{
|
|
288
|
+
"type": "array",
|
|
289
|
+
"items": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"required": ["name", "status", "detail"],
|
|
292
|
+
"properties": {
|
|
293
|
+
"name": { "type": "string" },
|
|
294
|
+
"status": { "enum": ["in-sync", "drifted", "unknown"] },
|
|
295
|
+
"detail": { "type": ["string", "null"] }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
221
299
|
]
|
|
222
300
|
}
|
|
223
301
|
}
|
|
@@ -255,9 +333,35 @@
|
|
|
255
333
|
}
|
|
256
334
|
}
|
|
257
335
|
}
|
|
336
|
+
},
|
|
337
|
+
"waiting": {
|
|
338
|
+
"description": "The open decision points the steward waits on (#2749): one for each of its Ops whose newest run is the steward's own and stopped on a question, in ops order. The state is the question's when the run stopped; `chant workspace points --open` has its state now, and hud asks a person there. The steward never answers one, and runs the Op again on the first round after it is answered.",
|
|
339
|
+
"type": "array",
|
|
340
|
+
"items": {
|
|
341
|
+
"allOf": [
|
|
342
|
+
{ "$ref": "#/$defs/stewardWait" },
|
|
343
|
+
{
|
|
344
|
+
"type": "object",
|
|
345
|
+
"required": ["op", "run"],
|
|
346
|
+
"properties": { "op": { "type": "string" }, "run": { "type": "string", "description": "The waiting run's id." } }
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
258
350
|
}
|
|
259
351
|
}
|
|
260
352
|
},
|
|
353
|
+
"stewardWait": {
|
|
354
|
+
"type": "object",
|
|
355
|
+
"required": ["id", "point", "state", "path", "subject", "since"],
|
|
356
|
+
"properties": {
|
|
357
|
+
"id": { "type": "string", "description": "The answer record's id, as `points` lists the question and `points answer` names it." },
|
|
358
|
+
"point": { "type": "string" },
|
|
359
|
+
"state": { "enum": ["escalated", "proposed"] },
|
|
360
|
+
"path": { "type": "string", "description": "The answer record, from the repository root." },
|
|
361
|
+
"subject": { "type": ["string", "null"] },
|
|
362
|
+
"since": { "type": "string", "description": "When the run stopped on it." }
|
|
363
|
+
}
|
|
364
|
+
},
|
|
261
365
|
"box": {
|
|
262
366
|
"type": "object",
|
|
263
367
|
"required": ["capabilities", "isolation"],
|
|
@@ -371,7 +475,7 @@
|
|
|
371
475
|
}
|
|
372
476
|
},
|
|
373
477
|
"needed": { "type": "integer", "minimum": 1, "description": "The human approvals the gate needs: its quorum's count, 1 without one." },
|
|
374
|
-
"approve": { "type": "string", "description": "The chant approve line for the gate, run in the member's directory: chant approve <component> <gate>, with --env <env> when env is not null." }
|
|
478
|
+
"approve": { "type": "string", "description": "The chant approve line for the gate, run in the member's directory: chant approve <component> <gate>, with --env <env> when env is not null and --plan <planDigest> when planDigest is not null (#2832), so the line approves the plan this read showed." }
|
|
375
479
|
}
|
|
376
480
|
},
|
|
377
481
|
"environment": {
|
package/src/workspace/status.ts
CHANGED
|
@@ -43,7 +43,7 @@ import { readReleasePlan, type ReleasePlan } from "../lifecycle/plan-ledger";
|
|
|
43
43
|
import { listWorkLeases, type WorkLeaseState } from "../lifecycle/work-lease";
|
|
44
44
|
import { findWorkspaceRoot } from "../project-root";
|
|
45
45
|
import { resolveBoxes, type ResolvedIsolation } from "./box-isolation";
|
|
46
|
-
import { readDeclaration, readerVersion, WorkspaceReadError, type ErrorLocation, type Member } from "./declaration";
|
|
46
|
+
import { declaredRecordKinds, readDeclaration, readerVersion, WorkspaceReadError, type Declaration, type ErrorLocation, type Member } from "./declaration";
|
|
47
47
|
import type { ReasonCode } from "./reason-codes";
|
|
48
48
|
import { GATE_REASON_CODES, readMemberGates, type GateLedgerReader, type StatusGate, type StatusGateLedger } from "./status-gates";
|
|
49
49
|
import { STEWARD_REASON_CODES, readMemberStewards, type StatusSteward, type StewardReasonCode } from "./status-stewards";
|
|
@@ -190,6 +190,21 @@ export interface StatusLease extends Omit<WorkLeaseState, "ref"> {
|
|
|
190
190
|
ref: string;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* One work item's acceptance criteria, counted (#2772): each current record
|
|
195
|
+
* of a declared work kind with acceptance criteria that lists some.
|
|
196
|
+
*/
|
|
197
|
+
export interface StatusAcceptance {
|
|
198
|
+
/** The member that declares the work kind, or null for the workspace's own. */
|
|
199
|
+
member: string | null;
|
|
200
|
+
/** The work kind file, from the workspace root. */
|
|
201
|
+
kind: string;
|
|
202
|
+
item: string;
|
|
203
|
+
state: string | null;
|
|
204
|
+
met: number;
|
|
205
|
+
total: number;
|
|
206
|
+
}
|
|
207
|
+
|
|
193
208
|
export type StatusDocument =
|
|
194
209
|
| {
|
|
195
210
|
$schema: string;
|
|
@@ -202,6 +217,8 @@ export type StatusDocument =
|
|
|
202
217
|
members: StatusMember[];
|
|
203
218
|
/** Every work lease in the workspace's ledgers, active and expired, by member then item (#2732). A released lease has no ref and is not listed. */
|
|
204
219
|
leases: StatusLease[];
|
|
220
|
+
/** Each work item's acceptance criteria, met of total, by declared kind then item (#2772). Empty when no declared work kind has criteria. */
|
|
221
|
+
acceptance: StatusAcceptance[];
|
|
205
222
|
summary: { members: number; released: number; unreadable: number; differing: number | null };
|
|
206
223
|
}
|
|
207
224
|
| {
|
|
@@ -395,6 +412,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
395
412
|
for (const m of members) m.gateLedger.shared = m.gateLedger.layout === "flat" && flatGates > 1;
|
|
396
413
|
|
|
397
414
|
const leases = await readWorkspaceLeases(declaration.members, found.dir, new Date(now));
|
|
415
|
+
const acceptance = await readWorkspaceAcceptance(declaration, found.dir);
|
|
398
416
|
|
|
399
417
|
return {
|
|
400
418
|
...head,
|
|
@@ -404,6 +422,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
404
422
|
workspace: { name: declaration.name, root: rootDir === "" ? "." : rootDir, file: declaration.file },
|
|
405
423
|
members,
|
|
406
424
|
leases,
|
|
425
|
+
acceptance,
|
|
407
426
|
summary: {
|
|
408
427
|
members: members.length,
|
|
409
428
|
released: members.filter((m) => m.environments[0].releases.length > 0).length,
|
|
@@ -436,6 +455,16 @@ async function readWorkspaceLeases(members: Member[], cwd: string, now: Date): P
|
|
|
436
455
|
return out;
|
|
437
456
|
}
|
|
438
457
|
|
|
458
|
+
/** The acceptance counts of every declared work kind with criteria (#2772), read in the working tree. */
|
|
459
|
+
async function readWorkspaceAcceptance(declaration: Declaration, root: string): Promise<StatusAcceptance[]> {
|
|
460
|
+
if (declaredRecordKinds(declaration).length === 0) return [];
|
|
461
|
+
const { loadDeclaredKinds } = await import("./declared-kinds");
|
|
462
|
+
const kinds = await loadDeclaredKinds(declaration, workingTree(root), root, { acceptance: true });
|
|
463
|
+
return kinds.flatMap((k) =>
|
|
464
|
+
(k.acceptance ?? []).map((a) => ({ member: k.declared.member, kind: k.declared.path, item: a.item, state: a.state, met: a.met, total: a.total })),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
439
468
|
export async function runWorkspaceStatus(ctx: CommandContext): Promise<number> {
|
|
440
469
|
const { args } = ctx;
|
|
441
470
|
const env = args.extraPositional;
|
|
@@ -525,6 +554,12 @@ export function formatStatus(doc: Extract<StatusDocument, { members: unknown }>)
|
|
|
525
554
|
for (const l of doc.leases) rows.push([l.member ? `${l.member}/${l.item}` : l.item, l.holder, l.expiresAt, l.state]);
|
|
526
555
|
lines.push(...table(rows));
|
|
527
556
|
}
|
|
557
|
+
if (doc.acceptance.length > 0) {
|
|
558
|
+
lines.push("");
|
|
559
|
+
const rows: string[][] = [["WORK ITEM", "STATE", "CRITERIA MET"]];
|
|
560
|
+
for (const a of doc.acceptance) rows.push([a.member ? `${a.member}/${a.item}` : a.item, a.state ?? "-", `${a.met}/${a.total}`]);
|
|
561
|
+
lines.push(...table(rows));
|
|
562
|
+
}
|
|
528
563
|
const boxes = doc.members.filter((m) => m.box?.isolation);
|
|
529
564
|
if (boxes.length > 0) {
|
|
530
565
|
lines.push("");
|