@davidorex/pi-context 0.26.0 → 0.28.1

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 (123) hide show
  1. package/CHANGELOG.md +71 -15
  2. package/README.md +71 -18
  3. package/dist/block-api.d.ts +165 -18
  4. package/dist/block-api.d.ts.map +1 -1
  5. package/dist/block-api.js +800 -57
  6. package/dist/block-api.js.map +1 -1
  7. package/dist/citation-rot-scanner.d.ts +80 -0
  8. package/dist/citation-rot-scanner.d.ts.map +1 -0
  9. package/dist/citation-rot-scanner.js +421 -0
  10. package/dist/citation-rot-scanner.js.map +1 -0
  11. package/dist/content-hash.d.ts +25 -0
  12. package/dist/content-hash.d.ts.map +1 -0
  13. package/dist/content-hash.js +62 -0
  14. package/dist/content-hash.js.map +1 -0
  15. package/dist/context-dir.d.ts +126 -1
  16. package/dist/context-dir.d.ts.map +1 -1
  17. package/dist/context-dir.js +208 -4
  18. package/dist/context-dir.js.map +1 -1
  19. package/dist/context-registry.d.ts +122 -0
  20. package/dist/context-registry.d.ts.map +1 -0
  21. package/dist/context-registry.js +189 -0
  22. package/dist/context-registry.js.map +1 -0
  23. package/dist/context-sdk.d.ts +179 -12
  24. package/dist/context-sdk.d.ts.map +1 -1
  25. package/dist/context-sdk.js +536 -101
  26. package/dist/context-sdk.js.map +1 -1
  27. package/dist/context.d.ts +222 -4
  28. package/dist/context.d.ts.map +1 -1
  29. package/dist/context.js +244 -56
  30. package/dist/context.js.map +1 -1
  31. package/dist/git-env.d.ts +2 -0
  32. package/dist/git-env.d.ts.map +1 -0
  33. package/dist/git-env.js +29 -0
  34. package/dist/git-env.js.map +1 -0
  35. package/dist/index.d.ts +124 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +440 -1125
  38. package/dist/index.js.map +1 -1
  39. package/dist/lens-view.d.ts.map +1 -1
  40. package/dist/lens-view.js +5 -3
  41. package/dist/lens-view.js.map +1 -1
  42. package/dist/migration-registry-loader.d.ts +121 -0
  43. package/dist/migration-registry-loader.d.ts.map +1 -0
  44. package/dist/migration-registry-loader.js +309 -0
  45. package/dist/migration-registry-loader.js.map +1 -0
  46. package/dist/migrations-store.d.ts +141 -0
  47. package/dist/migrations-store.d.ts.map +1 -0
  48. package/dist/migrations-store.js +206 -0
  49. package/dist/migrations-store.js.map +1 -0
  50. package/dist/object-store.d.ts +26 -0
  51. package/dist/object-store.d.ts.map +1 -0
  52. package/dist/object-store.js +104 -0
  53. package/dist/object-store.js.map +1 -0
  54. package/dist/ops-registry.d.ts +44 -0
  55. package/dist/ops-registry.d.ts.map +1 -0
  56. package/dist/ops-registry.js +1223 -0
  57. package/dist/ops-registry.js.map +1 -0
  58. package/dist/orientation.d.ts +16 -0
  59. package/dist/orientation.d.ts.map +1 -0
  60. package/dist/orientation.js +49 -0
  61. package/dist/orientation.js.map +1 -0
  62. package/dist/promote-item.d.ts +28 -0
  63. package/dist/promote-item.d.ts.map +1 -0
  64. package/dist/promote-item.js +257 -0
  65. package/dist/promote-item.js.map +1 -0
  66. package/dist/read-element.d.ts +129 -0
  67. package/dist/read-element.d.ts.map +1 -0
  68. package/dist/read-element.js +296 -0
  69. package/dist/read-element.js.map +1 -0
  70. package/dist/rename-canonical-id.d.ts.map +1 -1
  71. package/dist/rename-canonical-id.js +22 -13
  72. package/dist/rename-canonical-id.js.map +1 -1
  73. package/dist/roadmap-plan.d.ts.map +1 -1
  74. package/dist/roadmap-plan.js +23 -13
  75. package/dist/roadmap-plan.js.map +1 -1
  76. package/dist/schema-validator.d.ts +1 -0
  77. package/dist/schema-validator.d.ts.map +1 -1
  78. package/dist/schema-validator.js +7 -3
  79. package/dist/schema-validator.js.map +1 -1
  80. package/dist/schema-write.d.ts +78 -4
  81. package/dist/schema-write.d.ts.map +1 -1
  82. package/dist/schema-write.js +297 -34
  83. package/dist/schema-write.js.map +1 -1
  84. package/dist/status-vocab.d.ts +1 -0
  85. package/dist/status-vocab.d.ts.map +1 -1
  86. package/dist/status-vocab.js +4 -0
  87. package/dist/status-vocab.js.map +1 -1
  88. package/dist/truncate.d.ts +34 -0
  89. package/dist/truncate.d.ts.map +1 -0
  90. package/dist/truncate.js +85 -0
  91. package/dist/truncate.js.map +1 -0
  92. package/dist/write-schema-migration-tool.d.ts +46 -0
  93. package/dist/write-schema-migration-tool.d.ts.map +1 -0
  94. package/dist/write-schema-migration-tool.js +115 -0
  95. package/dist/write-schema-migration-tool.js.map +1 -0
  96. package/package.json +35 -2
  97. package/samples/blocks/work-orders.json +3 -0
  98. package/samples/conception.json +22 -3
  99. package/samples/migrations.json +133 -0
  100. package/samples/schemas/context-contracts.schema.json +19 -4
  101. package/samples/schemas/conventions.schema.json +68 -12
  102. package/samples/schemas/decisions.schema.json +125 -28
  103. package/samples/schemas/features.schema.json +140 -26
  104. package/samples/schemas/framework-gaps.schema.json +171 -26
  105. package/samples/schemas/issues.schema.json +86 -13
  106. package/samples/schemas/layer-plans.schema.json +195 -35
  107. package/samples/schemas/phase.schema.json +18 -3
  108. package/samples/schemas/rationale.schema.json +37 -7
  109. package/samples/schemas/requirements.schema.json +77 -10
  110. package/samples/schemas/research.schema.json +169 -36
  111. package/samples/schemas/spec-reviews.schema.json +88 -18
  112. package/samples/schemas/story.schema.json +18 -3
  113. package/samples/schemas/tasks.schema.json +66 -10
  114. package/samples/schemas/verification.schema.json +70 -12
  115. package/samples/schemas/work-orders.schema.json +195 -0
  116. package/schemas/bootstrap.schema.json +18 -6
  117. package/schemas/config.schema.json +58 -26
  118. package/schemas/context-registry.schema.json +46 -0
  119. package/schemas/migrations.schema.json +123 -0
  120. package/schemas/relations.schema.json +41 -10
  121. package/skill-narrative.md +42 -21
  122. package/skills/pi-context/SKILL.md +159 -65
  123. package/skills/pi-context/references/bundled-resources.md +7 -2
