@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 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
- npm publish --access public
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`のcanonical
226
- JSON+LFを用意し、次で作成します。既存のv1〜v3は互換契約として維持され、`--schema-version`で取得できます。
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.78.1",
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 `${INFO}\n`;
975
- if (host === 'cursor') return `${JSON.stringify({ additional_context: INFO })}\n`;
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: INFO },
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') {