@shardworks/astrolabe-apparatus 0.1.212 → 0.1.214

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 CHANGED
@@ -159,7 +159,7 @@ The Astrolabe declares one book in Stacks:
159
159
  | `astrolabe.plan-init` | Creates a PlanDoc from the brief writ; validates codex presence |
160
160
  | `astrolabe.inventory-check` | Validates that the reader produced a non-empty inventory |
161
161
  | `astrolabe.decision-review` | Two-pass engine: blocks for patron review, then reconciles answers. Decisions with `selected` already pre-set by the analyst are auto-accepted — they are excluded from the InputRequestDoc, and if nothing remains reviewable the engine fast-paths to `writing` without opening the gate. |
162
- | `astrolabe.spec-publish` | Publishes the generated specification as a new writ |
162
+ | `astrolabe.spec-publish` | Publishes the generated specification as a new writ. Posts the spec body verbatim — any `<task-manifest>` block is preserved and is not fanned out into child `piece` writs. |
163
163
 
164
164
  ### Rig Templates (contributed to Spider)
165
165
 
@@ -1,5 +1,5 @@
1
1
  export { createPlanInitEngine } from './plan-init.ts';
2
2
  export { createInventoryCheckEngine } from './inventory-check.ts';
3
3
  export { createDecisionReviewEngine } from './decision-review.ts';
4
- export { createSpecPublishEngine, parseTaskManifest } from './spec-publish.ts';
4
+ export { createSpecPublishEngine } from './spec-publish.ts';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { createPlanInitEngine } from "./plan-init.js";
2
2
  export { createInventoryCheckEngine } from "./inventory-check.js";
3
3
  export { createDecisionReviewEngine } from "./decision-review.js";
4
- export { createSpecPublishEngine, parseTaskManifest } from "./spec-publish.js";
4
+ export { createSpecPublishEngine } from "./spec-publish.js";
5
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/engines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/engines/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -5,11 +5,12 @@
5
5
  * the originating brief via a 'refines' link, records generatedWritId on
6
6
  * the PlanDoc, and transitions the plan to 'completed'.
7
7
  *
8
- * When the spec contains a `<task-manifest>`, the engine:
9
- * 1. Strips the manifest block from the spec (mandate body).
10
- * 2. Posts the mandate writ in draft state.
11
- * 3. Creates one child piece writ per `<task>` element.
12
- * 4. Transitions the mandate from draft to open (ready for dispatch).
8
+ * The engine always posts the full spec verbatim including any
9
+ * `<task-manifest>` block the spec-writer produced. The planning pipeline
10
+ * does not fan the manifest out into child `piece` writs; the implementing
11
+ * artificer (or a downstream tool) is responsible for acting on the
12
+ * manifest if it chooses to. This keeps spec-publish a pure "publish what
13
+ * was written" step with no hidden surgery on the mandate body.
13
14
  *
14
15
  * Preconditions:
15
16
  * - plan.status must be 'writing'
@@ -18,16 +19,5 @@
18
19
  import type { EngineDesign } from '@shardworks/fabricator-apparatus';
19
20
  import type { Book } from '@shardworks/stacks-apparatus';
20
21
  import type { PlanDoc } from '../types.ts';
21
- /**
22
- * Parse the `<task-manifest>` block from the spec string.
23
- * Returns the individual `<task ...>...</task>` fragments and the spec
24
- * with the manifest block removed.
25
- *
26
- * If no manifest is found, returns null.
27
- */
28
- export declare function parseTaskManifest(spec: string): {
29
- tasks: string[];
30
- strippedSpec: string;
31
- } | null;
32
22
  export declare function createSpecPublishEngine(getPlansBook: () => Book<PlanDoc>): EngineDesign;
33
23
  //# sourceMappingURL=spec-publish.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spec-publish.d.ts","sourceRoot":"","sources":["../../src/engines/spec-publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAqC,MAAM,kCAAkC,CAAC;AACxG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC/C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAsBP;AAED,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,YAAY,CAmHvF"}
