@intentius/chant 0.100.0 → 0.101.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/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/build-options.d.ts +2 -0
- package/dist/cli/build-options.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +8 -0
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +22 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/lexicon.d.ts +24 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/plan-digest.d.ts +26 -5
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lint/config.d.ts +4 -4
- package/dist/op/activities/activity-contracts.d.ts +1 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +2 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/serializer.d.ts +8 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/telemetry-attribution.d.ts +77 -0
- package/dist/telemetry-attribution.d.ts.map +1 -0
- package/dist/workspace/agent-cli.d.ts +83 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes.d.ts +8 -1
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/checks/links.d.ts +1 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/live.d.ts +40 -0
- package/dist/workspace/checks/live.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +21 -2
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +63 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +32 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +138 -3
- package/dist/workspace/export-cli.d.ts +12 -0
- package/dist/workspace/export-cli.d.ts.map +1 -0
- package/dist/workspace/export.d.ts +145 -0
- package/dist/workspace/export.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/import.d.ts +73 -0
- package/dist/workspace/import.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +6 -2
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
- package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-adopt.d.ts +106 -0
- package/dist/workspace/lineage-adopt.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +9 -2
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-cli.d.ts +6 -1
- package/dist/workspace/lineage-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts +110 -0
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +10 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +147 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.d.ts +15 -3
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-provenance.d.ts +18 -0
- package/dist/workspace/lineage-provenance.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +30 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-versions.d.ts +116 -0
- package/dist/workspace/lineage-versions.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +31 -5
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/ls-generated.d.ts +37 -0
- package/dist/workspace/ls-generated.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/nested-graph.d.ts +56 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -0
- package/dist/workspace/nesting.d.ts +21 -0
- package/dist/workspace/nesting.d.ts.map +1 -0
- package/dist/workspace/pin-cli.d.ts +10 -0
- package/dist/workspace/pin-cli.d.ts.map +1 -0
- package/dist/workspace/pin-integrity.d.ts +51 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +26 -2
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-sessions.d.ts +7 -11
- package/dist/workspace/record-sessions.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +30 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +5 -2
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +22 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +43 -5
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/returns.d.ts +129 -0
- package/dist/workspace/returns.d.ts.map +1 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/template-manifest.d.ts +11 -3
- package/dist/workspace/template-manifest.d.ts.map +1 -1
- package/dist/workspace/trust/attestor.d.ts +8 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/dsse.d.ts +58 -0
- package/dist/workspace/trust/dsse.d.ts.map +1 -0
- package/dist/workspace/trust/evidence-cli.d.ts +66 -0
- package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
- package/dist/workspace/trust/evidence.d.ts +93 -0
- package/dist/workspace/trust/evidence.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +54 -1
- package/dist/workspace/trust/policy.d.ts.map +1 -1
- package/dist/workspace/trust/provenance.d.ts +21 -1
- package/dist/workspace/trust/provenance.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts +132 -0
- package/dist/workspace/trust/rotation.d.ts.map +1 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/signers-cli.d.ts +49 -0
- package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +12 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts +13 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/trust/verify.d.ts +15 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/write-scope.d.ts +199 -0
- package/dist/workspace/write-scope.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/build.ts +8 -0
- package/src/cli/build-options.ts +4 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/import-live.test.ts +69 -1
- package/src/cli/commands/import.ts +48 -22
- package/src/cli/handlers/fan-out.test.ts +6 -6
- package/src/cli/handlers/fan-out.ts +2 -1
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +22 -0
- package/src/cli/handlers/operator.ts +1 -1
- package/src/cli/main.test.ts +32 -0
- package/src/cli/main.ts +103 -6
- package/src/cli/mcp/workspace-tools.test.ts +1 -1
- package/src/cli/mcp/workspace-tools.ts +33 -2
- package/src/cli/registry.ts +22 -0
- package/src/cli/serve-mcp-workspace.test.ts +1 -1
- package/src/codegen/release-wiring.test.ts +5 -1
- package/src/components/fan-out-output.test.ts +1 -1
- package/src/components/fan-out.test.ts +1 -1
- package/src/components/promote.test.ts +1 -1
- package/src/config.ts +12 -0
- package/src/content-digest.test.ts +2 -2
- package/src/lexicon.ts +25 -1
- package/src/lifecycle/gate-ledger.test.ts +14 -0
- package/src/lifecycle/gate-ledger.ts +2 -1
- package/src/lifecycle/plan-digest.test.ts +54 -3
- package/src/lifecycle/plan-digest.ts +38 -8
- package/src/op/activities/activity-contracts.ts +1 -0
- package/src/op/activities/propose-upgrade.ts +8 -5
- package/src/op/gate-approval.test.ts +17 -0
- package/src/op/gate.ts +3 -3
- package/src/op/index.ts +1 -1
- package/src/serializer.ts +9 -0
- package/src/telemetry-attribution.test.ts +91 -0
- package/src/telemetry-attribution.ts +145 -0
- package/src/workspace/agent-cli.ts +134 -0
- package/src/workspace/agent.schema.json +356 -0
- package/src/workspace/behold-kinds.test.ts +1 -1
- package/src/workspace/changes-cli.ts +5 -0
- package/src/workspace/changes.schema.json +179 -1
- package/src/workspace/changes.ts +58 -4
- package/src/workspace/check-live.test.ts +192 -0
- package/src/workspace/check.schema.json +64 -0
- package/src/workspace/checks/links.ts +23 -2
- package/src/workspace/checks/live.ts +113 -0
- package/src/workspace/checks.test.ts +2 -2
- package/src/workspace/checks.ts +20 -3
- package/src/workspace/compose-graph.test.ts +47 -0
- package/src/workspace/compose-graph.ts +120 -3
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -0
- package/src/workspace/declaration.schema.json +138 -3
- package/src/workspace/declaration.ts +106 -1
- package/src/workspace/declared-kinds.test.ts +33 -0
- package/src/workspace/evidence.schema.json +279 -0
- package/src/workspace/export-cli.ts +180 -0
- package/src/workspace/export-import.test.ts +282 -0
- package/src/workspace/export.ts +486 -0
- package/src/workspace/graph-cli.ts +47 -1
- package/src/workspace/graph-contract.test.ts +89 -4
- package/src/workspace/graph.schema.json +206 -1
- package/src/workspace/import.ts +325 -0
- package/src/workspace/kinds.test.ts +5 -5
- package/src/workspace/kinds.ts +25 -3
- package/src/workspace/lineage-adopt-cli.ts +103 -0
- package/src/workspace/lineage-adopt.test.ts +552 -0
- package/src/workspace/lineage-adopt.ts +452 -0
- package/src/workspace/lineage-check.ts +26 -5
- package/src/workspace/lineage-cli.ts +12 -2
- package/src/workspace/lineage-hash-index.ts +305 -0
- package/src/workspace/lineage-init.test.ts +9 -0
- package/src/workspace/lineage-init.ts +29 -9
- package/src/workspace/lineage-lock.ts +54 -0
- package/src/workspace/lineage-migrations.test.ts +27 -0
- package/src/workspace/lineage-migrations.ts +44 -14
- package/src/workspace/lineage-provenance.ts +40 -0
- package/src/workspace/lineage-upgrade-cli.ts +9 -5
- package/src/workspace/lineage-upgrade.test.ts +92 -1
- package/src/workspace/lineage-upgrade.ts +111 -16
- package/src/workspace/lineage-versions.ts +348 -0
- package/src/workspace/links.test.ts +121 -2
- package/src/workspace/links.ts +97 -6
- package/src/workspace/ls-contract.test.ts +84 -1
- package/src/workspace/ls-generated.ts +111 -0
- package/src/workspace/ls.schema.json +19 -0
- package/src/workspace/ls.ts +8 -1
- package/src/workspace/member-commands.ts +3 -1
- package/src/workspace/nested-graph.test.ts +176 -0
- package/src/workspace/nested-graph.ts +169 -0
- package/src/workspace/nesting.ts +37 -0
- package/src/workspace/pin-cli.test.ts +71 -0
- package/src/workspace/pin-cli.ts +57 -0
- package/src/workspace/pin-integrity.test.ts +121 -0
- package/src/workspace/pin-integrity.ts +104 -0
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/read-contract.test.ts +24 -0
- package/src/workspace/reason-codes.test.ts +10 -0
- package/src/workspace/reason-codes.ts +29 -2
- package/src/workspace/record-sessions.ts +12 -14
- package/src/workspace/records-amend.schema.json +4 -0
- package/src/workspace/records-cli.ts +93 -12
- package/src/workspace/records-close.schema.json +6 -2
- package/src/workspace/records-close.ts +10 -2
- package/src/workspace/records-formats.test.ts +13 -5
- package/src/workspace/records-new.schema.json +4 -0
- package/src/workspace/records-review.schema.json +4 -0
- package/src/workspace/records-sessions.test.ts +23 -6
- package/src/workspace/records-write.test.ts +65 -8
- package/src/workspace/records-write.ts +70 -6
- package/src/workspace/records.schema.json +55 -3
- package/src/workspace/records.ts +96 -5
- package/src/workspace/returns.ts +328 -0
- package/src/workspace/signers.schema.json +206 -0
- package/src/workspace/status-gates.ts +2 -1
- package/src/workspace/template-manifest.ts +22 -4
- package/src/workspace/trust/attestor.ts +15 -0
- package/src/workspace/trust/dsse.ts +134 -0
- package/src/workspace/trust/evidence-cli.ts +195 -0
- package/src/workspace/trust/evidence.test.ts +241 -0
- package/src/workspace/trust/evidence.ts +207 -0
- package/src/workspace/trust/policy.ts +110 -3
- package/src/workspace/trust/provenance.ts +41 -4
- package/src/workspace/trust/record-seal.test.ts +1 -1
- package/src/workspace/trust/rotation.test.ts +258 -0
- package/src/workspace/trust/rotation.ts +336 -0
- package/src/workspace/trust/seal.ts +11 -0
- package/src/workspace/trust/signers-cli.ts +178 -0
- package/src/workspace/trust/ssh-commit.ts +53 -4
- package/src/workspace/trust/test-repo.ts +18 -0
- package/src/workspace/trust/trust.test.ts +8 -2
- package/src/workspace/trust/verify-cli.ts +1 -0
- package/src/workspace/trust/verify.ts +22 -0
- package/src/workspace/work-evidence.schema.json +4 -0
- package/src/workspace/write-scope.test.ts +340 -0
- package/src/workspace/write-scope.ts +448 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live link checks, WSP141 and WSP142 (#2549; #2524 D6, ws-062).
|
|
3
|
+
*
|
|
4
|
+
* `chant workspace check --live --env <env>` resolves each declared member
|
|
5
|
+
* link against what its producer's estate publishes now. It reads the live
|
|
6
|
+
* graph the way `chant workspace graph --live --env <env>` does: each `chant`
|
|
7
|
+
* member runs `chant graph --live` under its own toolchain, and the outputs of
|
|
8
|
+
* the live graph (the stack outputs a lexicon's live read reports) are the
|
|
9
|
+
* names a link may match, exactly. That is the one place this command reaches
|
|
10
|
+
* a network, through the member's own chant, with the member's own
|
|
11
|
+
* credentials. Without `--live` none of this runs, and the source checks
|
|
12
|
+
* (`./links.ts`) stay offline.
|
|
13
|
+
*
|
|
14
|
+
* | Id | Fails when |
|
|
15
|
+
* |---|---|
|
|
16
|
+
* | WSP141 | the producer was read live and publishes no output of that name |
|
|
17
|
+
* | WSP142 | the link could not be resolved live: the producer's kind is not read live, its read failed, or the read returned no outputs at all |
|
|
18
|
+
*
|
|
19
|
+
* Only declared links of kind `output` are resolved live. A `telemetry` link
|
|
20
|
+
* names a collector's pipeline or exporter, which the graph reports from the
|
|
21
|
+
* member's source, so it is not part of this check.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { WorkspaceCheck, WorkspaceDiagnostic } from "../checks";
|
|
25
|
+
import type { Declaration } from "../declaration";
|
|
26
|
+
import type { KindRegistry } from "../kinds";
|
|
27
|
+
import { DEFAULT_LINK_KIND, irMemberHandles, resolveLinks, type LinkRow } from "../links";
|
|
28
|
+
|
|
29
|
+
/** What a live read of the workspace found, for the live link checks. */
|
|
30
|
+
export interface LiveLinkFacts {
|
|
31
|
+
env: string;
|
|
32
|
+
/** Every declared `output` link, resolved against the live outputs. */
|
|
33
|
+
rows: LinkRow[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const describeLink = (row: LinkRow) => `${row.consumer}'s link to ${row.producer} output ${row.output}`;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Read the live graph of the workspace at `cwd` for `env` and resolve the
|
|
40
|
+
* declared links against it. `onStderr` gets each member's stderr, which is
|
|
41
|
+
* where a lexicon's live read reports an account it could not reach.
|
|
42
|
+
*/
|
|
43
|
+
export async function gatherLiveLinkFacts(cwd: string, declaration: Declaration, kinds: KindRegistry, env: string, onStderr?: (text: string) => void): Promise<LiveLinkFacts> {
|
|
44
|
+
const { workspaceGraph } = await import("../graph-cli");
|
|
45
|
+
const { doc } = await workspaceGraph({ cwd, args: { live: true, env }, noCache: true, ...(onStderr ? { onStderr } : {}) });
|
|
46
|
+
if ("error" in doc) {
|
|
47
|
+
return { env, rows: unreadRows(declaration, `the live graph could not be read: ${doc.error.code}: ${doc.error.message}`) };
|
|
48
|
+
}
|
|
49
|
+
const read = new Map(doc.members.map((m) => [m.name, m]));
|
|
50
|
+
const composed = doc.members.filter((m) => m.status === "composed").map((m) => m.name);
|
|
51
|
+
const handles = irMemberHandles({ composed, exports: doc.exports, imports: doc.imports });
|
|
52
|
+
const table = resolveLinks(declaration, handles);
|
|
53
|
+
const rows: LinkRow[] = [];
|
|
54
|
+
for (const r of table) {
|
|
55
|
+
if (r.status === "ambiguous" || r.origin !== "declared" || r.kind !== DEFAULT_LINK_KIND) continue;
|
|
56
|
+
const row: LinkRow = { ...r, resolves: "live" };
|
|
57
|
+
if (row.status !== "invalid") {
|
|
58
|
+
const producer = read.get(row.producer);
|
|
59
|
+
const kind = kinds.get(declaration.members.find((m) => m.name === row.producer)?.kind ?? "");
|
|
60
|
+
if (!producer || producer.status !== "composed") {
|
|
61
|
+
row.status = "unresolved";
|
|
62
|
+
row.reason =
|
|
63
|
+
producer?.reason?.message ??
|
|
64
|
+
(kind ? `${row.producer} is kind ${kind.name}, which chant does not read live` : `${row.producer} was not read live`);
|
|
65
|
+
} else if (row.status === "missing" && !doc.exports.some((e) => e.member === row.producer)) {
|
|
66
|
+
row.status = "unresolved";
|
|
67
|
+
row.reason = `the live read of ${row.producer} returned no outputs: the account may be unreachable, or its lexicon reports none`;
|
|
68
|
+
} else if (row.status === "missing") {
|
|
69
|
+
row.reason = row.reason?.replace(/^(\S+) has no output/, "$1 publishes no output") ?? row.reason;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
rows.push(row);
|
|
73
|
+
}
|
|
74
|
+
return { env, rows };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Every declared output link as unresolved, when the live graph could not be read at all. */
|
|
78
|
+
function unreadRows(declaration: Declaration, reason: string): LinkRow[] {
|
|
79
|
+
return resolveLinks(declaration, new Map())
|
|
80
|
+
.filter((r): r is LinkRow => r.status !== "ambiguous" && r.origin === "declared" && r.kind === DEFAULT_LINK_KIND)
|
|
81
|
+
.map((r) => ({ ...r, resolves: "live", ...(r.status === "invalid" ? {} : { status: "unresolved" as const, reason }) }));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function liveFinding(check: WorkspaceCheck, row: LinkRow, message: string): WorkspaceDiagnostic {
|
|
85
|
+
return { checkId: check.id, severity: check.severity, message, entity: row.consumer, pointer: `${row.pointer}/output` };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const LIVE_CHECKS: readonly WorkspaceCheck[] = [
|
|
89
|
+
{
|
|
90
|
+
id: "WSP141",
|
|
91
|
+
name: "link-live-missing",
|
|
92
|
+
description: "With --live, a member link names an output its producer's estate publishes now, matched exactly. A producer whose declared output isn't deployed yet, or was renamed in the estate, fails this for every consumer.",
|
|
93
|
+
severity: "warning",
|
|
94
|
+
configurable: true,
|
|
95
|
+
check(ctx) {
|
|
96
|
+
return (ctx.facts?.live?.rows ?? [])
|
|
97
|
+
.filter((r) => r.status === "missing")
|
|
98
|
+
.map((r) => liveFinding(this, r, `${describeLink(r)} does not resolve live in ${ctx.facts!.live!.env}: ${r.reason}`));
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "WSP142",
|
|
103
|
+
name: "link-live-unresolved",
|
|
104
|
+
description: "With --live, a member link that could not be resolved against a live read is kept, unresolved, and reported: the producer's kind is not read live, its read failed, or the read returned no outputs.",
|
|
105
|
+
severity: "info",
|
|
106
|
+
configurable: true,
|
|
107
|
+
check(ctx) {
|
|
108
|
+
return (ctx.facts?.live?.rows ?? [])
|
|
109
|
+
.filter((r) => r.status === "unresolved")
|
|
110
|
+
.map((r) => liveFinding(this, r, `${describeLink(r)} is kept unresolved in ${ctx.facts!.live!.env}: ${r.reason}`));
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
];
|
|
@@ -75,7 +75,7 @@ describe("the WSP catalog", () => {
|
|
|
75
75
|
expect(list.filter((id) => /^WSP(07|08|09|10)\d$/.test(id))).toEqual([
|
|
76
76
|
"WSP071", "WSP072", "WSP073",
|
|
77
77
|
"WSP081", "WSP082", "WSP083",
|
|
78
|
-
"WSP091", "WSP092", "WSP093", "WSP094", "WSP095", "WSP096", "WSP097",
|
|
78
|
+
"WSP091", "WSP092", "WSP093", "WSP094", "WSP095", "WSP096", "WSP097", "WSP098",
|
|
79
79
|
"WSP101", "WSP102", "WSP103", "WSP104", "WSP105", "WSP106",
|
|
80
80
|
]);
|
|
81
81
|
});
|
|
@@ -111,7 +111,7 @@ describe("declaration checks (#2535)", () => {
|
|
|
111
111
|
column: 15,
|
|
112
112
|
ruleId: "WSP003",
|
|
113
113
|
severity: "error",
|
|
114
|
-
message: "member infra has kind terraform, which no built-in kind or pinned package supplies; known kinds: chant, other, workspace",
|
|
114
|
+
message: "member infra has kind terraform, which no built-in kind or pinned package supplies; known kinds: chant, design, other, workspace",
|
|
115
115
|
entity: "infra",
|
|
116
116
|
},
|
|
117
117
|
]);
|
package/src/workspace/checks.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* | WSP001 to WSP011 | the declaration and member kinds (this file) |
|
|
25
25
|
* | WSP071, WSP072 | member ledgers |
|
|
26
26
|
* | WSP081 to WSP083 | recorded pipelines |
|
|
27
|
-
* | WSP091 to
|
|
27
|
+
* | WSP091 to WSP098 | member links (#2539, #2558) |
|
|
28
28
|
* | WSP101 to WSP106 | generated files |
|
|
29
29
|
* | WSP111 to WSP114 | records read with `--kind` (#2549) |
|
|
30
30
|
* | WSP115 | the record kinds the declaration names (#2680) |
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* | WSP123, WSP124 | box isolation: no shared port, state path or cookie, no literal machine path (#2727) |
|
|
35
35
|
* | WSP125 | boxes: a fountain Box declares the callback token fountain gives its sandbox (#2780) |
|
|
36
36
|
* | WSP126, WSP127 | box intent: the decision record a box names exists, and constrains a member or path of this workspace (#2850, #2857) |
|
|
37
|
+
* | WSP141, WSP142 | member links resolved against a live read, with `--live` (#2549) |
|
|
37
38
|
* | WSP131 to WSP133 | diagram artifacts: source and render exist, a recorded source hash still matches (#2764) |
|
|
38
39
|
*/
|
|
39
40
|
|
|
@@ -47,10 +48,11 @@ import { parseJsonText, pointerToken, type TextLocation } from "./jsonc";
|
|
|
47
48
|
import { loadKindRegistry, probeKind, resolveKind, type KindLoadProblem, type KindRegistry } from "./kinds";
|
|
48
49
|
import { gitTop, workingTree, type WorkspaceTree } from "./tree";
|
|
49
50
|
import { LINK_CHECKS, linkTable } from "./checks/links";
|
|
50
|
-
import type { LinkTableRow } from "./links";
|
|
51
|
+
import type { LinkRow, LinkTableRow } from "./links";
|
|
51
52
|
import { gatherGeneratedFacts, GENERATED_CHECKS, type GeneratedFileFacts } from "./checks/generated";
|
|
52
53
|
import { gatherLedgerFacts, LEDGER_CHECKS, type MemberLedgerFacts } from "./checks/ledgers";
|
|
53
54
|
import { gatherPipelineFacts, PIPELINE_CHECKS, type MemberPipelineFacts } from "./checks/pipelines";
|
|
55
|
+
import { gatherLiveLinkFacts, LIVE_CHECKS, type LiveLinkFacts } from "./checks/live";
|
|
54
56
|
import { RECORD_CHECKS, type RecordFacts } from "./checks/records";
|
|
55
57
|
import { BOX_CHECKS } from "./checks/boxes";
|
|
56
58
|
import { DIAGRAM_CHECKS } from "./checks/diagrams";
|
|
@@ -73,6 +75,8 @@ export interface WorkspaceFacts {
|
|
|
73
75
|
records?: RecordFacts;
|
|
74
76
|
/** The record kinds the declaration names, each loaded, or only looked for under `--at` (#2680). */
|
|
75
77
|
declaredKinds?: readonly DeclaredKind[];
|
|
78
|
+
/** The declared links resolved against a live read, with `--live` (#2549). */
|
|
79
|
+
live?: LiveLinkFacts;
|
|
76
80
|
/** The decision record each box's intent names, read from the working tree (#2850). */
|
|
77
81
|
boxIntents?: readonly ResolvedBoxIntent[];
|
|
78
82
|
}
|
|
@@ -137,6 +141,8 @@ export interface DeclarationCheckReport {
|
|
|
137
141
|
suppressed: SuppressedFinding[];
|
|
138
142
|
/** The member links, declared and inferred, as resolved in source (#2539). */
|
|
139
143
|
links: LinkTableRow[];
|
|
144
|
+
/** With `--live`, the declared links as resolved against a live read of `env` (#2549). */
|
|
145
|
+
live?: { env: string; links: LinkRow[] };
|
|
140
146
|
/** Whether an error-severity finding is active. */
|
|
141
147
|
ok: boolean;
|
|
142
148
|
}
|
|
@@ -319,7 +325,7 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
|
|
|
319
325
|
...LEDGER_CHECKS,
|
|
320
326
|
// Recorded pipelines (#2542).
|
|
321
327
|
...PIPELINE_CHECKS,
|
|
322
|
-
// Member links (#2539), WSP091 to
|
|
328
|
+
// Member links (#2539, #2558), WSP091 to WSP098.
|
|
323
329
|
...LINK_CHECKS,
|
|
324
330
|
// Generated files (#2541).
|
|
325
331
|
...GENERATED_CHECKS,
|
|
@@ -329,6 +335,8 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
|
|
|
329
335
|
...BOX_CHECKS,
|
|
330
336
|
// Diagram artifacts (#2764).
|
|
331
337
|
...DIAGRAM_CHECKS,
|
|
338
|
+
// Links resolved against a live read, with --live (#2549), WSP141 and WSP142.
|
|
339
|
+
...LIVE_CHECKS,
|
|
332
340
|
];
|
|
333
341
|
|
|
334
342
|
const BY_ID = new Map(WORKSPACE_CHECKS.map((c) => [c.id, c]));
|
|
@@ -406,6 +414,13 @@ export interface DeclarationCheckOptions {
|
|
|
406
414
|
tree?: WorkspaceTree;
|
|
407
415
|
/** The records read with `--kind`, in the same tree (#2549). Given with or without `tree`. */
|
|
408
416
|
records?: RecordFacts;
|
|
417
|
+
/**
|
|
418
|
+
* Resolve the declared links against a live read of this environment
|
|
419
|
+
* (`--live --env`, #2549). Each `chant` member runs `chant graph --live`, so
|
|
420
|
+
* this reaches whatever those reach. Not with `tree`: a live read is of the
|
|
421
|
+
* account now. `onStderr` gets each member's stderr.
|
|
422
|
+
*/
|
|
423
|
+
live?: { env: string; onStderr?: (text: string) => void };
|
|
409
424
|
}
|
|
410
425
|
|
|
411
426
|
/**
|
|
@@ -480,6 +495,7 @@ export async function runDeclarationChecks(
|
|
|
480
495
|
...(options.records ? { records: options.records } : {}),
|
|
481
496
|
...(declaredKinds ? { declaredKinds } : {}),
|
|
482
497
|
...(boxIntents ? { boxIntents } : {}),
|
|
498
|
+
...(options.live && !options.tree ? { live: await gatherLiveLinkFacts(root, declaration, registry, options.live.env, options.live.onStderr) } : {}),
|
|
483
499
|
};
|
|
484
500
|
const ctx: WorkspaceCheckContext = { declaration, tree, groups, kinds: registry, kindProblems: problems, facts };
|
|
485
501
|
const findings = runWorkspaceChecks(ctx);
|
|
@@ -507,6 +523,7 @@ export async function runDeclarationChecks(
|
|
|
507
523
|
diagnostics,
|
|
508
524
|
suppressed: suppressed.map((s) => ({ ...toFinding(s), reason: s.reason })).sort(order),
|
|
509
525
|
links: linkTable(ctx),
|
|
526
|
+
...(facts.live ? { live: { env: facts.live.env, links: facts.live.rows } } : {}),
|
|
510
527
|
ok: !diagnostics.some((d) => d.severity === "error"),
|
|
511
528
|
};
|
|
512
529
|
}
|
|
@@ -146,3 +146,50 @@ describe("the links section (#2539)", () => {
|
|
|
146
146
|
]);
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
describe("collectors (#2559)", () => {
|
|
151
|
+
const topology = {
|
|
152
|
+
pipelines: [{ id: "traces", signal: "traces", receivers: ["otlp"], processors: ["batch"], exporters: ["otlphttp/honeycomb"] }],
|
|
153
|
+
components: [
|
|
154
|
+
{ id: "otlp", kind: "receiver", type: "otlp", builtin: true, endpoints: ["0.0.0.0:4317"], protocols: ["grpc"], pipelines: ["traces"] },
|
|
155
|
+
{ id: "otlphttp/honeycomb", kind: "exporter", type: "otlphttp", builtin: true, endpoints: ["https://api.honeycomb.io"], protocols: ["http/protobuf"], pipelines: ["traces"] },
|
|
156
|
+
],
|
|
157
|
+
exporters: [{ id: "otlphttp/honeycomb", type: "otlphttp", endpoints: ["https://api.honeycomb.io"], pipelines: ["traces"], signals: ["traces"] }],
|
|
158
|
+
edges: [],
|
|
159
|
+
semconv: [],
|
|
160
|
+
};
|
|
161
|
+
const withMeta = (meta: Record<string, unknown>): GraphIR => ({ ...structuredClone(db), meta });
|
|
162
|
+
|
|
163
|
+
test("lifts a member's meta.collector into a section and keeps the rest of its meta", () => {
|
|
164
|
+
const inputs = [
|
|
165
|
+
{ member: member("web", "web"), ir: structuredClone(web) },
|
|
166
|
+
{ member: member("ops", "ops"), ir: withMeta({ collector: topology, other: 1 }) },
|
|
167
|
+
];
|
|
168
|
+
const doc = composeWorkspaceGraph({ name: "acme", root: "/w" }, inputs);
|
|
169
|
+
expect(doc.collectors).toEqual([
|
|
170
|
+
{
|
|
171
|
+
member: "ops",
|
|
172
|
+
pipelines: topology.pipelines,
|
|
173
|
+
components: topology.components.map(({ builtin: _b, ...c }) => c),
|
|
174
|
+
exporters: topology.exporters,
|
|
175
|
+
},
|
|
176
|
+
]);
|
|
177
|
+
expect(doc.members.find((m) => m.name === "ops")!.meta).toEqual({ other: 1 });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("is empty when no member reports a collector, and leaves meta alone", () => {
|
|
181
|
+
const doc = composeWorkspaceGraph({ name: "acme", root: "/w" }, [
|
|
182
|
+
{ member: member("web", "web"), ir: structuredClone(web) },
|
|
183
|
+
{ member: member("ops", "ops"), ir: withMeta({ _behaviour: { engine: "x" } }) },
|
|
184
|
+
]);
|
|
185
|
+
expect(doc.collectors).toEqual([]);
|
|
186
|
+
expect(doc.members.find((m) => m.name === "ops")!.meta).toEqual({ _behaviour: { engine: "x" } });
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("ignores a meta.collector that is not a topology", () => {
|
|
190
|
+
for (const bad of ["x", 1, null, {}, { pipelines: "no" }]) {
|
|
191
|
+
const doc = composeWorkspaceGraph({ name: "acme", root: "/w" }, [{ member: member("ops", "ops"), ir: withMeta({ collector: bad }) }]);
|
|
192
|
+
expect(doc.collectors, JSON.stringify(bad)).toEqual([]);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -87,12 +87,75 @@ export interface ComposedMember {
|
|
|
87
87
|
cached?: boolean;
|
|
88
88
|
/** The member's stamp (#2876): what the cache keys the read on, or null when none could be taken. */
|
|
89
89
|
stamp?: string | null;
|
|
90
|
+
/**
|
|
91
|
+
* For a member of kind `workspace` (#2551, ws-071): the nested workspace's
|
|
92
|
+
* own view, read through its own `chant workspace graph`. Its nodes are in
|
|
93
|
+
* the outer `nodes` with `outer/inner/id` ids.
|
|
94
|
+
*/
|
|
95
|
+
nested?: NestedWorkspace;
|
|
90
96
|
/** Whole-read facts the member's IR carried (`meta`, `pipeline`), kept apart from the composed sections. */
|
|
91
97
|
meta?: Record<string, unknown>;
|
|
92
98
|
pipeline?: unknown;
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
/** A nested workspace as the outer graph shows it (#2551, ./nested-graph.ts). */
|
|
102
|
+
export interface NestedWorkspace {
|
|
103
|
+
/** The nested declaration's name. */
|
|
104
|
+
name: string;
|
|
105
|
+
/** The contract version of the document the nested read printed. */
|
|
106
|
+
contract: number;
|
|
107
|
+
/** The nested workspace's members, as its own graph lists them. */
|
|
108
|
+
members: ComposedMember[];
|
|
109
|
+
/** The nested workspace's links, with member names as the nested side writes them. */
|
|
110
|
+
links: (LinkTableRow | RecordLinkRow)[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A collector pipeline as a member's IR reports it (#2559). Plain data, so
|
|
115
|
+
* core never imports the lexicon that computes it.
|
|
116
|
+
*/
|
|
117
|
+
export interface CollectorPipeline {
|
|
118
|
+
/** The id under `service.pipelines`, such as `traces` or `traces/backend`. */
|
|
119
|
+
id: string;
|
|
120
|
+
signal: string;
|
|
121
|
+
receivers: string[];
|
|
122
|
+
processors: string[];
|
|
123
|
+
exporters: string[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** One collector component with the addresses its config states. */
|
|
127
|
+
export interface CollectorComponent {
|
|
128
|
+
id: string;
|
|
129
|
+
kind: string;
|
|
130
|
+
type: string;
|
|
131
|
+
endpoints: string[];
|
|
132
|
+
/** The wire protocols it speaks (`grpc`, `http/protobuf`, `http/json`); empty when unknown (#2558). */
|
|
133
|
+
protocols: string[];
|
|
134
|
+
pipelines: string[];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** An exporter and the signals that reach it: where a member's telemetry goes. */
|
|
138
|
+
export interface CollectorExporter {
|
|
139
|
+
id: string;
|
|
140
|
+
type: string;
|
|
141
|
+
endpoints: string[];
|
|
142
|
+
pipelines: string[];
|
|
143
|
+
signals: string[];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The topology of one member's collector config, from the lexicon that types it. */
|
|
147
|
+
export interface MemberCollector {
|
|
148
|
+
member: string;
|
|
149
|
+
pipelines: CollectorPipeline[];
|
|
150
|
+
components: CollectorComponent[];
|
|
151
|
+
exporters: CollectorExporter[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The meta key a lexicon's `graphMeta` hook answers the collector under. */
|
|
155
|
+
export const COLLECTOR_META_KEY = "collector";
|
|
156
|
+
|
|
157
|
+
/** `nested` names the nested workspace's member a node was read through (#2551); absent for the outer workspace's own nodes. */
|
|
158
|
+
export type ComposedNode = IRNode & { member: string; nested?: string };
|
|
96
159
|
export type ComposedEdge = IREdge & { member: string };
|
|
97
160
|
|
|
98
161
|
export interface WorkspaceGraph {
|
|
@@ -110,6 +173,11 @@ export interface WorkspaceGraph {
|
|
|
110
173
|
* the records read with `--kind` (#2549). Empty when no declaration is given.
|
|
111
174
|
*/
|
|
112
175
|
links: (LinkTableRow | RecordLinkRow)[];
|
|
176
|
+
/**
|
|
177
|
+
* The collector pipelines and exporters of each member that declares a
|
|
178
|
+
* collector, in declaration order (#2559). Empty when none does.
|
|
179
|
+
*/
|
|
180
|
+
collectors: MemberCollector[];
|
|
113
181
|
/** The records read with `--kind` (#2524 D4, #2549). Empty without it. */
|
|
114
182
|
records: GraphRecord[];
|
|
115
183
|
}
|
|
@@ -163,6 +231,46 @@ export interface ComposeInput {
|
|
|
163
231
|
|
|
164
232
|
const prefix = (member: string, id: string): string => `${member}/${id}`;
|
|
165
233
|
|
|
234
|
+
const strings = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : []);
|
|
235
|
+
const records = (v: unknown): Record<string, unknown>[] =>
|
|
236
|
+
Array.isArray(v) ? v.filter((x): x is Record<string, unknown> => typeof x === "object" && x !== null) : [];
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* A member's collector topology from its IR meta, or null when the meta holds
|
|
240
|
+
* none or something that isn't one. Read defensively: the topology comes from
|
|
241
|
+
* a lexicon the member's own toolchain loaded, which may be newer or older
|
|
242
|
+
* than this chant.
|
|
243
|
+
*/
|
|
244
|
+
function readCollector(member: string, value: unknown): MemberCollector | null {
|
|
245
|
+
if (typeof value !== "object" || value === null || !Array.isArray((value as { pipelines?: unknown }).pipelines)) return null;
|
|
246
|
+
const v = value as Record<string, unknown>;
|
|
247
|
+
return {
|
|
248
|
+
member,
|
|
249
|
+
pipelines: records(v.pipelines).map((p) => ({
|
|
250
|
+
id: String(p.id),
|
|
251
|
+
signal: String(p.signal),
|
|
252
|
+
receivers: strings(p.receivers),
|
|
253
|
+
processors: strings(p.processors),
|
|
254
|
+
exporters: strings(p.exporters),
|
|
255
|
+
})),
|
|
256
|
+
components: records(v.components).map((c) => ({
|
|
257
|
+
id: String(c.id),
|
|
258
|
+
kind: String(c.kind),
|
|
259
|
+
type: String(c.type),
|
|
260
|
+
endpoints: strings(c.endpoints),
|
|
261
|
+
protocols: strings(c.protocols),
|
|
262
|
+
pipelines: strings(c.pipelines),
|
|
263
|
+
})),
|
|
264
|
+
exporters: records(v.exporters).map((e) => ({
|
|
265
|
+
id: String(e.id),
|
|
266
|
+
type: String(e.type),
|
|
267
|
+
endpoints: strings(e.endpoints),
|
|
268
|
+
pipelines: strings(e.pipelines),
|
|
269
|
+
signals: strings(e.signals),
|
|
270
|
+
})),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
166
274
|
function prefixRefs(value: unknown, member: string): unknown {
|
|
167
275
|
if (Array.isArray(value)) return value.map((v) => prefixRefs(v, member));
|
|
168
276
|
if (value === null || typeof value !== "object") return value;
|
|
@@ -202,6 +310,7 @@ export function composeWorkspaceGraph(
|
|
|
202
310
|
const shared: Record<"byLexicon" | "byComposite", Record<string, string[]>> = { byLexicon: {}, byComposite: {} };
|
|
203
311
|
const scoped: Record<"byStack" | "byContainer" | "byWave", Record<string, string[]>> = { byStack: {}, byContainer: {}, byWave: {} };
|
|
204
312
|
const derivedAttrs: Record<string, Set<string>> = {};
|
|
313
|
+
const collectors: MemberCollector[] = [];
|
|
205
314
|
|
|
206
315
|
for (const { member, ir } of inputs) {
|
|
207
316
|
if (!ir) continue;
|
|
@@ -225,7 +334,14 @@ export function composeWorkspaceGraph(
|
|
|
225
334
|
for (const e of ir.exports ?? []) exports.push({ ...e, ...(e.node ? { node: prefix(m, e.node) } : {}), member: m });
|
|
226
335
|
for (const i of ir.imports ?? []) imports.push({ ...i, node: prefix(m, i.node), member: m });
|
|
227
336
|
for (const [kind, attrs] of Object.entries(ir.derivedAttrs ?? {})) for (const a of attrs) (derivedAttrs[kind] ??= new Set()).add(a);
|
|
228
|
-
if (ir.meta)
|
|
337
|
+
if (ir.meta) {
|
|
338
|
+
// The collector topology has a section of its own; the rest of the meta stays on the member.
|
|
339
|
+
const { [COLLECTOR_META_KEY]: collector, ...rest } = ir.meta;
|
|
340
|
+
const read = readCollector(m, collector);
|
|
341
|
+
if (read) collectors.push(read);
|
|
342
|
+
if (!read) member.meta = ir.meta;
|
|
343
|
+
else if (Object.keys(rest).length > 0) member.meta = rest;
|
|
344
|
+
}
|
|
229
345
|
if (ir.pipeline) member.pipeline = ir.pipeline;
|
|
230
346
|
}
|
|
231
347
|
|
|
@@ -247,8 +363,9 @@ export function composeWorkspaceGraph(
|
|
|
247
363
|
exports,
|
|
248
364
|
imports,
|
|
249
365
|
links: links
|
|
250
|
-
? graphLinks(links.declaration, { composed: inputs.filter((i) => i.ir).map((i) => i.member.name), exports, imports }, links.kinds)
|
|
366
|
+
? graphLinks(links.declaration, { composed: inputs.filter((i) => i.ir).map((i) => i.member.name), collectors, exports, imports }, links.kinds)
|
|
251
367
|
: [],
|
|
368
|
+
collectors,
|
|
252
369
|
records: [],
|
|
253
370
|
};
|
|
254
371
|
if (Object.keys(derivedAttrs).length) {
|
|
@@ -14,8 +14,14 @@ export const recordKind = {
|
|
|
14
14
|
idField: "id",
|
|
15
15
|
stateField: "state",
|
|
16
16
|
states: ["proposed", "decided", "ratified", "superseded", "withdrawn"],
|
|
17
|
-
// Sealed once reached (#2555)
|
|
17
|
+
// Sealed once reached (#2555): records amend and new write the whole-file
|
|
18
|
+
// seal into closed_digest when a decision enters one, and records checks it
|
|
19
|
+
// on every read (#2546, ws-063).
|
|
18
20
|
closedStates: ["ratified", "superseded"],
|
|
21
|
+
seal: { field: "closed_digest" },
|
|
22
|
+
// The current decisions, with the files they pin, are the workspace's spec:
|
|
23
|
+
// records --current --json lists them under spec (#2524 D20, #2546).
|
|
24
|
+
spec: true,
|
|
19
25
|
supersedes: { field: "supersedes", key: "decision" },
|
|
20
26
|
// A remediates link names a closed decision this one fixes the consequence
|
|
21
27
|
// of, without replacing it: the target's state never changes (#2774).
|
|
@@ -227,6 +227,11 @@
|
|
|
227
227
|
"signature": { "description": "The armored ssh signature, as ssh-keygen -Y sign prints it.", "type": "string", "pattern": "^-----BEGIN SSH SIGNATURE-----\\n" }
|
|
228
228
|
}
|
|
229
229
|
},
|
|
230
|
+
"closed_digest": {
|
|
231
|
+
"description": "The decision's whole-file seal (#2546, ws-063): sha256: and the lowercase hex SHA-256 of the JCS form of {body, core}, where core is this front matter without closed_digest and body is the text below it, line endings LF. chant workspace records amend and new write it when the decision enters a closed state (ratified or superseded), and records reports record-seal-mismatch if the file changes afterwards. It covers the reviews, the author seal and the state, which the review digest leaves out, and the digest leaves this field out.",
|
|
232
|
+
"type": "string",
|
|
233
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
234
|
+
},
|
|
230
235
|
"constrains": {
|
|
231
236
|
"description": "What this decision governs: issues (owner/repo#n), other decisions (their ids), workspace members (member:<name>) or files and directories in the workspace (path:<path>, the grammar of an evidence path, #2549). At least one entry: a decision that governs nothing is refused.",
|
|
232
237
|
"type": "array",
|
|
@@ -101,6 +101,36 @@
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
},
|
|
105
|
+
"writeScope": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"description": "Who may write what (#2524 D5, #2548, ws-067): for each principal class, the members whose files it may write and the record kinds it may write, with which verbs. A class with no entry is not restricted. An agent is always bound to its session's member (agents); its entry can only narrow the record kinds and verbs. chant workspace records new, amend, review and close and the MCP record tools refuse a write outside the writer's scope with write-scope-member or write-scope-kind, and chant workspace check --changes reports each commit that writes outside its author's scope, read from the declaration and the trust policy at the range's base. Added in schema 1 by chant 0.101.0, so a declaration that uses it sets minReader to 0.101.0 or newer.",
|
|
109
|
+
"properties": {
|
|
110
|
+
"human": {
|
|
111
|
+
"$ref": "#/$defs/classScope",
|
|
112
|
+
"description": "Principals holding none of the agent, runner and service roles in the trust policy at base."
|
|
113
|
+
},
|
|
114
|
+
"agent": {
|
|
115
|
+
"$ref": "#/$defs/agentScope",
|
|
116
|
+
"description": "An agent session (agents), or a principal holding the agent role in the trust policy at base."
|
|
117
|
+
},
|
|
118
|
+
"runner": {
|
|
119
|
+
"$ref": "#/$defs/classScope",
|
|
120
|
+
"description": "Principals holding the runner role in the trust policy at base, such as a CI job's signing identity."
|
|
121
|
+
},
|
|
122
|
+
"service": {
|
|
123
|
+
"$ref": "#/$defs/classScope",
|
|
124
|
+
"description": "Principals holding the service role in the trust policy at base."
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"agents": {
|
|
129
|
+
"type": "array",
|
|
130
|
+
"description": "The agent sessions the workspace declares (#2524 D20, #2548, ws-067). Each is bound to one member: it writes that member's files and the records of kinds that member or the workspace declares, as writeScope.agent allows. A session resumes from chant workspace agent <name> --json, which prints its member, its scope and the spec, read from the repository alone. A write names its session with the CHANT_AGENT environment variable, or a commit with a Chant-Agent trailer. Added in schema 1 by chant 0.101.0, so a declaration that uses it sets minReader to 0.101.0 or newer.",
|
|
131
|
+
"items": {
|
|
132
|
+
"$ref": "#/$defs/agent"
|
|
133
|
+
}
|
|
104
134
|
}
|
|
105
135
|
},
|
|
106
136
|
"patternProperties": {
|
|
@@ -173,7 +203,7 @@
|
|
|
173
203
|
},
|
|
174
204
|
"link": {
|
|
175
205
|
"type": "object",
|
|
176
|
-
"description": "A member link: this member reads output of member (#2539).",
|
|
206
|
+
"description": "A member link: this member reads output of member (#2539), or sends its telemetry to a pipeline of its collector (#2558).",
|
|
177
207
|
"required": [
|
|
178
208
|
"member",
|
|
179
209
|
"output"
|
|
@@ -185,11 +215,19 @@
|
|
|
185
215
|
},
|
|
186
216
|
"output": {
|
|
187
217
|
"$ref": "#/$defs/outputName",
|
|
188
|
-
"description": "The producer's output, matched exactly. It must be one the producer's kind exposes."
|
|
218
|
+
"description": "The producer's output, matched exactly. It must be one the producer's kind exposes. For a telemetry link, the id of a pipeline or an exporter in the producer's collector."
|
|
189
219
|
},
|
|
190
220
|
"kind": {
|
|
191
221
|
"$ref": "#/$defs/kindName",
|
|
192
|
-
"description": "The link kind. output, the default
|
|
222
|
+
"description": "The link kind. output, the default: the consumer reads the named output. telemetry (#2558): the consumer sends its telemetry to the named pipeline or exporter of the producer's collector, and chant workspace graph checks that it exists and, when protocol is given, that it speaks it. A declaration that uses telemetry sets minReader to 0.101.0 or newer."
|
|
223
|
+
},
|
|
224
|
+
"protocol": {
|
|
225
|
+
"enum": [
|
|
226
|
+
"grpc",
|
|
227
|
+
"http/protobuf",
|
|
228
|
+
"http/json"
|
|
229
|
+
],
|
|
230
|
+
"description": "For a telemetry link, the OTLP protocol the consumer sends, as OTEL_EXPORTER_OTLP_PROTOCOL names it. Refused on any other kind. Added in schema 1 by chant 0.101.0."
|
|
193
231
|
}
|
|
194
232
|
},
|
|
195
233
|
"patternProperties": {
|
|
@@ -320,6 +358,10 @@
|
|
|
320
358
|
"minLength": 1,
|
|
321
359
|
"description": "Where an embedded member's files come from. Versions live in the lock."
|
|
322
360
|
},
|
|
361
|
+
"travel": {
|
|
362
|
+
"type": "boolean",
|
|
363
|
+
"description": "Whether the member goes with an export (#2524 D10, #2552). chant workspace export writes only members that set travel to true into the export member, and refuses to name one that does not. Added in schema 1 by chant 0.101.0, so a declaration that uses it sets minReader to 0.101.0 or newer."
|
|
364
|
+
},
|
|
323
365
|
"because": {
|
|
324
366
|
"type": "string",
|
|
325
367
|
"minLength": 1,
|
|
@@ -858,6 +900,99 @@
|
|
|
858
900
|
"type": "string",
|
|
859
901
|
"description": "A glob over file paths, relative to the workspace root: * and ? within one segment, ** across segments, [...] classes and {a,b} alternatives. No leading /, no . or .. segments.",
|
|
860
902
|
"pattern": "^(?!\\.\\.?(/|$))[^/\\\\]+(/(?!\\.\\.?(/|$))[^/\\\\]+)*$"
|
|
903
|
+
},
|
|
904
|
+
"recordScope": {
|
|
905
|
+
"type": "object",
|
|
906
|
+
"description": "Record kind names, as the kind file's recordKind.name or the name the declaration gives it, each with the verbs the class may write it with: new, amend, review and close (a review session). A kind left out may not be written at all. A record file a commit deletes is never in scope.",
|
|
907
|
+
"propertyNames": {
|
|
908
|
+
"$ref": "#/$defs/kindName"
|
|
909
|
+
},
|
|
910
|
+
"additionalProperties": {
|
|
911
|
+
"type": "array",
|
|
912
|
+
"uniqueItems": true,
|
|
913
|
+
"items": {
|
|
914
|
+
"enum": [
|
|
915
|
+
"new",
|
|
916
|
+
"amend",
|
|
917
|
+
"review",
|
|
918
|
+
"close"
|
|
919
|
+
]
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
"classScope": {
|
|
924
|
+
"type": "object",
|
|
925
|
+
"description": "A principal class's write scope.",
|
|
926
|
+
"properties": {
|
|
927
|
+
"members": {
|
|
928
|
+
"description": "The members whose files the class may write, by name, or * for every path. Without it, every path. A path in no member is in scope only under *.",
|
|
929
|
+
"oneOf": [
|
|
930
|
+
{
|
|
931
|
+
"const": "*"
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
"type": "array",
|
|
935
|
+
"uniqueItems": true,
|
|
936
|
+
"items": {
|
|
937
|
+
"$ref": "#/$defs/name"
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
]
|
|
941
|
+
},
|
|
942
|
+
"records": {
|
|
943
|
+
"$ref": "#/$defs/recordScope",
|
|
944
|
+
"description": "The record kinds the class may write, and how. Without it, every kind in reach, with every verb. A kind a member declares is in reach when that member is; the workspace's own kinds always are."
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
"patternProperties": {
|
|
948
|
+
"^x-": true
|
|
949
|
+
},
|
|
950
|
+
"additionalProperties": false
|
|
951
|
+
},
|
|
952
|
+
"agentScope": {
|
|
953
|
+
"type": "object",
|
|
954
|
+
"description": "The agent class's write scope. Its members are always its session's one member, so only the record kinds can be set.",
|
|
955
|
+
"properties": {
|
|
956
|
+
"records": {
|
|
957
|
+
"$ref": "#/$defs/recordScope",
|
|
958
|
+
"description": "The record kinds an agent may write, and how. Without it, every kind in reach of its member, with every verb."
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
"patternProperties": {
|
|
962
|
+
"^x-": true
|
|
963
|
+
},
|
|
964
|
+
"additionalProperties": false
|
|
965
|
+
},
|
|
966
|
+
"agent": {
|
|
967
|
+
"type": "object",
|
|
968
|
+
"description": "An agent session (#2548).",
|
|
969
|
+
"required": [
|
|
970
|
+
"name",
|
|
971
|
+
"member"
|
|
972
|
+
],
|
|
973
|
+
"properties": {
|
|
974
|
+
"name": {
|
|
975
|
+
"$ref": "#/$defs/name",
|
|
976
|
+
"description": "The session's name, unique among agents: what CHANT_AGENT and a Chant-Agent trailer give."
|
|
977
|
+
},
|
|
978
|
+
"member": {
|
|
979
|
+
"$ref": "#/$defs/name",
|
|
980
|
+
"description": "The one member the session is bound to. It must be a declared member, not an example group."
|
|
981
|
+
},
|
|
982
|
+
"principals": {
|
|
983
|
+
"type": "array",
|
|
984
|
+
"uniqueItems": true,
|
|
985
|
+
"items": {
|
|
986
|
+
"type": "string",
|
|
987
|
+
"minLength": 1
|
|
988
|
+
},
|
|
989
|
+
"description": "Principals that write only as this session: a commit attested by one, or a write whose by names one, is judged as the session. A principal is listed by one session at most."
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
"patternProperties": {
|
|
993
|
+
"^x-": true
|
|
994
|
+
},
|
|
995
|
+
"additionalProperties": false
|
|
861
996
|
}
|
|
862
997
|
}
|
|
863
998
|
}
|