@intentius/chant 0.124.24 → 0.124.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +4 -2
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +39 -4
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +21 -7
- package/dist/cli/mcp/workspace-tools.d.ts +3 -3
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.js +27 -4
- package/dist/effect-receipt.d.ts +13 -0
- package/dist/effect-receipt.d.ts.map +1 -1
- package/dist/effect-receipt.js +19 -0
- package/dist/gate-decision.d.ts +1 -1
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +1 -1
- package/dist/lexicon-module.d.ts +7 -1
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +7 -2
- package/dist/lifecycle/gate-audit.d.ts +7 -1
- package/dist/lifecycle/gate-audit.d.ts.map +1 -1
- package/dist/lifecycle/gate-audit.js +40 -3
- package/dist/lifecycle/gate-checkpoint.d.ts +78 -0
- package/dist/lifecycle/gate-checkpoint.d.ts.map +1 -0
- package/dist/lifecycle/gate-checkpoint.js +103 -0
- package/dist/lifecycle/gate-ledger.d.ts +41 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/receipt-plan.d.ts.map +1 -1
- package/dist/lifecycle/receipt-plan.js +4 -2
- package/dist/lifecycle/run-ledger.d.ts +2 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +2 -0
- package/dist/lint/rules/op/index.d.ts +6 -2
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +7 -3
- package/dist/lint/rules/op/ops016-gate-resume.d.ts +14 -0
- package/dist/lint/rules/op/ops016-gate-resume.d.ts.map +1 -0
- package/dist/lint/rules/op/ops016-gate-resume.js +31 -0
- package/dist/lint/rules/op/ops017-gate-alias.d.ts +30 -0
- package/dist/lint/rules/op/ops017-gate-alias.d.ts.map +1 -0
- package/dist/lint/rules/op/ops017-gate-alias.js +67 -0
- package/dist/op/activity-contract.d.ts +14 -0
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-contract.js +16 -0
- package/dist/op/builders.d.ts +7 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +13 -0
- package/dist/op/gate-alias.d.ts +57 -0
- package/dist/op/gate-alias.d.ts.map +1 -0
- package/dist/op/gate-alias.js +127 -0
- package/dist/op/gate-checkpoint.d.ts +123 -0
- package/dist/op/gate-checkpoint.d.ts.map +1 -0
- package/dist/op/gate-checkpoint.js +280 -0
- package/dist/op/gate.d.ts +54 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +142 -1
- package/dist/op/index.d.ts +4 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +2 -1
- package/dist/op/local-executor.d.ts +57 -2
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +200 -11
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/local-output.js +20 -2
- package/dist/op/op-ir.d.ts +4 -0
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/op-ir.js +4 -0
- package/dist/op/receipt-store.d.ts +5 -0
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/receipt-store.js +4 -3
- package/dist/op/runtime.d.ts +15 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/runtimes/local.js +5 -0
- package/dist/op/step-output-ref.d.ts +26 -18
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/step-output-ref.js +86 -63
- package/dist/op/types.d.ts +31 -0
- package/dist/op/types.d.ts.map +1 -1
- package/dist/wave-runtime.mjs +223 -221
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/agent-cli.js +3 -0
- package/dist/workspace/conformance/__writer_fixture__/answers/answer.schema.json +16 -1
- package/dist/workspace/conformance/writer.js +1 -1
- package/dist/workspace/decide.d.ts +51 -9
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/decide.js +125 -14
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +20 -11
- package/dist/workspace/point-answer.schema.json +16 -1
- package/dist/workspace/points-cli.d.ts +5 -3
- package/dist/workspace/points-cli.d.ts.map +1 -1
- package/dist/workspace/points-cli.js +16 -7
- package/dist/workspace/points-write.schema.json +39 -4
- package/dist/workspace/points.schema.json +42 -3
- package/dist/workspace/reason-codes.d.ts +2 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +2 -1
- package/dist/workspace/records-cli.d.ts +12 -2
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-cli.js +27 -7
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records-write.js +2 -3
- package/dist/workspace/records.d.ts +10 -4
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +16 -3
- package/dist/workspace/records.schema.json +2 -1
- package/dist/workspace/trust/gate-seal.d.ts +32 -1
- package/dist/workspace/trust/gate-seal.d.ts.map +1 -1
- package/dist/workspace/trust/gate-seal.js +41 -2
- package/dist/workspace/trust/seal.d.ts +1 -1
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +1 -1
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -1
- package/dist/workspace/trust/verify-cli.js +2 -1
- package/dist/workspace/trust/verify.d.ts +22 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -1
- package/dist/workspace/trust/verify.js +94 -14
- package/dist/workspace/write-scope.d.ts +3 -1
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/workspace/write-scope.js +11 -4
- package/package.json +1 -1
- package/src/cli/commands/build.ts +9 -2
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/compiled-cli.e2e.test.ts +45 -1
- package/src/cli/handlers/approve.ts +46 -4
- package/src/cli/handlers/operator.test.ts +91 -0
- package/src/cli/main.test.ts +10 -0
- package/src/cli/main.ts +21 -7
- package/src/cli/mcp/workspace-tools.e2e.test.ts +1 -1
- package/src/cli/mcp/workspace-tools.ts +27 -4
- package/src/effect-receipt.ts +22 -0
- package/src/gate-decision.ts +2 -0
- package/src/lexicon-module.ts +9 -2
- package/src/lifecycle/gate-audit.ts +41 -4
- package/src/lifecycle/gate-checkpoint.test.ts +128 -0
- package/src/lifecycle/gate-checkpoint.ts +133 -0
- package/src/lifecycle/gate-ledger.ts +61 -0
- package/src/lifecycle/receipt-plan.test.ts +9 -0
- package/src/lifecycle/receipt-plan.ts +4 -1
- package/src/lifecycle/run-ledger.ts +4 -0
- package/src/lint/rules/op/index.ts +9 -3
- package/src/lint/rules/op/ops016-gate-resume.test.ts +53 -0
- package/src/lint/rules/op/ops016-gate-resume.ts +48 -0
- package/src/lint/rules/op/ops017-gate-alias.test.ts +85 -0
- package/src/lint/rules/op/ops017-gate-alias.ts +89 -0
- package/src/op/activity-contract.ts +22 -1
- package/src/op/builders.ts +21 -1
- package/src/op/effect-step.test.ts +95 -1
- package/src/op/gate-alias.e2e.test.ts +78 -0
- package/src/op/gate-alias.test.ts +350 -0
- package/src/op/gate-alias.ts +137 -0
- package/src/op/gate-checkpoint.test.ts +284 -0
- package/src/op/gate-checkpoint.ts +322 -0
- package/src/op/gate.ts +198 -4
- package/src/op/index.ts +4 -1
- package/src/op/local-executor.test.ts +65 -0
- package/src/op/local-executor.ts +248 -12
- package/src/op/local-output.ts +18 -2
- package/src/op/op-ir.test.ts +25 -0
- package/src/op/op-ir.ts +8 -0
- package/src/op/receipt-store.ts +13 -3
- package/src/op/runtime.ts +8 -0
- package/src/op/runtimes/local.ts +5 -0
- package/src/op/step-output-ref.test.ts +68 -24
- package/src/op/step-output-ref.ts +85 -65
- package/src/op/types.ts +32 -0
- package/src/workspace/agent-cli.ts +2 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +16 -1
- package/src/workspace/conformance/writer.ts +1 -1
- package/src/workspace/decide.test.ts +87 -3
- package/src/workspace/decide.ts +142 -17
- package/src/workspace/lineage-upgrade.test.ts +21 -0
- package/src/workspace/lineage-upgrade.ts +19 -11
- package/src/workspace/point-answer.schema.json +16 -1
- package/src/workspace/points-cli.ts +16 -8
- package/src/workspace/points-write.schema.json +39 -4
- package/src/workspace/points.schema.json +42 -3
- package/src/workspace/reason-codes.ts +2 -1
- package/src/workspace/record-assets.test.ts +14 -0
- package/src/workspace/records-cli.ts +28 -7
- package/src/workspace/records-write-commands.e2e.test.ts +32 -0
- package/src/workspace/records-write.ts +2 -3
- package/src/workspace/records.schema.json +2 -1
- package/src/workspace/records.ts +17 -4
- package/src/workspace/trust/gate-seal.test.ts +29 -2
- package/src/workspace/trust/gate-seal.ts +46 -2
- package/src/workspace/trust/seal.ts +1 -1
- package/src/workspace/trust/trust.test.ts +105 -0
- package/src/workspace/trust/verify-cli.ts +2 -1
- package/src/workspace/trust/verify.ts +106 -13
- package/src/workspace/write-scope.test.ts +18 -0
- package/src/workspace/write-scope.ts +16 -5
|
@@ -131,8 +131,9 @@ export const SEAL_REASON_CODES = [
|
|
|
131
131
|
"seal-unverifiable",
|
|
132
132
|
];
|
|
133
133
|
/**
|
|
134
|
-
* A warning
|
|
135
|
-
*
|
|
134
|
+
* A warning `records` raises with the policy at base, never by a write: about
|
|
135
|
+
* a record's own seal (#2688), or a ratified record's quorum (#2878). Closed,
|
|
136
|
+
* like the reason codes.
|
|
136
137
|
*/
|
|
137
138
|
export const SEAL_WARNING_CODES = [
|
|
138
139
|
/**
|
|
@@ -142,6 +143,13 @@ export const SEAL_WARNING_CODES = [
|
|
|
142
143
|
* fails. The record is still read, and still valid.
|
|
143
144
|
*/
|
|
144
145
|
"record-unattested",
|
|
146
|
+
/**
|
|
147
|
+
* The record is in its kind's ratified state (`reviews.ratified`, #2873),
|
|
148
|
+
* and its quorum, computed as `records` computes it, is not met: it was
|
|
149
|
+
* written to that state by hand, past the check of `records new` and
|
|
150
|
+
* `amend`. The record is still read, and still valid (#2878).
|
|
151
|
+
*/
|
|
152
|
+
"record-ratified-below-quorum",
|
|
145
153
|
];
|
|
146
154
|
/**
|
|
147
155
|
* The top-level field that holds a record's author seal (#2688), on a kind
|
|
@@ -547,12 +555,17 @@ export async function readSessionSubjects(loaded, root, source) {
|
|
|
547
555
|
* Import the kind file at `path` through the path loader lexicons use (#2520).
|
|
548
556
|
* The kind is registered under a name no lexicon package can have (npm names
|
|
549
557
|
* hold no `:`), imported and unregistered again, so no lexicon lookup sees it.
|
|
558
|
+
*
|
|
559
|
+
* A kind file is a data-only ES module, so a `.mjs`, `.cjs` or `.js` one loads
|
|
560
|
+
* with plain node and only a TypeScript one registers tsx (#3276). That keeps
|
|
561
|
+
* `workspace ls`, `status` and `records` from paying for tsx's loader thread
|
|
562
|
+
* on every read.
|
|
550
563
|
*/
|
|
551
564
|
export async function importKindModule(path) {
|
|
552
565
|
const name = `record-kind:${path}`;
|
|
553
566
|
registerLexiconDeclarations([{ name, module: path }], dirname(path));
|
|
554
567
|
try {
|
|
555
|
-
const mod = await importLexiconModule(name);
|
|
568
|
+
const mod = await importLexiconModule(name, { typeScript: !/\.[cm]?js$/.test(path) });
|
|
556
569
|
if (!mod)
|
|
557
570
|
throw new Error(`${path} was not registered`);
|
|
558
571
|
return mod;
|
|
@@ -33,8 +33,30 @@ export interface SealedGateApproval {
|
|
|
33
33
|
/** Who relayed the approval for `resolvedBy` (#3402), when someone did. */
|
|
34
34
|
relayedBy?: string;
|
|
35
35
|
timestamp: string;
|
|
36
|
+
/** The checkpoint the approval lets a run resume from (#3947), when it carries one. */
|
|
37
|
+
checkpointDigest?: string;
|
|
38
|
+
/** The line a carried approval came from (#3949): the names and time its seal was made under. */
|
|
39
|
+
carriedFrom?: {
|
|
40
|
+
op: string;
|
|
41
|
+
gate: string;
|
|
42
|
+
timestamp: string;
|
|
43
|
+
checkpointDigest?: string;
|
|
44
|
+
};
|
|
36
45
|
seal?: unknown;
|
|
37
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The approval as it was sealed (#3949). A carried approval is recorded
|
|
49
|
+
* under a gate's new name and the time it was carried, and its seal was
|
|
50
|
+
* made over the line it came from, so its op, gate, time and checkpoint
|
|
51
|
+
* digest are read off `carriedFrom`. Any other approval is returned as it is.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sealedFieldsOf(a: SealedGateApproval): SealedGateApproval;
|
|
54
|
+
/**
|
|
55
|
+
* The version of a seal over `a` (#3947): 2 when the approval carries a
|
|
56
|
+
* checkpoint digest, which the payload signs, and 1 otherwise. `chant approve
|
|
57
|
+
* --sign` records 2 on the seal; a seal records no version for 1.
|
|
58
|
+
*/
|
|
59
|
+
export declare function gateSealVersion(a: Pick<SealedGateApproval, "checkpointDigest" | "carriedFrom">): 1 | 2;
|
|
38
60
|
/**
|
|
39
61
|
* The bytes a gate approval's seal signs (#3163): the op (or component), the
|
|
40
62
|
* gate, the environment, the plan digest, the approver and the time, joined
|
|
@@ -43,8 +65,17 @@ export interface SealedGateApproval {
|
|
|
43
65
|
* `relayed-by <principal>`, so the relay can't be added, changed or removed
|
|
44
66
|
* after sealing; an approval nobody relayed signs the six lines it always did,
|
|
45
67
|
* so seals made before #3402 still verify.
|
|
68
|
+
*
|
|
69
|
+
* Seal version 2 (#3947) adds the checkpoint a resumed run carries its step
|
|
70
|
+
* outputs from: an approval with a `checkpointDigest` signs one more line,
|
|
71
|
+
* `checkpoint <digest>`, last. The values the steps after the gate read are
|
|
72
|
+
* then covered by the seal too, and removing or swapping the digest stops it
|
|
73
|
+
* verifying. An approval with none signs what a version 1 seal signed.
|
|
74
|
+
*
|
|
75
|
+
* A carried approval (#3949) signs what the line it came from signed: see
|
|
76
|
+
* {@link sealedFieldsOf}.
|
|
46
77
|
*/
|
|
47
|
-
export declare function gateSealPayload(
|
|
78
|
+
export declare function gateSealPayload(approval: SealedGateApproval): Buffer;
|
|
48
79
|
/** `SHA256:<base64>`, the fingerprint ssh-keygen prints for a public key blob. */
|
|
49
80
|
export declare function sshKeyFingerprint(blob: Buffer): string;
|
|
50
81
|
/** What {@link verifySshSignatureWith} found. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate-seal.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/gate-seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD,kIAAkI;AAClI,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED
|
|
1
|
+
{"version":3,"file":"gate-seal.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/gate-seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD,kIAAkI;AAClI,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iGAAiG;IACjG,WAAW,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,CAWxE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAGtG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAKpE;AA8BD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAmCD,iDAAiD;AACjD,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA0CtI;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU9D;AAID;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID,kJAAkJ;AAClJ,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAYrF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAmB1F"}
|
|
@@ -23,6 +23,34 @@ import { createHash, createPublicKey, verify } from "node:crypto";
|
|
|
23
23
|
* verdict, record or commit signature can't stand in for one.
|
|
24
24
|
*/
|
|
25
25
|
export const GATE_SEAL_NAMESPACE = "chant-gate";
|
|
26
|
+
/**
|
|
27
|
+
* The approval as it was sealed (#3949). A carried approval is recorded
|
|
28
|
+
* under a gate's new name and the time it was carried, and its seal was
|
|
29
|
+
* made over the line it came from, so its op, gate, time and checkpoint
|
|
30
|
+
* digest are read off `carriedFrom`. Any other approval is returned as it is.
|
|
31
|
+
*/
|
|
32
|
+
export function sealedFieldsOf(a) {
|
|
33
|
+
const from = a.carriedFrom;
|
|
34
|
+
if (from === undefined)
|
|
35
|
+
return a;
|
|
36
|
+
const { carriedFrom: _from, checkpointDigest: _checkpoint, ...rest } = a;
|
|
37
|
+
return {
|
|
38
|
+
...rest,
|
|
39
|
+
op: from.op,
|
|
40
|
+
gate: from.gate,
|
|
41
|
+
timestamp: from.timestamp,
|
|
42
|
+
...(from.checkpointDigest !== undefined ? { checkpointDigest: from.checkpointDigest } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The version of a seal over `a` (#3947): 2 when the approval carries a
|
|
47
|
+
* checkpoint digest, which the payload signs, and 1 otherwise. `chant approve
|
|
48
|
+
* --sign` records 2 on the seal; a seal records no version for 1.
|
|
49
|
+
*/
|
|
50
|
+
export function gateSealVersion(a) {
|
|
51
|
+
const checkpoint = a.carriedFrom !== undefined ? a.carriedFrom.checkpointDigest : a.checkpointDigest;
|
|
52
|
+
return checkpoint !== undefined ? 2 : 1;
|
|
53
|
+
}
|
|
26
54
|
/**
|
|
27
55
|
* The bytes a gate approval's seal signs (#3163): the op (or component), the
|
|
28
56
|
* gate, the environment, the plan digest, the approver and the time, joined
|
|
@@ -31,10 +59,21 @@ export const GATE_SEAL_NAMESPACE = "chant-gate";
|
|
|
31
59
|
* `relayed-by <principal>`, so the relay can't be added, changed or removed
|
|
32
60
|
* after sealing; an approval nobody relayed signs the six lines it always did,
|
|
33
61
|
* so seals made before #3402 still verify.
|
|
62
|
+
*
|
|
63
|
+
* Seal version 2 (#3947) adds the checkpoint a resumed run carries its step
|
|
64
|
+
* outputs from: an approval with a `checkpointDigest` signs one more line,
|
|
65
|
+
* `checkpoint <digest>`, last. The values the steps after the gate read are
|
|
66
|
+
* then covered by the seal too, and removing or swapping the digest stops it
|
|
67
|
+
* verifying. An approval with none signs what a version 1 seal signed.
|
|
68
|
+
*
|
|
69
|
+
* A carried approval (#3949) signs what the line it came from signed: see
|
|
70
|
+
* {@link sealedFieldsOf}.
|
|
34
71
|
*/
|
|
35
|
-
export function gateSealPayload(
|
|
72
|
+
export function gateSealPayload(approval) {
|
|
73
|
+
const a = sealedFieldsOf(approval);
|
|
36
74
|
const relay = a.relayedBy !== undefined ? `\nrelayed-by ${a.relayedBy}` : "";
|
|
37
|
-
|
|
75
|
+
const checkpoint = gateSealVersion(a) === 2 ? `\ncheckpoint ${a.checkpointDigest}` : "";
|
|
76
|
+
return Buffer.from(`${a.op}\n${a.gate}\n${a.environment ?? ""}\n${a.planDigest ?? ""}\n${a.resolvedBy}\n${a.timestamp}${relay}${checkpoint}`, "utf-8");
|
|
38
77
|
}
|
|
39
78
|
// ── SSHSIG ───────────────────────────────────────────────────────────────
|
|
40
79
|
/** A reader over ssh wire format: `take(n)` bytes, or `take()` one length-prefixed string. */
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import type { SealCode } from "../records.js";
|
|
25
25
|
import { type TrustPolicy } from "./policy.js";
|
|
26
26
|
import { type SealedGateApproval } from "./gate-seal.js";
|
|
27
|
-
export { GATE_SEAL_NAMESPACE, gateSealPayload, type SealedGateApproval } from "./gate-seal.js";
|
|
27
|
+
export { GATE_SEAL_NAMESPACE, gateSealPayload, gateSealVersion, sealedFieldsOf, type SealedGateApproval } from "./gate-seal.js";
|
|
28
28
|
/** The ssh signature namespace of a verdict seal. */
|
|
29
29
|
export declare const REVIEW_SEAL_NAMESPACE = "chant-review";
|
|
30
30
|
/** The ssh signature namespace of a record's author seal (#2688). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAmG,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvJ,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/seal.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,7 +27,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
27
27
|
import { join, resolve } from "node:path";
|
|
28
28
|
import { sshKeygen, verifySshSignature } from "./ssh-commit.js";
|
|
29
29
|
import { GATE_SEAL_NAMESPACE, gateSealPayload, namespaceAllowed, sshSignatureKey, verifySshSignatureWith } from "./gate-seal.js";
|
|
30
|
-
export { GATE_SEAL_NAMESPACE, gateSealPayload } from "./gate-seal.js";
|
|
30
|
+
export { GATE_SEAL_NAMESPACE, gateSealPayload, gateSealVersion, sealedFieldsOf } from "./gate-seal.js";
|
|
31
31
|
/** The ssh signature namespace of a verdict seal. */
|
|
32
32
|
export const REVIEW_SEAL_NAMESPACE = "chant-review";
|
|
33
33
|
/** The ssh signature namespace of a record's author seal (#2688). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-cli.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"verify-cli.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7E"}
|
|
@@ -22,6 +22,7 @@ export async function runWorkspaceVerify(ctx) {
|
|
|
22
22
|
}
|
|
23
23
|
const report = verifyChange({
|
|
24
24
|
repo,
|
|
25
|
+
cwd: process.cwd(),
|
|
25
26
|
base: args.base,
|
|
26
27
|
head: args.head,
|
|
27
28
|
require: args.require === "attested" || args.require === "attested-runs" ? args.require : undefined,
|
|
@@ -50,7 +51,7 @@ function printReport(r) {
|
|
|
50
51
|
for (const c of r.runs.commits)
|
|
51
52
|
lines.push(` ${c.commit.slice(0, 8)} agent run ${c.runs.join(", ") || c.run}: ${c.attested ? "attested" : "not attested"}, ${c.reason}`);
|
|
52
53
|
for (const w of r.protectedWrites)
|
|
53
|
-
lines.push(` protected write ${w.commit.slice(0, 8)} ${w.paths.join(", ")}: ${w.allowed ? "allowed" : "refused"}, ${w.reason}`);
|
|
54
|
+
lines.push(` protected write ${w.commit.slice(0, 8)} ${w.paths.join(", ")}${w.blocks ? ` (${w.blocks.join(", ")})` : ""}: ${w.allowed ? "allowed" : "refused"}, ${w.reason}`);
|
|
54
55
|
if (r.rotation && "to" in r.rotation)
|
|
55
56
|
lines.push(` signer set: version ${r.rotation.from} to ${r.rotation.to}, signed by ${r.rotation.signedBy.join(", ")}`);
|
|
56
57
|
for (const n of r.notes)
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* that touches those files must be attested by a signer the base policy
|
|
9
9
|
* trusts, and by an admin when the base policy grants the admin role.
|
|
10
10
|
*
|
|
11
|
+
* The write scope is policy too (#3079): when the workspace declaration at
|
|
12
|
+
* base has a `writeScope` or `agents` block, a commit that changes either
|
|
13
|
+
* block, or removes the declaration, is a protected write on the same terms.
|
|
14
|
+
* Other edits to the declaration are not. The declaration is found from
|
|
15
|
+
* `cwd`, as `chant workspace check --changes` finds it.
|
|
16
|
+
*
|
|
11
17
|
* Attestation is opt-in (#2525 rule 6). Without a signers file at base the
|
|
12
18
|
* check passes and says so, unless `--require attested` asks for more.
|
|
13
19
|
*
|
|
@@ -36,6 +42,8 @@ export interface CommitVerdict extends RecordProvenance {
|
|
|
36
42
|
export interface ProtectedWrite {
|
|
37
43
|
commit: string;
|
|
38
44
|
paths: string[];
|
|
45
|
+
/** For a declaration write (#3079): the scope blocks the commit changes. */
|
|
46
|
+
blocks?: ScopeBlock[];
|
|
39
47
|
level: ProvenanceLevel;
|
|
40
48
|
principal?: string;
|
|
41
49
|
allowed: boolean;
|
|
@@ -61,6 +69,16 @@ export interface ChangeReport {
|
|
|
61
69
|
/** Protected paths whose content differs between the merge base and head. */
|
|
62
70
|
protectedChanged: string[];
|
|
63
71
|
protectedWrites: ProtectedWrite[];
|
|
72
|
+
/**
|
|
73
|
+
* The workspace declaration at base, when it has a scope block (#3079): its
|
|
74
|
+
* path from the repository root, the blocks it has, and the blocks that
|
|
75
|
+
* differ between the merge base and head.
|
|
76
|
+
*/
|
|
77
|
+
declaration: {
|
|
78
|
+
path: string;
|
|
79
|
+
blocks: ScopeBlock[];
|
|
80
|
+
changed: ScopeBlock[];
|
|
81
|
+
} | null;
|
|
64
82
|
/**
|
|
65
83
|
* When the change edits the signer set (#2553): the version it proposes and
|
|
66
84
|
* who signed it, or, when the new set is refused, the version it had to
|
|
@@ -81,6 +99,8 @@ export interface ChangeReport {
|
|
|
81
99
|
}
|
|
82
100
|
export interface VerifyOptions {
|
|
83
101
|
repo: string;
|
|
102
|
+
/** Where to look for the workspace declaration, as `check --changes` does; the repository root by default. */
|
|
103
|
+
cwd?: string;
|
|
84
104
|
base?: string;
|
|
85
105
|
head?: string;
|
|
86
106
|
require?: VerifyRequirement;
|
|
@@ -91,5 +111,7 @@ export interface VerifyOptions {
|
|
|
91
111
|
* changes. Merges count only for what they change beyond every parent.
|
|
92
112
|
*/
|
|
93
113
|
export declare function commitsTouching(repo: string, range: string, paths: string[]): Map<string, string[]>;
|
|
114
|
+
/** The declaration blocks whose change is a protected write (#3079). */
|
|
115
|
+
export type ScopeBlock = "writeScope" | "agents";
|
|
94
116
|
export declare function verifyChange(opts: VerifyOptions): ChangeReport;
|
|
95
117
|
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* that touches those files must be attested by a signer the base policy
|
|
9
9
|
* trusts, and by an admin when the base policy grants the admin role.
|
|
10
10
|
*
|
|
11
|
+
* The write scope is policy too (#3079): when the workspace declaration at
|
|
12
|
+
* base has a `writeScope` or `agents` block, a commit that changes either
|
|
13
|
+
* block, or removes the declaration, is a protected write on the same terms.
|
|
14
|
+
* Other edits to the declaration are not. The declaration is found from
|
|
15
|
+
* `cwd`, as `chant workspace check --changes` finds it.
|
|
16
|
+
*
|
|
11
17
|
* Attestation is opt-in (#2525 rule 6). Without a signers file at base the
|
|
12
18
|
* check passes and says so, unless `--require attested` asks for more.
|
|
13
19
|
*
|
|
@@ -21,6 +27,11 @@
|
|
|
21
27
|
* `--require`, `runs` reports and nothing fails (studio-035 d).
|
|
22
28
|
*/
|
|
23
29
|
import { execFileSync } from "node:child_process";
|
|
30
|
+
import { posix } from "node:path";
|
|
31
|
+
import { isDeepStrictEqual } from "node:util";
|
|
32
|
+
import { DECLARATION_FILES, WorkspaceReadError } from "../declaration.js";
|
|
33
|
+
import { parseJsonText } from "../jsonc.js";
|
|
34
|
+
import { locateWorkspace } from "../which-chant.js";
|
|
24
35
|
import { policyAtBase, commitProvenance, resolveBase } from "./provenance.js";
|
|
25
36
|
import { policyWriters, protectedPaths } from "./policy.js";
|
|
26
37
|
import { fileAt, nextVersion, rotationPath, signerHistory } from "./rotation.js";
|
|
@@ -45,6 +56,47 @@ export function commitsTouching(repo, range, paths) {
|
|
|
45
56
|
}
|
|
46
57
|
return out;
|
|
47
58
|
}
|
|
59
|
+
const SCOPE_BLOCKS = ["writeScope", "agents"];
|
|
60
|
+
function scopeState(repo, rev, dir) {
|
|
61
|
+
const present = DECLARATION_FILES.map((name) => ({ name, text: fileAt(repo, rev, posix.join(dir, name)) })).filter((f) => f.text !== undefined);
|
|
62
|
+
if (present.length === 0)
|
|
63
|
+
return { blocks: {} };
|
|
64
|
+
if (present.length > 1)
|
|
65
|
+
return { unreadable: present.map((f) => f.text).join("\0") };
|
|
66
|
+
const parsed = parseJsonText(present[0].text, { jsonc: present[0].name.endsWith(".jsonc") });
|
|
67
|
+
if (!parsed.ok || typeof parsed.value !== "object" || parsed.value === null || Array.isArray(parsed.value))
|
|
68
|
+
return { unreadable: present[0].text };
|
|
69
|
+
const obj = parsed.value;
|
|
70
|
+
return { blocks: Object.fromEntries(SCOPE_BLOCKS.filter((b) => obj[b] !== undefined).map((b) => [b, obj[b]])) };
|
|
71
|
+
}
|
|
72
|
+
/** The scope blocks that differ between two states; all of them when either can't be read and the texts differ. */
|
|
73
|
+
function changedBlocks(a, b) {
|
|
74
|
+
if ("unreadable" in a || "unreadable" in b) {
|
|
75
|
+
return "unreadable" in a && "unreadable" in b && a.unreadable === b.unreadable ? [] : [...SCOPE_BLOCKS];
|
|
76
|
+
}
|
|
77
|
+
return SCOPE_BLOCKS.filter((k) => !isDeepStrictEqual(a.blocks[k], b.blocks[k]));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The directory, from the repository root, of the declaration found from
|
|
81
|
+
* `cwd` at `base`, with the scope blocks it has there. Null when there is no
|
|
82
|
+
* declaration, or one with neither block: then nothing in it is protected. A
|
|
83
|
+
* declaration that can't be read at base counts as having both.
|
|
84
|
+
*/
|
|
85
|
+
function scopedDeclarationAt(repo, cwd, base) {
|
|
86
|
+
let dir;
|
|
87
|
+
try {
|
|
88
|
+
const root = locateWorkspace(cwd, base).root;
|
|
89
|
+
dir = root === "." ? "" : root;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err instanceof WorkspaceReadError)
|
|
93
|
+
return null;
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
const state = scopeState(repo, base, dir);
|
|
97
|
+
const blocks = "unreadable" in state ? [...SCOPE_BLOCKS] : SCOPE_BLOCKS.filter((b) => b in state.blocks);
|
|
98
|
+
return blocks.length > 0 ? { dir, blocks } : null;
|
|
99
|
+
}
|
|
48
100
|
/** Whether a merge commit changes nothing beyond what its parents bring. */
|
|
49
101
|
function mergeWithoutOwnChanges(repo, commit, parents) {
|
|
50
102
|
if (parents < 2)
|
|
@@ -73,6 +125,7 @@ export function verifyChange(opts) {
|
|
|
73
125
|
protectedPaths: protectedPaths(policy),
|
|
74
126
|
protectedChanged: [],
|
|
75
127
|
protectedWrites: [],
|
|
128
|
+
declaration: null,
|
|
76
129
|
rotation: null,
|
|
77
130
|
notes: [],
|
|
78
131
|
failures: [],
|
|
@@ -99,7 +152,8 @@ export function verifyChange(opts) {
|
|
|
99
152
|
}
|
|
100
153
|
// Every commit in the change, oldest first, with its parents.
|
|
101
154
|
const listing = git(repo, ["rev-list", "--reverse", "--parents", `${base.commit}..${head}`]).split("\n").filter(Boolean);
|
|
102
|
-
const
|
|
155
|
+
const parentList = new Map(listing.map((l) => { const [c, ...p] = l.split(" "); return [c, p]; }));
|
|
156
|
+
const parentsOf = new Map([...parentList].map(([c, p]) => [c, p.length]));
|
|
103
157
|
report.runs = changeRuns(repo, [...parentsOf.keys()], policy.runners);
|
|
104
158
|
if (opts.require === "attested-runs") {
|
|
105
159
|
if (policy.runners.length === 0 && report.runs.commits.length > 0) {
|
|
@@ -145,24 +199,50 @@ export function verifyChange(opts) {
|
|
|
145
199
|
report.commits.push(v);
|
|
146
200
|
}
|
|
147
201
|
// Protected writes: every commit that touches the policy needs a writer's signature.
|
|
202
|
+
const writers = policyWriters(policy);
|
|
203
|
+
const judge = (commit, paths, blocks) => {
|
|
204
|
+
const v = verdicts.get(commit) ?? { ...commitProvenance(repo, policy, commit, opts.attestors), commit, subject: "" };
|
|
205
|
+
const allowed = v.level === "attested" && v.principal !== undefined && writers.has(v.principal);
|
|
206
|
+
const who = policy.roles.admin?.length ? "an admin the policy at base names" : "a signer the policy at base lists";
|
|
207
|
+
const what = blocks ? `${paths.join(", ")} (${blocks.join(", ")})` : paths.join(", ");
|
|
208
|
+
const reason = allowed
|
|
209
|
+
? `signed by ${v.principal}, ${who.replace(/^an? /, "")}`
|
|
210
|
+
: v.level === "attested"
|
|
211
|
+
? `signed by ${v.principal}, who is not ${who}`
|
|
212
|
+
: `${what} is protected and needs a signature by ${who}: ${v.reason}`;
|
|
213
|
+
report.protectedWrites.push({ commit, paths, ...(blocks ? { blocks } : {}), level: v.level, ...(v.principal ? { principal: v.principal } : {}), allowed, reason });
|
|
214
|
+
if (!allowed)
|
|
215
|
+
report.failures.push(`${commit.slice(0, 8)} changes ${what}: ${reason}`);
|
|
216
|
+
};
|
|
148
217
|
if (changedPaths.length > 0) {
|
|
149
|
-
const writers = policyWriters(policy);
|
|
150
218
|
const touching = commitsTouching(repo, `${base.commit}..${head}`, report.protectedPaths);
|
|
151
219
|
if (touching.size === 0) {
|
|
152
220
|
report.failures.push(`the policy files ${changedPaths.join(", ")} differ from base, and no commit in the change accounts for it`);
|
|
153
221
|
}
|
|
154
|
-
for (const [commit, paths] of touching)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
222
|
+
for (const [commit, paths] of touching)
|
|
223
|
+
judge(commit, paths);
|
|
224
|
+
}
|
|
225
|
+
// The write scope in the declaration at base (#3079): a commit that changes writeScope or agents.
|
|
226
|
+
const scoped = scopedDeclarationAt(repo, opts.cwd ?? repo, base.commit);
|
|
227
|
+
if (scoped) {
|
|
228
|
+
const files = DECLARATION_FILES.map((name) => posix.join(scoped.dir, name));
|
|
229
|
+
const changed = changedBlocks(scopeState(repo, mergeBase, scoped.dir), scopeState(repo, head, scoped.dir));
|
|
230
|
+
report.declaration = { path: files.find((f) => fileAt(repo, base.commit, f) !== undefined) ?? files[0], blocks: scoped.blocks, changed };
|
|
231
|
+
if (changed.length > 0) {
|
|
232
|
+
let accounted = false;
|
|
233
|
+
for (const [commit, paths] of commitsTouching(repo, `${base.commit}..${head}`, files)) {
|
|
234
|
+
const after = scopeState(repo, commit, scoped.dir);
|
|
235
|
+
// A merge changes a block only when it differs from every parent.
|
|
236
|
+
const per = (parentList.get(commit) ?? []).map((p) => new Set(changedBlocks(scopeState(repo, p, scoped.dir), after)));
|
|
237
|
+
const blocks = SCOPE_BLOCKS.filter((b) => per.length > 0 && per.every((s) => s.has(b)));
|
|
238
|
+
if (blocks.length === 0)
|
|
239
|
+
continue;
|
|
240
|
+
accounted = true;
|
|
241
|
+
judge(commit, paths, blocks);
|
|
242
|
+
}
|
|
243
|
+
if (!accounted) {
|
|
244
|
+
report.failures.push(`${changed.join(" and ")} in ${report.declaration.path} differ from base, and no commit in the change accounts for it`);
|
|
245
|
+
}
|
|
166
246
|
}
|
|
167
247
|
}
|
|
168
248
|
// A new signer set must be signed by a threshold of the set at base (#2553).
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* `write-scope-class-unknown` until the plugin is installed at the pinned
|
|
38
38
|
* version or the entry is removed.
|
|
39
39
|
*/
|
|
40
|
-
import { type AgentDeclaration, type ClassScope, type Declaration, type PrincipalClass, type ProtectedPath, type WriteVerb } from "./declaration.js";
|
|
40
|
+
import { WorkspaceReadError, type AgentDeclaration, type ClassScope, type Declaration, type PrincipalClass, type ProtectedPath, type WriteVerb } from "./declaration.js";
|
|
41
41
|
import { classesOf, roleGrants, type ClassRegistry } from "./principal-classes.js";
|
|
42
42
|
import { type TrustPolicy } from "./trust/policy.js";
|
|
43
43
|
import { AGENT_TRAILER } from "./trailers.js";
|
|
@@ -164,6 +164,8 @@ export interface ScopeSource {
|
|
|
164
164
|
policy: TrustPolicy;
|
|
165
165
|
/** The principal classes: the core four and those the declaration's pins supply, path pins read where the declaration was (#3080). */
|
|
166
166
|
classes: ClassRegistry;
|
|
167
|
+
/** Why the working tree's declaration couldn't be read when `declaration` is null: declaration-missing, or declaration-invalid with the problem (#3433). */
|
|
168
|
+
failure?: WorkspaceReadError;
|
|
167
169
|
}
|
|
168
170
|
/**
|
|
169
171
|
* The scope for a write from `cwd`: the declaration and the trust policy at
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-scope.d.ts","sourceRoot":"","sources":["../../src/workspace/write-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAOH,OAAO,
|
|
1
|
+
{"version":3,"file":"write-scope.d.ts","sourceRoot":"","sources":["../../src/workspace/write-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -275,17 +275,22 @@ export function judgePath(declaration, writer, path, change) {
|
|
|
275
275
|
message: `${who(writer)} may not write ${path}${allowance}: writeScope.${writer.class}.protected lists ${guarded.path}`,
|
|
276
276
|
};
|
|
277
277
|
}
|
|
278
|
-
|
|
278
|
+
/** The declaration at `at` (the working tree without it), or the read error that says why there is none. */
|
|
279
|
+
function readOrFailure(cwd, at) {
|
|
279
280
|
try {
|
|
280
281
|
const located = locateWorkspace(cwd, at);
|
|
281
282
|
return { declaration: readDeclaration(located.tree), rootOnDisk: located.rootOnDisk, root: located.root, tree: located.tree };
|
|
282
283
|
}
|
|
283
284
|
catch (err) {
|
|
284
285
|
if (err instanceof WorkspaceReadError)
|
|
285
|
-
return
|
|
286
|
+
return err;
|
|
286
287
|
throw err;
|
|
287
288
|
}
|
|
288
289
|
}
|
|
290
|
+
function readAt(cwd, at) {
|
|
291
|
+
const read = readOrFailure(cwd, at);
|
|
292
|
+
return read instanceof WorkspaceReadError ? null : read;
|
|
293
|
+
}
|
|
289
294
|
/**
|
|
290
295
|
* The scope for a write from `cwd`: the declaration and the trust policy at
|
|
291
296
|
* the base revision (`origin/HEAD`, `main` or `master`), so a write can't
|
|
@@ -306,8 +311,10 @@ export function scopeSource(cwd, at) {
|
|
|
306
311
|
if (atBase)
|
|
307
312
|
return { ...withClasses(atBase), from: "base", policy };
|
|
308
313
|
}
|
|
309
|
-
const here =
|
|
310
|
-
|
|
314
|
+
const here = readOrFailure(cwd);
|
|
315
|
+
if (here instanceof WorkspaceReadError)
|
|
316
|
+
return { declaration: null, from: null, rootOnDisk: null, root: null, policy, classes: coreClassRegistry(), failure: here };
|
|
317
|
+
return { ...withClasses(here), from: "working-tree", policy };
|
|
311
318
|
}
|
|
312
319
|
const toPosix = (p) => (sep === "/" ? p : p.split(sep).join("/"));
|
|
313
320
|
/**
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ import { coreKnowledgeChecks } from "../../lint/knowledge-checks";
|
|
|
27
27
|
import { applyConfiguredSeverity, applyConfiguredPreset, resolvePresetIds } from "../../lint/config";
|
|
28
28
|
import { applyInlineSuppressions, type SuppressionMetaFinding } from "../../lint/suppressions";
|
|
29
29
|
import type { Declarable } from "../../declarable";
|
|
30
|
-
import { coreOpChecks } from "../../lint/rules/op";
|
|
30
|
+
import { coreOpChecks, workspaceIdentityGates } from "../../lint/rules/op";
|
|
31
31
|
import { loadPolicyChecks } from "../../lint/policy";
|
|
32
32
|
import { armSandboxPolicyExecution, runProjectPolicies } from "../../lint/policy-sandbox";
|
|
33
33
|
import { sortedJsonReplacer } from "../../utils";
|
|
@@ -588,7 +588,14 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
588
588
|
// FULL build result regardless of which plugins loaded — same as the
|
|
589
589
|
// receipt/output/knowledge checks above.
|
|
590
590
|
if (result.errors.length === 0) {
|
|
591
|
-
|
|
591
|
+
// OPS017 (#3949) reads the workspace's identity.gates, when an Op has gate aliases.
|
|
592
|
+
const identityGates = await workspaceIdentityGates(configDir, result.entities);
|
|
593
|
+
const opDiags = runPostSynthChecks(
|
|
594
|
+
coreOpChecks({ decideBackends: config.decide?.backends, ...(identityGates ? { identityGates } : {}) }),
|
|
595
|
+
result,
|
|
596
|
+
env,
|
|
597
|
+
{ activityContracts },
|
|
598
|
+
);
|
|
592
599
|
const { diagnostics: activeDiags, suppressed } = applyConfiguredSeverity(opDiags, config.lint?.rules);
|
|
593
600
|
suppressedPostSynthCount += suppressed.length;
|
|
594
601
|
for (const diag of activeDiags) {
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { loadComponentChecks } from "../../lint/rules/comp/index";
|
|
|
13
13
|
import { runComponentChecks, type ComponentCheckDiagnostic } from "../../lint/component-checks";
|
|
14
14
|
import { buildCapabilityRegistry } from "../../components/capability-plugin-loader";
|
|
15
15
|
import type { RollbackPolicy } from "../../components/capability";
|
|
16
|
-
import { coreOpChecks } from "../../lint/rules/op";
|
|
16
|
+
import { coreOpChecks, workspaceIdentityGates } from "../../lint/rules/op";
|
|
17
17
|
import { loadActivityContracts } from "../../op/activity-contract-registry";
|
|
18
18
|
import { runPostSynthChecks } from "../../lint/post-synth";
|
|
19
19
|
import { rule } from "../../lint/declarative";
|
|
@@ -627,8 +627,10 @@ async function runOpCheckDiagnostics(
|
|
|
627
627
|
.then((loaded) => loaded.config.decide?.backends)
|
|
628
628
|
.catch(() => undefined);
|
|
629
629
|
|
|
630
|
+
// OPS017 (#3949) reads the workspace's identity.gates, when an Op has gate aliases.
|
|
631
|
+
const identityGates = await workspaceIdentityGates(findProjectRoot(infraPath), entities);
|
|
630
632
|
const raw = runPostSynthChecks(
|
|
631
|
-
coreOpChecks({ decideBackends }),
|
|
633
|
+
coreOpChecks({ decideBackends, ...(identityGates ? { identityGates } : {}) }),
|
|
632
634
|
{
|
|
633
635
|
outputs: new Map(),
|
|
634
636
|
entities: entities as Map<string, never>,
|