@@ -1,54 +1,125 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "pi-context://schemas/layer-plans",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "title": "Layer Restructure Plans",
6
6
  "description": "Plans for restructuring the .project/ directory from flat-block storage into a layered artifact-ownership model. Each plan names its conceptual source (e.g. Muni five-layer), describes each layer's purpose and its current-vs-target block mapping, and sequences the enactment into phases with exit criteria. Layer plans are themselves L2 specification-layer artifacts — they are spec-level decisions about how the framework organizes project state.",
7
7
  "type": "object",
8
- "required": ["plans"],
8
+ "required": [
9
+ "plans"
10
+ ],
9
11
  "properties": {
10
12
  "plans": {
11
13
  "type": "array",
12
14
  "items": {
13
15
  "type": "object",
14
16
  "additionalProperties": false,
15
- "required": ["id", "title", "status", "model", "layers", "migration_phases", "created_by", "created_at"],
17
+ "required": [
18
+ "id",
19
+ "title",
20
+ "status",
21
+ "model",
22
+ "layers",
23
+ "migration_phases",
24
+ "created_by",
25
+ "created_at"
26
+ ],
16
27
  "properties": {
17
- "id": { "type": "string", "pattern": "^PLAN-\\d{3}$" },
18
- "title": { "type": "string" },
28
+ "id": {
29
+ "type": "string",
30
+ "pattern": "^PLAN-\\d{3}$"
31
+ },
32
+ "title": {
33
+ "type": "string"
34
+ },
19
35
  "status": {
20
36
  "type": "string",
21
- "enum": ["draft", "proposed", "decided", "in-progress", "complete", "abandoned"]
37
+ "enum": [
38
+ "draft",
39
+ "proposed",
40
+ "decided",
41
+ "in-progress",
42
+ "complete",
43
+ "abandoned"
44
+ ]
45
+ },
46
+ "model": {
47
+ "type": "string",
48
+ "description": "Conceptual source — e.g. 'Muni five-layer'"
49
+ },
50
+ "description": {
51
+ "type": "string",
52
+ "x-prompt-budget": {
53
+ "tokens": 1000,
54
+ "words": 800
55
+ }
22
56
  },
23
- "model": { "type": "string", "description": "Conceptual source — e.g. 'Muni five-layer'" },
24
- "description": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
25
57
  "layers": {
26
58
  "type": "array",
27
59
  "items": {
28
60
  "type": "object",
29
61
  "additionalProperties": false,
30
- "required": ["id", "name", "purpose", "current_blocks", "target_blocks"],
62
+ "required": [
63
+ "id",
64
+ "name",
65
+ "purpose",
66
+ "current_blocks",
67
+ "target_blocks"
68
+ ],
31
69
  "properties": {
32
- "id": { "type": "string" },
33
- "name": { "type": "string" },
34
- "purpose": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
35
- "canonical_vocabulary": { "type": "string" },
70
+ "id": {
71
+ "type": "string"
72
+ },
73
+ "name": {
74
+ "type": "string"
75
+ },
76
+ "purpose": {
77
+ "type": "string",
78
+ "x-prompt-budget": {
79
+ "tokens": 1000,
80
+ "words": 800
81
+ }
82
+ },
83
+ "canonical_vocabulary": {
84
+ "type": "string"
85
+ },
36
86
  "lifetime": {
37
87
  "type": "string",
38
88
  "description": "Cadence at which this layer changes — project-lifetime, per-design-cycle, per-iteration, per-session, ever-accumulating"
39
89
  },
40
- "ownership_principle": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
41
- "current_blocks": { "type": "array", "items": { "type": "string" } },
90
+ "ownership_principle": {
91
+ "type": "string",
92
+ "x-prompt-budget": {
93
+ "tokens": 1000,
94
+ "words": 800
95
+ }
96
+ },
97
+ "current_blocks": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "string"
101
+ }
102
+ },
42
103
  "target_blocks": {
43
104
  "type": "array",
44
105
  "items": {
45
106
  "type": "object",
46
107
  "additionalProperties": false,
47
- "required": ["name", "shape"],
108
+ "required": [
109
+ "name",
110
+ "shape"
111
+ ],
48
112
  "properties": {
49
- "name": { "type": "string" },
50
- "shape": { "type": "string", "description": "flat | nested | materialized-view" },
51
- "notes": { "type": "string" }
113
+ "name": {
114
+ "type": "string"
115
+ },
116
+ "shape": {
117
+ "type": "string",
118
+ "description": "flat | nested | materialized-view"
119
+ },
120
+ "notes": {
121
+ "type": "string"
122
+ }
52
123
  }
53
124
  }
54
125
  }
@@ -60,33 +131,122 @@
60
131
  "items": {
61
132
  "type": "object",
62
133
  "additionalProperties": false,
63
- "required": ["id", "name", "description", "depends_on", "exit_criteria"],
134
+ "required": [
135
+ "id",
136
+ "name",
137
+ "description",
138
+ "depends_on",
139
+ "exit_criteria"
140
+ ],
64
141
  "properties": {
65
- "id": { "type": "string" },
66
- "name": { "type": "string" },
67
- "description": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
68
- "depends_on": { "type": "array", "items": { "type": "string" } },
69
- "exit_criteria": { "type": "array", "items": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } } },
70
- "produces": { "type": "array", "items": { "type": "string" } },
71
- "status": { "type": "string", "enum": ["pending", "in-progress", "complete", "blocked"] }
142
+ "id": {
143
+ "type": "string"
144
+ },
145
+ "name": {
146
+ "type": "string"
147
+ },
148
+ "description": {
149
+ "type": "string",
150
+ "x-prompt-budget": {
151
+ "tokens": 1000,
152
+ "words": 800
153
+ }
154
+ },
155
+ "depends_on": {
156
+ "type": "array",
157
+ "items": {
158
+ "type": "string"
159
+ }
160
+ },
161
+ "exit_criteria": {
162
+ "type": "array",
163
+ "items": {
164
+ "type": "string",
165
+ "x-prompt-budget": {
166
+ "tokens": 1000,
167
+ "words": 800
168
+ }
169
+ }
170
+ },
171
+ "produces": {
172
+ "type": "array",
173
+ "items": {
174
+ "type": "string"
175
+ }
176
+ },
177
+ "status": {
178
+ "type": "string",
179
+ "enum": [
180
+ "pending",
181
+ "in-progress",
182
+ "complete",
183
+ "blocked"
184
+ ]
185
+ }
72
186
  }
73
187
  }
74
188
  },
75
- "created_by": { "type": "string" },
76
- "created_at": { "type": "string" }
189
+ "created_by": {
190
+ "type": "string"
191
+ },
192
+ "created_at": {
193
+ "type": "string"
194
+ },
195
+ "oid": {
196
+ "type": "string",
197
+ "pattern": "^[0-9a-f]{32}$",
198
+ "description": "Content-independent substrate-stable item identity (content-addressed substrate identity, Cycle 3). Minted once at item birth via mintOid(substrate_id); immutable across content versions. Optional in the schema so pre-Cycle-3 items validate; stamped on next write."
199
+ },
200
+ "content_hash": {
201
+ "type": "string",
202
+ "pattern": "^[0-9a-f]{64}$",
203
+ "description": "SHA-256 (hex) of the RFC-8785-canonical content projection of this item (metadata fields excluded). Recomputed on every stamping write; moves with content, stable across metadata-only churn."
204
+ },
205
+ "content_parent": {
206
+ "type": "string",
207
+ "pattern": "^[0-9a-f]{64}$",
208
+ "description": "content_hash of the immediately-prior version of this item; set on a content-changing update, absent on the first (v1) version. Forms the per-item content version chain."
209
+ }
77
210
  }
78
211
  }
