@alphazede/bearing-lite 0.1.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.
Files changed (35) hide show
  1. package/CODE_OF_CONDUCT.md +45 -0
  2. package/CONTRIBUTING.md +74 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/README.md +213 -0
  5. package/SECURITY.md +89 -0
  6. package/guide/migration.md +168 -0
  7. package/hooks/activation.cjs +163 -0
  8. package/hooks/closeout.cjs +242 -0
  9. package/hooks/protected-action.cjs +229 -0
  10. package/hooks/transition-order.cjs +290 -0
  11. package/package.json +37 -0
  12. package/plugin.json +22 -0
  13. package/skills/bearing-lite/SKILL.md +40 -0
  14. package/skills/bearing-lite/assets/role-routing.png +0 -0
  15. package/skills/bearing-lite/assets/task-state.png +0 -0
  16. package/skills/bearing-lite/references/role-routing.mmd +34 -0
  17. package/skills/bearing-lite/references/task-state.md +42 -0
  18. package/skills/bearing-lite/references/task-state.mmd +29 -0
  19. package/skills/bearing-lite/templates/task.md +55 -0
  20. package/skills/crewmate/SKILL.md +37 -0
  21. package/skills/delegate-authority/SKILL.md +38 -0
  22. package/skills/explorer/SKILL.md +38 -0
  23. package/skills/gather-supplies/SKILL.md +33 -0
  24. package/skills/map-the-route/SKILL.md +33 -0
  25. package/skills/map-the-route/references/artifact-grammar.md +83 -0
  26. package/skills/navigator/SKILL.md +44 -0
  27. package/skills/park-ranger/SKILL.md +38 -0
  28. package/skills/repository-fit/SKILL.md +33 -0
  29. package/skills/set-bearings/SKILL.md +33 -0
  30. package/skills/sub-explorer/SKILL.md +37 -0
  31. package/skills/surveyor/SKILL.md +38 -0
  32. package/skills/trail-boss/SKILL.md +38 -0
  33. package/skills/trail-boss/agents/openai.yaml +4 -0
  34. package/skills/validator/SKILL.md +41 -0
  35. package/skills/validator/references/grading-rubric.md +39 -0
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ /**
5
+ * Bearing Lite protected-action integrity adapter (CONTRACT-HOOK-01).
6
+ * BLOCKs only explicit positive protected-action violations.
7
+ * Repair, status, owner communication, and safe rollback remain reachable.
8
+ * Infrastructure failure never becomes BLOCK or implicit permission.
9
+ */
10
+
11
+ const HOOK_CLASS = "protected_action";
12
+ const OUTCOMES = Object.freeze(["ADVISE", "REROUTE", "BLOCK", "UNAVAILABLE"]);
13
+ const ENFORCEMENT = "procedural";
14
+
15
+ const RECOVERY_UNAVAILABLE =
16
+ "Keep the action pending; obtain normal owner approval when owner-only, otherwise complete the equivalent procedural integrity check before retrying";
17
+ const RECOVERY_ALLOW =
18
+ "Proceed only within declared authority; leave the project plan as the only task record";
19
+ const RECOVERY_CHANNEL =
20
+ "Keep repair, status, owner communication, and safe rollback available while the protected action stays pending";
21
+ const RECOVERY_OWNER =
22
+ "Route to OWNER_DECISION_REQUIRED with evidence, blocker, and the smallest owner choices; do not expand authority";
23
+
24
+ const SAFE_CHANNELS = new Set([
25
+ "repair",
26
+ "status",
27
+ "owner_communication",
28
+ "safe_rollback",
29
+ ]);
30
+
31
+ /** Explicit positive violation categories (design hard-block list). */
32
+ const VIOLATION_KEYS = Object.freeze([
33
+ "owner_authority_violation",
34
+ "destructive_public_without_authority",
35
+ "secret_exposure",
36
+ "repository_ambiguous",
37
+ "evidence_dishonest",
38
+ ]);
39
+
40
+ /**
41
+ * Owner authorization may waive only authority-dependent actions.
42
+ * Secret exposure, dishonest evidence, and repository ambiguity are never waivable.
43
+ */
44
+ const WAIVABLE_VIOLATIONS = new Set([
45
+ "owner_authority_violation",
46
+ "destructive_public_without_authority",
47
+ ]);
48
+
49
+ function isPlainObject(value) {
50
+ return value !== null && typeof value === "object" && !Array.isArray(value);
51
+ }
52
+
53
+ function result(outcome, reason, recovery, extra) {
54
+ const body = {
55
+ hook_class: HOOK_CLASS,
56
+ outcome,
57
+ reason,
58
+ recovery,
59
+ enforcement: ENFORCEMENT,
60
+ };
61
+ if (extra && typeof extra.protected_action === "string" && extra.protected_action) {
62
+ body.protected_action = extra.protected_action;
63
+ }
64
+ return body;
65
+ }
66
+
67
+ function unavailable(reason) {
68
+ return result("UNAVAILABLE", reason, RECOVERY_UNAVAILABLE);
69
+ }
70
+
71
+ function actionId(input) {
72
+ if (typeof input.protected_action === "string" && input.protected_action.trim()) {
73
+ return input.protected_action.trim();
74
+ }
75
+ if (typeof input.action === "string" && input.action.trim()) {
76
+ return input.action.trim();
77
+ }
78
+ return "protected_action";
79
+ }
80
+
81
+ /**
82
+ * @param {unknown} input
83
+ */
84
+ function evaluate(input) {
85
+ try {
86
+ if (input === undefined || input === null) {
87
+ return unavailable("missing_input");
88
+ }
89
+ if (typeof input === "string") {
90
+ try {
91
+ input = JSON.parse(input);
92
+ } catch {
93
+ return unavailable("malformed_input");
94
+ }
95
+ }
96
+ if (!isPlainObject(input)) {
97
+ return unavailable("malformed_input");
98
+ }
99
+
100
+ if (input.infrastructure_failure) {
101
+ return unavailable(String(input.infrastructure_failure) || "infrastructure_failure");
102
+ }
103
+
104
+ // Never block the communication path needed to resolve a finding.
105
+ if (SAFE_CHANNELS.has(input.channel) || SAFE_CHANNELS.has(input.action_kind)) {
106
+ return result("ADVISE", "channel_open", RECOVERY_CHANNEL);
107
+ }
108
+
109
+ const id = actionId(input);
110
+ const ownerAuthorized = input.owner_authorized === true;
111
+
112
+ // Collect only explicit positive violation flags (truthy booleans or listed categories).
113
+ const categories = Array.isArray(input.violation_categories)
114
+ ? input.violation_categories.map(String)
115
+ : [];
116
+ const hits = [];
117
+
118
+ for (const key of VIOLATION_KEYS) {
119
+ if (input[key] === true || categories.includes(key)) {
120
+ hits.push(key);
121
+ }
122
+ }
123
+
124
+ // Generic explicit_violation requires a named category; bare true alone is not enough
125
+ // to invent a security finding.
126
+ if (input.explicit_violation === true && hits.length === 0) {
127
+ if (typeof input.reason_code === "string" && input.reason_code.trim()) {
128
+ hits.push(input.reason_code.trim());
129
+ }
130
+ }
131
+
132
+ if (hits.length > 0) {
133
+ const nonWaivable = hits.filter((h) => !WAIVABLE_VIOLATIONS.has(h));
134
+ if (nonWaivable.length > 0) {
135
+ // Non-waivable integrity findings always hard-block, even with owner_authorized.
136
+ return result(
137
+ "BLOCK",
138
+ "protected_violation:" + nonWaivable.join("+"),
139
+ RECOVERY_OWNER,
140
+ { protected_action: id }
141
+ );
142
+ }
143
+ if (!ownerAuthorized) {
144
+ return result(
145
+ "BLOCK",
146
+ "protected_violation:" + hits.join("+"),
147
+ RECOVERY_OWNER,
148
+ { protected_action: id }
149
+ );
150
+ }
151
+ // All hits are authority-dependent and explicitly owner-authorized.
152
+ return result(
153
+ "ADVISE",
154
+ "owner_authorized:" + hits.join("+"),
155
+ RECOVERY_ALLOW,
156
+ { protected_action: id }
157
+ );
158
+ }
159
+
160
+ // Owner-only action declared without owner confirmation: keep pending (not a fabricated block
161
+ // unless a positive violation flag is set). Report REROUTE to owner when marked owner_only.
162
+ if (input.owner_only === true && !ownerAuthorized) {
163
+ return result(
164
+ "REROUTE",
165
+ "owner_confirmation_required",
166
+ RECOVERY_OWNER,
167
+ { protected_action: id }
168
+ );
169
+ }
170
+
171
+ return result("ADVISE", "protected_action_clear", RECOVERY_ALLOW, {
172
+ protected_action: id,
173
+ });
174
+ } catch {
175
+ return unavailable("adapter_exception");
176
+ }
177
+ }
178
+
179
+ function readStdinSync() {
180
+ try {
181
+ return require("node:fs").readFileSync(0, "utf8");
182
+ } catch (err) {
183
+ const code = err && err.code;
184
+ if (code === "EAGAIN" || code === "EOF") return "";
185
+ throw err;
186
+ }
187
+ }
188
+
189
+ function main() {
190
+ let raw = "";
191
+ try {
192
+ raw = readStdinSync();
193
+ } catch {
194
+ process.stdout.write(JSON.stringify(unavailable("stdin_read_failure")) + "\n");
195
+ process.exit(0);
196
+ return;
197
+ }
198
+
199
+ const trimmed = raw.trim();
200
+ if (!trimmed) {
201
+ process.stdout.write(JSON.stringify(unavailable("missing_input")) + "\n");
202
+ process.exit(0);
203
+ return;
204
+ }
205
+
206
+ let parsed;
207
+ try {
208
+ parsed = JSON.parse(trimmed);
209
+ } catch {
210
+ process.stdout.write(JSON.stringify(unavailable("malformed_input")) + "\n");
211
+ process.exit(0);
212
+ return;
213
+ }
214
+
215
+ process.stdout.write(JSON.stringify(evaluate(parsed)) + "\n");
216
+ process.exit(0);
217
+ }
218
+
219
+ module.exports = {
220
+ HOOK_CLASS,
221
+ OUTCOMES,
222
+ ENFORCEMENT,
223
+ VIOLATION_KEYS,
224
+ evaluate,
225
+ };
226
+
227
+ if (require.main === module) {
228
+ main();
229
+ }
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ /**
5
+ * Bearing Lite transition-order integrity adapter (CONTRACT-HOOK-01).
6
+ * Reroutes missing prerequisites; BLOCKs only explicit positive sequence violations.
7
+ * Never fabricates BLOCK from infrastructure failure. Safe channels stay open.
8
+ */
9
+
10
+ const HOOK_CLASS = "transition";
11
+ const OUTCOMES = Object.freeze(["ADVISE", "REROUTE", "BLOCK", "UNAVAILABLE"]);
12
+ const ENFORCEMENT = "procedural";
13
+
14
+ const RECOVERY_UNAVAILABLE =
15
+ "Keep the current state, run the transition checklist procedurally, and record the result before retrying";
16
+ const RECOVERY_ALLOW =
17
+ "Proceed with the requested transition and update the project plan as the only task record";
18
+ const RECOVERY_CHANNEL =
19
+ "Keep repair, status, owner communication, and safe rollback available; do not treat them as sequence violations";
20
+
21
+ /** Legal directed edges from CONTRACT-STATE-01 / task-state text. */
22
+ const LEGAL = Object.freeze({
23
+ PROPOSED: ["READY", "WAITING_ON"],
24
+ READY: ["IN_PROGRESS", "OWNER_DECISION_REQUIRED"],
25
+ WAITING_ON: ["READY", "EVIDENCE_READY", "CANCELLED"],
26
+ IN_PROGRESS: ["EVIDENCE_READY", "CORRECTION_REQUIRED", "OWNER_DECISION_REQUIRED"],
27
+ EVIDENCE_READY: [
28
+ "VALIDATING",
29
+ "REVIEWING",
30
+ "ACCEPTANCE",
31
+ "WAITING_ON",
32
+ ],
33
+ VALIDATING: ["EVIDENCE_READY", "CORRECTION_REQUIRED"],
34
+ REVIEWING: ["EVIDENCE_READY", "CORRECTION_REQUIRED", "OWNER_DECISION_REQUIRED"],
35
+ ACCEPTANCE: ["COMPLETE", "CORRECTION_REQUIRED"],
36
+ CORRECTION_REQUIRED: ["READY", "OWNER_DECISION_REQUIRED"],
37
+ OWNER_DECISION_REQUIRED: ["READY", "CANCELLED"],
38
+ COMPLETE: [],
39
+ CANCELLED: [],
40
+ });
41
+
42
+ const SAFE_CHANNELS = new Set([
43
+ "repair",
44
+ "status",
45
+ "owner_communication",
46
+ "safe_rollback",
47
+ ]);
48
+
49
+ function isPlainObject(value) {
50
+ return value !== null && typeof value === "object" && !Array.isArray(value);
51
+ }
52
+
53
+ function result(outcome, reason, recovery, extra) {
54
+ const body = {
55
+ hook_class: HOOK_CLASS,
56
+ outcome,
57
+ reason,
58
+ recovery,
59
+ enforcement: ENFORCEMENT,
60
+ };
61
+ if (extra && typeof extra.protected_action === "string" && extra.protected_action) {
62
+ body.protected_action = extra.protected_action;
63
+ }
64
+ return body;
65
+ }
66
+
67
+ function unavailable(reason) {
68
+ return result("UNAVAILABLE", reason, RECOVERY_UNAVAILABLE);
69
+ }
70
+
71
+ function normalizeAssurance(value) {
72
+ if (value === undefined || value === null || value === "" || value === "none") {
73
+ return [];
74
+ }
75
+ if (Array.isArray(value)) {
76
+ return value.map(String).filter((s) => s && s !== "none");
77
+ }
78
+ return [String(value)].filter((s) => s && s !== "none");
79
+ }
80
+
81
+ /** Normalize role labels for order comparison (Validator vs validator vs park-ranger). */
82
+ function normalizeRoleKey(role) {
83
+ return String(role)
84
+ .trim()
85
+ .toLowerCase()
86
+ .replace(/[\s_]+/g, "-");
87
+ }
88
+
89
+ /**
90
+ * Map the next missing assurance role to the state that should receive it.
91
+ * Validator -> VALIDATING, Park Ranger -> REVIEWING, Surveyor -> ACCEPTANCE.
92
+ */
93
+ function assuranceTargetState(role) {
94
+ const key = normalizeRoleKey(role);
95
+ if (key === "validator") return "VALIDATING";
96
+ if (key === "park-ranger" || key === "parkranger") return "REVIEWING";
97
+ if (key === "surveyor") return "ACCEPTANCE";
98
+ return null;
99
+ }
100
+
101
+ /**
102
+ * @param {unknown} input
103
+ */
104
+ function evaluate(input) {
105
+ try {
106
+ if (input === undefined || input === null) {
107
+ return unavailable("missing_input");
108
+ }
109
+ if (typeof input === "string") {
110
+ try {
111
+ input = JSON.parse(input);
112
+ } catch {
113
+ return unavailable("malformed_input");
114
+ }
115
+ }
116
+ if (!isPlainObject(input)) {
117
+ return unavailable("malformed_input");
118
+ }
119
+
120
+ if (input.infrastructure_failure) {
121
+ return unavailable(String(input.infrastructure_failure) || "infrastructure_failure");
122
+ }
123
+
124
+ if (SAFE_CHANNELS.has(input.channel) || SAFE_CHANNELS.has(input.action_kind)) {
125
+ return result("ADVISE", "channel_open", RECOVERY_CHANNEL);
126
+ }
127
+
128
+ const from = typeof input.from_state === "string" ? input.from_state.trim() : "";
129
+ const to = typeof input.to_state === "string" ? input.to_state.trim() : "";
130
+ if (!from || !to) {
131
+ return unavailable("malformed_input");
132
+ }
133
+ if (!Object.prototype.hasOwnProperty.call(LEGAL, from)) {
134
+ return unavailable("unknown_from_state");
135
+ }
136
+
137
+ const legalTargets = LEGAL[from] || [];
138
+ const required = normalizeAssurance(input.required_assurance);
139
+ const completed = Array.isArray(input.assurance_completed)
140
+ ? input.assurance_completed.map(String)
141
+ : [];
142
+ const missingAssurance = required.filter((role) => !completed.includes(role));
143
+ const prerequisitesMet = input.prerequisites_met !== false;
144
+ const explicitSkip = input.skip_required_step === true;
145
+ const afterReroute = input.after_reroute === true;
146
+ const invalidatesDependents = input.invalidates_dependents === true;
147
+
148
+ // Hard block only for explicit positive violations (DEC-HOOK-03 / DEC-HOOK-02).
149
+ if (explicitSkip && afterReroute) {
150
+ return result(
151
+ "BLOCK",
152
+ "required_step_skipped_after_reroute",
153
+ "Stop the invalid transition; reroute remains recorded; restore the missing step before retrying",
154
+ { protected_action: "transition:" + from + "->" + to }
155
+ );
156
+ }
157
+
158
+ if (explicitSkip && invalidatesDependents) {
159
+ return result(
160
+ "BLOCK",
161
+ "sequence_violation_invalidates_dependents",
162
+ "Stop the transition that would invalidate dependents; repair the sequence before retrying",
163
+ { protected_action: "transition:" + from + "->" + to }
164
+ );
165
+ }
166
+
167
+ if (!legalTargets.includes(to)) {
168
+ return result(
169
+ "REROUTE",
170
+ "illegal_transition:" + from + "->" + to,
171
+ "Return to a legal edge from " + from + "; do not invent a state jump"
172
+ );
173
+ }
174
+
175
+ if (!prerequisitesMet) {
176
+ const missing =
177
+ typeof input.missing_step === "string" && input.missing_step.trim()
178
+ ? input.missing_step.trim()
179
+ : "prerequisite";
180
+ return result(
181
+ "REROUTE",
182
+ "missing_prerequisite:" + missing,
183
+ "Perform missing step `" + missing + "` and record evidence before retrying " + from + "->" + to
184
+ );
185
+ }
186
+
187
+ // Completing while required assurance is still open.
188
+ if (to === "COMPLETE" && missingAssurance.length > 0) {
189
+ if (explicitSkip) {
190
+ return result(
191
+ "BLOCK",
192
+ "required_assurance_skipped:" + missingAssurance.join(","),
193
+ "Complete required assurance roles before protected completion",
194
+ { protected_action: "transition:ACCEPTANCE->COMPLETE" }
195
+ );
196
+ }
197
+ return result(
198
+ "REROUTE",
199
+ "required_assurance_pending:" + missingAssurance.join(","),
200
+ "Dispatch the next missing assurance role: " + missingAssurance[0]
201
+ );
202
+ }
203
+
204
+ // Assurance order: only the next missing role's state is allowed.
205
+ // E.g. Validator done, Park Ranger pending -> REVIEWING allowed, ACCEPTANCE reroutes.
206
+ if (
207
+ missingAssurance.length > 0 &&
208
+ (to === "VALIDATING" || to === "REVIEWING" || to === "ACCEPTANCE")
209
+ ) {
210
+ const nextRole = missingAssurance[0];
211
+ const expectedTo = assuranceTargetState(nextRole);
212
+ if (!expectedTo || to !== expectedTo) {
213
+ return result(
214
+ "REROUTE",
215
+ "assurance_order:" + nextRole,
216
+ "Run assurance in declared order; next required role is " + nextRole
217
+ );
218
+ }
219
+ }
220
+
221
+ if (explicitSkip) {
222
+ // First detection: reroute before hard block.
223
+ const step =
224
+ typeof input.missing_step === "string" && input.missing_step.trim()
225
+ ? input.missing_step.trim()
226
+ : missingAssurance[0] || "required_step";
227
+ return result(
228
+ "REROUTE",
229
+ "required_step_skipped:" + step,
230
+ "Reroute to missing step `" + step + "`; hard block only after a repeated skip"
231
+ );
232
+ }
233
+
234
+ return result("ADVISE", "transition_allowed:" + from + "->" + to, RECOVERY_ALLOW);
235
+ } catch {
236
+ return unavailable("adapter_exception");
237
+ }
238
+ }
239
+
240
+ function readStdinSync() {
241
+ try {
242
+ return require("node:fs").readFileSync(0, "utf8");
243
+ } catch (err) {
244
+ const code = err && err.code;
245
+ if (code === "EAGAIN" || code === "EOF") return "";
246
+ throw err;
247
+ }
248
+ }
249
+
250
+ function main() {
251
+ let raw = "";
252
+ try {
253
+ raw = readStdinSync();
254
+ } catch {
255
+ process.stdout.write(JSON.stringify(unavailable("stdin_read_failure")) + "\n");
256
+ process.exit(0);
257
+ return;
258
+ }
259
+
260
+ const trimmed = raw.trim();
261
+ if (!trimmed) {
262
+ process.stdout.write(JSON.stringify(unavailable("missing_input")) + "\n");
263
+ process.exit(0);
264
+ return;
265
+ }
266
+
267
+ let parsed;
268
+ try {
269
+ parsed = JSON.parse(trimmed);
270
+ } catch {
271
+ process.stdout.write(JSON.stringify(unavailable("malformed_input")) + "\n");
272
+ process.exit(0);
273
+ return;
274
+ }
275
+
276
+ process.stdout.write(JSON.stringify(evaluate(parsed)) + "\n");
277
+ process.exit(0);
278
+ }
279
+
280
+ module.exports = {
281
+ HOOK_CLASS,
282
+ OUTCOMES,
283
+ ENFORCEMENT,
284
+ LEGAL,
285
+ evaluate,
286
+ };
287
+
288
+ if (require.main === module) {
289
+ main();
290
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@alphazede/bearing-lite",
3
+ "version": "0.1.0",
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
+ "keywords": [
6
+ "agent-plugins",
7
+ "agent-skills",
8
+ "coding-agent",
9
+ "planning",
10
+ "workflow",
11
+ "developer-tools",
12
+ "skills-first",
13
+ "evidence",
14
+ "code-review"
15
+ ],
16
+ "author": "William Rumph / AlphaZede",
17
+ "license": "Apache-2.0",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/alphazede/bearing-lite.git"
21
+ },
22
+ "homepage": "https://github.com/alphazede/bearing-lite#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/alphazede/bearing-lite/issues"
25
+ },
26
+ "files": [
27
+ "plugin.json",
28
+ "skills/",
29
+ "hooks/",
30
+ "README.md",
31
+ "CODE_OF_CONDUCT.md",
32
+ "CONTRIBUTING.md",
33
+ "SECURITY.md",
34
+ "LICENSE-APACHE",
35
+ "guide/migration.md"
36
+ ]
37
+ }
package/plugin.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "bearing-lite",
4
+ "version": "0.1.0",
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
+ "author": {
7
+ "name": "William Rumph / AlphaZede",
8
+ "url": "https://github.com/alphazede"
9
+ },
10
+ "homepage": "https://github.com/alphazede/bearing-lite#readme",
11
+ "repository": "https://github.com/alphazede/bearing-lite",
12
+ "license": "Apache-2.0",
13
+ "keywords": [
14
+ "agent-plugins",
15
+ "agent-skills",
16
+ "planning",
17
+ "workflow",
18
+ "routing",
19
+ "developer-tools",
20
+ "skills-first"
21
+ ]
22
+ }
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: bearing-lite
3
+ description: >
4
+ Bearing Lite router for entry, resume, or unclear next action on repository
5
+ work. Locates the project plan, next ready task, dependency state, risk, and
6
+ smallest valid route among planning stages and roles. Activate for bearing
7
+ lite, start/resume bearing, route the next task, or choose the minimum role
8
+ path. Do not use for model or provider selection, package publication, deep
9
+ harness control-room work, or a single already-assigned role packet.
10
+ ---
11
+
12
+ # Bearing Lite Router
13
+
14
+ Plugin entry skill. Not a work role. Owner Authority remains human-only.
15
+
16
+ ## Inputs
17
+
18
+ Plan path, next ready task, dependencies, risk, and available native capabilities.
19
+
20
+ ## Select the smallest route
21
+
22
+ 1. Identify the project plan and next ready task. Missing plan fields stay `PROPOSED`.
23
+ 2. Invoke only missing planning stages: `repository-fit`, `set-bearings`, `gather-supplies`, `map-the-route`. Reuse valid evidence; do not replay completed stages.
24
+ 3. Choose the least costly role route that preserves dependencies and assurance:
25
+ - **Direct:** Crewmate → author self-check plus coordinator confirmation when `required_assurance` is `none`; otherwise the declared assurance roles. Add Park Ranger only when required or owner-selected. Validator and Park Ranger appear only when declared, owner-selected, or at a mandatory integrated phase gate.
26
+ - **Single wave:** Explorer → Crewmate packets with the same assurance rule → Surveyor when multi-packet or owner acceptance requires it. Never automatic Validator per packet.
27
+ - **Expedition:** Navigator → Trail Boss only for concurrent/conflicting waves → Explorer lanes → optional Sub-explorer → Crewmates, with assurance only where `required_assurance`, owner selection, or a mandatory phase gate requires it.
28
+ - **Long multi-phase:** Delegate Authority only when the owner explicitly delegates across sessions or Navigator replacement.
29
+ 4. Leave dormant roles unselected. No placeholder, receipt, or hidden state.
30
+ 5. Report honest hook coverage: full, partial, or skills-only procedural checks.
31
+
32
+ ## Never
33
+
34
+ - Select models, providers, credentials, launchers, or tool routes.
35
+ - Create, import, or interpret `.bearing` state, MCP, CLI, or a scheduler.
36
+ - Expand owner authority, publish, or self-certify as Validator, Park Ranger, or Surveyor.
37
+
38
+ ## State and task record
39
+
40
+ Task status lives only in the project plan. See `references/task-state.md` and `templates/task.md`. Orientation diagrams may load later under `references/` and `assets/`; text remains authoritative.
@@ -0,0 +1,34 @@
1
+ flowchart TD
2
+ O[Owner Authority] --> R[Bearing Lite Router]
3
+ R --> P{Next missing planning stage?}
4
+ P -->|Repository Fit| PF[Repository Fit]
5
+ P -->|Set Bearings| SB[Set Bearings]
6
+ P -->|Gather Supplies| GS[Gather Supplies]
7
+ P -->|Map the Route| MR[Map the Route]
8
+ PF --> R
9
+ SB --> R
10
+ GS --> R
11
+ MR --> R
12
+ P -->|none| Q{Smallest valid route}
13
+ Q -->|direct| C[Crewmate]
14
+ Q -->|wave| E[Explorer]
15
+ Q -->|expedition| N[Navigator]
16
+ Q -->|long multi-phase| D[Delegate Authority]
17
+ D --> N
18
+ N --> TB{Multiple active or conflicting waves?}
19
+ TB -->|yes| T[Trail Boss]
20
+ TB -->|no| E
21
+ T --> E
22
+ E --> X{Nested split needed?}
23
+ X -->|yes| SE[Sub-explorer]
24
+ SE --> C
25
+ X -->|no| C
26
+ C --> A{required_assurance / next missing?}
27
+ A -->|none: coordinator confirms| Q
28
+ A -->|Validator| V[Validator]
29
+ A -->|Park Ranger| PK[Park Ranger]
30
+ A -->|Surveyor| S[Surveyor]
31
+ A -->|owner acceptance| O
32
+ V --> A
33
+ PK --> A
34
+ S --> O