@davidorex/pi-context 0.26.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.
Files changed (126) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +195 -0
  3. package/dist/block-api.d.ts +328 -0
  4. package/dist/block-api.d.ts.map +1 -0
  5. package/dist/block-api.js +1167 -0
  6. package/dist/block-api.js.map +1 -0
  7. package/dist/block-validation.d.ts +33 -0
  8. package/dist/block-validation.d.ts.map +1 -0
  9. package/dist/block-validation.js +213 -0
  10. package/dist/block-validation.js.map +1 -0
  11. package/dist/context-dir.d.ts +93 -0
  12. package/dist/context-dir.d.ts.map +1 -0
  13. package/dist/context-dir.js +224 -0
  14. package/dist/context-dir.js.map +1 -0
  15. package/dist/context-sdk.d.ts +428 -0
  16. package/dist/context-sdk.d.ts.map +1 -0
  17. package/dist/context-sdk.js +1327 -0
  18. package/dist/context-sdk.js.map +1 -0
  19. package/dist/context.d.ts +497 -0
  20. package/dist/context.d.ts.map +1 -0
  21. package/dist/context.js +1060 -0
  22. package/dist/context.js.map +1 -0
  23. package/dist/dispatch-context.d.ts +112 -0
  24. package/dist/dispatch-context.d.ts.map +1 -0
  25. package/dist/dispatch-context.js +119 -0
  26. package/dist/dispatch-context.js.map +1 -0
  27. package/dist/execution-context.d.ts +138 -0
  28. package/dist/execution-context.d.ts.map +1 -0
  29. package/dist/execution-context.js +151 -0
  30. package/dist/execution-context.js.map +1 -0
  31. package/dist/index.d.ts +34 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1655 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lens-validator.d.ts +50 -0
  36. package/dist/lens-validator.d.ts.map +1 -0
  37. package/dist/lens-validator.js +46 -0
  38. package/dist/lens-validator.js.map +1 -0
  39. package/dist/lens-view.d.ts +75 -0
  40. package/dist/lens-view.d.ts.map +1 -0
  41. package/dist/lens-view.js +253 -0
  42. package/dist/lens-view.js.map +1 -0
  43. package/dist/rename-canonical-id.d.ts +64 -0
  44. package/dist/rename-canonical-id.d.ts.map +1 -0
  45. package/dist/rename-canonical-id.js +378 -0
  46. package/dist/rename-canonical-id.js.map +1 -0
  47. package/dist/roadmap-plan.d.ts +186 -0
  48. package/dist/roadmap-plan.d.ts.map +1 -0
  49. package/dist/roadmap-plan.js +534 -0
  50. package/dist/roadmap-plan.js.map +1 -0
  51. package/dist/samples-catalog.d.ts +48 -0
  52. package/dist/samples-catalog.d.ts.map +1 -0
  53. package/dist/samples-catalog.js +128 -0
  54. package/dist/samples-catalog.js.map +1 -0
  55. package/dist/schema-migrations.d.ts +82 -0
  56. package/dist/schema-migrations.d.ts.map +1 -0
  57. package/dist/schema-migrations.js +106 -0
  58. package/dist/schema-migrations.js.map +1 -0
  59. package/dist/schema-validator.d.ts +76 -0
  60. package/dist/schema-validator.d.ts.map +1 -0
  61. package/dist/schema-validator.js +219 -0
  62. package/dist/schema-validator.js.map +1 -0
  63. package/dist/schema-write.d.ts +122 -0
  64. package/dist/schema-write.d.ts.map +1 -0
  65. package/dist/schema-write.js +210 -0
  66. package/dist/schema-write.js.map +1 -0
  67. package/dist/status-vocab.d.ts +60 -0
  68. package/dist/status-vocab.d.ts.map +1 -0
  69. package/dist/status-vocab.js +109 -0
  70. package/dist/status-vocab.js.map +1 -0
  71. package/dist/test-helpers.d.ts +12 -0
  72. package/dist/test-helpers.d.ts.map +1 -0
  73. package/dist/test-helpers.js +28 -0
  74. package/dist/test-helpers.js.map +1 -0
  75. package/dist/topo.d.ts +19 -0
  76. package/dist/topo.d.ts.map +1 -0
  77. package/dist/topo.js +99 -0
  78. package/dist/topo.js.map +1 -0
  79. package/dist/update-check.d.ts +6 -0
  80. package/dist/update-check.d.ts.map +1 -0
  81. package/dist/update-check.js +82 -0
  82. package/dist/update-check.js.map +1 -0
  83. package/package.json +115 -0
  84. package/samples/blocks/context-contracts.json +3 -0
  85. package/samples/blocks/conventions.json +3 -0
  86. package/samples/blocks/decisions.json +3 -0
  87. package/samples/blocks/features.json +3 -0
  88. package/samples/blocks/framework-gaps.json +3 -0
  89. package/samples/blocks/issues.json +3 -0
  90. package/samples/blocks/layer-plans.json +3 -0
  91. package/samples/blocks/phase.json +3 -0
  92. package/samples/blocks/rationale.json +3 -0
  93. package/samples/blocks/requirements.json +3 -0
  94. package/samples/blocks/research.json +3 -0
  95. package/samples/blocks/spec-reviews.json +3 -0
  96. package/samples/blocks/story.json +3 -0
  97. package/samples/blocks/tasks.json +3 -0
  98. package/samples/blocks/verification.json +3 -0
  99. package/samples/conception.json +467 -0
  100. package/samples/schemas/context-contracts.schema.json +89 -0
  101. package/samples/schemas/conventions.schema.json +34 -0
  102. package/samples/schemas/decisions.schema.json +87 -0
  103. package/samples/schemas/features.schema.json +53 -0
  104. package/samples/schemas/framework-gaps.schema.json +64 -0
  105. package/samples/schemas/issues.schema.json +39 -0
  106. package/samples/schemas/layer-plans.schema.json +92 -0
  107. package/samples/schemas/phase.schema.json +118 -0
  108. package/samples/schemas/rationale.schema.json +24 -0
  109. package/samples/schemas/requirements.schema.json +34 -0
  110. package/samples/schemas/research.schema.json +134 -0
  111. package/samples/schemas/spec-reviews.schema.json +62 -0
  112. package/samples/schemas/story.schema.json +73 -0
  113. package/samples/schemas/tasks.schema.json +34 -0
  114. package/samples/schemas/verification.schema.json +37 -0
  115. package/schemas/bootstrap.schema.json +24 -0
  116. package/schemas/config.schema.json +193 -0
  117. package/schemas/layer.schema.json +9 -0
  118. package/schemas/priority.schema.json +9 -0
  119. package/schemas/relations.schema.json +31 -0
  120. package/schemas/severity.schema.json +9 -0
  121. package/schemas/source.schema.json +9 -0
  122. package/schemas/status.schema.json +9 -0
  123. package/schemas/verification-method.schema.json +9 -0
  124. package/skill-narrative.md +130 -0
  125. package/skills/pi-context/SKILL.md +684 -0
  126. package/skills/pi-context/references/bundled-resources.md +47 -0
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/story",
4
+ "version": "1.0.0",
5
+ "title": "Stories",
6
+ "description": "Top-level user-story / story array-block (FGAP-064 elevation). Stories are a work-altitude between feature and task: a feature decomposes into stories, a story decomposes into tasks. Cross-altitude relationships (feature→story, story→task, story→story dependency, story→gating-item, story→milestone/phase/project-goal) live in relations.json closure-table edges per DEC-0013 — NOT as inline arrays on the story. Sub-shape is structural (id/title/status/description/acceptance_criteria); the Agile user-story form (role/want/benefit) is a future optional enrichment, not imposed (DEC-deferred per FGAP-064 — no role/want/benefit present in current data, none fabricated).",
7
+ "type": "object",
8
+ "required": [
9
+ "stories"
10
+ ],
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "stories": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "required": [
19
+ "id",
20
+ "title",
21
+ "status"
22
+ ],
23
+ "properties": {
24
+ "id": {
25
+ "type": "string",
26
+ "pattern": "^STORY-\\d{3,}$",
27
+ "description": "Stable canonical id; STORY-NNN (3+ digits)."
28
+ },
29
+ "title": {
30
+ "type": "string"
31
+ },
32
+ "status": {
33
+ "type": "string",
34
+ "enum": [
35
+ "proposed",
36
+ "ready",
37
+ "in-progress",
38
+ "in-review",
39
+ "complete",
40
+ "blocked"
41
+ ],
42
+ "description": "Story lifecycle. Intentionally divergent from pi-context://schemas/status; reconciliation tracked in FGAP-021."
43
+ },
44
+ "description": {
45
+ "type": "string",
46
+ "x-prompt-budget": {
47
+ "tokens": 1000,
48
+ "words": 800
49
+ }
50
+ },
51
+ "acceptance_criteria": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "string"
55
+ }
56
+ },
57
+ "created_by": {
58
+ "type": "string"
59
+ },
60
+ "created_at": {
61
+ "type": "string"
62
+ },
63
+ "modified_by": {
64
+ "type": "string"
65
+ },
66
+ "modified_at": {
67
+ "type": "string"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/tasks",
4
+ "version": "1.0.0",
5
+ "title": "Tasks",
6
+ "description": "Discrete units of work with status lifecycle (planned, in-progress, completed, blocked, cancelled). The operational layer between gaps/requirements and code changes.",
7
+ "type": "object",
8
+ "required": ["tasks"],
9
+ "properties": {
10
+ "tasks": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "required": ["id", "description", "status"],
15
+ "properties": {
16
+ "id": { "type": "string", "pattern": "^TASK-\\d{3,}$" },
17
+ "description": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
18
+ "status": { "type": "string", "enum": ["planned", "in-progress", "completed", "blocked", "cancelled"] },
19
+ "files": {
20
+ "type": "array",
21
+ "items": { "type": "string" },
22
+ "description": "Files this task creates or modifies"
23
+ },
24
+ "acceptance_criteria": {
25
+ "type": "array",
26
+ "items": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } }
27
+ },
28
+ "assigned_agent": { "type": "string", "description": "Agent name if assigned" },
29
+ "notes": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/verification",
4
+ "version": "1.0.0",
5
+ "title": "Verification",
6
+ "description": "Evidence that work meets its criteria. Per-item completion evidence linking implementation to acceptance via a verification_verifies_item edge.",
7
+ "type": "object",
8
+ "required": ["verifications"],
9
+ "properties": {
10
+ "verifications": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "required": ["id", "status", "method"],
15
+ "properties": {
16
+ "id": { "type": "string", "pattern": "^VER-\\d{3,}$" },
17
+ "status": { "type": "string", "enum": ["passed", "failed", "partial", "skipped"] },
18
+ "method": { "type": "string", "enum": ["command", "inspect", "test"] },
19
+ "evidence": { "type": "string", "description": "What was checked and what was found" },
20
+ "timestamp": { "type": "string" },
21
+ "criteria_results": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "object",
25
+ "required": ["criterion", "status"],
26
+ "properties": {
27
+ "criterion": { "type": "string" },
28
+ "status": { "type": "string", "enum": ["passed", "failed", "skipped"] },
29
+ "evidence": { "type": "string" }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/bootstrap",
4
+ "version": "1.0.0",
5
+ "title": "pi-context bootstrap pointer",
6
+ "description": "Per-cwd bootstrap pointer at <cwd>/.pi-context.json declaring the substrate dir name. Read by resolveContextDir(cwd) before any substrate operation; substrate dir is config-driven per DEC-0015 (no hardcoded substrate-dir paths anywhere in pi-context, pi-jit-agents, pi-workflows, pi-behavior-monitors). Atomic-safety mechanism per FGAP-026 closure phase 1.1: written for legacy .project/-using repos containing {contextDir: '.project'} so substrate stays reachable when Phase 1.2 lands resolveContextDir replacing PROJECT_DIR + SCHEMAS_DIR constants. For new repos: written by /context init (prompt-required for dirName per DEC-0015; defaulting reintroduces hardcode-dressed-as-default this DEC rejects). Mutable post-init via /context migrate (Phase 10 of FGAP-026 closure) which atomically renames the dir + rewrites this pointer + updates config.root.",
7
+ "type": "object",
8
+ "properties": {
9
+ "contextDir": {
10
+ "type": "string",
11
+ "description": "Directory name (relative to cwd) where the substrate lives. User-decided at /context init time per DEC-0015. For pre-existing repos using legacy .project/ convention, value is '.project' until /context migrate runs."
12
+ },
13
+ "version": {
14
+ "type": "string",
15
+ "description": "Bootstrap pointer format version. '1.0.0' for the initial format. Future format changes increment via semver per FGAP-006 schema versioning discipline; migration registry handles transitions if breaking."
16
+ },
17
+ "created_at": {
18
+ "type": "string",
19
+ "description": "ISO 8601 timestamp when the bootstrap pointer was first written. Forensic; helps trace when the substrate was bootstrapped for this repo."
20
+ }
21
+ },
22
+ "required": ["contextDir"],
23
+ "additionalProperties": false
24
+ }
@@ -0,0 +1,193 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/config",
4
+ "version": "1.4.0",
5
+ "title": "pi-context substrate config",
6
+ "description": "Self-bootstrapping config block. Validates the config that configures the substrate. ConfigBlock is the canonical registry for block kinds, status buckets, relation types, display strings, layers, naming aliases, hierarchy declarations, and lens specs. Closes FGAP-001 (hierarchical block storage), FGAP-013 (status vocabulary registry), issue-089 class (PLAN- prefix collision) by making registries config-driven from line 1.",
7
+ "type": "object",
8
+ "required": ["schema_version", "block_kinds"],
9
+ "additionalProperties": false,
10
+ "properties": {
11
+ "schema_version": {
12
+ "type": "string",
13
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
14
+ "description": "Semver of this config schema. Surfaces FGAP-006 at the meta-layer."
15
+ },
16
+ "root": {
17
+ "type": "string",
18
+ "description": "Optional substrate-dir override. The substrate-dir name is set at /context init and recorded in the .pi-context.json bootstrap pointer (DEC-0015 — no shipped default); adoptConception writes this field at /context accept-all from the init-chosen dir. When absent, projectRoot resolves the substrate via the pointer (resolveContextDir). Not shipped by the packaged conception, which is a template. Closes GitHub issue #3 surface — relocatable per config."
19
+ },
20
+ "naming": {
21
+ "type": "object",
22
+ "description": "Canonical-id → user-facing-name alias map. Code references canonical ids; UI/macros render configured names. Decouples terminology from code.",
23
+ "additionalProperties": { "type": "string" }
24
+ },
25
+ "layers": {
26
+ "type": "array",
27
+ "description": "Work-altitude tier registry (L1..L5 or user-defined). FGAP-016 layer registry surface.",
28
+ "items": {
29
+ "type": "object",
30
+ "required": ["id", "display_name"],
31
+ "additionalProperties": false,
32
+ "properties": {
33
+ "id": { "type": "string" },
34
+ "display_name": { "type": "string" },
35
+ "description": { "type": "string" }
36
+ }
37
+ }
38
+ },
39
+ "block_kinds": {
40
+ "type": "array",
41
+ "description": "Canonical block-kind registry. Replaces the previously hardcoded ID_PREFIX_TO_BLOCK table — prefix conflicts surface at config-registration time, not at fixture-write time.",
42
+ "items": {
43
+ "type": "object",
44
+ "required": ["canonical_id", "display_name", "prefix", "schema_path", "array_key", "data_path"],
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "canonical_id": { "type": "string", "description": "Opaque identifier (e.g. 'decisions-block')" },
48
+ "display_name": { "type": "string", "description": "Mutable user-facing label (e.g. 'Design Decisions')" },
49
+ "prefix": { "type": "string", "description": "Id-prefix token (e.g. 'DEC-'). Empty string is valid for slug-id blocks (e.g. conventions) that do not use prefix+number ids — such blocks are excluded from prefix-based id resolution (FGAP-062)." },
50
+ "schema_path": { "type": "string", "description": "Relative path to schema file" },
51
+ "array_key": { "type": "string", "description": "Top-level array property in block JSON" },
52
+ "data_path": { "type": "string", "description": "Relative path to block data file" },
53
+ "layer": { "type": "string", "description": "FK to layers[].id (optional)" }
54
+ }
55
+ }
56
+ },
57
+ "status_buckets": {
58
+ "type": "object",
59
+ "description": "Per-project status normalization. Maps raw status string → bucket. FGAP-013 closure surface.",
60
+ "additionalProperties": {
61
+ "type": "string",
62
+ "enum": ["complete", "in_progress", "blocked", "todo", "unknown"]
63
+ }
64
+ },
65
+ "display_strings": {
66
+ "type": "object",
67
+ "description": "Validation-code / opaque-slug → display label map.",
68
+ "additionalProperties": { "type": "string" }
69
+ },
70
+ "relation_types": {
71
+ "type": "array",
72
+ "description": "Canonical relation_type registry (DEC-0013 single-source-of-truth pattern).",
73
+ "items": {
74
+ "type": "object",
75
+ "required": ["canonical_id", "display_name", "category"],
76
+ "additionalProperties": false,
77
+ "properties": {
78
+ "canonical_id": { "type": "string" },
79
+ "display_name": { "type": "string" },
80
+ "category": { "type": "string", "enum": ["ordering", "data_flow", "membership"] },
81
+ "cycle_allowed": { "type": "boolean" },
82
+ "source_kinds": { "type": "array", "items": { "type": "string" }, "description": "Block_kind canonical_ids this relation's source (edge.parent) may be; literal \"*\" = any kind. Optional; absence => endpoint-kind unchecked." },
83
+ "target_kinds": { "type": "array", "items": { "type": "string" }, "description": "Block_kind canonical_ids this relation's target (edge.child) may be; literal \"*\" = any kind. Optional; absence => endpoint-kind unchecked." }
84
+ }
85
+ }
86
+ },
87
+ "invariants": {
88
+ "type": "array",
89
+ "description": "Config-declared substrate invariants enforced generically by validateProject. Each names block kinds / relation_types / status values purely as DATA — no vocabulary literal lives in source (DEC-0025). User-overridable: a project ships only the invariants its conception requires. Discriminated by `class`: requires-edge (item must occupy an endpoint on ≥1 edge of a given relation_type) or status-consistency (an item's status bucket must/must-not co-occur with a related item's status bucket across an edge).",
90
+ "items": {
91
+ "oneOf": [
92
+ {
93
+ "type": "object",
94
+ "required": ["id", "class", "block", "relation_types", "direction"],
95
+ "additionalProperties": false,
96
+ "properties": {
97
+ "id": { "type": "string", "description": "Stable invariant id, surfaced in diagnostics + the issue code field." },
98
+ "class": { "type": "string", "const": "requires-edge", "description": "Invariant class: qualifying items must occupy an endpoint on an edge of one of relation_types." },
99
+ "block": { "type": "string", "description": "Data-file basename of the block whose items the invariant ranges over (matched against ItemLocation.block)." },
100
+ "where": { "type": "object", "description": "Optional field-equality predicate. Item qualifies only when EVERY (field,value) matches item[field] === value. Absent → all items qualify.", "additionalProperties": { "type": ["string", "number", "boolean"] } },
101
+ "relation_types": { "type": "array", "minItems": 1, "items": { "type": "string" }, "description": "canonical_ids of relation_types that satisfy the invariant. Qualifying item passes if ≥1 incident edge (in direction) has relation_type in this set." },
102
+ "direction": { "type": "string", "enum": ["as_parent", "as_child"], "description": "Which endpoint the qualifying item must occupy. as_parent → item.id === edge.parent; as_child → item.id === edge.child." },
103
+ "severity": { "type": "string", "enum": ["error", "warning"], "default": "error", "description": "Diagnostic severity when unsatisfied." },
104
+ "message": { "type": "string", "description": "Optional message template. Tokens {id} and {block} substitute. Absent → generic default." }
105
+ }
106
+ },
107
+ {
108
+ "type": "object",
109
+ "required": ["id", "class", "block", "relation_types", "direction"],
110
+ "additionalProperties": false,
111
+ "properties": {
112
+ "id": { "type": "string", "description": "Stable invariant id, surfaced in diagnostics + the issue code field." },
113
+ "class": { "type": "string", "const": "status-consistency", "description": "Invariant class: a qualifying item's status bucket must (require_target_bucket) or must-not (forbid_target_bucket) co-occur with a related item's status bucket across an incident edge." },
114
+ "block": { "type": "string", "description": "Data-file basename of the block whose items the invariant ranges over (matched against ItemLocation.block)." },
115
+ "relation_types": { "type": "array", "minItems": 1, "items": { "type": "string" }, "description": "canonical_ids of relation_types whose edges are inspected for the related (target) item." },
116
+ "direction": { "type": "string", "enum": ["as_parent", "as_child"], "description": "Which endpoint the qualifying item occupies on the inspected edge; the target item is the other endpoint." },
117
+ "when_bucket": { "type": "string", "enum": ["complete", "in_progress", "blocked", "todo", "unknown"], "description": "Optional gate: only items whose own status bucket equals this are checked. Absent → all items in block are checked." },
118
+ "require_target_bucket": { "type": "string", "enum": ["complete", "in_progress", "blocked", "todo", "unknown"], "description": "Violation when the target item's status bucket differs from this. Mutually exclusive with forbid_target_bucket at runtime." },
119
+ "forbid_target_bucket": { "type": "string", "enum": ["complete", "in_progress", "blocked", "todo", "unknown"], "description": "Violation when the target item's status bucket equals this. Mutually exclusive with require_target_bucket at runtime." },
120
+ "severity": { "type": "string", "enum": ["error", "warning"], "default": "error", "description": "Diagnostic severity when violated." },
121
+ "message": { "type": "string", "description": "Optional message template. Tokens {id} and {block} substitute. Absent → generic default." }
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ },
127
+ "hierarchy": {
128
+ "type": "array",
129
+ "description": "Constraint declaration: which (parent_block, child_block, relation_type) edges are legal in relations.json.",
130
+ "items": {
131
+ "type": "object",
132
+ "required": ["parent_block", "child_block", "relation_type"],
133
+ "additionalProperties": false,
134
+ "properties": {
135
+ "parent_block": { "type": "string" },
136
+ "child_block": { "type": "string" },
137
+ "relation_type": { "type": "string" }
138
+ }
139
+ }
140
+ },
141
+ "lenses": {
142
+ "type": "array",
143
+ "description": "Named projections over a target block (or a composition over multiple). Supports kind=target (single target) and kind=composition (members reference sub-lenses or {from, where} item-filters).",
144
+ "items": {
145
+ "type": "object",
146
+ "required": ["id", "bins"],
147
+ "additionalProperties": false,
148
+ "properties": {
149
+ "id": { "type": "string" },
150
+ "kind": { "type": "string", "enum": ["target", "composition"] },
151
+ "target": { "type": "string", "description": "Canonical block id (kind=target)" },
152
+ "targets": {
153
+ "type": "array",
154
+ "description": "Block ids the composition spans (kind=composition)",
155
+ "items": { "type": "string" }
156
+ },
157
+ "members": {
158
+ "type": "array",
159
+ "description": "Composition members — sub-lens reference or {from, where} item-filter",
160
+ "items": {
161
+ "type": "object",
162
+ "additionalProperties": false,
163
+ "properties": {
164
+ "lens": { "type": "string" },
165
+ "from": { "type": "string" },
166
+ "where": {
167
+ "type": "object",
168
+ "additionalProperties": {
169
+ "type": ["string", "number", "boolean"]
170
+ }
171
+ }
172
+ }
173
+ }
174
+ },
175
+ "relation_type": { "type": "string" },
176
+ "derived_from_field": { "type": ["string", "null"] },
177
+ "bins": { "type": "array", "items": { "type": "string" } },
178
+ "render_uncategorized": { "type": "boolean", "default": true }
179
+ }
180
+ }
181
+ },
182
+ "installed_schemas": {
183
+ "type": "array",
184
+ "description": "Schemas the project has opted into via /context install (DEC-0011).",
185
+ "items": { "type": "string" }
186
+ },
187
+ "installed_blocks": {
188
+ "type": "array",
189
+ "description": "Blocks the project has opted into via /context install (DEC-0011).",
190
+ "items": { "type": "string" }
191
+ }
192
+ }
193
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/layer",
4
+ "version": "1.0.0",
5
+ "title": "Layer canonical enum",
6
+ "description": "Opaque canonical layer IDs (work-altitude tiers L1..L5). Display names per project come from config.layers[].display_name.",
7
+ "type": "string",
8
+ "enum": ["L1", "L2", "L3", "L4", "L5"]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/priority",
4
+ "version": "1.0.0",
5
+ "title": "Priority canonical enum",
6
+ "description": "Opaque canonical priority IDs. Display labels per consumer come from config.priority_buckets.",
7
+ "type": "string",
8
+ "enum": ["p-low", "p-medium", "p-high", "p-critical"]
9
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/relations",
4
+ "version": "1.0.0",
5
+ "title": "pi-context closure-table relations",
6
+ "description": "Generic closure-table substrate. One row per (parent, child, relation_type) edge. Multiple parallel hierarchies coexist as different relation_type values. Reassignment is a single field write. The schema is intentionally permissive on edge.parent's namespace — disambiguation between hierarchy edges (parent ∈ canonical ids) and lens edges (parent ∈ lens.bins) is cross-document and lives at substrate-SDK validateRelations(). Cycle detection lives at SDK traversal primitives.",
7
+ "type": "array",
8
+ "items": {
9
+ "type": "object",
10
+ "required": ["parent", "child", "relation_type"],
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "parent": {
14
+ "type": "string",
15
+ "description": "Either a canonical id (DEC-NNNN, FGAP-NNN, issue-NNN) or a bin name from a lens definition. Mixing is intentional — a lens bin acts as a virtual parent node."
16
+ },
17
+ "child": {
18
+ "type": "string",
19
+ "description": "Canonical id of the member item."
20
+ },
21
+ "relation_type": {
22
+ "type": "string",
23
+ "description": "Lens id, hierarchy edge type, or registered relation_type canonical_id. Allows multiple parallel hierarchies over the same items."
24
+ },
25
+ "ordinal": {
26
+ "type": "integer",
27
+ "description": "Optional sibling-ordering within (parent, relation_type)."
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/severity",
4
+ "version": "1.0.0",
5
+ "title": "Severity canonical enum",
6
+ "description": "Opaque canonical severity IDs. Display labels per consumer come from config.display_strings or equivalent registry.",
7
+ "type": "string",
8
+ "enum": ["info", "warning", "error"]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/source",
4
+ "version": "1.0.0",
5
+ "title": "Source canonical enum",
6
+ "description": "Opaque canonical source IDs identifying which actor produced a piece of substrate state (human, agent run, monitor classification, workflow execution).",
7
+ "type": "string",
8
+ "enum": ["human", "agent", "monitor", "workflow"]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/status",
4
+ "version": "1.0.0",
5
+ "title": "Status canonical enum",
6
+ "description": "Opaque canonical status IDs. Display labels per consumer come from config.status_buckets normalization. Matches POC-C StatusBucket vocabulary.",
7
+ "type": "string",
8
+ "enum": ["todo", "in_progress", "blocked", "complete", "unknown"]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "pi-context://schemas/verification-method",
4
+ "version": "1.0.0",
5
+ "title": "Verification method canonical enum",
6
+ "description": "Opaque canonical verification-method IDs identifying how a verification claim is to be substantiated.",
7
+ "type": "string",
8
+ "enum": ["command", "grep", "ast", "inspect", "human", "test"]
9
+ }
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: pi-context
3
+ description: >
4
+ Schema-driven project state management with typed JSON blocks, schema validation,
5
+ substrate config, lens views, closure-table relations, and cross-block referential
6
+ integrity. Use when managing .project/ blocks, scaffolding project structure,
7
+ installing block kinds from the packaged samples catalog, validating project state,
8
+ rendering lens views, or adding work items.
9
+ ---
10
+
11
+ <objective>
12
+ pi-context manages structured project state in `.project/` — a directory of JSON block files validated against schemas. The substrate (config + lenses + closure-table relations) is degree-zero state that defines where the rest lives and how items group into views.
13
+ </objective>
14
+
15
+ <block_files>
16
+ Blocks are JSON files under the substrate root (e.g., `gaps.json`, `decisions.json`). Each block has a corresponding schema in `<root>/schemas/`. When you write to a block via the tools, the data is validated against its schema before persisting. Writes are atomic (tmp file + rename) and serialised per block via `withBlockLock`. The substrate root is the dir chosen at init (recorded in the `.pi-context.json` bootstrap pointer) and written to `config.json`'s `root` field by `/context accept-all`; the framework ships no default (DEC-0015). block-api routes through `resolveContextDir(cwd)` — which resolves `config.root` when set and otherwise falls back to the pointer — so a relocated root reaches every read/write site.
17
+ </block_files>
18
+
19
+ <schema_validation>
20
+ Every block write validates against `<root>/schemas/<blockname>.schema.json`. If the schema file doesn't exist, writes proceed without validation. Validation errors include the specific JSON Schema violations.
21
+ </schema_validation>
22
+
23
+ <context_init>
24
+ `/context init <dir>` creates the substrate skeleton: the `.pi-context.json` bootstrap pointer (declaring the substrate-dir name per DEC-0015) plus the substrate root and its `schemas/` directory. Nothing is imposed — no `config.json`, no schemas, and no starter blocks are written (DEC-0011 ship-no-defaults). Idempotent: re-running preserves existing dirs. Populate the substrate next with `/context accept-all` (adopt the canonical conception) followed by `/context install`.
25
+ </context_init>
26
+
27
+ <context_accept_all>
28
+ `/context accept-all` adopts the package's canonical packaged conception (`samples/conception.json`) as the substrate's `config.json` — the full vocabulary (`block_kinds`, `relation_types`, `lenses`, `invariants`) plus the `installed_schemas` / `installed_blocks` manifest — with `root` set to the actual substrate dir. It writes `config.json` only (run `/context install` after to materialize the schemas + starter blocks) and is idempotent: it never overwrites an existing `config.json` (offer, don't impose). This is the accept-all path; per-entry step-through curation is a separate surface.
29
+ </context_accept_all>
30
+
31
+ <context_install>
32
+ `/context install` reconciles the substrate against the `installed_schemas` and `installed_blocks` lists declared in `config.json`. For each declared name it copies the matching asset from the package-shipped samples catalog (`samples/schemas/` for schemas, `samples/blocks/` for starter blocks) into the substrate. Default behavior is skip-if-exists (preserves user edits); pass `--update` to overwrite and report the asset as `updated`. Sources missing from the catalog are reported as `notFound`. Empty install lists are not an error — the result is a clean no-op message instructing the user to edit `config.json`.
33
+
34
+ The installable catalog IS the packaged conception (`samples/conception.json`): its `block_kinds` enumerate the available kinds, each carrying its schema (`samples/schemas/`) and starter block (`samples/blocks/`). The generated installable-catalog table below lists the authoritative names — declare any subset in `installed_*` and run `/context install`, or take the whole conception via `/context accept-all`.
35
+ </context_install>
36
+
37
+ <substrate_config>
38
+ `.project/config.json` is the substrate bootstrap. Its `root` field declares where every other block, schema, agent, and template lives — closing the GitHub #3 surface where downstream consumers had to assume `.project/`. `naming` aliases canonical block ids to display names (used by `/context view` rendering). `hierarchy` declares legal closure-table edges (parent block → child block via relation_type). `lenses` declares named projections over a target block. `installed_schemas` / `installed_blocks` are the install manifest consumed by `/context install`.
39
+
40
+ `config.json` and `relations.json` are exempt from `root` redirection — they always live at `.project/` because they are the substrate that defines `root`. All other state lives under `<config.root>/...` per `resolveContextDir(cwd)`. The package ships their schemas in `schemas/` (config.schema.json, relations.schema.json) and resolves them via three-tier search: project override > user override > package-shipped.
41
+
42
+ The `loadContext(cwd)` SDK returns an mtime-keyed cached snapshot of `{ config, relations, configMtime, relationsMtime }` for one cwd. Consumers must not mutate.
43
+ </substrate_config>
44
+
45
+ <lens_views>
46
+ Lenses are named projections over a target block. A lens declares `id`, `target` (block name), `relation_type`, `derived_from_field` (optional — synthesizes edges from a per-item field instead of requiring authored edges), `bins` (named groupings), and `render_uncategorized`.
47
+
48
+ Edges live in `.project/relations.json` as a closure table — each row is `{ parent, child, relation_type }`. `parent` is either a canonical id (hierarchy edges) or a lens.bins value (lens edges); disambiguation lives in `validateRelations`.
49
+
50
+ The lens-view algorithm: `edgesForLens(lens, items, authoredEdges)` returns synthetic edges (when `derived_from_field` is set) or filtered authored edges (otherwise). `groupByLens(items, lens, lensEdges)` produces a `Map<binName, ItemRecord[]>`. `walkDescendants(parentId, relationType, edges)` traverses the closure table from any parent.
51
+
52
+ `/context view <lensId>` loads the lens via `loadLensView(cwd, lensId)`, runs `groupByLens`, and renders the result as markdown headings + bullet lines (id + status + title) into the conversation via `renderLensView`. `lens.render_uncategorized: false` omits the uncategorized bucket.
53
+ </lens_views>
54
+
55
+ <context_lens_curate>
56
+ `/context lens-curate <lensId>` walks items in the lens's target block that have no edge in any declared bin and surfaces bin-assignment suggestions (would-be `relations.json` edge appends) as a follow-up turn. The LLM reads the suggestions and persists the chosen edges via `append-block-item` against `relations.json`. The command itself does not write — curation is a follow-up-turn pattern so the model decides which suggestions to enact.
57
+ </context_lens_curate>
58
+
59
+ <context_view>
60
+ `/context view <lensId>` renders a configured lens as markdown into the conversation. Bins become headings, items become bullet lines (id + status + title where present). `naming` aliases from config.json are honored for the target block name. Errors (missing config, unknown lens, unreadable target block, no array property in target) surface via `ctx.ui.notify` with severity `error`.
61
+ </context_view>
62
+
63
+ <substrate_validation>
64
+ `validateRelations(cwd)` (exposed as the `context-validate-relations` tool) checks the closure-table edges in `relations.json` against the config + per-block item snapshots. Diagnostics codes: `edge_unknown_relation_type`, `edge_parent_not_in_bins`, `edge_unresolved_parent`, `edge_unresolved_child`, `edge_parent_wrong_block`, `edge_child_wrong_block`, `edge_cycle_detected`. Returns `{ status: "clean" | "warnings" | "invalid", issues[] }` where each issue carries the offending edge or cycle path.
65
+
66
+ Two derived substrate tools complement validation: `context-edges-for-lens` returns the materialized `Edge[]` for a named lens (synthetic from `derived_from_field` or filtered authored edges); `context-walk-descendants` returns the transitive descendant id list from a parent under a given relation_type.
67
+ </substrate_validation>
68
+
69
+ <block_item_reads>
70
+ Item-level reads complement whole-block `read-block` (which is all-or-nothing and caps at the 50KB read limit): `read-block-item` returns one item from a named block by its id (block-scoped — null if absent; distinct from `resolve-item-by-id`, which searches every block by kind-prefixed id). `read-block-page` paginates a block too large to fetch whole — `{ items, total, hasMore }` where `total` is the full item count and pagination uses `offset`/`limit` (defaults 0/50).
71
+ `join-blocks` — one-call cross-block join, EDGE mode (relations.json relation_type, leftEndpoint parent|child) or FIELD mode (shared field value), optional left pre-filter; returns {left, right[]} pairs; replaces N+1 read+resolve.
72
+ </block_item_reads>
73
+
74
+ <context_status>
75
+ `/context status` derives project state dynamically from the filesystem:
76
+ - Source file count and line count (`.ts` files excluding tests)
77
+ - Test count and test file count
78
+ - Schema count, block count, phase count
79
+ - Block summaries with array item counts and status distributions
80
+ - Requirements summary (total, by status, by priority) — from requirements.json (if installed)
81
+ - Tasks summary (total, by status) — from tasks.json (if installed)
82
+ - Domain entry count — from domain.json (if installed)
83
+ - Verification summary (total, passed, failed) — from verification.json (if installed)
84
+ - Handoff presence — whether handoff.json exists
85
+ - Recent git commits
86
+ - Current phase detection
87
+ </context_status>
88
+
89
+ <context_add_work>
90
+ `/context add-work` discovers appendable blocks (blocks with array schemas), reads their schemas, and sends a structured instruction to the LLM to extract items from the conversation into the typed blocks. This is a follow-up message that triggers the LLM to use the `append-block-item` tool.
91
+ </context_add_work>
92
+
93
+ <duplicate_detection>
94
+ `append-block-item` checks for duplicate items by `id` field before appending. If an item with the same `id` already exists in the target array, it returns a message instead of appending.
95
+ </duplicate_detection>
96
+
97
+ <context_validate>
98
+ Two separate validators address two concerns:
99
+
100
+ `/context validate` (the `context-validate` tool) checks cross-block referential integrity:
101
+ - task.phase references a valid phase
102
+ - task.depends_on references valid task IDs
103
+ - decision.phase references a valid phase
104
+ - gap.resolved_by references a valid ID
105
+ - requirement.traces_to references valid phase/task IDs
106
+ - requirement.depends_on references valid requirement IDs
107
+ - verification.target references a valid target ID
108
+ - rationale.related_decisions references valid decision IDs
109
+
110
+ Returns errors (broken dependency references) and warnings (unresolved cross-references).
111
+
112
+ The `context-validate-relations` tool (see `<substrate_validation>`) validates closure-table edges in `relations.json` — a separate concern from cross-block ID resolution.
113
+ </context_validate>
114
+
115
+ <update_check>
116
+ On `session_start`, checks npm registry for newer versions of `@davidorex/pi-project-workflows` and notifies via UI if an update is available. Non-blocking — failures are silently ignored.
117
+ </update_check>
118
+
119
+ <success_criteria>
120
+ - `.project/`, `.project/schemas/`, `.project/phases/`, and `.project/config.json` exist after `/context init`
121
+ - `installed_schemas` / `installed_blocks` declared in `config.json` are reified by `/context install`; `--update` overwrites
122
+ - Block writes validate against schemas — invalid data rejected with specific error
123
+ - `/context status` returns current derived state without errors
124
+ - `/context validate` returns no errors for well-formed cross-block references
125
+ - `context-validate-relations` returns no errors for a well-formed `relations.json`
126
+ - `/context view <lensId>` renders the configured projection; `/context lens-curate <lensId>` surfaces suggestions for uncategorized items
127
+ - `append-block-item` rejects duplicate IDs
128
+ - Schema customizations (field additions, enum changes) take effect on next write
129
+ - A relocated `config.root` reaches every read/write because all path construction routes through `resolveContextDir(cwd)`
130
+ </success_criteria>