79
212
  }
80
213
  },
81
214
  "x-lifecycle": {
82
215
  "field": "status",
83
- "states": ["draft", "proposed", "decided", "in-progress", "complete", "abandoned"],
216
+ "states": [
217
+ "draft",
218
+ "proposed",
219
+ "decided",
220
+ "in-progress",
221
+ "complete",
222
+ "abandoned"
223
+ ],
84
224
  "transitions": [
85
- { "from": "draft", "to": "proposed", "required_authority": "any" },
86
- { "from": "proposed", "to": "decided", "required_authority": "user" },
87
- { "from": "decided", "to": "in-progress", "required_authority": "any" },
88
- { "from": "in-progress", "to": "complete", "required_authority": "any" },
89
- { "from": "draft", "to": "abandoned", "required_authority": "user" }
225
+ {
226
+ "from": "draft",
227
+ "to": "proposed",
228
+ "required_authority": "any"
229
+ },
230
+ {
231
+ "from": "proposed",
232
+ "to": "decided",
233
+ "required_authority": "user"
234
+ },
235
+ {
236
+ "from": "decided",
237
+ "to": "in-progress",
238
+ "required_authority": "any"
239
+ },
240
+ {
241
+ "from": "in-progress",
242
+ "to": "complete",
243
+ "required_authority": "any"
244
+ },
245
+ {
246
+ "from": "draft",
247
+ "to": "abandoned",
248
+ "required_authority": "user"
249
+ }
90
250
  ]
91
251
  }
