@intentius/chant 0.91.0 → 0.94.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-plugins.d.ts +6 -6
- 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/box-intent.d.ts +85 -0
- package/dist/workspace/box-intent.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 +104 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -0
- package/dist/workspace/checks/boxes.d.ts +47 -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 +7 -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/decision-points.schema.json +3 -3
- package/dist/workspace/declaration.d.ts +58 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +119 -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-joins.d.ts +5 -5
- 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 +2 -0
- package/dist/workspace/points-cli.d.ts.map +1 -1
- package/dist/workspace/points.d.ts +4 -6
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/reason-codes.d.ts +16 -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 +65 -1
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +24 -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 +23 -3
- package/src/cli/mcp/workspace-plugins.ts +6 -6
- 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/cli/serve-mcp-workspace.test.ts +6 -6
- 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 +308 -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 +364 -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/box-intent.test.ts +205 -0
- package/src/workspace/box-intent.ts +159 -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 +169 -0
- package/src/workspace/check-contract.test.ts +4 -2
- package/src/workspace/check.schema.json +19 -5
- package/src/workspace/checks/boxes.test.ts +82 -2
- package/src/workspace/checks/boxes.ts +202 -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 +20 -2
- 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/decision-points.schema.json +3 -3
- package/src/workspace/declaration.schema.json +119 -1
- package/src/workspace/declaration.test.ts +58 -0
- package/src/workspace/declaration.ts +112 -0
- package/src/workspace/declared-kinds.test.ts +31 -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-joins.test.ts +5 -5
- package/src/workspace/intent-joins.ts +5 -5
- 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 +17 -3
- package/src/workspace/points-write.schema.json +11 -1
- package/src/workspace/points.schema.json +51 -0
- package/src/workspace/points.test.ts +16 -15
- package/src/workspace/points.ts +5 -17
- package/src/workspace/read-contract.test.ts +9 -1
- package/src/workspace/reason-codes.test.ts +18 -8
- package/src/workspace/reason-codes.ts +21 -2
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-cli.ts +27 -4
- package/src/workspace/records-close.schema.json +2 -0
- package/src/workspace/records-formats.test.ts +15 -15
- 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-since.test.ts +8 -7
- 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 +49 -4
- package/src/workspace/records.ts +109 -3
- package/src/workspace/status-contract.test.ts +14 -8
- package/src/workspace/status-gates.ts +9 -4
- package/src/workspace/status-stewards.ts +155 -5
- package/src/workspace/status.schema.json +129 -8
- package/src/workspace/status.ts +46 -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-readiness-chud.test.ts → work-readiness.test.ts} +30 -32
- package/src/workspace/work.test.ts +20 -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
|
@@ -40,7 +40,14 @@ import {
|
|
|
40
40
|
} from "../converge-rule";
|
|
41
41
|
import { classifyOpVerbClass, type OpVerbClass } from "../op-verb-class";
|
|
42
42
|
import { discoverOps } from "../discover";
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
deriveSymptoms,
|
|
45
|
+
parseResourceObservation,
|
|
46
|
+
CONVERGE_RESOURCE_ENV,
|
|
47
|
+
type ConvergeSymptom,
|
|
48
|
+
type ObservedResource,
|
|
49
|
+
type ResourceSymptom,
|
|
50
|
+
} from "../../lifecycle/symptoms";
|
|
44
51
|
import {
|
|
45
52
|
appendConvergeRecord,
|
|
46
53
|
readConvergeLedger,
|
|
@@ -88,6 +95,14 @@ export interface ConvergeTickArgs {
|
|
|
88
95
|
* (this activity re-derives its own symptom independently).
|
|
89
96
|
*/
|
|
90
97
|
preflightDrift?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* The observer step's result, for a ConvergeOp declared with `observe`
|
|
100
|
+
* (#2778): a {@link ResourceObservation}, or a `shell()` result whose
|
|
101
|
+
* stdout is one. Present, the tick observes nothing itself: it evaluates
|
|
102
|
+
* `rules` once per reported resource, against a `ResourceSymptom`, and
|
|
103
|
+
* dispatches a fired `run()` for that resource.
|
|
104
|
+
*/
|
|
105
|
+
observed?: unknown;
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
export interface ConvergeTickResult {
|
|
@@ -209,9 +224,10 @@ export function classifyDispatchFailure(raw: string): { gateName: string } | und
|
|
|
209
224
|
async function dispatchOp(
|
|
210
225
|
opName: string,
|
|
211
226
|
signal?: AbortSignal,
|
|
227
|
+
env?: Record<string, string>,
|
|
212
228
|
): Promise<{ ok: true } | { ok: false; error: string; gateName?: string }> {
|
|
213
229
|
try {
|
|
214
|
-
await execAsync(`chant run ${shellQuote(opName)} --json`, { signal });
|
|
230
|
+
await execAsync(`chant run ${shellQuote(opName)} --json`, { signal, ...(env ? { env: { ...process.env, ...env } } : {}) });
|
|
215
231
|
return { ok: true };
|
|
216
232
|
} catch (err) {
|
|
217
233
|
const e = err as { code?: number; stdout?: string; stderr?: string; message?: string };
|
|
@@ -383,25 +399,106 @@ function renderLog(env: string, s: ConvergeSymptom, counts: ReturnType<typeof su
|
|
|
383
399
|
);
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
|
|
387
|
-
await fetchLifecycle().catch(() => undefined);
|
|
402
|
+
// ── Resources an observer step reports (#2778) ──────────────────────────────
|
|
388
403
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
404
|
+
/**
|
|
405
|
+
* How many consecutive most-recent ticks of `op` fired `ruleId` for
|
|
406
|
+
* `resource`. A rule of a ConvergeOp with an observer step fires once per
|
|
407
|
+
* resource, so its flap damping is counted per resource: a rule that
|
|
408
|
+
* restarts `app` on one tick and `hud` on the next is not flapping.
|
|
409
|
+
*/
|
|
410
|
+
export function consecutiveResourceFires(records: ConvergeTickRecord[], op: string, ruleId: string, resource: string): number {
|
|
411
|
+
let count = 0;
|
|
412
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
413
|
+
if (records[i].op !== op) continue;
|
|
414
|
+
if (!records[i].outcomes.some((o) => o.ruleId === ruleId && o.resource === resource)) break;
|
|
415
|
+
count++;
|
|
416
|
+
}
|
|
417
|
+
return count;
|
|
418
|
+
}
|
|
394
419
|
|
|
395
|
-
|
|
396
|
-
|
|
420
|
+
/**
|
|
421
|
+
* The pure planning core of a resource tick (#2778): every rule against every
|
|
422
|
+
* observed resource, in authored order within each resource. The rules of
|
|
423
|
+
* {@link planConvergeTick} hold per resource: `unknown` never remediates, a
|
|
424
|
+
* `report()` never dispatches, the dial decides whether a `run()` may, and
|
|
425
|
+
* `budget` bounds the dispatches across the whole tick.
|
|
426
|
+
*/
|
|
427
|
+
export function planResourceTick(
|
|
428
|
+
env: string,
|
|
429
|
+
resources: ObservedResource[],
|
|
430
|
+
rules: ConvergeRule<ResourceSymptom>[],
|
|
431
|
+
priorRecords: ConvergeTickRecord[],
|
|
432
|
+
op: string,
|
|
433
|
+
dial: ConvergeTickArgs["dial"],
|
|
434
|
+
budget: number,
|
|
435
|
+
): ConvergeTickPlan {
|
|
436
|
+
const fired = new Set<string>();
|
|
437
|
+
const outcomes: ConvergeRuleOutcome[] = [];
|
|
438
|
+
let dispatched = 0;
|
|
397
439
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
440
|
+
for (const resource of resources) {
|
|
441
|
+
const symptom: ResourceSymptom = { env, resource: resource.name, status: resource.status, detail: resource.detail ?? "" };
|
|
442
|
+
for (const rule of rules) {
|
|
443
|
+
if (!evaluatePredicate(rule.when, symptom)) continue;
|
|
444
|
+
fired.add(rule.id);
|
|
445
|
+
const base = { ruleId: rule.id, resource: resource.name };
|
|
446
|
+
|
|
447
|
+
const threshold = rule.flapThreshold ?? DEFAULT_FLAP_THRESHOLD;
|
|
448
|
+
const consecutive = consecutiveResourceFires(priorRecords, op, rule.id, resource.name);
|
|
449
|
+
if (consecutive >= threshold) {
|
|
450
|
+
outcomes.push({
|
|
451
|
+
...base,
|
|
452
|
+
action: "skipped-flap",
|
|
453
|
+
reason: `fired for ${resource.name} ${consecutive + 1} consecutive ticks without clearing (threshold ${threshold}) — escalated to report`,
|
|
454
|
+
});
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const action: RuleAction = rule.then;
|
|
459
|
+
if (resource.status === "unknown" && action.kind === "run") {
|
|
460
|
+
outcomes.push({
|
|
461
|
+
...base,
|
|
462
|
+
action: "reported",
|
|
463
|
+
reason: `${resource.name} is unknown (${resource.detail || "unobserved"}) — unknown never remediates`,
|
|
464
|
+
});
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (action.kind === "report") {
|
|
468
|
+
outcomes.push({ ...base, action: "reported", reason: action.reason });
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (!dialAllowsDispatch(dial)) {
|
|
472
|
+
outcomes.push({ ...base, action: "reported", reason: `dial "${dial}" does not permit dispatch — report only`, op: action.op });
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (dispatched >= budget) {
|
|
476
|
+
outcomes.push({ ...base, action: "skipped-budget", op: action.op });
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
dispatched++;
|
|
480
|
+
outcomes.push({ ...base, action: "ran", op: action.op });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return { firedRuleIds: [...fired], outcomes };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function renderResourceLog(env: string, resources: ObservedResource[], counts: ReturnType<typeof summarizeOutcomes>): string {
|
|
487
|
+
const n = (status: ObservedResource["status"]) => resources.filter((r) => r.status === status).length;
|
|
488
|
+
return (
|
|
489
|
+
`converge(${env}): resources=${resources.length} in-sync=${n("in-sync")} drifted=${n("drifted")} unknown=${n("unknown")} ` +
|
|
490
|
+
`remediated=${counts.remediated} reported=${counts.reported} skipped-budget=${counts.skippedBudget} ` +
|
|
491
|
+
`skipped-flap=${counts.skippedFlap} gated=${counts.gated}`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Run the planned dispatches of a tick, in place: the verb-class backstop, then `chant run`, then what came of it. */
|
|
496
|
+
async function dispatchPlanned(
|
|
497
|
+
outcomes: ConvergeRuleOutcome[],
|
|
498
|
+
args: ConvergeTickArgs,
|
|
499
|
+
signal?: AbortSignal,
|
|
500
|
+
): Promise<void> {
|
|
501
|
+
for (const outcome of outcomes) {
|
|
405
502
|
if (outcome.action !== "ran" || !outcome.op) continue;
|
|
406
503
|
|
|
407
504
|
const verbClass = await classifyDispatchTarget(outcome.op);
|
|
@@ -410,7 +507,9 @@ export async function convergeTick(args: ConvergeTickArgs, signal?: AbortSignal)
|
|
|
410
507
|
outcome.reason = backstopped.reason;
|
|
411
508
|
if (outcome.action !== "ran") continue;
|
|
412
509
|
|
|
413
|
-
|
|
510
|
+
// A rule fired for one resource (#2778) hands the dispatched Op its name.
|
|
511
|
+
const env = outcome.resource !== undefined ? { [CONVERGE_RESOURCE_ENV]: outcome.resource } : undefined;
|
|
512
|
+
const result = await dispatchOp(outcome.op, signal, env);
|
|
414
513
|
if (!result.ok && result.gateName) {
|
|
415
514
|
// Gate-as-fact (#1485, closed by #2119): a terminal, durable,
|
|
416
515
|
// non-blocking fact — not an ordinary dispatch failure the operator
|
|
@@ -432,6 +531,95 @@ export async function convergeTick(args: ConvergeTickArgs, signal?: AbortSignal)
|
|
|
432
531
|
outcome.reason = `dispatch of "${outcome.op}" failed: ${result.error}`;
|
|
433
532
|
}
|
|
434
533
|
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** Push the tick's ledger record, and say whether it reached the remote (chant#2337). */
|
|
537
|
+
async function pushTick(): Promise<{ pushed: boolean; pushWarning?: string }> {
|
|
538
|
+
try {
|
|
539
|
+
const pushed = await pushLifecycle();
|
|
540
|
+
return pushed ? { pushed } : { pushed, pushWarning: "no remote is configured for chant/lifecycle — the tick record was recorded locally only" };
|
|
541
|
+
} catch (err) {
|
|
542
|
+
return { pushed: false, pushWarning: err instanceof Error ? err.message : String(err) };
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** A tick of a ConvergeOp with an observer step (#2778): the observation is already in `args.observed`. */
|
|
547
|
+
async function resourceTick(args: ConvergeTickArgs, signal?: AbortSignal): Promise<ConvergeTickResult> {
|
|
548
|
+
const { resources } = parseResourceObservation(args.observed);
|
|
549
|
+
const ledger = await readConvergeLedger(args.env);
|
|
550
|
+
const plan = planResourceTick(
|
|
551
|
+
args.env,
|
|
552
|
+
resources,
|
|
553
|
+
args.rules as unknown as ConvergeRule<ResourceSymptom>[],
|
|
554
|
+
ledger.records,
|
|
555
|
+
args.opName,
|
|
556
|
+
args.dial,
|
|
557
|
+
args.budget,
|
|
558
|
+
);
|
|
559
|
+
await dispatchPlanned(plan.outcomes, args, signal);
|
|
560
|
+
|
|
561
|
+
const counts = summarizeOutcomes(plan.outcomes);
|
|
562
|
+
const log = renderResourceLog(args.env, resources, counts);
|
|
563
|
+
const drifted = resources.filter((r) => r.status === "drifted").length;
|
|
564
|
+
const { record } = await appendConvergeRecord({
|
|
565
|
+
op: args.opName,
|
|
566
|
+
env: args.env,
|
|
567
|
+
timestamp: new Date().toISOString(),
|
|
568
|
+
firedRuleIds: plan.firedRuleIds,
|
|
569
|
+
outcomes: plan.outcomes,
|
|
570
|
+
resources: resources.map((r) => ({
|
|
571
|
+
name: r.name,
|
|
572
|
+
status: r.status,
|
|
573
|
+
...(r.detail ? { detail: sanitizeLedgerText(r.detail) } : {}),
|
|
574
|
+
})),
|
|
575
|
+
summary: {
|
|
576
|
+
drifted,
|
|
577
|
+
remediated: counts.remediated,
|
|
578
|
+
reported: counts.reported,
|
|
579
|
+
skippedBudget: counts.skippedBudget,
|
|
580
|
+
skippedFlap: counts.skippedFlap,
|
|
581
|
+
gated: counts.gated,
|
|
582
|
+
unobserved: resources.filter((r) => r.status === "unknown").length,
|
|
583
|
+
adopted: 0,
|
|
584
|
+
},
|
|
585
|
+
log,
|
|
586
|
+
});
|
|
587
|
+
const push = await pushTick();
|
|
588
|
+
return {
|
|
589
|
+
id: record.id,
|
|
590
|
+
drifted: drifted > 0,
|
|
591
|
+
remediated: record.summary.remediated,
|
|
592
|
+
reported: record.summary.reported,
|
|
593
|
+
skippedBudget: record.summary.skippedBudget,
|
|
594
|
+
skippedFlap: record.summary.skippedFlap,
|
|
595
|
+
gated: record.summary.gated ?? 0,
|
|
596
|
+
unobserved: record.summary.unobserved,
|
|
597
|
+
adopted: 0,
|
|
598
|
+
log: record.log,
|
|
599
|
+
...push,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export async function convergeTick(args: ConvergeTickArgs, signal?: AbortSignal): Promise<ConvergeTickResult> {
|
|
604
|
+
await fetchLifecycle().catch(() => undefined);
|
|
605
|
+
if (args.observed !== undefined) return resourceTick(args, signal);
|
|
606
|
+
|
|
607
|
+
const [cs, statusRows, ledger] = await Promise.all([
|
|
608
|
+
observeChangeSet(args.env, signal),
|
|
609
|
+
observeStatusRows(args.env, signal),
|
|
610
|
+
readConvergeLedger(args.env),
|
|
611
|
+
]);
|
|
612
|
+
|
|
613
|
+
const symptom = deriveSymptoms(args.env, cs, statusRows);
|
|
614
|
+
const plan = planConvergeTick(symptom, args.rules, ledger.records, args.dial, args.budget);
|
|
615
|
+
|
|
616
|
+
// Execute: only "ran" outcomes cause a subprocess dispatch. Before that
|
|
617
|
+
// dispatch, the runtime backstop (Finding A, #1954 pre-merge review)
|
|
618
|
+
// re-classifies the target and downgrades to "reported" if this dial/verb
|
|
619
|
+
// class pairing was never supposed to reach dispatch — OPS014 (build time)
|
|
620
|
+
// is the primary defense; this is what catches a rule table that reached
|
|
621
|
+
// `convergeTick` without going through it.
|
|
622
|
+
await dispatchPlanned(plan.outcomes, args, signal);
|
|
435
623
|
|
|
436
624
|
const counts = summarizeOutcomes(plan.outcomes);
|
|
437
625
|
const log = renderLog(args.env, symptom, counts);
|
|
@@ -461,16 +649,7 @@ export async function convergeTick(args: ConvergeTickArgs, signal?: AbortSignal)
|
|
|
461
649
|
// chant#2337 — reported rather than swallowed, the way #2336 does it for the
|
|
462
650
|
// gate. A push that did not land leaves a correct local record nobody else
|
|
463
651
|
// can read, and the caller is the only one positioned to say so.
|
|
464
|
-
|
|
465
|
-
let pushWarning: string | undefined;
|
|
466
|
-
try {
|
|
467
|
-
pushed = await pushLifecycle();
|
|
468
|
-
if (!pushed) {
|
|
469
|
-
pushWarning = "no remote is configured for chant/lifecycle — the tick record was recorded locally only";
|
|
470
|
-
}
|
|
471
|
-
} catch (err) {
|
|
472
|
-
pushWarning = err instanceof Error ? err.message : String(err);
|
|
473
|
-
}
|
|
652
|
+
const { pushed, pushWarning } = await pushTick();
|
|
474
653
|
|
|
475
654
|
return {
|
|
476
655
|
id: record.id,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decide activity against a real Jev-compatible server (#2740). Gated:
|
|
3
|
+
* it runs only when TYPESAFE_API_KEY is set, and is skipped otherwise, so the
|
|
4
|
+
* suite never needs an account or the network.
|
|
5
|
+
*
|
|
6
|
+
* TYPESAFE_API_KEY=... npx vitest run packages/core/src/op/activities/decide.live.test.ts
|
|
7
|
+
*
|
|
8
|
+
* TYPESAFE_BASE_URL points it at another compatible server (the default is
|
|
9
|
+
* https://api.typesafe.ai), and TYPESAFE_MODEL at another pinned model id (the
|
|
10
|
+
* default is jev-1.13.0). A model id is always a versioned one, never an alias.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { afterAll, describe, expect, test } from "vitest";
|
|
14
|
+
import { isPointWait } from "../steward-points";
|
|
15
|
+
import { workspacePoints } from "../../workspace/points-cli";
|
|
16
|
+
import { postQuestion } from "../decide-backend";
|
|
17
|
+
import { cleanScratch, workspace } from "../../workspace/__fixtures__/decide-workspace";
|
|
18
|
+
import { runDecide } from "./decide";
|
|
19
|
+
|
|
20
|
+
const key = process.env.TYPESAFE_API_KEY;
|
|
21
|
+
const url = process.env.TYPESAFE_BASE_URL ?? "https://api.typesafe.ai";
|
|
22
|
+
const model = process.env.TYPESAFE_MODEL ?? "jev-1.13.0";
|
|
23
|
+
const backend = { url, key: { env: "TYPESAFE_API_KEY" }, timeoutMs: 60_000 };
|
|
24
|
+
|
|
25
|
+
afterAll(cleanScratch);
|
|
26
|
+
|
|
27
|
+
describe.skipIf(!key)("decide against a real Jev-compatible server (TYPESAFE_API_KEY is set)", () => {
|
|
28
|
+
test("the server answers each question type in the wire format", async () => {
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
const noul = await postQuestion(backend, "systemone", {
|
|
31
|
+
point: "live-noul",
|
|
32
|
+
backend: "systemone",
|
|
33
|
+
model,
|
|
34
|
+
question: { type: "noul", instructions: "Is the sky in the state blue?", criteria: { true: "It is blue.", false: "It is not blue." } },
|
|
35
|
+
state: { sky: "a clear blue sky at noon" },
|
|
36
|
+
}, { cwd });
|
|
37
|
+
expect(noul.model).toBe(model);
|
|
38
|
+
expect(noul.answer.type).toBe("noul");
|
|
39
|
+
expect((noul.answer as { noul: number }).noul).toBeGreaterThanOrEqual(0);
|
|
40
|
+
|
|
41
|
+
const choice = await postQuestion(backend, "systemone", {
|
|
42
|
+
point: "live-choice",
|
|
43
|
+
backend: "systemone",
|
|
44
|
+
model,
|
|
45
|
+
question: { type: "choice", instructions: "Which team takes the ticket in the state?", criteria: { billing: "Payments and invoices.", platform: "Servers and deploys." } },
|
|
46
|
+
state: { ticket: "The production deploy failed with a disk full error." },
|
|
47
|
+
}, { cwd });
|
|
48
|
+
expect(choice.answer).toMatchObject({ type: "choice" });
|
|
49
|
+
expect(["billing", "platform"]).toContain((choice.answer as { choice: string }).choice);
|
|
50
|
+
|
|
51
|
+
const score = await postQuestion(backend, "systemone", {
|
|
52
|
+
point: "live-score",
|
|
53
|
+
backend: "systemone",
|
|
54
|
+
model,
|
|
55
|
+
question: { type: "score", instructions: "How urgent is the ticket in the state?", criteria: ["not urgent", "somewhat urgent", "very urgent"] },
|
|
56
|
+
state: { ticket: "Every customer is locked out of their account." },
|
|
57
|
+
}, { cwd });
|
|
58
|
+
expect(score.answer).toMatchObject({ type: "score" });
|
|
59
|
+
}, 120_000);
|
|
60
|
+
|
|
61
|
+
test("the activity asks a point and records the model's answer or its escalation", async () => {
|
|
62
|
+
const root = workspace();
|
|
63
|
+
const outcome = await runDecide({ cwd: root, point: "triage", inputs: { "record.size": 5, "record.risky": true }, backends: { systemone: backend } }, {}).then(
|
|
64
|
+
(r) => r.id,
|
|
65
|
+
(e: unknown) => {
|
|
66
|
+
if (isPointWait(e)) return e.question.id;
|
|
67
|
+
throw e;
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
const doc = await workspacePoints({ cwd: root });
|
|
71
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
72
|
+
const q = doc.questions.find((x) => x.id === outcome)!;
|
|
73
|
+
// The server answered: a proposal at the pin, or an escalation below the threshold with the model's lean. Never unreachable.
|
|
74
|
+
expect(["proposed", "escalated"]).toContain(q.state);
|
|
75
|
+
if (q.state === "proposed") expect(q.decider).toMatchObject({ kind: "model", model });
|
|
76
|
+
else expect(q.escalations.find((e) => e.kind === "model")?.reason).not.toContain("could not answer");
|
|
77
|
+
}, 120_000);
|
|
78
|
+
});
|