@hobin/developer 0.1.10 → 0.1.12

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.
@@ -46,6 +46,36 @@ export type QuestionUpdateStatus =
46
46
  export type ImplementationProfile =
47
47
  | "ordinary"
48
48
  | "behavior-preserving-structure";
49
+ export type InvariantHandling =
50
+ | {
51
+ kind: "not-applicable";
52
+ reason: string;
53
+ }
54
+ | {
55
+ kind: "evidence-preserving-boundary";
56
+ rawRepresentation: string;
57
+ refinedRepresentation: string;
58
+ producer: string;
59
+ failure: string;
60
+ firstEffect: string;
61
+ }
62
+ | {
63
+ kind: "trusted-compiler-gap";
64
+ assertion: string;
65
+ establishedBy: string;
66
+ limitation: string;
67
+ containment: string;
68
+ verification: string;
69
+ };
70
+
71
+ export function formatInvariantHandling(value: InvariantHandling): string {
72
+ if (value.kind === "not-applicable")
73
+ return `not applicable — ${value.reason}`;
74
+ if (value.kind === "evidence-preserving-boundary") {
75
+ return `${value.rawRepresentation} -> ${value.refinedRepresentation} via ${value.producer}; failure=${value.failure}; first effect=${value.firstEffect}`;
76
+ }
77
+ return `trusted compiler gap — ${value.assertion}; established by ${value.establishedBy}; limitation=${value.limitation}; contained at ${value.containment}; check=${value.verification}`;
78
+ }
49
79
 