92
252
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "pi-context://schemas/phase",
4
- "version": "2.0.0",
4
+ "version": "2.0.1",
5
5
  "title": "Phases",
6
- "description": "Array-block of planned implementation phases (DEC-0028). Each phase carries a PHASE-NNN string id, intent, optional goal, success criteria, decomposed specs, and status lifecycle (planned, in-progress, completed). Inter-phase dependencies live in relations.json edges, not inline.",
6
+ "description": "Array-block of planned implementation phases. Each phase carries a PHASE-NNN string id, intent, optional goal, success criteria, decomposed specs, and status lifecycle (planned, in-progress, completed). Inter-phase dependencies live in relations.json edges, not inline.",
7
7
  "type": "object",
8
8
  "required": [
9
9
  "phases"
@@ -25,7 +25,7 @@
25
25
  "id": {
26
26
  "type": "string",
27
27
  "pattern": "^PHASE-[A-Z0-9-]+$",
28
- "description": "Canonical phase id (e.g. PHASE-001). Replaces the integer `number` discriminator of the file-per-phase form."
28
+ "description": "Canonical phase id (e.g.). Replaces the integer `number` discriminator of the file-per-phase form."
29
29
  },
30
30
  "name": {
31
31
  "type": "string"
@@ -110,6 +110,21 @@
110
110
  "items": {
111
111
  "type": "string"
112
112
  }
113
+ },
114
+ "oid": {
115
+ "type": "string",
116
+ "pattern": "^[0-9a-f]{32}$",
117
+ "description": "Content-independent substrate-stable item identity (content-addressed substrate identity, Cycle 3). Minted once at item birth via mintOid(substrate_id); immutable across content versions. Optional in the schema so pre-Cycle-3 items validate; stamped on next write."
118
+ },
119
+ "content_hash": {
120
+ "type": "string",
121
+ "pattern": "^[0-9a-f]{64}$",
122
+ "description": "SHA-256 (hex) of the RFC-8785-canonical content projection of this item (metadata fields excluded). Recomputed on every stamping write; moves with content, stable across metadata-only churn."
123
+ },
124
+ "content_parent": {
125
+ "type": "string",
126
+ "pattern": "^[0-9a-f]{64}$",
127
+ "description": "content_hash of the immediately-prior version of this item; set on a content-changing update, absent on the first (v1) version. Forms the per-item content version chain."
113
128
  }
114
129
  }
