@quolu/lattice 0.36.0 → 0.37.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/README.ja.md +14 -0
- package/README.md +9 -0
- package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +312 -0
- package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -0
- package/docs/schemas/lattice.todo_revision.v2.schema.json +220 -0
- package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -0
- package/package.json +5 -1
- package/src/cli-help.mjs +10 -1
- package/src/todo-cli.mjs +132 -4
- package/src/todo-contracts.mjs +14 -3
- package/src/todo-store.mjs +40 -12
package/README.ja.md
CHANGED
|
@@ -213,6 +213,20 @@ lattice todo phase review --plan <key> --phase terminal-audit --reason <text>
|
|
|
213
213
|
lattice todo phase accept --plan <key> --phase terminal-audit --input <file>
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
**過去の工程は監査できません。** 監査対象のコードが既に変化しているためです。そこで
|
|
217
|
+
「監査なしで閉じた」という状態を別に持ちます——`accepted`(監査を通った)へは絶対に化けず、
|
|
218
|
+
記録は永久に区別できます。工程図では畳まれるので監査待ちの札は外れます。
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
lattice todo phase close-unaudited --plan <key> --phase terminal-audit --reason <text>
|
|
222
|
+
lattice todo phase baseline --reason <text> --except <監査したいplan_key> # 一括
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
一括の入口は、現在監査待ちで一度も監査に触れていないPhaseだけを対象にします。**自動では
|
|
226
|
+
実行しません**——どれを監査し、どれを歴史として畳むかは人が決め、Latticeは宣言を受け取って
|
|
227
|
+
記録するだけです。`--except`は「最近の作業でコードも生きているので本当に監査したい」planを
|
|
228
|
+
残すための口です。規約は[ADR 0148](docs/adr/0148-history-closes-unaudited-not-audited.md)が正です。
|
|
229
|
+
|
|
216
230
|
誤ってphase無しで作ったplanへ後からPhaseを被せる場合は、`revise-phase`の
|
|
217
231
|
`state_policy: acquire_phase`でdone状態を保ったまま獲得できます(未割当→割当の向きだけを許し、
|
|
218
232
|
既にphaseを持つtaskの付け替えは拒否します)。
|
package/README.md
CHANGED
|
@@ -158,6 +158,15 @@ rejected over it; the requirement is reported instead. And the audit gate never
|
|
|
158
158
|
phases order reviews, the ToDo DAG orders work
|
|
159
159
|
([ADR 0147](docs/adr/0147-audit-is-on-by-default.md)).
|
|
160
160
|
|
|
161
|
+
**History closes unaudited, never audited.** Work that finished long ago cannot be audited — the
|
|
162
|
+
code under review has already moved. Demanding an audit there produces either a false finding
|
|
163
|
+
(pointing at a later, intentional change) or a rubber stamp. So there is a third terminal state,
|
|
164
|
+
`closed_unaudited`: recorded with a reason, folded away like finished work, and **structurally
|
|
165
|
+
incapable of passing as `accepted`** — phase-accept dependencies unlock on `accepted` alone. The
|
|
166
|
+
bulk entry point never runs by itself, and the machine never infers "old enough to skip"; a human
|
|
167
|
+
decides what gets audited and what becomes history
|
|
168
|
+
([ADR 0148](docs/adr/0148-history-closes-unaudited-not-audited.md)).
|
|
169
|
+
|
|
161
170
|
## Patent
|
|
162
171
|
|
|
163
172
|
The design in this repository is the subject of a Japanese patent application:
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.phase_todo_revision.v3.schema.json",
|
|
4
|
+
"title": "lattice.phase_todo_revision.v3",
|
|
5
|
+
"description": "`lattice todo revise-phase`が受理する最新契約。v1/v2のphase-audited desired state・task/phase migrationへ、runtime task migration・source inventory・per-ToDo source cutover batchを加えた12 keyの完全契約。",
|
|
6
|
+
"$comment": "Runtime validation (src/todo-revision.mjs validatePhaseTodoRevision) additionally enforces: canonical JSON+LF bytes; strict sort order on task_migration (by from_task_id), runtime_task_migration.entries (by predecessor_task_id), runtime_task_migration.entries[].successor_task_ids/evidence_digests, and source_inventory.excluded_tombstones/source_cutover_batch.operations (by source_ref); uniqueness constraints across all of those; desired_plan.plan_version === phaseTodoRevisionPlanVersion(...); runtime_task_migration projects onto task_migration exactly (validRuntimeTodoProjection) and its successor_task_ids cover exactly desired_plan.tasks; phase_migration's target phase set equals desired_plan.phases exactly; every reconciliation.*_digest field against its own recomputation; and revision_digest as the canonical self-digest over every other top-level field.",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": [
|
|
10
|
+
"schema", "project_id", "plan_key", "predecessor", "desired_plan",
|
|
11
|
+
"runtime_task_migration", "task_migration", "phase_migration", "source_inventory",
|
|
12
|
+
"reconciliation", "source_cutover_batch", "revision_digest"
|
|
13
|
+
],
|
|
14
|
+
"properties": {
|
|
15
|
+
"schema": { "const": "lattice.phase_todo_revision.v3" },
|
|
16
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
17
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
18
|
+
"predecessor": { "$ref": "#/$defs/predecessor" },
|
|
19
|
+
"desired_plan": { "$ref": "#/$defs/desiredPlanV5" },
|
|
20
|
+
"runtime_task_migration": { "$ref": "#/$defs/runtimeTaskMigration" },
|
|
21
|
+
"task_migration": {
|
|
22
|
+
"type": "array", "minItems": 1, "maxItems": 512,
|
|
23
|
+
"items": { "$ref": "#/$defs/taskMigrationEntry" }
|
|
24
|
+
},
|
|
25
|
+
"phase_migration": {
|
|
26
|
+
"type": "array", "minItems": 1, "maxItems": 512,
|
|
27
|
+
"items": { "$ref": "#/$defs/phaseMigrationEntry" }
|
|
28
|
+
},
|
|
29
|
+
"source_inventory": { "$ref": "#/$defs/sourceInventory" },
|
|
30
|
+
"reconciliation": {
|
|
31
|
+
"type": "object", "additionalProperties": false,
|
|
32
|
+
"required": [
|
|
33
|
+
"predecessor_reconciliation_digest", "source_inventory_digest", "desired_plan_digest",
|
|
34
|
+
"runtime_task_migration_digest", "task_migration_digest", "phase_migration_digest",
|
|
35
|
+
"source_cutover_batch_digest", "reconciliation_digest"
|
|
36
|
+
],
|
|
37
|
+
"properties": {
|
|
38
|
+
"predecessor_reconciliation_digest": { "$ref": "#/$defs/digest" },
|
|
39
|
+
"source_inventory_digest": { "$ref": "#/$defs/digest" },
|
|
40
|
+
"desired_plan_digest": { "$ref": "#/$defs/digest" },
|
|
41
|
+
"runtime_task_migration_digest": { "$ref": "#/$defs/digest" },
|
|
42
|
+
"task_migration_digest": { "$ref": "#/$defs/digest" },
|
|
43
|
+
"phase_migration_digest": { "$ref": "#/$defs/digest" },
|
|
44
|
+
"source_cutover_batch_digest": { "$ref": "#/$defs/digest" },
|
|
45
|
+
"reconciliation_digest": { "$ref": "#/$defs/digest" }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"source_cutover_batch": { "$ref": "#/$defs/sourceCutoverBatch" },
|
|
49
|
+
"revision_digest": { "$ref": "#/$defs/digest" }
|
|
50
|
+
},
|
|
51
|
+
"$defs": {
|
|
52
|
+
"identifier": { "type": "string", "pattern": "^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$" },
|
|
53
|
+
"digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
54
|
+
"nullableDigest": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/digest" }] },
|
|
55
|
+
"gitSha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
|
56
|
+
"repoRef": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1,
|
|
59
|
+
"maxLength": 1024,
|
|
60
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*$"
|
|
61
|
+
},
|
|
62
|
+
"sourceLineRef": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"maxLength": 1024,
|
|
66
|
+
"$comment": "repoRefと同じ形に加え#L<line>を必須にする。実際の判定はparseTodoSourceRef(src/todo-revision.mjs)。",
|
|
67
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*#L[1-9][0-9]*$"
|
|
68
|
+
},
|
|
69
|
+
"nullableRepoRef": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/repoRef" }] },
|
|
70
|
+
"text": { "type": "string", "minLength": 1, "maxLength": 16384 },
|
|
71
|
+
"nullableText": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/text" }] },
|
|
72
|
+
"nodeRef": {
|
|
73
|
+
"type": "object", "additionalProperties": false,
|
|
74
|
+
"required": ["project_id", "plan_key", "task_id"],
|
|
75
|
+
"properties": {
|
|
76
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
77
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
78
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
79
|
+
"expected_topology_digest": { "$ref": "#/$defs/digest" }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"phaseRef": {
|
|
83
|
+
"type": "object", "additionalProperties": false,
|
|
84
|
+
"required": ["project_id", "plan_key", "phase_id"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
87
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
88
|
+
"phase_id": { "$ref": "#/$defs/identifier" },
|
|
89
|
+
"expected_topology_digest": { "$ref": "#/$defs/digest" }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"edge": {
|
|
93
|
+
"type": "object", "additionalProperties": false, "required": ["from", "to"],
|
|
94
|
+
"properties": { "from": { "$ref": "#/$defs/nodeRef" }, "to": { "$ref": "#/$defs/nodeRef" } }
|
|
95
|
+
},
|
|
96
|
+
"phaseAcceptDependency": {
|
|
97
|
+
"type": "object", "additionalProperties": false, "required": ["from", "to"],
|
|
98
|
+
"properties": { "from": { "$ref": "#/$defs/phaseRef" }, "to": { "$ref": "#/$defs/nodeRef" } }
|
|
99
|
+
},
|
|
100
|
+
"join": {
|
|
101
|
+
"type": "object", "additionalProperties": false, "required": ["id", "after", "before"],
|
|
102
|
+
"properties": {
|
|
103
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
104
|
+
"after": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/nodeRef" } },
|
|
105
|
+
"before": { "$ref": "#/$defs/nodeRef" }
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"narrativeAnchor": {
|
|
109
|
+
"type": "object", "additionalProperties": false,
|
|
110
|
+
"required": ["origin_plan_ref", "origin_line", "source_commit", "source_line_digest"],
|
|
111
|
+
"properties": {
|
|
112
|
+
"origin_plan_ref": { "$ref": "#/$defs/repoRef" },
|
|
113
|
+
"origin_line": { "type": "integer", "minimum": 1 },
|
|
114
|
+
"source_commit": { "type": "string", "pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$" },
|
|
115
|
+
"source_line_digest": { "$ref": "#/$defs/digest" }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"compileBinding": {
|
|
119
|
+
"oneOf": [
|
|
120
|
+
{ "type": "null" },
|
|
121
|
+
{
|
|
122
|
+
"type": "object", "additionalProperties": false,
|
|
123
|
+
"required": ["boundary_manifest_digest", "compiled_plan_digest", "topology_digest", "base_sha"],
|
|
124
|
+
"properties": {
|
|
125
|
+
"boundary_manifest_digest": { "$ref": "#/$defs/digest" },
|
|
126
|
+
"compiled_plan_digest": { "$ref": "#/$defs/digest" },
|
|
127
|
+
"topology_digest": { "$ref": "#/$defs/digest" },
|
|
128
|
+
"base_sha": { "$ref": "#/$defs/gitSha" }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"predecessor": {
|
|
134
|
+
"type": "object", "additionalProperties": false,
|
|
135
|
+
"required": ["plan_digest", "journal_head_digest", "plan_version"],
|
|
136
|
+
"properties": {
|
|
137
|
+
"plan_digest": { "$ref": "#/$defs/digest" },
|
|
138
|
+
"journal_head_digest": { "$ref": "#/$defs/digest" },
|
|
139
|
+
"plan_version": { "$ref": "#/$defs/identifier" }
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"taskV4": {
|
|
143
|
+
"type": "object", "additionalProperties": false,
|
|
144
|
+
"required": [
|
|
145
|
+
"task_id", "title", "lane", "narrative_ref", "narrative_anchor", "compile_binding",
|
|
146
|
+
"parent_task_id", "phase_id"
|
|
147
|
+
],
|
|
148
|
+
"properties": {
|
|
149
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
150
|
+
"title": { "$ref": "#/$defs/nullableText" },
|
|
151
|
+
"lane": { "$ref": "#/$defs/identifier" },
|
|
152
|
+
"narrative_ref": { "$ref": "#/$defs/nullableRepoRef" },
|
|
153
|
+
"narrative_anchor": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/narrativeAnchor" }] },
|
|
154
|
+
"compile_binding": { "$ref": "#/$defs/compileBinding" },
|
|
155
|
+
"parent_task_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
|
|
156
|
+
"phase_id": { "$ref": "#/$defs/identifier" }
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"phase": {
|
|
160
|
+
"type": "object", "additionalProperties": false,
|
|
161
|
+
"required": ["phase_id", "title", "gate_policy", "predecessor_phase_ids", "required_evidence_slots"],
|
|
162
|
+
"properties": {
|
|
163
|
+
"phase_id": { "$ref": "#/$defs/identifier" },
|
|
164
|
+
"title": { "$ref": "#/$defs/nullableText" },
|
|
165
|
+
"gate_policy": { "$ref": "#/$defs/identifier" },
|
|
166
|
+
"predecessor_phase_ids": {
|
|
167
|
+
"type": "array", "items": { "$ref": "#/$defs/identifier" }, "uniqueItems": true
|
|
168
|
+
},
|
|
169
|
+
"required_evidence_slots": {
|
|
170
|
+
"type": "array", "minItems": 1, "items": { "$ref": "#/$defs/identifier" }, "uniqueItems": true
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"desiredPlanV5": {
|
|
175
|
+
"type": "object", "additionalProperties": false,
|
|
176
|
+
"required": [
|
|
177
|
+
"schema", "project_id", "plan_key", "plan_version", "predecessor_plan_digest",
|
|
178
|
+
"tasks", "hard_dependencies", "joins", "phases", "phase_accept_dependencies",
|
|
179
|
+
"topology_digest", "plan_digest"
|
|
180
|
+
],
|
|
181
|
+
"properties": {
|
|
182
|
+
"schema": { "const": "lattice.todo_plan.v5" },
|
|
183
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
184
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
185
|
+
"plan_version": { "$ref": "#/$defs/identifier" },
|
|
186
|
+
"predecessor_plan_digest": { "$ref": "#/$defs/nullableDigest" },
|
|
187
|
+
"tasks": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskV4" } },
|
|
188
|
+
"hard_dependencies": { "type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/edge" } },
|
|
189
|
+
"joins": { "type": "array", "maxItems": 128, "items": { "$ref": "#/$defs/join" } },
|
|
190
|
+
"phases": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/phase" } },
|
|
191
|
+
"phase_accept_dependencies": {
|
|
192
|
+
"type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/phaseAcceptDependency" }
|
|
193
|
+
},
|
|
194
|
+
"topology_digest": { "$ref": "#/$defs/digest" },
|
|
195
|
+
"plan_digest": { "$ref": "#/$defs/digest" }
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"taskMigrationEntry": {
|
|
199
|
+
"type": "object", "additionalProperties": false,
|
|
200
|
+
"required": ["from_task_id", "to_task_id", "state_policy"],
|
|
201
|
+
"properties": {
|
|
202
|
+
"from_task_id": { "$ref": "#/$defs/identifier" },
|
|
203
|
+
"to_task_id": { "oneOf": [{ "const": "removed" }, { "$ref": "#/$defs/identifier" }] },
|
|
204
|
+
"state_policy": {
|
|
205
|
+
"enum": ["carry", "carry_reconciled_metadata", "reset_pending", "removed"],
|
|
206
|
+
"$comment": "'removed'はto_task_id==='removed'の時だけ、他の3値はto_task_idがidentifierの時だけ許される(runtime cross-check)。runtime_task_migrationのdisposition投影とも一致していなければならない(validRuntimeTodoProjection)。"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"phaseMigrationEntry": {
|
|
211
|
+
"type": "object", "additionalProperties": false,
|
|
212
|
+
"required": ["from_phase_id", "to_phase_id", "state_policy"],
|
|
213
|
+
"properties": {
|
|
214
|
+
"from_phase_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
|
|
215
|
+
"to_phase_id": { "oneOf": [{ "const": "removed" }, { "$ref": "#/$defs/identifier" }] },
|
|
216
|
+
"state_policy": {
|
|
217
|
+
"enum": ["carry", "reset", "removed"],
|
|
218
|
+
"$comment": "carryはfrom_phase_id!=null かつ to_phase_id!=='removed'の時だけ、removedはfrom_phase_id!=null かつ to_phase_id==='removed'の時だけ、resetはto_phase_id!=='removed'の時だけ許される(runtime cross-check)。"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"runtimeTaskMigrationEntry": {
|
|
223
|
+
"type": "object", "additionalProperties": false,
|
|
224
|
+
"required": ["predecessor_task_id", "disposition", "successor_task_ids", "reason", "evidence_digests"],
|
|
225
|
+
"properties": {
|
|
226
|
+
"predecessor_task_id": { "$ref": "#/$defs/identifier" },
|
|
227
|
+
"disposition": { "enum": ["carry", "stay", "replace", "split", "retire"] },
|
|
228
|
+
"successor_task_ids": {
|
|
229
|
+
"type": "array", "maxItems": 512, "items": { "$ref": "#/$defs/identifier" }, "uniqueItems": true,
|
|
230
|
+
"$comment": "carry/stayは要素1つでpredecessor_task_idと同一、retireは空、replace/splitは1つ以上(runtime cross-check)。全entry合算で重複禁止。"
|
|
231
|
+
},
|
|
232
|
+
"reason": { "$ref": "#/$defs/text" },
|
|
233
|
+
"evidence_digests": {
|
|
234
|
+
"type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/digest" },
|
|
235
|
+
"uniqueItems": true
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"runtimeTaskMigration": {
|
|
240
|
+
"type": "object", "additionalProperties": false,
|
|
241
|
+
"required": ["schema", "entries", "migration_digest"],
|
|
242
|
+
"properties": {
|
|
243
|
+
"schema": { "const": "lattice.runtime_task_migration.v1" },
|
|
244
|
+
"entries": {
|
|
245
|
+
"type": "array", "minItems": 1, "maxItems": 512,
|
|
246
|
+
"items": { "$ref": "#/$defs/runtimeTaskMigrationEntry" }
|
|
247
|
+
},
|
|
248
|
+
"migration_digest": { "$ref": "#/$defs/digest" }
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"sourceInventoryActiveEntry": {
|
|
252
|
+
"type": "object", "additionalProperties": false,
|
|
253
|
+
"required": ["task_id", "source_ref", "source_digest"],
|
|
254
|
+
"properties": {
|
|
255
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
256
|
+
"source_ref": { "$ref": "#/$defs/sourceLineRef" },
|
|
257
|
+
"source_digest": { "$ref": "#/$defs/digest" }
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"sourceInventoryTombstone": {
|
|
261
|
+
"type": "object", "additionalProperties": false,
|
|
262
|
+
"required": ["source_ref", "source_digest", "exclusion_reason"],
|
|
263
|
+
"properties": {
|
|
264
|
+
"source_ref": { "$ref": "#/$defs/sourceLineRef" },
|
|
265
|
+
"source_digest": { "$ref": "#/$defs/digest" },
|
|
266
|
+
"exclusion_reason": { "$ref": "#/$defs/text" }
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"sourceInventory": {
|
|
270
|
+
"type": "object", "additionalProperties": false,
|
|
271
|
+
"required": ["active", "excluded_tombstones"],
|
|
272
|
+
"$comment": "activeはdesired_plan.tasksと同じ順序・同じtask_id集合でなければならず、各entryのsource_refはそのtaskのnarrative_refと一致する必要がある(revise-phase v3固有のrequireNarrativeRef、runtime cross-check)。",
|
|
273
|
+
"properties": {
|
|
274
|
+
"active": {
|
|
275
|
+
"type": "array", "maxItems": 512, "items": { "$ref": "#/$defs/sourceInventoryActiveEntry" }
|
|
276
|
+
},
|
|
277
|
+
"excluded_tombstones": {
|
|
278
|
+
"type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/sourceInventoryTombstone" }
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
"sourceCutoverOperation": {
|
|
283
|
+
"type": "object", "additionalProperties": false,
|
|
284
|
+
"required": ["task_id", "disposition", "source_ref", "source_digest", "live_replacement"],
|
|
285
|
+
"properties": {
|
|
286
|
+
"task_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
|
|
287
|
+
"disposition": { "enum": ["active", "excluded"] },
|
|
288
|
+
"source_ref": { "$ref": "#/$defs/sourceLineRef" },
|
|
289
|
+
"source_digest": { "$ref": "#/$defs/digest" },
|
|
290
|
+
"live_replacement": {
|
|
291
|
+
"type": "string", "minLength": 1, "maxLength": 4096,
|
|
292
|
+
"$comment": "改行・制御文字禁止に加え、Markdownチェックボックス行に見える文字列は拒否される(validLiveReplacement)。"
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"sourceCutoverBatch": {
|
|
297
|
+
"type": "object", "additionalProperties": false,
|
|
298
|
+
"required": ["batch_id", "archive_ref", "operations", "batch_digest"],
|
|
299
|
+
"properties": {
|
|
300
|
+
"batch_id": { "$ref": "#/$defs/identifier" },
|
|
301
|
+
"archive_ref": {
|
|
302
|
+
"allOf": [{ "$ref": "#/$defs/repoRef" }, { "pattern": "\\.md$" }],
|
|
303
|
+
"$comment": "#L<line>付きのsourceLineRef形にはなり得ない(runtime cross-check)。"
|
|
304
|
+
},
|
|
305
|
+
"operations": {
|
|
306
|
+
"type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/sourceCutoverOperation" }
|
|
307
|
+
},
|
|
308
|
+
"batch_digest": { "$ref": "#/$defs/digest" }
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v2.schema.json",
|
|
4
|
+
"title": "lattice.todo_extraction.v2",
|
|
5
|
+
"description": "G4 migration-wave-only intermediate artifact authored by AI. Version 2 adds fail-closed historical in-progress registration without changing v1.",
|
|
6
|
+
"$comment": "Runtime validation additionally enforces UTF-8 byte bounds, strict sort order, unique and resolvable task ids, and extraction_digest as the canonical self-digest. Registration separately rejects an extraction with no register_* task.",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": [
|
|
10
|
+
"schema",
|
|
11
|
+
"project_id",
|
|
12
|
+
"plan_key",
|
|
13
|
+
"plan_version",
|
|
14
|
+
"actor",
|
|
15
|
+
"recorded_at",
|
|
16
|
+
"tasks",
|
|
17
|
+
"hard_dependencies",
|
|
18
|
+
"joins",
|
|
19
|
+
"extraction_digest"
|
|
20
|
+
],
|
|
21
|
+
"properties": {
|
|
22
|
+
"schema": { "const": "lattice.todo_extraction.v2" },
|
|
23
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
24
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
25
|
+
"plan_version": { "$ref": "#/$defs/identifier" },
|
|
26
|
+
"actor": { "$ref": "#/$defs/actor" },
|
|
27
|
+
"recorded_at": { "$ref": "#/$defs/strictTimestamp" },
|
|
28
|
+
"tasks": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"minItems": 1,
|
|
31
|
+
"maxItems": 512,
|
|
32
|
+
"items": { "$ref": "#/$defs/task" }
|
|
33
|
+
},
|
|
34
|
+
"hard_dependencies": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"maxItems": 2048,
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["from", "to"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"from": { "$ref": "#/$defs/nodeRef" },
|
|
43
|
+
"to": { "$ref": "#/$defs/nodeRef" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"joins": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"maxItems": 128,
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"required": ["id", "after", "before"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
56
|
+
"after": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"minItems": 1,
|
|
59
|
+
"maxItems": 512,
|
|
60
|
+
"items": { "$ref": "#/$defs/nodeRef" }
|
|
61
|
+
},
|
|
62
|
+
"before": { "$ref": "#/$defs/nodeRef" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"extraction_digest": { "$ref": "#/$defs/digest" }
|
|
67
|
+
},
|
|
68
|
+
"$defs": {
|
|
69
|
+
"identifier": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"pattern": "^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$"
|
|
72
|
+
},
|
|
73
|
+
"digest": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
76
|
+
},
|
|
77
|
+
"repoRef": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"minLength": 1,
|
|
80
|
+
"maxLength": 1024,
|
|
81
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*$"
|
|
82
|
+
},
|
|
83
|
+
"text": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"minLength": 1,
|
|
86
|
+
"maxLength": 16384
|
|
87
|
+
},
|
|
88
|
+
"nullableText": {
|
|
89
|
+
"oneOf": [
|
|
90
|
+
{ "type": "null" },
|
|
91
|
+
{ "$ref": "#/$defs/text" }
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"strictTimestamp": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$",
|
|
97
|
+
"$comment": "Runtime validation also requires Date.parse(value).toISOString() byte equality."
|
|
98
|
+
},
|
|
99
|
+
"actor": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"additionalProperties": false,
|
|
102
|
+
"required": ["host", "session", "agent"],
|
|
103
|
+
"properties": {
|
|
104
|
+
"host": { "$ref": "#/$defs/identifier" },
|
|
105
|
+
"session": { "$ref": "#/$defs/identifier" },
|
|
106
|
+
"agent": { "$ref": "#/$defs/identifier" }
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"nodeRef": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"required": ["project_id", "plan_key", "task_id"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
115
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
116
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
117
|
+
"expected_topology_digest": { "$ref": "#/$defs/digest" }
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"sourceLocation": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"additionalProperties": false,
|
|
123
|
+
"required": [
|
|
124
|
+
"origin_plan_ref",
|
|
125
|
+
"origin_line",
|
|
126
|
+
"source_commit",
|
|
127
|
+
"heading_path",
|
|
128
|
+
"markdown_depth",
|
|
129
|
+
"parent_task_id",
|
|
130
|
+
"checkbox_state"
|
|
131
|
+
],
|
|
132
|
+
"properties": {
|
|
133
|
+
"origin_plan_ref": { "$ref": "#/$defs/repoRef" },
|
|
134
|
+
"origin_line": { "type": "integer", "minimum": 1 },
|
|
135
|
+
"source_commit": { "type": "string", "pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$" },
|
|
136
|
+
"heading_path": {
|
|
137
|
+
"type": "array",
|
|
138
|
+
"maxItems": 32,
|
|
139
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 1024 }
|
|
140
|
+
},
|
|
141
|
+
"markdown_depth": { "type": "integer", "minimum": 0, "maximum": 64 },
|
|
142
|
+
"parent_task_id": {
|
|
143
|
+
"oneOf": [
|
|
144
|
+
{ "type": "null" },
|
|
145
|
+
{ "$ref": "#/$defs/identifier" }
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
"checkbox_state": {
|
|
149
|
+
"enum": ["checked", "unchecked", "absent", "ambiguous"]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"migrationContext": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"additionalProperties": false,
|
|
156
|
+
"required": [
|
|
157
|
+
"external_canonical_ref",
|
|
158
|
+
"carry_over_ref",
|
|
159
|
+
"h_required",
|
|
160
|
+
"condition",
|
|
161
|
+
"evidence_refs",
|
|
162
|
+
"notes"
|
|
163
|
+
],
|
|
164
|
+
"properties": {
|
|
165
|
+
"external_canonical_ref": { "$ref": "#/$defs/nullableText" },
|
|
166
|
+
"carry_over_ref": { "$ref": "#/$defs/nullableText" },
|
|
167
|
+
"h_required": { "type": "boolean" },
|
|
168
|
+
"condition": { "$ref": "#/$defs/nullableText" },
|
|
169
|
+
"evidence_refs": {
|
|
170
|
+
"type": "array",
|
|
171
|
+
"maxItems": 64,
|
|
172
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 4096 }
|
|
173
|
+
},
|
|
174
|
+
"notes": {
|
|
175
|
+
"type": "array",
|
|
176
|
+
"maxItems": 64,
|
|
177
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 4096 }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"historicalStart": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"additionalProperties": false,
|
|
184
|
+
"required": ["start_mode", "status", "started_at"],
|
|
185
|
+
"properties": {
|
|
186
|
+
"start_mode": { "const": "historical_import" },
|
|
187
|
+
"status": { "const": "in-progress" },
|
|
188
|
+
"started_at": {
|
|
189
|
+
"oneOf": [
|
|
190
|
+
{ "const": "unknown_requires_evidence" },
|
|
191
|
+
{ "$ref": "#/$defs/strictTimestamp" }
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"historicalCompletion": {
|
|
197
|
+
"type": "object",
|
|
198
|
+
"additionalProperties": false,
|
|
199
|
+
"required": ["done_mode", "completed_at"],
|
|
200
|
+
"properties": {
|
|
201
|
+
"done_mode": { "const": "historical_import" },
|
|
202
|
+
"completed_at": {
|
|
203
|
+
"oneOf": [
|
|
204
|
+
{ "const": "unknown_requires_evidence" },
|
|
205
|
+
{ "$ref": "#/$defs/strictTimestamp" }
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"taskBase": {
|
|
211
|
+
"type": "object",
|
|
212
|
+
"additionalProperties": false,
|
|
213
|
+
"required": [
|
|
214
|
+
"task_id",
|
|
215
|
+
"title",
|
|
216
|
+
"lane",
|
|
217
|
+
"narrative_ref",
|
|
218
|
+
"compile_binding",
|
|
219
|
+
"disposition",
|
|
220
|
+
"start",
|
|
221
|
+
"completion",
|
|
222
|
+
"source",
|
|
223
|
+
"migration_context"
|
|
224
|
+
],
|
|
225
|
+
"properties": {
|
|
226
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
227
|
+
"title": { "$ref": "#/$defs/text" },
|
|
228
|
+
"lane": { "$ref": "#/$defs/identifier" },
|
|
229
|
+
"narrative_ref": {
|
|
230
|
+
"oneOf": [
|
|
231
|
+
{ "type": "null" },
|
|
232
|
+
{ "$ref": "#/$defs/repoRef" }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
"compile_binding": { "type": "null" },
|
|
236
|
+
"disposition": {
|
|
237
|
+
"enum": [
|
|
238
|
+
"register_pending",
|
|
239
|
+
"register_in_progress",
|
|
240
|
+
"register_done",
|
|
241
|
+
"exclude_superseded",
|
|
242
|
+
"exclude_compatibility_record",
|
|
243
|
+
"unknown_requires_evidence"
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
"start": {
|
|
247
|
+
"oneOf": [
|
|
248
|
+
{ "type": "null" },
|
|
249
|
+
{ "$ref": "#/$defs/historicalStart" }
|
|
250
|
+
]
|
|
251
|
+
},
|
|
252
|
+
"completion": {
|
|
253
|
+
"oneOf": [
|
|
254
|
+
{ "type": "null" },
|
|
255
|
+
{ "$ref": "#/$defs/historicalCompletion" }
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
"source": { "$ref": "#/$defs/sourceLocation" },
|
|
259
|
+
"migration_context": { "$ref": "#/$defs/migrationContext" }
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"task": {
|
|
263
|
+
"allOf": [
|
|
264
|
+
{ "$ref": "#/$defs/taskBase" },
|
|
265
|
+
{
|
|
266
|
+
"if": {
|
|
267
|
+
"properties": { "disposition": { "const": "register_in_progress" } },
|
|
268
|
+
"required": ["disposition"]
|
|
269
|
+
},
|
|
270
|
+
"then": {
|
|
271
|
+
"properties": {
|
|
272
|
+
"start": { "$ref": "#/$defs/historicalStart" },
|
|
273
|
+
"completion": { "type": "null" }
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"else": {
|
|
277
|
+
"if": {
|
|
278
|
+
"properties": { "disposition": { "const": "register_done" } },
|
|
279
|
+
"required": ["disposition"]
|
|
280
|
+
},
|
|
281
|
+
"then": {
|
|
282
|
+
"properties": {
|
|
283
|
+
"start": { "type": "null" },
|
|
284
|
+
"completion": { "$ref": "#/$defs/historicalCompletion" }
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"else": {
|
|
288
|
+
"properties": {
|
|
289
|
+
"start": { "type": "null" },
|
|
290
|
+
"completion": { "type": "null" }
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|