@davidorex/pi-context 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +12 -11
- package/dist/block-api.d.ts.map +1 -1
- package/dist/block-api.js +27 -2
- package/dist/block-api.js.map +1 -1
- package/dist/context-sdk.d.ts +71 -33
- package/dist/context-sdk.d.ts.map +1 -1
- package/dist/context-sdk.js +547 -149
- package/dist/context-sdk.js.map +1 -1
- package/dist/context.d.ts +153 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +75 -5
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +71 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +405 -94
- package/dist/index.js.map +1 -1
- package/dist/lens-view.d.ts +0 -5
- package/dist/lens-view.d.ts.map +1 -1
- package/dist/lens-view.js +43 -1
- package/dist/lens-view.js.map +1 -1
- package/dist/migration-registry-loader.d.ts +20 -12
- package/dist/migration-registry-loader.d.ts.map +1 -1
- package/dist/migration-registry-loader.js +46 -17
- package/dist/migration-registry-loader.js.map +1 -1
- package/dist/migrations-store.d.ts +45 -18
- package/dist/migrations-store.d.ts.map +1 -1
- package/dist/migrations-store.js +56 -22
- package/dist/migrations-store.js.map +1 -1
- package/dist/ops-registry.d.ts.map +1 -1
- package/dist/ops-registry.js +91 -118
- package/dist/ops-registry.js.map +1 -1
- package/dist/promote-item.d.ts.map +1 -1
- package/dist/promote-item.js +41 -12
- package/dist/promote-item.js.map +1 -1
- package/dist/roadmap-plan.d.ts +121 -99
- package/dist/roadmap-plan.d.ts.map +1 -1
- package/dist/roadmap-plan.js +281 -345
- package/dist/roadmap-plan.js.map +1 -1
- package/dist/status-vocab.d.ts +12 -2
- package/dist/status-vocab.d.ts.map +1 -1
- package/dist/status-vocab.js +14 -1
- package/dist/status-vocab.js.map +1 -1
- package/package.json +1 -1
- package/samples/blocks/milestone.json +3 -0
- package/samples/blocks/session-notes.json +1 -0
- package/samples/conception.json +308 -15
- package/samples/migrations.json +8 -0
- package/samples/schemas/context-contracts.schema.json +4 -0
- package/samples/schemas/conventions.schema.json +4 -0
- package/samples/schemas/decisions.schema.json +4 -0
- package/samples/schemas/features.schema.json +4 -0
- package/samples/schemas/framework-gaps.schema.json +4 -0
- package/samples/schemas/issues.schema.json +28 -0
- package/samples/schemas/layer-plans.schema.json +4 -0
- package/samples/schemas/milestone.schema.json +79 -0
- package/samples/schemas/phase.schema.json +4 -0
- package/samples/schemas/rationale.schema.json +4 -0
- package/samples/schemas/requirements.schema.json +4 -0
- package/samples/schemas/research.schema.json +4 -0
- package/samples/schemas/session-notes.schema.json +89 -0
- package/samples/schemas/spec-reviews.schema.json +4 -0
- package/samples/schemas/story.schema.json +8 -0
- package/samples/schemas/tasks.schema.json +4 -0
- package/samples/schemas/verification.schema.json +4 -0
- package/samples/schemas/work-orders.schema.json +4 -0
- package/schemas/config.schema.json +77 -3
- package/schemas/migrations.schema.json +25 -0
- package/skill-narrative.md +7 -5
- package/skills/pi-context/SKILL.md +44 -49
- package/skills/pi-context/references/bundled-resources.md +5 -1
package/dist/ops-registry.js
CHANGED
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import { Type } from "typebox";
|
|
21
21
|
import { appendToBlock, appendToNestedArray, nextId, readBlock, readBlockDir, removeFromBlock, removeFromNestedArray, updateItemInBlock, updateNestedArrayItem, upsertItemInBlock, writeBlock, } from "./block-api.js";
|
|
22
|
-
import { adoptConception, amendConfigEntry, loadConfig
|
|
22
|
+
import { adoptConception, amendConfigEntry, loadConfig } from "./context.js";
|
|
23
23
|
import { BootstrapNotFoundError, schemaPath, tryResolveContextDir } from "./context-dir.js";
|
|
24
|
-
import { appendRelationByRef, appendRelationsByRef, completeTask, contextState, currentState, deriveBootstrapState, filterBlockItems, joinBlocks, readBlockItem, readBlockPage, removeRelationByRef, replaceRelationByRef, resolveItemById, resolveItemsByIds, validateContext, } from "./context-sdk.js";
|
|
24
|
+
import { appendRelationByRef, appendRelationsByRef, completeTask, contextState, currentState, deriveBootstrapState, endpointKey, filterBlockItems, joinBlocks, readBlockItem, readBlockPage, removeRelationByRef, replaceRelationByRef, resolveItemById, resolveItemsByIds, validateContext, } from "./context-sdk.js";
|
|
25
25
|
import { gatherExecutionContext } from "./execution-context.js";
|
|
26
26
|
// initProject + the switch/list/archive helpers are defined in index.ts (shared
|
|
27
27
|
// with the /context command handlers + the context-* tools). This is a cyclic
|
|
@@ -34,7 +34,7 @@ import { edgesForLensByName, findReferencesInRepo, loadLensView, validateContext
|
|
|
34
34
|
import { promoteItem } from "./promote-item.js";
|
|
35
35
|
import { addressInto, pageArray, renderReadText, structureForRead } from "./read-element.js";
|
|
36
36
|
import { renameCanonicalId } from "./rename-canonical-id.js";
|
|
37
|
-
import {
|
|
37
|
+
import { loadRoadmap, renderRoadmap, validateRoadmap } from "./roadmap-plan.js";
|
|
38
38
|
import { samplesCatalog } from "./samples-catalog.js";
|
|
39
39
|
import { readSchema, writeSchemaChecked } from "./schema-write.js";
|
|
40
40
|
import { truncateHead } from "./truncate.js";
|
|
@@ -115,31 +115,6 @@ export function boundedJsonOutput(r) {
|
|
|
115
115
|
const { over, totalBytes } = overReadCap(s);
|
|
116
116
|
return over ? { data: null, truncated: true, totalBytes, complete: false } : r.json;
|
|
117
117
|
}
|
|
118
|
-
// ── serializeRoadmapView ────────────────────────────────────────────────────
|
|
119
|
-
// Strip non-serializable fields (suggestionTemplate fn, grouped Map) from the
|
|
120
|
-
// embedded LoadedLensView records before tool serialization. Relocated verbatim
|
|
121
|
-
// from the extension factory; consumed by the context-roadmap-load op.
|
|
122
|
-
const serializeRoadmapView = (view) => ({
|
|
123
|
-
roadmap: view.roadmap,
|
|
124
|
-
phases: view.phases.map((pv) => ({
|
|
125
|
-
phase: pv.phase,
|
|
126
|
-
lensView: "error" in pv.lensView
|
|
127
|
-
? pv.lensView
|
|
128
|
-
: {
|
|
129
|
-
lens: pv.lensView.lens,
|
|
130
|
-
items: pv.lensView.items,
|
|
131
|
-
edges: pv.lensView.edges,
|
|
132
|
-
grouped: Object.fromEntries(pv.lensView.grouped),
|
|
133
|
-
uncategorized: pv.lensView.uncategorized,
|
|
134
|
-
},
|
|
135
|
-
status: pv.status,
|
|
136
|
-
...(pv.milestone ? { milestone: pv.milestone } : {}),
|
|
137
|
-
...(pv.milestoneSatisfied !== undefined ? { milestoneSatisfied: pv.milestoneSatisfied } : {}),
|
|
138
|
-
})),
|
|
139
|
-
phaseOrder: view.phaseOrder,
|
|
140
|
-
cycles: view.cycles,
|
|
141
|
-
edges: view.edges,
|
|
142
|
-
});
|
|
143
118
|
export const ops = [
|
|
144
119
|
{
|
|
145
120
|
name: "append-block-item",
|
|
@@ -211,17 +186,30 @@ export const ops = [
|
|
|
211
186
|
{
|
|
212
187
|
name: "append-relation",
|
|
213
188
|
label: "Append Relation",
|
|
214
|
-
description: "Append a closure-table relation (edge:
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
|
|
189
|
+
description: "Append a closure-table relation (edge: relation_type, optional ordinal) to relations.json. Orient the edge with " +
|
|
190
|
+
"EITHER raw --parent/--child OR the role-typed --primary/--counter (which maps to parent/child via the relation's " +
|
|
191
|
+
"declared role_direction); the two pairs are mutually exclusive. A bare --parent/--child append of a relation that " +
|
|
192
|
+
"is BOTH role-bearing and orientation-ambiguous (its source/target kinds overlap) is rejected — re-issue with " +
|
|
193
|
+
"--primary/--counter. Shape is AJV-validated; an exact-duplicate edge (same parent+child+relation_type) is a no-op. " +
|
|
194
|
+
"Reference integrity (endpoints resolve, relation_type registered, no cycle) is NOT checked here — run " +
|
|
195
|
+
"context-validate after. Creates relations.json if absent.",
|
|
196
|
+
promptSnippet: "Create a relation/edge between two items (raw --parent/--child, or role-typed --primary/--counter mapped via role_direction)",
|
|
219
197
|
examples: [
|
|
220
198
|
`pi-context append-relation --parent VER-001 --child TASK-001 --relation_type verification_verifies_item --writer '{"kind":"human","user":"you@example.com"}' --json`,
|
|
221
199
|
],
|
|
222
200
|
parameters: Type.Object({
|
|
223
|
-
parent: Type.String({
|
|
224
|
-
|
|
201
|
+
parent: Type.Optional(Type.String({
|
|
202
|
+
description: "Parent-endpoint selector (canonical id / <alias>:<refname> / lens bin) — RAW orientation. Mutually exclusive with --primary/--counter.",
|
|
203
|
+
})),
|
|
204
|
+
child: Type.Optional(Type.String({
|
|
205
|
+
description: "Child-endpoint selector — RAW orientation. Mutually exclusive with --primary/--counter.",
|
|
206
|
+
})),
|
|
207
|
+
primary: Type.Optional(Type.String({
|
|
208
|
+
description: "Selector of the endpoint holding the relation's PRIMARY semantic role (ROLE-TYPED orientation; mapped to parent/child via the relation's declared role_direction). Requires --counter; the relation_type must declare role_direction.",
|
|
209
|
+
})),
|
|
210
|
+
counter: Type.Optional(Type.String({
|
|
211
|
+
description: "Selector of the endpoint holding the relation's COUNTER role (ROLE-TYPED orientation). Requires --primary.",
|
|
212
|
+
})),
|
|
225
213
|
relation_type: Type.String({
|
|
226
214
|
description: "Registered relation_type canonical_id / hierarchy edge type / lens id",
|
|
227
215
|
}),
|
|
@@ -231,26 +219,32 @@ export const ops = [
|
|
|
231
219
|
surface: "use",
|
|
232
220
|
run(cwd, params, ctx) {
|
|
233
221
|
// Cycle-5 porcelain: STRING selectors (bare refname / <alias>:<refname> /
|
|
234
|
-
// lens-bin) are resolved to structured EdgeEndpoints and written via the
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
222
|
+
// lens-bin) are resolved to structured EdgeEndpoints and written via the raw
|
|
223
|
+
// plumbing. The append accepts EITHER raw --parent/--child OR the role-typed
|
|
224
|
+
// --primary/--counter form (FGAP-113); messaging renders the RESOLVED stored
|
|
225
|
+
// orientation (endpointKey of the returned edge), so a role-typed call reports
|
|
226
|
+
// the parent/child it actually filed. Under dryRun the byRef fn validates the
|
|
227
|
+
// prospective relations + dedup-checks without writing (TASK-010 shared
|
|
228
|
+
// preview path).
|
|
229
|
+
const { appended, edge } = appendRelationByRef(cwd, {
|
|
230
|
+
...(params.parent !== undefined ? { parent: params.parent } : {}),
|
|
231
|
+
...(params.child !== undefined ? { child: params.child } : {}),
|
|
232
|
+
...(params.primary !== undefined ? { primary: params.primary } : {}),
|
|
233
|
+
...(params.counter !== undefined ? { counter: params.counter } : {}),
|
|
242
234
|
relation_type: params.relation_type,
|
|
243
235
|
...(params.ordinal !== undefined ? { ordinal: params.ordinal } : {}),
|
|
244
236
|
}, ctx, { dryRun: params.dryRun });
|
|
237
|
+
const from = endpointKey(edge.parent);
|
|
238
|
+
const to = endpointKey(edge.child);
|
|
245
239
|
const ordinalNote = params.ordinal !== undefined ? ` (ordinal ${params.ordinal})` : "";
|
|
246
240
|
if (params.dryRun) {
|
|
247
241
|
return appended
|
|
248
|
-
? `would append relation ${
|
|
249
|
-
: `would no-op (duplicate): relation ${
|
|
242
|
+
? `would append relation ${from} -[${params.relation_type}]-> ${to}${ordinalNote}`
|
|
243
|
+
: `would no-op (duplicate): relation ${from} -[${params.relation_type}]-> ${to}`;
|
|
250
244
|
}
|
|
251
245
|
return appended
|
|
252
|
-
? `Appended relation ${
|
|
253
|
-
: `Relation ${
|
|
246
|
+
? `Appended relation ${from} -[${params.relation_type}]-> ${to}${ordinalNote}`
|
|
247
|
+
: `Relation ${from} -[${params.relation_type}]-> ${to} already exists — no-op`;
|
|
254
248
|
},
|
|
255
249
|
},
|
|
256
250
|
{
|
|
@@ -296,7 +290,9 @@ export const ops = [
|
|
|
296
290
|
description: "Atomically replace one closure-table relation with another in a SINGLE write (no half-state: the old edge and " +
|
|
297
291
|
"the new edge never coexist on disk). The old edge is matched on the (parent, child, relation_type) dedup identity; " +
|
|
298
292
|
"the new edge is written with its optional ordinal. If the old edge is absent the call is effectively an append of " +
|
|
299
|
-
"the new edge.
|
|
293
|
+
"the new edge. This op takes RAW parent/child (old + new) and BYPASSES the write-time orientation gate that " +
|
|
294
|
+
"append-relation applies — it writes the endpoints verbatim, so it is the affordance for re-orienting an existing " +
|
|
295
|
+
"edge; reference integrity is NOT checked here — run context-validate after.",
|
|
300
296
|
promptSnippet: "Atomically swap one relation/edge for another in a single write",
|
|
301
297
|
examples: [
|
|
302
298
|
`pi-context replace-relation --old_parent TASK-001 --old_child DEC-0001 --old_relation_type task_informed_by_decision --parent TASK-001 --child DEC-0002 --relation_type task_informed_by_decision --writer '{"kind":"human","user":"you@example.com"}' --json`,
|
|
@@ -355,17 +351,20 @@ export const ops = [
|
|
|
355
351
|
{
|
|
356
352
|
name: "append-relations",
|
|
357
353
|
label: "Append Relations (bulk)",
|
|
358
|
-
description: "Append MANY closure-table relations to relations.json in a single write. Each edge is an object " +
|
|
359
|
-
"{
|
|
360
|
-
"
|
|
361
|
-
"
|
|
362
|
-
|
|
354
|
+
description: "Append MANY closure-table relations to relations.json in a single write. Each edge is an object with " +
|
|
355
|
+
"{ relation_type, ordinal? } plus EITHER a raw { parent, child } pair OR the role-typed { primary, counter } pair " +
|
|
356
|
+
"(mapped to parent/child via the relation's declared role_direction); the two pairs are mutually exclusive per edge, " +
|
|
357
|
+
"and a bare { parent, child } for an orientation-ambiguous role-bearing relation rejects the whole batch before any " +
|
|
358
|
+
"write. Per-(parent, child, relation_type) duplicates are skipped (against on-disk edges AND earlier edges in the " +
|
|
359
|
+
"same batch). Returns appended/skipped counts. Reference integrity is NOT checked here — run context-validate " +
|
|
360
|
+
"after. Creates relations.json if absent.",
|
|
361
|
+
promptSnippet: "Create many relations/edges between items in one write (raw or role-typed per edge)",
|
|
363
362
|
examples: [
|
|
364
363
|
`pi-context append-relations --edges '[{"parent":"FEAT-008","child":"TASK-042","relation_type":"feature_decomposed_into_task"}]' --writer '{"kind":"human","user":"you@example.com"}' --json`,
|
|
365
364
|
],
|
|
366
365
|
parameters: Type.Object({
|
|
367
366
|
edges: Type.Unknown({
|
|
368
|
-
description: "JSON array of { parent, child
|
|
367
|
+
description: "JSON array of edge objects. Each edge is { relation_type, ordinal? } plus EITHER a raw { parent, child } pair OR the role-typed { primary, counter } pair (mapped to parent/child via the relation's declared role_direction); the two orientation pairs are mutually exclusive per edge. Selectors are id / <alias>:<refname> / lens-bin.",
|
|
369
368
|
}),
|
|
370
369
|
dryRun: Type.Optional(Type.Boolean({ description: "Preview without writing relations.json" })),
|
|
371
370
|
}),
|
|
@@ -382,12 +381,16 @@ export const ops = [
|
|
|
382
381
|
}
|
|
383
382
|
}
|
|
384
383
|
if (!Array.isArray(edges)) {
|
|
385
|
-
throw new Error(`edges parameter must be a JSON array of
|
|
384
|
+
throw new Error(`edges parameter must be a JSON array of relation edge objects`);
|
|
386
385
|
}
|
|
387
386
|
// Under dryRun the byRef fn replays the on-disk + in-batch dedup and
|
|
388
387
|
// validates the prospective relations without writing (TASK-010 shared
|
|
389
|
-
// preview path).
|
|
390
|
-
|
|
388
|
+
// preview path). Each edge accepts raw {parent,child} or role-typed
|
|
389
|
+
// {primary,counter} (FGAP-113); orientation + the ambiguous-bare-append
|
|
390
|
+
// reject are applied inside appendRelationsByRef before any write.
|
|
391
|
+
const { appended, skipped } = appendRelationsByRef(cwd, edges, ctx, {
|
|
392
|
+
dryRun: params.dryRun,
|
|
393
|
+
});
|
|
391
394
|
return params.dryRun
|
|
392
395
|
? `would append ${appended}, skip ${skipped} (duplicates)`
|
|
393
396
|
: `appended ${appended}, skipped ${skipped} (duplicates)`;
|
|
@@ -680,7 +683,7 @@ export const ops = [
|
|
|
680
683
|
{
|
|
681
684
|
name: "context-check-status",
|
|
682
685
|
label: "Context Check Status",
|
|
683
|
-
description: "Read-only installed-vs-catalog schema drift report — per installed schema the drift state, the baseline and catalog versions, and for behind schemas (catalog-ahead / both-diverged) the version delta (baseline -> catalog) or the content-only basis when the version string is unchanged. The front of the check-status -> update --dryRun -> update sequence; writes nothing.",
|
|
686
|
+
description: "Read-only installed-vs-catalog schema drift report — per installed schema the drift state, the baseline and catalog versions, and for behind schemas (catalog-ahead / both-diverged) the version delta (baseline -> catalog) or the content-only basis when the version string is unchanged. The front of the check-status -> update --dryRun -> update sequence. Like every substrate-lifecycle ceremony it seeds the catalog's config migration declarations into migrations.json (idempotent) before its first config read, so a version-lagging legacy substrate is diagnosable; beyond that seed it writes nothing.",
|
|
684
687
|
promptSnippet: "Report installed-vs-catalog schema drift + the version gap for behind schemas (read-only)",
|
|
685
688
|
examples: [`pi-context context-check-status --json`],
|
|
686
689
|
parameters: Type.Object({}),
|
|
@@ -868,8 +871,8 @@ export const ops = [
|
|
|
868
871
|
{
|
|
869
872
|
name: "context-current-state",
|
|
870
873
|
label: "Context Current State",
|
|
871
|
-
description: "Derive 'where are we + what's next' purely from the substrate — focus, in-flight
|
|
872
|
-
promptSnippet: "Derive current project state — focus, in-flight, next actions, blocked",
|
|
874
|
+
description: "Derive 'where are we + what's next' purely from the substrate — focus, in-flight items, ranked atomic-next actions, blocked items, and milestone rollups. Every facet derives from the config-declared `state_derivation` registry: which block kinds + status bucket count as in-flight, the focus fallback kind + bucket, the ordered cross-kind next-actions push order with per-entry ranking (a named field + ordered value list, e.g. gap priority P0..P3) or topo ordering over the blocking-relation graph, the relation_types whose edges contribute blockers (the stock set being `task_depends_on_task` dependencies + `task_gated_by_item` gates), the membership rollups (e.g. milestones over `phase_positioned_in_milestone`) with their complete/incomplete status strings, and the next-actions head-size cap. A blocked item's dependency/gate target that has not reached the complete bucket is reported in blockedBy and held out of nextActions; a target reaching its complete status releases it. A substrate whose config declares no `state_derivation` reports focus 'state-derivation not configured' with empty arrays. No writes; nothing hand-stored.",
|
|
875
|
+
promptSnippet: "Derive current project state from the config-declared state_derivation registry — focus, in-flight, ranked next actions, blocked, milestone rollups",
|
|
873
876
|
examples: [`pi-context context-current-state --json`],
|
|
874
877
|
parameters: Type.Object({}),
|
|
875
878
|
surface: "use",
|
|
@@ -1062,7 +1065,7 @@ export const ops = [
|
|
|
1062
1065
|
{
|
|
1063
1066
|
name: "resolve-blocked",
|
|
1064
1067
|
label: "Resolve Blocked",
|
|
1065
|
-
description: "Commit the resolution of a blocked schema surfaced by update. Run AFTER fixing the block's items (or widening the local schema): when the block file carries git-style failure markers (written by update), strips the full-line marker sentinels first, then re-validates the corrected block against the pinned target schema from the pending-blocked record; on pass registers the migration chain, writes the target schema, advances the merge base to the target (so a subsequent update converges instead of re-blocking), and clears the pending entry; on fail reports the remaining per-item failures and writes nothing.",
|
|
1068
|
+
description: "Commit the resolution of a blocked schema surfaced by update. Run AFTER fixing the block's items (or widening the local schema): when the block file carries git-style failure markers (written by update), strips the full-line marker sentinels first, then re-validates the corrected block against the pinned target schema from the pending-blocked record; on pass registers the migration chain, writes the target schema, advances the merge base to the target (so a subsequent update converges instead of re-blocking), and clears the pending entry; on fail reports the remaining per-item failures and writes nothing. The commit is all-or-nothing: a throw partway through it restores every touched file byte-exact — migrations.json, the installed schema, the block file, config.json, and the pending record — and reports the failure, never a partial commit. On a substrate whose config carries no substrate_id, resolve-blocked establishes the identity at entry (mints, persists, registers) before the commit's stamping write and reports it under substrateIdEstablished.",
|
|
1066
1069
|
promptSnippet: "Commit a blocked schema's resolution: strip any git-style failure markers, re-validate the corrected block against the pinned target, then write the target schema + advance the base + clear the pending record (run after fixing the items update reported blocked)",
|
|
1067
1070
|
examples: [`pi-context resolve-blocked --schemaName tasks --yes --json`],
|
|
1068
1071
|
parameters: Type.Object({
|
|
@@ -1079,7 +1082,7 @@ export const ops = [
|
|
|
1079
1082
|
{
|
|
1080
1083
|
name: "write-schema-migration",
|
|
1081
1084
|
label: "Write Schema Migration",
|
|
1082
|
-
description: "Declare a schema version-bump migration into substrate (migrations.json). operation 'create' appends a new declaration; 'replace' overwrites an existing declaration matched by (schemaName, fromVersion); 'remove' drops a declaration. kind='identity' asserts the bump is shape-compatible (no data transform); kind='declarative-transform' carries a TransformSpec of rename/set/delete/coerce operations on dotted JSON paths. The loaded MigrationRegistry resolves the recorded edge at next read/write so block items declaring an older schema_version walk forward without process restart. Requires user authorization via interactive confirmation at the pi-dispatch auth-gate; on confirm, the verified terminal-operator identity is stamped as writer.",
|
|
1085
|
+
description: "Declare a schema version-bump migration into substrate (migrations.json). operation 'create' appends a new declaration; 'replace' overwrites an existing declaration matched by (schemaName, fromVersion); 'remove' drops a declaration. kind='identity' asserts the bump is shape-compatible (no data transform); kind='declarative-transform' carries a TransformSpec of rename/set/delete/coerce/map_each operations on dotted JSON paths; map_each addresses an array — table mode maps each string element through a lookup (unmatched elements become {relation_type, item_endpoint} with parent/child fallback), set-on-each mode sets a field on every object element. The loaded MigrationRegistry resolves the recorded edge at next read/write so block items declaring an older schema_version walk forward without process restart. Requires user authorization via interactive confirmation at the pi-dispatch auth-gate; on confirm, the verified terminal-operator identity is stamped as writer.",
|
|
1083
1086
|
promptSnippet: "Declare a schema version-bump migration (identity or declarative-transform) into migrations.json",
|
|
1084
1087
|
examples: [
|
|
1085
1088
|
`pi-context write-schema-migration --operation create --schemaName tasks --fromVersion 1.0.0 --toVersion 1.1.0 --kind identity --writer '{"kind":"human","user":"you@example.com"}' --json`,
|
|
@@ -1156,7 +1159,7 @@ export const ops = [
|
|
|
1156
1159
|
{
|
|
1157
1160
|
name: "context-install",
|
|
1158
1161
|
label: "Context Install",
|
|
1159
|
-
description: "Install (materialize) the schemas and starter blocks declared in config.json's installed_schemas / installed_blocks from the package samples catalog. Default skip-if-exists (installed files never overwritten without --update); populated block data is always preserved (even with --update); empty or absent blocks get the catalog starter. Records the install baseline (config.installed_from: catalog source + per-schema fingerprint) for installed-vs-catalog drift detection (schemas only). A re-install on an unchanged substrate is idempotent.",
|
|
1162
|
+
description: "Install (materialize) the schemas and starter blocks declared in config.json's installed_schemas / installed_blocks from the package samples catalog. Default skip-if-exists (installed files never overwritten without --update); populated block data is always preserved (even with --update); empty or absent blocks get the catalog starter. Records the install baseline (config.installed_from: catalog source + per-schema fingerprint) for installed-vs-catalog drift detection (schemas only). A re-install on an unchanged substrate is idempotent. On a substrate whose config carries no substrate_id, install establishes the identity at entry (mints, persists to config.json, registers in the project registry) and reports it under substrateIdEstablished; an established identity is never re-minted.",
|
|
1160
1163
|
promptSnippet: "Install declared schemas + starter blocks from the samples catalog (skip-if-exists; --update re-syncs schemas + replaces empty blocks; records the config.installed_from baseline)",
|
|
1161
1164
|
examples: ["pi-context context-install --json", "pi-context context-install --update true --json"],
|
|
1162
1165
|
parameters: Type.Object({
|
|
@@ -1176,8 +1179,8 @@ export const ops = [
|
|
|
1176
1179
|
{
|
|
1177
1180
|
name: "update",
|
|
1178
1181
|
label: "Update Installed Model",
|
|
1179
|
-
description: "Bring the installed substrate model (schemas) current with the packaged catalog. Per installed schema, consults the read-only drift check and routes by state: an already-current (in-sync) schema is a no-op; a schema the package shipped a newer version of (catalog-ahead) is re-synced through the migration-aware path; a schema edited locally (locally-modified / both-diverged) is reconciled by a deterministic 3-way merge of base (the as-installed body in the object store, keyed by the recorded baseline content_hash) × ours (the installed schema) × theirs (the catalog schema) — disjoint edits auto-merge so both the user's and the catalog's changes survive (required / enum / array-valued type nodes merge as sets), and a schema with irreconcilable per-path conflicts is left unmodified — the conflict set is returned in the op output (under conflicts) alongside a readable report, and the calling agent reconciles it then commits via resolve-conflict — which writes the reconciled body AND advances the merge base to the catalog so update stops re-reporting it (no subordinate resolver is spawned); undecidable / absent schemas (no-baseline / missing-catalog / missing-installed) are reported, not touched. Update also additively propagates catalog-new config-registry entries (relation_types / invariants / block_kinds / lenses) that are absent from the substrate config, preserving every user-authored entry and any locally-diverged body of an existing entry (additive-only — present entries are never overwritten). Update reports, under migrationsRegistered, the migration declarations a version-bump resync registers into migrations.json (each as schema / from / to). A blocked (refused) catalog-ahead schema additionally carries its diagnostic detail under blockedDetail (one entry per blocked schema): the refusal reason — no-migration-chain (no shipped chain reaches the catalog version) vs validation-failed (the forward-migrated items fail the catalog schema) — the installed -> catalog version pair, and for a validation failure the per-item failures naming the failing item id, field, and constraint. A live blocked resync also persists a pending-blocked record (pinning the target catalog schema + the chain reaching it) consumable by resolve-blocked, which commits the resolution once the block's items are fixed. Pass dryRun to preview the per-schema action plan; dryRun predicts the precise per-schema catalog-ahead outcome (resync / migrate / block / merge / conflict) by running the forward-migration + re-validation in memory, the per-blocked-schema diagnostic detail, the config-registry entries that would be added, AND the migration declarations that would be registered, writing nothing. When a catalog-ahead resync is blocked because the block's items fail the catalog schema (validation-failed), update inscribes git-style failure markers INTO the block file at the offending items (full-line `<<<<<<< BLOCKED …` / `>>>>>>> target: …` sentinels), pinning the pre-marker bytes so resolve-blocked can strip the markers and re-validate; the schema and migrations.json stay byte-unchanged. A dryRun preview writes no markers.",
|
|
1180
|
-
promptSnippet: "Update the installed schema model from the catalog (3-way merges locally-modified schemas, preserving non-conflicting edits; conflicts → returned in the op output + a report for the calling agent to reconcile and commit via resolve-conflict; a blocked resync carries blockedDetail — reason, version pair, per-item failures — and persists a pending-blocked record (target catalog schema + the chain reaching it) resolved via resolve-blocked once the block's items are fixed; a validation-failed block is marked in place with git-style failure markers (recoverable; stripped + re-validated by resolve-blocked); --dry-run predicts the precise per-schema outcome — resync / migrate / block / merge / conflict — via in-memory forward-migration + re-validation, writing nothing)",
|
|
1182
|
+
description: "Bring the installed substrate model (schemas) current with the packaged catalog. Per installed schema, consults the read-only drift check and routes by state: an already-current (in-sync) schema is a no-op; a schema the package shipped a newer version of (catalog-ahead) is re-synced through the migration-aware path; a schema edited locally (locally-modified / both-diverged) is reconciled by a deterministic 3-way merge of base (the as-installed body in the object store, keyed by the recorded baseline content_hash) × ours (the installed schema) × theirs (the catalog schema) — disjoint edits auto-merge so both the user's and the catalog's changes survive (required / enum / array-valued type nodes merge as sets), and a schema with irreconcilable per-path conflicts is left unmodified — the conflict set is returned in the op output (under conflicts) alongside a readable report, and the calling agent reconciles it then commits via resolve-conflict — which writes the reconciled body AND advances the merge base to the catalog so update stops re-reporting it (no subordinate resolver is spawned); undecidable / absent schemas (no-baseline / missing-catalog / missing-installed) are reported, not touched. Update also additively propagates catalog-new config-registry entries (relation_types / invariants / block_kinds / lenses) that are absent from the substrate config, preserving every user-authored entry and any locally-diverged body of an existing entry (additive-only — present entries are never overwritten). Update reports, under migrationsRegistered, the migration declarations a version-bump resync registers into migrations.json (each as schema / from / to). A blocked (refused) catalog-ahead schema additionally carries its diagnostic detail under blockedDetail (one entry per blocked schema): the refusal reason — no-migration-chain (no shipped chain reaches the catalog version) vs validation-failed (the forward-migrated items fail the catalog schema) vs write-failed (a non-validation throw at the write boundary, e.g. the block writer's duplicate-item-id guard; the failures entry carries the thrown message, the items were NOT flagged invalid, and no markers or pending-blocked record are produced) — the installed -> catalog version pair, and for a validation failure the per-item failures naming the failing item id, field, and constraint. A live blocked resync also persists a pending-blocked record (pinning the target catalog schema + the chain reaching it) consumable by resolve-blocked, which commits the resolution once the block's items are fixed. Pass dryRun to preview the per-schema action plan; dryRun predicts the precise per-schema catalog-ahead outcome (resync / migrate / block / merge / conflict) by running the forward-migration + re-validation in memory, the per-blocked-schema diagnostic detail, the config-registry entries that would be added, AND the migration declarations that would be registered, writing nothing beyond the idempotent ceremony seed of the catalog's config migration declarations into migrations.json (every substrate-lifecycle ceremony seeds at entry, before its first config read, so a version-lagging legacy substrate heals instead of throwing). When a catalog-ahead resync is blocked because the block's items fail the catalog schema (validation-failed), update inscribes git-style failure markers INTO the block file at the offending items (full-line `<<<<<<< BLOCKED …` / `>>>>>>> target: …` sentinels), pinning the pre-marker bytes so resolve-blocked can strip the markers and re-validate; the schema and migrations.json stay byte-unchanged. A dryRun preview writes no markers. Because update applies per-component (a blocked schema rolls back only itself; the additive registry propagation writes regardless), a run that refuses any schema while applying registry additions or other-schema resyncs/migrations/merges reports the partiality under partialApplication — applied and notApplied channel mirrors plus a one-line summary naming what was applied alongside what was refused and why — so a blocked run never reads as a no-op; dryRun reports the predicted partiality in the same shape. On a substrate whose config carries no substrate_id, a LIVE update establishes the identity at entry (mints, persists to config.json, registers in the project registry) before its first identity-stamping write — so a pre-identity substrate heals on the ceremony instead of refusing — and reports it under substrateIdEstablished; an established identity is never re-minted, and dryRun (no stamping writes) establishes nothing.",
|
|
1183
|
+
promptSnippet: "Update the installed schema model from the catalog (3-way merges locally-modified schemas, preserving non-conflicting edits; conflicts → returned in the op output + a report for the calling agent to reconcile and commit via resolve-conflict; a blocked resync carries blockedDetail — reason (no-migration-chain / validation-failed / write-failed for a non-validation write-boundary throw), version pair, per-item failures — and a validation-failed block persists a pending-blocked record (target catalog schema + the chain reaching it) resolved via resolve-blocked once the block's items are fixed; a validation-failed block is marked in place with git-style failure markers (recoverable; stripped + re-validated by resolve-blocked); --dry-run predicts the precise per-schema outcome — resync / migrate / block / merge / conflict — via in-memory forward-migration + re-validation, writing nothing; a run that refuses any schema while applying registry additions or other-schema updates surfaces the partiality under partialApplication with a one-line summary, so a blocked run never reads as a no-op)",
|
|
1181
1184
|
examples: [`pi-context update --dryRun true --json`],
|
|
1182
1185
|
parameters: Type.Object({
|
|
1183
1186
|
dryRun: Type.Optional(Type.Boolean({ description: "Preview the per-schema action plan without writing anything." })),
|
|
@@ -1553,7 +1556,7 @@ export const ops = [
|
|
|
1553
1556
|
{
|
|
1554
1557
|
name: "context-walk-descendants",
|
|
1555
1558
|
label: "Context Walk Descendants",
|
|
1556
|
-
description: "Walk closure-table descendants of a parent id under a given relation_type. Returns string[] of descendant ids (may be empty if no children or relations.json absent).",
|
|
1559
|
+
description: "Walk closure-table descendants of a parent id under a given relation_type. Returns string[] of descendant ids (may be empty if no children or relations.json absent). For a DISJOINT-kind relation, querying from the wrong (target-kind) end THROWS naming walk-ancestors instead of silently returning []; same-kind / wildcard relations return [] honestly.",
|
|
1557
1560
|
promptSnippet: "Walk closure-table descendants under a relation_type",
|
|
1558
1561
|
examples: [
|
|
1559
1562
|
`pi-context context-walk-descendants --parentId FEAT-008 --relationType feature_decomposed_into_task --json`,
|
|
@@ -1571,7 +1574,7 @@ export const ops = [
|
|
|
1571
1574
|
{
|
|
1572
1575
|
name: "walk-ancestors",
|
|
1573
1576
|
label: "Walk Ancestors",
|
|
1574
|
-
description: "Walk closure-table ancestors of an item id under a given relation_type — reverse-direction counterpart to context-walk-descendants. Returns string[] of ancestor ids (may be empty if no parents or relations.json absent).",
|
|
1577
|
+
description: "Walk closure-table ancestors of an item id under a given relation_type — reverse-direction counterpart to context-walk-descendants. Returns string[] of ancestor ids (may be empty if no parents or relations.json absent). For a DISJOINT-kind relation, querying from the wrong (source-kind) end THROWS naming context-walk-descendants instead of silently returning []; same-kind / wildcard relations return [] honestly.",
|
|
1575
1578
|
promptSnippet: "Walk closure-table ancestors under a relation_type",
|
|
1576
1579
|
examples: [`pi-context walk-ancestors --itemId TASK-042 --relationType feature_decomposed_into_task --json`],
|
|
1577
1580
|
parameters: Type.Object({
|
|
@@ -1632,75 +1635,45 @@ export const ops = [
|
|
|
1632
1635
|
{
|
|
1633
1636
|
name: "context-roadmap-load",
|
|
1634
1637
|
label: "Context: load roadmap",
|
|
1635
|
-
description: "Load
|
|
1636
|
-
promptSnippet: "Load
|
|
1637
|
-
examples: [`pi-context context-roadmap-load --
|
|
1638
|
-
parameters: Type.Object({
|
|
1639
|
-
roadmapId: Type.String({ description: "ROADMAP-NNN id from <config.root>/roadmap.json" }),
|
|
1640
|
-
}),
|
|
1638
|
+
description: "Load the derived roadmap view over the milestone_precedes_milestone DAG: milestone-block items topo-ordered by the authored precedes edges (order + cycles), each milestone carrying its derived status/phaseCount (currentState's milestone rollup), its member phases (parents of phase_positioned_in_milestone edges), each phase's tasks (parents of task_positioned_in_phase edges), and per-phase + per-milestone status rollups. Adjacency comes strictly from the authored edges — never inferred from order. Zero milestones is a valid empty view.",
|
|
1639
|
+
promptSnippet: "Load the derived milestone roadmap view",
|
|
1640
|
+
examples: [`pi-context context-roadmap-load --json`],
|
|
1641
|
+
parameters: Type.Object({}),
|
|
1641
1642
|
surface: "use",
|
|
1642
|
-
run(cwd,
|
|
1643
|
-
const view = loadRoadmap(cwd
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1648
|
-
// whole: a structured RoadmapView (phases + lens-views + rollups) — keep
|
|
1649
|
-
// the view shape intact rather than paging an inner array.
|
|
1650
|
-
const read = structureForRead(serializeRoadmapView(view), {
|
|
1651
|
-
whole: true,
|
|
1652
|
-
label: `roadmap ${params.roadmapId}`,
|
|
1653
|
-
});
|
|
1643
|
+
run(cwd, _params) {
|
|
1644
|
+
const view = loadRoadmap(cwd);
|
|
1645
|
+
// whole: a structured MilestoneRoadmapView (milestones + rollups + edges)
|
|
1646
|
+
// — keep the view shape intact rather than paging an inner array.
|
|
1647
|
+
const read = structureForRead(view, { whole: true, label: "roadmap (derived)" });
|
|
1654
1648
|
return { read };
|
|
1655
1649
|
},
|
|
1656
1650
|
},
|
|
1657
1651
|
{
|
|
1658
1652
|
name: "context-roadmap-render",
|
|
1659
1653
|
label: "Context: render roadmap",
|
|
1660
|
-
description: "Render
|
|
1661
|
-
promptSnippet: "Render
|
|
1662
|
-
examples: [`pi-context context-roadmap-render
|
|
1663
|
-
parameters: Type.Object({
|
|
1664
|
-
roadmapId: Type.String({ description: "ROADMAP-NNN id from <config.root>/roadmap.json" }),
|
|
1665
|
-
}),
|
|
1654
|
+
description: "Render the derived roadmap as pure-textual markdown — milestone order list (topo over the authored milestone_precedes_milestone edges), per-milestone sections with **Preceded by:** adjacency lines sourced strictly from those edges (alphabetically sorted; '—' when none), per-milestone rollup counts, and per-phase task tables. Cycle participants surface under a separate heading with a Cycles-detected line. NO mermaid / graph syntax; adjacency is never inferred from order consecutive pairs.",
|
|
1655
|
+
promptSnippet: "Render the derived milestone roadmap as markdown",
|
|
1656
|
+
examples: [`pi-context context-roadmap-render`],
|
|
1657
|
+
parameters: Type.Object({}),
|
|
1666
1658
|
surface: "use",
|
|
1667
|
-
run(cwd,
|
|
1668
|
-
const view = loadRoadmap(cwd
|
|
1659
|
+
run(cwd, _params) {
|
|
1660
|
+
const view = loadRoadmap(cwd);
|
|
1669
1661
|
if ("error" in view) {
|
|
1670
1662
|
return { json: view };
|
|
1671
1663
|
}
|
|
1672
|
-
|
|
1673
|
-
return renderRoadmap(view, naming);
|
|
1664
|
+
return renderRoadmap(view);
|
|
1674
1665
|
},
|
|
1675
1666
|
},
|
|
1676
1667
|
{
|
|
1677
1668
|
name: "context-roadmap-validate",
|
|
1678
|
-
label: "Context: validate roadmap
|
|
1679
|
-
description: "Validate
|
|
1680
|
-
promptSnippet: "Validate
|
|
1681
|
-
examples: [`pi-context context-roadmap-validate --
|
|
1682
|
-
parameters: Type.Object({
|
|
1683
|
-
roadmapId: Type.Optional(Type.String({ description: "Filter to issues matching this roadmap_id (omit for full-project validation)" })),
|
|
1684
|
-
}),
|
|
1685
|
-
surface: "use",
|
|
1686
|
-
run(cwd, params) {
|
|
1687
|
-
const result = validateRoadmaps(cwd);
|
|
1688
|
-
const filtered = params.roadmapId
|
|
1689
|
-
? result.issues.filter((i) => !i.roadmap_id || i.roadmap_id === params.roadmapId)
|
|
1690
|
-
: result.issues;
|
|
1691
|
-
return { json: { status: result.status, issues: filtered } };
|
|
1692
|
-
},
|
|
1693
|
-
},
|
|
1694
|
-
{
|
|
1695
|
-
name: "context-roadmap-list",
|
|
1696
|
-
label: "Context: list roadmaps",
|
|
1697
|
-
description: "List every roadmap in <config.root>/roadmap.json with id, title, optional status, and phase count. Returns [] when roadmap.json absent (opt-in block; absence is the truthful answer).",
|
|
1698
|
-
promptSnippet: "List roadmaps",
|
|
1699
|
-
examples: [`pi-context context-roadmap-list --json`],
|
|
1669
|
+
label: "Context: validate roadmap",
|
|
1670
|
+
description: "Validate the derived roadmap over the milestone_precedes_milestone edges. Error codes: roadmap_precedes_endpoint_missing (a precedes-edge endpoint that is not a milestone-block item), roadmap_milestone_cycle (a cycle in the precedes graph), roadmap_milestone_missing (a phase_positioned_in_milestone edge whose child is not a known milestone). Warning: roadmap_status_unknown_value (a member phase whose task rollup buckets unknown with items present). Info: roadmap_milestone_isolated (a milestone with zero precedes edges while others are ordered) — info never affects status: invalid iff any error-code issue, warnings iff any warning-code issue, else clean. Display strings flow through config.display_strings (pi-context divergence).",
|
|
1671
|
+
promptSnippet: "Validate the derived milestone roadmap",
|
|
1672
|
+
examples: [`pi-context context-roadmap-validate --json`],
|
|
1700
1673
|
parameters: Type.Object({}),
|
|
1701
1674
|
surface: "use",
|
|
1702
1675
|
run(cwd, _params) {
|
|
1703
|
-
return { json:
|
|
1676
|
+
return { json: validateRoadmap(cwd) };
|
|
1704
1677
|
},
|
|
1705
1678
|
},
|
|
1706
1679
|
];
|