kotonoha 0.4.0 → 0.4.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.
@@ -0,0 +1,479 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$ref": "#/$defs/Request",
4
+ "title": "kotonoha Obsidian execution contract",
5
+ "x-contract-version": 1,
6
+ "x-commands": {
7
+ "inspect": {
8
+ "required": ["--request"],
9
+ "optional": [],
10
+ "result": "InspectResult",
11
+ "vault_write": false
12
+ },
13
+ "read": {
14
+ "required": ["--request", "--path"],
15
+ "optional": [],
16
+ "result": "ReadResult",
17
+ "vault_write": false
18
+ },
19
+ "plan": {
20
+ "required": ["--request", "--candidate"],
21
+ "optional": ["--review"],
22
+ "result": "PlanResult",
23
+ "context_schema": "PlanRequest",
24
+ "preconditions": [
25
+ "intent is create or proofread; write_target is an authorized .md path",
26
+ "proofread requires a non-null base_sha256 matching the original source used for candidate generation and the current target bytes",
27
+ "unchanged proofread returns no_changes before requiring semantic review; it never creates an applyable Plan"
28
+ ],
29
+ "vault_write": false
30
+ },
31
+ "apply": {
32
+ "required": ["--request", "--plan", "--approve-plan-sha256"],
33
+ "optional": [],
34
+ "result": "ApplyResult",
35
+ "vault_write": true
36
+ }
37
+ },
38
+ "x-exit-codes": {
39
+ "0": "completed",
40
+ "1": "input_or_runtime_failure",
41
+ "2": "authorization_conflict_or_gate_block",
42
+ "3": "partial",
43
+ "4": "missing_capability_or_dependency"
44
+ },
45
+ "x-exit-policy": [
46
+ {"priority": 1, "condition": "current_invocation_commit_outcome_uncertain", "status": "failed", "exit_code": 1, "write_status_allowed": ["unknown"]},
47
+ {"priority": 2, "condition": "control_input_or_precommit_runtime_error", "status": "failed", "exit_code": 1, "write_status_allowed": ["unsaved", "not_requested"]},
48
+ {"priority": 3, "condition": "operation_authorization_conflict_or_gate_denial", "status": "not_performed", "exit_code": 2, "write_status_allowed": ["unsaved", "not_requested"]},
49
+ {"priority": 4, "condition": "missing_dependency_or_capability", "status": "not_performed", "exit_code": 4, "write_status_allowed": ["unsaved", "not_requested"]},
50
+ {"priority": 5, "condition": "link_limitation_or_unprocessed_input", "status": "partial", "exit_code": 3, "write_status_allowed": ["unsaved", "not_requested"]},
51
+ {"priority": 6, "condition": "complete", "status": "completed", "exit_code": 0, "write_status_allowed": ["unsaved", "not_requested", "saved"]}
52
+ ],
53
+ "x-reason-context": [
54
+ {"command": "inspect", "reasons": ["excluded_management_directory", "excluded_example", "external_url"], "effect": "informational_only; does not reduce coverage or status"},
55
+ {"command": "inspect", "reasons": ["read_failed"], "effect": "partial, exit 3, reads_complete false; no definitive absence or uniqueness in the affected necessary domain"},
56
+ {"command": "inspect", "reasons": ["target_parse_incomplete"], "effect": "partial, exit 3; a single parse-failed candidate is indeterminate with or without a heading fragment"},
57
+ {"command": "inspect", "reasons": ["source_parse_incomplete"], "effect": "partial, exit 3, parses_complete false; no confirmed links from that source; unsupported YAML additionally emits a separate unsupported_format Issue"},
58
+ {"command": "read", "reasons": ["read_failed"], "effect": "failed, exit 1, not_requested; includes missing authorized target"},
59
+ {"command": "apply", "reasons": ["commit_outcome_uncertain"], "effect": "current attempt committed or may have committed: failed/1/unknown; prior attempt uncertain with no current write: not_performed/2/unsaved"},
60
+ {"command": "apply", "reasons": ["already_applied"], "effect": "completed/0/saved, changed false, verified true; no current write"},
61
+ {"command": "apply", "reasons": ["target_exists"], "effect": "not_performed/2/unsaved; includes preexisting stage_target"},
62
+ {"command": "*", "reasons": ["consent_missing"], "effect": "not_performed/2; deny all Vault content processing without model_context, and plan/apply workspace artifacts without workspace_copies"}
63
+ ],
64
+ "$defs": {
65
+ "Nonempty": {"type": "string", "minLength": 1},
66
+ "Hash": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
67
+ "NullableHash": {"oneOf": [{"$ref": "#/$defs/Hash"}, {"type": "null"}]},
68
+ "NullableString": {"type": ["string", "null"], "minLength": 1},
69
+ "Paths": {
70
+ "type": "array",
71
+ "items": {"$ref": "#/$defs/Nonempty"},
72
+ "uniqueItems": true
73
+ },
74
+ "OperationState": {"enum": ["scoped", "proposed", "validated", "approved", "saved", "blocked"]},
75
+ "LinkClassification": {"enum": ["resolved", "unresolved", "ambiguous", "indeterminate"]},
76
+ "Reason": {
77
+ "enum": [
78
+ "note_found", "heading_found", "note_missing", "heading_missing",
79
+ "multiple_notes", "multiple_headings", "scope_incomplete", "outside_scope",
80
+ "unsupported_syntax", "unsupported_heading", "invalid_encoding",
81
+ "excluded_example", "external_url", "excluded_management_directory",
82
+ "invalid_request", "invalid_path", "symlink_not_followed", "read_failed",
83
+ "unsupported_format", "limit_reached", "dependency_missing",
84
+ "capability_missing", "consent_missing", "candidate_changed",
85
+ "request_changed", "implementation_changed", "gate_blocked",
86
+ "semantic_review_required", "plan_not_approved", "target_exists",
87
+ "target_changed", "parent_changed", "write_failed_before_commit",
88
+ "commit_outcome_uncertain", "already_applied", "saved", "no_changes",
89
+ "runtime_error", "source_parse_incomplete", "target_parse_incomplete"
90
+ ]
91
+ },
92
+ "Scope": {
93
+ "type": "object",
94
+ "additionalProperties": false,
95
+ "required": ["kind", "path"],
96
+ "properties": {
97
+ "kind": {"enum": ["file", "directory"]},
98
+ "path": {"$ref": "#/$defs/Nonempty"}
99
+ }
100
+ },
101
+ "Request": {
102
+ "type": "object",
103
+ "additionalProperties": false,
104
+ "required": [
105
+ "schema_version", "vault_root", "read_scopes", "purpose", "intent",
106
+ "write_target", "base_sha256", "deliverable", "declared_rules", "protected_terms",
107
+ "workspace", "consent", "limits"
108
+ ],
109
+ "properties": {
110
+ "schema_version": {"const": 1},
111
+ "vault_root": {"$ref": "#/$defs/Nonempty"},
112
+ "read_scopes": {
113
+ "type": "array",
114
+ "minItems": 1,
115
+ "uniqueItems": true,
116
+ "items": {"$ref": "#/$defs/Scope"}
117
+ },
118
+ "purpose": {"$ref": "#/$defs/Nonempty"},
119
+ "intent": {"enum": ["conventions", "related", "links", "create", "proofread"]},
120
+ "write_target": {"$ref": "#/$defs/NullableString"},
121
+ "base_sha256": {"$ref": "#/$defs/NullableHash"},
122
+ "deliverable": {"enum": ["note", "template", null]},
123
+ "declared_rules": {"$ref": "#/$defs/Paths"},
124
+ "protected_terms": {"$ref": "#/$defs/Paths"},
125
+ "workspace": {"$ref": "#/$defs/NullableString"},
126
+ "consent": {
127
+ "type": "object",
128
+ "additionalProperties": false,
129
+ "required": ["model_context", "workspace_copies"],
130
+ "properties": {
131
+ "model_context": {"type": "boolean"},
132
+ "workspace_copies": {"type": "boolean"}
133
+ }
134
+ },
135
+ "limits": {
136
+ "type": "object",
137
+ "additionalProperties": false,
138
+ "required": ["max_files", "max_file_bytes", "max_total_bytes"],
139
+ "properties": {
140
+ "max_files": {"type": ["integer", "null"], "minimum": 1},
141
+ "max_file_bytes": {"type": ["integer", "null"], "minimum": 1},
142
+ "max_total_bytes": {"type": ["integer", "null"], "minimum": 1}
143
+ }
144
+ }
145
+ }
146
+ },
147
+ "PlanRequest": {
148
+ "description": "Contextual safety preconditions for plan, checked after Request syntax validation. Rejection is not_performed/2, not a malformed JSON error. Request itself also serves read before a base hash exists.",
149
+ "allOf": [
150
+ {"$ref": "#/$defs/Request"},
151
+ {
152
+ "properties": {
153
+ "intent": {"enum": ["create", "proofread"]},
154
+ "write_target": {"$ref": "#/$defs/Nonempty"},
155
+ "workspace": {"$ref": "#/$defs/Nonempty"}
156
+ },
157
+ "if": {"properties": {"intent": {"const": "proofread"}}},
158
+ "then": {"properties": {"base_sha256": {"$ref": "#/$defs/Hash"}, "deliverable": {"type": "null"}}},
159
+ "else": {"properties": {"base_sha256": {"type": "null"}, "deliverable": {"enum": ["note", "template"]}}}
160
+ }
161
+ ]
162
+ },
163
+ "Issue": {
164
+ "type": "object",
165
+ "additionalProperties": false,
166
+ "required": ["code", "path", "message"],
167
+ "properties": {
168
+ "code": {"$ref": "#/$defs/Reason"},
169
+ "path": {"$ref": "#/$defs/NullableString"},
170
+ "message": {"$ref": "#/$defs/Nonempty"}
171
+ }
172
+ },
173
+ "Link": {
174
+ "type": "object",
175
+ "additionalProperties": false,
176
+ "required": [
177
+ "source_path", "block_start_line", "occurrence", "raw",
178
+ "classification", "reason", "candidates"
179
+ ],
180
+ "properties": {
181
+ "source_path": {"$ref": "#/$defs/Nonempty"},
182
+ "block_start_line": {"type": "integer", "minimum": 1},
183
+ "occurrence": {"type": "integer", "minimum": 1},
184
+ "raw": {"$ref": "#/$defs/Nonempty"},
185
+ "classification": {"$ref": "#/$defs/LinkClassification"},
186
+ "reason": {"$ref": "#/$defs/Reason"},
187
+ "candidates": {"$ref": "#/$defs/Paths"}
188
+ }
189
+ },
190
+ "Observation": {
191
+ "type": "object",
192
+ "additionalProperties": false,
193
+ "required": ["kind", "value", "paths", "count", "label"],
194
+ "properties": {
195
+ "kind": {"enum": ["name", "folder", "property_name", "property_type", "tag"]},
196
+ "value": {"$ref": "#/$defs/Nonempty"},
197
+ "paths": {"$ref": "#/$defs/Paths"},
198
+ "count": {"type": "integer", "minimum": 1},
199
+ "label": {"const": "observation"}
200
+ }
201
+ },
202
+ "Note": {
203
+ "type": "object",
204
+ "additionalProperties": false,
205
+ "required": ["path", "sha256", "bytes", "heading_texts"],
206
+ "properties": {
207
+ "path": {"$ref": "#/$defs/Nonempty"},
208
+ "sha256": {"$ref": "#/$defs/Hash"},
209
+ "bytes": {"type": "integer", "minimum": 0},
210
+ "heading_texts": {"type": "array", "items": {"type": "string"}}
211
+ }
212
+ },
213
+ "InspectResult": {
214
+ "type": "object",
215
+ "additionalProperties": false,
216
+ "required": ["notes", "observations", "declarations", "links", "external_url_count"],
217
+ "properties": {
218
+ "notes": {"type": "array", "items": {"$ref": "#/$defs/Note"}},
219
+ "observations": {"type": "array", "items": {"$ref": "#/$defs/Observation"}},
220
+ "declarations": {"type": "array", "items": {"$ref": "#/$defs/Note"}},
221
+ "links": {"type": "array", "items": {"$ref": "#/$defs/Link"}},
222
+ "external_url_count": {"type": "integer", "minimum": 0}
223
+ }
224
+ },
225
+ "ReadResult": {
226
+ "type": "object",
227
+ "additionalProperties": false,
228
+ "required": ["path", "sha256", "bytes", "content"],
229
+ "properties": {
230
+ "path": {"$ref": "#/$defs/Nonempty"},
231
+ "sha256": {"$ref": "#/$defs/Hash"},
232
+ "bytes": {"type": "integer", "minimum": 0},
233
+ "content": {"type": "string"}
234
+ }
235
+ },
236
+ "PlanResult": {
237
+ "type": "object",
238
+ "additionalProperties": false,
239
+ "required": ["plan_path", "plan_sha256", "operation_id", "gate_status", "no_changes"],
240
+ "properties": {
241
+ "plan_path": {"$ref": "#/$defs/NullableString"},
242
+ "plan_sha256": {"$ref": "#/$defs/NullableHash"},
243
+ "operation_id": {"$ref": "#/$defs/NullableHash"},
244
+ "gate_status": {"enum": ["passed", "blocked", "review_required", "not_applicable"]},
245
+ "no_changes": {"type": "boolean"}
246
+ },
247
+ "allOf": [
248
+ {
249
+ "if": {"properties": {"no_changes": {"const": true}}},
250
+ "then": {"properties": {"plan_path": {"type": "null"}, "plan_sha256": {"type": "null"}, "operation_id": {"type": "null"}, "gate_status": {"const": "passed"}}},
251
+ "else": {"properties": {"plan_path": {"$ref": "#/$defs/Nonempty"}, "plan_sha256": {"$ref": "#/$defs/Hash"}, "operation_id": {"$ref": "#/$defs/Hash"}}}
252
+ }
253
+ ]
254
+ },
255
+ "ApplyResult": {
256
+ "type": "object",
257
+ "additionalProperties": false,
258
+ "required": ["target", "after_sha256", "operation_id", "changed", "verified"],
259
+ "properties": {
260
+ "target": {"$ref": "#/$defs/Nonempty"},
261
+ "after_sha256": {"$ref": "#/$defs/NullableHash"},
262
+ "operation_id": {"$ref": "#/$defs/Hash"},
263
+ "changed": {"type": ["boolean", "null"]},
264
+ "verified": {"type": "boolean"}
265
+ }
266
+ },
267
+ "Coverage": {
268
+ "type": "object",
269
+ "additionalProperties": false,
270
+ "required": ["whole_vault", "enumeration_complete", "reads_complete", "parses_complete", "processed", "unprocessed"],
271
+ "properties": {
272
+ "whole_vault": {"type": "boolean"},
273
+ "enumeration_complete": {"type": "boolean"},
274
+ "reads_complete": {"type": "boolean"},
275
+ "parses_complete": {"type": "boolean"},
276
+ "processed": {"$ref": "#/$defs/Paths"},
277
+ "unprocessed": {"$ref": "#/$defs/Paths"}
278
+ }
279
+ },
280
+ "Report": {
281
+ "type": "object",
282
+ "additionalProperties": false,
283
+ "required": [
284
+ "schema_version", "command", "status", "exit_code", "write_status",
285
+ "operation_state", "request_sha256", "coverage", "issues", "data"
286
+ ],
287
+ "properties": {
288
+ "schema_version": {"const": 1},
289
+ "command": {"enum": ["inspect", "read", "plan", "apply"]},
290
+ "status": {"enum": ["completed", "partial", "not_performed", "failed"]},
291
+ "exit_code": {"type": "integer", "minimum": 0, "maximum": 4},
292
+ "write_status": {"enum": ["not_requested", "unsaved", "saved", "unknown"]},
293
+ "operation_state": {"$ref": "#/$defs/OperationState"},
294
+ "request_sha256": {"$ref": "#/$defs/NullableHash"},
295
+ "coverage": {"$ref": "#/$defs/Coverage"},
296
+ "issues": {"type": "array", "items": {"$ref": "#/$defs/Issue"}},
297
+ "data": {
298
+ "oneOf": [
299
+ {"type": "null"},
300
+ {"$ref": "#/$defs/InspectResult"},
301
+ {"$ref": "#/$defs/ReadResult"},
302
+ {"$ref": "#/$defs/PlanResult"},
303
+ {"$ref": "#/$defs/ApplyResult"}
304
+ ]
305
+ }
306
+ },
307
+ "allOf": [
308
+ {
309
+ "if": {"properties": {"command": {"const": "inspect"}}},
310
+ "then": {"properties": {"data": {"oneOf": [{"type": "null"}, {"$ref": "#/$defs/InspectResult"}]}}}
311
+ },
312
+ {
313
+ "if": {"properties": {"command": {"const": "read"}}},
314
+ "then": {"properties": {"data": {"oneOf": [{"type": "null"}, {"$ref": "#/$defs/ReadResult"}]}}}
315
+ },
316
+ {
317
+ "if": {"properties": {"command": {"const": "plan"}}},
318
+ "then": {"properties": {"data": {"oneOf": [{"type": "null"}, {"$ref": "#/$defs/PlanResult"}]}}}
319
+ },
320
+ {
321
+ "if": {"properties": {"command": {"const": "apply"}}},
322
+ "then": {"properties": {"data": {"oneOf": [{"type": "null"}, {"$ref": "#/$defs/ApplyResult"}]}}}
323
+ },
324
+ {
325
+ "if": {"properties": {"status": {"const": "completed"}}},
326
+ "then": {"properties": {"data": {"not": {"type": "null"}}, "exit_code": {"const": 0}}}
327
+ },
328
+ {
329
+ "if": {"properties": {"status": {"const": "failed"}}},
330
+ "then": {"properties": {"exit_code": {"const": 1}}}
331
+ },
332
+ {
333
+ "if": {"properties": {"status": {"const": "partial"}}},
334
+ "then": {"properties": {"exit_code": {"const": 3}}}
335
+ },
336
+ {
337
+ "if": {"properties": {"status": {"const": "not_performed"}}},
338
+ "then": {"properties": {"exit_code": {"enum": [2, 4]}, "write_status": {"enum": ["unsaved", "not_requested"]}}}
339
+ },
340
+ {
341
+ "if": {"properties": {"write_status": {"enum": ["saved", "unknown"]}}},
342
+ "then": {"properties": {"command": {"const": "apply"}}}
343
+ },
344
+ {
345
+ "if": {"properties": {"write_status": {"const": "saved"}}},
346
+ "then": {"properties": {"status": {"const": "completed"}, "operation_state": {"const": "saved"}}}
347
+ },
348
+ {
349
+ "if": {"properties": {"write_status": {"const": "unknown"}}},
350
+ "then": {"properties": {"status": {"const": "failed"}}}
351
+ },
352
+ {
353
+ "if": {"properties": {"command": {"enum": ["inspect", "read"]}}},
354
+ "then": {"properties": {"write_status": {"const": "not_requested"}}}
355
+ },
356
+ {
357
+ "if": {"properties": {"command": {"const": "plan"}}},
358
+ "then": {"properties": {"write_status": {"const": "unsaved"}}}
359
+ },
360
+ {
361
+ "if": {"properties": {"command": {"const": "plan"}, "status": {"const": "completed"}}},
362
+ "then": {
363
+ "properties": {"data": {"properties": {"gate_status": {"enum": ["passed", "not_applicable"]}}}},
364
+ "allOf": [{
365
+ "if": {"properties": {"data": {"properties": {"no_changes": {"const": true}}}}},
366
+ "then": {"properties": {"operation_state": {"const": "scoped"}}},
367
+ "else": {"properties": {"operation_state": {"const": "validated"}}}
368
+ }]
369
+ }
370
+ },
371
+ {
372
+ "if": {"properties": {"status": {"const": "completed"}, "command": {"const": "apply"}}},
373
+ "then": {
374
+ "properties": {
375
+ "write_status": {"const": "saved"},
376
+ "operation_state": {"const": "saved"},
377
+ "data": {
378
+ "properties": {
379
+ "verified": {"const": true},
380
+ "changed": {"type": "boolean"},
381
+ "after_sha256": {"$ref": "#/$defs/Hash"}
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+ ]
388
+ },
389
+ "Plan": {
390
+ "type": "object",
391
+ "additionalProperties": false,
392
+ "required": [
393
+ "schema_version", "operation_id", "request_sha256", "contract_sha256",
394
+ "implementation_sha256", "vault_identity", "target", "stage_target", "kind",
395
+ "before_sha256", "after_sha256", "diff_sha256", "review_sha256", "state",
396
+ "artifacts", "protected_terms"
397
+ ],
398
+ "properties": {
399
+ "schema_version": {"const": 1},
400
+ "operation_id": {"$ref": "#/$defs/Hash"},
401
+ "request_sha256": {"$ref": "#/$defs/Hash"},
402
+ "contract_sha256": {"$ref": "#/$defs/Hash"},
403
+ "implementation_sha256": {"$ref": "#/$defs/Hash"},
404
+ "vault_identity": {
405
+ "type": "object",
406
+ "additionalProperties": false,
407
+ "required": ["device", "inode"],
408
+ "properties": {
409
+ "device": {"type": "integer", "minimum": 0},
410
+ "inode": {"type": "integer", "minimum": 0}
411
+ }
412
+ },
413
+ "target": {"$ref": "#/$defs/Nonempty"},
414
+ "stage_target": {"$ref": "#/$defs/Nonempty"},
415
+ "kind": {"enum": ["create", "edit"]},
416
+ "before_sha256": {"$ref": "#/$defs/NullableHash"},
417
+ "after_sha256": {"$ref": "#/$defs/Hash"},
418
+ "diff_sha256": {"$ref": "#/$defs/Hash"},
419
+ "review_sha256": {"$ref": "#/$defs/NullableHash"},
420
+ "state": {"enum": ["proposed", "validated"]},
421
+ "artifacts": {
422
+ "type": "object",
423
+ "additionalProperties": false,
424
+ "required": ["before", "candidate", "diff", "review"],
425
+ "properties": {
426
+ "before": {"$ref": "#/$defs/NullableString"},
427
+ "candidate": {"$ref": "#/$defs/Nonempty"},
428
+ "diff": {"$ref": "#/$defs/Nonempty"},
429
+ "review": {"$ref": "#/$defs/NullableString"}
430
+ }
431
+ },
432
+ "protected_terms": {"$ref": "#/$defs/Paths"}
433
+ },
434
+ "allOf": [
435
+ {
436
+ "if": {"properties": {"kind": {"const": "create"}}},
437
+ "then": {
438
+ "properties": {
439
+ "before_sha256": {"type": "null"},
440
+ "artifacts": {"properties": {"before": {"type": "null"}}}
441
+ }
442
+ }
443
+ },
444
+ {
445
+ "if": {"properties": {"kind": {"const": "edit"}}},
446
+ "then": {
447
+ "properties": {
448
+ "before_sha256": {"$ref": "#/$defs/Hash"},
449
+ "artifacts": {"properties": {"before": {"$ref": "#/$defs/Nonempty"}}}
450
+ }
451
+ }
452
+ },
453
+ {
454
+ "if": {"properties": {"kind": {"const": "edit"}, "state": {"const": "validated"}}},
455
+ "then": {
456
+ "properties": {
457
+ "review_sha256": {"$ref": "#/$defs/Hash"},
458
+ "artifacts": {"properties": {"review": {"$ref": "#/$defs/Nonempty"}}}
459
+ }
460
+ }
461
+ }
462
+ ]
463
+ },
464
+ "Receipt": {
465
+ "type": "object",
466
+ "additionalProperties": false,
467
+ "required": ["schema_version", "operation_id", "plan_sha256", "request_sha256", "target", "after_sha256", "verified"],
468
+ "properties": {
469
+ "schema_version": {"const": 1},
470
+ "operation_id": {"$ref": "#/$defs/Hash"},
471
+ "plan_sha256": {"$ref": "#/$defs/Hash"},
472
+ "request_sha256": {"$ref": "#/$defs/Hash"},
473
+ "target": {"$ref": "#/$defs/Nonempty"},
474
+ "after_sha256": {"$ref": "#/$defs/Hash"},
475
+ "verified": {"const": true}
476
+ }
477
+ }
478
+ }
479
+ }
@@ -0,0 +1,151 @@
1
+ # Obsidian操作ガイド
2
+
3
+ 対象のVaultと読み取り範囲を指定し、診断・候補・保存を分けて実行する。
4
+ Obsidianアプリ、公式CLI、MCP、コミュニティプラグインは不要である。
5
+ Skillへの自然言語の依頼例は次のとおり。
6
+
7
+ ```text
8
+ /absolute/path/to/vault の Notes と Targets を読み取り対象にして、
9
+ 内部リンクを診断して。未作成リンクは修復しないで。
10
+ ```
11
+
12
+ ```text
13
+ このVaultのNotes/draft.mdを誤りだけ添削して。
14
+ 差分を確認するまで保存しないで。
15
+ ```
16
+
17
+ ## 導入と起動
18
+
19
+ 既存の同名Skillを無断で置換しない。必要な兄弟Skillは別途導入する。
20
+
21
+ ```bash
22
+ npx kotonoha install --skill obsidian-vault
23
+ npx kotonoha install --skill tech-writer
24
+ npx kotonoha install --skill japanese-prose
25
+ ```
26
+
27
+ 既存のjapanese-proseが古い場合は共通ゲートを利用できず、依存不足となる。
28
+ 既存Skillのローカル変更を確認・退避し、利用者が更新を許可してから、対象Skillだけを`--force`付きで再導入する。
29
+
30
+ Linux、CPython 3.12、uvが必要である。依存関係の初回準備は通信が発生し得るため、利用者の別途許可を得て実行する。
31
+
32
+ ```bash
33
+ uv sync --locked --python 3.12 --script .github/skills/obsidian-vault/scripts/vault.py
34
+ ```
35
+
36
+ 実際のノート処理は必ずオフラインの固定起動経路を使う。
37
+ 起動に失敗した場合、ホストは機能を未実施と説明する。未起動のスクリプトのJSON結果を捏造しない。
38
+
39
+ ```bash
40
+ uv run --offline --locked --python 3.12 .github/skills/obsidian-vault/scripts/vault.py inspect --request /absolute/workspace/request.json
41
+ ```
42
+
43
+ ## 依頼ファイル
44
+
45
+ 正本は[execution-contract.json](execution-contract.json)である。
46
+ 以下は、指定範囲のリンクを調べる依頼の例である。絶対パスは実際の場所に置き換える。
47
+ model_contextは、この範囲の情報をホストモデルへ渡す同意を得た場合だけtrueにする。
48
+
49
+ ```json
50
+ {
51
+ "schema_version": 1,
52
+ "vault_root": "/absolute/path/to/vault",
53
+ "read_scopes": [
54
+ {"kind": "directory", "path": "Notes"},
55
+ {"kind": "directory", "path": "Targets"}
56
+ ],
57
+ "purpose": "許可された範囲のリンク診断",
58
+ "intent": "links",
59
+ "write_target": null,
60
+ "base_sha256": null,
61
+ "deliverable": null,
62
+ "declared_rules": [],
63
+ "protected_terms": [],
64
+ "workspace": null,
65
+ "consent": {"model_context": true, "workspace_copies": false},
66
+ "limits": {"max_files": null, "max_file_bytes": null, "max_total_bytes": null}
67
+ }
68
+ ```
69
+
70
+ 全Vaultを明示的に許可した場合だけ、read_scopesをdirectoryの`.`にする。
71
+ ファイル単位ではkindをfileとする。`.obsidian`と`.git`は通常探索から除外し、内容を取得しない。
72
+ 上限のnullは、指定範囲にその上限を設けない指定である。上限を使う場合は事前に合意し、打ち切り結果を確認する。
73
+
74
+ intentはconventions、related、links、create、proofreadから選ぶ。
75
+ 規約を宣言した資料は、許可範囲内のパスとしてdeclared_rulesに指定する。
76
+ 観測は名前・フォルダー・プロパティ名と型・タグの事実であり、一般的な規約の推測とは区別する。
77
+
78
+ ## 原文、計画、適用
79
+
80
+ 以降のコマンドでは、共通の起動部分を省略している。
81
+
82
+ ```text
83
+ read --request request.json --path Notes/draft.md
84
+ plan --request request.json --candidate /absolute/workspace/candidate.md --review /absolute/workspace/review.json
85
+ apply --request request.json --plan /absolute/workspace/OPERATION_ID/plan.json --approve-plan-sha256 PRESENTED_HASH
86
+ ```
87
+
88
+ 添削ではintentをproofread、write_targetを対象パスにする。
89
+ 最初のreadではbase_sha256をnullにできる。候補作成前に取得した原文のSHA-256を設定してからplanを呼ぶ。
90
+ 原文を再取得せずにハッシュだけを新しいものに付け替えない。原文が変わったら候補とレビューも作り直す。
91
+ workspaceにはVault外の既存の絶対パスを指定し、コピーへの同意を得てworkspace_copiesをtrueにする。
92
+
93
+ レビューの形式は[japanese-proseの意味レビュー手順](../../japanese-prose/references/review-workflow.md)に従う。
94
+ planは入力の候補をそのまま検査し、検査のために保護対象を自動修復しない。
95
+ 合格した計画はworkspace内の操作IDごとの0700ディレクトリに保存する。
96
+ before.md、candidate.md、diff.patch、semantic-review.json、plan.jsonは0600で保持する。
97
+ 原文と候補が同じ場合はno_changesとなり、計画も保存も行わない。
98
+
99
+ 新規作成はintentをcreateにし、base_sha256をnullにする。
100
+ 完成ノートではdeliverableをnote、再利用用テンプレートではtemplateとする。
101
+ 両方とも本文・保存先・計画の承認が必要である。完成ノートに変数が残っていれば保存を拒否する。
102
+ 親フォルダーを先に用意し、対象を`.md`のパスにする。既存ファイルは上書きしない。
103
+ 新規ノートの権限は通常のファイル作成と同じくumaskを適用し、編集では既存の所有者・グループ・権限を維持する。
104
+
105
+ planの内容と差分を実際に確認してから、その計画のSHA-256を承認する。
106
+ applyの引数は承認されたバイト列を固定するためのもので、人間の承認の代わりにはならない。
107
+ applyは同じ親に`.kotonoha-OPERATION_ID.tmp`を作り、完全な内容を同期してから確定する。
108
+ 作成済み対象の再確認が必要な再実行では、その対象への読み取り許可も必要である。
109
+
110
+ ## 診断と失敗の読み方
111
+
112
+ stdoutのJSONにあるexit_codeは終了コードと一致する。
113
+
114
+ | 終了コード | 処理結果 | 対処 |
115
+ |---|---|---|
116
+ | 0 | completed | データと保存状態を確認する |
117
+ | 1 | failed | 入力または実行時の問題。保存状態がunknownなら内容確認が先 |
118
+ | 2 | not_performed | 権限、承認、競合、保護検査の理由を確認する |
119
+ | 3 | partial | 未処理・判定不能の範囲と理由を確認する |
120
+ | 4 | not_performed | 依存関係または必要機能の不足を確認する |
121
+
122
+ 未解決リンクと曖昧なリンクだけでは処理失敗にしない。
123
+ 名前だけのwikilinkは、部分範囲で候補が0件・1件でも判定不能となる。
124
+ 許可範囲で同名候補を2件確認できた場合は曖昧となる。
125
+ 明示パスと同一ノートの見出しは、必要な有限範囲を確認できれば部分範囲でも診断する。
126
+ コードとObsidianコメントの例示は数えず、外部URLは集計するだけでアクセスしない。
127
+
128
+ 見出しは平文の完全一致、大文字小文字とUnicodeは厳密比較とする。
129
+ 装飾・文字参照の変換、slug、ブロックID、添付ファイルなどには対応しない。
130
+ 閉じていないフェンス・コメント・frontmatterや非対応YAMLを正常として扱わない。
131
+ プロパティは平坦な辞書とスカラーまたは平坦なリストに限定し、アンカー、重複キー、入れ子を拒否する。
132
+ Obsidian自体と同じ解決結果になるとは限らない。
133
+
134
+ ## 保存の限界と復旧
135
+
136
+ 保存対象は通常ファイルに限り、シンボリックリンクを追跡しない。
137
+ LinuxのローカルFSでも確認できた方式だけを使う。未知のFSやネットワークFSは保存しない。
138
+ 所有者・グループ・権限を維持できない対象、複数ハードリンク、拡張属性付きの対象も保存しない。
139
+ SELinuxラベルも拡張属性に含む。安全機能の削除を回避策にしない。
140
+
141
+ 同じSkillの保存は直列化するが、最終照合直後の外部エディターの変更を完全には防げない。
142
+ ホストの同意の取り違え、LLMの意味判断、同権限プロセスの改変も完全には防げない。
143
+ 保護検査、意味レビュー、人間の差分承認を別々の確認として扱う。
144
+
145
+ receipt.jsonは保存後の検証済み記録、result.jsonは本文を含めない結果記録である。
146
+ 保存開始時にはresult.jsonへ結果未確定の記録を置き、強制終了を検出できるようにする。
147
+ 成功記録と現在の対象が一致すれば、再実行は再保存せずalready_appliedを返す。
148
+ 成功記録が不正、欠落した記録から以前の保存が疑われる、現在の内容が変わった、という場合は自動再保存しない。
149
+ unknownは「変更なし」を意味しない。対象、原文、候補、一時ファイル、記録を確認し、新しい計画が必要か判断する。
150
+ 一時ファイルや操作ディレクトリは利用者の確認なしに掃除しない。
151
+ 削除を依頼された場合も、確認した操作ディレクトリの既知ファイルだけを対象にする。