1
+ {"version":3,"file":"spec-publish.d.ts","sourceRoot":"","sources":["../../src/engines/spec-publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAqC,MAAM,kCAAkC,CAAC;AACxG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,YAAY,CA+DvF"}
@@ -5,44 +5,18 @@
5
5
  * the originating brief via a 'refines' link, records generatedWritId on
6
6
  * the PlanDoc, and transitions the plan to 'completed'.
7
7
  *
8
- * When the spec contains a `<task-manifest>`, the engine:
9
- * 1. Strips the manifest block from the spec (mandate body).
10
- * 2. Posts the mandate writ in draft state.
11
- * 3. Creates one child piece writ per `<task>` element.
12
- * 4. Transitions the mandate from draft to open (ready for dispatch).
8
+ * The engine always posts the full spec verbatim including any
9
+ * `<task-manifest>` block the spec-writer produced. The planning pipeline
10
+ * does not fan the manifest out into child `piece` writs; the implementing
11
+ * artificer (or a downstream tool) is responsible for acting on the
12
+ * manifest if it chooses to. This keeps spec-publish a pure "publish what
13
+ * was written" step with no hidden surgery on the mandate body.
13
14
  *
14
15
  * Preconditions:
15
16
  * - plan.status must be 'writing'
16
17
  * - plan.spec must be a non-empty string
17
18
  */
18
19
  import { guild } from '@shardworks/nexus-core';
19
- /**
20
- * Parse the `<task-manifest>` block from the spec string.
21
- * Returns the individual `<task ...>...</task>` fragments and the spec
22
- * with the manifest block removed.
23
- *
24
- * If no manifest is found, returns null.
25
- */
26
- export function parseTaskManifest(spec) {
27
- // Match the full <task-manifest>...</task-manifest> block (greedy, single match)
28
- const manifestMatch = spec.match(/<task-manifest>([\s\S]*?)<\/task-manifest>/);
29
- if (!manifestMatch)
30
- return null;
31
- const manifestBlock = manifestMatch[0];
32
- const manifestInner = manifestMatch[1];
33
- // Extract individual <task ...>...</task> elements
34
- const taskRegex = /<task\b[^>]*>[\s\S]*?<\/task>/g;
35
- const tasks = [];
36
- let match;
37
- while ((match = taskRegex.exec(manifestInner)) !== null) {
38
- tasks.push(match[0].trim());
39
- }
40
- if (tasks.length === 0)
41
- return null;
42
- // Strip the manifest block from the spec
43
- const strippedSpec = spec.replace(manifestBlock, '').trim();
44
- return { tasks, strippedSpec };
45
- }
46
20
  export function createSpecPublishEngine(getPlansBook) {
47
21
  return {
48
22
  id: 'astrolabe.spec-publish',
@@ -66,50 +40,7 @@ export function createSpecPublishEngine(getPlansBook) {
66
40
  const briefWrit = await clerk.show(planId);
67
41
  // Resolve generated writ type from config
68
42
  const generatedWritType = guild().guildConfig().astrolabe?.generatedWritType ?? 'mandate';
69
- // Check for task manifest
70
- const manifestResult = parseTaskManifest(plan.spec);
71
- if (manifestResult) {
72
- // ── Piece-aware path: manifest found ──
73
- const { tasks, strippedSpec } = manifestResult;
74
- // 1. Post mandate in draft state (pieces need parent to exist)
75
- const generatedWrit = await clerk.post({
76
- type: generatedWritType,
77
- title: briefWrit.title,
78
- body: strippedSpec,
79
- codex: plan.codex,
80
- draft: true,
81
- });
82
- // 2. Create child piece writs (one per task, in manifest order)
83
- for (const taskXml of tasks) {
84
- // Extract task id for a meaningful title
85
- const idMatch = taskXml.match(/<task\s+id="([^"]+)"/);
86
- const taskId = idMatch?.[1] ?? 'task';
87
- const nameMatch = taskXml.match(/<name>([\s\S]*?)<\/name>/);
88
- const taskName = nameMatch?.[1]?.trim() ?? taskId;
89
- await clerk.post({
90
- type: 'piece',
91
- title: taskName,
92
- body: taskXml,
93
- parentId: generatedWrit.id,
94
- });
95
- }
96
- // 3. Link: mandate (source) → brief (target), type 'refines'
97
- await clerk.link(generatedWrit.id, planId, 'refines');
98
- // 4. Transition mandate from draft to open (ready for dispatch)
99
- await clerk.transition(generatedWrit.id, 'open');
100
- // 5. Update PlanDoc
101
- const now = new Date().toISOString();
102
- await book.patch(planId, {
103
- generatedWritId: generatedWrit.id,
104
- status: 'completed',
105
- updatedAt: now,
106
- });
107
- return {
108
- status: 'completed',
109
- yields: { generatedWritId: generatedWrit.id },
110
- };
111
- }
112
- // ── Legacy path: no manifest ──
43
+ // Post the mandate with the full spec verbatim (task-manifest included if present).
113
44
  const generatedWrit = await clerk.post({
114
45
  type: generatedWritType,
115
46
  title: briefWrit.title,
@@ -1 +1 @@
1
- {"version":3,"file":"spec-publish.js","sourceRoot":"","sources":["../../src/engines/spec-publish.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec-publish.js","sourceRoot":"","sources":["../../src/engines/spec-publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAM/C,MAAM,UAAU,uBAAuB,CAAC,YAAiC;IACvE,OAAO;QACL,EAAE,EAAE,wBAAwB;QAE5B,KAAK,CAAC,GAAG,CACP,MAA+B,EAC/B,QAA0B;YAE1B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAgB,CAAC;YACvC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;YAE5B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,SAAS,MAAM,cAAc,CAAC,CAAC;YACjD,CAAC;YAED,kBAAkB;YAClB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CACb,yDAAyD,IAAI,CAAC,MAAM,eAAe,MAAM,IAAI,CAC9F,CAAC;YACJ,CAAC;YAED,uBAAuB;YACvB,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5D,MAAM,IAAI,KAAK,CACb,SAAS,MAAM,2DAA2D,CAC3E,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC,SAAS,CAAW,OAAO,CAAC,CAAC;YAEnD,oCAAoC;YACpC,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE3C,0CAA0C;YAC1C,MAAM,iBAAiB,GAAG,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,iBAAiB,IAAI,SAAS,CAAC;YAE1F,oFAAoF;YACpF,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC;gBACrC,IAAI,EAAE,iBAAiB;gBACvB,KAAK,EAAE,SAAS,CAAC,KAAK;gBACtB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC,CAAC;YAEH,0DAA0D;YAC1D,MAAM,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;YAEtD,iBAAiB;YACjB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBACvB,eAAe,EAAE,aAAa,CAAC,EAAE;gBACjC,MAAM,EAAE,WAAW;gBACnB,SAAS,EAAE,GAAG;aACf,CAAC,CAAC;YAEH,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,EAAE;aAC9C,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardworks/astrolabe-apparatus",
3
- "version": "0.1.212",
3
+ "version": "0.1.214",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,16 +20,16 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "zod": "4.3.6",
23
- "@shardworks/stacks-apparatus": "0.1.212",
24
- "@shardworks/tools-apparatus": "0.1.212",
25
- "@shardworks/clerk-apparatus": "0.1.212",
26
- "@shardworks/fabricator-apparatus": "0.1.212",
27
- "@shardworks/loom-apparatus": "0.1.212",
28
- "@shardworks/spider-apparatus": "0.1.212"
23
+ "@shardworks/stacks-apparatus": "0.1.214",
24
+ "@shardworks/clerk-apparatus": "0.1.214",
25
+ "@shardworks/tools-apparatus": "0.1.214",
26
+ "@shardworks/spider-apparatus": "0.1.214",
27
+ "@shardworks/fabricator-apparatus": "0.1.214",
28
+ "@shardworks/loom-apparatus": "0.1.214"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "25.5.0",
32
- "@shardworks/nexus-core": "0.1.212"
32
+ "@shardworks/nexus-core": "0.1.214"
33
33
  },
34
34
  "files": [
35
35
  "dist",
package/sage-analyst.md CHANGED
@@ -80,8 +80,9 @@ Not every brief produces decisions. If the existing codebase patterns truly dict
80
80
  - What's the simplest version of this that a new operator would use on day one? Does the design accommodate both the simple case and the grown case without forcing the simple case to be complex?
81
81
 
82
82
  5. **Classify the decision** (see Decision Analysis Metadata below).
83
- 6. **Apply the razor.** Check the decision against the five razor criteria in *The Razor* below. If it matches one, leave `selected` unset so the decision surfaces to the patron. If it does not match, apply the three defaults **investigate, don't punt:** uncertainty about a non-razor decision is a cue to read more code or re-read the brief, not a cue to hand the decision to the patron.
84
- 7. **Recommend.** Pick the best option. State why in one line. For non-razor decisions, pre-fill `selected` with your choice.
83
+ 6. **Pre-emption check brief first.** Before applying the razor, check whether the brief (or an architecture spec it references) explicitly answers the question. If so, record the answer as both `recommendation` and `selected`, and cite the source in `rationale`. The patron has already decided; skip the razor.
84
+ 7. **Apply the razor.** For any decision the brief did not pre-empt, check it against the five razor criteria in *The Razor* below. If it matches, leave `selected` unset so the decision surfaces to the patron. If it does not match, apply the three defaults — **investigate, don't punt:** uncertainty about a non-razor decision is a cue to read more code or re-read the brief, not a cue to hand the decision to the patron.
85
+ 8. **Recommend.** Pick the best option. State why in one line. For auto-decided decisions, pre-fill `selected` with your choice.
85
86
 
86
87
  **How to form recommendations:**
87
88
 
@@ -92,23 +93,25 @@ Not every brief produces decisions. If the existing codebase patterns truly dict
92
93
 
93
94
  Not every decision warrants the patron's time. Over the last 38 specs the patron overrode only 3.7% of decisions — the rest were rubber-stamps. Most decisions can be settled by the analyst with a recorded recommendation, and only a narrow class should actually block on patron review.
94
95
 
95
- **Surface a decision to the patron (leave `selected` unset) if it matches any of these five criteria:**
96
+ **Pre-emption: the brief has the last word.** Before applying the razor, check whether the brief (or an architecture spec it references) explicitly answers the question — by prescribing a value, a behavior, or a pattern to follow. If so, pre-fill `selected` with that answer and cite the source in `rationale`, regardless of whether the question would otherwise match a razor criterion. The patron has already decided by writing the brief; re-surfacing a settled question drains attention from the decisions that are genuinely open.
96
97
 
97
- 1. **Divergence from convention.** The recommendation departs from an established codebase pattern. *Example:* "This package stores config in `guild.json`, but for this feature we would want a dedicated `foo.config.ts` — should we?"
98
- 2. **High consumer stakes.** A consumer of the API, feature, or workflow would materially notice the difference — ergonomics, runtime behavior, error semantics, performance. *Example:* "Should `post()` return the full writ or only the id?"
99
- 3. **Observable product surface.** An operator or end user would notice the choice in naming, UX, or behavior. *Example:* "Should the new page be titled `Reviews` or `Review History`?"
100
- 4. **Irreversibility.** The choice is hard to undo without a migration, a deprecation cycle, or breaking downstream consumers. *Example:* "Should the new field be stored as an index column or only in the document body?"
101
- 5. **Genuine ambiguity.** The codebase and the brief give no clear signal and two or more options are equally valid; no amount of further investigation would break the tie. *Example:* "Should we name this `digest` or `summary`? Both terms appear in the codebase with comparable frequency."
98
+ **Otherwise, surface the decision to the patron (leave `selected` unset) if and only if it falls into one of these five categories:**
102
99
 
103
- **Investigate, don't punt.** When you feel uncertainty about a decision that does *not* match one of these five criteria, that uncertainty is a signal to read more code, trace another caller, or re-read the brief not a signal to surface the decision to the patron. Punting a non-razor decision drains patron attention from the decisions that actually need it.
100
+ 1. **Vocabulary/pattern establishment.** New guild terms, categorical distinctions, or patterns other code will follow. *Example:* "Should we call the new state 'parked' or 'deferred'?"
101
+ 2. **Human-facing surface.** CLI text, error messages, agent personalities, doc phrasing, or UX details a patron or operator will read. *Example:* "Should the error read 'Writ not found' or 'No writ with id X'?"
102
+ 3. **Scope boundary.** Cutlines between the current commission and follow-up work — the 'should we also do X?' questions. *Example:* "Should this change also update the two-phase-planning rig, or is that a separate commission?"
103
+ 4. **Shape of persisted or inter-component data.** Typed vs opaque, required vs optional, configured vs convention — when other components will consume the shape. *Example:* "Should `Decision.scope` be `string[]` or a typed `ScopeRef[]`?"
104
+ 5. **Component responsibility boundaries.** Who owns a behavior across engines, tools, and apparatuses — when the decision sets a pattern for ownership. *Example:* "Should the sage write decisions through a tool, or through direct book access?"
105
+
106
+ **Investigate, don't punt.** When you feel uncertainty about a decision that does *not* match one of these five categories, that uncertainty is a signal to read more code, trace another caller, or re-read the brief — not a signal to surface the decision to the patron. Punting a non-razor decision drains patron attention from the decisions that actually need it.
104
107
 
105
108
  #### The Three Defaults
106
109
 
107
- For any decision that does **not** match the razor, apply these defaults in order and pre-fill `selected` with the answer they produce:
110
+ For any decision that was not pre-empted by the brief and does **not** match the razor, apply these defaults and pre-fill `selected` with the answer they produce:
108
111
 
109
- 1. **Match the codebase.** If the existing code already handles an analogous situation, follow the established pattern. The patron is most likely to accept choices that follow suit.
110
- 2. **Match the brief.** If the codebase is silent, use the brief's terminology, naming, and framing verbatim. The brief reflects what the patron has already chosen to say out loud.
111
- 3. **Pick the simplest viable option.** If neither the codebase nor the brief breaks the tie, pick the option that keeps the day-one shape simple and does not foreclose the grown-case. Record the rationale.
112
+ 1. **Prefer removal to deprecation.** When refactoring, rip out the old path. No deprecation windows unless the patron explicitly asks for one.
113
+ 2. **Prefer fail-loud to silent fallback.** Throw on missing input; no defaults-when-absent unless the absent case is itself a legitimate state.
114
+ 3. **Extend the API at the right layer; don't route around it.** If the recommendation involves a workaround or "the anima handles it via prompt," default to adding the method/tool instead.
112
115
 
113
116
  Each decision needs:
114
117
  - `id` — sequential identifier (D1, D2, ...)
@@ -118,7 +121,7 @@ Each decision needs:
118
121
  - `options` — key → description map of reasonable approaches (keep descriptions to one line each)
119
122
  - `recommendation` — the option key you recommend
120
123
  - `rationale` — why this option, in one line
121
- - `selected` — **If the decision matches any of the five razor criteria, leave `selected` unset.** Otherwise, apply the three defaults, pick the answer, and pre-fill `selected` with your choice. Pre-filled decisions are auto-accepted — the engine drops them from the patron-review gate entirely, so the patron only sees decisions that genuinely warrant their attention. The patron changes `selected` only when overriding a surfaced decision, and if they write a custom override the reconcile loop replaces `selected` with `patronOverride` automatically. Never set both yourself.
124
+ - `selected` — **If the brief pre-empts the decision, set `selected` to the brief's answer.** Otherwise, if the decision matches any of the five razor criteria, leave `selected` unset. Otherwise, apply the three defaults and pre-fill `selected` with your choice. Pre-filled decisions are auto-accepted — the engine drops them from the patron-review gate entirely, so the patron only sees decisions that genuinely warrant their attention. The patron changes `selected` only when overriding a surfaced decision, and if they write a custom override the reconcile loop replaces `selected` with `patronOverride` automatically. Never set both yourself.
122
125
  - `analysis` — classification metadata (see below)
123
126
 
124
127
  Order decisions by scope item, then by category (product → api → implementation).
@@ -115,8 +115,9 @@ Not every brief produces decisions. If the existing codebase patterns truly dict
115
115
  - What's the simplest version of this that a new operator would use on day one? Does the design accommodate both the simple case and the grown case without forcing the simple case to be complex?
116
116
 
117
117
  5. **Classify the decision** (see Decision Analysis Metadata below).
118
- 6. **Apply the razor.** Check the decision against the five razor criteria in *The Razor* below. If it matches one, leave `selected` unset so the decision surfaces to the patron. If it does not match, apply the three defaults **investigate, don't punt:** uncertainty about a non-razor decision is a cue to read more code or re-read the brief, not a cue to hand the decision to the patron.
119
- 7. **Recommend.** Pick the best option. State why in one line. For non-razor decisions, pre-fill `selected` with your choice.
118
+ 6. **Pre-emption check brief first.** Before applying the razor, check whether the brief (or an architecture spec it references) explicitly answers the question. If so, record the answer as both `recommendation` and `selected`, and cite the source in `rationale`. The patron has already decided; skip the razor.
119
+ 7. **Apply the razor.** For any decision the brief did not pre-empt, check it against the five razor criteria in *The Razor* below. If it matches, leave `selected` unset so the decision surfaces to the patron. If it does not match, apply the three defaults — **investigate, don't punt:** uncertainty about a non-razor decision is a cue to read more code or re-read the brief, not a cue to hand the decision to the patron.
120
+ 8. **Recommend.** Pick the best option. State why in one line. For auto-decided decisions, pre-fill `selected` with your choice.
120
121
 
121
122
  **How to form recommendations:**
122
123
 
@@ -127,23 +128,25 @@ Not every brief produces decisions. If the existing codebase patterns truly dict
127
128
 
128
129
  Not every decision warrants the patron's time. Over the last 38 specs the patron overrode only 3.7% of decisions — the rest were rubber-stamps. Most decisions can be settled by the analyst with a recorded recommendation, and only a narrow class should actually block on patron review.
129
130
 
130
- **Surface a decision to the patron (leave `selected` unset) if it matches any of these five criteria:**
131
+ **Pre-emption: the brief has the last word.** Before applying the razor, check whether the brief (or an architecture spec it references) explicitly answers the question — by prescribing a value, a behavior, or a pattern to follow. If so, pre-fill `selected` with that answer and cite the source in `rationale`, regardless of whether the question would otherwise match a razor criterion. The patron has already decided by writing the brief; re-surfacing a settled question drains attention from the decisions that are genuinely open.
131
132
 
132
- 1. **Divergence from convention.** The recommendation departs from an established codebase pattern. *Example:* "This package stores config in `guild.json`, but for this feature we would want a dedicated `foo.config.ts` — should we?"
133
- 2. **High consumer stakes.** A consumer of the API, feature, or workflow would materially notice the difference — ergonomics, runtime behavior, error semantics, performance. *Example:* "Should `post()` return the full writ or only the id?"
134
- 3. **Observable product surface.** An operator or end user would notice the choice in naming, UX, or behavior. *Example:* "Should the new page be titled `Reviews` or `Review History`?"
135
- 4. **Irreversibility.** The choice is hard to undo without a migration, a deprecation cycle, or breaking downstream consumers. *Example:* "Should the new field be stored as an index column or only in the document body?"
136
- 5. **Genuine ambiguity.** The codebase and the brief give no clear signal and two or more options are equally valid; no amount of further investigation would break the tie. *Example:* "Should we name this `digest` or `summary`? Both terms appear in the codebase with comparable frequency."
133
+ **Otherwise, surface the decision to the patron (leave `selected` unset) if and only if it falls into one of these five categories:**
137
134
 
138
- **Investigate, don't punt.** When you feel uncertainty about a decision that does *not* match one of these five criteria, that uncertainty is a signal to read more code, trace another caller, or re-read the brief not a signal to surface the decision to the patron. Punting a non-razor decision drains patron attention from the decisions that actually need it.
135
+ 1. **Vocabulary/pattern establishment.** New guild terms, categorical distinctions, or patterns other code will follow. *Example:* "Should we call the new state 'parked' or 'deferred'?"
136
+ 2. **Human-facing surface.** CLI text, error messages, agent personalities, doc phrasing, or UX details a patron or operator will read. *Example:* "Should the error read 'Writ not found' or 'No writ with id X'?"
137
+ 3. **Scope boundary.** Cutlines between the current commission and follow-up work — the 'should we also do X?' questions. *Example:* "Should this change also update the two-phase-planning rig, or is that a separate commission?"
138
+ 4. **Shape of persisted or inter-component data.** Typed vs opaque, required vs optional, configured vs convention — when other components will consume the shape. *Example:* "Should `Decision.scope` be `string[]` or a typed `ScopeRef[]`?"
139
+ 5. **Component responsibility boundaries.** Who owns a behavior across engines, tools, and apparatuses — when the decision sets a pattern for ownership. *Example:* "Should the sage write decisions through a tool, or through direct book access?"
140
+
141
+ **Investigate, don't punt.** When you feel uncertainty about a decision that does *not* match one of these five categories, that uncertainty is a signal to read more code, trace another caller, or re-read the brief — not a signal to surface the decision to the patron. Punting a non-razor decision drains patron attention from the decisions that actually need it.
139
142
 
140
143
  #### The Three Defaults
141
144
 
142
- For any decision that does **not** match the razor, apply these defaults in order and pre-fill `selected` with the answer they produce:
145
+ For any decision that was not pre-empted by the brief and does **not** match the razor, apply these defaults and pre-fill `selected` with the answer they produce:
143
146
 
144
- 1. **Match the codebase.** If the existing code already handles an analogous situation, follow the established pattern. The patron is most likely to accept choices that follow suit.
145
- 2. **Match the brief.** If the codebase is silent, use the brief's terminology, naming, and framing verbatim. The brief reflects what the patron has already chosen to say out loud.
146
- 3. **Pick the simplest viable option.** If neither the codebase nor the brief breaks the tie, pick the option that keeps the day-one shape simple and does not foreclose the grown-case. Record the rationale.
147
+ 1. **Prefer removal to deprecation.** When refactoring, rip out the old path. No deprecation windows unless the patron explicitly asks for one.
148
+ 2. **Prefer fail-loud to silent fallback.** Throw on missing input; no defaults-when-absent unless the absent case is itself a legitimate state.
149
+ 3. **Extend the API at the right layer; don't route around it.** If the recommendation involves a workaround or "the anima handles it via prompt," default to adding the method/tool instead.
147
150
 
148
151
  Each decision needs:
149
152
  - `id` — sequential identifier (D1, D2, ...)
@@ -153,7 +156,7 @@ Each decision needs:
153
156
  - `options` — key → description map of reasonable approaches (keep descriptions to one line each)
154
157
  - `recommendation` — the option key you recommend
155
158
  - `rationale` — why this option, in one line
156
- - `selected` — **If the decision matches any of the five razor criteria, leave `selected` unset.** Otherwise, apply the three defaults, pick the answer, and pre-fill `selected` with your choice. Pre-filled decisions are auto-accepted — the engine drops them from the patron-review gate entirely, so the patron only sees decisions that genuinely warrant their attention. The patron changes `selected` only when overriding a surfaced decision, and if they write a custom override the reconcile loop replaces `selected` with `patronOverride` automatically. Never set both yourself.
159
+ - `selected` — **If the brief pre-empts the decision, set `selected` to the brief's answer.** Otherwise, if the decision matches any of the five razor criteria, leave `selected` unset. Otherwise, apply the three defaults and pre-fill `selected` with your choice. Pre-filled decisions are auto-accepted — the engine drops them from the patron-review gate entirely, so the patron only sees decisions that genuinely warrant their attention. The patron changes `selected` only when overriding a surfaced decision, and if they write a custom override the reconcile loop replaces `selected` with `patronOverride` automatically. Never set both yourself.
157
160
  - `analysis` — classification metadata (see below)
158
161
 
159
162
  Order decisions by scope item, then by category (product → api → implementation).