50
80
  export interface ActivationEvent {
51
81
  protocol: typeof PROTOCOL;
@@ -63,6 +93,7 @@ export interface ImplementationStepContract {
63
93
  movement: string;
64
94
  stopCondition: string;
65
95
  verification: string;
96
+ invariantHandling: InvariantHandling;
66
97
  }
67
98
 
68
99
  export interface RouteAlternative {
@@ -248,6 +279,20 @@ function isStringArray(value: unknown): value is string[] {
248
279
  );
249
280
  }
250
281
 
282
+ function parseArray<T>(
283
+ value: unknown,
284
+ parse: (item: unknown) => T | undefined,
285
+ ): T[] | undefined {
286
+ if (!Array.isArray(value)) return undefined;
287
+ const parsed: T[] = [];
288
+ for (const item of value) {
289
+ const result = parse(item);
290
+ if (result === undefined) return undefined;
291
+ parsed.push(result);
292
+ }
293
+ return parsed;
294
+ }
295
+
251
296
  function isJudgmentStatus(value: unknown): value is JudgmentStatus {
252
297
  return (
253
298
  value === "resolved" ||
@@ -263,6 +308,51 @@ function isImplementationProfile(
263
308
  return value === "ordinary" || value === "behavior-preserving-structure";
264
309
  }
265
310
 
311
+ function parseInvariantHandling(value: unknown): InvariantHandling | undefined {
312
+ if (!isObject(value) || typeof value.kind !== "string") return undefined;
313
+ if (value.kind === "not-applicable") {
314
+ if (typeof value.reason !== "string") return undefined;
315
+ return { kind: value.kind, reason: value.reason };
316
+ }
317
+ if (value.kind === "evidence-preserving-boundary") {
318
+ if (
319
+ typeof value.rawRepresentation !== "string" ||
320
+ typeof value.refinedRepresentation !== "string" ||
321
+ typeof value.producer !== "string" ||
322
+ typeof value.failure !== "string" ||
323
+ typeof value.firstEffect !== "string"
324
+ ) {
325
+ return undefined;
326
+ }
327
+ return {
328
+ kind: value.kind,
329
+ rawRepresentation: value.rawRepresentation,
330
+ refinedRepresentation: value.refinedRepresentation,
331
+ producer: value.producer,
332
+ failure: value.failure,
333
+ firstEffect: value.firstEffect,
334
+ };
335
+ }
336
+ if (value.kind !== "trusted-compiler-gap") return undefined;
337
+ if (
338
+ typeof value.assertion !== "string" ||
339
+ typeof value.establishedBy !== "string" ||
340
+ typeof value.limitation !== "string" ||
341
+ typeof value.containment !== "string" ||
342
+ typeof value.verification !== "string"
343
+ ) {
344
+ return undefined;
345
+ }
346
+ return {
347
+ kind: value.kind,
348
+ assertion: value.assertion,
349
+ establishedBy: value.establishedBy,
350
+ limitation: value.limitation,
351
+ containment: value.containment,
352
+ verification: value.verification,
353
+ };
354
+ }
355
+
266
356
  function parseImplementationStep(
267
357
  value: unknown,
268
358
  ): ImplementationStepContract | undefined {
@@ -274,10 +364,20 @@ function parseImplementationStep(
274
364
  ) {
275
365
  return undefined;
276
366
  }
367
+ const invariantHandling =
368
+ value.invariantHandling === undefined
369
+ ? {
370
+ kind: "not-applicable" as const,
371
+ reason:
372
+ "Legacy implementation route predates the invariant-handling declaration.",
373
+ }
374
+ : parseInvariantHandling(value.invariantHandling);
375
+ if (!invariantHandling) return undefined;
277
376
  return {
278
377
  movement: value.movement,
279
378
  stopCondition: value.stopCondition,
280
379
  verification: value.verification,
380
+ invariantHandling,
281
381
  };
282
382
  }
283
383
 
@@ -622,19 +722,21 @@ export function normalizeDeveloperEvent(
622
722
  ) {
623
723
  return undefined;
624
724
  }
625
- const consideredAlternatives = Array.isArray(value.consideredAlternatives)
626
- ? value.consideredAlternatives.map(parseRouteAlternative)
627
- : [];
628
- if (consideredAlternatives.some((alternative) => !alternative))
629
- return undefined;
630
- const referenceRoutes = Array.isArray(value.referenceRoutes)
631
- ? value.referenceRoutes.map(parseReferencePolicyRoute)
632
- : [];
633
- if (referenceRoutes.some((route) => !route)) return undefined;
634
- const loadedReferences = Array.isArray(value.loadedReferences)
635
- ? value.loadedReferences.map(parseReferenceLoad)
636
- : [];
637
- if (loadedReferences.some((reference) => !reference)) return undefined;
725
+ const consideredAlternatives =
726
+ value.consideredAlternatives === undefined
727
+ ? []
728
+ : parseArray(value.consideredAlternatives, parseRouteAlternative);
729
+ if (!consideredAlternatives) return undefined;
730
+ const referenceRoutes =
731
+ value.referenceRoutes === undefined
732
+ ? []
733
+ : parseArray(value.referenceRoutes, parseReferencePolicyRoute);
734
+ if (!referenceRoutes) return undefined;
735
+ const loadedReferences =
736
+ value.loadedReferences === undefined
737
+ ? []
738
+ : parseArray(value.loadedReferences, parseReferenceLoad);
739
+ if (!loadedReferences) return undefined;
638
740
  return {
639
741
  protocol: PROTOCOL,
640
742
  kind: "route",
@@ -643,15 +745,15 @@ export function normalizeDeveloperEvent(
643
745
  target: value.target,
644
746
  reason: value.reason,
645
747
  knownEvidence: value.knownEvidence,
646
- consideredAlternatives: consideredAlternatives as RouteAlternative[],
748
+ consideredAlternatives,
647
749
  availableReferences: value.availableReferences ?? [],
648
- referenceRoutes: referenceRoutes as ReferencePolicyRoute[],
750
+ referenceRoutes,
649
751
  referenceExemptionCriteria:
650
752
  value.referenceExemptionCriteria === undefined
651
753
  ? undefined
652
754
  : parseReferencePolicyExemption(value.referenceExemptionCriteria),
653
755
  referencePolicySha256: value.referencePolicySha256,
654
- loadedReferences: loadedReferences as ReferenceLoad[],
756
+ loadedReferences,
655
757
  targetQuestionId: value.targetQuestionId,
656
758
  methodLocation: value.methodLocation,
657
759
  executionProfile: value.executionProfile,
@@ -698,21 +800,21 @@ export function normalizeDeveloperEvent(
698
800
  return undefined;
699
801
  }
700
802
 
701
- const openedQuestions = value.openedQuestions.map(parsePendingQuestion);
702
- if (openedQuestions.some((question) => !question)) return undefined;
703
- const referenceBasis = Array.isArray(value.referenceBasis)
704
- ? value.referenceBasis.map(parseReferenceBasis)
705
- : [];
706
- if (referenceBasis.some((basis) => !basis)) return undefined;
707
- if (
708
- value.questionUpdates !== undefined &&
709
- !Array.isArray(value.questionUpdates)
710
- )
711
- return undefined;
712
- const questionUpdates = Array.isArray(value.questionUpdates)
713
- ? value.questionUpdates.map(parseQuestionUpdate)
714
- : [];
715
- if (questionUpdates.some((update) => !update)) return undefined;
803
+ const openedQuestions = parseArray(
804
+ value.openedQuestions,
805
+ parsePendingQuestion,
806
+ );
807
+ if (!openedQuestions) return undefined;
808
+ const referenceBasis =
809
+ value.referenceBasis === undefined
810
+ ? []
811
+ : parseArray(value.referenceBasis, parseReferenceBasis);
812
+ if (!referenceBasis) return undefined;
813
+ const questionUpdates =
814
+ value.questionUpdates === undefined
815
+ ? []
816
+ : parseArray(value.questionUpdates, parseQuestionUpdate);
817
+ if (!questionUpdates) return undefined;
716
818
  return {
717
819
  protocol: PROTOCOL,
718
820
  kind: "judgment",
@@ -722,13 +824,13 @@ export function normalizeDeveloperEvent(
722
824
  status: value.status,
723
825
  result: value.result,
724
826
  basis: value.basis,
725
- referenceBasis: referenceBasis as ReferenceBasis[],
827
+ referenceBasis,
726
828
  referenceExemption:
727
829
  value.referenceExemption === undefined
728
830
  ? undefined
729
831
  : parseReferenceExemption(value.referenceExemption),
730
- openedQuestions: openedQuestions as PendingQuestion[],
731
- questionUpdates: questionUpdates as QuestionUpdate[],
832
+ openedQuestions,
833
+ questionUpdates,
732
834
  artifacts: value.artifacts,
733
835
  changedArtifacts:
734
836
  typeof value.changedArtifacts === "boolean"
package/extensions/tui.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  } from "@earendil-works/pi-tui";
23
23
 
24
24
  import {
25
+ formatInvariantHandling,
25
26
  protocolState,
26
27
  type ChoiceResponseField,
27
28
  type ChoiceResponseOption,
@@ -1416,6 +1417,10 @@ export class DeveloperHistoryDetailPanel {
1416
1417
  addField("movement", route.implementationStep.movement);
1417
1418
  addField("stop condition", route.implementationStep.stopCondition);
1418
1419
  addField("verification", route.implementationStep.verification);
1420
+ addField(
1421
+ "invariant handling",
1422
+ formatInvariantHandling(route.implementationStep.invariantHandling),
1423
+ );
1419
1424
  }
1420
1425
  addValues("known evidence", route.knownEvidence);
1421
1426
  addValues(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hobin/developer",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Branch-aware judgment routing, evidence gates, and focused development skills for Pi.",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package", "pi", "developer", "skills"],
@@ -0,0 +1,284 @@
1
+ ---
2
+ name: doctor
3
+ description: "Diagnose a user-selected or explicitly bounded existing codebase scope and produce an evidence-backed improvement plan. Use for repository, package, directory, module, runtime-flow, boundary, or feature-slice review when behavior to preserve, invariant risk, structural pressure, evidence gaps, and justified now/next/later/leave-alone work must be assessed together. Not for pull-request diff review, environment setup checks, speculative cleanup, or automatic repair."
4
+ ---
5
+
6
+ # Doctor
7
+
8
+ Diagnose one bounded existing-code scope, consult the focused Developer judgments
9
+ that its evidence actually triggers, and synthesize a treatment plan without
10
+ turning age, style, or code smells into disease.
11
+
12
+ ## Core Question
13
+
14
+ Within this explicit codebase scope, what must be preserved, what is at risk or
15
+ obstructing change, and what should be treated now, prepared next, observed, or
16
+ left alone?
17
+
18
+ ## Judgment Spine
19
+
20
+ ```text
21
+ requested target and change horizon
22
+ -> requested / inspected / claim scope
23
+ -> behavior-preservation baseline
24
+ -> broad, shallow disposition of every Developer judgment lens
25
+ -> narrow, deep owner-skill consultations and triggered references
26
+ -> evidence-backed mechanisms and treatment candidates
27
+ -> treat-now | prepare-next | observe | leave-alone | needs-decision
28
+ -> first concrete handoff
29
+ ```
30
+
31
+ Doctor is broad in lens coverage and bounded in code coverage. It is not a fixed
32
+ prelude for ordinary tasks.
33
+
34
+ ## Inputs
35
+
36
+ - User-selected target, or evidence sufficient to propose one
37
+ - Repository structure, entry points, callers, runtime flows, and public boundaries
38
+ - Accepted behavior, tests, traces, incidents, schemas, and compatibility evidence
39
+ - Current change pressure and intended horizon when available
40
+ - Explicit review depth, lenses, exclusions, and evidence budget
41
+
42
+ ## Scope Contract
43
+
44
+ Establish scope before diagnosis. Separate what was requested from what was
45
+ actually inspected and from the area to which conclusions may apply.
46
+
47
+ | Field | Required meaning |
48
+ | --- | --- |
49
+ | Requested target | What the user asked Doctor to review |
50
+ | Selected scope | Path, symbol, runtime flow, boundary, feature slice, subsystem, or repository sample |
51
+ | Included | Concrete paths, symbols, entry points, flows, or collaborators admitted |
52
+ | Excluded | Nearby areas this review does not cover |
53
+ | Depth | `local`, `collaborators`, `flow`, `subsystem`, or `repository-sample` |
54
+ | Lenses | Requested concerns, or `balanced` when no concern dominates |
55
+ | Evidence budget | Bounded files, history, commands, traces, or sampling rule |
56
+ | Inspected scope | Evidence actually read or executed |
57
+ | Claim boundary | The largest area current evidence can support conclusions about |
58
+
59
+ If the user did not select a useful scope, perform only a cheap orientation pass:
60
+ inspect manifests, subsystem boundaries, entry points, current failures or
61
+ requests, recent change pressure, dependency hubs, and irreversible effects.
62
+ Then propose the smallest valuable scope with evidence. Do not call an
63
+ orientation sample a whole-repository diagnosis.
64
+
65
+ A dependency outside scope may be inspected when the declared depth includes it.
66
+ Otherwise present a scope-expansion candidate with the trigger, added surface,
67
+ expected value, and cost. Never expand recursively by curiosity.
68
+
69
+ ## Review Mode
70
+
71
+ Use `thorough-within-scope` unless the user explicitly asks for a faster pass.
72
+
73
+ - `quick`: inspect the strongest observable risk and label all omitted lenses and
74
+ code surfaces as unreviewed;
75
+ - `standard`: disposition every Developer lens, then deepen only the
76
+ highest-consequence or currently pressured consultations;
77
+ - `thorough-within-scope`: disposition every available Developer lens, route
78
+ every triggered distinct consultation, inspect every policy-route trigger in
79
+ each selected owner skill, and apply every reference required by every
80
+ selected route.
81
+
82
+ Exhaustiveness applies to judgment coverage inside the claim boundary, not to
83
+ unbounded file traversal or unsupported whole-system claims.
84
+
85
+ ## Evidence Order
86
+
87
+ Prefer evidence in this order while keeping disagreements visible:
88
+
89
+ 1. accepted user or product contract;
90
+ 2. observed externally meaningful behavior, production trace, incident, or
91
+ compatibility obligation;
92
+ 3. executable tests and fixtures whose observers match the claim;
93
+ 4. callers, state transitions, persisted representations, and effect order;
94
+ 5. implementation structure and static relationships;
95
+ 6. history, churn, coverage counts, complexity, and code smells as scope or
96
+ pressure clues only.
97
+
98
+ A smell becomes a finding only when it is connected to a behavior, invariant,
99
+ change cost, failure mode, evidence gap, or compatibility consequence. Age,
100
+ language fashion, low coverage, duplication, or a long function alone does not
101
+ justify treatment.
102
+
103
+ ## Preservation Baseline
104
+
105
+ Before finding defects, record behavior and obligations that treatment must not
106
+ break.
107
+
108
+ | Behavior or contract | Owner / observers | Evidence | Confidence | Failure consequence |
109
+ | --- | --- | --- | --- | --- |
110
+
111
+ Cover relevant public APIs, critical user flows, persisted or legacy data,
112
+ external input boundaries, state transitions, effect order, failure and retry
113
+ behavior, and compatibility surfaces. Record `baseline evidence gap` when an
114
+ important obligation has no distinguishing observer; do not silently treat it
115
+ as absent.
116
+
117
+ ## Universal Lens Sweep
118
+
119
+ In `standard` and `thorough-within-scope`, give every available Developer skill
120
+ other than Doctor one disposition: `triggered`, `no-trigger`, `needs-evidence`,
121
+ `out-of-scope`, or `blocked`. Each disposition requires concrete scope evidence.
122
+
123
+ | Owner skill | Diagnostic question | Representative trigger |
124
+ | --- | --- | --- |
125
+ | `specify` | Is code deciding unresolved product meaning or scope? | docs, tests, callers, or defaults imply different product senses |
126
+ | `model` | Are admitted, forbidden, absent, replacement, relational, or temporal conditions unsettled? | null/default/legacy combinations, invalid states, retries, ordering, solver or query semantics |
127
+ | `sketch` | Is an implementable owner, boundary, representation, data flow, state flow, or collaboration missing? | unchecked narrowing, shotgun parsing, navigation leaks, mixed effects, repeated ownership decisions |
128
+ | `signal` | Is observable structural movement present rather than mere similarity? | repeated change, closest parallel cases, model-code mismatch, boundary pressure |
129
+ | `naming-judgment` | Do names preserve stable domain sense and expose consequential effects? | filler names, effect-hiding verbs, implementation-shaped names, sense collisions |
130
+ | `abstraction-review` | Can callers safely rely on an already-shaped helper, API, interface, boundary, or workflow rule? | a concrete candidate makes unstable or leaking promises |
131
+ | `schedule` | When does a stable concrete structural candidate belong? | current invariant pressure, nested work, delay cost, or lost reversibility |
132
+ | `verify` | What do current checks prove, and what plausible wrong shape still passes? | green checks with observer, source, branch, construction-path, or effect-order gaps |
133
+ | `adversarial-eval` | Does a consequential claim need escalating finite counterexamples? | ordinary checks can pass while security, data, compatibility, or workflow behavior is wrong |
134
+ | `visualize` | Would a visual surface materially lower the cost of judging relationships? | state, ownership, flow, dependency, or ordering is hard to inspect in prose |
135
+
136
+ Use this inspection surface:
137
+
138
+ | Skill | Disposition | Scope evidence | Concrete consultation question or exemption |
139
+ | --- | --- | --- | --- |
140
+
141
+ `no-trigger` means observed evidence makes the compact question irrelevant here;
142
+ it does not mean the entire codebase is healthy. `needs-evidence` names the
143
+ smallest observation that could change the disposition.
144
+
145
+ ## Consultation Workflow
146
+
147
+ Doctor does not impersonate all other skills and must not read sibling skill
148
+ references inside a Doctor route.
149
+
150
+ For every `triggered` disposition:
151
+
152
+ 1. write one concrete consultation question and its trigger evidence;
153
+ 2. close the Doctor triage route;
154
+ 3. route the question to its owning Developer skill;
155
+ 4. inspect every reference-policy route in that skill;
156
+ 5. select every route whose observable trigger answers a distinct unresolved
157
+ question;
158
+ 6. load and apply every co-required reference in the declared order;
159
+ 7. preserve the owner skill's artifact, stop, separation boundary, and
160
+ `reference_basis`;
161
+ 8. return to Doctor only after consultations resolve or become explicit gaps.
162
+
163
+ Use dependency only when evidence requires it: unresolved meaning or condition
164
+ space precedes a design that depends on it; `signal` precedes promotion of a
165
+ new structural candidate; `sketch` creates an original surface;
166
+ `abstraction-review` judges an existing surface; `schedule` requires a stable
167
+ candidate; `verify` and `adversarial-eval` challenge the resulting claims. Do
168
+ not run this as a mandatory phase sequence when a lens is `no-trigger`.
169
+
170
+ When Developer protocol is active and consultations remain, keep one
171
+ agent-owned `before-completion` Doctor synthesis question rather than opening one
172
+ pending question per lens. Owner-skill judgments may resolve their narrow
173
+ question while retaining that synthesis question with updated remaining work.
174
+ The final Doctor route resolves it after integrating the consultation ledger.
175
+
176
+ Use a consultation ledger:
177
+
178
+ | Skill / policy route | Trigger evidence | Judgment artifact | Result | Residual gap |
179
+ | --- | --- | --- | --- | --- |
180
+
181
+ ## Diagnosis
182
+
183
+ Synthesize consultations into findings without erasing disagreement or evidence
184
+ limits.
185
+
186
+ | ID | Observation | Consequence | Mechanism | Evidence | Confidence | Affected scope | Owner judgment |
187
+ | --- | --- | --- | --- | --- | --- | --- | --- |
188
+
189
+ A valid diagnosis names:
190
+
191
+ - the observable behavior, invariant risk, change obstruction, or evidence gap;
192
+ - the mechanism connecting code shape to that consequence;
193
+ - supporting and contradicting evidence;
194
+ - confidence and the exact claim boundary;
195
+ - the smallest plausible falsifier;
196
+ - a concrete candidate only when enough meaning and shape are stable.
197
+
198
+ Do not merge independent findings merely because one treatment could touch the
199
+ same files.
200
+
201
+ ## Treatment Plan
202
+
203
+ Use ordinal decisions rather than pseudo-precise health scores.
204
+
205
+ | Candidate | Diagnosis | Behavior to preserve | Guarantee gained | Current pressure | Dependencies / reversibility | Stable landing | Verifier | Decision |
206
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
207
+
208
+ Decide:
209
+
210
+ - `treat-now`: current correctness, security, data, compatibility, or invariant
211
+ risk justifies the smallest stable treatment;
212
+ - `prepare-next`: the treatment is needed before the next related change to
213
+ avoid nested work or prevent a known recurrence;
214
+ - `observe`: current pressure is insufficient; record an observable reopen
215
+ condition;
216
+ - `leave-alone`: treatment is speculative, destroys useful freedom, or costs
217
+ more than the evidenced problem;
218
+ - `needs-decision`: product, operational, or risk ownership cannot be inferred.
219
+
220
+ Do not turn the treatment plan into a predetermined multi-step implementation
221
+ queue. Provide one first handoff with its owner skill, preserved behavior,
222
+ smallest stable landing, narrow verifier, and explicitly deferred work. Reobserve
223
+ and reroute after that landing.
224
+
225
+ ## Output
226
+
227
+ Lead with scope and the user's actual change or risk, not a health score. A
228
+ resolved Doctor report contains:
229
+
230
+ 1. Doctor scope and actual coverage;
231
+ 2. preservation baseline;
232
+ 3. all required lens dispositions;
233
+ 4. consultation ledger with policy-route and reference provenance;
234
+ 5. diagnosis table;
235
+ 6. treatment plan;
236
+ 7. first concrete handoff and residual unknowns;
237
+ 8. an ASCII flow, ownership, state, or dependency map when relationships are
238
+ materially consequential.
239
+
240
+ When used inside a larger task, return:
241
+
242
+ ```text
243
+ Status: resolved | needs-evidence | not-applicable | blocked
244
+ Result: scope-bound diagnosis and treatment order, or the consultation plan still required
245
+ Basis: inspected code, behavior, callers, tests, traces, history, and owner-skill judgments
246
+ Open questions: unresolved consultations, scope choices, product decisions, or none
247
+ Artifacts: scope/coverage map, preservation baseline, lens matrix, consultation ledger, findings, treatment plan, and first handoff
248
+ ```
249
+
250
+ Return only Doctor's scope, triage, or synthesis judgment for the current route;
251
+ leave owner-skill consultation and implementation routing to the caller.
252
+
253
+ ## Completion
254
+
255
+ A `thorough-within-scope` diagnosis is resolved only when:
256
+
257
+ - requested, selected, inspected, and claim scopes are explicit;
258
+ - every available non-Doctor skill has an evidence-backed disposition;
259
+ - every triggered consultation is resolved or retained as an explicit gap;
260
+ - every selected owner-skill policy route loaded and applied all required
261
+ references;
262
+ - every finding has an observable consequence, mechanism, evidence, confidence,
263
+ scope, and falsifier;
264
+ - every treatment preserves named behavior and has an owner and verifier;
265
+ - every `treat-now` item has a smallest stable landing;
266
+ - every `observe` item has a reopen condition;
267
+ - intentionally unchanged areas are visible.
268
+
269
+ Return `needs-evidence` after triage when owner-skill consultations, a baseline
270
+ observer, or bounded repository evidence can settle the diagnosis. Return
271
+ `blocked` when required product, operational, access, or risk ownership cannot
272
+ be obtained. Return `not-applicable` when the request is a local diff review,
273
+ environment check, already-shaped single judgment, or implementation task better
274
+ owned directly by another skill.
275
+
276
+ ## Boundary
277
+
278
+ Do not implement treatment, automatically repair files, recommend a rewrite
279
+ because code is old, equate style or metrics with risk, invent product meaning,
280
+ promote every duplication into an abstraction, flatten sibling skills into a
281
+ checklist, load all references without triggers, or claim coverage beyond the
282
+ inspected scope. Doctor owns scope, lens coverage, consultation planning,
283
+ diagnostic synthesis, and treatment ordering; focused Developer skills own the
284
+ actual judgments and their routed references.
@@ -78,8 +78,12 @@ states, transitions, or policy decisions invalidate the model.
78
78
  5. Select one specialized model only when contract, relation, time, exhaustive
79
79
  evidence, or search has an independent artifact and stop condition.
80
80
  6. State the ability lost and guarantee gained by the chosen representation.
81
- 7. Place each guarantee at the appropriate type, boundary, validation, test,
82
- property, proof, model check, or human decision.
81
+ 7. Place each guarantee at the appropriate type, boundary, parser, constructor,
82
+ database constraint, test, property, proof, model check, or human decision.
83
+ A validation result that carries no refined value and an unchecked assertion,
84
+ cast, non-null claim, or typed deserialization target cannot own a
85
+ representable-state guarantee. When raw and admitted domains differ, require
86
+ an evidence-bearing transition without designing its API.
83
87
  8. Derive counterexamples and verification targets without designing the API.
84
88
 
85
89
  ## Missing Evidence
@@ -134,8 +134,10 @@ to remain aligned.
134
134
 
135
135
  For every important rule, name the cheapest trustworthy owner and evidence target:
136
136
 
137
- - type for representable-state restrictions;
138
- - constructor or runtime validation for hostile input;
137
+ - type or abstract representation for representable-state restrictions, but only
138
+ when every construction path establishes the restriction;
139
+ - parser or smart constructor for hostile or broader input, returning the
140
+ invariant-carrying value or explicit failure rather than discarding the check;
139
141
  - contract for caller/callee meaning;
140
142
  - database constraint for persisted relational facts;
141
143
  - state transition owner for history-sensitive rules;
@@ -144,7 +146,11 @@ For every important rule, name the cheapest trustworthy owner and evidence targe
144
146
  - model, solver, or proof for bounded exhaustive relationships;
145
147
  - human decision for policy.
146
148
 
147
- An unchecked statement may guide design, but it is not evidence.
149
+ An unchecked statement may guide design, but it is not evidence. Validation that
150
+ returns only success, `Unit`, or `Bool` cannot by itself place a guarantee at a
151
+ stronger type, and an assertion, cast, non-null claim, ignored conversion result,
152
+ or typed deserialization target does not repair that gap. Record the required
153
+ raw-to-refined transition and hand its concrete caller surface to `sketch`.
148
154
 
149
155
  ## Model Artifact
150
156
 
@@ -177,6 +183,10 @@ Separate rather than expanding this reference when:
177
183
 
178
184
  ## Source Trace
179
185
 
186
+ - Alexis King, “Parse, don’t validate,” published November 5, 2019, for the
187
+ distinction between checks that discard learned information and fallible
188
+ transitions that return a refined representation. Concrete parser and
189
+ smart-constructor surfaces remain owned by `sketch`.
180
190
  - Hillel Wayne, *Logic for Programmers*, v0.14.0, 2026-05-04:
181
191
  Chapters 2-4, pp. 5-46, for predicates, quantifiers, ability/guarantee,
182
192
  specifications, and logic/runtime boundaries. Recorded beta defects are
@@ -50,10 +50,13 @@ sketch must be inspectable as an implementation shape, not only narrated in
50
50
  paragraphs. Produce:
51
51
 
52
52
  1. a compact case/check table;
53
- 2. concrete data or state definitions;
53
+ 2. concrete data or state definitions, including raw and refined forms when an
54
+ input's provenance is broader than the accepted domain;
54
55
  3. wishful top-level code, pseudocode, or an interaction skeleton in a fenced
55
56
  code block;
56
57
  4. a wished-interface table with contract, owner, hidden detail, and stop check;
58
+ when raw and refined forms differ, show the parser or smart constructor whose
59
+ success returns the refined value and whose failure precedes dependent effects;
57
60
  5. a small ordered implementation queue and explicitly deferred abstractions;
58
61
  6. an ASCII flow, relation map, state transition, or boundary diagram whenever
59
62
  two or more components, states, or collaborations are materially related.
@@ -78,7 +81,10 @@ routing to the caller.
78
81
 
79
82
  Finish when the first implementation item is small enough to execute and check,
80
83
  and non-local or invariant-bearing candidates are explicit rather than silently
81
- assumed. `resolved` is not valid for a prose-only sketch: the output must show
84
+ assumed. If broader, external, persisted, legacy, or otherwise less-trusted data
85
+ enters the surface, `resolved` also requires an evidence-preserving transition to
86
+ the domain representation; validation followed by an assertion is not such a
87
+ transition. `resolved` is not valid for a prose-only sketch: the output must show
82
88
  the code or interaction skeleton and the checks that make the first item
83
89
  executable. Revisit when implementation evidence breaks the ownership or
84
90
  data-flow assumptions.
@@ -87,7 +93,9 @@ data-flow assumptions.
87
93
 
88
94
  1. Choose the strongest available source of intent and state its confidence.
89
95
  2. State the design unit's purpose in the user's language.
90
- 3. Derive relevant data or state definitions and their ownership pressure.
96
+ 3. Derive relevant data or state definitions and their ownership pressure. Mark
97
+ provenance and the raw-to-refined boundary whenever callers cannot already
98
+ supply an invariant-carrying value. Treat unchecked narrowing as no evidence.
91
99
  4. List representative cases before choosing code shape.
92
100
  5. Name each independently unresolved design question and select only its routed
93
101
  extension; keep disputed meaning in `model`.