@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.
- package/README.md +29 -2
- package/REFERENCE_ROUTING.md +10 -1
- package/extensions/compaction-language.ts +5 -3
- package/extensions/developer.ts +170 -7
- package/extensions/references/behavior-preserving-structural-change.md +10 -2
- package/extensions/skills.ts +6 -2
- package/extensions/state.ts +136 -34
- package/extensions/tui.ts +5 -0
- package/package.json +1 -1
- package/skills/doctor/SKILL.md +284 -0
- package/skills/model/SKILL.md +6 -2
- package/skills/model/references/problem-modeling.md +13 -3
- package/skills/sketch/SKILL.md +11 -3
- package/skills/sketch/reference-policy.json +13 -3
- package/skills/sketch/references/evidence-preserving-boundaries.md +200 -0
- package/skills/verify/SKILL.md +5 -2
- package/skills/verify/references/verifier-selection-and-pass-but-wrong.md +12 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Use it when a task may hide product rules, consequential cases, ownership,
|
|
|
7
7
|
compatibility, structural timing, or pass-but-wrong verification. Developer:
|
|
8
8
|
|
|
9
9
|
- turns consequential uncertainty into explicit questions;
|
|
10
|
-
- routes Pi to one of
|
|
10
|
+
- routes Pi to one of eleven focused judgment skills;
|
|
11
11
|
- loads deeper references only when an observable trigger calls for them;
|
|
12
12
|
- keeps implementation, unresolved questions, and verification evidence visible
|
|
13
13
|
on the current session branch.
|
|
@@ -70,6 +70,19 @@ The tests pass after this cache rewrite.
|
|
|
70
70
|
Check what they do not prove before calling it done.
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
For a scope-bound existing-code diagnosis and improvement plan, ask for Doctor
|
|
74
|
+
explicitly and name a path, flow, boundary, package, or subsystem when you can:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
Run a thorough Doctor review of the checkout request-to-persistence flow.
|
|
78
|
+
Preserve current external behavior, consult every triggered Developer judgment,
|
|
79
|
+
and produce a now/next/observe/leave-alone plan without modifying files.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
When no useful scope is supplied, Doctor performs a cheap orientation pass and
|
|
83
|
+
proposes one. It distinguishes requested, inspected, and claim scope rather than
|
|
84
|
+
presenting a repository sample as an exhaustive review.
|
|
85
|
+
|
|
73
86
|
## Why Developer
|
|
74
87
|
|
|
75
88
|
Ordinary coding requests often contain decisions that look like implementation
|
|
@@ -84,7 +97,10 @@ details but are actually product, model, boundary, or evidence questions.
|
|
|
84
97
|
| Branches lose rationale | Evidence replays with its branch |
|
|
85
98
|
|
|
86
99
|
Developer coordinates judgment; Pi still reads, edits, runs, and tests the
|
|
87
|
-
product with its normal tools.
|
|
100
|
+
product with its normal tools. Across those judgments, an unchecked assertion,
|
|
101
|
+
cast, non-null claim, or typed decode is never treated as evidence that a domain
|
|
102
|
+
invariant holds: broader input must cross an owned parser or smart-constructor
|
|
103
|
+
boundary whose success returns the refined value.
|
|
88
104
|
|
|
89
105
|
## How it works
|
|
90
106
|
|
|
@@ -115,9 +131,11 @@ and the plausible judgment routes add no useful information.
|
|
|
115
131
|
|
|
116
132
|
| Need | Developer helps Pi… |
|
|
117
133
|
| --- | --- |
|
|
134
|
+
| Diagnose existing code | Bound the review, preserve behavior, consult triggered judgments, and order justified improvements |
|
|
118
135
|
| Clarify a feature | Separate meaning, constraints, examples, and blockers |
|
|
119
136
|
| Model behavior | Enumerate cases, contracts, transitions, and guarantees |
|
|
120
137
|
| Shape code | Expose data, interfaces, collaboration, state, and checks |
|
|
138
|
+
| Refine input safely | Turn broader input into invariant-carrying domain values before dependent effects |
|
|
121
139
|
| Inspect a design | Identify structural movement and model-code mismatch |
|
|
122
140
|
| Review an abstraction | Keep, revise, split, reject, or defer the candidate |
|
|
123
141
|
| Time a refactor | Separate behavior work and judge the cost of delay |
|
|
@@ -133,6 +151,7 @@ directly with `/skill:<name>`.
|
|
|
133
151
|
|
|
134
152
|
| Skill | Helps decide |
|
|
135
153
|
| --- | --- |
|
|
154
|
+
| `doctor` | What a bounded existing-code scope must preserve, treat, observe, or leave alone |
|
|
136
155
|
| `specify` | Product meaning, scope, invariants, and blockers |
|
|
137
156
|
| `model` | Cases, contracts, transitions, guarantees, and checks |
|
|
138
157
|
| `sketch` | Data, interfaces, collaboration, flow, state, and code shape |
|
|
@@ -148,6 +167,14 @@ Each skill produces an artifact suited to its question instead of defaulting to
|
|
|
148
167
|
undifferentiated prose. Simple judgments remain compact when prose is genuinely
|
|
149
168
|
clearer.
|
|
150
169
|
|
|
170
|
+
Doctor is an explicit coordination workflow, not the default front door for
|
|
171
|
+
ordinary tasks. It first makes a broad, shallow disposition of every available
|
|
172
|
+
Developer judgment lens inside a declared scope. It then closes so each
|
|
173
|
+
triggered question can run through its owning skill and all reference-policy
|
|
174
|
+
routes supported by observable evidence. A final Doctor route synthesizes those
|
|
175
|
+
owner judgments into a treatment plan; Doctor never reads sibling references or
|
|
176
|
+
replaces their methods with one giant checklist.
|
|
177
|
+
|
|
151
178
|
## Commands
|
|
152
179
|
|
|
153
180
|
| Command | Effect |
|
package/REFERENCE_ROUTING.md
CHANGED
|
@@ -198,9 +198,10 @@ separation behavior, and pass-but-wrong cases.
|
|
|
198
198
|
|
|
199
199
|
| Skill | Policy routes | Shape |
|
|
200
200
|
| --- | --- | --- |
|
|
201
|
+
| `doctor` | none | scope, preservation baseline, universal lens disposition, consultation ledger, diagnosis, and treatment order; owner skills select and apply their own references |
|
|
201
202
|
| `abstraction-review` | `candidate-contract-review`, `failed-candidate-check`, `candidate-calibration` | review, failure localization, or promise-based calibration; no shadow construction |
|
|
202
203
|
| `model` | `condition-space`, `contract-replacement`, `relational-constraints`, `temporal-behavior`, `proof-obligation`, `solver-result-boundary`, `logic-query-semantics`, `planning-model` | one model artifact per uncertainty and stop condition |
|
|
203
|
-
| `sketch` | `data-driven-design`, `data-shape-template`, `composition-by-wishes`, `earned-abstraction`, `generative-recursion`, `accumulator-invariant`, `design-levels`, `representation-barrier`, `closure-interface`, `process-resources`, `state-history-order`, `generic-dispatch`, `meaning-preserving-conversion`, `language-semantics`, `runtime-compilation`, `responsibility-collaboration`, `variation-role`, `type-transition`, `selection-creation` | one implementable design question per route, with route-local common kernels only where required |
|
|
204
|
+
| `sketch` | `data-driven-design`, `data-shape-template`, `composition-by-wishes`, `earned-abstraction`, `generative-recursion`, `accumulator-invariant`, `evidence-preserving-boundary`, `design-levels`, `representation-barrier`, `closure-interface`, `process-resources`, `state-history-order`, `generic-dispatch`, `meaning-preserving-conversion`, `language-semantics`, `runtime-compilation`, `responsibility-collaboration`, `variation-role`, `type-transition`, `selection-creation` | one implementable design question per route, with route-local common kernels only where required |
|
|
204
205
|
| `signal` | `behavior-preserving-movement` | singleton |
|
|
205
206
|
| `schedule` | `structural-timing-tradeoff` | singleton |
|
|
206
207
|
| `naming-judgment` | `domain-sense-boundary` | singleton |
|
|
@@ -209,6 +210,14 @@ separation behavior, and pass-but-wrong cases.
|
|
|
209
210
|
| `visualize` | none | no packaged reference |
|
|
210
211
|
| `adversarial-eval` | none | no packaged reference |
|
|
211
212
|
|
|
213
|
+
Doctor does not aggregate sibling reference files into its own policy. In a
|
|
214
|
+
thorough Doctor review it dispositions every available skill, closes the Doctor
|
|
215
|
+
triage route, and hands each triggered question to its owner skill. The owner
|
|
216
|
+
route then applies this contract to every independently triggered policy route
|
|
217
|
+
and co-required reference set before Doctor synthesizes the recorded judgments.
|
|
218
|
+
This makes all references eligible without loading unrelated methods or losing
|
|
219
|
+
route-local provenance.
|
|
220
|
+
|
|
212
221
|
The implementation execution profile under `extensions/references/` is selected
|
|
213
222
|
by `execution_profile`, not by skill reference routing, and remains a separate
|
|
214
223
|
contract.
|
|
@@ -264,7 +264,7 @@ export function reconstructCompactionLanguage(
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
export function continuityMarkerText(tag: LanguageTag): string {
|
|
267
|
-
return `
|
|
267
|
+
return `Silent control: do not acknowledge or quote this marker; continue the current task. User-visible prose language=${tag}; explicit user language requests win.`;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
export function continuityContextMessage(
|
|
@@ -295,9 +295,11 @@ export function projectCompactionContinuity<T>(
|
|
|
295
295
|
state: CompactionLanguageState;
|
|
296
296
|
}
|
|
297
297
|
| undefined {
|
|
298
|
-
if (!state.pending) return undefined;
|
|
298
|
+
if (!state.pending || state.pending.injected) return undefined;
|
|
299
|
+
// Pi presents custom messages to the model as user messages. Prepend this
|
|
300
|
+
// one-shot control so the retained task remains the latest request.
|
|
299
301
|
return {
|
|
300
|
-
messages: [
|
|
302
|
+
messages: [continuityContextMessage(state.pending, timestamp), ...messages],
|
|
301
303
|
state: markContinuityInjected(state),
|
|
302
304
|
};
|
|
303
305
|
}
|
package/extensions/developer.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
applyDeveloperEvent,
|
|
54
54
|
canApplyDeveloperEvent,
|
|
55
55
|
developerSnapshot,
|
|
56
|
+
formatInvariantHandling,
|
|
56
57
|
initialState,
|
|
57
58
|
normalizeDeveloperEvent,
|
|
58
59
|
parseChoiceResponseSpec,
|
|
@@ -62,6 +63,7 @@ import {
|
|
|
62
63
|
type ChoiceResponseSpec,
|
|
63
64
|
type DeveloperState,
|
|
64
65
|
type ImplementationProfile,
|
|
66
|
+
type InvariantHandling,
|
|
65
67
|
type FocusEvent,
|
|
66
68
|
type JudgmentEvent,
|
|
67
69
|
type PendingQuestion,
|
|
@@ -215,6 +217,59 @@ interface ChoiceResponseSpecInput {
|
|
|
215
217
|
fields: ChoiceResponseFieldInput[];
|
|
216
218
|
}
|
|
217
219
|
|
|
220
|
+
type InvariantHandlingInput =
|
|
221
|
+
| {
|
|
222
|
+
kind: "not-applicable";
|
|
223
|
+
reason: string;
|
|
224
|
+
}
|
|
225
|
+
| {
|
|
226
|
+
kind: "evidence-preserving-boundary";
|
|
227
|
+
raw_representation: string;
|
|
228
|
+
refined_representation: string;
|
|
229
|
+
producer: string;
|
|
230
|
+
failure: string;
|
|
231
|
+
first_effect: string;
|
|
232
|
+
}
|
|
233
|
+
| {
|
|
234
|
+
kind: "trusted-compiler-gap";
|
|
235
|
+
assertion: string;
|
|
236
|
+
established_by: string;
|
|
237
|
+
limitation: string;
|
|
238
|
+
containment: string;
|
|
239
|
+
verification: string;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
function invariantHandlingFromInput(
|
|
243
|
+
input: InvariantHandlingInput | undefined,
|
|
244
|
+
): InvariantHandling {
|
|
245
|
+
if (!input) {
|
|
246
|
+
return {
|
|
247
|
+
kind: "not-applicable",
|
|
248
|
+
reason:
|
|
249
|
+
"Legacy direct tool execution omitted the now-required invariant-handling declaration.",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (input.kind === "not-applicable") return input;
|
|
253
|
+
if (input.kind === "evidence-preserving-boundary") {
|
|
254
|
+
return {
|
|
255
|
+
kind: input.kind,
|
|
256
|
+
rawRepresentation: input.raw_representation,
|
|
257
|
+
refinedRepresentation: input.refined_representation,
|
|
258
|
+
producer: input.producer,
|
|
259
|
+
failure: input.failure,
|
|
260
|
+
firstEffect: input.first_effect,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
kind: input.kind,
|
|
265
|
+
assertion: input.assertion,
|
|
266
|
+
establishedBy: input.established_by,
|
|
267
|
+
limitation: input.limitation,
|
|
268
|
+
containment: input.containment,
|
|
269
|
+
verification: input.verification,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
218
273
|
interface OpenQuestionInput {
|
|
219
274
|
question: string;
|
|
220
275
|
context?: string;
|
|
@@ -460,9 +515,14 @@ function protocolPrompt(
|
|
|
460
515
|
"Developer protocol is active.",
|
|
461
516
|
"- Use the default topology as a conditional backbone, not a rigid lifecycle: clarify meaning when needed -> model consequential cases -> sketch the first implementation surface for new behavior or signal the smallest structural movement in existing code -> execute one implementation step -> verify current claims.",
|
|
462
517
|
"- Adapt away from that topology when evidence makes a stage not applicable, but never jump directly from a resolved model to mutation: use sketch for feature implementation or signal for existing-code structural movement first.",
|
|
518
|
+
"- Route doctor only for an explicitly requested scope-bound diagnosis and improvement plan across several judgment kinds; it is not a mandatory prelude, pull-request review, environment check, or synonym for ordinary implementation.",
|
|
519
|
+
"- A thorough Doctor review is broad and shallow before it is narrow and deep: establish requested, inspected, and claim scope; disposition every other available Developer skill with evidence; close Doctor triage; route every triggered distinct consultation to its owning skill; let that owner select and apply every triggered policy route and co-required reference; then return to Doctor to synthesize diagnosis and treatment order. Doctor must not load sibling references or flatten their methods into one checklist.",
|
|
520
|
+
"- While Doctor consultations remain, keep one agent-owned before-completion synthesis question and update its remaining consultation evidence after each owner judgment; do not open one pending question per lens. Resolve it only in the final Doctor synthesis route.",
|
|
463
521
|
"- Route by the requested work product, not keywords: use sketch to create an original caller-facing interface, representation boundary, ownership map, or collaboration; use abstraction-review only when a concrete candidate surface already exists to keep/revise/split/reject/defer; use model first only when unresolved cases, rules, or policy can materially change that surface.",
|
|
464
522
|
`- Call ${ROUTE_TOOL} for exactly one concrete judgment or one green-to-green implementation movement.`,
|
|
465
|
-
"- Use target=implementation only when the next local movement, stable landing,
|
|
523
|
+
"- Use target=implementation only when the next local movement, stable landing, narrow verification, and invariant handling are already justified; otherwise choose the focused skill whose scope fits the current question.",
|
|
524
|
+
"- Invariant gate: an unchecked assertion, cast, non-null claim, ignored conversion result, or typed deserialization target is not evidence that broader, external, persisted, legacy, or otherwise less-trusted input satisfies a domain invariant. Parse or construct a refined value whose success carries the learned information before dependent effects; validation followed by narrowing does not satisfy this gate.",
|
|
525
|
+
"- Every implementation route must classify invariant handling as not applicable, an evidence-preserving boundary, or a trusted compiler gap. A trusted compiler gap is valid only after a complete check or trusted contract already established the fact, the language cannot retain it, the unsafe operation is isolated to one owner, and a falsifier is named. Encountering a missing boundary requires sketch, not an implementation assertion.",
|
|
466
526
|
"- An implementation step has one observable difference and one structural or behavioral purpose. Stop when its failure is locally explainable and the repository is green, pausable, and reviewable.",
|
|
467
527
|
"- For implementation structural work intended to preserve behavior, set execution_profile=behavior-preserving-structure; omit the field for other implementation actions and every skill route.",
|
|
468
528
|
`- Follow the routed method, then close the route with ${JUDGMENT_TOOL}. After every implementation stable landing, route again from the new evidence before selecting another movement.`,
|
|
@@ -511,6 +571,11 @@ function protocolPrompt(
|
|
|
511
571
|
lines.push(
|
|
512
572
|
`Active route: ${state.activeRoute.routeId} · ${state.activeRoute.target} · ${state.activeRoute.question}`,
|
|
513
573
|
);
|
|
574
|
+
if (state.activeRoute.implementationStep) {
|
|
575
|
+
lines.push(
|
|
576
|
+
`Active invariant handling: ${formatInvariantHandling(state.activeRoute.implementationStep.invariantHandling)}. Stop and route sketch instead of mutating if this declaration is contradicted by the code.`,
|
|
577
|
+
);
|
|
578
|
+
}
|
|
514
579
|
if (state.activeRoute.methodLocation) {
|
|
515
580
|
lines.push(
|
|
516
581
|
`Active skill location: ${state.activeRoute.methodLocation}. Read it again if compaction or a later turn no longer contains the full instructions.`,
|
|
@@ -825,6 +890,83 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
825
890
|
},
|
|
826
891
|
{ additionalProperties: false },
|
|
827
892
|
);
|
|
893
|
+
const InvariantHandlingParam = Type.Union([
|
|
894
|
+
Type.Object(
|
|
895
|
+
{
|
|
896
|
+
kind: Type.Literal("not-applicable"),
|
|
897
|
+
reason: Type.String({
|
|
898
|
+
minLength: 1,
|
|
899
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
900
|
+
description:
|
|
901
|
+
"Why this movement neither creates nor narrows broader or less-trusted data into an invariant-carrying value",
|
|
902
|
+
}),
|
|
903
|
+
},
|
|
904
|
+
{ additionalProperties: false },
|
|
905
|
+
),
|
|
906
|
+
Type.Object(
|
|
907
|
+
{
|
|
908
|
+
kind: Type.Literal("evidence-preserving-boundary"),
|
|
909
|
+
raw_representation: Type.String({
|
|
910
|
+
minLength: 1,
|
|
911
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
912
|
+
}),
|
|
913
|
+
refined_representation: Type.String({
|
|
914
|
+
minLength: 1,
|
|
915
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
916
|
+
}),
|
|
917
|
+
producer: Type.String({
|
|
918
|
+
minLength: 1,
|
|
919
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
920
|
+
description:
|
|
921
|
+
"Parser, refinement function, or smart constructor whose success returns the refined value",
|
|
922
|
+
}),
|
|
923
|
+
failure: Type.String({
|
|
924
|
+
minLength: 1,
|
|
925
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
926
|
+
}),
|
|
927
|
+
first_effect: Type.String({
|
|
928
|
+
minLength: 1,
|
|
929
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
930
|
+
description:
|
|
931
|
+
"First dependent domain effect allowed only after successful refinement",
|
|
932
|
+
}),
|
|
933
|
+
},
|
|
934
|
+
{ additionalProperties: false },
|
|
935
|
+
),
|
|
936
|
+
Type.Object(
|
|
937
|
+
{
|
|
938
|
+
kind: Type.Literal("trusted-compiler-gap"),
|
|
939
|
+
assertion: Type.String({
|
|
940
|
+
minLength: 1,
|
|
941
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
942
|
+
}),
|
|
943
|
+
established_by: Type.String({
|
|
944
|
+
minLength: 1,
|
|
945
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
946
|
+
description:
|
|
947
|
+
"Complete check or trusted runtime contract that already established the asserted fact",
|
|
948
|
+
}),
|
|
949
|
+
limitation: Type.String({
|
|
950
|
+
minLength: 1,
|
|
951
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
952
|
+
description:
|
|
953
|
+
"Why ordinary language narrowing cannot retain the established fact",
|
|
954
|
+
}),
|
|
955
|
+
containment: Type.String({
|
|
956
|
+
minLength: 1,
|
|
957
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
958
|
+
description: "Single parser or constructor owner containing the gap",
|
|
959
|
+
}),
|
|
960
|
+
verification: Type.String({
|
|
961
|
+
minLength: 1,
|
|
962
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
963
|
+
description:
|
|
964
|
+
"Falsifier for the claimed prior evidence and containment",
|
|
965
|
+
}),
|
|
966
|
+
},
|
|
967
|
+
{ additionalProperties: false },
|
|
968
|
+
),
|
|
969
|
+
]);
|
|
828
970
|
const RouteParams = Type.Union([
|
|
829
971
|
Type.Object(
|
|
830
972
|
{
|
|
@@ -867,6 +1009,7 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
867
1009
|
description:
|
|
868
1010
|
"The narrowest check that can catch the likely break in this movement",
|
|
869
1011
|
}),
|
|
1012
|
+
invariant_handling: InvariantHandlingParam,
|
|
870
1013
|
alternatives_considered: Type.Optional(
|
|
871
1014
|
Type.Array(RouteAlternativeParam, {
|
|
872
1015
|
maxItems: 6,
|
|
@@ -892,11 +1035,11 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
892
1035
|
name: ROUTE_TOOL,
|
|
893
1036
|
label: "Developer Route Question",
|
|
894
1037
|
description:
|
|
895
|
-
"Route one concrete judgment or one green-to-green implementation movement. Route by work product: sketch creates an original design surface, abstraction-review judges an already-shaped candidate, and model settles unresolved conditions before design. Then use
|
|
1038
|
+
"Route one concrete judgment or one green-to-green implementation movement. Route by work product: doctor coordinates an explicitly requested scope-bound existing-code diagnosis, sketch creates an original design surface, abstraction-review judges an already-shaped candidate, and model settles unresolved conditions before design. Implementation must declare how invariant-bearing values are constructed without unchecked narrowing. Then use stable landings and verify before completion.",
|
|
896
1039
|
promptSnippet: "Choose how to handle one development question",
|
|
897
1040
|
promptGuidelines: [
|
|
898
1041
|
`Call ${ROUTE_TOOL} only when there is no active Developer route.`,
|
|
899
|
-
`Use ${ROUTE_TOOL} with the most focused skill supported by current evidence; choose sketch for an original interface or boundary, abstraction-review only for an already-shaped candidate, and model only for unresolved condition space. target=implementation requires one movement, one stable landing,
|
|
1042
|
+
`Use ${ROUTE_TOOL} with the most focused skill supported by current evidence; choose doctor only for an explicit scope-bound existing-code diagnosis and improvement plan, choose sketch for an original interface or boundary, abstraction-review only for an already-shaped candidate, and model only for unresolved condition space. target=implementation requires one movement, one stable landing, one narrow verification, and a truthful invariant_handling declaration. Validation followed by an assertion is not an evidence-preserving boundary.`,
|
|
900
1043
|
`When ${ROUTE_TOOL} follows an implementation judgment with another implementation route, cite the previous landing in known_evidence and record plausible skill routes in alternatives_considered instead of selecting implementation by momentum.`,
|
|
901
1044
|
`After a resolved model, use sketch for first feature implementation framing or signal for existing-code structural movement before implementation mutation.`,
|
|
902
1045
|
],
|
|
@@ -1041,6 +1184,10 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1041
1184
|
"# Implementation action",
|
|
1042
1185
|
"",
|
|
1043
1186
|
"The next local action is already justified. Keep this route open while using Pi implementation tools and collecting evidence.",
|
|
1187
|
+
"",
|
|
1188
|
+
"## Invariant gate",
|
|
1189
|
+
"",
|
|
1190
|
+
"Follow the route's invariant-handling declaration. Broader or less-trusted input must become an invariant-carrying value through a parser, refinement function, or smart constructor before dependent effects. Validation followed by an assertion, cast, non-null claim, ignored conversion result, or typed decode is not evidence. If the declared handling is incomplete, stop without mutation and route sketch. Keep any trusted compiler gap isolated to its declared owner and verify its prior evidence and containment.",
|
|
1044
1191
|
].join("\n");
|
|
1045
1192
|
}
|
|
1046
1193
|
|
|
@@ -1059,6 +1206,11 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1059
1206
|
? params.verification
|
|
1060
1207
|
: "Run the narrowest relevant check and inspect the resulting diff or output."
|
|
1061
1208
|
).trim(),
|
|
1209
|
+
invariantHandling: invariantHandlingFromInput(
|
|
1210
|
+
"invariant_handling" in params
|
|
1211
|
+
? params.invariant_handling
|
|
1212
|
+
: undefined,
|
|
1213
|
+
),
|
|
1062
1214
|
}
|
|
1063
1215
|
: undefined;
|
|
1064
1216
|
|
|
@@ -1096,6 +1248,7 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1096
1248
|
`Movement: ${implementationStep.movement}`,
|
|
1097
1249
|
`Stable landing: ${implementationStep.stopCondition}`,
|
|
1098
1250
|
`Narrow verification: ${implementationStep.verification}`,
|
|
1251
|
+
`Invariant handling: ${formatInvariantHandling(implementationStep.invariantHandling)}`,
|
|
1099
1252
|
"Stop this implementation route at that landing, record the evidence, and route again before another movement.",
|
|
1100
1253
|
]
|
|
1101
1254
|
: []),
|
|
@@ -1111,6 +1264,11 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1111
1264
|
`Reference contract: choose policy routes by observable trigger, use ${REFERENCE_TOOL} for every reference in each selected route's co-required set, and provide reference_basis for every loaded path; use reference_exemption only when no policy route applies.`,
|
|
1112
1265
|
]
|
|
1113
1266
|
: []),
|
|
1267
|
+
...(skill && event.availableReferences.length === 0
|
|
1268
|
+
? [
|
|
1269
|
+
"Reference contract: this skill has no packaged references; omit both reference_basis and reference_exemption from its judgment.",
|
|
1270
|
+
]
|
|
1271
|
+
: []),
|
|
1114
1272
|
`Known evidence: ${event.knownEvidence.length > 0 ? event.knownEvidence.join(" | ") : "none"}`,
|
|
1115
1273
|
`Alternatives reconsidered: ${
|
|
1116
1274
|
event.consideredAlternatives.length > 0
|
|
@@ -1173,7 +1331,8 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1173
1331
|
context.lastComponent,
|
|
1174
1332
|
);
|
|
1175
1333
|
}
|
|
1176
|
-
const
|
|
1334
|
+
const normalized = normalizeDeveloperEvent(result.details);
|
|
1335
|
+
const event = normalized?.kind === "route" ? normalized : undefined;
|
|
1177
1336
|
let text = theme.fg("success", `routed ${routeRenderText(event)}`);
|
|
1178
1337
|
if (expanded && event) {
|
|
1179
1338
|
text += `\n${detailLine(theme, "route", event.routeId, "text")}`;
|
|
@@ -1207,6 +1366,7 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1207
1366
|
text += `\n${detailLine(theme, "movement", event.implementationStep.movement, "text")}`;
|
|
1208
1367
|
text += `\n${detailLine(theme, "landing", event.implementationStep.stopCondition)}`;
|
|
1209
1368
|
text += `\n${detailLine(theme, "verify", event.implementationStep.verification)}`;
|
|
1369
|
+
text += `\n${detailLine(theme, "invariant", formatInvariantHandling(event.implementationStep.invariantHandling))}`;
|
|
1210
1370
|
}
|
|
1211
1371
|
}
|
|
1212
1372
|
if (!expanded && event)
|
|
@@ -1382,7 +1542,9 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1382
1542
|
context.lastComponent,
|
|
1383
1543
|
);
|
|
1384
1544
|
}
|
|
1385
|
-
const
|
|
1545
|
+
const normalized = normalizeDeveloperEvent(result.details);
|
|
1546
|
+
const event =
|
|
1547
|
+
normalized?.kind === "reference-load" ? normalized : undefined;
|
|
1386
1548
|
let text = theme.fg(
|
|
1387
1549
|
"success",
|
|
1388
1550
|
`loaded ${event?.path ?? "Developer reference"}`,
|
|
@@ -1619,7 +1781,7 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1619
1781
|
promptGuidelines: [
|
|
1620
1782
|
`Use ${JUDGMENT_TOOL} with the exact active Developer route ID.`,
|
|
1621
1783
|
`Use ${JUDGMENT_TOOL} result as Markdown and preserve the routed skill's inspectable tables, diagrams, matrices, timelines, and code blocks instead of reducing them to prose.`,
|
|
1622
|
-
`For a resolved skill route with available references, use ${JUDGMENT_TOOL} reference_basis to connect each relied-on ${REFERENCE_TOOL} load to its trigger, applied rule, and resulting artifact, or use reference_exemption when no reference is relevant. Never provide both.`,
|
|
1784
|
+
`For a resolved skill route with available references, use ${JUDGMENT_TOOL} reference_basis to connect each relied-on ${REFERENCE_TOOL} load to its trigger, applied rule, and resulting artifact, or use reference_exemption when no reference is relevant. Never provide both. For a skill with no packaged references, omit both fields.`,
|
|
1623
1785
|
`Use ${JUDGMENT_TOOL} open_questions with resolution_owner, gate, and resolution_criteria. User-owned gated questions require explanatory context before controls; for finite required decisions, add a choice-form response_spec with one field per decision. Use question_updates whenever current evidence settles or changes any existing pending question.`,
|
|
1624
1786
|
`Do not use ${JUDGMENT_TOOL} with resolved, not-applicable, or blocked status without at least one concrete basis.`,
|
|
1625
1787
|
],
|
|
@@ -2011,7 +2173,8 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
2011
2173
|
context.lastComponent,
|
|
2012
2174
|
);
|
|
2013
2175
|
}
|
|
2014
|
-
const
|
|
2176
|
+
const normalized = normalizeDeveloperEvent(result.details);
|
|
2177
|
+
const event = normalized?.kind === "judgment" ? normalized : undefined;
|
|
2015
2178
|
const summary = judgmentRenderText(event);
|
|
2016
2179
|
let text =
|
|
2017
2180
|
event?.status === "resolved"
|
|
@@ -46,7 +46,10 @@ For movement across a boundary, update one sender or caller at a time when the
|
|
|
46
46
|
old and new forms can safely coexist. Confirm that each intermediate state is
|
|
47
47
|
valid. Do not rely on a final large diff to explain which step changed behavior.
|
|
48
48
|
A forwarding interface over the old implementation can be a temporary seam, but
|
|
49
|
-
it is not evidence that the interface is a stable public abstraction.
|
|
49
|
+
it is not evidence that the interface is a stable public abstraction. An
|
|
50
|
+
assertion, cast, non-null claim, or unchecked typed decode is not a compatibility
|
|
51
|
+
seam: if old or external data is broader than the new invariant, an adapter must
|
|
52
|
+
parse and return the new representation or explicit failure.
|
|
50
53
|
|
|
51
54
|
For suspected dead code, combine static references, dynamic reachability, and a
|
|
52
55
|
scoped observation window appropriate to rare jobs, reflection, configuration,
|
|
@@ -192,10 +195,15 @@ The protocol is being misused when:
|
|
|
192
195
|
- behavior work is hidden inside a structural label;
|
|
193
196
|
- the route continues after a new human-owned policy question appears;
|
|
194
197
|
- an intermediate state cannot safely run or be reviewed;
|
|
195
|
-
- cleanup continues past the accepted pressure because the code could be nicer
|
|
198
|
+
- cleanup continues past the accepted pressure because the code could be nicer;
|
|
199
|
+
- validation is preserved while its result is discarded and an assertion claims
|
|
200
|
+
the new representation.
|
|
196
201
|
|
|
197
202
|
## Source Trace
|
|
198
203
|
|
|
204
|
+
- Alexis King, “Parse, don’t validate,” published November 5, 2019, for
|
|
205
|
+
evidence-preserving refinement before execution. The compatibility-seam and
|
|
206
|
+
migration wording is a Developer adaptation.
|
|
199
207
|
- Sandi Metz, Katrina Owen, and TJ Stankus, *99 Bottles of OOP*, Second
|
|
200
208
|
Edition, version 2.2.2, 2024: Chapters 3-5, pp. 51-137, especially
|
|
201
209
|
pp. 58-71, 84-101, and 113-125, on methodical transformations, flocking,
|
package/extensions/skills.ts
CHANGED
|
@@ -86,13 +86,17 @@ export function availablePackageSkills(
|
|
|
86
86
|
return available;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function hasErrorCode(error: unknown, code: string): boolean {
|
|
90
|
+
return isRecord(error) && error.code === code;
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
export async function skillReferencePaths(skill: Skill): Promise<string[]> {
|
|
90
94
|
const referencesRoot = resolve(skill.baseDir, "references");
|
|
91
95
|
let entries: Dirent[];
|
|
92
96
|
try {
|
|
93
97
|
entries = await readdir(referencesRoot, { withFileTypes: true });
|
|
94
98
|
} catch (error) {
|
|
95
|
-
if ((error
|
|
99
|
+
if (hasErrorCode(error, "ENOENT")) return [];
|
|
96
100
|
throw error;
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -248,7 +252,7 @@ export async function loadSkillReferencePolicy(
|
|
|
248
252
|
try {
|
|
249
253
|
source = await readFile(policyPath, "utf8");
|
|
250
254
|
} catch (error) {
|
|
251
|
-
if ((error
|
|
255
|
+
if (hasErrorCode(error, "ENOENT")) {
|
|
252
256
|
if (catalog.length > 0) {
|
|
253
257
|
throw new Error(
|
|
254
258
|
`Developer skill ${skill.name} has references but no reference-policy.json.`,
|