@alphazede/bearing-lite 0.2.0 → 0.2.2
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 +17 -1
- package/hooks/assurance-budget.cjs +1 -19
- package/hooks/closeout.cjs +33 -0
- package/hooks/owner-stops.cjs +154 -0
- package/hooks/plan-package.cjs +177 -0
- package/hooks/planning-review.cjs +9 -14
- package/hooks/policy.cjs +45 -0
- package/hooks/reconcile.cjs +145 -0
- package/hooks/te-host.cjs +25 -1
- package/hooks/transition-order.cjs +12 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/schemas/authority.schema.json +55 -34
- package/schemas/event.schema.json +59 -0
- package/schemas/implementation.schema.json +41 -18
- package/schemas/journey.schema.json +142 -35
- package/skills/bearing-lite/SKILL.md +14 -15
- package/skills/bearing-lite/references/lineups.md +31 -0
- package/skills/bearing-lite/references/owner-stops.md +122 -0
- package/skills/bearing-lite/references/review-policy.md +5 -0
- package/skills/bearing-lite/templates/task.md +4 -1
- package/skills/explorer/SKILL.md +3 -3
- package/skills/light-implementer/SKILL.md +53 -0
- package/skills/map-the-route/SKILL.md +5 -3
- package/skills/map-the-route/references/artifact-grammar.md +23 -6
- package/skills/plan-integrator/SKILL.md +11 -2
- package/skills/requirements-engineer/SKILL.md +57 -0
- package/skills/scribe/SKILL.md +4 -0
- package/skills/set-bearings/SKILL.md +16 -7
- package/skills/set-bearings/templates/workspace.md +28 -0
- package/skills/test-engineer/SKILL.md +2 -0
- package/skills/bearing-lite/templates/default-role-lineup.md +0 -32
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** Deterministically apply evidence events to Journey state (#64). */
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
|
|
6
|
+
const REQUIRED = [
|
|
7
|
+
"schema_version", "event_id", "source", "journey", "repository", "unit",
|
|
8
|
+
"candidate_revision", "generation", "evidence", "transition", "occurred_at",
|
|
9
|
+
];
|
|
10
|
+
const KINDS = new Set(["implementation", "verification", "assurance", "acceptance", "merge", "issue_close"]);
|
|
11
|
+
const FIELDS = {
|
|
12
|
+
implementation: "implementation",
|
|
13
|
+
verification: "verification",
|
|
14
|
+
assurance: "assurance",
|
|
15
|
+
acceptance: "acceptance",
|
|
16
|
+
merge: "observed_merge",
|
|
17
|
+
issue_close: "observed_issue_close",
|
|
18
|
+
};
|
|
19
|
+
const PREDECESSORS = { verification: "implementation", assurance: "verification", acceptance: "assurance" };
|
|
20
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
21
|
+
const nonempty = (value) => typeof value === "string" && value.length > 0;
|
|
22
|
+
const only = (value, names) => Object.keys(value).every((name) => names.includes(name));
|
|
23
|
+
const dateTime = (value) => nonempty(value) && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && !Number.isNaN(Date.parse(value));
|
|
24
|
+
|
|
25
|
+
function receiptFor(event) {
|
|
26
|
+
return {
|
|
27
|
+
event_id: event?.event_id ?? null,
|
|
28
|
+
applied: false,
|
|
29
|
+
code: "invalid_event",
|
|
30
|
+
transition: event?.transition?.kind ?? null,
|
|
31
|
+
slice: event?.unit?.slice ?? null,
|
|
32
|
+
candidate_revision: event?.candidate_revision ?? null,
|
|
33
|
+
evidence_sha256: event?.evidence?.sha256 ?? null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function structuralError(event) {
|
|
38
|
+
if (!object(event) || !REQUIRED.every((name) => Object.hasOwn(event, name)) || !only(event, REQUIRED)) return true;
|
|
39
|
+
if (event.schema_version !== 1 || !nonempty(event.event_id) || !nonempty(event.source) ||
|
|
40
|
+
!nonempty(event.journey) || !nonempty(event.repository) || !nonempty(event.candidate_revision) ||
|
|
41
|
+
!Number.isInteger(event.generation) || event.generation < 0 || !dateTime(event.occurred_at)) return true;
|
|
42
|
+
if (!object(event.unit) || !only(event.unit, ["wave", "slice"]) || !nonempty(event.unit.slice) ||
|
|
43
|
+
(Object.hasOwn(event.unit, "wave") && !nonempty(event.unit.wave))) return true;
|
|
44
|
+
if (!object(event.evidence) || !only(event.evidence, ["ref", "sha256"])) return true;
|
|
45
|
+
if (!object(event.transition) || !only(event.transition, ["kind", "verdict"]) ||
|
|
46
|
+
!KINDS.has(event.transition.kind) || !nonempty(event.transition.verdict)) return true;
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function reject(state, receipt, code) {
|
|
51
|
+
return { state, receipt: { ...receipt, code, reason: code } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyEvent(state, event, options = {}) {
|
|
55
|
+
const receipt = receiptFor(event);
|
|
56
|
+
if (structuralError(event)) return reject(state, receipt, "invalid_event");
|
|
57
|
+
if (options.expected_version !== undefined && options.expected_version !== state.version) {
|
|
58
|
+
return reject(state, receipt, "stale_write");
|
|
59
|
+
}
|
|
60
|
+
if (state.applied_event_ids.includes(event.event_id)) return reject(state, receipt, "duplicate_event");
|
|
61
|
+
if (event.journey !== state.journey || event.repository !== state.repository) {
|
|
62
|
+
return reject(state, receipt, "unrelated_journey");
|
|
63
|
+
}
|
|
64
|
+
if (event.generation < state.generation) return reject(state, receipt, "stale_generation");
|
|
65
|
+
|
|
66
|
+
const hasEvidence = nonempty(event.evidence.ref) && nonempty(event.evidence.sha256);
|
|
67
|
+
if (!hasEvidence && event.transition.verdict === "PASS") return reject(state, receipt, "bare_pass");
|
|
68
|
+
if (!hasEvidence) return reject(state, receipt, "missing_evidence");
|
|
69
|
+
if (!/^[0-9a-f]{64}$/.test(event.evidence.sha256)) return reject(state, receipt, "invalid_event");
|
|
70
|
+
|
|
71
|
+
const kind = event.transition.kind;
|
|
72
|
+
const slice = state.slices[event.unit.slice] || {};
|
|
73
|
+
const field = FIELDS[kind];
|
|
74
|
+
const prior = slice[field];
|
|
75
|
+
const history = prior ? {
|
|
76
|
+
...prior.history,
|
|
77
|
+
[prior.candidate_revision]: { verdict: prior.verdict, sha256: prior.evidence.sha256 },
|
|
78
|
+
} : {};
|
|
79
|
+
const h = Object.hasOwn(history, event.candidate_revision) ? history[event.candidate_revision] : undefined;
|
|
80
|
+
if (h && (h.verdict !== event.transition.verdict || h.sha256 !== event.evidence.sha256)) {
|
|
81
|
+
return reject(state, receipt, "conflicting_receipt");
|
|
82
|
+
}
|
|
83
|
+
if (h) {
|
|
84
|
+
return reject(state, receipt, event.candidate_revision === prior.candidate_revision ? "duplicate_event" : "stale_candidate");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const implementation = slice.implementation;
|
|
88
|
+
if (["verification", "assurance", "acceptance"].includes(kind) &&
|
|
89
|
+
implementation?.candidate_revision !== event.candidate_revision) {
|
|
90
|
+
return reject(state, receipt, implementation ? "stale_candidate" : "predecessor_missing");
|
|
91
|
+
}
|
|
92
|
+
const predecessor = PREDECESSORS[kind];
|
|
93
|
+
if (predecessor && slice[predecessor]?.candidate_revision !== event.candidate_revision) {
|
|
94
|
+
return reject(state, receipt, "predecessor_missing");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const entry = {
|
|
98
|
+
candidate_revision: event.candidate_revision,
|
|
99
|
+
verdict: event.transition.verdict,
|
|
100
|
+
evidence: { ...event.evidence },
|
|
101
|
+
event_id: event.event_id,
|
|
102
|
+
history: { ...history, [event.candidate_revision]: { verdict: event.transition.verdict, sha256: event.evidence.sha256 } },
|
|
103
|
+
};
|
|
104
|
+
const next = {
|
|
105
|
+
...state,
|
|
106
|
+
generation: Math.max(state.generation, event.generation),
|
|
107
|
+
version: state.version + 1,
|
|
108
|
+
applied_event_ids: [...state.applied_event_ids, event.event_id],
|
|
109
|
+
slices: { ...state.slices, [event.unit.slice]: { ...slice, [field]: entry } },
|
|
110
|
+
};
|
|
111
|
+
return { state: next, receipt: { ...receipt, applied: true, code: "applied" } };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function replay(state, events) {
|
|
115
|
+
const receipts = [];
|
|
116
|
+
let current = state;
|
|
117
|
+
for (const event of events) {
|
|
118
|
+
const result = applyEvent(current, event);
|
|
119
|
+
current = result.state;
|
|
120
|
+
receipts.push(result.receipt);
|
|
121
|
+
}
|
|
122
|
+
return { state: current, receipts };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function main(args = process.argv.slice(2)) {
|
|
126
|
+
const [stateFile, eventsFile] = args;
|
|
127
|
+
try {
|
|
128
|
+
if (!stateFile || !eventsFile) throw new Error("usage: node hooks/reconcile.cjs <state.json> <events.ndjson>");
|
|
129
|
+
const state = JSON.parse(fs.readFileSync(stateFile, "utf8"));
|
|
130
|
+
const events = fs.readFileSync(eventsFile, "utf8").split(/\r?\n/).filter(Boolean).map(JSON.parse);
|
|
131
|
+
const result = replay(state, events);
|
|
132
|
+
const temporary = `${stateFile}.tmp-${process.pid}`;
|
|
133
|
+
fs.writeFileSync(temporary, JSON.stringify(result.state, null, 2) + "\n");
|
|
134
|
+
fs.renameSync(temporary, stateFile);
|
|
135
|
+
for (const receipt of result.receipts) process.stdout.write(JSON.stringify(receipt) + "\n");
|
|
136
|
+
return 0;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
process.stderr.write(`${error.message}\n`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { applyEvent, replay };
|
|
144
|
+
|
|
145
|
+
if (require.main === module) process.exitCode = main();
|
package/hooks/te-host.cjs
CHANGED
|
@@ -584,6 +584,28 @@ function readStdinSync() {
|
|
|
584
584
|
}
|
|
585
585
|
}
|
|
586
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Project the internal response onto the host wire (#71). Codex validates
|
|
589
|
+
* hookSpecificOutput with additionalProperties:false per event, so only host
|
|
590
|
+
* fields go to stdout: a PreToolUse deny, a Stop/SubagentStop block, or an
|
|
591
|
+
* empty object (allow / fail-open). The full internal verdict goes to stderr
|
|
592
|
+
* for audit; the exit stays 0 so no host reads stderr as a blocking reason.
|
|
593
|
+
*/
|
|
594
|
+
function toWire(response) {
|
|
595
|
+
const inner = isPlainObject(response.hookSpecificOutput) ? response.hookSpecificOutput : {};
|
|
596
|
+
if (inner.permissionDecision === "deny") {
|
|
597
|
+
return {
|
|
598
|
+
hookSpecificOutput: {
|
|
599
|
+
hookEventName: "PreToolUse",
|
|
600
|
+
permissionDecision: "deny",
|
|
601
|
+
permissionDecisionReason: inner.permissionDecisionReason,
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
if (response.decision === "block") return { decision: "block", reason: response.reason };
|
|
606
|
+
return {};
|
|
607
|
+
}
|
|
608
|
+
|
|
587
609
|
function main() {
|
|
588
610
|
let raw = "";
|
|
589
611
|
let response;
|
|
@@ -600,7 +622,8 @@ function main() {
|
|
|
600
622
|
"adapter_failure: the test-engineering adapter could not run; unavailable"
|
|
601
623
|
);
|
|
602
624
|
}
|
|
603
|
-
process.
|
|
625
|
+
process.stderr.write(JSON.stringify(response) + "\n");
|
|
626
|
+
process.stdout.write(JSON.stringify(toWire(response)) + "\n");
|
|
604
627
|
process.exit(0);
|
|
605
628
|
}
|
|
606
629
|
|
|
@@ -613,6 +636,7 @@ module.exports = {
|
|
|
613
636
|
supportChannel,
|
|
614
637
|
handle,
|
|
615
638
|
toHostResponse,
|
|
639
|
+
toWire,
|
|
616
640
|
};
|
|
617
641
|
|
|
618
642
|
if (require.main === module) {
|
|
@@ -12,6 +12,7 @@ const OUTCOMES = Object.freeze(["ADVISE", "REROUTE", "BLOCK", "UNAVAILABLE"]);
|
|
|
12
12
|
const ENFORCEMENT = "procedural";
|
|
13
13
|
const { evaluatePlanningReview } = require("./planning-review.cjs");
|
|
14
14
|
const { evaluateAssuranceBudget } = require("./assurance-budget.cjs");
|
|
15
|
+
const { evaluateOwnerStop } = require("./owner-stops.cjs");
|
|
15
16
|
|
|
16
17
|
const RECOVERY_UNAVAILABLE =
|
|
17
18
|
"Keep the current state, run the transition checklist procedurally, and record the result before retrying";
|
|
@@ -132,6 +133,17 @@ function evaluate(input) {
|
|
|
132
133
|
return result("ADVISE", "channel_open", RECOVERY_CHANNEL);
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
if (input.action_kind === "owner_stop_check") {
|
|
137
|
+
const stop = evaluateOwnerStop(input.owner_stop);
|
|
138
|
+
return {
|
|
139
|
+
...result(stop.disposition === "NEEDS_MORE_EVIDENCE" ? "UNAVAILABLE" :
|
|
140
|
+
stop.disposition === "ASK" ? "REROUTE" : "ADVISE",
|
|
141
|
+
"owner_stop:" + stop.disposition + ":" + stop.reason,
|
|
142
|
+
"Apply references/owner-stops.md; this check grants no action or dispatch authority"),
|
|
143
|
+
owner_stop: stop,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
135
147
|
if (input.action_kind === "planning_review_transition") {
|
|
136
148
|
const verdict = evaluatePlanningReview(input.planning_review);
|
|
137
149
|
if (verdict.outcome === "PASS") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alphazede/bearing-lite",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Skills-first Agent Plugin for planning, routing, bounded execution, and independent review of repository work—without CLI, MCP, server, or hidden runtime state.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-plugins",
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "bearing-lite",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Skills-first portable plugin that routes repository work through the smallest valid planning stages and agent roles, using project Markdown as the only task record.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "William Rumph / AlphaZede",
|
|
@@ -23,28 +23,22 @@
|
|
|
23
23
|
"approval_receipt"
|
|
24
24
|
],
|
|
25
25
|
"properties": {
|
|
26
|
-
"schema_version": {
|
|
27
|
-
"schema": {
|
|
28
|
-
"id": {
|
|
29
|
-
"state": {
|
|
30
|
-
"subject": {
|
|
31
|
-
"repositories": {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
"baseline": { "type": "object", "minProperties": 1, "additionalProperties": true },
|
|
36
|
-
"granting_owner_decisions": {
|
|
37
|
-
"type": "array",
|
|
38
|
-
"items": { "type": "string", "minLength": 1 }
|
|
39
|
-
},
|
|
26
|
+
"schema_version": {"type": "string", "minLength": 1},
|
|
27
|
+
"schema": {"type": "string", "minLength": 1, "description": "Envelope schema identity"},
|
|
28
|
+
"id": {"type": "string", "minLength": 1, "description": "Authority envelope ID"},
|
|
29
|
+
"state": {"type": "string", "minLength": 1},
|
|
30
|
+
"subject": {"type": "object", "minProperties": 1, "additionalProperties": true},
|
|
31
|
+
"repositories": {"type": "array", "items": {"type": "object", "additionalProperties": true}},
|
|
32
|
+
"baseline": {"type": "object", "minProperties": 1, "additionalProperties": true},
|
|
33
|
+
"granting_owner_decisions": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
40
34
|
"allowed": {
|
|
41
35
|
"type": "object",
|
|
42
36
|
"additionalProperties": true,
|
|
43
37
|
"required": ["scope", "actions", "paths"],
|
|
44
38
|
"properties": {
|
|
45
|
-
"scope": {
|
|
46
|
-
"actions": {
|
|
47
|
-
"paths": {
|
|
39
|
+
"scope": {"type": "array", "items": {"type": "string"}},
|
|
40
|
+
"actions": {"type": "array", "items": {"type": "string"}},
|
|
41
|
+
"paths": {"type": "array", "items": {"type": "string"}}
|
|
48
42
|
}
|
|
49
43
|
},
|
|
50
44
|
"prohibited": {
|
|
@@ -52,24 +46,51 @@
|
|
|
52
46
|
"additionalProperties": true,
|
|
53
47
|
"required": ["scope", "actions", "paths"],
|
|
54
48
|
"properties": {
|
|
55
|
-
"scope": {
|
|
56
|
-
"actions": {
|
|
57
|
-
"paths": {
|
|
49
|
+
"scope": {"type": "array", "items": {"type": "string"}},
|
|
50
|
+
"actions": {"type": "array", "items": {"type": "string"}},
|
|
51
|
+
"paths": {"type": "array", "items": {"type": "string"}}
|
|
58
52
|
}
|
|
59
53
|
},
|
|
60
|
-
"role_grants": {
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
"effective": { "type": "boolean", "description": "Machine-enforced effectiveness. Boolean only: a string is not an effectiveness decision." },
|
|
65
|
-
"expiry_conditions": {
|
|
66
|
-
"type": "array",
|
|
67
|
-
"items": { "type": "string", "minLength": 1 }
|
|
68
|
-
},
|
|
69
|
-
"supersedes": {
|
|
70
|
-
"type": "array",
|
|
71
|
-
"items": { "type": "string" }
|
|
54
|
+
"role_grants": {"type": "array", "items": {"type": "object", "additionalProperties": true}},
|
|
55
|
+
"effective": {
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
"description": "Machine-enforced effectiveness. Boolean only: a string is not an effectiveness decision."
|
|
72
58
|
},
|
|
73
|
-
"
|
|
74
|
-
|
|
59
|
+
"expiry_conditions": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
60
|
+
"supersedes": {"type": "array", "items": {"type": "string"}},
|
|
61
|
+
"approval_receipt": {"type": "object", "additionalProperties": true},
|
|
62
|
+
"continuation": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"required": ["granted", "owner_decision_id", "exclusions", "expires_at"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"granted": {"type": "boolean"},
|
|
68
|
+
"owner_decision_id": {"type": "string", "minLength": 1},
|
|
69
|
+
"exclusions": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"uniqueItems": true,
|
|
72
|
+
"items": {"type": "string", "minLength": 1},
|
|
73
|
+
"allOf": [
|
|
74
|
+
{"contains": {"const": "scope_change"}},
|
|
75
|
+
{"contains": {"const": "budget_exhaustion"}},
|
|
76
|
+
{"contains": {"const": "owner_hold"}},
|
|
77
|
+
{"contains": {"const": "owner_only_actions"}}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"expires_at": {"type": ["string", "null"], "format": "date-time", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$"}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"allOf": [
|
|
85
|
+
{
|
|
86
|
+
"if": {"required": ["continuation"]},
|
|
87
|
+
"then": {
|
|
88
|
+
"properties": {
|
|
89
|
+
"expiry_conditions": {"minItems": 1},
|
|
90
|
+
"granting_owner_decisions": {"minItems": 1},
|
|
91
|
+
"approval_receipt": {"minProperties": 1}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
]
|
|
75
96
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/alphazede/bearing-lite/schemas/event.schema.json",
|
|
4
|
+
"title": "Bearing Lite evidence event",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"event_id",
|
|
10
|
+
"source",
|
|
11
|
+
"journey",
|
|
12
|
+
"repository",
|
|
13
|
+
"unit",
|
|
14
|
+
"candidate_revision",
|
|
15
|
+
"generation",
|
|
16
|
+
"evidence",
|
|
17
|
+
"transition",
|
|
18
|
+
"occurred_at"
|
|
19
|
+
],
|
|
20
|
+
"properties": {
|
|
21
|
+
"schema_version": { "const": 1 },
|
|
22
|
+
"event_id": { "type": "string", "minLength": 1 },
|
|
23
|
+
"source": { "type": "string", "minLength": 1 },
|
|
24
|
+
"journey": { "type": "string", "minLength": 1 },
|
|
25
|
+
"repository": { "type": "string", "minLength": 1 },
|
|
26
|
+
"unit": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["slice"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"wave": { "type": "string", "minLength": 1 },
|
|
32
|
+
"slice": { "type": "string", "minLength": 1 }
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"candidate_revision": { "type": "string", "minLength": 1 },
|
|
36
|
+
"generation": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"evidence": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["ref", "sha256"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
43
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"transition": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"required": ["kind", "verdict"],
|
|
50
|
+
"properties": {
|
|
51
|
+
"kind": {
|
|
52
|
+
"enum": ["implementation", "verification", "assurance", "acceptance", "merge", "issue_close"]
|
|
53
|
+
},
|
|
54
|
+
"verdict": { "type": "string", "minLength": 1 }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"occurred_at": { "type": "string", "format": "date-time" }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -64,27 +64,50 @@
|
|
|
64
64
|
"human_decision": { "type": ["string", "object", "null"] },
|
|
65
65
|
"authority_id": { "type": ["string", "null"] },
|
|
66
66
|
"dispatchable": { "type": "boolean" },
|
|
67
|
-
"slice_status": { "type": "string" }
|
|
67
|
+
"slice_status": { "type": "string" },
|
|
68
|
+
"work_class": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["light", "judgement"],
|
|
71
|
+
"description": "light: inputs fully determine the output and the packet's command verifies it (skills/light-implementer). Default judgement."
|
|
72
|
+
},
|
|
73
|
+
"work_class_reason": { "type": "string", "minLength": 1 }
|
|
68
74
|
},
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
{
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
"allOf": [
|
|
76
|
+
{
|
|
77
|
+
"if": {
|
|
78
|
+
"not": {
|
|
79
|
+
"anyOf": [
|
|
80
|
+
{
|
|
81
|
+
"properties": { "dispatchable": { "const": false } },
|
|
82
|
+
"required": ["dispatchable"]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"properties": { "slice_status": { "const": "publication-blocked" } },
|
|
86
|
+
"required": ["slice_status"]
|
|
87
|
+
}
|
|
88
|
+
]
|
|
79
89
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
90
|
+
},
|
|
91
|
+
"then": {
|
|
92
|
+
"properties": {
|
|
93
|
+
"authority_id": { "type": "string", "minLength": 1 }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"if": {
|
|
99
|
+
"properties": { "work_class": { "const": "light" } },
|
|
100
|
+
"required": ["work_class"]
|
|
101
|
+
},
|
|
102
|
+
"then": {
|
|
103
|
+
"required": ["work_class_reason"],
|
|
104
|
+
"properties": {
|
|
105
|
+
"role": { "const": "Light Implementer" },
|
|
106
|
+
"command_ids": { "minItems": 1 }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
86
109
|
}
|
|
87
|
-
|
|
110
|
+
]
|
|
88
111
|
}
|
|
89
112
|
},
|
|
90
113
|
"properties": {
|