@intentius/chant 0.85.0 → 0.87.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/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +13 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +13 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/sessions.d.ts +23 -0
- package/dist/workspace/__fixtures__/sessions.d.ts.map +1 -0
- package/dist/workspace/checks/records.d.ts +1 -0
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +4 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/composites.d.ts +14 -1
- package/dist/workspace/composites.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts +211 -0
- package/dist/workspace/conformance/index.d.ts.map +1 -0
- package/dist/workspace/conformance/vitest.d.ts +11 -0
- package/dist/workspace/conformance/vitest.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +28 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +40 -0
- package/dist/workspace/declared-kinds.d.ts +43 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +11 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.d.ts +2 -1
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-joins.d.ts +45 -8
- package/dist/workspace/intent-joins.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +71 -7
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/ls.d.ts +31 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/reason-codes.d.ts +47 -4
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-sessions.d.ts +51 -0
- package/dist/workspace/record-sessions.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +2 -0
- package/dist/workspace/record-source.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +71 -4
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-since.d.ts +90 -0
- package/dist/workspace/records-since.d.ts.map +1 -0
- package/dist/workspace/records-write.d.ts +171 -0
- package/dist/workspace/records-write.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +244 -15
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runtimes.d.ts +60 -0
- package/dist/workspace/runtimes.d.ts.map +1 -0
- package/dist/workspace/status-gates.d.ts +90 -0
- package/dist/workspace/status-gates.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +17 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/trust/seal.d.ts +85 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +7 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/work.d.ts +56 -0
- package/dist/workspace/work.d.ts.map +1 -0
- package/package.json +19 -1
- package/src/cli/main.ts +55 -3
- package/src/cli/registry.ts +13 -1
- package/src/lifecycle/gate-ledger.ts +14 -0
- package/src/workspace/__fixtures__/sessions.ts +66 -0
- package/src/workspace/checks/records.ts +19 -0
- package/src/workspace/checks.test.ts +2 -0
- package/src/workspace/checks.ts +7 -1
- package/src/workspace/composites.schema.json +65 -3
- package/src/workspace/composites.test.ts +95 -5
- package/src/workspace/composites.ts +28 -7
- package/src/workspace/conformance/__fixture__/app/package.json +7 -0
- package/src/workspace/conformance/__fixture__/app/src/server.mjs +29 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +32 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +376 -0
- package/src/workspace/conformance/__fixture__/decisions/fix-001-how-the-app-is-deployed.md +40 -0
- package/src/workspace/conformance/__fixture__/delivery/chant.config.ts +7 -0
- package/src/workspace/conformance/__fixture__/delivery/lexicon/index.ts +26 -0
- package/src/workspace/conformance/__fixture__/delivery/package.json +7 -0
- package/src/workspace/conformance/__fixture__/delivery/src/app.component.ts +14 -0
- package/src/workspace/conformance/__fixture__/delivery/src/app.ts +4 -0
- package/src/workspace/conformance/conformance.test.ts +149 -0
- package/src/workspace/conformance/index.mjs +31 -0
- package/src/workspace/conformance/index.ts +453 -0
- package/src/workspace/conformance/vitest.ts +62 -0
- package/src/workspace/declaration.schema.json +40 -0
- package/src/workspace/declaration.ts +62 -0
- package/src/workspace/declared-kinds.test.ts +321 -0
- package/src/workspace/declared-kinds.ts +76 -0
- package/src/workspace/graph-cli.ts +8 -0
- package/src/workspace/intent-cli.ts +29 -6
- package/src/workspace/intent-gaps.test.ts +217 -0
- package/src/workspace/intent-joins.test.ts +60 -0
- package/src/workspace/intent-joins.ts +71 -19
- package/src/workspace/intent.schema.json +304 -7
- package/src/workspace/intent.test.ts +99 -0
- package/src/workspace/intent.ts +365 -46
- package/src/workspace/ls.schema.json +34 -0
- package/src/workspace/ls.ts +69 -4
- package/src/workspace/read-contract.test.ts +30 -9
- package/src/workspace/reason-codes.test.ts +16 -4
- package/src/workspace/reason-codes.ts +55 -4
- package/src/workspace/record-assets.test.ts +3 -1
- package/src/workspace/record-sessions.ts +105 -0
- package/src/workspace/record-source.ts +14 -5
- package/src/workspace/records-amend.schema.json +167 -0
- package/src/workspace/records-cli.ts +308 -19
- package/src/workspace/records-contract.test.ts +57 -2
- package/src/workspace/records-formats.test.ts +640 -0
- package/src/workspace/records-new.schema.json +158 -0
- package/src/workspace/records-quorum.test.ts +196 -0
- package/src/workspace/records-review.schema.json +227 -0
- package/src/workspace/records-sessions.test.ts +108 -0
- package/src/workspace/records-since.schema.json +193 -0
- package/src/workspace/records-since.test.ts +174 -0
- package/src/workspace/records-since.ts +259 -0
- package/src/workspace/records-write-contract.test.ts +125 -0
- package/src/workspace/records-write.test.ts +373 -0
- package/src/workspace/records-write.ts +765 -0
- package/src/workspace/records.schema.json +202 -9
- package/src/workspace/records.ts +700 -41
- package/src/workspace/runtimes.ts +107 -0
- package/src/workspace/status-contract.test.ts +163 -0
- package/src/workspace/status-gates.ts +215 -0
- package/src/workspace/status.schema.json +69 -3
- package/src/workspace/status.ts +35 -2
- package/src/workspace/trust/seal.test.ts +232 -0
- package/src/workspace/trust/seal.ts +195 -0
- package/src/workspace/trust/ssh-commit.ts +2 -2
- package/src/workspace/work.test.ts +390 -0
- package/src/workspace/work.ts +163 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sealed verdicts (#2687, part of #2547): a review entry signed by the
|
|
3
|
+
* principal it names, so the quorum counts it only when that principal
|
|
4
|
+
* signed it.
|
|
5
|
+
*
|
|
6
|
+
* A seal is a detached ssh signature, the same mechanism as the ssh-commit
|
|
7
|
+
* attestor (./ssh-commit.ts): `ssh-keygen -Y sign` to make it, and
|
|
8
|
+
* `ssh-keygen -Y verify` against the signers read at base to check it. It is
|
|
9
|
+
* made in its own namespace, `chant-review`, so neither a commit signature
|
|
10
|
+
* nor a signer-set signature can stand in for one.
|
|
11
|
+
*
|
|
12
|
+
* The signed bytes are the record id, the verdict's digest, the verdict, the
|
|
13
|
+
* reviewer and the date, each on its own line with no final newline. The
|
|
14
|
+
* digest is the record's text digest (`recordTextDigest`), so the seal binds
|
|
15
|
+
* the verdict to the text it judged. The seal lives inside the reviews block,
|
|
16
|
+
* which the digest leaves out, so sealing a verdict never moves the digest.
|
|
17
|
+
*/
|
|
18
|
+
import type { SealCode } from "../records.js";
|
|
19
|
+
import { type TrustPolicy } from "./policy.js";
|
|
20
|
+
/** The ssh signature namespace of a verdict seal. */
|
|
21
|
+
export declare const REVIEW_SEAL_NAMESPACE = "chant-review";
|
|
22
|
+
/** A seal as a review entry holds it. */
|
|
23
|
+
export interface VerdictSeal {
|
|
24
|
+
/** The principal who signed: the reviewer, as the signers file names them. */
|
|
25
|
+
signer: string;
|
|
26
|
+
/** The signing key's fingerprint, such as `SHA256:...`. Reported, never trusted: the signature is the proof. */
|
|
27
|
+
key: string;
|
|
28
|
+
/** The armored ssh signature. */
|
|
29
|
+
signature: string;
|
|
30
|
+
}
|
|
31
|
+
/** What a verdict's seal establishes. */
|
|
32
|
+
export interface SealCheck {
|
|
33
|
+
/** true: the seal verifies for the reviewer against the signers at base. false: it is missing under an active policy, or it fails. null: nothing here can say. */
|
|
34
|
+
attested: boolean | null;
|
|
35
|
+
/** Why it is not attested. Absent when `attested` is true. */
|
|
36
|
+
code?: SealCode;
|
|
37
|
+
message: string;
|
|
38
|
+
/** The fingerprint of the key that made the signature, when the signature was checked. */
|
|
39
|
+
key?: string;
|
|
40
|
+
}
|
|
41
|
+
/** The verdict a seal covers. */
|
|
42
|
+
export interface SealedVerdict {
|
|
43
|
+
record: string | null;
|
|
44
|
+
reviewer: string;
|
|
45
|
+
verdict: string;
|
|
46
|
+
on: unknown;
|
|
47
|
+
digest: string | null;
|
|
48
|
+
seal: unknown;
|
|
49
|
+
}
|
|
50
|
+
/** The bytes a verdict seal signs. */
|
|
51
|
+
export declare function reviewSealPayload(record: string, digest: string, verdict: string, reviewer: string, on: string): Buffer;
|
|
52
|
+
/**
|
|
53
|
+
* Check a verdict's seal. With a signers file active at base, it verifies
|
|
54
|
+
* against the keys listed there for the reviewer. With none, a seal present
|
|
55
|
+
* is checked for integrity only (`ssh-keygen -Y check-novalidate`): nothing
|
|
56
|
+
* says whose key it is.
|
|
57
|
+
*/
|
|
58
|
+
export declare function checkVerdictSeal(policy: TrustPolicy, v: SealedVerdict): SealCheck;
|
|
59
|
+
/** Why a seal could not be made. */
|
|
60
|
+
export declare class SealError extends Error {
|
|
61
|
+
constructor(message: string);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The key `--sign` names: the file given, or with none, git's
|
|
65
|
+
* `user.signingkey` when `gpg.format` is `ssh`, as `git commit -S` reads it.
|
|
66
|
+
* A literal public key (`key::ssh-...`, or `ssh-...`) signs through the ssh
|
|
67
|
+
* agent holding its private half. Returns the file to pass to ssh-keygen,
|
|
68
|
+
* and a cleanup for a temporary one.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveSigningKey(sign: string | true, cwd: string): {
|
|
71
|
+
file: string;
|
|
72
|
+
cleanup: () => void;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Seal a verdict with the key in `keyFile` (a private key, or a public key
|
|
76
|
+
* whose private half the ssh agent holds). Throws a {@link SealError}.
|
|
77
|
+
*/
|
|
78
|
+
export declare function sealVerdict(keyFile: string, v: {
|
|
79
|
+
record: string;
|
|
80
|
+
digest: string;
|
|
81
|
+
verdict: string;
|
|
82
|
+
reviewer: string;
|
|
83
|
+
on: string;
|
|
84
|
+
}): VerdictSeal;
|
|
85
|
+
//# sourceMappingURL=seal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AAEpD,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,gHAAgH;IAChH,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yCAAyC;AACzC,MAAM,WAAW,SAAS;IACxB,kKAAkK;IAClK,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,sCAAsC;AACtC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEvH;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,aAAa,GAAG,SAAS,CA2BjF;AAyBD,oCAAoC;AACpC,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAuBzG;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAmB9I"}
|
|
@@ -25,6 +25,13 @@ export interface SignedCommit {
|
|
|
25
25
|
* continuation lines, to get the bytes it signed.
|
|
26
26
|
*/
|
|
27
27
|
export declare function splitSignedCommit(raw: Buffer, header?: string): SignedCommit;
|
|
28
|
+
/** Run ssh-keygen; `missing` is true when it is not installed. Shared with the verdict seals (./seal.ts). */
|
|
29
|
+
export declare function sshKeygen(args: string[], input?: Buffer): {
|
|
30
|
+
status: number | null;
|
|
31
|
+
stdout: string;
|
|
32
|
+
stderr: string;
|
|
33
|
+
missing: boolean;
|
|
34
|
+
};
|
|
28
35
|
/**
|
|
29
36
|
* Check a detached ssh signature over `payload` in `namespace` against
|
|
30
37
|
* `signers`. Shared with the rotation check (#2553), which signs signer sets
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssh-commit.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/ssh-commit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAsC,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAI7D,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,YAAY,CAsB9E;
|
|
1
|
+
{"version":3,"file":"ssh-commit.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/ssh-commit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAsC,cAAc,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAI7D,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,YAAY,CAsB9E;AAUD,6GAA6G;AAC7G,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAIrI;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAuBjG;AAED,eAAO,MAAM,iBAAiB,EAAE,cAW/B,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Work items on read (#2683): a record kind with a `work` block.
|
|
3
|
+
*
|
|
4
|
+
* A work item is a record in the workspace, a git-tracked file with its
|
|
5
|
+
* dependencies written inside it, that people and agents share as one queue
|
|
6
|
+
* with no server. Most work items come from a gap the intent graph already
|
|
7
|
+
* reports (`source.finding`), given an id, an owner and a lifecycle.
|
|
8
|
+
*
|
|
9
|
+
* `records.ts` calls {@link applyWork} after it has read a work kind's
|
|
10
|
+
* records, before it sets `valid` and applies `--current`. This module reads
|
|
11
|
+
* the decision kind the work kind names, at the same revision, and gives each
|
|
12
|
+
* work record:
|
|
13
|
+
*
|
|
14
|
+
* - `ready`: its state is the kind's open state, every need is done, and the
|
|
15
|
+
* record is valid and not superseded;
|
|
16
|
+
* - `blockedBy`: each need that is not done, with its state;
|
|
17
|
+
* - `implements`: each decision it names, with that decision's state;
|
|
18
|
+
*
|
|
19
|
+
* and each decision `implementedBy`, the work records naming it. The
|
|
20
|
+
* warnings are closed codes. `work-done-gap-open` is not raised here: it
|
|
21
|
+
* takes a walk of the item's region, which `graph --intent` makes and
|
|
22
|
+
* `records` asks it for (#2686). It never writes a record.
|
|
23
|
+
*/
|
|
24
|
+
import { type LoadedRecordKind, type ReadRecordsOptions, type RecordEntry } from "./records.js";
|
|
25
|
+
/** Why a work record carries a warning. Closed, like the record warning codes. */
|
|
26
|
+
export declare const WORK_WARNING_CODES: readonly ["work-needs-unknown", "work-implements-unknown", "work-needs-cycle", "work-implements-undecided", "work-done-unpinned", "work-closed-without-date", "work-done-gap-open"];
|
|
27
|
+
export type WorkWarningCode = (typeof WORK_WARNING_CODES)[number];
|
|
28
|
+
/** A link from a work record to another record, with that record's state now. */
|
|
29
|
+
export interface WorkLink {
|
|
30
|
+
id: string;
|
|
31
|
+
/** The linked record's state, or null when no record has the id. */
|
|
32
|
+
state: string | null;
|
|
33
|
+
}
|
|
34
|
+
/** A decision as a work read lists it: its state and the work records implementing it. */
|
|
35
|
+
export interface DecisionWork {
|
|
36
|
+
id: string;
|
|
37
|
+
path: string;
|
|
38
|
+
state: string | null;
|
|
39
|
+
supersededBy: string | null;
|
|
40
|
+
implementedBy: WorkLink[];
|
|
41
|
+
}
|
|
42
|
+
/** The strings of a front-matter list, or none. */
|
|
43
|
+
export declare function idList(data: Record<string, unknown> | null, field: string): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Whether `state` counts as decided in the decision kind: ranked above 0 by
|
|
46
|
+
* its approval ranks, or, for a kind without them, closed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isDecided(kind: LoadedRecordKind["kind"], state: string | null): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Give each work record its links, ready and blockedBy, and its warnings, in
|
|
51
|
+
* place. Throws a `RecordReadError` when the decision kind can't be read.
|
|
52
|
+
*/
|
|
53
|
+
export declare function applyWork(loaded: LoadedRecordKind, entries: RecordEntry[], options: ReadRecordsOptions): Promise<{
|
|
54
|
+
decisions: DecisionWork[];
|
|
55
|
+
}>;
|
|
56
|
+
//# sourceMappingURL=work.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/workspace/work.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAA+B,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE1H,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,qLAeW,CAAC;AAC3C,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,0FAA0F;AAC1F,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,QAAQ,EAAE,CAAC;CAC3B;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAGpF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAGvF;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CAgFrJ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.87.0",
|
|
4
4
|
"description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -50,6 +50,16 @@
|
|
|
50
50
|
"default": "./src/cli/index.ts"
|
|
51
51
|
},
|
|
52
52
|
"./workspace/*.schema.json": "./src/workspace/*.schema.json",
|
|
53
|
+
"./workspace/conformance": {
|
|
54
|
+
"development": "./src/workspace/conformance/index.ts",
|
|
55
|
+
"types": "./dist/workspace/conformance/index.d.ts",
|
|
56
|
+
"default": "./src/workspace/conformance/index.mjs"
|
|
57
|
+
},
|
|
58
|
+
"./workspace/conformance/vitest": {
|
|
59
|
+
"development": "./src/workspace/conformance/vitest.ts",
|
|
60
|
+
"types": "./dist/workspace/conformance/vitest.d.ts",
|
|
61
|
+
"default": "./src/workspace/conformance/vitest.ts"
|
|
62
|
+
},
|
|
53
63
|
"./cli/*": {
|
|
54
64
|
"development": "./src/cli/*.ts",
|
|
55
65
|
"types": "./dist/cli/*.d.ts",
|
|
@@ -104,6 +114,14 @@
|
|
|
104
114
|
"devDependencies": {
|
|
105
115
|
"@cdktn/hcl2json": "^0.24.0"
|
|
106
116
|
},
|
|
117
|
+
"peerDependencies": {
|
|
118
|
+
"vitest": ">=2"
|
|
119
|
+
},
|
|
120
|
+
"peerDependenciesMeta": {
|
|
121
|
+
"vitest": {
|
|
122
|
+
"optional": true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
107
125
|
"overrides": {
|
|
108
126
|
"esbuild": "^0.28.1"
|
|
109
127
|
}
|
package/src/cli/main.ts
CHANGED
|
@@ -375,6 +375,31 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
375
375
|
if (!result.intent || result.intent.startsWith("-")) throw new Error("--intent needs a region: --intent <path[:start-end]>");
|
|
376
376
|
} else if (arg === "--current") {
|
|
377
377
|
result.current = true;
|
|
378
|
+
} else if (arg === "--set") {
|
|
379
|
+
// `chant workspace records amend <id> --set <file|->` (#2670)
|
|
380
|
+
result.set = args[++i];
|
|
381
|
+
if (!result.set || (result.set.startsWith("-") && result.set !== "-")) throw new Error("--set needs a JSON file, or - for standard input: --set <file|->");
|
|
382
|
+
} else if (arg === "--verdict") {
|
|
383
|
+
// `chant workspace records review <id> --verdict agree|dissent|abstain` (#2670)
|
|
384
|
+
result.verdict = args[++i];
|
|
385
|
+
if (!result.verdict || result.verdict.startsWith("-")) throw new Error("--verdict needs agree, dissent or abstain");
|
|
386
|
+
} else if (arg === "--by") {
|
|
387
|
+
// `chant workspace records review <id> --by <principal>` (#2670): whoever the caller says.
|
|
388
|
+
result.by = args[++i];
|
|
389
|
+
if (!result.by || result.by.startsWith("-")) throw new Error("--by needs the reviewer: --by <principal>");
|
|
390
|
+
} else if (arg === "--sign") {
|
|
391
|
+
// `chant workspace records review <id> --sign [<key file>]` (#2687): seal the verdict.
|
|
392
|
+
// With no key file, git's user.signingkey, as `git commit -S` reads it.
|
|
393
|
+
const next = args[i + 1];
|
|
394
|
+
result.sign = next !== undefined && !next.startsWith("-") ? args[++i] : true;
|
|
395
|
+
} else if (arg === "--session") {
|
|
396
|
+
// `chant workspace records review <id> --session <id>` (#2670)
|
|
397
|
+
result.session = args[++i];
|
|
398
|
+
if (!result.session || result.session.startsWith("-")) throw new Error("--session needs a session id: --session <id>");
|
|
399
|
+
} else if (arg === "--prefix") {
|
|
400
|
+
// `chant workspace records new <kind> --prefix <prefix>` (#2670): the id prefix to allocate under.
|
|
401
|
+
result.prefix = args[++i];
|
|
402
|
+
if (!result.prefix || result.prefix.startsWith("-")) throw new Error("--prefix needs an id prefix: --prefix <prefix>");
|
|
378
403
|
} else if (arg === "--require") {
|
|
379
404
|
// `chant workspace records|verify --require attested` (#2547)
|
|
380
405
|
result.require = args[++i];
|
|
@@ -721,8 +746,9 @@ Workspace (level 1, #2524):
|
|
|
721
746
|
beside it and marks the members whose digests differ.
|
|
722
747
|
Read only; never fetches. --json prints the
|
|
723
748
|
read-contract document
|
|
724
|
-
workspace records --kind <kind file> [--current] [--at <rev>] [--base <rev>] [--require attested] [--json]
|
|
725
|
-
|
|
749
|
+
workspace records [--kind <kind file>] [--current] [--at <rev>] [--base <rev>] [--require attested] [--json]
|
|
750
|
+
Without --kind, every record kind the declaration
|
|
751
|
+
names. Read the records a record kind locates, validated
|
|
726
752
|
against its schema, with reason codes for invalid
|
|
727
753
|
ones. --current leaves out superseded records; --at
|
|
728
754
|
reads a commit's git objects. Needs no workspace file.
|
|
@@ -731,9 +757,34 @@ Workspace (level 1, #2524):
|
|
|
731
757
|
--require attested exits 2 if any record is not
|
|
732
758
|
attested. A pinned file that changed is a warning,
|
|
733
759
|
asset-drift or asset-missing
|
|
760
|
+
workspace records [--kind <kind file>] --since <rev> [--at <rev>] [--json]
|
|
761
|
+
What changed in the records between <rev> and --at
|
|
762
|
+
(default: the working tree): new and removed records,
|
|
763
|
+
state transitions, new verdicts, new supersessions
|
|
764
|
+
and changed pins
|
|
734
765
|
workspace records pin <path>
|
|
735
766
|
Print the path from the workspace root and the
|
|
736
767
|
sha256 of a file, for a decision's evidence pin
|
|
768
|
+
workspace records new [<kind file>] --from <file|-> [--prefix <prefix>] [--dry-run]
|
|
769
|
+
Write one new record in the kind's directory from
|
|
770
|
+
the JSON fields given, after validating them as
|
|
771
|
+
records would read them. Without a kind file, the one
|
|
772
|
+
kind the declaration names. Allocates the next id when
|
|
773
|
+
the fields hold none. Prints {path, id} as JSON and
|
|
774
|
+
never commits
|
|
775
|
+
workspace records amend <id> [--kind <kind file>] --set <file|-> [--dry-run]
|
|
776
|
+
Set top-level fields of one record. A closed record
|
|
777
|
+
never changes, and an approved one changes only its
|
|
778
|
+
state (upward), pins and reviews; anything else is
|
|
779
|
+
refused with amend-supersede-instead. Prints
|
|
780
|
+
{path, id, changed}
|
|
781
|
+
workspace records review <id> [--kind <kind file>] --verdict agree|dissent|abstain --by <principal> [--note <text>] [--session <id>] [--sign [<key file>]] [--dry-run]
|
|
782
|
+
Append a review to one record, dated and bound to
|
|
783
|
+
the digest of the record text. A dissent needs
|
|
784
|
+
--note. --sign seals it with an ssh key (git's
|
|
785
|
+
user.signingkey without a file); under a signers
|
|
786
|
+
file at base only a sealed verdict counts. Prints
|
|
787
|
+
{path, id, review}
|
|
737
788
|
workspace verify [--base <rev>] [--head <rev>] [--require attested]
|
|
738
789
|
Check the commits in base..head against the signers
|
|
739
790
|
and roles read from base. A change to the signers file
|
|
@@ -784,7 +835,8 @@ Workspace (level 1, #2524):
|
|
|
784
835
|
workspace graph --intent <path[:start-end]> [--at <rev>] [--kind <kind file>...] [--json]
|
|
785
836
|
The intent graph over one region: the commits that
|
|
786
837
|
touched it, the decisions whose constrains cover it,
|
|
787
|
-
the artifacts they pin, and findings with closed codes
|
|
838
|
+
the artifacts they pin, and findings with closed codes.
|
|
839
|
+
Without --kind, every record kind the declaration names
|
|
788
840
|
|
|
789
841
|
Lifecycle (alias: lc):
|
|
790
842
|
lifecycle snapshot <env> Query API, save metadata to orphan branch
|
package/src/cli/registry.ts
CHANGED
|
@@ -291,6 +291,18 @@ export interface ParsedArgs {
|
|
|
291
291
|
intent?: string;
|
|
292
292
|
/** `chant workspace records --current` (#2546): leave out records a closed record supersedes. */
|
|
293
293
|
current?: boolean;
|
|
294
|
+
/** `chant workspace records amend <id> --set <file|->` (#2670): the JSON fields to set. */
|
|
295
|
+
set?: string;
|
|
296
|
+
/** `chant workspace records review <id> --verdict <v>` (#2670): agree, dissent or abstain. */
|
|
297
|
+
verdict?: string;
|
|
298
|
+
/** `chant workspace records review <id> --by <principal>` (#2670): the reviewer, as the caller names them. */
|
|
299
|
+
by?: string;
|
|
300
|
+
/** `chant workspace records review <id> --sign [<key file>]` (#2687): the key that seals the verdict, or true for git's user.signingkey. */
|
|
301
|
+
sign?: string | true;
|
|
302
|
+
/** `chant workspace records review <id> --session <id>` (#2670): the review session the verdict was given in. */
|
|
303
|
+
session?: string;
|
|
304
|
+
/** `chant workspace records new <kind> --prefix <prefix>` (#2670): the id prefix to allocate under. */
|
|
305
|
+
prefix?: string;
|
|
294
306
|
/** `chant workspace records|verify --require attested` (#2547): the provenance level a gate requires. */
|
|
295
307
|
require?: string;
|
|
296
308
|
/**
|
|
@@ -416,7 +428,7 @@ export interface ParsedArgs {
|
|
|
416
428
|
plan?: string;
|
|
417
429
|
/** `chant operator log --op <name>` (#2029) — restrict the tick history to one ConvergeOp by name. Omitted, every discovered ConvergeOp's ticks are merged into one timeline. */
|
|
418
430
|
op?: string;
|
|
419
|
-
/** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. */
|
|
431
|
+
/** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. `chant workspace records --since <rev>` (#2673) — the revision to compare the records with. */
|
|
420
432
|
since?: string;
|
|
421
433
|
/** `chant operator log --limit <n>` (#2029) — keep only the newest n entries (still printed oldest-first). */
|
|
422
434
|
limit?: number;
|
|
@@ -47,6 +47,9 @@ import type { GateApprover, GatePolicyDecision, ResolvedGateApproval } from "../
|
|
|
47
47
|
import { readBlobFromPath, readPathSha, readBlobBySha, writeBlobToPath, RefCASConflictError } from "./git";
|
|
48
48
|
|
|
49
49
|
const DIR = "_gates";
|
|
50
|
+
|
|
51
|
+
/** The directory on the `chant/lifecycle` branch that holds gate ledgers, under a member's prefix when it has one. */
|
|
52
|
+
export const GATES_DIR = DIR;
|
|
50
53
|
const APPEND_RETRY_ATTEMPTS = 5;
|
|
51
54
|
|
|
52
55
|
/**
|
|
@@ -350,7 +353,18 @@ export async function readGateLedger(
|
|
|
350
353
|
): Promise<{ resolutions: GateResolutionRecord[]; pending: PendingGateRecord[]; malformed: number }> {
|
|
351
354
|
const content = await readBlobFromPath(DIR, filename(op), opts);
|
|
352
355
|
if (!content) return { resolutions: [], pending: [], malformed: 0 };
|
|
356
|
+
return parseGateLedger(content);
|
|
357
|
+
}
|
|
353
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Split the text of one gate ledger file into its two kinds of line, oldest
|
|
361
|
+
* first, the way {@link readGateLedger} reads it. Exported for a reader that
|
|
362
|
+
* fetches the file itself, such as `chant workspace status` (#2674), which
|
|
363
|
+
* reads every member's ledger by its full path from the workspace root.
|
|
364
|
+
*/
|
|
365
|
+
export function parseGateLedger(
|
|
366
|
+
content: string,
|
|
367
|
+
): { resolutions: GateResolutionRecord[]; pending: PendingGateRecord[]; malformed: number } {
|
|
354
368
|
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
355
369
|
const resolutions: GateResolutionRecord[] = [];
|
|
356
370
|
const pending: PendingGateRecord[] = [];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A repository with the reference workspace's decisions and design member,
|
|
3
|
+
* and helpers that write sessions and review entries into it, for the
|
|
4
|
+
* session and records --since tests (#2673).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { cpSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { sessionSeal } from "../record-sessions";
|
|
10
|
+
import { commitAll, git, REPO, scratchDir } from "./contract-repo";
|
|
11
|
+
|
|
12
|
+
export const SESSIONS_KIND = "design/sessions/session.kind.mjs";
|
|
13
|
+
export const DECISIONS_KIND = "decisions/decision.kind.mjs";
|
|
14
|
+
|
|
15
|
+
/** A git repository holding a copy of the reference workspace's decisions and design member. */
|
|
16
|
+
export function sessionsRepo(): string {
|
|
17
|
+
const root = scratchDir("chant-sessions-");
|
|
18
|
+
for (const d of ["decisions", "design"]) cpSync(join(REPO, "reference-workspace", d), join(root, d), { recursive: true });
|
|
19
|
+
git(root, "init", "-q");
|
|
20
|
+
commitAll(root, "reference");
|
|
21
|
+
return root;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A session file's text, sealed when it is closed. */
|
|
25
|
+
export function sessionText(s: { id: string; state: "open" | "closed"; verdicts?: { record: string; principal: string; verdict: string }[]; agenda?: string[] }): string {
|
|
26
|
+
const lines = [
|
|
27
|
+
"---",
|
|
28
|
+
"schema: 1",
|
|
29
|
+
`id: "${s.id}"`,
|
|
30
|
+
`title: "Session ${s.id}"`,
|
|
31
|
+
`state: "${s.state}"`,
|
|
32
|
+
"agenda:",
|
|
33
|
+
...(s.agenda ?? ["ref-001"]).map((r) => ` - record: "${r}"`),
|
|
34
|
+
"attendance:",
|
|
35
|
+
' - principal: "lex00"',
|
|
36
|
+
' class: "person"',
|
|
37
|
+
' - principal: "alice"',
|
|
38
|
+
' class: "person"',
|
|
39
|
+
' - principal: "helper"',
|
|
40
|
+
' class: "agent"',
|
|
41
|
+
'opened: "2026-09-24T18:00:00Z"',
|
|
42
|
+
`closed: ${s.state === "closed" ? '"2026-09-24T19:00:00Z"' : "null"}`,
|
|
43
|
+
...(s.verdicts?.length
|
|
44
|
+
? ["verdicts:", ...s.verdicts.flatMap((v) => [` - record: "${v.record}"`, ` principal: "${v.principal}"`, ` verdict: "${v.verdict}"`])]
|
|
45
|
+
: ["verdicts: []"]),
|
|
46
|
+
...(s.state === "closed" ? ['closed_digest: "SEAL"'] : []),
|
|
47
|
+
"---",
|
|
48
|
+
"",
|
|
49
|
+
`# Session ${s.id}`,
|
|
50
|
+
"",
|
|
51
|
+
];
|
|
52
|
+
const text = lines.join("\n");
|
|
53
|
+
return s.state === "closed" ? text.replace('"SEAL"', `"${sessionSeal(text, "closed_digest")}"`) : text;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Rewrite ref-001 with the given review entries, each naming `session`. */
|
|
57
|
+
export function reviewed(root: string, reviewers: string[], session: string, state = "decided"): void {
|
|
58
|
+
const file = join(root, "decisions", "ref-001-how-the-app-is-deployed.md");
|
|
59
|
+
const reviews = reviewers.map((r) => ` - reviewer: "${r}"\n verdict: "agree"\n on: "2026-09-24"\n session: "${session}"`).join("\n");
|
|
60
|
+
writeFileSync(
|
|
61
|
+
file,
|
|
62
|
+
readFileSync(file, "utf-8")
|
|
63
|
+
.replace(/^state: .*$/m, `state: "${state}"`)
|
|
64
|
+
.replace(/^reviews: .*$/m, reviewers.length ? `reviews:\n${reviews}` : "reviews: []"),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* | WSP112 | a pinned file that does not exist |
|
|
14
14
|
* | WSP113 | a pinned file that a superseding record pins at the old hash: the artifact did not follow the decision |
|
|
15
15
|
* | WSP114 | the records of `--kind` can't be read (fixed) |
|
|
16
|
+
* | WSP115 | a record kind the declaration names is missing or does not load as one (fixed, #2680) |
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import type { WorkspaceCheck, WorkspaceDiagnostic } from "../checks";
|
|
@@ -80,4 +81,22 @@ export const RECORD_CHECKS: readonly WorkspaceCheck[] = [
|
|
|
80
81
|
return [{ checkId: this.id, severity: this.severity, message: `--kind ${facts.kind}: ${facts.error.code}: ${facts.error.message}`, pointer: "" }];
|
|
81
82
|
},
|
|
82
83
|
},
|
|
84
|
+
{
|
|
85
|
+
id: "WSP115",
|
|
86
|
+
name: "record-kind-unloadable",
|
|
87
|
+
description: "Every record kind the declaration names is a file that exports a valid recordKind, with the schema it names.",
|
|
88
|
+
severity: "error",
|
|
89
|
+
configurable: false,
|
|
90
|
+
check(ctx) {
|
|
91
|
+
return (ctx.facts?.declaredKinds ?? [])
|
|
92
|
+
.filter((k) => k.reason !== null)
|
|
93
|
+
.map((k) => ({
|
|
94
|
+
checkId: this.id,
|
|
95
|
+
severity: this.severity,
|
|
96
|
+
message: `${k.declared.member === null ? "the workspace" : `member ${k.declared.member}`} declares the record kind ${k.declared.kind}, which can't be loaded: ${k.reason!.code}: ${k.reason!.message}`,
|
|
97
|
+
...(k.declared.member !== null ? { entity: k.declared.member } : {}),
|
|
98
|
+
pointer: `${k.declared.pointer}/kind`,
|
|
99
|
+
}));
|
|
100
|
+
},
|
|
101
|
+
},
|
|
83
102
|
];
|
|
@@ -58,6 +58,8 @@ describe("the WSP catalog", () => {
|
|
|
58
58
|
"link-kind-unknown",
|
|
59
59
|
// --kind names a record kind, and records that can't be read check nothing (#2549).
|
|
60
60
|
"records-unreadable",
|
|
61
|
+
// A declared record kind that doesn't load breaks every reader's default (#2680).
|
|
62
|
+
"record-kind-unloadable",
|
|
61
63
|
]);
|
|
62
64
|
});
|
|
63
65
|
|
package/src/workspace/checks.ts
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* | WSP091 to WSP097 | member links (#2539) |
|
|
28
28
|
* | WSP101 to WSP106 | generated files |
|
|
29
29
|
* | WSP111 to WSP114 | records read with `--kind` (#2549) |
|
|
30
|
+
* | WSP115 | the record kinds the declaration names (#2680) |
|
|
30
31
|
*/
|
|
31
32
|
|
|
32
33
|
import { realpathSync } from "node:fs";
|
|
@@ -44,6 +45,7 @@ import { gatherGeneratedFacts, GENERATED_CHECKS, type GeneratedFileFacts } from
|
|
|
44
45
|
import { gatherLedgerFacts, LEDGER_CHECKS, type MemberLedgerFacts } from "./checks/ledgers";
|
|
45
46
|
import { gatherPipelineFacts, PIPELINE_CHECKS, type MemberPipelineFacts } from "./checks/pipelines";
|
|
46
47
|
import { RECORD_CHECKS, type RecordFacts } from "./checks/records";
|
|
48
|
+
import { loadDeclaredKinds, type DeclaredKind } from "./declared-kinds";
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* What the checks beyond the declaration read, gathered from the checkout
|
|
@@ -59,6 +61,8 @@ export interface WorkspaceFacts {
|
|
|
59
61
|
generated?: readonly GeneratedFileFacts[];
|
|
60
62
|
/** The records of the kind named with `--kind`, with their pins checked (#2549). */
|
|
61
63
|
records?: RecordFacts;
|
|
64
|
+
/** The record kinds the declaration names, each loaded, or only looked for under `--at` (#2680). */
|
|
65
|
+
declaredKinds?: readonly DeclaredKind[];
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
/** What every declaration check reads. */
|
|
@@ -444,7 +448,9 @@ export async function runDeclarationChecks(
|
|
|
444
448
|
}
|
|
445
449
|
const { registry, problems } = loadKindRegistry(declaration.pins, root);
|
|
446
450
|
const gathered = options.gather === false || options.tree ? {} : await gatherWorkspaceFacts(root, declaration, options);
|
|
447
|
-
|
|
451
|
+
// The declared record kinds load from the working tree; under --at only whether each exists at the revision is checked (#2680).
|
|
452
|
+
const declaredKinds = options.gather === false ? undefined : await loadDeclaredKinds(declaration, tree, root, { load: !options.tree });
|
|
453
|
+
const facts: WorkspaceFacts = { ...gathered, ...(options.records ? { records: options.records } : {}), ...(declaredKinds ? { declaredKinds } : {}) };
|
|
448
454
|
const ctx: WorkspaceCheckContext = { declaration, tree, groups, kinds: registry, kindProblems: problems, facts };
|
|
449
455
|
const findings = runWorkspaceChecks(ctx);
|
|
450
456
|
const { active, suppressed } = applyCheckSettings(declaration, findings);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://intentius.io/chant/schemas/workspace/composites/v1/composites.schema.json",
|
|
4
4
|
"title": "chant workspace graph --composites output",
|
|
5
|
-
"description": "What `chant workspace graph --composites` prints: each composite instance the workspace's members declare, joined to the components whose contract can deploy it (#2662). Each member of kind chant is read through its own `chant graph --format ir` and `chant graph --components --format ir`, under its own toolchain. An instance with no component is listed with an empty `components` array. chant supplies the rows and never picks one: each match says how it was made and how it crosses members. Readers ignore fields they do not know; a field is only ever added within a version. The reason and error codes are closed lists. Contract version 1 is written by chant 0.81.0 and newer, and this document was added to it by #2662; a chant without --composites refuses the flag. Every code is in the one closed list of `reason-codes.ts`.",
|
|
5
|
+
"description": "What `chant workspace graph --composites` prints: each composite instance the workspace's members declare, joined to the components whose contract can deploy it (#2662). Each member of kind chant is read through its own `chant graph --format ir` and `chant graph --components --format ir`, under its own toolchain. An instance with no component is listed with an empty `components` array. chant supplies the rows and never picks one: each match says how it was made and how it crosses members. Readers ignore fields they do not know; a field is only ever added within a version. The reason and error codes are closed lists. Contract version 1 is written by chant 0.81.0 and newer, and this document was added to it by #2662; a chant without --composites refuses the flag. Each component lists the runtimes it can deploy on, read from its member's chant.config.ts (#2674). Every code is in the one closed list of `reason-codes.ts`.",
|
|
6
6
|
"oneOf": [
|
|
7
7
|
{
|
|
8
8
|
"$ref": "#/$defs/result"
|
|
@@ -130,7 +130,8 @@
|
|
|
130
130
|
"kind",
|
|
131
131
|
"status",
|
|
132
132
|
"reason",
|
|
133
|
-
"chant"
|
|
133
|
+
"chant",
|
|
134
|
+
"runtimeReasons"
|
|
134
135
|
],
|
|
135
136
|
"properties": {
|
|
136
137
|
"name": {
|
|
@@ -187,6 +188,28 @@
|
|
|
187
188
|
"null"
|
|
188
189
|
],
|
|
189
190
|
"description": "The chant version the member's toolchain reported, or null when a chant older than member-run read it."
|
|
191
|
+
},
|
|
192
|
+
"runtimeReasons": {
|
|
193
|
+
"description": "Why the runtimes of the member's components leave something out: its chant.config.ts, or a lexicon it lists, could not be read. Empty for a member not of kind chant, and when nothing was left out.",
|
|
194
|
+
"type": "array",
|
|
195
|
+
"items": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"required": [
|
|
198
|
+
"code",
|
|
199
|
+
"message"
|
|
200
|
+
],
|
|
201
|
+
"properties": {
|
|
202
|
+
"code": {
|
|
203
|
+
"enum": [
|
|
204
|
+
"runtimes-config-unreadable",
|
|
205
|
+
"runtimes-lexicon-unreadable"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
"message": {
|
|
209
|
+
"type": "string"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
190
213
|
}
|
|
191
214
|
},
|
|
192
215
|
"if": {
|
|
@@ -327,7 +350,8 @@
|
|
|
327
350
|
"member",
|
|
328
351
|
"archetype",
|
|
329
352
|
"composites",
|
|
330
|
-
"file"
|
|
353
|
+
"file",
|
|
354
|
+
"runtimes"
|
|
331
355
|
],
|
|
332
356
|
"properties": {
|
|
333
357
|
"id": {
|
|
@@ -368,6 +392,14 @@
|
|
|
368
392
|
"null"
|
|
369
393
|
],
|
|
370
394
|
"description": "The component's file from the workspace root, when the member's chant names it."
|
|
395
|
+
},
|
|
396
|
+
"runtimes": {
|
|
397
|
+
"description": "The runtimes the component can deploy on, the list chant run --components --on accepts in its member: local first, then each lexicon the member's chant.config.ts lists whose opRuntime hosts component runs, in config order.",
|
|
398
|
+
"type": "array",
|
|
399
|
+
"minItems": 1,
|
|
400
|
+
"items": {
|
|
401
|
+
"$ref": "#/$defs/runtime"
|
|
402
|
+
}
|
|
371
403
|
}
|
|
372
404
|
}
|
|
373
405
|
},
|
|
@@ -466,6 +498,36 @@
|
|
|
466
498
|
},
|
|
467
499
|
"composites": false
|
|
468
500
|
}
|
|
501
|
+
},
|
|
502
|
+
"runtime": {
|
|
503
|
+
"type": "object",
|
|
504
|
+
"required": [
|
|
505
|
+
"name",
|
|
506
|
+
"lexicon",
|
|
507
|
+
"default",
|
|
508
|
+
"command"
|
|
509
|
+
],
|
|
510
|
+
"properties": {
|
|
511
|
+
"name": {
|
|
512
|
+
"type": "string",
|
|
513
|
+
"description": "What --on takes: local, or the lexicon's name."
|
|
514
|
+
},
|
|
515
|
+
"lexicon": {
|
|
516
|
+
"type": [
|
|
517
|
+
"string",
|
|
518
|
+
"null"
|
|
519
|
+
],
|
|
520
|
+
"description": "The lexicon that hosts the runtime, or null for local."
|
|
521
|
+
},
|
|
522
|
+
"default": {
|
|
523
|
+
"type": "boolean",
|
|
524
|
+
"description": "True for the runtime chant run --components uses without --on. That is local until chant.config.ts can name a default."
|
|
525
|
+
},
|
|
526
|
+
"command": {
|
|
527
|
+
"type": "string",
|
|
528
|
+
"description": "The command that deploys the component on this runtime, run in the member's directory: chant run --components <name>, with --on <runtime> for any runtime but local."
|
|
529
|
+
}
|
|
530
|
+
}
|
|
469
531
|
}
|
|
470
532
|
}
|
|
471
533
|
}
|