@deftai/directive-core 0.88.0 → 0.89.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/authz/actions.d.ts +55 -0
- package/dist/authz/actions.js +125 -0
- package/dist/authz/classify.d.ts +23 -0
- package/dist/authz/classify.js +217 -0
- package/dist/authz/closed-verb.d.ts +42 -0
- package/dist/authz/closed-verb.js +279 -0
- package/dist/authz/evaluate.d.ts +41 -0
- package/dist/authz/evaluate.js +298 -0
- package/dist/authz/index.d.ts +15 -0
- package/dist/authz/index.js +15 -0
- package/dist/authz/origin.d.ts +28 -0
- package/dist/authz/origin.js +64 -0
- package/dist/authz/paths.d.ts +6 -0
- package/dist/authz/paths.js +19 -0
- package/dist/authz/store.d.ts +40 -0
- package/dist/authz/store.js +317 -0
- package/dist/authz/templates.d.ts +139 -0
- package/dist/authz/templates.js +241 -0
- package/dist/authz/types.d.ts +111 -0
- package/dist/authz/types.js +41 -0
- package/dist/authz/verb-classification.d.ts +44 -0
- package/dist/authz/verb-classification.js +237 -0
- package/dist/branch/evaluate.js +6 -1
- package/dist/cache/fetch.js +10 -5
- package/dist/cache/io.d.ts +9 -2
- package/dist/cache/io.js +30 -10
- package/dist/cache/task-cache/store.js +11 -7
- package/dist/capacity/backfill.js +10 -4
- package/dist/category-b-namespace/index.js +10 -4
- package/dist/codebase/default-extractor.js +3 -0
- package/dist/codebase/map.js +11 -4
- package/dist/doctor/doctor-state.js +28 -4
- package/dist/doctor/main.d.ts +10 -0
- package/dist/doctor/main.js +160 -0
- package/dist/doctor/types.d.ts +11 -0
- package/dist/escalation/actions.d.ts +63 -0
- package/dist/escalation/actions.js +137 -0
- package/dist/escalation/index.d.ts +8 -0
- package/dist/escalation/index.js +8 -0
- package/dist/escalation/paths.d.ts +3 -0
- package/dist/escalation/paths.js +10 -0
- package/dist/escalation/store.d.ts +17 -0
- package/dist/escalation/store.js +172 -0
- package/dist/escalation/types.d.ts +73 -0
- package/dist/escalation/types.js +43 -0
- package/dist/eval/crud-telemetry.js +30 -10
- package/dist/eval/health.js +22 -7
- package/dist/eval/readback.js +11 -10
- package/dist/eval/run.js +32 -16
- package/dist/events/attribution-enrichment.js +10 -4
- package/dist/finish-loop/directive-finish-loop.d.ts +37 -0
- package/dist/finish-loop/directive-finish-loop.js +239 -0
- package/dist/finish-loop/grant-gate.d.ts +31 -0
- package/dist/finish-loop/grant-gate.js +169 -0
- package/dist/finish-loop/index.d.ts +11 -0
- package/dist/finish-loop/index.js +11 -0
- package/dist/finish-loop/main.d.ts +35 -0
- package/dist/finish-loop/main.js +365 -0
- package/dist/finish-loop/pr-finish-loop.d.ts +34 -0
- package/dist/finish-loop/pr-finish-loop.js +217 -0
- package/dist/finish-loop/progress.d.ts +17 -0
- package/dist/finish-loop/progress.js +45 -0
- package/dist/finish-loop/queue.d.ts +16 -0
- package/dist/finish-loop/queue.js +65 -0
- package/dist/finish-loop/types.d.ts +52 -0
- package/dist/finish-loop/types.js +10 -0
- package/dist/fs/contained-write.d.ts +101 -0
- package/dist/fs/contained-write.js +281 -0
- package/dist/hooks/classify/classify.d.ts +10 -0
- package/dist/hooks/classify/classify.js +37 -0
- package/dist/hooks/classify/index.d.ts +14 -0
- package/dist/hooks/classify/index.js +13 -0
- package/dist/hooks/classify/paths.d.ts +22 -0
- package/dist/hooks/classify/paths.js +72 -0
- package/dist/hooks/classify/payload.d.ts +16 -0
- package/dist/hooks/classify/payload.js +45 -0
- package/dist/hooks/classify/stdin.d.ts +12 -0
- package/dist/hooks/classify/stdin.js +63 -0
- package/dist/hooks/classify/tool-name.d.ts +18 -0
- package/dist/hooks/classify/tool-name.js +85 -0
- package/dist/hooks/classify/types.d.ts +41 -0
- package/dist/hooks/classify/types.js +7 -0
- package/dist/hooks/dispatcher.d.ts +21 -18
- package/dist/hooks/dispatcher.js +212 -163
- package/dist/hooks/fixtures/cases.d.ts +44 -0
- package/dist/hooks/fixtures/cases.js +595 -0
- package/dist/hooks/fixtures/index.d.ts +2 -0
- package/dist/hooks/fixtures/index.js +2 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/init-deposit/agent-hooks.js +27 -8
- package/dist/init-deposit/gitignore.js +11 -3
- package/dist/init-deposit/headless-manifest.js +12 -4
- package/dist/init-deposit/migrate.d.ts +1 -1
- package/dist/init-deposit/migrate.js +13 -3
- package/dist/init-deposit/prettierignore.js +10 -3
- package/dist/init-deposit/scaffold.js +35 -26
- package/dist/init-deposit/xbrief-projections.js +11 -4
- package/dist/intake/candidates-log.js +46 -35
- package/dist/intake/github-body-cli.d.ts +6 -0
- package/dist/intake/github-body-cli.js +17 -0
- package/dist/intake/github-body.d.ts +46 -3
- package/dist/intake/github-body.js +191 -20
- package/dist/intake/issue-emit.d.ts +1 -0
- package/dist/intake/issue-emit.js +41 -23
- package/dist/lifecycle/event-detect.js +12 -4
- package/dist/lifecycle/events.js +29 -20
- package/dist/lifecycle/lifecycle-hygiene.js +9 -3
- package/dist/packs/pack-render.js +19 -6
- package/dist/plan-sequence/store.js +11 -4
- package/dist/policy/hotfix-criteria.d.ts +79 -0
- package/dist/policy/hotfix-criteria.js +197 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.js +31 -1
- package/dist/policy/intent-ceiling.d.ts +79 -0
- package/dist/policy/intent-ceiling.js +181 -0
- package/dist/policy/no-deft-directive.js +10 -3
- package/dist/policy/org-force-on-migration.js +9 -5
- package/dist/policy/require-human-merge.d.ts +75 -0
- package/dist/policy/require-human-merge.js +324 -0
- package/dist/policy/resolve.js +17 -6
- package/dist/policy/runtime-authority.d.ts +15 -2
- package/dist/policy/runtime-authority.js +23 -3
- package/dist/policy/write-fence.d.ts +78 -0
- package/dist/policy/write-fence.js +164 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +10 -0
- package/dist/pr-wait-mergeable/cascade.js +28 -0
- package/dist/preflight/evaluate.js +10 -0
- package/dist/product-signal/consent.js +21 -5
- package/dist/product-signal/submit.js +22 -12
- package/dist/release/build-dist-runner.js +5 -2
- package/dist/release/build-dist.d.ts +19 -1
- package/dist/release/build-dist.js +50 -2
- package/dist/release/native-steps.js +7 -2
- package/dist/release-publish/pipeline.d.ts +13 -0
- package/dist/release-publish/pipeline.js +46 -1
- package/dist/scope/audit-log.js +19 -24
- package/dist/scope/decompose.js +10 -5
- package/dist/scope/decomposed-refs.js +18 -3
- package/dist/scope/demote.js +9 -2
- package/dist/scope/undo.js +9 -2
- package/dist/session/release-availability.js +26 -6
- package/dist/session/ritual-sentinel.js +19 -12
- package/dist/session/session-start.js +7 -0
- package/dist/staleness-tickler/state.js +26 -6
- package/dist/swarm/launch.js +13 -3
- package/dist/swarm/routing.js +9 -3
- package/dist/task-surface/index.js +24 -9
- package/dist/triage/bootstrap/gitignore.js +53 -10
- package/dist/triage/cache-path.js +10 -3
- package/dist/triage/welcome/summary.js +11 -4
- package/dist/triage/welcome/writers.js +45 -16
- package/dist/validate-content/validate-links.js +5 -0
- package/dist/value/readback.js +11 -6
- package/dist/vbrief-activate/activate.js +12 -4
- package/dist/vbrief-build/project-definition-io.js +14 -6
- package/dist/verify-source/contained-writes.d.ts +59 -0
- package/dist/verify-source/contained-writes.js +272 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/verify-source/openclaw-tier1.js +14 -1
- package/dist/verify-source/verify-stubs.js +3 -0
- package/dist/xbrief-migrate/migrate-project.js +26 -12
- package/dist/xbrief-migrate/transforms.d.ts +4 -2
- package/dist/xbrief-migrate/transforms.js +37 -14
- package/package.json +15 -3
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closed-verb evaluator for release-class high-blast verbs (#1095 Wave 4 / #2948).
|
|
3
|
+
*
|
|
4
|
+
* Allow only when:
|
|
5
|
+
* (a) DEFT_ALLOW_<VERB>=1 ephemeral env bypass, OR
|
|
6
|
+
* (b) a matching human-origin grant whose operations cover the verb and
|
|
7
|
+
* surfaces cover the target (empty surfaces = any target).
|
|
8
|
+
*
|
|
9
|
+
* Agent-authored / rejected-origin grants never satisfy. Does not invent a
|
|
10
|
+
* second mint path — grants come from Wave 1 mintHumanOriginGrant / authz:grant.
|
|
11
|
+
*/
|
|
12
|
+
import { evidenceSatisfiesImplementationApproval, isHumanOriginGrant, isRejectedOriginKind, } from "./origin.js";
|
|
13
|
+
import { builtinReleaseVerbClassification, getVerbRow, loadVerbClassification, } from "./verb-classification.js";
|
|
14
|
+
function sanitize(text) {
|
|
15
|
+
return text.replace(/[\r\n]+/g, " ");
|
|
16
|
+
}
|
|
17
|
+
function deny(code, reason, input, extra = {}) {
|
|
18
|
+
return {
|
|
19
|
+
allowed: false,
|
|
20
|
+
code,
|
|
21
|
+
reason: sanitize(reason),
|
|
22
|
+
verb: sanitize(input.verb),
|
|
23
|
+
target: input.target === null ? null : sanitize(input.target),
|
|
24
|
+
humanApprovalRef: extra.humanApprovalRef ?? null,
|
|
25
|
+
envBypassKey: extra.envBypassKey ?? null,
|
|
26
|
+
skillPointer: extra.skillPointer ?? null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function allow(code, reason, input, extra = {}) {
|
|
30
|
+
return {
|
|
31
|
+
allowed: true,
|
|
32
|
+
code,
|
|
33
|
+
reason: sanitize(reason),
|
|
34
|
+
verb: sanitize(input.verb),
|
|
35
|
+
target: input.target === null ? null : sanitize(input.target),
|
|
36
|
+
humanApprovalRef: extra.humanApprovalRef ?? null,
|
|
37
|
+
envBypassKey: extra.envBypassKey ?? null,
|
|
38
|
+
skillPointer: extra.skillPointer ?? null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Normalise version/target for surface matching (strip leading v, lower-case). */
|
|
42
|
+
export function normaliseClosedVerbTarget(target) {
|
|
43
|
+
if (target === null || target === undefined)
|
|
44
|
+
return null;
|
|
45
|
+
let t = target.trim();
|
|
46
|
+
if (t.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
if (t.startsWith("v") || t.startsWith("V"))
|
|
49
|
+
t = t.slice(1);
|
|
50
|
+
return t.toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
/** Surface forms that match a target (version, v-version, raw). */
|
|
53
|
+
export function targetSurfaceCandidates(target) {
|
|
54
|
+
if (target === null)
|
|
55
|
+
return [];
|
|
56
|
+
const raw = target.trim();
|
|
57
|
+
const norm = normaliseClosedVerbTarget(target);
|
|
58
|
+
const out = new Set();
|
|
59
|
+
if (raw.length > 0)
|
|
60
|
+
out.add(raw);
|
|
61
|
+
if (norm !== null) {
|
|
62
|
+
out.add(norm);
|
|
63
|
+
out.add(`v${norm}`);
|
|
64
|
+
}
|
|
65
|
+
return [...out];
|
|
66
|
+
}
|
|
67
|
+
function envTruthy(value) {
|
|
68
|
+
if (value === undefined)
|
|
69
|
+
return false;
|
|
70
|
+
const v = value.trim().toLowerCase();
|
|
71
|
+
return v === "1" || v === "true" || v === "yes";
|
|
72
|
+
}
|
|
73
|
+
function grantCoversOperation(grant, acceptedOps) {
|
|
74
|
+
const want = new Set(acceptedOps.map((o) => o.toLowerCase()));
|
|
75
|
+
return grant.scope.operations.some((op) => want.has(op.toLowerCase()));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Target match: empty surfaces = unrestricted targets for this closed verb.
|
|
79
|
+
* Non-empty: any surface must equal a candidate form of the target (exact).
|
|
80
|
+
* Wildcard `*` is rejected for release-class (wildcard_allowed=false) unless
|
|
81
|
+
* the classification row allows it (Wave 4 rows do not).
|
|
82
|
+
*/
|
|
83
|
+
function grantCoversTarget(grant, target, wildcardAllowed) {
|
|
84
|
+
if (grant.scope.surfaces.length === 0)
|
|
85
|
+
return true;
|
|
86
|
+
if (target === null)
|
|
87
|
+
return false;
|
|
88
|
+
const candidates = new Set(targetSurfaceCandidates(target).map((c) => c.toLowerCase()));
|
|
89
|
+
for (const surface of grant.scope.surfaces) {
|
|
90
|
+
const s = surface.trim();
|
|
91
|
+
if (s === "*") {
|
|
92
|
+
if (wildcardAllowed)
|
|
93
|
+
return true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (candidates.has(s.toLowerCase()))
|
|
97
|
+
return true;
|
|
98
|
+
const sn = normaliseClosedVerbTarget(s);
|
|
99
|
+
if (sn !== null && candidates.has(sn))
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** When a grant pins repo/branch, the attempt must supply a matching value. */
|
|
105
|
+
function grantContextMatches(grant, input) {
|
|
106
|
+
const s = grant.scope;
|
|
107
|
+
if (s.repo !== null) {
|
|
108
|
+
if (input.repo === null || input.repo === undefined)
|
|
109
|
+
return false;
|
|
110
|
+
if (s.repo.toLowerCase() !== input.repo.toLowerCase())
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (s.branch !== null) {
|
|
114
|
+
if (input.branch === null || input.branch === undefined)
|
|
115
|
+
return false;
|
|
116
|
+
if (s.branch !== input.branch)
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
function grantValidity(grant, now) {
|
|
122
|
+
if (!isHumanOriginGrant(grant)) {
|
|
123
|
+
const kind = grant.origin.kind;
|
|
124
|
+
if (isRejectedOriginKind(kind)) {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
code: "closed-verb-deny-origin",
|
|
128
|
+
reason: `Directive denied closed verb: grant ${grant.id} origin.kind=${kind} is ` +
|
|
129
|
+
"agent/self-authored and cannot authorize release-class verbs. " +
|
|
130
|
+
"Human action required: `deft authz:grant -- --template <release-*> --target <ver>`.",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
code: "closed-verb-deny-origin",
|
|
136
|
+
reason: `Directive denied closed verb: grant ${grant.id} lacks human-origin provenance. ` +
|
|
137
|
+
"Human action required: mint via `deft authz:grant` (operator-cli).",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (grant.semantics.revokedAt !== null) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
code: "closed-verb-deny-revoked",
|
|
144
|
+
reason: `Directive denied closed verb: grant ${grant.id} was revoked at ${grant.semantics.revokedAt}.`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (grant.semantics.singleUse && grant.semantics.usedAt !== null) {
|
|
148
|
+
return {
|
|
149
|
+
ok: false,
|
|
150
|
+
code: "closed-verb-deny-spent",
|
|
151
|
+
reason: `Directive denied closed verb: single-use grant ${grant.id} already spent at ${grant.semantics.usedAt}.`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (grant.semantics.expiresAt !== null) {
|
|
155
|
+
const exp = Date.parse(grant.semantics.expiresAt);
|
|
156
|
+
// Fail closed on unparseable expiresAt (malformed must not count as forever-valid).
|
|
157
|
+
if (Number.isNaN(exp) || exp <= now.getTime()) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
code: "closed-verb-deny-expired",
|
|
161
|
+
reason: `Directive denied closed verb: grant ${grant.id} expired or has unparseable expiresAt ` +
|
|
162
|
+
`(${grant.semantics.expiresAt}). Human action required: mint a fresh grant via \`deft authz:grant\`.`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { ok: true };
|
|
167
|
+
}
|
|
168
|
+
function resolveClassification(input) {
|
|
169
|
+
if (input.classification !== undefined)
|
|
170
|
+
return input.classification;
|
|
171
|
+
if (input.projectRoot !== null &&
|
|
172
|
+
input.projectRoot !== undefined &&
|
|
173
|
+
input.projectRoot.length > 0) {
|
|
174
|
+
return loadVerbClassification(input.projectRoot);
|
|
175
|
+
}
|
|
176
|
+
return builtinReleaseVerbClassification();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Closed-verb gate. Fail closed for unknown verbs and missing grants.
|
|
180
|
+
* Loads conventions/verb-classification.json when projectRoot is provided.
|
|
181
|
+
*/
|
|
182
|
+
export function evaluateClosedVerb(input) {
|
|
183
|
+
const table = resolveClassification(input);
|
|
184
|
+
const verbKey = sanitize(input.verb.trim().toLowerCase());
|
|
185
|
+
const row = getVerbRow(table, verbKey);
|
|
186
|
+
if (row === null) {
|
|
187
|
+
return deny("closed-verb-unknown", `Directive denied closed verb: unknown verb '${input.verb}'. ` +
|
|
188
|
+
"Known Wave 4 release-class verbs: release-cut, release-publish, release-rollback.", input);
|
|
189
|
+
}
|
|
190
|
+
const skillPointer = sanitize(`${row.skill} ${row.phase}`);
|
|
191
|
+
const envBypassKey = row.env_bypass;
|
|
192
|
+
const env = input.env ?? process.env;
|
|
193
|
+
if (envTruthy(env[envBypassKey])) {
|
|
194
|
+
return allow("closed-verb-env-bypass", `Directive allowed ${verbKey} via ephemeral env bypass ${envBypassKey}=1 ` +
|
|
195
|
+
`(precondition: ${skillPointer}).`, input, { envBypassKey, skillPointer });
|
|
196
|
+
}
|
|
197
|
+
const now = input.now ?? new Date();
|
|
198
|
+
let lastReject = null;
|
|
199
|
+
for (const grant of input.grants) {
|
|
200
|
+
// evidenceSatisfiesImplementationApproval and grantValidity both reject
|
|
201
|
+
// non-human origins; run full validity (origin + expiry + single-use + revoked).
|
|
202
|
+
if (!evidenceSatisfiesImplementationApproval({ grant })) {
|
|
203
|
+
const validity = grantValidity(grant, now);
|
|
204
|
+
lastReject = validity.ok
|
|
205
|
+
? {
|
|
206
|
+
code: "closed-verb-deny-origin",
|
|
207
|
+
reason: `Directive denied closed verb: grant ${grant.id} does not satisfy human-origin ` +
|
|
208
|
+
"implementation approval (self-authored lifecycle/dispatch tokens never count).",
|
|
209
|
+
id: grant.id,
|
|
210
|
+
}
|
|
211
|
+
: { code: validity.code, reason: validity.reason, id: grant.id };
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const validity = grantValidity(grant, now);
|
|
215
|
+
if (!validity.ok) {
|
|
216
|
+
lastReject = { code: validity.code, reason: validity.reason, id: grant.id };
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (!grantCoversOperation(grant, row.authz_operations)) {
|
|
220
|
+
lastReject = {
|
|
221
|
+
code: "closed-verb-deny-scope",
|
|
222
|
+
reason: `Directive denied closed verb: grant ${grant.id} operations ` +
|
|
223
|
+
`[${grant.scope.operations.join(",")}] do not cover '${verbKey}' ` +
|
|
224
|
+
`(need one of: ${row.authz_operations.join(", ")}). ` +
|
|
225
|
+
`Human action required: \`deft authz:grant -- --template ${verbKey} --target <ver>\`.`,
|
|
226
|
+
id: grant.id,
|
|
227
|
+
};
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (!grantCoversTarget(grant, input.target, row.wildcard_allowed)) {
|
|
231
|
+
lastReject = {
|
|
232
|
+
code: "closed-verb-deny-scope",
|
|
233
|
+
reason: `Directive denied closed verb: grant ${grant.id} surfaces ` +
|
|
234
|
+
`[${grant.scope.surfaces.join("|") || "*"}] do not cover target ` +
|
|
235
|
+
`'${input.target ?? "(none)"}'. Human action required: remint with ` +
|
|
236
|
+
`\`--template ${verbKey} --target ${input.target ?? "<ver>"}\`.`,
|
|
237
|
+
id: grant.id,
|
|
238
|
+
};
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (!grantContextMatches(grant, input)) {
|
|
242
|
+
lastReject = {
|
|
243
|
+
code: "closed-verb-deny-scope",
|
|
244
|
+
reason: `Directive denied closed verb: grant ${grant.id} is bound to a different ` +
|
|
245
|
+
"repo/branch context than the attempted operation.",
|
|
246
|
+
id: grant.id,
|
|
247
|
+
};
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
return allow("closed-verb-allow", `Directive allowed ${verbKey}` +
|
|
251
|
+
(input.target !== null ? ` target=${input.target}` : "") +
|
|
252
|
+
` via human-origin grant ${grant.id} (precondition: ${skillPointer}).`, input, { humanApprovalRef: grant.id, skillPointer });
|
|
253
|
+
}
|
|
254
|
+
if (lastReject !== null) {
|
|
255
|
+
return deny(lastReject.code, lastReject.reason, input, {
|
|
256
|
+
humanApprovalRef: lastReject.id ?? null,
|
|
257
|
+
envBypassKey,
|
|
258
|
+
skillPointer,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return deny("closed-verb-deny-missing", `Directive denied closed verb '${verbKey}'` +
|
|
262
|
+
(input.target !== null ? ` target=${input.target}` : "") +
|
|
263
|
+
`: no human-origin grant covers this verb/target and ${envBypassKey} is not set. ` +
|
|
264
|
+
`Human action required: \`deft authz:grant -- --template ${verbKey}` +
|
|
265
|
+
(input.target !== null ? ` --target ${input.target}` : " --target <ver>") +
|
|
266
|
+
`\` or set ${envBypassKey}=1 for a single-shell ephemeral bypass. ` +
|
|
267
|
+
`Skill precondition: ${skillPointer}. ` +
|
|
268
|
+
"Note: walk-away finish-loop uses `authz:grant --template finish-loop` (#871), " +
|
|
269
|
+
"not this release-class closed-verb template.", input, { envBypassKey, skillPointer });
|
|
270
|
+
}
|
|
271
|
+
/** Map verb name → DEFT_ALLOW_* key (from loaded/builtin table). */
|
|
272
|
+
export function closedVerbEnvBypassKey(verb, projectRoot) {
|
|
273
|
+
const table = projectRoot !== null && projectRoot !== undefined && projectRoot.length > 0
|
|
274
|
+
? loadVerbClassification(projectRoot)
|
|
275
|
+
: builtinReleaseVerbClassification();
|
|
276
|
+
const row = getVerbRow(table, verb);
|
|
277
|
+
return row?.env_bypass ?? null;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=closed-verb.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authz mutation evaluation: UAT lease + human-origin grant gates (#2944).
|
|
3
|
+
*
|
|
4
|
+
* Composes with runtimeAuthority (#1394 / #2711): this layer answers "did a
|
|
5
|
+
* human authorize this cohort/op/surface?" — not path capability policy.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuthzDecision, AuthzOperation, AuthzState, GrantScope, HumanOriginGrant } from "./types.js";
|
|
8
|
+
/** Paths that remain writable under UAT without a fix-cohort grant (evidence / defect capture). */
|
|
9
|
+
export declare const UAT_SAFE_WRITE_GLOBS: readonly string[];
|
|
10
|
+
export interface EvaluateAuthzInput {
|
|
11
|
+
readonly state: AuthzState;
|
|
12
|
+
readonly grants: readonly HumanOriginGrant[];
|
|
13
|
+
readonly op: AuthzOperation | "test" | "evidence" | "unknown";
|
|
14
|
+
/** Project-relative POSIX path for edit ops; null when unclassifiable. */
|
|
15
|
+
readonly path: string | null;
|
|
16
|
+
readonly now?: Date;
|
|
17
|
+
/** Optional structural context the grant may bind to. */
|
|
18
|
+
readonly repo?: string | null;
|
|
19
|
+
readonly branch?: string | null;
|
|
20
|
+
readonly worktree?: string | null;
|
|
21
|
+
readonly planRef?: string | null;
|
|
22
|
+
readonly storyIds?: readonly string[];
|
|
23
|
+
readonly issueIds?: readonly number[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Evaluate a mutation under UAT lease + human-origin grants.
|
|
27
|
+
*
|
|
28
|
+
* When UAT is inactive, returns allow/authz-inactive (no new denials) so Wave 1
|
|
29
|
+
* does not break non-UAT workflows. When UAT is active, product mutations fail
|
|
30
|
+
* closed unless a named fix-cohort human-origin grant covers the op/surface.
|
|
31
|
+
* Test / evidence / issue_mutation ops stay allowed under UAT without a grant.
|
|
32
|
+
*/
|
|
33
|
+
export declare function evaluateAuthzMutation(input: EvaluateAuthzInput): AuthzDecision;
|
|
34
|
+
/**
|
|
35
|
+
* Whether a decision should mark a single-use grant spent after allow.
|
|
36
|
+
* Callers (dispatcher) persist via markGrantUsed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function shouldConsumeSingleUseGrant(decision: AuthzDecision): boolean;
|
|
39
|
+
/** Snapshot of approved scope for audit/deny messages. */
|
|
40
|
+
export declare function describeScope(scope: GrantScope | null): string;
|
|
41
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authz mutation evaluation: UAT lease + human-origin grant gates (#2944).
|
|
3
|
+
*
|
|
4
|
+
* Composes with runtimeAuthority (#1394 / #2711): this layer answers "did a
|
|
5
|
+
* human authorize this cohort/op/surface?" — not path capability policy.
|
|
6
|
+
*/
|
|
7
|
+
import { matchAny } from "../orchestration/pathspec.js";
|
|
8
|
+
import { evidenceSatisfiesImplementationApproval, isHumanOrigin, isHumanOriginGrant, isRejectedOriginKind, } from "./origin.js";
|
|
9
|
+
/** Paths that remain writable under UAT without a fix-cohort grant (evidence / defect capture). */
|
|
10
|
+
export const UAT_SAFE_WRITE_GLOBS = [
|
|
11
|
+
"xbrief/proposed/**",
|
|
12
|
+
"vbrief/proposed/**",
|
|
13
|
+
"**/evidence/**",
|
|
14
|
+
"**/uat-evidence/**",
|
|
15
|
+
"incidents/**",
|
|
16
|
+
];
|
|
17
|
+
function deny(code, reason, input, grant = null) {
|
|
18
|
+
return {
|
|
19
|
+
allowed: false,
|
|
20
|
+
code,
|
|
21
|
+
reason,
|
|
22
|
+
humanApprovalRef: grant?.id ?? null,
|
|
23
|
+
approvedScope: grant?.scope ?? null,
|
|
24
|
+
attemptedOp: input.op,
|
|
25
|
+
path: input.path,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function allow(code, reason, input, grant = null) {
|
|
29
|
+
return {
|
|
30
|
+
allowed: true,
|
|
31
|
+
code,
|
|
32
|
+
reason,
|
|
33
|
+
humanApprovalRef: grant?.id ?? null,
|
|
34
|
+
approvedScope: grant?.scope ?? null,
|
|
35
|
+
attemptedOp: input.op,
|
|
36
|
+
path: input.path,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function activeUat(state) {
|
|
40
|
+
const uat = state.uat;
|
|
41
|
+
if (uat === null || !uat.active)
|
|
42
|
+
return null;
|
|
43
|
+
// startedBy must itself be human-origin — agent-minted UAT start is ignored.
|
|
44
|
+
if (!isHumanOrigin(uat.startedBy))
|
|
45
|
+
return null;
|
|
46
|
+
return uat;
|
|
47
|
+
}
|
|
48
|
+
function isUatSafeWritePath(path) {
|
|
49
|
+
if (path === null)
|
|
50
|
+
return false;
|
|
51
|
+
return matchAny(UAT_SAFE_WRITE_GLOBS, path);
|
|
52
|
+
}
|
|
53
|
+
function grantCoversOp(grant, op) {
|
|
54
|
+
return grant.scope.operations.includes(op);
|
|
55
|
+
}
|
|
56
|
+
function grantCoversSurface(grant, path) {
|
|
57
|
+
if (path === null) {
|
|
58
|
+
// Unclassifiable path: require empty surfaces (unrestricted) to allow.
|
|
59
|
+
return grant.scope.surfaces.length === 0;
|
|
60
|
+
}
|
|
61
|
+
if (grant.scope.surfaces.length === 0)
|
|
62
|
+
return true;
|
|
63
|
+
return matchAny(grant.scope.surfaces, path);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Structural context binding: when a grant pins a field, the attempt must supply
|
|
67
|
+
* a matching value. Missing attempt context does NOT skip the check (fail closed).
|
|
68
|
+
*/
|
|
69
|
+
function grantContextMatches(grant, input) {
|
|
70
|
+
const s = grant.scope;
|
|
71
|
+
if (s.repo !== null) {
|
|
72
|
+
if (input.repo === null || input.repo === undefined)
|
|
73
|
+
return false;
|
|
74
|
+
if (s.repo.toLowerCase() !== input.repo.toLowerCase())
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (s.branch !== null) {
|
|
78
|
+
if (input.branch === null || input.branch === undefined)
|
|
79
|
+
return false;
|
|
80
|
+
if (s.branch !== input.branch)
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (s.worktree !== null) {
|
|
84
|
+
if (input.worktree === null || input.worktree === undefined)
|
|
85
|
+
return false;
|
|
86
|
+
if (s.worktree !== input.worktree)
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (s.planRef !== null) {
|
|
90
|
+
if (input.planRef === null || input.planRef === undefined)
|
|
91
|
+
return false;
|
|
92
|
+
if (s.planRef !== input.planRef)
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (s.storyIds.length > 0) {
|
|
96
|
+
const attempt = input.storyIds ?? [];
|
|
97
|
+
if (attempt.length === 0)
|
|
98
|
+
return false;
|
|
99
|
+
const want = new Set(s.storyIds);
|
|
100
|
+
if (!attempt.some((id) => want.has(id)))
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
if (s.issueIds.length > 0) {
|
|
104
|
+
const attempt = input.issueIds ?? [];
|
|
105
|
+
if (attempt.length === 0)
|
|
106
|
+
return false;
|
|
107
|
+
const want = new Set(s.issueIds);
|
|
108
|
+
if (!attempt.some((id) => want.has(id)))
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function grantValidity(grant, now) {
|
|
114
|
+
if (!isHumanOriginGrant(grant)) {
|
|
115
|
+
const kind = grant.origin.kind;
|
|
116
|
+
if (isRejectedOriginKind(kind)) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
code: "authz-grant-origin-reject",
|
|
120
|
+
reason: `Directive denied this mutation: grant ${grant.id} origin.kind=${kind} is ` +
|
|
121
|
+
"agent/self-authored and cannot satisfy an implementation-approval gate. " +
|
|
122
|
+
"Human action required: run `deft authz:grant` (operator-cli) for the approved cohort.",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
code: "authz-grant-origin-reject",
|
|
128
|
+
reason: `Directive denied this mutation: grant ${grant.id} lacks human-origin provenance. ` +
|
|
129
|
+
"Human action required: mint a grant via `deft authz:grant` with operator-cli origin.",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (grant.semantics.revokedAt !== null) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
code: "authz-grant-revoked",
|
|
136
|
+
reason: `Directive denied this mutation: grant ${grant.id} was revoked at ${grant.semantics.revokedAt}.`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (grant.semantics.singleUse && grant.semantics.usedAt !== null) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
code: "authz-grant-single-use-spent",
|
|
143
|
+
reason: `Directive denied this mutation: single-use grant ${grant.id} already spent at ${grant.semantics.usedAt}.`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (grant.semantics.expiresAt !== null) {
|
|
147
|
+
const exp = Date.parse(grant.semantics.expiresAt);
|
|
148
|
+
if (!Number.isNaN(exp) && exp <= now.getTime()) {
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
code: "authz-grant-expired",
|
|
152
|
+
reason: `Directive denied this mutation: grant ${grant.id} expired at ${grant.semantics.expiresAt}. ` +
|
|
153
|
+
"Human action required: mint a fresh grant via `deft authz:grant`.",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { ok: true };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Find a live human-origin grant that structurally covers the attempted op/surface.
|
|
161
|
+
* Returns the first matching grant, or the best rejection reason if none match.
|
|
162
|
+
*/
|
|
163
|
+
function findCoveringGrant(input, op) {
|
|
164
|
+
const now = input.now ?? new Date();
|
|
165
|
+
let lastReject = null;
|
|
166
|
+
for (const grant of input.grants) {
|
|
167
|
+
// Production path: only human-origin grants satisfy implementation approval (#2944).
|
|
168
|
+
if (!evidenceSatisfiesImplementationApproval({ grant })) {
|
|
169
|
+
const validity = grantValidity(grant, now);
|
|
170
|
+
if (!validity.ok) {
|
|
171
|
+
lastReject = { code: validity.code, reason: validity.reason };
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
lastReject = {
|
|
175
|
+
code: "authz-grant-origin-reject",
|
|
176
|
+
reason: `Directive denied this mutation: grant ${grant.id} does not satisfy human-origin ` +
|
|
177
|
+
"implementation approval (self-authored lifecycle/dispatch tokens never count). " +
|
|
178
|
+
"Human action required: run `deft authz:grant`.",
|
|
179
|
+
};
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const validity = grantValidity(grant, now);
|
|
183
|
+
if (!validity.ok) {
|
|
184
|
+
lastReject = { code: validity.code, reason: validity.reason };
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// Under active UAT, product mutations require a named fix cohort.
|
|
188
|
+
if (grant.scope.cohortId === null || grant.scope.cohortId.length === 0) {
|
|
189
|
+
lastReject = {
|
|
190
|
+
code: "authz-grant-scope-deny",
|
|
191
|
+
reason: `Directive denied this mutation under active UAT: grant ${grant.id} has no named ` +
|
|
192
|
+
"fix cohort (cohortId). Approving product changes during UAT requires a named " +
|
|
193
|
+
"cohort via `deft authz:grant -- --cohort <id> ...`.",
|
|
194
|
+
};
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!grantCoversOp(grant, op)) {
|
|
198
|
+
lastReject = {
|
|
199
|
+
code: "authz-grant-scope-deny",
|
|
200
|
+
reason: `Directive denied this mutation: grant ${grant.id} does not include operation ` +
|
|
201
|
+
`'${String(op).replace(/[\r\n]/g, " ")}'. Human action required: mint/extend a grant ` +
|
|
202
|
+
`with operations including ${String(op).replace(/[\r\n]/g, " ")} ` +
|
|
203
|
+
`(\`deft authz:grant -- --operations ${String(op).replace(/[\r\n]/g, " ")},...\`).`,
|
|
204
|
+
};
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (op === "edit" && !grantCoversSurface(grant, input.path)) {
|
|
208
|
+
lastReject = {
|
|
209
|
+
code: "authz-grant-scope-deny",
|
|
210
|
+
reason: `Directive denied this mutation: path ${input.path ?? "(unknown)"} is outside ` +
|
|
211
|
+
`grant ${grant.id} surfaces. Human action required: include the surface in the ` +
|
|
212
|
+
"approved grant (`deft authz:grant -- --surfaces <glob>`).",
|
|
213
|
+
};
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (!grantContextMatches(grant, input)) {
|
|
217
|
+
lastReject = {
|
|
218
|
+
code: "authz-grant-scope-deny",
|
|
219
|
+
reason: `Directive denied this mutation: grant ${grant.id} is bound to a different ` +
|
|
220
|
+
"repo/branch/plan/story context than the attempted operation.",
|
|
221
|
+
};
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
return { grant };
|
|
225
|
+
}
|
|
226
|
+
if (lastReject !== null)
|
|
227
|
+
return { grant: null, ...lastReject };
|
|
228
|
+
return {
|
|
229
|
+
grant: null,
|
|
230
|
+
code: "authz-grant-missing",
|
|
231
|
+
reason: "Directive denied this mutation: no human-origin approval grant covers the attempted " +
|
|
232
|
+
`operation '${op}'. Human action required: run \`deft authz:grant\` for the approved ` +
|
|
233
|
+
"cohort (self-authored xBRIEF/lifecycle/dispatch tokens do not count).",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Evaluate a mutation under UAT lease + human-origin grants.
|
|
238
|
+
*
|
|
239
|
+
* When UAT is inactive, returns allow/authz-inactive (no new denials) so Wave 1
|
|
240
|
+
* does not break non-UAT workflows. When UAT is active, product mutations fail
|
|
241
|
+
* closed unless a named fix-cohort human-origin grant covers the op/surface.
|
|
242
|
+
* Test / evidence / issue_mutation ops stay allowed under UAT without a grant.
|
|
243
|
+
*/
|
|
244
|
+
export function evaluateAuthzMutation(input) {
|
|
245
|
+
const uat = activeUat(input.state);
|
|
246
|
+
// Always-allowed under UAT (and outside UAT): tests, evidence, issue filing.
|
|
247
|
+
if (input.op === "test" || input.op === "evidence" || input.op === "issue_mutation") {
|
|
248
|
+
return allow(uat !== null ? "authz-allow" : "authz-inactive", uat !== null
|
|
249
|
+
? `Directive allowed ${input.op} under active UAT campaign ${uat.campaignId}.`
|
|
250
|
+
: `Directive authz inactive; ${input.op} unrestricted by Wave 1 gates.`, input);
|
|
251
|
+
}
|
|
252
|
+
if (uat === null) {
|
|
253
|
+
// Outside UAT: Wave 1 does not require grants for every edit (that would
|
|
254
|
+
// break normal implement sessions). Self-authored evidence still never
|
|
255
|
+
// counts if a caller asks isHumanOriginGrant / evidenceSatisfies… —
|
|
256
|
+
// the hard fail-closed posture is the active UAT lease.
|
|
257
|
+
return allow("authz-inactive", "Directive authz UAT lease inactive; mutation not gated by Wave 1 UAT fail-closed path.", input);
|
|
258
|
+
}
|
|
259
|
+
// Active UAT: product mutations require a named fix-cohort human-origin grant.
|
|
260
|
+
if (input.op === "unknown") {
|
|
261
|
+
return deny("authz-uat-deny", `Directive denied unclassifiable mutation under active UAT campaign ${uat.campaignId}. ` +
|
|
262
|
+
"Fail closed: classify the operation or suspend UAT (`deft authz:uat-suspend`) / " +
|
|
263
|
+
"mint a named fix cohort (`deft authz:grant -- --cohort <id>`).", input);
|
|
264
|
+
}
|
|
265
|
+
// Safe write paths (defect capture / evidence) stay open without a cohort grant.
|
|
266
|
+
if (input.op === "edit" && isUatSafeWritePath(input.path)) {
|
|
267
|
+
return allow("authz-allow", `Directive allowed UAT-safe write to ${input.path} (evidence/defect capture) ` +
|
|
268
|
+
`under campaign ${uat.campaignId}.`, input);
|
|
269
|
+
}
|
|
270
|
+
const covered = findCoveringGrant(input, input.op);
|
|
271
|
+
if (covered.grant === null) {
|
|
272
|
+
return deny(covered.code, covered.reason.includes("active UAT")
|
|
273
|
+
? covered.reason
|
|
274
|
+
: `${covered.reason} Active UAT campaign: ${uat.campaignId}.`, input);
|
|
275
|
+
}
|
|
276
|
+
return allow("authz-allow", `Directive allowed ${input.op} under UAT campaign ${uat.campaignId} via human-origin ` +
|
|
277
|
+
`grant ${covered.grant.id} (cohort ${covered.grant.scope.cohortId ?? "n/a"}).`, input, covered.grant);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Whether a decision should mark a single-use grant spent after allow.
|
|
281
|
+
* Callers (dispatcher) persist via markGrantUsed.
|
|
282
|
+
*/
|
|
283
|
+
export function shouldConsumeSingleUseGrant(decision) {
|
|
284
|
+
return decision.allowed && decision.humanApprovalRef !== null && decision.code === "authz-allow";
|
|
285
|
+
}
|
|
286
|
+
/** Snapshot of approved scope for audit/deny messages. */
|
|
287
|
+
export function describeScope(scope) {
|
|
288
|
+
if (scope === null)
|
|
289
|
+
return "(none)";
|
|
290
|
+
const parts = [
|
|
291
|
+
`ops=[${scope.operations.join(",")}]`,
|
|
292
|
+
`surfaces=${scope.surfaces.length === 0 ? "*" : scope.surfaces.join("|")}`,
|
|
293
|
+
scope.cohortId === null ? null : `cohort=${scope.cohortId}`,
|
|
294
|
+
scope.planRef === null ? null : `plan=${scope.planRef}`,
|
|
295
|
+
].filter((p) => p !== null);
|
|
296
|
+
return parts.join(" ");
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-origin approval grants + UAT mutation lease (#2944 Wave 1)
|
|
3
|
+
* + closed-verb release gates / AFK templates (#1095 Wave 4).
|
|
4
|
+
*/
|
|
5
|
+
export * from "./actions.js";
|
|
6
|
+
export * from "./classify.js";
|
|
7
|
+
export * from "./closed-verb.js";
|
|
8
|
+
export * from "./evaluate.js";
|
|
9
|
+
export * from "./origin.js";
|
|
10
|
+
export * from "./paths.js";
|
|
11
|
+
export * from "./store.js";
|
|
12
|
+
export * from "./templates.js";
|
|
13
|
+
export * from "./types.js";
|
|
14
|
+
export * from "./verb-classification.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-origin approval grants + UAT mutation lease (#2944 Wave 1)
|
|
3
|
+
* + closed-verb release gates / AFK templates (#1095 Wave 4).
|
|
4
|
+
*/
|
|
5
|
+
export * from "./actions.js";
|
|
6
|
+
export * from "./classify.js";
|
|
7
|
+
export * from "./closed-verb.js";
|
|
8
|
+
export * from "./evaluate.js";
|
|
9
|
+
export * from "./origin.js";
|
|
10
|
+
export * from "./paths.js";
|
|
11
|
+
export * from "./store.js";
|
|
12
|
+
export * from "./templates.js";
|
|
13
|
+
export * from "./types.js";
|
|
14
|
+
export * from "./verb-classification.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-origin provenance predicates (#2944).
|
|
3
|
+
* Self-authored lifecycle / dispatch / xBRIEF state never counts as approval.
|
|
4
|
+
*/
|
|
5
|
+
import { type GrantOrigin, type HumanOriginGrant, type HumanOriginKind } from "./types.js";
|
|
6
|
+
/** True when origin.kind is an accepted human-origin kind. */
|
|
7
|
+
export declare function isHumanOriginKind(kind: string | null | undefined): kind is HumanOriginKind;
|
|
8
|
+
/** True when origin.kind is explicitly agent/self-authored (always reject). */
|
|
9
|
+
export declare function isRejectedOriginKind(kind: string | null | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Structural human-origin check for a grant record.
|
|
12
|
+
* Rejects missing kind, rejected kinds, empty actor, and agent-like actors.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isHumanOriginGrant(grant: HumanOriginGrant | null | undefined): boolean;
|
|
15
|
+
export declare function isHumanOrigin(origin: GrantOrigin | null | undefined): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Reject evidence shapes agents commonly invent as consent:
|
|
18
|
+
* allocation_context free-text, xBRIEF status, lifecycle flags, dispatch envelopes.
|
|
19
|
+
* These never independently satisfy an implementation-approval gate.
|
|
20
|
+
*/
|
|
21
|
+
export declare function evidenceSatisfiesImplementationApproval(evidence: {
|
|
22
|
+
readonly grant?: HumanOriginGrant | null;
|
|
23
|
+
readonly xbriefStatus?: string | null;
|
|
24
|
+
readonly allocationContext?: Record<string, unknown> | null;
|
|
25
|
+
readonly dispatchEnvelope?: string | null;
|
|
26
|
+
readonly lifecycleAdvancedBy?: string | null;
|
|
27
|
+
}): boolean;
|
|
28
|
+
//# sourceMappingURL=origin.d.ts.map
|