@intentius/chant 0.124.14 → 0.124.15
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/build.js +1 -0
- package/dist/change-set.d.ts +24 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +50 -1
- package/dist/ci-jobs.d.ts +227 -0
- package/dist/ci-jobs.d.ts.map +1 -0
- package/dist/ci-jobs.js +448 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +3 -2
- package/dist/composite.d.ts +10 -2
- package/dist/composite.d.ts.map +1 -1
- package/dist/composite.js +8 -0
- package/dist/discovery/collect.d.ts +2 -1
- package/dist/discovery/collect.d.ts.map +1 -1
- package/dist/discovery/collect.js +16 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +2 -107
- package/dist/discovery/host-composite.d.ts +35 -0
- package/dist/discovery/host-composite.d.ts.map +1 -0
- package/dist/discovery/host-composite.js +93 -0
- package/dist/discovery/index.d.ts +9 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +1 -1
- package/dist/gate-decision.d.ts +20 -9
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +72 -19
- package/dist/lifecycle/gate-audit.d.ts +69 -2
- package/dist/lifecycle/gate-audit.d.ts.map +1 -1
- package/dist/lifecycle/gate-audit.js +76 -2
- package/dist/lifecycle/gate-ledger.d.ts +24 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +9 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +8 -3
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +5 -2
- package/dist/op/activities/reconcile.d.ts +28 -16
- package/dist/op/activities/reconcile.d.ts.map +1 -1
- package/dist/op/activities/reconcile.js +34 -20
- package/dist/op/builders.d.ts +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +1 -1
- package/dist/op/op-waves-run.d.ts +1 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +3 -3
- package/dist/workspace/__fixtures__/contract-repo.d.ts +7 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +17 -1
- package/dist/workspace/__fixtures__/sessions.d.ts +1 -1
- package/dist/workspace/__fixtures__/sessions.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/sessions.js +3 -3
- package/dist/workspace/change-set.schema.json +5 -3
- package/dist/workspace/ci-green-workflow.d.ts +3 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +5 -2
- package/dist/workspace/class-data.d.ts +84 -0
- package/dist/workspace/class-data.d.ts.map +1 -0
- package/dist/workspace/class-data.js +238 -0
- package/dist/workspace/kinds.d.ts +3 -7
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/kinds.js +6 -40
- package/dist/workspace/principal-classes.d.ts +4 -11
- package/dist/workspace/principal-classes.d.ts.map +1 -1
- package/dist/workspace/principal-classes.js +11 -94
- package/package.json +1 -1
- package/src/build.ts +8 -0
- package/src/change-set.test.ts +48 -0
- package/src/change-set.ts +52 -2
- package/src/ci-jobs.test.ts +49 -0
- package/src/ci-jobs.ts +515 -0
- package/src/components/pr-pipeline.ts +3 -2
- package/src/composite.ts +15 -3
- package/src/discovery/collect.ts +15 -1
- package/src/discovery/fold-build-parity.test.ts +71 -0
- package/src/discovery/fold-import.ts +2 -113
- package/src/discovery/fold-project-provenance.test.ts +21 -4
- package/src/discovery/host-composite.ts +101 -0
- package/src/discovery/index.ts +11 -1
- package/src/gate-decision-bundle.test.ts +5 -4
- package/src/gate-decision-identity.test.ts +59 -6
- package/src/gate-decision.ts +81 -21
- package/src/lifecycle/gate-audit.test.ts +55 -1
- package/src/lifecycle/gate-audit.ts +116 -3
- package/src/lifecycle/gate-ledger.test.ts +20 -1
- package/src/lifecycle/gate-ledger.ts +33 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +11 -3
- package/src/op/activities/lexicon-upgrade.test.ts +2 -0
- package/src/op/activities/reconcile.test.ts +62 -2
- package/src/op/activities/reconcile.ts +46 -22
- package/src/op/builders.ts +1 -1
- package/src/op/op-waves-run.ts +4 -3
- package/src/op/op-waves.test.ts +2 -0
- package/src/op/work-lease-run.test.ts +12 -4
- package/src/workspace/__fixtures__/contract-repo.ts +17 -1
- package/src/workspace/__fixtures__/sessions.ts +3 -3
- package/src/workspace/change-set.schema.json +5 -3
- package/src/workspace/ci-green-workflow.test.ts +4 -4
- package/src/workspace/ci-green-workflow.ts +5 -2
- package/src/workspace/class-data.test.ts +64 -0
- package/src/workspace/class-data.ts +236 -0
- package/src/workspace/kinds.ts +6 -35
- package/src/workspace/principal-classes.ts +11 -94
- package/src/workspace/records-formats.test.ts +7 -2
- package/src/workspace/records-sessions-write.test.ts +3 -3
- package/src/workspace/status-contract.e2e.test.ts +37 -0
- package/src/workspace/status-contract.test.ts +23 -21
|
@@ -18,6 +18,7 @@ import { collectCompositeOrigins, collectParamDependencies, collectTagOrigins, s
|
|
|
18
18
|
import { getInterpolationFields, getProvenance, setPathProvenance, setProvenance } from "../provenance.js";
|
|
19
19
|
import { foldProvenanceOfEntities } from "../fold-provenance.js";
|
|
20
20
|
import { collectEntities } from "./collect.js";
|
|
21
|
+
import { hostCompositeName, markHostCompositeInstance } from "./host-composite.js";
|
|
21
22
|
import { intrinsicCallFoldsEagerly, intrinsicTagFolds } from "../lexicon.js";
|
|
22
23
|
/**
|
|
23
24
|
* chant #1063 — the package specifier a lexicon NAME (`"aws"`, `"gitlab"`)
|
|
@@ -2265,52 +2266,6 @@ async function interpretCompositeFactory(factory, args, ctx, call) {
|
|
|
2265
2266
|
return undefined;
|
|
2266
2267
|
}
|
|
2267
2268
|
}
|
|
2268
|
-
/**
|
|
2269
|
-
* chant#3598 — a host composite instance's own marker. Non-enumerable and
|
|
2270
|
-
* symbol-keyed, like provenance, so nothing that serializes or compares the
|
|
2271
|
-
* exports sees it. It carries the composite's name, which the members object
|
|
2272
|
-
* otherwise has no record of once it is returned.
|
|
2273
|
-
*/
|
|
2274
|
-
const HOST_COMPOSITE_INSTANCE = Symbol.for("chant.fold.hostCompositeInstance");
|
|
2275
|
-
/**
|
|
2276
|
-
* Mark `members` as the instance a host's composite produced, and return it
|
|
2277
|
-
* unchanged.
|
|
2278
|
-
*
|
|
2279
|
-
* chant#3608 — every declarable member, and every member of a nested host
|
|
2280
|
-
* instance, is also stamped with the composite, the way `expandComposite`
|
|
2281
|
-
* stamps a chant instance's members. A destructured member is never expanded,
|
|
2282
|
-
* so this is the only record it has that a composite wrote it, and without
|
|
2283
|
-
* it a path the fold did not attribute reads as authored and so `direct`.
|
|
2284
|
-
* `setProvenance` keeps the first writer, and a nested instance was marked
|
|
2285
|
-
* when its own call returned, so a member keeps its innermost composite.
|
|
2286
|
-
*/
|
|
2287
|
-
function markHostCompositeInstance(members, compositeName) {
|
|
2288
|
-
if (Object.isExtensible(members) && !Object.prototype.hasOwnProperty.call(members, HOST_COMPOSITE_INSTANCE)) {
|
|
2289
|
-
Object.defineProperty(members, HOST_COMPOSITE_INSTANCE, {
|
|
2290
|
-
value: compositeName,
|
|
2291
|
-
enumerable: false,
|
|
2292
|
-
configurable: true,
|
|
2293
|
-
});
|
|
2294
|
-
}
|
|
2295
|
-
stampHostCompositeMembers(members, compositeName);
|
|
2296
|
-
return members;
|
|
2297
|
-
}
|
|
2298
|
-
/** Stamp `composite` on each declarable member of a host instance, descending into nested host instances. */
|
|
2299
|
-
function stampHostCompositeMembers(members, compositeName) {
|
|
2300
|
-
for (const member of Object.values(members)) {
|
|
2301
|
-
if (isDeclarable(member))
|
|
2302
|
-
setProvenance(member, { composite: compositeName });
|
|
2303
|
-
else if (hostCompositeName(member) !== undefined)
|
|
2304
|
-
stampHostCompositeMembers(member, compositeName);
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
/** The composite name a host composite instance was marked with, or `undefined` for any other value. */
|
|
2308
|
-
function hostCompositeName(value) {
|
|
2309
|
-
if (!isIndexableObject(value))
|
|
2310
|
-
return undefined;
|
|
2311
|
-
const name = value[HOST_COMPOSITE_INSTANCE];
|
|
2312
|
-
return typeof name === "string" ? name : undefined;
|
|
2313
|
-
}
|
|
2314
2269
|
/** Evaluate an admissible factory body's statements in order, binding each `const`, and return the `return` expression's value. */
|
|
2315
2270
|
async function interpretFactoryBody(fn, ctx, bind) {
|
|
2316
2271
|
if (!ts.isBlock(fn.body))
|
|
@@ -4036,7 +3991,7 @@ export async function foldProject(files, intrinsics = [], options = {}) {
|
|
|
4036
3991
|
const attempt = attempts.get(file);
|
|
4037
3992
|
return {
|
|
4038
3993
|
file,
|
|
4039
|
-
exports: attempt?.ok === true ?
|
|
3994
|
+
exports: attempt?.ok === true ? Object.fromEntries(attempt.exportedValues) : {},
|
|
4040
3995
|
};
|
|
4041
3996
|
}));
|
|
4042
3997
|
const out = new Map();
|
|
@@ -4103,66 +4058,6 @@ function foldProvenanceByFile(modules) {
|
|
|
4103
4058
|
}
|
|
4104
4059
|
return out;
|
|
4105
4060
|
}
|
|
4106
|
-
/**
|
|
4107
|
-
* chant#3598 — a file's exports with every host composite instance replaced by
|
|
4108
|
-
* its members, for {@link foldProvenanceByFile} only.
|
|
4109
|
-
*
|
|
4110
|
-
* A host's registration form (chant#2442) returns the members object the
|
|
4111
|
-
* factory built, not a chant `CompositeInstance`, because wrapping it would run
|
|
4112
|
-
* chant's member validation against the host's own entities. `collectEntities`
|
|
4113
|
-
* expands only chant's instances, so an instance exported whole
|
|
4114
|
-
* (`export const store = Store({...})`) contributed no entity and therefore no
|
|
4115
|
-
* record, while a destructured member, a plain declarable export, did.
|
|
4116
|
-
*
|
|
4117
|
-
* The members are named and stamped the way `expandComposite` names and stamps
|
|
4118
|
-
* a chant instance: `${exportName}${Member}`, with a whole-entity `composite`
|
|
4119
|
-
* origin under the paths the fold already attributed. That origin is written
|
|
4120
|
-
* before `collectEntities` adds its `authored` one, and the first writer wins,
|
|
4121
|
-
* so a path the fold did not attribute is `unknown` rather than `direct`.
|
|
4122
|
-
*
|
|
4123
|
-
* Only a top-level export is expanded. A host instance nested in another
|
|
4124
|
-
* composite's body or placed in an array does not fold today, so its file is
|
|
4125
|
-
* `run` and has no record to give.
|
|
4126
|
-
*
|
|
4127
|
-
* The exports a caller receives in the verdict are the originals.
|
|
4128
|
-
*/
|
|
4129
|
-
function expandHostCompositeExports(exports) {
|
|
4130
|
-
const out = {};
|
|
4131
|
-
for (const [name, value] of Object.entries(exports)) {
|
|
4132
|
-
if (hostCompositeName(value) === undefined) {
|
|
4133
|
-
out[name] = value;
|
|
4134
|
-
continue;
|
|
4135
|
-
}
|
|
4136
|
-
expandHostCompositeInstance(name, value, name, out);
|
|
4137
|
-
}
|
|
4138
|
-
return out;
|
|
4139
|
-
}
|
|
4140
|
-
/**
|
|
4141
|
-
* chant#3608 — one host instance's members into `out`, a nested host instance
|
|
4142
|
-
* expanded in place under its member's name (`siteMainBucket`), as
|
|
4143
|
-
* `expandComposite` expands a nested chant instance. Every member belongs to
|
|
4144
|
-
* the top-level `instance`; its composite is the innermost one, which
|
|
4145
|
-
* {@link markHostCompositeInstance} already stamped, so the whole-entity
|
|
4146
|
-
* origin names that composite too.
|
|
4147
|
-
*/
|
|
4148
|
-
function expandHostCompositeInstance(prefix, members, instance, out) {
|
|
4149
|
-
const compositeName = hostCompositeName(members);
|
|
4150
|
-
for (const [memberName, member] of Object.entries(members)) {
|
|
4151
|
-
if (memberName.length === 0)
|
|
4152
|
-
continue;
|
|
4153
|
-
const fullName = `${prefix}${memberName[0].toUpperCase()}${memberName.slice(1)}`;
|
|
4154
|
-
if (hostCompositeName(member) !== undefined) {
|
|
4155
|
-
expandHostCompositeInstance(fullName, member, instance, out);
|
|
4156
|
-
continue;
|
|
4157
|
-
}
|
|
4158
|
-
if (!isDeclarable(member))
|
|
4159
|
-
continue;
|
|
4160
|
-
setProvenance(member, { composite: compositeName, compositeInstance: instance });
|
|
4161
|
-
const composite = getProvenance(member)?.composite ?? compositeName;
|
|
4162
|
-
setPathProvenance(member, "", { kind: "composite", composite, instance });
|
|
4163
|
-
out[fullName] = member;
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
4061
|
/** The deepest directory every one of `files` sits under. */
|
|
4167
4062
|
function commonDirectory(files) {
|
|
4168
4063
|
const split = files.map((file) => dirname(resolvePath(file)).split(sep));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Declarable } from "../declarable.js";
|
|
2
|
+
/**
|
|
3
|
+
* Mark `members` as the instance a host's composite produced, and return it
|
|
4
|
+
* unchanged.
|
|
5
|
+
*
|
|
6
|
+
* chant#3608 — every declarable member, and every member of a nested host
|
|
7
|
+
* instance, is also stamped with the composite, the way `expandComposite`
|
|
8
|
+
* stamps a chant instance's members. A destructured member is never expanded,
|
|
9
|
+
* so this is the only record it has that a composite wrote it, and without
|
|
10
|
+
* it a path the fold did not attribute reads as authored and so `direct`.
|
|
11
|
+
* `setProvenance` keeps the first writer, and a nested instance was marked
|
|
12
|
+
* when its own call returned, so a member keeps its innermost composite.
|
|
13
|
+
*/
|
|
14
|
+
export declare function markHostCompositeInstance(members: Record<string, unknown>, compositeName: string): Record<string, unknown>;
|
|
15
|
+
/** The composite name a host composite instance was marked with, or `undefined` for any other value. */
|
|
16
|
+
export declare function hostCompositeName(value: unknown): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* chant#3598, chant#3612 — a host composite instance's members as entities,
|
|
19
|
+
* for `collectEntities`.
|
|
20
|
+
*
|
|
21
|
+
* `collectEntities` is the one place `build()` and `foldProject` turn exports
|
|
22
|
+
* into entities, so expanding here is what makes the two report the same set:
|
|
23
|
+
* `export const archive = Archive({...})` gives `archiveBucket` to both.
|
|
24
|
+
*
|
|
25
|
+
* The members are named and stamped the way `expandComposite` names and stamps
|
|
26
|
+
* a chant instance: `${exportName}${Member}`, a nested host instance expanded
|
|
27
|
+
* in place under its member's name (`siteMainBucket`), with a whole-entity
|
|
28
|
+
* `composite` origin under the paths the fold already attributed. That origin
|
|
29
|
+
* is written before `collectEntities` adds its own record, and the first
|
|
30
|
+
* writer wins, so a path the fold did not attribute is `unknown` rather than
|
|
31
|
+
* `direct`. Every member belongs to the top-level `instance`; its composite is
|
|
32
|
+
* the innermost one, which {@link markHostCompositeInstance} already stamped.
|
|
33
|
+
*/
|
|
34
|
+
export declare function expandHostComposite(prefix: string, members: Record<string, unknown>, instance?: string): Array<[string, Declarable]>;
|
|
35
|
+
//# sourceMappingURL=host-composite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-composite.d.ts","sourceRoot":"","sources":["../../src/discovery/host-composite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAoB9D;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,aAAa,EAAE,MAAM,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAUD,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,GAAE,MAAe,GACxB,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAiB7B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { isDeclarable } from "../declarable.js";
|
|
2
|
+
import { getProvenance, setPathProvenance, setProvenance } from "../provenance.js";
|
|
3
|
+
/**
|
|
4
|
+
* chant#3598 — a host composite instance's own marker. Non-enumerable and
|
|
5
|
+
* symbol-keyed, like provenance, so nothing that serializes or compares the
|
|
6
|
+
* exports sees it. It carries the composite's name, which the members object
|
|
7
|
+
* otherwise has no record of once it is returned.
|
|
8
|
+
*
|
|
9
|
+
* Only the fold path sets it: a host's registration form (chant#2442) returns
|
|
10
|
+
* the members object the factory built, not a chant `CompositeInstance`, and
|
|
11
|
+
* chant knows the object came from a composite only because the fold resolved
|
|
12
|
+
* the call. A file that runs instead has no such knowledge.
|
|
13
|
+
*/
|
|
14
|
+
const HOST_COMPOSITE_INSTANCE = Symbol.for("chant.fold.hostCompositeInstance");
|
|
15
|
+
function isPlainIndexable(value) {
|
|
16
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mark `members` as the instance a host's composite produced, and return it
|
|
20
|
+
* unchanged.
|
|
21
|
+
*
|
|
22
|
+
* chant#3608 — every declarable member, and every member of a nested host
|
|
23
|
+
* instance, is also stamped with the composite, the way `expandComposite`
|
|
24
|
+
* stamps a chant instance's members. A destructured member is never expanded,
|
|
25
|
+
* so this is the only record it has that a composite wrote it, and without
|
|
26
|
+
* it a path the fold did not attribute reads as authored and so `direct`.
|
|
27
|
+
* `setProvenance` keeps the first writer, and a nested instance was marked
|
|
28
|
+
* when its own call returned, so a member keeps its innermost composite.
|
|
29
|
+
*/
|
|
30
|
+
export function markHostCompositeInstance(members, compositeName) {
|
|
31
|
+
if (Object.isExtensible(members) && !Object.prototype.hasOwnProperty.call(members, HOST_COMPOSITE_INSTANCE)) {
|
|
32
|
+
Object.defineProperty(members, HOST_COMPOSITE_INSTANCE, {
|
|
33
|
+
value: compositeName,
|
|
34
|
+
enumerable: false,
|
|
35
|
+
configurable: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
stampHostCompositeMembers(members, compositeName);
|
|
39
|
+
return members;
|
|
40
|
+
}
|
|
41
|
+
/** Stamp `composite` on each declarable member of a host instance, descending into nested host instances. */
|
|
42
|
+
function stampHostCompositeMembers(members, compositeName) {
|
|
43
|
+
for (const member of Object.values(members)) {
|
|
44
|
+
if (isDeclarable(member))
|
|
45
|
+
setProvenance(member, { composite: compositeName });
|
|
46
|
+
else if (hostCompositeName(member) !== undefined)
|
|
47
|
+
stampHostCompositeMembers(member, compositeName);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** The composite name a host composite instance was marked with, or `undefined` for any other value. */
|
|
51
|
+
export function hostCompositeName(value) {
|
|
52
|
+
if (!isPlainIndexable(value))
|
|
53
|
+
return undefined;
|
|
54
|
+
const name = value[HOST_COMPOSITE_INSTANCE];
|
|
55
|
+
return typeof name === "string" ? name : undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* chant#3598, chant#3612 — a host composite instance's members as entities,
|
|
59
|
+
* for `collectEntities`.
|
|
60
|
+
*
|
|
61
|
+
* `collectEntities` is the one place `build()` and `foldProject` turn exports
|
|
62
|
+
* into entities, so expanding here is what makes the two report the same set:
|
|
63
|
+
* `export const archive = Archive({...})` gives `archiveBucket` to both.
|
|
64
|
+
*
|
|
65
|
+
* The members are named and stamped the way `expandComposite` names and stamps
|
|
66
|
+
* a chant instance: `${exportName}${Member}`, a nested host instance expanded
|
|
67
|
+
* in place under its member's name (`siteMainBucket`), with a whole-entity
|
|
68
|
+
* `composite` origin under the paths the fold already attributed. That origin
|
|
69
|
+
* is written before `collectEntities` adds its own record, and the first
|
|
70
|
+
* writer wins, so a path the fold did not attribute is `unknown` rather than
|
|
71
|
+
* `direct`. Every member belongs to the top-level `instance`; its composite is
|
|
72
|
+
* the innermost one, which {@link markHostCompositeInstance} already stamped.
|
|
73
|
+
*/
|
|
74
|
+
export function expandHostComposite(prefix, members, instance = prefix) {
|
|
75
|
+
const out = [];
|
|
76
|
+
const compositeName = hostCompositeName(members);
|
|
77
|
+
for (const [memberName, member] of Object.entries(members)) {
|
|
78
|
+
if (memberName.length === 0)
|
|
79
|
+
continue;
|
|
80
|
+
const fullName = `${prefix}${memberName[0].toUpperCase()}${memberName.slice(1)}`;
|
|
81
|
+
if (hostCompositeName(member) !== undefined) {
|
|
82
|
+
out.push(...expandHostComposite(fullName, member, instance));
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!isDeclarable(member))
|
|
86
|
+
continue;
|
|
87
|
+
setProvenance(member, { composite: compositeName, compositeInstance: instance });
|
|
88
|
+
const composite = getProvenance(member)?.composite ?? compositeName;
|
|
89
|
+
setPathProvenance(member, "", { kind: "composite", composite, instance });
|
|
90
|
+
out.push([fullName, member]);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
@@ -70,6 +70,15 @@ export interface DiscoveryOptions {
|
|
|
70
70
|
* at all, rather than a looser fallback.
|
|
71
71
|
*/
|
|
72
72
|
lexicons?: readonly string[];
|
|
73
|
+
/**
|
|
74
|
+
* chant#3612 — package specifiers to follow a bare import into, verbatim,
|
|
75
|
+
* alongside whatever {@link lexicons} names. The same allowlist
|
|
76
|
+
* `FoldProjectOptions.lexiconPackages` gives `foldProject` (chant#2438), so
|
|
77
|
+
* a build over a host of its own folds the files `foldProject` folds. A
|
|
78
|
+
* chant project never needs it: its packages are `lexicons`. Only
|
|
79
|
+
* meaningful when {@link fold} is set. Default: none.
|
|
80
|
+
*/
|
|
81
|
+
lexiconPackages?: readonly string[];
|
|
73
82
|
/**
|
|
74
83
|
* chant #1045 Phase 2 — opt-in: whatever would otherwise reach the
|
|
75
84
|
* in-process `importModule` step (every file, when {@link fold} isn't set;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA8D1D;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8DAA8D;IAC9D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,qDAAqD;IACrD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB;;;OAGG;IACH,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+SjG"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA8D1D;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8DAA8D;IAC9D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,qDAAqD;IACrD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB;;;OAGG;IACH,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+SjG"}
|
package/dist/discovery/index.js
CHANGED
|
@@ -134,7 +134,7 @@ export async function discover(path, options) {
|
|
|
134
134
|
"project code and the second exists to invoke it. Pass one.");
|
|
135
135
|
}
|
|
136
136
|
const foldSession = options?.fold
|
|
137
|
-
? createFoldSession(options.intrinsics, buildParamValuesMap, options.lexicons, options.sandbox === true, [], options.executing === true, files)
|
|
137
|
+
? createFoldSession(options.intrinsics, buildParamValuesMap, options.lexicons, options.sandbox === true, options.lexiconPackages ?? [], options.executing === true, files)
|
|
138
138
|
: undefined;
|
|
139
139
|
if (options?.fold) {
|
|
140
140
|
for (const file of files) {
|
package/dist/gate-decision.d.ts
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
*
|
|
19
19
|
* A gate the declaration at base names under `identity.gates` counts only a
|
|
20
20
|
* sealed approval from its class: {@link identityGateAdmissionAt} reads that
|
|
21
|
-
* rule from a bare clone at base
|
|
22
|
-
*
|
|
21
|
+
* rule from a bare clone at base, with the principal classes the pinned
|
|
22
|
+
* packages supply read as data (#3888), and judges by `admitApprover`, the
|
|
23
|
+
* function chant's `gateAdmission` judges by (#3862); pass its `refuses` to
|
|
23
24
|
* {@link decideGateApproval}.
|
|
24
25
|
*
|
|
25
26
|
* With `spend` (#3784), an approval a wave already applied under is spent and
|
|
@@ -27,8 +28,9 @@
|
|
|
27
28
|
* the lines of `_gates/<op>/applied.jsonl` ({@link parseAppliedGateLines}).
|
|
28
29
|
*
|
|
29
30
|
* The module reads the ledger lines and the review it is handed; reading
|
|
30
|
-
* them is the caller's. It imports no TypeScript, esbuild, zod, lint,
|
|
31
|
-
* codegen module, and none of chant's git ledger
|
|
31
|
+
* them is the caller's. It imports no TypeScript, esbuild, zod, ajv, lint,
|
|
32
|
+
* fold or codegen module, no package's code, and none of chant's git ledger
|
|
33
|
+
* code, which
|
|
32
34
|
* `gate-decision-bundle.test.ts` holds it to. Its rules are chant's own:
|
|
33
35
|
* `./lifecycle/gate-ledger-select.ts` is what `evaluateGate` decides by, and
|
|
34
36
|
* `gate-decision.test.ts` decides the same ledgers both ways.
|
|
@@ -90,15 +92,24 @@ export interface IdentityGateAdmission {
|
|
|
90
92
|
* class, the role grants in `.chant/trust.json` at base put the approver in
|
|
91
93
|
* it. Both judge with the same function ({@link admitApprover}).
|
|
92
94
|
*
|
|
93
|
-
* The classes are the four core ones, then
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
95
|
+
* The classes are the four core ones, then the domain classes the
|
|
96
|
+
* declaration's pins supply, in pin order (#3888), read as chant reads them:
|
|
97
|
+
* a path pin's `./workspace-principals` file at `base`, and a package pin's
|
|
98
|
+
* from the package installed under `packages` (default `repo`) at the
|
|
99
|
+
* pinned version. A bare clone has no `node_modules`, so a caller that reads
|
|
100
|
+
* one passes the directory its packages are installed under. Only data is
|
|
101
|
+
* read; no package is imported. `classes`, when given, stands for every
|
|
102
|
+
* domain class instead and no pin is read.
|
|
103
|
+
*
|
|
104
|
+
* When a pin can't be read (its package is not installed, or not at the
|
|
105
|
+
* pinned version), a gate whose class it could change (a domain class, or
|
|
106
|
+
* `human`) refuses every approval and says which pins were not read; a gate
|
|
107
|
+
* naming `agent`, `runner` or `service`, or no class, is judged as usual.
|
|
98
108
|
* Throws when the declaration at base cannot be parsed.
|
|
99
109
|
*/
|
|
100
110
|
export declare function identityGateAdmissionAt(repo: string, base: string, gate: string, opts?: {
|
|
101
111
|
classes?: readonly ClassGrant[];
|
|
112
|
+
packages?: string;
|
|
102
113
|
}): IdentityGateAdmission | null;
|
|
103
114
|
/** What a pull request's review says about a wave, under `pr-review`. */
|
|
104
115
|
export interface ReviewApproval {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate-decision.d.ts","sourceRoot":"","sources":["../src/gate-decision.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gate-decision.d.ts","sourceRoot":"","sources":["../src/gate-decision.ts"],"names":[],"mappings":"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"}
|
package/dist/gate-decision.js
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
*
|
|
19
19
|
* A gate the declaration at base names under `identity.gates` counts only a
|
|
20
20
|
* sealed approval from its class: {@link identityGateAdmissionAt} reads that
|
|
21
|
-
* rule from a bare clone at base
|
|
22
|
-
*
|
|
21
|
+
* rule from a bare clone at base, with the principal classes the pinned
|
|
22
|
+
* packages supply read as data (#3888), and judges by `admitApprover`, the
|
|
23
|
+
* function chant's `gateAdmission` judges by (#3862); pass its `refuses` to
|
|
23
24
|
* {@link decideGateApproval}.
|
|
24
25
|
*
|
|
25
26
|
* With `spend` (#3784), an approval a wave already applied under is spent and
|
|
@@ -27,13 +28,16 @@
|
|
|
27
28
|
* the lines of `_gates/<op>/applied.jsonl` ({@link parseAppliedGateLines}).
|
|
28
29
|
*
|
|
29
30
|
* The module reads the ledger lines and the review it is handed; reading
|
|
30
|
-
* them is the caller's. It imports no TypeScript, esbuild, zod, lint,
|
|
31
|
-
* codegen module, and none of chant's git ledger
|
|
31
|
+
* them is the caller's. It imports no TypeScript, esbuild, zod, ajv, lint,
|
|
32
|
+
* fold or codegen module, no package's code, and none of chant's git ledger
|
|
33
|
+
* code, which
|
|
32
34
|
* `gate-decision-bundle.test.ts` holds it to. Its rules are chant's own:
|
|
33
35
|
* `./lifecycle/gate-ledger-select.ts` is what `evaluateGate` decides by, and
|
|
34
36
|
* `gate-decision.test.ts` decides the same ledgers both ways.
|
|
35
37
|
*/
|
|
36
38
|
import { spawnSync } from "node:child_process";
|
|
39
|
+
import { readFileSync } from "node:fs";
|
|
40
|
+
import { join, posix } from "node:path";
|
|
37
41
|
import { isPendingGateExpired, latestPendingGate, latestResolutionForPlan, } from "./lifecycle/gate-ledger-select.js";
|
|
38
42
|
import { samePlanDigest } from "./lifecycle/plan-digest.js";
|
|
39
43
|
import { isApprovalSpent } from "./lifecycle/gate-applied.js";
|
|
@@ -41,6 +45,7 @@ import { gateSealRefusal } from "./workspace/trust/gate-seal.js";
|
|
|
41
45
|
import { DEFAULT_SIGNERS_PATH, TRUST_CONFIG_PATH, parseAllowedSigners, trustConfigProblems, } from "./workspace/trust/trust-files.js";
|
|
42
46
|
import { parseJsonText } from "./workspace/jsonc.js";
|
|
43
47
|
import { CORE_CLASSES, admitApprover } from "./workspace/identity-gate-rule.js";
|
|
48
|
+
import { diskFiles, findInstalledPackage, mergePinClasses, parseClassDataByHand, readClassFiles } from "./workspace/class-data.js";
|
|
44
49
|
export { GATE_APPROVAL_SOURCES } from "./op/op-waves.js";
|
|
45
50
|
export { isPendingGateExpired, latestPendingGate, latestResolutionForPlan, latestResolutionSince, } from "./lifecycle/gate-ledger-select.js";
|
|
46
51
|
export { GATE_SEAL_NAMESPACE, gateSealPayload, gateSealRefusal, sshKeyFingerprint, verifySshSignatureWith, } from "./workspace/trust/gate-seal.js";
|
|
@@ -141,6 +146,49 @@ export function identityGatesAt(repo, base) {
|
|
|
141
146
|
const declaration = declarationAt(repo, base);
|
|
142
147
|
return declaration === null ? [] : identityGatesOf(declaration.value);
|
|
143
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Read the classes each pin supplies, as chant's `gateAdmission(cwd, gate,
|
|
151
|
+
* base)` reads them: a path pin's `./workspace-principals` file from `base`
|
|
152
|
+
* itself, so a change can't remap a class by editing its own copy of the
|
|
153
|
+
* plugin; a package pin's from the package installed under `packages` (its
|
|
154
|
+
* `node_modules` or a parent's), which must be at the pinned version. Reads
|
|
155
|
+
* data only and imports no package.
|
|
156
|
+
*/
|
|
157
|
+
function pinnedClassesAt(repo, base, pins, packages) {
|
|
158
|
+
const reads = [];
|
|
159
|
+
const unread = [];
|
|
160
|
+
(Array.isArray(pins) ? pins : []).forEach((raw, i) => {
|
|
161
|
+
const pin = (raw !== null && typeof raw === "object" ? raw : {});
|
|
162
|
+
if (typeof pin.path === "string" && pin.path) {
|
|
163
|
+
const dir = posix.normalize(pin.path);
|
|
164
|
+
const files = (path) => showAt(repo, base, dir === "." ? path : posix.join(dir, path));
|
|
165
|
+
reads.push({ pin: i, source: pin.path, read: readClassFiles(files, pin.path, parseClassDataByHand) });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (typeof pin.package !== "string" || !pin.package)
|
|
169
|
+
return;
|
|
170
|
+
const dir = findInstalledPackage(pin.package, packages);
|
|
171
|
+
if (!dir) {
|
|
172
|
+
unread.push(`${pin.package} is not installed under ${packages}`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
let installed;
|
|
176
|
+
try {
|
|
177
|
+
installed = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8")).version;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
installed = undefined;
|
|
181
|
+
}
|
|
182
|
+
if (typeof pin.version === "string" && pin.version && installed !== pin.version) {
|
|
183
|
+
unread.push(`${pin.package} is pinned at ${pin.version}, and ${String(installed ?? "an unknown version")} is installed under ${packages}`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
reads.push({ pin: i, source: pin.package, read: readClassFiles(diskFiles(dir), pin.package, parseClassDataByHand) });
|
|
187
|
+
});
|
|
188
|
+
return { classes: mergePinClasses(reads).classes, unread };
|
|
189
|
+
}
|
|
190
|
+
/** The core classes whose members no domain class can change: every one but `human`, which is the principals in no other class. */
|
|
191
|
+
const PIN_FREE = new Set(CORE_CLASSES.filter((c) => c.role !== null).map((c) => c.name));
|
|
144
192
|
/**
|
|
145
193
|
* The `identity.gates` rule for `gate` at `base`, or null when the
|
|
146
194
|
* declaration there names none: what chant's `gateAdmission(cwd, gate, base)`
|
|
@@ -149,11 +197,19 @@ export function identityGatesAt(repo, base) {
|
|
|
149
197
|
* class, the role grants in `.chant/trust.json` at base put the approver in
|
|
150
198
|
* it. Both judge with the same function ({@link admitApprover}).
|
|
151
199
|
*
|
|
152
|
-
* The classes are the four core ones, then
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
200
|
+
* The classes are the four core ones, then the domain classes the
|
|
201
|
+
* declaration's pins supply, in pin order (#3888), read as chant reads them:
|
|
202
|
+
* a path pin's `./workspace-principals` file at `base`, and a package pin's
|
|
203
|
+
* from the package installed under `packages` (default `repo`) at the
|
|
204
|
+
* pinned version. A bare clone has no `node_modules`, so a caller that reads
|
|
205
|
+
* one passes the directory its packages are installed under. Only data is
|
|
206
|
+
* read; no package is imported. `classes`, when given, stands for every
|
|
207
|
+
* domain class instead and no pin is read.
|
|
208
|
+
*
|
|
209
|
+
* When a pin can't be read (its package is not installed, or not at the
|
|
210
|
+
* pinned version), a gate whose class it could change (a domain class, or
|
|
211
|
+
* `human`) refuses every approval and says which pins were not read; a gate
|
|
212
|
+
* naming `agent`, `runner` or `service`, or no class, is judged as usual.
|
|
157
213
|
* Throws when the declaration at base cannot be parsed.
|
|
158
214
|
*/
|
|
159
215
|
export function identityGateAdmissionAt(repo, base, gate, opts = {}) {
|
|
@@ -163,16 +219,13 @@ export function identityGateAdmissionAt(repo, base, gate, opts = {}) {
|
|
|
163
219
|
return null;
|
|
164
220
|
const { signers, roles } = trustAt(repo, base);
|
|
165
221
|
const pins = declaration.value.pins;
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
? { unknownClass: `is not a core class, and this reader does not load the classes the pinned packages in ${declaration.file} at base supply` }
|
|
174
|
-
: {}),
|
|
175
|
-
};
|
|
222
|
+
const pinned = opts.classes ? { classes: opts.classes, unread: [] } : pinnedClassesAt(repo, base, pins, opts.packages ?? repo);
|
|
223
|
+
const want = requirement.class;
|
|
224
|
+
if (want !== null && !PIN_FREE.has(want) && pinned.unread.length > 0) {
|
|
225
|
+
const reason = `identity.gates.${gate} names the class ${want}, and the pins in ${declaration.file} at base could not all be read, so no approval can show it is in that class: ${pinned.unread.join("; ")}`;
|
|
226
|
+
return { requirement, refuses: () => reason };
|
|
227
|
+
}
|
|
228
|
+
const rule = { gate, class: want, signers, roles, classes: [...CORE_CLASSES, ...pinned.classes] };
|
|
176
229
|
return { requirement, refuses: (a) => admitApprover(rule, a) };
|
|
177
230
|
}
|
|
178
231
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { GateLedgerPort } from "../op/gate.js";
|
|
2
|
-
|
|
2
|
+
import type { LedgerReadPort } from "./ledger-port.js";
|
|
3
|
+
import type { GateApprovalSource } from "../op/op-waves.js";
|
|
4
|
+
export declare const GATE_AUDIT_KINDS: readonly ["approval-requested", "approved", "override-requested", "override-recorded", "revoked", "applied", "override-spent", "hook"];
|
|
3
5
|
export type GateAuditKind = (typeof GATE_AUDIT_KINDS)[number];
|
|
4
6
|
/** How an approval was authorised: a forge review, an ssh seal, or neither. A review wins; a review that is also sealed has `sealed: true` beside it. */
|
|
5
7
|
export type GateAuditSeal = "sealed" | "review" | "unsigned";
|
|
@@ -63,7 +65,7 @@ export interface GateAuditSource {
|
|
|
63
65
|
commit?: string;
|
|
64
66
|
/** The commit that added the line, or for a revocation removed it, when the line came from history. */
|
|
65
67
|
author?: GateLedgerCommit;
|
|
66
|
-
change: "added" | "removed" | "apply";
|
|
68
|
+
change: "added" | "removed" | "apply" | "decision";
|
|
67
69
|
}
|
|
68
70
|
export interface GateAuditEntry {
|
|
69
71
|
/** `sha256:` over where the entry came from: the same sources give the same id. */
|
|
@@ -111,8 +113,27 @@ export interface GateAuditEntry {
|
|
|
111
113
|
name: string;
|
|
112
114
|
op?: string;
|
|
113
115
|
};
|
|
116
|
+
/** Pending facts of a hold (#3886): the hook that holds the wave. */
|
|
117
|
+
heldBy?: {
|
|
118
|
+
hook: string;
|
|
119
|
+
moment: string;
|
|
120
|
+
target?: string;
|
|
121
|
+
};
|
|
122
|
+
/** Pending facts of an op wave (#3861): the approval mode the wave asked under. */
|
|
123
|
+
approvalMode?: GateApprovalSource;
|
|
114
124
|
/** Overrides: the rules the denial named (from the denial line when there is one). */
|
|
115
125
|
rules?: string[];
|
|
126
|
+
/** A hook entry: the hook's moment, name, run, how it came out, and why a moment was skipped (#3810). */
|
|
127
|
+
hook?: {
|
|
128
|
+
moment: string;
|
|
129
|
+
name: string;
|
|
130
|
+
target?: string;
|
|
131
|
+
outcome: "passed" | "failed" | "held" | "skipped";
|
|
132
|
+
exit: number;
|
|
133
|
+
reason?: string;
|
|
134
|
+
};
|
|
135
|
+
/** A hook entry: the applied entry of the same wave and digest, if the caller passed that apply in. */
|
|
136
|
+
applied?: string;
|
|
116
137
|
/** A revoked entry: what was revoked. */
|
|
117
138
|
subject?: "approval" | "override";
|
|
118
139
|
/** An applied entry: the approval entry it ran under, if one is held. An override-spent entry: the override entry. */
|
|
@@ -128,6 +149,40 @@ export interface GateAuditGap {
|
|
|
128
149
|
digest?: string;
|
|
129
150
|
detail: string;
|
|
130
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* An op-wave decision record (`.chant/op-waves/<name>/wave-<k>.json`, written
|
|
154
|
+
* by `chant run wave`), as far as the audit reads it: the wave's gate and
|
|
155
|
+
* digest, and the `outcomes` of its hooks (#3804, #3811, #3847). Typed
|
|
156
|
+
* structurally so this file imports nothing from `../op/`.
|
|
157
|
+
*/
|
|
158
|
+
export interface WaveDecisionRecord {
|
|
159
|
+
wave: number;
|
|
160
|
+
name: string;
|
|
161
|
+
op: string;
|
|
162
|
+
gate: string;
|
|
163
|
+
digest?: string;
|
|
164
|
+
heldBy?: string[];
|
|
165
|
+
heldOn?: unknown;
|
|
166
|
+
outcomes?: ReadonlyArray<{
|
|
167
|
+
moment: string;
|
|
168
|
+
name: string;
|
|
169
|
+
target?: string;
|
|
170
|
+
exit: number;
|
|
171
|
+
outcome: string;
|
|
172
|
+
reason?: string;
|
|
173
|
+
}>;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* A decision record the caller passes in. The record carries no time and no
|
|
177
|
+
* place, so `ref` (a file path, an artifact, a row) and `at` (when it was
|
|
178
|
+
* written) come from the caller. Give the parsed `record`, or its `text`.
|
|
179
|
+
*/
|
|
180
|
+
export interface GateAuditDecision {
|
|
181
|
+
ref: string;
|
|
182
|
+
at: string;
|
|
183
|
+
record?: unknown;
|
|
184
|
+
text?: string;
|
|
185
|
+
}
|
|
131
186
|
export interface GateAuditOptions {
|
|
132
187
|
/** Whether a gate is an override gate. Default: op `policy-override`, or a gate named `override-...`. */
|
|
133
188
|
isOverride?: (op: string, gate: string) => boolean;
|
|
@@ -143,6 +198,8 @@ export interface GateAuditOptions {
|
|
|
143
198
|
export interface GateAuditInput extends GateAuditOptions {
|
|
144
199
|
ledgers: readonly GateAuditLedger[];
|
|
145
200
|
applies?: readonly GateAuditApply[];
|
|
201
|
+
/** Op-wave decision records: each hook outcome in them becomes a `hook` entry linked to the wave's approval and apply (#3810). */
|
|
202
|
+
decisions?: readonly GateAuditDecision[];
|
|
146
203
|
}
|
|
147
204
|
export declare const POLICY_OVERRIDE_OP = "policy-override";
|
|
148
205
|
/** The default {@link GateAuditOptions.isOverride}. */
|
|
@@ -172,4 +229,14 @@ export declare function gateLedgerLinesFromLog(log: string): GateLedgerLine[];
|
|
|
172
229
|
* history through {@link gateLedgerLinesFromLog} for that.
|
|
173
230
|
*/
|
|
174
231
|
export declare function readGateAuditLedgers(port: Pick<GateLedgerPort, "read">, ops: readonly string[]): Promise<GateAuditLedger[]>;
|
|
232
|
+
/**
|
|
233
|
+
* The thin reader for decision records kept on the ledger branch: every
|
|
234
|
+
* `.json` file directly under `dir` (such as a product's `_wave-decisions/<name>`),
|
|
235
|
+
* as input for {@link GateAuditInput.decisions}. A decision record holds no
|
|
236
|
+
* time, so `at` stamps them all: when the product collected them. Where the
|
|
237
|
+
* records live is the product's business; chant writes them to
|
|
238
|
+
* `.chant/op-waves/<name>/wave-<k>.json` in the job, and a product that
|
|
239
|
+
* collects them elsewhere passes them in directly.
|
|
240
|
+
*/
|
|
241
|
+
export declare function readGateAuditDecisions(port: Pick<LedgerReadPort, "read" | "list">, dir: string, at: string): Promise<GateAuditDecision[]>;
|
|
175
242
|
//# sourceMappingURL=gate-audit.d.ts.map
|