@alphazede/bearing-lite 0.1.11 → 0.2.1

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 (50) hide show
  1. package/CONTRIBUTING.md +25 -0
  2. package/README.md +46 -14
  3. package/hooks/assurance-budget.cjs +149 -0
  4. package/hooks/closeout.cjs +16 -0
  5. package/hooks/com.anthropic.claude-code/mapping.md +72 -0
  6. package/hooks/com.cursor/hooks.json +11 -1
  7. package/hooks/hooks.json +45 -1
  8. package/hooks/plan-package.cjs +177 -0
  9. package/hooks/planning-review.cjs +9 -14
  10. package/hooks/policy.cjs +45 -0
  11. package/hooks/reconcile.cjs +145 -0
  12. package/hooks/te-capability.cjs +137 -0
  13. package/hooks/te-host.cjs +644 -0
  14. package/hooks/transition-order.cjs +32 -2
  15. package/lineups.json +1 -0
  16. package/package.json +4 -2
  17. package/plugin.json +1 -1
  18. package/schemas/authority.schema.json +75 -0
  19. package/schemas/event.schema.json +59 -0
  20. package/schemas/implementation.schema.json +150 -0
  21. package/schemas/journey.schema.json +101 -0
  22. package/schemas/lineups.schema.json +145 -0
  23. package/schemas/seit.schema.json +108 -0
  24. package/skills/bearing-lite/SKILL.md +20 -21
  25. package/skills/bearing-lite/references/assurance-policy.md +66 -0
  26. package/skills/bearing-lite/references/lineups.md +107 -0
  27. package/skills/bearing-lite/references/peer-synthesis.md +7 -3
  28. package/skills/bearing-lite/references/role-routing.mmd +2 -2
  29. package/skills/bearing-lite/references/task-state.md +4 -4
  30. package/skills/bearing-lite/references/task-state.mmd +1 -1
  31. package/skills/bearing-lite/templates/task.md +20 -11
  32. package/skills/crewmate/SKILL.md +3 -0
  33. package/skills/explorer/SKILL.md +7 -6
  34. package/skills/gather-supplies/SKILL.md +7 -1
  35. package/skills/integration-engineer/SKILL.md +41 -0
  36. package/skills/light-implementer/SKILL.md +53 -0
  37. package/skills/map-the-route/SKILL.md +9 -7
  38. package/skills/map-the-route/references/artifact-grammar.md +67 -32
  39. package/skills/park-ranger/SKILL.md +14 -9
  40. package/skills/plan-integrator/SKILL.md +46 -0
  41. package/skills/requirements-engineer/SKILL.md +57 -0
  42. package/skills/scribe/SKILL.md +34 -0
  43. package/skills/set-bearings/SKILL.md +16 -7
  44. package/skills/set-bearings/templates/workspace.md +28 -0
  45. package/skills/surveyor/SKILL.md +15 -9
  46. package/skills/systems-modeler/SKILL.md +35 -0
  47. package/skills/test-engineer/SKILL.md +48 -0
  48. package/skills/validator/SKILL.md +20 -27
  49. package/skills/validator/references/grading-rubric.md +5 -4
  50. package/skills/bearing-lite/templates/default-role-lineup.md +0 -26
package/CONTRIBUTING.md CHANGED
@@ -54,8 +54,33 @@ checkout):
54
54
  node --test test/diagram-sync.test.mjs
55
55
  node --test test/public-boundary.test.mjs
56
56
  node --test test/skills-conformance.test.mjs