115
130
  }
@@ -1,22 +1,52 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "pi-context://schemas/rationale",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "title": "Design Rationale",
6
6
  "description": "The deeper reasoning behind decisions — why this approach over others, what constraints drove the choice, what tradeoffs were accepted.",
7
7
  "type": "object",
8
- "required": ["rationales"],
8
+ "required": [
9
+ "rationales"
10
+ ],
9
11
  "properties": {
10
12
  "rationales": {
11
13
  "type": "array",
12
14
  "items": {
13
15
  "type": "object",
14
- "required": ["id", "title", "narrative"],
16
+ "required": [
17
+ "id",
18
+ "title",
19
+ "narrative"
20
+ ],
15
21
  "properties": {
16
- "id": { "type": "string", "pattern": "^RAT-\\d{3}$" },
17
- "title": { "type": "string" },
18
- "narrative": { "type": "string" },
19
- "phase": { "type": "integer" }
22
+ "id": {
23
+ "type": "string",
24
+ "pattern": "^RAT-\\d{3}$"
25
+ },
26
+ "title": {
27
+ "type": "string"
28
+ },
29
+ "narrative": {
30
+ "type": "string"
31
+ },
32
+ "phase": {
33
+ "type": "integer"
34
+ },
35
+ "oid": {
36
+ "type": "string",
37
+ "pattern": "^[0-9a-f]{32}$",
38
+ "description": "Content-independent substrate-stable item identity (content-addressed substrate identity, Cycle 3). Minted once at item birth via mintOid(substrate_id); immutable across content versions. Optional in the schema so pre-Cycle-3 items validate; stamped on next write."
39
+ },
40
+ "content_hash": {
41
+ "type": "string",
42
+ "pattern": "^[0-9a-f]{64}$",
43
+ "description": "SHA-256 (hex) of the RFC-8785-canonical content projection of this item (metadata fields excluded). Recomputed on every stamping write; moves with content, stable across metadata-only churn."
44
+ },
45
+ "content_parent": {
46
+ "type": "string",
47
+ "pattern": "^[0-9a-f]{64}$",
48
+ "description": "content_hash of the immediately-prior version of this item; set on a content-changing update, absent on the first (v1) version. Forms the per-item content version chain."
49
+ }
20
50
  }
21
51
  }
22
52
  }
@@ -1,31 +1,98 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "pi-context://schemas/requirements",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "title": "Requirements",
6
6
  "description": "What the project must deliver. Functional and non-functional requirements with MoSCoW priority and lifecycle states (proposed, accepted, implemented, verified). The contract between intent and implementation.",
7
7
  "type": "object",
8
- "required": ["requirements"],
8
+ "required": [
9
+ "requirements"
10
+ ],
9
11
  "properties": {
10
12
  "requirements": {
11
13
  "type": "array",
12
14
  "items": {
13
15
  "type": "object",
14
- "required": ["id", "description", "type", "status", "priority"],
16
+ "required": [
17
+ "id",
18
+ "description",
19
+ "type",
20
+ "status",
21
+ "priority"
22
+ ],
15
23
  "properties": {
16
- "id": { "type": "string", "pattern": "^REQ-\\d{3,}$" },
17
- "description": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } },
18
- "type": { "type": "string", "enum": ["functional", "non-functional", "constraint", "integration"] },
19
- "status": { "type": "string", "enum": ["proposed", "accepted", "deferred", "implemented", "verified"] },
20
- "priority": { "type": "string", "enum": ["must", "should", "could", "wont"] },
24
+ "id": {
25
+ "type": "string",
26
+ "pattern": "^REQ-\\d{3,}$"
27
+ },
28
+ "description": {
29
+ "type": "string",
30
+ "x-prompt-budget": {
31
+ "tokens": 1000,
32
+ "words": 800
33
+ }
34
+ },
35
+ "type": {
36
+ "type": "string",
37
+ "enum": [
38
+ "functional",
39
+ "non-functional",
40
+ "constraint",
41
+ "integration"
42
+ ]
43
+ },
44
+ "status": {
45
+ "type": "string",
46
+ "enum": [
47
+ "proposed",
48
+ "accepted",
49
+ "deferred",
50
+ "implemented",
51
+ "verified"
52
+ ]
53
+ },
54
+ "priority": {
55
+ "type": "string",
56
+ "enum": [
57
+ "must",
58
+ "should",
59
+ "could",
60
+ "wont"
61
+ ]
62
+ },
21
63
  "acceptance_criteria": {
22
64
  "type": "array",
23
- "items": { "type": "string", "x-prompt-budget": { "tokens": 1000, "words": 800 } }
65
+ "items": {
66
+ "type": "string",
67
+ "x-prompt-budget": {
68
+ "tokens": 1000,
69
+ "words": 800
70
+ }
71
+ }
24
72
  },
25
73
  "source": {
26
74
  "type": "string",
27
- "enum": ["human", "agent", "analysis"],
75
+ "enum": [
76
+ "human",
77
+ "agent",
78
+ "analysis"
79
+ ],
28
80
  "description": "How this requirement was created"
81
+ },
82
+ "oid": {
83
+ "type": "string",
84
+ "pattern": "^[0-9a-f]{32}$",
85
+ "description": "Content-independent substrate-stable item identity (content-addressed substrate identity, Cycle 3). Minted once at item birth via mintOid(substrate_id); immutable across content versions. Optional in the schema so pre-Cycle-3 items validate; stamped on next write."
86
+ },
87
+ "content_hash": {
88
+ "type": "string",
89
+ "pattern": "^[0-9a-f]{64}$",
90
+ "description": "SHA-256 (hex) of the RFC-8785-canonical content projection of this item (metadata fields excluded). Recomputed on every stamping write; moves with content, stable across metadata-only churn."
91
+ },
92
+ "content_parent": {
93
+ "type": "string",
94
+ "pattern": "^[0-9a-f]{64}$",
95
+ "description": "content_hash of the immediately-prior version of this item; set on a content-changing update, absent on the first (v1) version. Forms the per-item content version chain."
29
96
  }
30
97
  }
31
98
  }