@titan-design/authority 0.1.0 → 0.2.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/README.md +18 -2
- package/dist/index.d.ts +55 -3
- package/dist/index.js +188 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/table.json +22 -0
package/README.md
CHANGED
|
@@ -20,10 +20,26 @@ canResolve(DEFAULT_TABLE, "MRG-CO", { class: "coordinator", tainted: false }); /
|
|
|
20
20
|
- `evaluate(table, request)`: the decision for one request. No matching rule means deny.
|
|
21
21
|
A tainted actor on a rule marked `taintEscalates` gets a gate only the owner at a
|
|
22
22
|
terminal resolves.
|
|
23
|
+
- A rule with `when` (only MRG-AU-RV today) applies only when every condition holds on
|
|
24
|
+
`request.facts` and `request.tainted` is an own property set to exactly `false`; otherwise the pair's unconditional rule
|
|
25
|
+
decides and the reason names what was unmet. `unmetConditions(when, facts)` lists the failing conditions.
|
|
26
|
+
`evaluate` reads each request field once, then copies the facts with `structuredClone`
|
|
27
|
+
and rebuilds them as plain data on null-prototype objects. Facts holding a function, a `toJSON` method, a Proxy, a Map, a Set, a Date, a BigInt, a cycle or a throwing getter fail every condition instead of
|
|
28
|
+
throwing, and so does a sparse array (a hole would read through to the prototype).
|
|
29
|
+
The rebuild reads own properties only. A class instance is copied as its own
|
|
30
|
+
data, without its prototype. An inherited or getter-supplied `tainted` still escalates
|
|
31
|
+
a `taintEscalates` row when truthy, but never unlocks a conditional row; inherited
|
|
32
|
+
`facts` are ignored. `evaluate` trusts the request object itself: a Proxy request whose traps report an own `tainted: false` is treated as untainted, because a caller that builds such a request is asserting that value.
|
|
33
|
+
A missing or wrongly typed fact fails its condition (booleans must be exactly `true` or
|
|
34
|
+
`false`, ids non-empty strings, lists arrays), and so does a check run missing any field.
|
|
35
|
+
A non-canonical or non-ASCII changed path, or one with a segment ending in a space or a
|
|
36
|
+
dot, counts as protected.
|
|
37
|
+
`allowedApps` is caller-supplied; Shepherd must pin GitHub Actions (app id 15368) itself.
|
|
23
38
|
- `canResolve(table, ruleId, resolver)`: false for any agent or automation class and for
|
|
24
39
|
any tainted resolver.
|
|
25
|
-
- `policyTableSchema`: rejects a table that misses or repeats
|
|
26
|
-
names anyone but `owner-terminal` or `owner-remote` as a
|
|
40
|
+
- `policyTableSchema`: rejects a table that misses or repeats the unconditional rule for
|
|
41
|
+
an action by actor pair, or names anyone but `owner-terminal` or `owner-remote` as a
|
|
42
|
+
resolver.
|
|
27
43
|
- `DEFAULT_TABLE`: the approved table, also shipped as `@titan-design/authority/table.json`
|
|
28
44
|
for plain-node hooks.
|
|
29
45
|
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,51 @@ declare const RESOLVER_CLASSES: readonly ["owner-terminal", "owner-remote"];
|
|
|
6
6
|
declare const ACTION_CLASSES: readonly ["merge", "release", "secret-read", "untrusted-ingest", "private-to-public", "private-egress", "hardware-actuate", "hardware-stop", "destructive-remote", "destructive-local", "destructive-foreign", "spawn", "spend-over-cap", "authority-config", "human-verb"];
|
|
7
7
|
declare const VERDICTS: readonly ["allow", "gate", "deny"];
|
|
8
8
|
/** Codes for the record each rule expects to leave behind, from a decision record to a device read-back. */
|
|
9
|
-
declare const EVIDENCE_KINDS: readonly ["E-dec", "E-gate", "E-ref", "E-byp", "E-gh", "E-npm", "E-spn", "E-prin", "E-dev"];
|
|
9
|
+
declare const EVIDENCE_KINDS: readonly ["E-dec", "E-gate", "E-ref", "E-byp", "E-gh", "E-npm", "E-spn", "E-prin", "E-dev", "E-rev"];
|
|
10
|
+
/** Facts a conditional rule checks on the request; the rule applies only when every one holds. */
|
|
11
|
+
declare const CONDITION_KINDS: readonly ["resolver-is-dispatched-reviewer", "verdict-merge-at-head", "required-contexts-green", "no-non-green-run", "merge-tree-clean", "repo-not-frozen", "no-protected-path-change", "seat-grants-merge-on-green-approve"];
|
|
10
12
|
type ActorClass = (typeof ACTOR_CLASSES)[number];
|
|
11
13
|
type ResolverClass = (typeof RESOLVER_CLASSES)[number];
|
|
12
14
|
type ActionClass = (typeof ACTION_CLASSES)[number];
|
|
13
15
|
type Verdict = (typeof VERDICTS)[number];
|
|
14
16
|
type EvidenceKind = (typeof EVIDENCE_KINDS)[number];
|
|
17
|
+
type ConditionKind = (typeof CONDITION_KINDS)[number];
|
|
18
|
+
|
|
19
|
+
interface AgentIdentity {
|
|
20
|
+
agentId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
}
|
|
23
|
+
interface CheckRunFact {
|
|
24
|
+
name: string;
|
|
25
|
+
appId: number;
|
|
26
|
+
headSha: string;
|
|
27
|
+
/** GitHub's conclusion; `null` while the run is queued or in progress. */
|
|
28
|
+
conclusion: string | null;
|
|
29
|
+
}
|
|
30
|
+
/** What the caller observed about a pull request it wants to merge. The evaluator re-derives every condition from these. */
|
|
31
|
+
interface MergeFacts {
|
|
32
|
+
head: string;
|
|
33
|
+
resolver: AgentIdentity;
|
|
34
|
+
dispatchedReviewer: AgentIdentity;
|
|
35
|
+
verdict: {
|
|
36
|
+
value: string;
|
|
37
|
+
head: string;
|
|
38
|
+
};
|
|
39
|
+
requiredContexts: string[];
|
|
40
|
+
/** Check-run app ids the caller trusts; the package pins none. */
|
|
41
|
+
allowedApps: number[];
|
|
42
|
+
checkRuns: CheckRunFact[];
|
|
43
|
+
mergeTreeClean: boolean;
|
|
44
|
+
repoFrozen: boolean;
|
|
45
|
+
/** Every path the pull request touches, including both sides of a rename. */
|
|
46
|
+
changedPaths: string[];
|
|
47
|
+
seatGrants: string[];
|
|
48
|
+
}
|
|
49
|
+
interface ConditionFacts {
|
|
50
|
+
merge?: MergeFacts;
|
|
51
|
+
}
|
|
52
|
+
/** The conditions that do not hold. Missing or non-plain facts fail every condition, so a caller that observed nothing gets nothing. */
|
|
53
|
+
declare function unmetConditions(conditions: readonly ConditionKind[], facts: ConditionFacts | undefined): ConditionKind[];
|
|
15
54
|
|
|
16
55
|
declare const ruleSchema: z.ZodObject<{
|
|
17
56
|
id: z.ZodString;
|
|
@@ -51,6 +90,16 @@ declare const ruleSchema: z.ZodObject<{
|
|
|
51
90
|
}>>>;
|
|
52
91
|
taintEscalates: z.ZodOptional<z.ZodBoolean>;
|
|
53
92
|
condition: z.ZodOptional<z.ZodString>;
|
|
93
|
+
when: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
94
|
+
"resolver-is-dispatched-reviewer": "resolver-is-dispatched-reviewer";
|
|
95
|
+
"verdict-merge-at-head": "verdict-merge-at-head";
|
|
96
|
+
"required-contexts-green": "required-contexts-green";
|
|
97
|
+
"no-non-green-run": "no-non-green-run";
|
|
98
|
+
"merge-tree-clean": "merge-tree-clean";
|
|
99
|
+
"repo-not-frozen": "repo-not-frozen";
|
|
100
|
+
"no-protected-path-change": "no-protected-path-change";
|
|
101
|
+
"seat-grants-merge-on-green-approve": "seat-grants-merge-on-green-approve";
|
|
102
|
+
}>>>;
|
|
54
103
|
evidence: z.ZodArray<z.ZodEnum<{
|
|
55
104
|
"E-dec": "E-dec";
|
|
56
105
|
"E-gate": "E-gate";
|
|
@@ -61,6 +110,7 @@ declare const ruleSchema: z.ZodObject<{
|
|
|
61
110
|
"E-spn": "E-spn";
|
|
62
111
|
"E-prin": "E-prin";
|
|
63
112
|
"E-dev": "E-dev";
|
|
113
|
+
"E-rev": "E-rev";
|
|
64
114
|
}>>;
|
|
65
115
|
}, z.core.$strip>;
|
|
66
116
|
type Rule = z.infer<typeof ruleSchema>;
|
|
@@ -68,7 +118,7 @@ interface PolicyTable {
|
|
|
68
118
|
version: string;
|
|
69
119
|
rules: Rule[];
|
|
70
120
|
}
|
|
71
|
-
/** Validates a table: every (action, actor) pair exactly
|
|
121
|
+
/** Validates a table: every (action, actor) pair has exactly one unconditional rule, and only owner classes resolve gates. */
|
|
72
122
|
declare const policyTableSchema: z.ZodType<PolicyTable>;
|
|
73
123
|
|
|
74
124
|
interface AuthorityRequest {
|
|
@@ -79,6 +129,8 @@ interface AuthorityRequest {
|
|
|
79
129
|
};
|
|
80
130
|
tainted: boolean;
|
|
81
131
|
subject: Record<string, string>;
|
|
132
|
+
/** Observed facts that a conditional rule (one with `when`) checks before it applies. */
|
|
133
|
+
facts?: ConditionFacts;
|
|
82
134
|
}
|
|
83
135
|
type Decision = {
|
|
84
136
|
verdict: "allow";
|
|
@@ -104,4 +156,4 @@ declare function canResolve(table: PolicyTable, ruleId: string, resolver: {
|
|
|
104
156
|
/** The owner-approved decision table, validated on load. The same data ships as `@titan-design/authority/table.json`. */
|
|
105
157
|
declare const DEFAULT_TABLE: PolicyTable;
|
|
106
158
|
|
|
107
|
-
export { ACTION_CLASSES, ACTOR_CLASSES, type ActionClass, type ActorClass, type AuthorityRequest, DEFAULT_TABLE, type Decision, EVIDENCE_KINDS, type EvidenceKind, type PolicyTable, RESOLVER_CLASSES, type ResolverClass, type Rule, VERDICTS, type Verdict, canResolve, evaluate, policyTableSchema };
|
|
159
|
+
export { ACTION_CLASSES, ACTOR_CLASSES, type ActionClass, type ActorClass, type AgentIdentity, type AuthorityRequest, CONDITION_KINDS, type CheckRunFact, type ConditionFacts, type ConditionKind, DEFAULT_TABLE, type Decision, EVIDENCE_KINDS, type EvidenceKind, type MergeFacts, type PolicyTable, RESOLVER_CLASSES, type ResolverClass, type Rule, VERDICTS, type Verdict, canResolve, evaluate, policyTableSchema, unmetConditions };
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,129 @@ var ACTION_CLASSES = [
|
|
|
19
19
|
"human-verb"
|
|
20
20
|
];
|
|
21
21
|
var VERDICTS = ["allow", "gate", "deny"];
|
|
22
|
-
var EVIDENCE_KINDS = ["E-dec", "E-gate", "E-ref", "E-byp", "E-gh", "E-npm", "E-spn", "E-prin", "E-dev"];
|
|
22
|
+
var EVIDENCE_KINDS = ["E-dec", "E-gate", "E-ref", "E-byp", "E-gh", "E-npm", "E-spn", "E-prin", "E-dev", "E-rev"];
|
|
23
|
+
var CONDITION_KINDS = [
|
|
24
|
+
"resolver-is-dispatched-reviewer",
|
|
25
|
+
"verdict-merge-at-head",
|
|
26
|
+
"required-contexts-green",
|
|
27
|
+
"no-non-green-run",
|
|
28
|
+
"merge-tree-clean",
|
|
29
|
+
"repo-not-frozen",
|
|
30
|
+
"no-protected-path-change",
|
|
31
|
+
"seat-grants-merge-on-green-approve"
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// src/conditions.ts
|
|
35
|
+
var GREEN_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "neutral", "skipped"]);
|
|
36
|
+
var FULL_SHA = /^[0-9a-f]{40}$/;
|
|
37
|
+
var PRINTABLE_ASCII = /^[\x20-\x7e]+$/;
|
|
38
|
+
var PROTECTED_DIRS = /* @__PURE__ */ new Set([".github"]);
|
|
39
|
+
var PROTECTED_FILES = /* @__PURE__ */ new Set(["codeowners", "docs/codeowners", ".gitmodules"]);
|
|
40
|
+
var NON_CANONICAL_SEGMENTS = /* @__PURE__ */ new Set(["", ".", ".."]);
|
|
41
|
+
var TRAILING_SPACE_OR_DOT = /[ .]$/;
|
|
42
|
+
function isId(value) {
|
|
43
|
+
return typeof value === "string" && value.length > 0;
|
|
44
|
+
}
|
|
45
|
+
function sameAgent(a, b) {
|
|
46
|
+
return isId(a.agentId) && isId(a.sessionId) && a.agentId === b.agentId && a.sessionId === b.sessionId;
|
|
47
|
+
}
|
|
48
|
+
function verdictMergeAtHead(facts) {
|
|
49
|
+
const { head } = facts;
|
|
50
|
+
return facts.verdict.value === "MERGE" && typeof head === "string" && FULL_SHA.test(head) && facts.verdict.head === head;
|
|
51
|
+
}
|
|
52
|
+
function isNonCanonicalSegment(segment) {
|
|
53
|
+
return NON_CANONICAL_SEGMENTS.has(segment) || TRAILING_SPACE_OR_DOT.test(segment);
|
|
54
|
+
}
|
|
55
|
+
function isNonCanonical(path) {
|
|
56
|
+
return !PRINTABLE_ASCII.test(path) || path.includes("\\") || path.split("/").some(isNonCanonicalSegment);
|
|
57
|
+
}
|
|
58
|
+
function isProtectedPath(path) {
|
|
59
|
+
if (typeof path !== "string" || isNonCanonical(path)) return true;
|
|
60
|
+
const folded = path.toLowerCase();
|
|
61
|
+
return PROTECTED_FILES.has(folded) || PROTECTED_DIRS.has(folded.split("/")[0]);
|
|
62
|
+
}
|
|
63
|
+
function isWellFormedRun(run) {
|
|
64
|
+
return isId(run.name) && Number.isInteger(run.appId) && isId(run.headSha) && (run.conclusion === null || typeof run.conclusion === "string");
|
|
65
|
+
}
|
|
66
|
+
function hasRunFacts(facts) {
|
|
67
|
+
return Array.isArray(facts.checkRuns) && Array.isArray(facts.allowedApps) && facts.allowedApps.every((app) => Number.isInteger(app));
|
|
68
|
+
}
|
|
69
|
+
function countedRuns(facts) {
|
|
70
|
+
return facts.checkRuns.filter((run) => isWellFormedRun(run) && run.headSha === facts.head && facts.allowedApps.includes(run.appId));
|
|
71
|
+
}
|
|
72
|
+
function requiredContextsGreen(facts) {
|
|
73
|
+
const contexts = facts.requiredContexts;
|
|
74
|
+
if (!hasRunFacts(facts) || !Array.isArray(contexts) || contexts.length === 0) return false;
|
|
75
|
+
const runs = countedRuns(facts);
|
|
76
|
+
return contexts.every((context) => isId(context) && runs.some((run) => run.name === context && run.conclusion === "success"));
|
|
77
|
+
}
|
|
78
|
+
function noNonGreenRun(facts) {
|
|
79
|
+
return hasRunFacts(facts) && facts.checkRuns.every(isWellFormedRun) && countedRuns(facts).every((run) => run.conclusion !== null && GREEN_CONCLUSIONS.has(run.conclusion));
|
|
80
|
+
}
|
|
81
|
+
function noProtectedPathChange(facts) {
|
|
82
|
+
const paths = facts.changedPaths;
|
|
83
|
+
return Array.isArray(paths) && paths.length > 0 && !paths.some(isProtectedPath);
|
|
84
|
+
}
|
|
85
|
+
var MERGE_CHECKS = {
|
|
86
|
+
"resolver-is-dispatched-reviewer": (facts) => sameAgent(facts.resolver, facts.dispatchedReviewer),
|
|
87
|
+
"verdict-merge-at-head": verdictMergeAtHead,
|
|
88
|
+
"required-contexts-green": requiredContextsGreen,
|
|
89
|
+
"no-non-green-run": noNonGreenRun,
|
|
90
|
+
"merge-tree-clean": (facts) => facts.mergeTreeClean === true,
|
|
91
|
+
"repo-not-frozen": (facts) => facts.repoFrozen === false,
|
|
92
|
+
"no-protected-path-change": noProtectedPathChange,
|
|
93
|
+
"seat-grants-merge-on-green-approve": (facts) => Array.isArray(facts.seatGrants) && facts.seatGrants.includes("merge-on-green-approve")
|
|
94
|
+
};
|
|
95
|
+
function holds(check, facts) {
|
|
96
|
+
try {
|
|
97
|
+
return check(facts);
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function isRecord(value) {
|
|
103
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
104
|
+
}
|
|
105
|
+
var LEAF_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "undefined"]);
|
|
106
|
+
function rebuildArray(value, ancestors) {
|
|
107
|
+
const copy = [];
|
|
108
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
109
|
+
if (!Object.hasOwn(value, index)) throw new TypeError("fact lists must not be sparse");
|
|
110
|
+
const item = toPlainData(value[index], ancestors);
|
|
111
|
+
Object.defineProperty(copy, index, { value: item, writable: true, enumerable: true, configurable: true });
|
|
112
|
+
}
|
|
113
|
+
return copy;
|
|
114
|
+
}
|
|
115
|
+
function rebuildRecord(value, ancestors) {
|
|
116
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) throw new TypeError("facts must be plain objects");
|
|
117
|
+
const copy = /* @__PURE__ */ Object.create(null);
|
|
118
|
+
for (const [key, field] of Object.entries(value)) copy[key] = toPlainData(field, ancestors);
|
|
119
|
+
return copy;
|
|
120
|
+
}
|
|
121
|
+
function toPlainData(value, ancestors) {
|
|
122
|
+
if (value === null || LEAF_TYPES.has(typeof value)) return value;
|
|
123
|
+
if (typeof value !== "object" || ancestors.has(value)) throw new TypeError("facts must be acyclic JSON-shaped data");
|
|
124
|
+
ancestors.add(value);
|
|
125
|
+
const copy = Array.isArray(value) ? rebuildArray(value, ancestors) : rebuildRecord(value, ancestors);
|
|
126
|
+
ancestors.delete(value);
|
|
127
|
+
return copy;
|
|
128
|
+
}
|
|
129
|
+
function plainMergeFacts(read) {
|
|
130
|
+
try {
|
|
131
|
+
const copy = toPlainData(structuredClone(read()), /* @__PURE__ */ new Set());
|
|
132
|
+
const merge = isRecord(copy) ? copy.merge : void 0;
|
|
133
|
+
return isRecord(merge) ? merge : void 0;
|
|
134
|
+
} catch {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function unmetMergeConditions(conditions, merge) {
|
|
139
|
+
if (!merge) return [...conditions];
|
|
140
|
+
return conditions.filter((condition) => !holds(MERGE_CHECKS[condition], merge));
|
|
141
|
+
}
|
|
142
|
+
function unmetConditions(conditions, facts) {
|
|
143
|
+
return unmetMergeConditions(conditions, plainMergeFacts(() => facts));
|
|
144
|
+
}
|
|
23
145
|
|
|
24
146
|
// src/schema.ts
|
|
25
147
|
import { z } from "zod";
|
|
@@ -31,20 +153,25 @@ var ruleSchema = z.object({
|
|
|
31
153
|
resolvers: z.array(z.enum(RESOLVER_CLASSES)).min(1).optional(),
|
|
32
154
|
taintEscalates: z.boolean().optional(),
|
|
33
155
|
condition: z.string().min(1).optional(),
|
|
156
|
+
when: z.array(z.enum(CONDITION_KINDS)).min(1).optional(),
|
|
34
157
|
evidence: z.array(z.enum(EVIDENCE_KINDS))
|
|
35
158
|
}).refine((rule) => rule.verdict === "gate" === (rule.resolvers !== void 0), {
|
|
36
159
|
message: "a gate rule needs resolvers, and only a gate rule may have them",
|
|
37
160
|
path: ["resolvers"]
|
|
161
|
+
}).refine((rule) => rule.when === void 0 || rule.verdict === "allow", {
|
|
162
|
+
message: "only an allow rule may carry when conditions",
|
|
163
|
+
path: ["when"]
|
|
38
164
|
});
|
|
39
165
|
function checkTotality(table, ctx) {
|
|
40
166
|
const seenPairs = /* @__PURE__ */ new Set();
|
|
41
167
|
const seenIds = /* @__PURE__ */ new Set();
|
|
42
168
|
table.rules.forEach((rule, index) => {
|
|
43
169
|
const pair = `${rule.action} by ${rule.actor}`;
|
|
44
|
-
if (seenPairs.has(pair)) ctx.addIssue({ code: "custom", message: `duplicate rule for ${pair}`, path: ["rules", index] });
|
|
45
170
|
if (seenIds.has(rule.id)) ctx.addIssue({ code: "custom", message: `duplicate rule id ${rule.id}`, path: ["rules", index, "id"] });
|
|
46
|
-
seenPairs.add(pair);
|
|
47
171
|
seenIds.add(rule.id);
|
|
172
|
+
if (rule.when) return;
|
|
173
|
+
if (seenPairs.has(pair)) ctx.addIssue({ code: "custom", message: `duplicate rule for ${pair}`, path: ["rules", index] });
|
|
174
|
+
seenPairs.add(pair);
|
|
48
175
|
});
|
|
49
176
|
for (const action of ACTION_CLASSES) {
|
|
50
177
|
for (const actor of ACTOR_CLASSES) {
|
|
@@ -56,19 +183,44 @@ var policyTableSchema = z.object({ version: z.string().min(1), rules: z.array(ru
|
|
|
56
183
|
|
|
57
184
|
// src/evaluate.ts
|
|
58
185
|
var TAINT_RESOLVERS = ["owner-terminal"];
|
|
59
|
-
function
|
|
60
|
-
|
|
186
|
+
function snapshotOf(request) {
|
|
187
|
+
const tainted = request.tainted;
|
|
188
|
+
return {
|
|
189
|
+
action: request.action,
|
|
190
|
+
actor: request.actor.class,
|
|
191
|
+
tainted,
|
|
192
|
+
ownUntainted: Object.hasOwn(request, "tainted") && tainted === false,
|
|
193
|
+
merge: plainMergeFacts(() => Object.hasOwn(request, "facts") ? request.facts : void 0)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function findRule(table, snapshot) {
|
|
197
|
+
const forPair = table.rules.filter((rule) => rule.action === snapshot.action && rule.actor === snapshot.actor);
|
|
198
|
+
const unmet = [];
|
|
199
|
+
for (const rule of forPair.filter((candidate) => candidate.when)) {
|
|
200
|
+
if (!snapshot.ownUntainted) {
|
|
201
|
+
unmet.push(`${rule.id} skipped: tainted is not false`);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const failed = unmetMergeConditions(rule.when ?? [], snapshot.merge);
|
|
205
|
+
if (failed.length === 0) return { rule, unmet: [] };
|
|
206
|
+
unmet.push(`${rule.id} unmet: ${failed.join(", ")}`);
|
|
207
|
+
}
|
|
208
|
+
return { rule: forPair.find((candidate) => !candidate.when), unmet };
|
|
209
|
+
}
|
|
210
|
+
function withUnmet(reason, unmet) {
|
|
211
|
+
return unmet.length === 0 ? reason : `${reason}; ${unmet.join("; ")}`;
|
|
61
212
|
}
|
|
62
213
|
function evaluate(table, request) {
|
|
63
|
-
const
|
|
64
|
-
const actor =
|
|
65
|
-
const rule = findRule(table,
|
|
214
|
+
const snapshot = snapshotOf(request);
|
|
215
|
+
const { action, actor } = snapshot;
|
|
216
|
+
const { rule, unmet } = findRule(table, snapshot);
|
|
66
217
|
if (!rule) return { verdict: "deny", ruleId: null, reason: `no rule for ${action} by ${actor}` };
|
|
67
|
-
if (rule.verdict === "deny") return { verdict: "deny", ruleId: rule.id, reason: `${rule.id} denies ${action} by ${actor}
|
|
218
|
+
if (rule.verdict === "deny") return { verdict: "deny", ruleId: rule.id, reason: withUnmet(`${rule.id} denies ${action} by ${actor}`, unmet) };
|
|
68
219
|
if (rule.verdict === "gate") {
|
|
69
|
-
|
|
220
|
+
const reason = withUnmet(`${rule.id} gates ${action} by ${actor}`, unmet);
|
|
221
|
+
return { verdict: "gate", ruleId: rule.id, resolvers: [...rule.resolvers ?? []], reason };
|
|
70
222
|
}
|
|
71
|
-
if (
|
|
223
|
+
if (snapshot.tainted && rule.taintEscalates) {
|
|
72
224
|
return { verdict: "gate", ruleId: rule.id, resolvers: [...TAINT_RESOLVERS], reason: `${rule.id} gates ${action} by a tainted ${actor}` };
|
|
73
225
|
}
|
|
74
226
|
return { verdict: "allow", ruleId: rule.id };
|
|
@@ -161,6 +313,28 @@ var table_default = {
|
|
|
161
313
|
"E-gh"
|
|
162
314
|
]
|
|
163
315
|
},
|
|
316
|
+
{
|
|
317
|
+
id: "MRG-AU-RV",
|
|
318
|
+
action: "merge",
|
|
319
|
+
actor: "automation",
|
|
320
|
+
verdict: "allow",
|
|
321
|
+
condition: "Owner decision D-A, all seats: the run-dispatched reviewer's MERGE verdict at the exact head, with required check runs green from the allowed apps, stands in for the owner's tap. A trusted collector, never the requester, observes the facts; its changed paths include both sides of every rename. A tainted request never matches. Otherwise MRG-AU gates.",
|
|
322
|
+
when: [
|
|
323
|
+
"resolver-is-dispatched-reviewer",
|
|
324
|
+
"verdict-merge-at-head",
|
|
325
|
+
"required-contexts-green",
|
|
326
|
+
"no-non-green-run",
|
|
327
|
+
"merge-tree-clean",
|
|
328
|
+
"repo-not-frozen",
|
|
329
|
+
"no-protected-path-change",
|
|
330
|
+
"seat-grants-merge-on-green-approve"
|
|
331
|
+
],
|
|
332
|
+
evidence: [
|
|
333
|
+
"E-gate",
|
|
334
|
+
"E-gh",
|
|
335
|
+
"E-rev"
|
|
336
|
+
]
|
|
337
|
+
},
|
|
164
338
|
{
|
|
165
339
|
id: "REL-OT",
|
|
166
340
|
action: "release",
|
|
@@ -961,12 +1135,14 @@ var DEFAULT_TABLE = policyTableSchema.parse(table_default);
|
|
|
961
1135
|
export {
|
|
962
1136
|
ACTION_CLASSES,
|
|
963
1137
|
ACTOR_CLASSES,
|
|
1138
|
+
CONDITION_KINDS,
|
|
964
1139
|
DEFAULT_TABLE,
|
|
965
1140
|
EVIDENCE_KINDS,
|
|
966
1141
|
RESOLVER_CLASSES,
|
|
967
1142
|
VERDICTS,
|
|
968
1143
|
canResolve,
|
|
969
1144
|
evaluate,
|
|
970
|
-
policyTableSchema
|
|
1145
|
+
policyTableSchema,
|
|
1146
|
+
unmetConditions
|
|
971
1147
|
};
|
|
972
1148
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vocabulary.ts","../src/schema.ts","../src/evaluate.ts","../src/table.json","../src/table.ts"],"sourcesContent":["export const ACTOR_CLASSES = [\"owner-terminal\", \"owner-remote\", \"coordinator\", \"worker\", \"headless\", \"automation\"] as const;\n\n/** The only classes that may resolve a gate: the owner, at a terminal or through a verified remote channel. */\nexport const RESOLVER_CLASSES = [\"owner-terminal\", \"owner-remote\"] as const;\n\nexport const ACTION_CLASSES = [\n \"merge\",\n \"release\",\n \"secret-read\",\n \"untrusted-ingest\",\n \"private-to-public\",\n \"private-egress\",\n \"hardware-actuate\",\n \"hardware-stop\",\n \"destructive-remote\",\n \"destructive-local\",\n \"destructive-foreign\",\n \"spawn\",\n \"spend-over-cap\",\n \"authority-config\",\n \"human-verb\",\n] as const;\n\nexport const VERDICTS = [\"allow\", \"gate\", \"deny\"] as const;\n\n/** Codes for the record each rule expects to leave behind, from a decision record to a device read-back. */\nexport const EVIDENCE_KINDS = [\"E-dec\", \"E-gate\", \"E-ref\", \"E-byp\", \"E-gh\", \"E-npm\", \"E-spn\", \"E-prin\", \"E-dev\"] as const;\n\nexport type ActorClass = (typeof ACTOR_CLASSES)[number];\nexport type ResolverClass = (typeof RESOLVER_CLASSES)[number];\nexport type ActionClass = (typeof ACTION_CLASSES)[number];\nexport type Verdict = (typeof VERDICTS)[number];\nexport type EvidenceKind = (typeof EVIDENCE_KINDS)[number];\n","import { z } from \"zod\";\nimport { ACTION_CLASSES, ACTOR_CLASSES, EVIDENCE_KINDS, RESOLVER_CLASSES, VERDICTS } from \"./vocabulary.js\";\n\nconst ruleSchema = z\n .object({\n id: z.string().min(1),\n action: z.enum(ACTION_CLASSES),\n actor: z.enum(ACTOR_CLASSES),\n verdict: z.enum(VERDICTS),\n resolvers: z.array(z.enum(RESOLVER_CLASSES)).min(1).optional(),\n taintEscalates: z.boolean().optional(),\n condition: z.string().min(1).optional(),\n evidence: z.array(z.enum(EVIDENCE_KINDS)),\n })\n .refine((rule) => (rule.verdict === \"gate\") === (rule.resolvers !== undefined), {\n message: \"a gate rule needs resolvers, and only a gate rule may have them\",\n path: [\"resolvers\"],\n });\n\nexport type Rule = z.infer<typeof ruleSchema>;\n\nexport interface PolicyTable {\n version: string;\n rules: Rule[];\n}\n\nfunction checkTotality(table: PolicyTable, ctx: z.RefinementCtx): void {\n const seenPairs = new Set<string>();\n const seenIds = new Set<string>();\n table.rules.forEach((rule, index) => {\n const pair = `${rule.action} by ${rule.actor}`;\n if (seenPairs.has(pair)) ctx.addIssue({ code: \"custom\", message: `duplicate rule for ${pair}`, path: [\"rules\", index] });\n if (seenIds.has(rule.id)) ctx.addIssue({ code: \"custom\", message: `duplicate rule id ${rule.id}`, path: [\"rules\", index, \"id\"] });\n seenPairs.add(pair);\n seenIds.add(rule.id);\n });\n for (const action of ACTION_CLASSES) {\n for (const actor of ACTOR_CLASSES) {\n if (!seenPairs.has(`${action} by ${actor}`)) ctx.addIssue({ code: \"custom\", message: `no rule for ${action} by ${actor}`, path: [\"rules\"] });\n }\n }\n}\n\n/** Validates a table: every (action, actor) pair exactly once, and only owner classes as gate resolvers. */\nexport const policyTableSchema: z.ZodType<PolicyTable> = z\n .object({ version: z.string().min(1), rules: z.array(ruleSchema) })\n .superRefine(checkTotality);\n","import type { PolicyTable, Rule } from \"./schema.js\";\nimport type { ActionClass, ActorClass, ResolverClass } from \"./vocabulary.js\";\nimport { RESOLVER_CLASSES } from \"./vocabulary.js\";\n\nexport interface AuthorityRequest {\n action: ActionClass;\n actor: { class: ActorClass; id: string };\n tainted: boolean;\n subject: Record<string, string>;\n}\n\nexport type Decision =\n | { verdict: \"allow\"; ruleId: string }\n | { verdict: \"gate\"; ruleId: string; resolvers: ResolverClass[]; reason: string }\n | { verdict: \"deny\"; ruleId: string | null; reason: string };\n\nconst TAINT_RESOLVERS: ResolverClass[] = [\"owner-terminal\"];\n\nfunction findRule(table: PolicyTable, action: string, actor: string): Rule | undefined {\n return table.rules.find((rule) => rule.action === action && rule.actor === actor);\n}\n\n/** Decides one request against a table. Pure: no clock, no environment, and no match means deny. */\nexport function evaluate(table: PolicyTable, request: AuthorityRequest): Decision {\n const { action } = request;\n const actor = request.actor.class;\n const rule = findRule(table, action, actor);\n if (!rule) return { verdict: \"deny\", ruleId: null, reason: `no rule for ${action} by ${actor}` };\n if (rule.verdict === \"deny\") return { verdict: \"deny\", ruleId: rule.id, reason: `${rule.id} denies ${action} by ${actor}` };\n if (rule.verdict === \"gate\") {\n return { verdict: \"gate\", ruleId: rule.id, resolvers: [...(rule.resolvers ?? [])], reason: `${rule.id} gates ${action} by ${actor}` };\n }\n if (request.tainted && rule.taintEscalates) {\n return { verdict: \"gate\", ruleId: rule.id, resolvers: [...TAINT_RESOLVERS], reason: `${rule.id} gates ${action} by a tainted ${actor}` };\n }\n return { verdict: \"allow\", ruleId: rule.id };\n}\n\nfunction gateResolvers(rule: Rule): readonly ResolverClass[] {\n if (rule.verdict === \"gate\") return rule.resolvers ?? [];\n if (rule.verdict === \"allow\" && rule.taintEscalates) return TAINT_RESOLVERS;\n return [];\n}\n\nfunction isResolverClass(actorClass: ActorClass): actorClass is ResolverClass {\n return (RESOLVER_CLASSES as readonly string[]).includes(actorClass);\n}\n\n/** Whether a resolver may resolve the gate a rule opens. Agents, automation and tainted sessions never may. */\nexport function canResolve(table: PolicyTable, ruleId: string, resolver: { class: ActorClass; tainted: boolean }): boolean {\n if (resolver.tainted || !isResolverClass(resolver.class)) return false;\n const rule = table.rules.find((candidate) => candidate.id === ruleId);\n return rule !== undefined && gateResolvers(rule).includes(resolver.class);\n}\n","{\n \"version\": \"1.0.0\",\n \"rules\": [\n {\n \"id\": \"MRG-OT\",\n \"action\": \"merge\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-gh\"\n ]\n },\n {\n \"id\": \"MRG-OR\",\n \"action\": \"merge\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-remote\"\n ],\n \"condition\": \"Read-back of repo, pull request number, head SHA and title. Resolves a coordinator or automation merge gate; no direct door.\",\n \"evidence\": [\n \"E-gate\",\n \"E-prin\"\n ]\n },\n {\n \"id\": \"MRG-CO\",\n \"action\": \"merge\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\",\n \"owner-remote\"\n ],\n \"evidence\": [\n \"E-gate\",\n \"E-dec\"\n ]\n },\n {\n \"id\": \"MRG-WK\",\n \"action\": \"merge\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"MRG-HD\",\n \"action\": \"merge\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"MRG-AU\",\n \"action\": \"merge\",\n \"actor\": \"automation\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\",\n \"owner-remote\"\n ],\n \"condition\": \"Merge on green plus owner approval.\",\n \"evidence\": [\n \"E-gate\",\n \"E-gh\"\n ]\n },\n {\n \"id\": \"REL-OT\",\n \"action\": \"release\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Includes the first hand publish of a new package.\",\n \"evidence\": [\n \"E-npm\"\n ]\n },\n {\n \"id\": \"REL-OR\",\n \"action\": \"release\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-CO\",\n \"action\": \"release\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\"\n ],\n \"condition\": \"A Version Packages pull request merge and a Worker deploy are releases.\",\n \"evidence\": [\n \"E-gate\",\n \"E-dec\"\n ]\n },\n {\n \"id\": \"REL-WK\",\n \"action\": \"release\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-HD\",\n \"action\": \"release\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-AU\",\n \"action\": \"release\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Only the release workflow on main after a merged Version Packages pull request.\",\n \"evidence\": [\n \"E-npm\"\n ]\n },\n {\n \"id\": \"SEC-OT\",\n \"action\": \"secret-read\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Secret values are never logged.\",\n \"evidence\": []\n },\n {\n \"id\": \"SEC-OR\",\n \"action\": \"secret-read\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-CO\",\n \"action\": \"secret-read\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-WK\",\n \"action\": \"secret-read\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-HD\",\n \"action\": \"secret-read\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-AU\",\n \"action\": \"secret-read\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Its own scoped credential only.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-OT\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"The receiving session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-OR\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"The message arrives human-endorsed, never as from the human; forwarded third-party text taints.\",\n \"evidence\": [\n \"E-prin\"\n ]\n },\n {\n \"id\": \"UNT-CO\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"The session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-WK\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"The session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-HD\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Only fenced as data with no authority tools; the run is tainted from the start.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-AU\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Only into a fenced field that a consumer treats as tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"PUB-OT\",\n \"action\": \"private-to-public\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"PUB-OR\",\n \"action\": \"private-to-public\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-CO\",\n \"action\": \"private-to-public\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-WK\",\n \"action\": \"private-to-public\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-HD\",\n \"action\": \"private-to-public\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-AU\",\n \"action\": \"private-to-public\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"condition\": \"The CI check fails.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-OT\",\n \"action\": \"private-egress\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"EXT-OR\",\n \"action\": \"private-egress\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"Read-back of open queue items to the owner's own device.\",\n \"evidence\": [\n \"E-prin\"\n ]\n },\n {\n \"id\": \"EXT-CO\",\n \"action\": \"private-egress\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"taintEscalates\": true,\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-WK\",\n \"action\": \"private-egress\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-HD\",\n \"action\": \"private-egress\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-AU\",\n \"action\": \"private-egress\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Through a registered pipe with redaction and minimization.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"HW-OT\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HW-OR\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"condition\": \"Engaging or raising load; hardware-stop covers stop and unload.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-CO\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\"\n ],\n \"condition\": \"The owner at the device confirms each engage step; a forced takeover of an engaged device is always gated.\",\n \"evidence\": [\n \"E-gate\",\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HW-WK\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-HD\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-AU\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HWS-OT\",\n \"action\": \"hardware-stop\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-OR\",\n \"action\": \"hardware-stop\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-CO\",\n \"action\": \"hardware-stop\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-WK\",\n \"action\": \"hardware-stop\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-HD\",\n \"action\": \"hardware-stop\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-AU\",\n \"action\": \"hardware-stop\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"DSR-OT\",\n \"action\": \"destructive-remote\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Through the git-safety bypass or a command the owner typed.\",\n \"evidence\": [\n \"E-byp\"\n ]\n },\n {\n \"id\": \"DSR-OR\",\n \"action\": \"destructive-remote\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-CO\",\n \"action\": \"destructive-remote\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-WK\",\n \"action\": \"destructive-remote\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-HD\",\n \"action\": \"destructive-remote\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-AU\",\n \"action\": \"destructive-remote\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-gh\"\n ]\n },\n {\n \"id\": \"DSL-OT\",\n \"action\": \"destructive-local\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-OR\",\n \"action\": \"destructive-local\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSL-CO\",\n \"action\": \"destructive-local\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own checkout.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"DSL-WK\",\n \"action\": \"destructive-local\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own worktree.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-HD\",\n \"action\": \"destructive-local\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own worktree.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-AU\",\n \"action\": \"destructive-local\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own workspace.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSX-OT\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Includes retire --force and a broker restart, which are terminal-only.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"DSX-OR\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-CO\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-WK\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-HD\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-AU\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-OT\",\n \"action\": \"spawn\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-spn\"\n ]\n },\n {\n \"id\": \"SPN-OR\",\n \"action\": \"spawn\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"Within service limits, which are never lifted; the owner is recorded as the principal.\",\n \"evidence\": [\n \"E-spn\",\n \"E-prin\"\n ]\n },\n {\n \"id\": \"SPN-CO\",\n \"action\": \"spawn\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"taintEscalates\": true,\n \"condition\": \"Within the spawn escalation, depth, rate, cwd and slot limits.\",\n \"evidence\": [\n \"E-spn\",\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-WK\",\n \"action\": \"spawn\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-HD\",\n \"action\": \"spawn\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-AU\",\n \"action\": \"spawn\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Within service limits, with no principal recorded.\",\n \"evidence\": [\n \"E-spn\"\n ]\n },\n {\n \"id\": \"SPD-OT\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-OR\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"condition\": \"There is no cap to raise remotely.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPD-CO\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-WK\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-HD\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-AU\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"CFG-OT\",\n \"action\": \"authority-config\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"CFG-OR\",\n \"action\": \"authority-config\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-CO\",\n \"action\": \"authority-config\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"condition\": \"A repository CLAUDE.md change goes through a pull request under merge, flagged as a sensitive path.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-WK\",\n \"action\": \"authority-config\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-HD\",\n \"action\": \"authority-config\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-AU\",\n \"action\": \"authority-config\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-OT\",\n \"action\": \"human-verb\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-gate\"\n ]\n },\n {\n \"id\": \"HV-OR\",\n \"action\": \"human-verb\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-remote\"\n ],\n \"condition\": \"Read-back of what is answered; voice caps an endorsement at 600 characters and a permission preview at 300.\",\n \"evidence\": [\n \"E-prin\",\n \"E-gate\"\n ]\n },\n {\n \"id\": \"HV-CO\",\n \"action\": \"human-verb\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-WK\",\n \"action\": \"human-verb\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-HD\",\n \"action\": \"human-verb\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-AU\",\n \"action\": \"human-verb\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"condition\": \"A queue mirror applying a verdict the owner gave remotely acts as owner-remote, not as automation.\",\n \"evidence\": [\n \"E-gate\"\n ]\n }\n ]\n}\n","import tableJson from \"./table.json\" with { type: \"json\" };\nimport type { PolicyTable } from \"./schema.js\";\nimport { policyTableSchema } from \"./schema.js\";\n\n/** The owner-approved decision table, validated on load. The same data ships as `@titan-design/authority/table.json`. */\nexport const DEFAULT_TABLE: PolicyTable = policyTableSchema.parse(tableJson);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vocabulary.ts","../src/conditions.ts","../src/schema.ts","../src/evaluate.ts","../src/table.json","../src/table.ts"],"sourcesContent":["export const ACTOR_CLASSES = [\"owner-terminal\", \"owner-remote\", \"coordinator\", \"worker\", \"headless\", \"automation\"] as const;\n\n/** The only classes that may resolve a gate: the owner, at a terminal or through a verified remote channel. */\nexport const RESOLVER_CLASSES = [\"owner-terminal\", \"owner-remote\"] as const;\n\nexport const ACTION_CLASSES = [\n \"merge\",\n \"release\",\n \"secret-read\",\n \"untrusted-ingest\",\n \"private-to-public\",\n \"private-egress\",\n \"hardware-actuate\",\n \"hardware-stop\",\n \"destructive-remote\",\n \"destructive-local\",\n \"destructive-foreign\",\n \"spawn\",\n \"spend-over-cap\",\n \"authority-config\",\n \"human-verb\",\n] as const;\n\nexport const VERDICTS = [\"allow\", \"gate\", \"deny\"] as const;\n\n/** Codes for the record each rule expects to leave behind, from a decision record to a device read-back. */\nexport const EVIDENCE_KINDS = [\"E-dec\", \"E-gate\", \"E-ref\", \"E-byp\", \"E-gh\", \"E-npm\", \"E-spn\", \"E-prin\", \"E-dev\", \"E-rev\"] as const;\n\n/** Facts a conditional rule checks on the request; the rule applies only when every one holds. */\nexport const CONDITION_KINDS = [\n \"resolver-is-dispatched-reviewer\",\n \"verdict-merge-at-head\",\n \"required-contexts-green\",\n \"no-non-green-run\",\n \"merge-tree-clean\",\n \"repo-not-frozen\",\n \"no-protected-path-change\",\n \"seat-grants-merge-on-green-approve\",\n] as const;\n\nexport type ActorClass = (typeof ACTOR_CLASSES)[number];\nexport type ResolverClass = (typeof RESOLVER_CLASSES)[number];\nexport type ActionClass = (typeof ACTION_CLASSES)[number];\nexport type Verdict = (typeof VERDICTS)[number];\nexport type EvidenceKind = (typeof EVIDENCE_KINDS)[number];\nexport type ConditionKind = (typeof CONDITION_KINDS)[number];\n","import type { ConditionKind } from \"./vocabulary.js\";\n\nexport interface AgentIdentity {\n agentId: string;\n sessionId: string;\n}\n\nexport interface CheckRunFact {\n name: string;\n appId: number;\n headSha: string;\n /** GitHub's conclusion; `null` while the run is queued or in progress. */\n conclusion: string | null;\n}\n\n/** What the caller observed about a pull request it wants to merge. The evaluator re-derives every condition from these. */\nexport interface MergeFacts {\n head: string;\n resolver: AgentIdentity;\n dispatchedReviewer: AgentIdentity;\n verdict: { value: string; head: string };\n requiredContexts: string[];\n /** Check-run app ids the caller trusts; the package pins none. */\n allowedApps: number[];\n checkRuns: CheckRunFact[];\n mergeTreeClean: boolean;\n repoFrozen: boolean;\n /** Every path the pull request touches, including both sides of a rename. */\n changedPaths: string[];\n seatGrants: string[];\n}\n\nexport interface ConditionFacts {\n merge?: MergeFacts;\n}\n\nconst GREEN_CONCLUSIONS = new Set([\"success\", \"neutral\", \"skipped\"]);\nconst FULL_SHA = /^[0-9a-f]{40}$/;\nconst PRINTABLE_ASCII = /^[\\x20-\\x7e]+$/;\nconst PROTECTED_DIRS = new Set([\".github\"]);\nconst PROTECTED_FILES = new Set([\"codeowners\", \"docs/codeowners\", \".gitmodules\"]);\nconst NON_CANONICAL_SEGMENTS = new Set([\"\", \".\", \"..\"]);\nconst TRAILING_SPACE_OR_DOT = /[ .]$/;\n\nfunction isId(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction sameAgent(a: AgentIdentity, b: AgentIdentity): boolean {\n return isId(a.agentId) && isId(a.sessionId) && a.agentId === b.agentId && a.sessionId === b.sessionId;\n}\n\nfunction verdictMergeAtHead(facts: MergeFacts): boolean {\n const { head } = facts;\n return facts.verdict.value === \"MERGE\" && typeof head === \"string\" && FULL_SHA.test(head) && facts.verdict.head === head;\n}\n\nfunction isNonCanonicalSegment(segment: string): boolean {\n return NON_CANONICAL_SEGMENTS.has(segment) || TRAILING_SPACE_OR_DOT.test(segment);\n}\n\n// A path we cannot compare exactly could alias a protected one, so it counts as protected.\nfunction isNonCanonical(path: string): boolean {\n return !PRINTABLE_ASCII.test(path) || path.includes(\"\\\\\") || path.split(\"/\").some(isNonCanonicalSegment);\n}\n\nfunction isProtectedPath(path: unknown): boolean {\n if (typeof path !== \"string\" || isNonCanonical(path)) return true;\n const folded = path.toLowerCase();\n return PROTECTED_FILES.has(folded) || PROTECTED_DIRS.has(folded.split(\"/\")[0]!);\n}\n\nfunction isWellFormedRun(run: CheckRunFact): boolean {\n return isId(run.name) && Number.isInteger(run.appId) && isId(run.headSha) &&\n (run.conclusion === null || typeof run.conclusion === \"string\");\n}\n\nfunction hasRunFacts(facts: MergeFacts): boolean {\n return Array.isArray(facts.checkRuns) && Array.isArray(facts.allowedApps) &&\n facts.allowedApps.every((app) => Number.isInteger(app));\n}\n\nfunction countedRuns(facts: MergeFacts): CheckRunFact[] {\n return facts.checkRuns.filter((run) => isWellFormedRun(run) && run.headSha === facts.head && facts.allowedApps.includes(run.appId));\n}\n\nfunction requiredContextsGreen(facts: MergeFacts): boolean {\n const contexts = facts.requiredContexts;\n if (!hasRunFacts(facts) || !Array.isArray(contexts) || contexts.length === 0) return false;\n const runs = countedRuns(facts);\n return contexts.every((context) => isId(context) && runs.some((run) => run.name === context && run.conclusion === \"success\"));\n}\n\n// A run we cannot attribute to a head or an app might be a failure at this head, so it gates.\nfunction noNonGreenRun(facts: MergeFacts): boolean {\n return hasRunFacts(facts) && facts.checkRuns.every(isWellFormedRun) &&\n countedRuns(facts).every((run) => run.conclusion !== null && GREEN_CONCLUSIONS.has(run.conclusion));\n}\n\nfunction noProtectedPathChange(facts: MergeFacts): boolean {\n const paths = facts.changedPaths;\n return Array.isArray(paths) && paths.length > 0 && !paths.some(isProtectedPath);\n}\n\nconst MERGE_CHECKS: Record<ConditionKind, (facts: MergeFacts) => boolean> = {\n \"resolver-is-dispatched-reviewer\": (facts) => sameAgent(facts.resolver, facts.dispatchedReviewer),\n \"verdict-merge-at-head\": verdictMergeAtHead,\n \"required-contexts-green\": requiredContextsGreen,\n \"no-non-green-run\": noNonGreenRun,\n \"merge-tree-clean\": (facts) => facts.mergeTreeClean === true,\n \"repo-not-frozen\": (facts) => facts.repoFrozen === false,\n \"no-protected-path-change\": noProtectedPathChange,\n \"seat-grants-merge-on-green-approve\": (facts) => Array.isArray(facts.seatGrants) && facts.seatGrants.includes(\"merge-on-green-approve\"),\n};\n\n// Facts arrive from outside the type system, so a malformed fact fails its condition instead of throwing.\nfunction holds(check: (facts: MergeFacts) => boolean, facts: MergeFacts): boolean {\n try {\n return check(facts);\n } catch {\n return false;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nconst LEAF_TYPES = new Set([\"string\", \"number\", \"boolean\", \"undefined\"]);\n\n// A hole would read through to a possibly polluted prototype, and no fact list has a use for one.\nfunction rebuildArray(value: unknown[], ancestors: Set<object>): unknown[] {\n const copy: unknown[] = [];\n for (let index = 0; index < value.length; index += 1) {\n if (!Object.hasOwn(value, index)) throw new TypeError(\"fact lists must not be sparse\");\n const item = toPlainData(value[index], ancestors);\n Object.defineProperty(copy, index, { value: item, writable: true, enumerable: true, configurable: true });\n }\n return copy;\n}\n\n// Copies onto null-prototype objects, so a polluted Object.prototype cannot supply a missing fact.\nfunction rebuildRecord(value: object, ancestors: Set<object>): Record<string, unknown> {\n if (Object.getPrototypeOf(value) !== Object.prototype) throw new TypeError(\"facts must be plain objects\");\n const copy: Record<string, unknown> = Object.create(null);\n for (const [key, field] of Object.entries(value)) copy[key] = toPlainData(field, ancestors);\n return copy;\n}\n\nfunction toPlainData(value: unknown, ancestors: Set<object>): unknown {\n if (value === null || LEAF_TYPES.has(typeof value)) return value;\n if (typeof value !== \"object\" || ancestors.has(value)) throw new TypeError(\"facts must be acyclic JSON-shaped data\");\n ancestors.add(value);\n const copy = Array.isArray(value) ? rebuildArray(value, ancestors) : rebuildRecord(value, ancestors);\n ancestors.delete(value);\n return copy;\n}\n\n/**\n * The merge facts as fresh plain data, read once, or undefined when they are not plain data.\n * structuredClone rejects functions (so no toJSON can speak for a value), Proxies and throwing getters;\n * the rebuild then reads own properties only and rejects Map, Set, Date, BigInt, cycles and sparse arrays.\n */\nexport function plainMergeFacts(read: () => unknown): MergeFacts | undefined {\n try {\n const copy = toPlainData(structuredClone(read()), new Set());\n const merge = isRecord(copy) ? copy.merge : undefined;\n return isRecord(merge) ? (merge as unknown as MergeFacts) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The conditions that do not hold on facts already made plain by `plainMergeFacts`. */\nexport function unmetMergeConditions(conditions: readonly ConditionKind[], merge: MergeFacts | undefined): ConditionKind[] {\n if (!merge) return [...conditions];\n return conditions.filter((condition) => !holds(MERGE_CHECKS[condition], merge));\n}\n\n/** The conditions that do not hold. Missing or non-plain facts fail every condition, so a caller that observed nothing gets nothing. */\nexport function unmetConditions(conditions: readonly ConditionKind[], facts: ConditionFacts | undefined): ConditionKind[] {\n return unmetMergeConditions(conditions, plainMergeFacts(() => facts));\n}\n","import { z } from \"zod\";\nimport { ACTION_CLASSES, ACTOR_CLASSES, CONDITION_KINDS, EVIDENCE_KINDS, RESOLVER_CLASSES, VERDICTS } from \"./vocabulary.js\";\n\nconst ruleSchema = z\n .object({\n id: z.string().min(1),\n action: z.enum(ACTION_CLASSES),\n actor: z.enum(ACTOR_CLASSES),\n verdict: z.enum(VERDICTS),\n resolvers: z.array(z.enum(RESOLVER_CLASSES)).min(1).optional(),\n taintEscalates: z.boolean().optional(),\n condition: z.string().min(1).optional(),\n when: z.array(z.enum(CONDITION_KINDS)).min(1).optional(),\n evidence: z.array(z.enum(EVIDENCE_KINDS)),\n })\n .refine((rule) => (rule.verdict === \"gate\") === (rule.resolvers !== undefined), {\n message: \"a gate rule needs resolvers, and only a gate rule may have them\",\n path: [\"resolvers\"],\n })\n .refine((rule) => rule.when === undefined || rule.verdict === \"allow\", {\n message: \"only an allow rule may carry when conditions\",\n path: [\"when\"],\n });\n\nexport type Rule = z.infer<typeof ruleSchema>;\n\nexport interface PolicyTable {\n version: string;\n rules: Rule[];\n}\n\nfunction checkTotality(table: PolicyTable, ctx: z.RefinementCtx): void {\n const seenPairs = new Set<string>();\n const seenIds = new Set<string>();\n table.rules.forEach((rule, index) => {\n const pair = `${rule.action} by ${rule.actor}`;\n if (seenIds.has(rule.id)) ctx.addIssue({ code: \"custom\", message: `duplicate rule id ${rule.id}`, path: [\"rules\", index, \"id\"] });\n seenIds.add(rule.id);\n if (rule.when) return;\n if (seenPairs.has(pair)) ctx.addIssue({ code: \"custom\", message: `duplicate rule for ${pair}`, path: [\"rules\", index] });\n seenPairs.add(pair);\n });\n for (const action of ACTION_CLASSES) {\n for (const actor of ACTOR_CLASSES) {\n if (!seenPairs.has(`${action} by ${actor}`)) ctx.addIssue({ code: \"custom\", message: `no rule for ${action} by ${actor}`, path: [\"rules\"] });\n }\n }\n}\n\n/** Validates a table: every (action, actor) pair has exactly one unconditional rule, and only owner classes resolve gates. */\nexport const policyTableSchema: z.ZodType<PolicyTable> = z\n .object({ version: z.string().min(1), rules: z.array(ruleSchema) })\n .superRefine(checkTotality);\n","import type { ConditionFacts, MergeFacts } from \"./conditions.js\";\nimport { plainMergeFacts, unmetMergeConditions } from \"./conditions.js\";\nimport type { PolicyTable, Rule } from \"./schema.js\";\nimport type { ActionClass, ActorClass, ResolverClass } from \"./vocabulary.js\";\nimport { RESOLVER_CLASSES } from \"./vocabulary.js\";\n\nexport interface AuthorityRequest {\n action: ActionClass;\n actor: { class: ActorClass; id: string };\n tainted: boolean;\n subject: Record<string, string>;\n /** Observed facts that a conditional rule (one with `when`) checks before it applies. */\n facts?: ConditionFacts;\n}\n\nexport type Decision =\n | { verdict: \"allow\"; ruleId: string }\n | { verdict: \"gate\"; ruleId: string; resolvers: ResolverClass[]; reason: string }\n | { verdict: \"deny\"; ruleId: string | null; reason: string };\n\nconst TAINT_RESOLVERS: ResolverClass[] = [\"owner-terminal\"];\n\ninterface RuleMatch {\n rule: Rule | undefined;\n unmet: string[];\n}\n\ninterface RequestSnapshot {\n action: ActionClass;\n actor: ActorClass;\n tainted: unknown;\n ownUntainted: boolean;\n merge: MergeFacts | undefined;\n}\n\n// Each field is read exactly once; a value the caller did not set as an own property may restrict a decision, never relax it.\nfunction snapshotOf(request: AuthorityRequest): RequestSnapshot {\n const tainted: unknown = request.tainted;\n return {\n action: request.action,\n actor: request.actor.class,\n tainted,\n ownUntainted: Object.hasOwn(request, \"tainted\") && tainted === false,\n merge: plainMergeFacts(() => (Object.hasOwn(request, \"facts\") ? request.facts : undefined)),\n };\n}\n\nfunction findRule(table: PolicyTable, snapshot: RequestSnapshot): RuleMatch {\n const forPair = table.rules.filter((rule) => rule.action === snapshot.action && rule.actor === snapshot.actor);\n const unmet: string[] = [];\n for (const rule of forPair.filter((candidate) => candidate.when)) {\n if (!snapshot.ownUntainted) {\n unmet.push(`${rule.id} skipped: tainted is not false`);\n continue;\n }\n const failed = unmetMergeConditions(rule.when ?? [], snapshot.merge);\n if (failed.length === 0) return { rule, unmet: [] };\n unmet.push(`${rule.id} unmet: ${failed.join(\", \")}`);\n }\n return { rule: forPair.find((candidate) => !candidate.when), unmet };\n}\n\nfunction withUnmet(reason: string, unmet: string[]): string {\n return unmet.length === 0 ? reason : `${reason}; ${unmet.join(\"; \")}`;\n}\n\n/** Decides one request against a table. Pure: no clock, no environment, and no match means deny. */\nexport function evaluate(table: PolicyTable, request: AuthorityRequest): Decision {\n const snapshot = snapshotOf(request);\n const { action, actor } = snapshot;\n const { rule, unmet } = findRule(table, snapshot);\n if (!rule) return { verdict: \"deny\", ruleId: null, reason: `no rule for ${action} by ${actor}` };\n if (rule.verdict === \"deny\") return { verdict: \"deny\", ruleId: rule.id, reason: withUnmet(`${rule.id} denies ${action} by ${actor}`, unmet) };\n if (rule.verdict === \"gate\") {\n const reason = withUnmet(`${rule.id} gates ${action} by ${actor}`, unmet);\n return { verdict: \"gate\", ruleId: rule.id, resolvers: [...(rule.resolvers ?? [])], reason };\n }\n if (snapshot.tainted && rule.taintEscalates) {\n return { verdict: \"gate\", ruleId: rule.id, resolvers: [...TAINT_RESOLVERS], reason: `${rule.id} gates ${action} by a tainted ${actor}` };\n }\n return { verdict: \"allow\", ruleId: rule.id };\n}\n\nfunction gateResolvers(rule: Rule): readonly ResolverClass[] {\n if (rule.verdict === \"gate\") return rule.resolvers ?? [];\n if (rule.verdict === \"allow\" && rule.taintEscalates) return TAINT_RESOLVERS;\n return [];\n}\n\nfunction isResolverClass(actorClass: ActorClass): actorClass is ResolverClass {\n return (RESOLVER_CLASSES as readonly string[]).includes(actorClass);\n}\n\n/** Whether a resolver may resolve the gate a rule opens. Agents, automation and tainted sessions never may. */\nexport function canResolve(table: PolicyTable, ruleId: string, resolver: { class: ActorClass; tainted: boolean }): boolean {\n if (resolver.tainted || !isResolverClass(resolver.class)) return false;\n const rule = table.rules.find((candidate) => candidate.id === ruleId);\n return rule !== undefined && gateResolvers(rule).includes(resolver.class);\n}\n","{\n \"version\": \"1.0.0\",\n \"rules\": [\n {\n \"id\": \"MRG-OT\",\n \"action\": \"merge\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-gh\"\n ]\n },\n {\n \"id\": \"MRG-OR\",\n \"action\": \"merge\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-remote\"\n ],\n \"condition\": \"Read-back of repo, pull request number, head SHA and title. Resolves a coordinator or automation merge gate; no direct door.\",\n \"evidence\": [\n \"E-gate\",\n \"E-prin\"\n ]\n },\n {\n \"id\": \"MRG-CO\",\n \"action\": \"merge\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\",\n \"owner-remote\"\n ],\n \"evidence\": [\n \"E-gate\",\n \"E-dec\"\n ]\n },\n {\n \"id\": \"MRG-WK\",\n \"action\": \"merge\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"MRG-HD\",\n \"action\": \"merge\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"MRG-AU\",\n \"action\": \"merge\",\n \"actor\": \"automation\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\",\n \"owner-remote\"\n ],\n \"condition\": \"Merge on green plus owner approval.\",\n \"evidence\": [\n \"E-gate\",\n \"E-gh\"\n ]\n },\n {\n \"id\": \"MRG-AU-RV\",\n \"action\": \"merge\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Owner decision D-A, all seats: the run-dispatched reviewer's MERGE verdict at the exact head, with required check runs green from the allowed apps, stands in for the owner's tap. A trusted collector, never the requester, observes the facts; its changed paths include both sides of every rename. A tainted request never matches. Otherwise MRG-AU gates.\",\n \"when\": [\n \"resolver-is-dispatched-reviewer\",\n \"verdict-merge-at-head\",\n \"required-contexts-green\",\n \"no-non-green-run\",\n \"merge-tree-clean\",\n \"repo-not-frozen\",\n \"no-protected-path-change\",\n \"seat-grants-merge-on-green-approve\"\n ],\n \"evidence\": [\n \"E-gate\",\n \"E-gh\",\n \"E-rev\"\n ]\n },\n {\n \"id\": \"REL-OT\",\n \"action\": \"release\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Includes the first hand publish of a new package.\",\n \"evidence\": [\n \"E-npm\"\n ]\n },\n {\n \"id\": \"REL-OR\",\n \"action\": \"release\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-CO\",\n \"action\": \"release\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\"\n ],\n \"condition\": \"A Version Packages pull request merge and a Worker deploy are releases.\",\n \"evidence\": [\n \"E-gate\",\n \"E-dec\"\n ]\n },\n {\n \"id\": \"REL-WK\",\n \"action\": \"release\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-HD\",\n \"action\": \"release\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"REL-AU\",\n \"action\": \"release\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Only the release workflow on main after a merged Version Packages pull request.\",\n \"evidence\": [\n \"E-npm\"\n ]\n },\n {\n \"id\": \"SEC-OT\",\n \"action\": \"secret-read\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Secret values are never logged.\",\n \"evidence\": []\n },\n {\n \"id\": \"SEC-OR\",\n \"action\": \"secret-read\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-CO\",\n \"action\": \"secret-read\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-WK\",\n \"action\": \"secret-read\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-HD\",\n \"action\": \"secret-read\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SEC-AU\",\n \"action\": \"secret-read\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Its own scoped credential only.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-OT\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"The receiving session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-OR\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"The message arrives human-endorsed, never as from the human; forwarded third-party text taints.\",\n \"evidence\": [\n \"E-prin\"\n ]\n },\n {\n \"id\": \"UNT-CO\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"The session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-WK\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"The session becomes tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-HD\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Only fenced as data with no authority tools; the run is tainted from the start.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"UNT-AU\",\n \"action\": \"untrusted-ingest\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Only into a fenced field that a consumer treats as tainted.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"PUB-OT\",\n \"action\": \"private-to-public\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"PUB-OR\",\n \"action\": \"private-to-public\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-CO\",\n \"action\": \"private-to-public\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-WK\",\n \"action\": \"private-to-public\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-HD\",\n \"action\": \"private-to-public\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"PUB-AU\",\n \"action\": \"private-to-public\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"condition\": \"The CI check fails.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-OT\",\n \"action\": \"private-egress\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"EXT-OR\",\n \"action\": \"private-egress\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"Read-back of open queue items to the owner's own device.\",\n \"evidence\": [\n \"E-prin\"\n ]\n },\n {\n \"id\": \"EXT-CO\",\n \"action\": \"private-egress\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"taintEscalates\": true,\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-WK\",\n \"action\": \"private-egress\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-HD\",\n \"action\": \"private-egress\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"condition\": \"Outside registered pipes.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"EXT-AU\",\n \"action\": \"private-egress\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Through a registered pipe with redaction and minimization.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"HW-OT\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HW-OR\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"condition\": \"Engaging or raising load; hardware-stop covers stop and unload.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-CO\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"coordinator\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-terminal\"\n ],\n \"condition\": \"The owner at the device confirms each engage step; a forced takeover of an engaged device is always gated.\",\n \"evidence\": [\n \"E-gate\",\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HW-WK\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-HD\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HW-AU\",\n \"action\": \"hardware-actuate\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HWS-OT\",\n \"action\": \"hardware-stop\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-OR\",\n \"action\": \"hardware-stop\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-CO\",\n \"action\": \"hardware-stop\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-WK\",\n \"action\": \"hardware-stop\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-HD\",\n \"action\": \"hardware-stop\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"HWS-AU\",\n \"action\": \"hardware-stop\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dev\"\n ]\n },\n {\n \"id\": \"DSR-OT\",\n \"action\": \"destructive-remote\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Through the git-safety bypass or a command the owner typed.\",\n \"evidence\": [\n \"E-byp\"\n ]\n },\n {\n \"id\": \"DSR-OR\",\n \"action\": \"destructive-remote\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-CO\",\n \"action\": \"destructive-remote\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-WK\",\n \"action\": \"destructive-remote\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-HD\",\n \"action\": \"destructive-remote\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSR-AU\",\n \"action\": \"destructive-remote\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-gh\"\n ]\n },\n {\n \"id\": \"DSL-OT\",\n \"action\": \"destructive-local\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-OR\",\n \"action\": \"destructive-local\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSL-CO\",\n \"action\": \"destructive-local\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own checkout.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"DSL-WK\",\n \"action\": \"destructive-local\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own worktree.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-HD\",\n \"action\": \"destructive-local\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own worktree.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSL-AU\",\n \"action\": \"destructive-local\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Inside its own workspace.\",\n \"evidence\": []\n },\n {\n \"id\": \"DSX-OT\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"condition\": \"Includes retire --force and a broker restart, which are terminal-only.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"DSX-OR\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-CO\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-WK\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-HD\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"DSX-AU\",\n \"action\": \"destructive-foreign\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-OT\",\n \"action\": \"spawn\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-spn\"\n ]\n },\n {\n \"id\": \"SPN-OR\",\n \"action\": \"spawn\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"allow\",\n \"condition\": \"Within service limits, which are never lifted; the owner is recorded as the principal.\",\n \"evidence\": [\n \"E-spn\",\n \"E-prin\"\n ]\n },\n {\n \"id\": \"SPN-CO\",\n \"action\": \"spawn\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"taintEscalates\": true,\n \"condition\": \"Within the spawn escalation, depth, rate, cwd and slot limits.\",\n \"evidence\": [\n \"E-spn\",\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-WK\",\n \"action\": \"spawn\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-HD\",\n \"action\": \"spawn\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPN-AU\",\n \"action\": \"spawn\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Within service limits, with no principal recorded.\",\n \"evidence\": [\n \"E-spn\"\n ]\n },\n {\n \"id\": \"SPD-OT\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-OR\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"condition\": \"There is no cap to raise remotely.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"SPD-CO\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"coordinator\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-WK\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"worker\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-HD\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"headless\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"SPD-AU\",\n \"action\": \"spend-over-cap\",\n \"actor\": \"automation\",\n \"verdict\": \"allow\",\n \"condition\": \"Spend is recorded and crossing the notice threshold notifies the owner; the run continues.\",\n \"evidence\": [\n \"E-dec\"\n ]\n },\n {\n \"id\": \"CFG-OT\",\n \"action\": \"authority-config\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": []\n },\n {\n \"id\": \"CFG-OR\",\n \"action\": \"authority-config\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-CO\",\n \"action\": \"authority-config\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"condition\": \"A repository CLAUDE.md change goes through a pull request under merge, flagged as a sensitive path.\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-WK\",\n \"action\": \"authority-config\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-HD\",\n \"action\": \"authority-config\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"CFG-AU\",\n \"action\": \"authority-config\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-OT\",\n \"action\": \"human-verb\",\n \"actor\": \"owner-terminal\",\n \"verdict\": \"allow\",\n \"evidence\": [\n \"E-gate\"\n ]\n },\n {\n \"id\": \"HV-OR\",\n \"action\": \"human-verb\",\n \"actor\": \"owner-remote\",\n \"verdict\": \"gate\",\n \"resolvers\": [\n \"owner-remote\"\n ],\n \"condition\": \"Read-back of what is answered; voice caps an endorsement at 600 characters and a permission preview at 300.\",\n \"evidence\": [\n \"E-prin\",\n \"E-gate\"\n ]\n },\n {\n \"id\": \"HV-CO\",\n \"action\": \"human-verb\",\n \"actor\": \"coordinator\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-WK\",\n \"action\": \"human-verb\",\n \"actor\": \"worker\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-HD\",\n \"action\": \"human-verb\",\n \"actor\": \"headless\",\n \"verdict\": \"deny\",\n \"evidence\": [\n \"E-ref\"\n ]\n },\n {\n \"id\": \"HV-AU\",\n \"action\": \"human-verb\",\n \"actor\": \"automation\",\n \"verdict\": \"deny\",\n \"condition\": \"A queue mirror applying a verdict the owner gave remotely acts as owner-remote, not as automation.\",\n \"evidence\": [\n \"E-gate\"\n ]\n }\n ]\n}\n","import tableJson from \"./table.json\" with { type: \"json\" };\nimport type { PolicyTable } from \"./schema.js\";\nimport { policyTableSchema } from \"./schema.js\";\n\n/** The owner-approved decision table, validated on load. The same data ships as `@titan-design/authority/table.json`. */\nexport const DEFAULT_TABLE: PolicyTable = policyTableSchema.parse(tableJson);\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titan-design/authority",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The authority decision table as data: who may merge, release, read secrets, spawn or actuate hardware, with a pure evaluator",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/table.json
CHANGED
|
@@ -71,6 +71,28 @@
|
|
|
71
71
|
"E-gh"
|
|
72
72
|
]
|
|
73
73
|
},
|
|
74
|
+
{
|
|
75
|
+
"id": "MRG-AU-RV",
|
|
76
|
+
"action": "merge",
|
|
77
|
+
"actor": "automation",
|
|
78
|
+
"verdict": "allow",
|
|
79
|
+
"condition": "Owner decision D-A, all seats: the run-dispatched reviewer's MERGE verdict at the exact head, with required check runs green from the allowed apps, stands in for the owner's tap. A trusted collector, never the requester, observes the facts; its changed paths include both sides of every rename. A tainted request never matches. Otherwise MRG-AU gates.",
|
|
80
|
+
"when": [
|
|
81
|
+
"resolver-is-dispatched-reviewer",
|
|
82
|
+
"verdict-merge-at-head",
|
|
83
|
+
"required-contexts-green",
|
|
84
|
+
"no-non-green-run",
|
|
85
|
+
"merge-tree-clean",
|
|
86
|
+
"repo-not-frozen",
|
|
87
|
+
"no-protected-path-change",
|
|
88
|
+
"seat-grants-merge-on-green-approve"
|
|
89
|
+
],
|
|
90
|
+
"evidence": [
|
|
91
|
+
"E-gate",
|
|
92
|
+
"E-gh",
|
|
93
|
+
"E-rev"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
74
96
|
{
|
|
75
97
|
"id": "REL-OT",
|
|
76
98
|
"action": "release",
|