57
+ python3 test/schema-validation.py
57
58
  ```
58
59
 
60
+ Schema validation requires Python 3.12. `python3 test/schema-validation.py` is
61
+ the required developer and CI gate (`CMD-LITE-SCHEMA-VALIDATE`). It installs the
62
+ pinned hashes from `test/schema-validator-requirements.txt` into an isolated
63
+ temporary directory with `pip install --isolated --require-hashes --no-deps` and
64
+ runs Draft 2020-12 validation with no remote `$ref` retrieval. Do not install
65
+ those packages globally, with `--user`, or as production dependencies of this
66
+ package.
67
+
68
+ CI on `ubuntu-24.04` with Python 3.12 x64 performs the same isolated
69
+ `--require-hashes --no-deps` install into a job-local target directory, then
70
+ runs `python3 test/schema-validation.py`. To reuse a local isolated tree instead
71
+ of a temporary install:
72
+
73
+ ```sh
74
+ python3.12 -m venv .venv-schema-validator
75
+ .venv-schema-validator/bin/python -m pip install \
76
+ --isolated --require-hashes --no-deps \
77
+ -r test/schema-validator-requirements.txt
78
+ BEARING_LITE_SCHEMA_ISOLATED_ROOT=.venv-schema-validator/lib/python3.12/site-packages \
79
+ python3 test/schema-validation.py
80
+ ```
81
+
82
+ Keep that virtualenv uncommitted.
83
+
59
84
  Keep skills lean. Prefer references and assets only when progressive disclosure
60
85
  needs them. Do not add dependencies or change the lockfile without explicit
61
86
  maintainer approval. Do not add `mcp.json`, postinstall scripts, or global hook
package/README.md CHANGED
@@ -30,9 +30,12 @@ Then give your agent a real task:
30
30
  > responses. Require one independent review at the end.
31
31
 
32
32
  Bearing Lite fills missing planning stages, then Map the Route creates the
33
- complete five-artifact package with proposed route, lineup, role states,
34
- reasoning, and at-end cadence. One integrated owner review approves or changes
35
- that package before bounded sessions dispatch with visible Markdown state.
33
+ complete five-artifact package (`<journey-topic>-technical-plan.md`,
34
+ `design.md`, `seit.json`, `implementation.json`, and two-state `review.html`)
35
+ with proposed route, lineup, role states, reasoning, and at-end cadence. One
36
+ integrated owner review approves or changes that package before bounded
37
+ sessions dispatch, with visible Markdown task state over those JSON
38
+ authorities.
36
39
 
37
40
  If Bearing Lite helps keep a long agent task scoped and reviewable,
38
41
  [star the repository](https://github.com/alphazede/bearing-lite). It helps other
@@ -97,16 +100,25 @@ register hooks. That path remains first-class. See
97
100
  1. **Fill only missing planning stages:** Repository Fit → Set Bearings → Gather
98
101
  Supplies.
99
102
  2. **Invoke Map the Route** after material intent is settled. It creates
100
- specification, design, SEIT, implementation, and `review.html` together.
103
+ technical-plan, `design.md`, `seit.json`, `implementation.json`, and
104
+ two-state `review.html` together.
101
105
  3. **Review once:** approve or change the proposed route, user-owned
102
106
  primary/fallback lineup, role states, reasoning, at-end cadence, and plan.
103
107
  4. **Dispatch bounded sessions** with compact receipts. Crewmate and Explorer may
104
108
  continue in-wave; assurance always starts fresh at the end.
105
- 5. **Record state visibly** in human-readable Markdown artifacts only.
109
+ 5. **Record state visibly.** The project's human-readable Markdown plan is the
110
+ only task-state record: task blocks, task states, and their transitions. The
111
+ Journey, execution, V&V, and authority records are machine-readable JSON
112
+ validated against `schemas/`: `journey.json` (Journey identity, checkout
113
+ lease, decisions, planning receipts, lineup selection), `seit.json` (V&V
114
+ proof rows), `implementation.json` (nested execution authority, waves,
115
+ slices, dispatch), and `authority.json` (authority envelope). `review.html`
116
+ and diagrams present state; like diagrams, they never authorize a
117
+ transition, create state, or grant authority.
106
118
 
107
119
  Bearing Lite never selects models, providers, credentials, or launchers. The
108
120
  owner provides each role's primary/fallback agent or harness, model, and
109
- reasoning level in `~/.agents/bearing-lite/default-role-lineup.md`, then confirms
121
+ reasoning level in `~/.agents/bearing-lite/lineups.json`, then confirms
110
122
  the applicable Journey snapshot before implementation.
111
123
 
112
124
  ## Routes and scaling
@@ -133,8 +145,8 @@ invokes only missing planning stages, has Map the Route generate all five
133
145
  artifacts with proposed lineup and `review_cadence: at-end`, then presents one
134
146
  integrated owner review before dispatching an Explorer Journey or Expedition. Explorer
135
147
  coordinates proven-independent in-wave lanes without a nested coordinator.
136
- Validator, Park Ranger, and Surveyor appear only when declared and only at the
137
- end on the final integrated candidate. Diagrams
148
+ Assurance Test Engineer, Park Ranger, and Surveyor appear only when declared
149
+ and only at the end on the final integrated candidate. Diagrams
138
150
  explain orientation; they never authorize a transition.
139
151
 
140
152
  ## Roles and authority
@@ -144,14 +156,27 @@ explain orientation; they never authorize a transition.
144
156
  | **Router** | Stateful planning controller | no | User-facing; planning-state writer; Expedition sequencing |
145
157
  | **Navigator** | Compatibility diagnostic | no | Not a normal role; existing plans reroute to Router |
146
158
  | **Explorer** | One-wave controller | no | Dispatches Crewmates; owns proven-independent lanes |
147
- | **Crewmate** | Bounded implementer | yes | Most hands-on work; exact write set |
148
- | **Validator** | Evidence sufficiency | no | Independent of the author |
149
- | **Park Ranger** | Defect review | no | Independent of the author |
150
- | **Surveyor** | User-facing acceptance | no | Read-only outcome judgment |
159
+ | **Crewmate** | Bounded implementer | yes | Split test-writing versus product; neither self-certifies |
160
+ | **Light Implementer** | Mechanical implementer | yes | `work_class: light` slices only; verified by the packet's command; no repair loop |
161
+ | **Scribe** | Event side lane | no | Transcribes; cannot activate authority |
162
+ | **Plan Integrator** | Artifact reconciliation | no | Generates `implementation.json` and `review.html` |
163
+ | **Systems Modeler** | Engineering views | no | After requirements; before design finalization |
164
+ | **Integration Engineer** | Progressive assembly | no | Dual planning and execution sessions |
165
+ | **Test Engineer** | Planning and Assurance V&V | no | Retires Validator; Validator is not an active role |
166
+ | **Park Ranger** | Defect review | no | Consumes Assurance Test Engineer; no routine TE |
167
+ | **Surveyor** | User-facing acceptance | no | Read-only RE, SysML Modeling, and TE |
151
168
  | **Owner Authority** | Human decision | n/a | Never an agent role |
152
169
 
153
- **Independent review:** a candidate author never provides their own Validator,
154
- Park Ranger, or Surveyor verdict.
170
+ **Independent review:** a candidate author never provides their own Assurance
171
+ Test Engineer, Park Ranger, or Surveyor verdict.
172
+
173
+ Public Bearing Lite remains usable without AlphaZede-specific skills
174
+ (`requirements-engineering`, `sysml-modeling`, `test-engineering`,
175
+ `integration-engineering`). Specialized capabilities activate when selected
176
+ or required. Unavailability of a selected-or-required capability is a typed capability gap,
177
+ not success and not invented behavior. Selected-only missing and required-only
178
+ missing are each typed gaps. Only unselected and unrequired absence remains
179
+ inactive / not a global failure.
155
180
 
156
181
  Failure escalates to the nearest role whose scope can see it:
157
182
 
@@ -177,6 +202,11 @@ Ordinary execution corrections remain bounded. The assurance gate allows one
177
202
  review-directed repair, followed by deterministic coordinator verification and
178
203
  no second review. Diagrams never create state or authorize transitions.
179
204
 
205
+ `hooks/reconcile.cjs` deterministically applies evidence events to Journey state.
206
+ It is a short-lived Router-run invocation, not a daemon or host event adapter;
207
+ hosts emit no events today, so invocation remains a procedural limitation.
208
+ It observes merge and issue closure but never grants acceptance, merges, or closes issues.
209
+
180
210
  ## Implementation process (explanatory)
181
211
 
182
212
  Default packet completion is author self-check plus coordinator confirmation.
@@ -204,6 +234,8 @@ flowchart LR
204
234
  | `plugin.json` | Agent Plugins v1.0.0 manifest |
205
235
  | `skills/` | Router, planning stages, and role skills |
206
236
  | `hooks/` | Four portable class adapters plus the verified Claude Code / Codex mapping |
237
+ | `lineups.json` | Empty shipped catalog; no packaged providers, models, or defaults |
238
+ | `schemas/` | JSON Schema for `seit.json`, `implementation.json`, `authority.json`, and `journey.json` |
207
239
  | `README.md` and governance docs | Public product and conduct surfaces |
208
240
 
209
241
  There is no `mcp.json`, `bin` entrypoint, postinstall, or runtime dependency on
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Per-declared-phase-or-wave assurance budget (ROUTER-EMV-CADENCE-IMPLEMENTATION-001).
5
+ * Exact runtime mirror of `skills/bearing-lite/references/assurance-policy.md`.
6
+ * A pure evaluator behind the existing `transition` class, like planning-review.cjs:
7
+ * it declares no HOOK_CLASS and registers no host event.
8
+ */
9
+
10
+ const fs = require("node:fs");
11
+
12
+ const { ASSURANCE_BUDGET_POLICY: POLICY } = require("./policy.cjs");
13
+
14
+ /** The sentinel unit when the frozen declaration names neither waves nor phases. */
15
+ const DIRECT = "direct";
16
+ const PENDING_RECEIPTS = new Set(["WAITING_ON", "UNAVAILABLE"]);
17
+
18
+ const isCount = (value) => Number.isInteger(value) && value >= 0;
19
+
20
+ const isPlainObject = (value) =>
21
+ value !== null && typeof value === "object" && !Array.isArray(value);
22
+
23
+ function readJson(file) {
24
+ if (typeof file !== "string" || !file) return null;
25
+ try {
26
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
27
+ return isPlainObject(parsed) ? parsed : null;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ /** Declared unit ids from the frozen declaration, in declaration order. */
34
+ function declaredUnits(declaration) {
35
+ const waves = Array.isArray(declaration.waves) ? declaration.waves : [];
36
+ const waveIds = waves.map((wave) => wave && wave.id).filter((id) => typeof id === "string" && id);
37
+ if (waveIds.length) return waveIds;
38
+ const phases = Array.isArray(declaration.phases) ? declaration.phases : [];
39
+ const phaseIds = phases
40
+ .map((phase) => phase && phase.phaseId)
41
+ .filter((id) => typeof id === "string" && id);
42
+ if (phaseIds.length) return phaseIds;
43
+ return [DIRECT];
44
+ }
45
+
46
+ const verdict = (outcome, reason) => ({ outcome, reason });
47
+
48
+ /**
49
+ * @param {unknown} input assurance request
50
+ * @returns {{ outcome: string, reason: string }}
51
+ */
52
+ function evaluateAssuranceBudget(input) {
53
+ try {
54
+ if (!isPlainObject(input)) {
55
+ return verdict("NEEDS_MORE_EVIDENCE", "assurance_request_missing");
56
+ }
57
+ if (
58
+ POLICY.automatic_per_slice_review === "prohibited" &&
59
+ (input.request_scope === "slice" || input.unit_kind === "slice")
60
+ ) {
61
+ return verdict("OWNER_AMENDMENT_REQUIRED", "automatic_per_slice_review_prohibited");
62
+ }
63
+ if (
64
+ POLICY.automatic_rereview_of_same_unit === "prohibited" &&
65
+ (input.automatic_rereview_requested === true || input.review_after_repair === true)
66
+ ) {
67
+ return verdict("OWNER_AMENDMENT_REQUIRED", "automatic_rereview_prohibited");
68
+ }
69
+
70
+ const declaration = readJson(input.declaration_path);
71
+ if (!declaration) {
72
+ return verdict("NEEDS_MORE_EVIDENCE", "frozen_declaration_unreadable");
73
+ }
74
+ const record = readJson(input.task_record_path);
75
+ if (!record) {
76
+ return verdict("NEEDS_MORE_EVIDENCE", "task_record_unreadable");
77
+ }
78
+ // The budget key is `journey + unit_kind + unit_id`; a record from another
79
+ // Journey never answers for this one.
80
+ if (
81
+ typeof record.journey === "string" &&
82
+ typeof input.journey === "string" &&
83
+ record.journey !== input.journey
84
+ ) {
85
+ return verdict("NEEDS_MORE_EVIDENCE", "task_record_journey_mismatch");
86
+ }
87
+
88
+ const declared = declaredUnits(declaration);
89
+ const requested =
90
+ typeof input.assurance_unit === "string" && input.assurance_unit
91
+ ? input.assurance_unit
92
+ : declared.length === 1 && declared[0] === DIRECT
93
+ ? DIRECT
94
+ : "";
95
+ if (!requested || !declared.includes(requested)) {
96
+ return verdict("OWNER_AMENDMENT_REQUIRED", "undeclared_review_unit");
97
+ }
98
+
99
+ const receipts = Array.isArray(input.receipts) ? input.receipts : [];
100
+ if (receipts.some((receipt) => PENDING_RECEIPTS.has(receipt && receipt.verdict))) {
101
+ return verdict("NEEDS_MORE_EVIDENCE", "assurance_transport_pending");
102
+ }
103
+
104
+ const units = Array.isArray(record.units) ? record.units : [];
105
+ const spent = units.find((unit) => {
106
+ if (!isPlainObject(unit)) return false;
107
+ const unitId = unit.unit_id || unit.assurance_unit;
108
+ const id = typeof unitId === "string" && unitId ? unitId : DIRECT;
109
+ if (id !== requested) return false;
110
+ if (typeof unit.journey === "string" && unit.journey !== input.journey) return false;
111
+ if (
112
+ typeof unit.unit_kind === "string" &&
113
+ typeof input.unit_kind === "string" &&
114
+ unit.unit_kind !== input.unit_kind
115
+ ) {
116
+ return false;
117
+ }
118
+ return true;
119
+ });
120
+ const counter = (field) => {
121
+ const value = spent && spent[field];
122
+ return value === undefined || value === null ? 0 : value;
123
+ };
124
+ const rounds = counter("assurance_rounds");
125
+ const repairs = counter("assurance_repairs");
126
+ // A corrupt counter is not a spent-zero budget.
127
+ if (!isCount(rounds) || !isCount(repairs)) {
128
+ return verdict("NEEDS_MORE_EVIDENCE", "invalid_assurance_counter");
129
+ }
130
+
131
+ if (repairs > POLICY.aggregated_repairs_max) {
132
+ return verdict("HALT", "assurance_repair_limit");
133
+ }
134
+ if (
135
+ repairs === POLICY.aggregated_repairs_max &&
136
+ (spent && spent.deterministic_gate) !== POLICY.post_repair_gate.split("_")[1]
137
+ ) {
138
+ return verdict("HALT", "deterministic_post_repair_gate_required");
139
+ }
140
+ if (rounds >= POLICY.review_rounds) {
141
+ return verdict("HALT", "assurance_round_limit");
142
+ }
143
+ return verdict("PASS", "assurance_round_available");
144
+ } catch {
145
+ return verdict("NEEDS_MORE_EVIDENCE", "assurance_budget_unavailable");
146
+ }
147
+ }
148
+
149
+ module.exports = { POLICY, evaluateAssuranceBudget };
@@ -39,6 +39,19 @@ const VERDICT_VALUES = new Set([
39
39
  "WAITING_ON",
40
40
  ]);
41
41
 
42
+ /** Closed verdicts that refuse protected completion (#55). */
43
+ const NEGATIVE_VERDICTS = new Set([
44
+ "BLOCK",
45
+ "FAIL",
46
+ "GAPS",
47
+ "NEEDS_MORE_EVIDENCE",
48
+ "OWNER_DECISION_REQUIRED",
49
+ "PARTIAL",
50
+ "REPAIR_REQUIRED",
51
+ "REROUTED",
52
+ "WAITING_ON",
53
+ ]);
54
+
42
55
  const RECOVERY_UNAVAILABLE =
43
56
  "Report UNAVAILABLE, complete the handoff checklist manually, and do not request protected completion until required fields and assurance are present";
44
57
  const RECOVERY_HANDOFF =
@@ -169,6 +182,8 @@ function evaluate(input) {
169
182
  const blockers = [];
170
183
  const handoffProblems = [...missing, ...invalid];
171
184
  if (handoffProblems.length > 0) blockers.push("handoff:" + handoffProblems.join(","));
185
+ const verdict = String((isPlainObject(input.handoff) ? input.handoff : input).verdict ?? "").trim();
186
+ if (NEGATIVE_VERDICTS.has(verdict)) blockers.push("verdict:" + verdict);
172
187
  if (missingAssurance.length > 0) {
173
188
  blockers.push("assurance:" + missingAssurance.join(","));
174
189
  }
@@ -283,6 +298,7 @@ module.exports = {
283
298
  ENFORCEMENT,
284
299
  HANDOFF_FIELDS,
285
300
  VERDICT_VALUES,
301
+ NEGATIVE_VERDICTS,
286
302
  evaluate,
287
303
  };
288
304
 
@@ -31,8 +31,15 @@ adapters into global hook configuration.
31
31
  |---|---|---|
32
32
  | `SessionStart`, `sessionStart`, `session_start` | activation | yes, advisory |
33
33
  | `Stop`, `stop` | closeout | yes, advisory only |
34
+ | `PreToolUse`, `preToolUse`, `beforeShellExecution`, `apply_patch` | te_test_write | only where the host has a native write-time deny |
35
+ | `Stop`, `stop`, `SubagentStop`, `subagentStop` | te_completion | only where the host has a native completion or child-stop deny |
34
36
  | any other host event | none | unmapped; fail open as `UNAVAILABLE` |
35
37
 
38
+ `hooks/com.anthropic.claude-code/host.cjs` owns activation and closeout and
39
+ maps no Test Engineering class. `hooks/te-host.cjs` owns te_test_write and
40
+ te_completion and maps neither of the original four. The two adapters are
41
+ registered separately, and a `Stop` event reaches both.
42
+
36
43
  The shared planning-review evaluator is used by transition and closeout when a
37
44
  client supplies a structured `planning_review` record. Current session-start /
38
45
  stop mappings cannot derive that nested record safely, so Claude Code, Codex,
@@ -41,9 +48,74 @@ AGY, Pi, and DeepCode remain skills-only. Do not claim full planning-review
41
48
  enforcement for any of these hosts until a native event supplies the complete
42
49
  record.
43
50
 
51
+ The per-declared-phase-or-wave assurance record
52
+ (`skills/bearing-lite/references/assurance-policy.md`) is evaluated by the same
53
+ `transition` class through the `assurance_transition` action_kind. No mapped
54
+ host exposes a hook event that carries the frozen declaration and the visible
55
+ task record, so the assurance budget is procedural on every mapped host:
56
+ Claude Code, Codex, Grok Build, Cursor, and Kimi Code stay partial, and AGY,
57
+ Pi, and DeepCode stay skills-only. No host may advertise an enforcement of the
58
+ assurance budget it cannot perform.
59
+
44
60
  The adapter accepts snake_case and camelCase (`hook_event_name` /
45
61
  `hookEventName`, `cwd` / `workspaceRoot`).
46
62
 
63
+ ## Test Engineering classes
64
+
65
+ `te_test_write` and `te_completion` are **additional** classes, shipped by
66
+ `hooks/te-capability.cjs` and `hooks/te-host.cjs`. They do not rename or
67
+ replace activation, closeout, transition-order, or protected-action, and they
68
+ do not reuse those classes' outcome tokens. Their verdicts are `ALLOW`,
69
+ `DENY_ROUTE_TO_TE`, `DENY_RECEIPT_REQUIRED`, and `UNAVAILABLE`.
70
+
71
+ Bearing Lite ships no Test Engineering evaluator and no Test Engineering
72
+ method content. The adapter resolves the `test-engineering` capability, loads
73
+ the evaluator at `skills/test-engineering/hooks/te-evaluator.cjs` when that
74
+ capability is present in the workspace, builds the request from the real Git
75
+ checkout and the coordinator-authored plan, and returns the evaluator verdict
76
+ unchanged.
77
+
78
+ | Capability state | Class result |
79
+ |---|---|
80
+ | neither selected nor required | `UNAVAILABLE`; the class fails open and this is not a failure |
81
+ | selected or required, evaluator absent | `UNAVAILABLE` with `code: typed_capability_gap`; a typed gap, never silent success |
82
+ | selected or required, evaluator loaded | the evaluator verdict, verbatim |
83
+
84
+ Trusted assignment comes from the visible coordinator-authored plan, never
85
+ from the tool payload or the transcript. Candidate state comes from the
86
+ installed Git executable: the committed `diff_base..HEAD` change plus the
87
+ working tree, scoped to the trusted write set. An empty trusted scope covers
88
+ nothing, so unrelated shared dirt never enters the candidate. Derived runtime
89
+ state under the Bearing runtime directory is never a candidate change.
90
+
91
+ An artifact on disk is never a local pass token; only the evaluator reads it.
92
+ An `ALLOW` completion verdict is not user acceptance and grants no planning or
93
+ publication authority.
94
+
95
+ ### Host honesty
96
+
97
+ `native` means the host exposes a verified hard deny for that channel.
98
+ Everything else is `UNAVAILABLE`: the class is procedural on that host and
99
+ this adapter must not advertise enforcement it cannot perform.
100
+
101
+ | Host | te_test_write channel | te_completion at parent stop | te_completion at child stop |
102
+ |---|---|---|---|
103
+ | Claude Code | native `PreToolUse` deny | native `Stop` deny | native `SubagentStop` deny |
104
+ | Codex | native `PreToolUse` deny | native `Stop` deny | native `SubagentStop` deny |
105
+ | Grok Build | native `PreToolUse` deny | native `Stop` deny | native `SubagentStop` deny |
106
+ | Cursor | native `preToolUse` and `beforeShellExecution` deny | te_completion UNAVAILABLE | child te_completion UNAVAILABLE |
107
+ | Kimi Code | te_test_write UNAVAILABLE | te_completion UNAVAILABLE | child te_completion UNAVAILABLE |
108
+ | Pi | te_test_write UNAVAILABLE | te_completion UNAVAILABLE | child te_completion UNAVAILABLE |
109
+ | AGY | te_test_write UNAVAILABLE | te_completion UNAVAILABLE | child te_completion UNAVAILABLE |
110
+ | DeepCode | te_test_write UNAVAILABLE | te_completion UNAVAILABLE | child te_completion UNAVAILABLE |
111
+
112
+ Cursor's `stop` guidance message is advisory text, not a hard completion
113
+ block, and Cursor exposes no distinct hard child-stop deny; neither is
114
+ registered as one. Kimi Code, Pi, AGY, and DeepCode carry no verified native
115
+ Test Engineering blocking at all, so `hooks/te-host.cjs` returns `UNAVAILABLE`
116
+ for every class on those hosts rather than claiming an enforcement it cannot
117
+ deliver.
118
+
47
119
  ## Derived fields
48
120
 
49
121
  The host event supplies session metadata. The adapter reads visible Markdown
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Bearing Lite Cursor mapping. Partial coverage: sessionStart activation, stop closeout. Transition-order and protected-action remain procedural.",
2
+ "description": "Bearing Lite Cursor mapping. Partial coverage: sessionStart activation, stop closeout, and write-time te_test_write on preToolUse and beforeShellExecution. Cursor exposes no hard completion or child-stop deny, so te_completion stays procedural. Transition-order and protected-action remain procedural.",
3
3
  "hooks": {
4
4
  "sessionStart": [
5
5
  {
@@ -10,6 +10,16 @@
10
10
  {
11
11
  "command": "node ./hooks/com.anthropic.claude-code/host.cjs"
12
12
  }
13
+ ],
14
+ "preToolUse": [
15
+ {
16
+ "command": "node ./hooks/te-host.cjs --host=cursor"
17
+ }
18
+ ],
19
+ "beforeShellExecution": [
20
+ {
21
+ "command": "node ./hooks/te-host.cjs --host=cursor"
22
+ }
13
23
  ]
14
24
  }
15
25
  }
package/hooks/hooks.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Bearing Lite verified Claude Code and Codex mapping. Partial coverage: activation on SessionStart, closeout on Stop. Transition-order and protected-action remain procedural.",
2
+ "description": "Bearing Lite verified Claude Code and Codex mapping. Partial coverage: activation on SessionStart, closeout on Stop, and the Test Engineering classes te_test_write on PreToolUse and te_completion on Stop and SubagentStop. Transition-order and protected-action remain procedural.",
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
@@ -16,6 +16,22 @@
16
16
  ]
17
17
  }
18
18
  ],
19
+ "PreToolUse": [
20
+ {
21
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|apply_patch|Bash",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "node",
26
+ "args": [
27
+ "${CLAUDE_PLUGIN_ROOT}/hooks/te-host.cjs"
28
+ ],
29
+ "timeout": 15,
30
+ "statusMessage": "Bearing Lite test-engineering write check"
31
+ }
32
+ ]
33
+ }
34
+ ],
19
35
  "Stop": [
20
36
  {
21
37
  "hooks": [
@@ -29,6 +45,34 @@
29
45
  "statusMessage": "Bearing Lite closeout"
30
46
  }
31
47
  ]
48
+ },
49
+ {
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "node",
54
+ "args": [
55
+ "${CLAUDE_PLUGIN_ROOT}/hooks/te-host.cjs"
56
+ ],
57
+ "timeout": 15,
58
+ "statusMessage": "Bearing Lite test-engineering completion check"
59
+ }
60
+ ]
61
+ }
62
+ ],
63
+ "SubagentStop": [
64
+ {
65
+ "hooks": [
66
+ {
67
+ "type": "command",
68
+ "command": "node",
69
+ "args": [
70
+ "${CLAUDE_PLUGIN_ROOT}/hooks/te-host.cjs"
71
+ ],
72
+ "timeout": 15,
73
+ "statusMessage": "Bearing Lite test-engineering child completion check"
74
+ }
75
+ ]
32
76
  }
33
77
  ]
34
78
  }