@quolu/lattice 0.78.1 → 0.79.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 +21 -3
- package/README.md +22 -0
- package/bin/lattice.mjs +31 -0
- package/docs/schemas/lattice.plan_backchain.v1.schema.json +103 -0
- package/package.json +4 -3
- package/src/cli-help.mjs +4 -0
- package/src/hooks-cli.mjs +19 -9
- package/src/plan-backchain.mjs +628 -0
- package/src/project-cli.mjs +3 -2
- package/src/todo-authoring-input.mjs +2 -0
- package/src/todo-cli.mjs +19 -6
- package/src/todo-revision.mjs +47 -7
- package/src/todo-store.mjs +118 -3
package/README.ja.md
CHANGED
|
@@ -162,7 +162,7 @@ release commitを既定branchへ着地・pushしてから、同じ版をregistry
|
|
|
162
162
|
```bash
|
|
163
163
|
npm run ci
|
|
164
164
|
npm run verify:release-commit
|
|
165
|
-
|
|
165
|
+
gh workflow run publish.yml --repo kitepon/Lattice --ref main
|
|
166
166
|
npm install -g @quolu/lattice@<version>
|
|
167
167
|
lattice --version
|
|
168
168
|
lattice status --json
|
|
@@ -188,6 +188,9 @@ npm run check
|
|
|
188
188
|
npm run ci
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
最初にrootの依存を`npm ci`で導入します。`npm test`は内蔵sensorの依存とbuildを準備してから
|
|
192
|
+
製品testを実行するため、隔離checkoutでも別のbuild手順は要りません。
|
|
193
|
+
|
|
191
194
|
製品localの保守で必要な時だけ次を使います。
|
|
192
195
|
|
|
193
196
|
```bash
|
|
@@ -222,8 +225,23 @@ lattice status --json
|
|
|
222
225
|
|
|
223
226
|
`state`は`uninitialized | ready | active_run | invalid`のいずれかです。`uninitialized`は
|
|
224
227
|
正常な未初期化状態で、`next_action`が正規の初期authoring入口を返します。新規planはPhase監査と
|
|
225
|
-
ToDo schedulingを分離し、全ToDoに設計メモを持つ`lattice.plan_create_input.v4`の
|
|
226
|
-
JSON
|
|
228
|
+
ToDo schedulingを分離し、全ToDoに設計メモを持つ`lattice.plan_create_input.v4`の
|
|
229
|
+
repo内JSONを用意し、次で作成します。既存のv1〜v3は互換契約として維持され、`--schema-version`で取得できます。
|
|
230
|
+
|
|
231
|
+
AIへ「Latticeで工程作って」と頼むと、製品hookが機能逆算の入口を示します。AIは最終成果と
|
|
232
|
+
受入条件から必要な機能を逆向きにたどり、各ToDoが必要とする機能と作る機能を定義します。
|
|
233
|
+
全体目的、後続の利用、未知の理由と次の問いも担当者へ渡します。同じ入口で既存storeへの
|
|
234
|
+
新plan追加と、開発中にゴールが変わったplanの後継版作成まで行えます。
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
lattice plan backchain --schema --json
|
|
238
|
+
lattice plan backchain --input .lattice/backchain.json --dry-run --json
|
|
239
|
+
lattice plan backchain --input .lattice/backchain.json --json
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`backchain.json`はrepo内の`lattice.plan_create_input.v4`を`authoring_ref`で参照します。
|
|
243
|
+
機能の意味を判断するのは操作AIで、Latticeは宣言した機能・工程・依存の接続を検査します。
|
|
244
|
+
旧planは従来どおり読めます。
|
|
227
245
|
|
|
228
246
|
```bash
|
|
229
247
|
lattice plan create --schema-version 4 --json # v4のJSON Schemaを取る(作成はしない)
|
package/README.md
CHANGED
|
@@ -175,6 +175,25 @@ AIの設定がまだ無い初回は`lattice setup --host claude --json`のよう
|
|
|
175
175
|
|
|
176
176
|
## Quick start
|
|
177
177
|
|
|
178
|
+
Ask a configured AI host **「Latticeで工程作って」** to plan from an outcome. The
|
|
179
|
+
Lattice prompt hook points the agent to `lattice plan backchain`: the agent starts
|
|
180
|
+
with the final acceptance conditions, works backward through required capabilities,
|
|
181
|
+
and defines what each ToDo needs and provides. The result carries the overall purpose
|
|
182
|
+
and downstream consumers into `todo show` and `todo start`. Unknown capabilities keep
|
|
183
|
+
their reason and next question. The same entry revises an active plan when the goal
|
|
184
|
+
changes, preserving completed work that still satisfies the new goal.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
lattice plan backchain --schema --json
|
|
188
|
+
lattice plan backchain --input .lattice/backchain.json --dry-run --json
|
|
189
|
+
lattice plan backchain --input .lattice/backchain.json --json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The input refers to an AI-authored `lattice.plan_create_input.v4` file in the same
|
|
193
|
+
repository. Lattice validates the declared feature relationships and records them;
|
|
194
|
+
the operating agent judges their meaning. Existing plans without a feature chain keep
|
|
195
|
+
their previous behavior.
|
|
196
|
+
|
|
178
197
|
Every project begins with typed discovery. Never guess from directory layout:
|
|
179
198
|
|
|
180
199
|
```bash
|
|
@@ -326,6 +345,9 @@ npm run check # syntax + control-character gate
|
|
|
326
345
|
npm run ci # full gate
|
|
327
346
|
```
|
|
328
347
|
|
|
348
|
+
The test entry builds the bundled sensor before running product fixtures. Install
|
|
349
|
+
the root dependencies with `npm ci` once; the test entry prepares the sensor package.
|
|
350
|
+
|
|
329
351
|
Product-local maintenance, when relevant:
|
|
330
352
|
|
|
331
353
|
```bash
|
package/bin/lattice.mjs
CHANGED
|
@@ -112,6 +112,37 @@ if (help !== null) {
|
|
|
112
112
|
} catch (error) {
|
|
113
113
|
process.exitCode = projectCliFailure(process.stderr, error);
|
|
114
114
|
}
|
|
115
|
+
} else if (args.length === 4 && args[0] === 'plan' && args[1] === 'backchain'
|
|
116
|
+
&& args[2] === '--schema' && args[3] === '--json') {
|
|
117
|
+
const { backchainSchema } = await import('../src/plan-backchain.mjs');
|
|
118
|
+
const { projectCliFailure } = await import('../src/project-cli.mjs');
|
|
119
|
+
try {
|
|
120
|
+
process.stdout.write(`${JSON.stringify(await backchainSchema())}\n`);
|
|
121
|
+
process.exitCode = 0;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
process.exitCode = projectCliFailure(process.stderr, error);
|
|
124
|
+
}
|
|
125
|
+
} else if (args[0] === 'plan' && args[1] === 'backchain') {
|
|
126
|
+
const { matchFlagCommand } = await import('../src/todo-authoring-input.mjs');
|
|
127
|
+
const flags = matchFlagCommand(args, ['plan', 'backchain'], {
|
|
128
|
+
known: ['input', 'dry-run', 'json'], required: ['input'],
|
|
129
|
+
booleans: ['dry-run', 'json'],
|
|
130
|
+
});
|
|
131
|
+
if (flags === null || flags.json !== true) {
|
|
132
|
+
process.stderr.write(`lattice: unsupported command or arguments: ${args.join(' ')}\n`);
|
|
133
|
+
process.exitCode = 2;
|
|
134
|
+
} else {
|
|
135
|
+
const { runPlanBackchain } = await import('../src/plan-backchain.mjs');
|
|
136
|
+
const { projectCliFailure } = await import('../src/project-cli.mjs');
|
|
137
|
+
try {
|
|
138
|
+
process.exitCode = await runPlanBackchain({
|
|
139
|
+
cwd: process.cwd(), inputRef: flags.input, dryRun: flags['dry-run'] === true,
|
|
140
|
+
stdout: process.stdout, env: process.env,
|
|
141
|
+
});
|
|
142
|
+
} catch (error) {
|
|
143
|
+
process.exitCode = projectCliFailure(process.stderr, error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
115
146
|
} else if (args.length === 2 && args[0] === 'factory-diagnostics' && args[1] === '--json') {
|
|
116
147
|
const { buildFactoryDiagnostics } = await import('../src/factory-diagnostics.mjs');
|
|
117
148
|
const diagnostics = await buildFactoryDiagnostics();
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_backchain.v1.schema.json",
|
|
4
|
+
"title": "lattice.plan_backchain.v1",
|
|
5
|
+
"description": "AI-authored backward feature chain. Start with the final outcome, declare each required capability and what supplies it, then bind every registered ToDo to the capabilities it needs and provides. Unknowns carry a reason and a question instead of blocking authoring.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema", "authoring_ref", "project_id", "plan_key", "plan_version", "goal", "features", "tasks", "chain_digest"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "const": "lattice.plan_backchain.v1" },
|
|
11
|
+
"authoring_ref": { "$ref": "#/$defs/repoRef" },
|
|
12
|
+
"project_id": { "$ref": "#/$defs/identifier" },
|
|
13
|
+
"plan_key": { "$ref": "#/$defs/identifier" },
|
|
14
|
+
"plan_version": { "$ref": "#/$defs/identifier" },
|
|
15
|
+
"goal": {
|
|
16
|
+
"type": "object", "additionalProperties": false,
|
|
17
|
+
"required": ["purpose", "outcome", "acceptance", "required_feature_ids"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"purpose": { "$ref": "#/$defs/text" },
|
|
20
|
+
"outcome": { "$ref": "#/$defs/text" },
|
|
21
|
+
"acceptance": { "$ref": "#/$defs/text" },
|
|
22
|
+
"required_feature_ids": { "$ref": "#/$defs/nonemptyIds" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"features": {
|
|
26
|
+
"type": "array", "minItems": 1, "maxItems": 2048,
|
|
27
|
+
"items": { "$ref": "#/$defs/feature" }
|
|
28
|
+
},
|
|
29
|
+
"tasks": {
|
|
30
|
+
"type": "array", "minItems": 1, "maxItems": 512,
|
|
31
|
+
"items": { "$ref": "#/$defs/task" }
|
|
32
|
+
},
|
|
33
|
+
"continuity": {
|
|
34
|
+
"type": "array", "maxItems": 512, "uniqueItems": true,
|
|
35
|
+
"description": "Optional goal-change decisions for predecessor tasks. A carry decision is an explicit AI judgment that previous work still satisfies the revised goal; reset and retire remain available without proving equivalence.",
|
|
36
|
+
"items": { "$ref": "#/$defs/continuity" }
|
|
37
|
+
},
|
|
38
|
+
"chain_digest": { "$ref": "#/$defs/digest" }
|
|
39
|
+
},
|
|
40
|
+
"$defs": {
|
|
41
|
+
"identifier": { "type": "string", "pattern": "^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$" },
|
|
42
|
+
"digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
43
|
+
"text": { "type": "string", "minLength": 1, "maxLength": 16384, "pattern": "\\S" },
|
|
44
|
+
"repoRef": {
|
|
45
|
+
"type": "string", "minLength": 1, "maxLength": 1024,
|
|
46
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*$"
|
|
47
|
+
},
|
|
48
|
+
"ids": { "type": "array", "maxItems": 2048, "uniqueItems": true, "items": { "$ref": "#/$defs/identifier" } },
|
|
49
|
+
"nonemptyIds": { "allOf": [{ "$ref": "#/$defs/ids" }, { "minItems": 1 }] },
|
|
50
|
+
"provider": {
|
|
51
|
+
"oneOf": [
|
|
52
|
+
{
|
|
53
|
+
"type": "object", "additionalProperties": false,
|
|
54
|
+
"required": ["kind", "task_id"],
|
|
55
|
+
"properties": { "kind": { "const": "task" }, "task_id": { "$ref": "#/$defs/identifier" } }
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "object", "additionalProperties": false,
|
|
59
|
+
"required": ["kind", "evidence_ref"],
|
|
60
|
+
"properties": { "kind": { "const": "existing" }, "evidence_ref": { "$ref": "#/$defs/repoRef" } }
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "object", "additionalProperties": false,
|
|
64
|
+
"required": ["kind", "reason", "question"],
|
|
65
|
+
"properties": {
|
|
66
|
+
"kind": { "const": "unknown" },
|
|
67
|
+
"reason": { "$ref": "#/$defs/text" },
|
|
68
|
+
"question": { "$ref": "#/$defs/text" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"feature": {
|
|
74
|
+
"type": "object", "additionalProperties": false,
|
|
75
|
+
"required": ["feature_id", "capability", "acceptance", "requires_feature_ids", "provider"],
|
|
76
|
+
"properties": {
|
|
77
|
+
"feature_id": { "$ref": "#/$defs/identifier" },
|
|
78
|
+
"capability": { "$ref": "#/$defs/text" },
|
|
79
|
+
"acceptance": { "$ref": "#/$defs/text" },
|
|
80
|
+
"requires_feature_ids": { "$ref": "#/$defs/ids" },
|
|
81
|
+
"provider": { "$ref": "#/$defs/provider" }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"task": {
|
|
85
|
+
"type": "object", "additionalProperties": false,
|
|
86
|
+
"required": ["task_id", "requires_feature_ids", "provides_feature_ids"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
89
|
+
"requires_feature_ids": { "$ref": "#/$defs/ids" },
|
|
90
|
+
"provides_feature_ids": { "$ref": "#/$defs/nonemptyIds" }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"continuity": {
|
|
94
|
+
"type": "object", "additionalProperties": false,
|
|
95
|
+
"required": ["task_id", "policy", "reason"],
|
|
96
|
+
"properties": {
|
|
97
|
+
"task_id": { "$ref": "#/$defs/identifier" },
|
|
98
|
+
"policy": { "enum": ["carry", "reset", "retire"] },
|
|
99
|
+
"reason": { "$ref": "#/$defs/text" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quolu/lattice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.0",
|
|
4
4
|
"description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Quo / クオ at kitepon.dev",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"docs/schemas/lattice.plan_create_input.v3.schema.json",
|
|
31
31
|
"docs/schemas/lattice.plan_create_input.v4.schema.json",
|
|
32
32
|
"docs/schemas/lattice.plan_scope_review.v1.schema.json",
|
|
33
|
+
"docs/schemas/lattice.plan_backchain.v1.schema.json",
|
|
33
34
|
"docs/schemas/lattice.todo_revision.v2.schema.json",
|
|
34
35
|
"docs/schemas/lattice.todo_revision_set.v3.schema.json",
|
|
35
36
|
"docs/schemas/lattice.phase_todo_revision.v3.schema.json",
|
|
@@ -79,8 +80,8 @@
|
|
|
79
80
|
"prepack": "npm --prefix sensor run build",
|
|
80
81
|
"verify:release-commit": "node scripts/verify-release-commit.mjs",
|
|
81
82
|
"prepublishOnly": "npm run verify:release-commit",
|
|
82
|
-
"test": "node scripts/run-product-tests.mjs",
|
|
83
|
-
"test:profile": "node scripts/run-product-tests.mjs",
|
|
83
|
+
"test": "npm --prefix sensor ci && npm --prefix sensor run build && node scripts/run-product-tests.mjs",
|
|
84
|
+
"test:profile": "npm --prefix sensor ci && npm --prefix sensor run build && node scripts/run-product-tests.mjs",
|
|
84
85
|
"test:sensor": "npm --prefix sensor test",
|
|
85
86
|
"check": "node scripts/check-syntax.mjs",
|
|
86
87
|
"ci": "npm run check:docs && npm run test && npm run test:sensor && npm run check && npm run check:cli-surface && npm run check:open-questions && npm run check:reachability && npm run verify:todo-store",
|
package/src/cli-help.mjs
CHANGED
|
@@ -29,6 +29,9 @@ Commands:
|
|
|
29
29
|
# 結果へdispatch_shapeを載せる。--serialization-reviewedは互換のため受理するだけで門ではない
|
|
30
30
|
create --schema --json # 既定は最新版(v4)のJSON Schemaを返す
|
|
31
31
|
create --schema-version <1|2|3|4> --json
|
|
32
|
+
backchain --schema --json # 最終成果から必要な機能を逆算する入力契約
|
|
33
|
+
backchain --input <file> [--dry-run] --json
|
|
34
|
+
# 各工程の必要・提供機能を照合し、初回又は既存storeへのplan登録まで行う
|
|
32
35
|
show <plan_key> --json # task・依存・phase・状態をplan本体から1コマンドで投影する
|
|
33
36
|
compile --request <request.json>
|
|
34
37
|
compile --schema --json # lattice.run_request.v1 の JSON Schema を出す
|
|
@@ -231,6 +234,7 @@ const SUBCOMMAND_USAGE = Object.freeze({
|
|
|
231
234
|
'plan create': 'plan create --input <file> [--serialization-reviewed] | --schema --json | --schema-version <1|2|3|4> --json',
|
|
232
235
|
'plan show': 'plan show <plan_key> --json',
|
|
233
236
|
'plan scope-review': 'plan scope-review --plan-input <plan-create-or-extraction.json> --review <scope-review.json> --json | --schema --json',
|
|
237
|
+
'plan backchain': 'plan backchain --input <file> [--dry-run] --json | --schema --json',
|
|
234
238
|
'plan compile': 'plan compile --request <request.json> | --schema --json',
|
|
235
239
|
'plan verify': 'plan verify --request <request.json> --plan <plan.json>',
|
|
236
240
|
'run start': 'run start --request <request.json> --executor <adapter>'
|
package/src/hooks-cli.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import { hostPaths } from './setup-hosts.mjs';
|
|
12
12
|
|
|
13
13
|
const INFO = 'INFO: このrepoにはLattice sensor index(.lattice/sensor/)があります。コード構造の調査はsensor入口(MCP: lattice_sensor_explore 等/CLI: lattice sensor)を優先できます。';
|
|
14
|
+
const BACKCHAIN_INFO = 'Lattice工程作成: 最終成果の目的と受入条件から必要な機能を逆算し、各機能の前提・提供元と各ToDoの必要・提供機能を定義する。未知は理由と次の問いを残す。まず lattice status --json と lattice plan backchain --schema --json を読み、repo内のauthoring JSONと機能契約を作る。lattice plan backchain --input <機能契約.json> --json が初回作成・既存storeへの追加・ゴール変更後の工程修正まで行う。AIが意味を判断し、Latticeは宣言の接続と記録を検査する。';
|
|
14
15
|
const HOSTS = new Set(['claude', 'codex', 'cursor']);
|
|
15
16
|
const HOST_USAGE = 'usage: lattice hooks <install|status|uninstall|emit> --host <claude|codex|cursor>';
|
|
16
17
|
const MAX_STDIN_BYTES = 64 * 1024;
|
|
@@ -970,11 +971,11 @@ async function acquireClaim(claim) {
|
|
|
970
971
|
}
|
|
971
972
|
}
|
|
972
973
|
|
|
973
|
-
function outputLine(host) {
|
|
974
|
-
if (host === 'claude') return `${
|
|
975
|
-
if (host === 'cursor') return `${JSON.stringify({ additional_context:
|
|
974
|
+
function outputLine(host, message = INFO) {
|
|
975
|
+
if (host === 'claude') return `${message}\n`;
|
|
976
|
+
if (host === 'cursor') return `${JSON.stringify({ additional_context: message })}\n`;
|
|
976
977
|
return `${JSON.stringify({
|
|
977
|
-
hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext:
|
|
978
|
+
hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: message },
|
|
978
979
|
})}\n`;
|
|
979
980
|
}
|
|
980
981
|
|
|
@@ -986,16 +987,25 @@ async function writeOutput(stdout, line) {
|
|
|
986
987
|
|
|
987
988
|
async function emit(host, env, stdin, stdout, spawnImpl, gitTimeoutMs, testHooks) {
|
|
988
989
|
if (env.LATTICE_HOOKS === 'off') return 0;
|
|
989
|
-
let state;
|
|
990
|
-
try { state = await secureStateDirectory(env, ['lattice', 'hooks']); } catch {
|
|
991
|
-
stdout.write('Lattice hooks: state directory unavailable\n');
|
|
992
|
-
return 0;
|
|
993
|
-
}
|
|
994
990
|
let event;
|
|
995
991
|
try { event = await readHookInput(stdin); } catch (error) {
|
|
992
|
+
let state;
|
|
993
|
+
try { state = await secureStateDirectory(env, ['lattice', 'hooks']); } catch {
|
|
994
|
+
stdout.write('Lattice hooks: state directory unavailable\n');
|
|
995
|
+
return 0;
|
|
996
|
+
}
|
|
996
997
|
await recordOrDiagnose(state, stdout, error.message, 'cannot record invalid stdin');
|
|
997
998
|
return 0;
|
|
998
999
|
}
|
|
1000
|
+
if (typeof event.prompt === 'string' && /Latticeで工程(?:を)?作って/iu.test(event.prompt)) {
|
|
1001
|
+
await writeOutput(stdout, outputLine(host, BACKCHAIN_INFO));
|
|
1002
|
+
return 0;
|
|
1003
|
+
}
|
|
1004
|
+
let state;
|
|
1005
|
+
try { state = await secureStateDirectory(env, ['lattice', 'hooks']); } catch {
|
|
1006
|
+
stdout.write('Lattice hooks: state directory unavailable\n');
|
|
1007
|
+
return 0;
|
|
1008
|
+
}
|
|
999
1009
|
const git = await gitRoot(event.cwd, spawnImpl, gitTimeoutMs);
|
|
1000
1010
|
if (git.kind === 'not_git') return 0;
|
|
1001
1011
|
if (git.kind === 'error') {
|