Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 0.75.0 — 2026-10-03

### runtime errorの収集と送信がWindowsで動く(ADR 0194)

- Windowsでは、Latticeの故障を1件も記録できなかった(0.73.0からは `collection: "unsupported"` と答えていた)。
storeを本人だけが触れる形で置けると確かめる方法が無かったためである。Windowsでも記録し、送れるようにした。
- 確かめ方: `icacls` でDACLを読み、本人・SYSTEM・Administratorsへの許可だけで出来ている時だけ使う。
それ以外は、POSIXと同じく `store_unsafe` で止める。
- 置き場(Windows):
- store: `%LOCALAPPDATA%\Lattice\runtime-errors\`。このフォルダは継承を切って本人・SYSTEM・Administrators
だけに絞る。他のaccountが触れる形で中身があるフォルダは、絞らずに止める。
- 送信の設定: `%LOCALAPPDATA%\Lattice\runtime-error-reporting.json`
- 合鍵: `%LOCALAPPDATA%\bughub\product-credentials\lattice.json`。他のaccountが読める形なら
`credential_unsafe`(理由 `acl_not_owner_only`)で、送らない。
- Windowsで収集を有効にするのは `lattice runtime-errors reporting enable --json` である。有効にするまでは
`collection: "disabled"` を返す。`unsupported` は、macOS・Linux・Windows以外のOSの答えとして残る。
- macOS・Linuxの動きは変わらない。
- 所有者は確かめない(`icacls` は所有者を返さない)。理由と範囲はADR 0194に書いた。

## 0.74.0 — 2026-10-03

### runtime errorの送信をLattice自身が持つ(ADR 0193)
Expand Down
19 changes: 14 additions & 5 deletions docs/01_integration-package.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,25 @@ native Windowsでは`HOST_PLATFORM_UNSUPPORTED`を返し、設定やstateへ書
(schema `lattice.runtime_errors.v1`。Caveat同型の工場契約)。**opt-in**=工場共有config
`~/.config/dotagents/factory-reporter.json`の`collection.enabled`か、Lattice自身の送信設定(下)の
どちらかが有効な時だけ収集する。storeそのものは外部送信しない。
固定catalog 5 code・fingerprint集約・cursor/ack・resolved+ack済み30日compact・POSIX owner-only検査で
固定catalog 5 code・fingerprint集約・cursor/ack・resolved+ack済み30日compact・owner-only検査で
fail closed。正典は`src/runtime-errors.mjs`
- owner-only検査: storeは本人だけが触れる形でしか使わない。確かめられなければ`store_unsafe`で止める。
POSIXはフォルダ0700・file 0600・所有者が本人。Windows(ADR 0194)は、DACLが本人・SYSTEM・Administratorsへの
許可だけで出来ていること(`icacls /save`のSDDLで読む。正典は`src/windows-owner-only.mjs`)。
- 置き場: `${XDG_STATE_HOME:-~/.local/state}/lattice/runtime-errors.json`。Windowsは
`%LOCALAPPDATA%\Lattice\runtime-errors\runtime-errors.json`で、このフォルダは継承を切って本人・SYSTEM・
Administratorsだけに絞る。他のaccountが触れる形で中身があるフォルダは、絞らずに止める。
- runtime errorの送信(ADR 0193): `lattice runtime-errors report --json`と
`lattice runtime-errors reporting <status|enable|disable> --json`。Lattice自身が、未受領の記録を
BugHubの製品報告の受け口へ送る。正典は`src/runtime-error-reporting.mjs`
- **既定では通信しない。** `reporting enable`を打った端末(設定は
`${XDG_CONFIG_HOME:-~/.config}/lattice/runtime-error-reporting.json`)で、BugHubの持ち主が置いた合鍵のfile
(`~/.config/bughub/product-credentials/lattice.json`、本人所有・0600・symlinkでない)がある時だけ送る。
dotagentsの設定は読まない。宛先は合鍵のfileの`url`。
- Windowsの置き場は、設定が`%LOCALAPPDATA%\Lattice\runtime-error-reporting.json`、合鍵が
`%LOCALAPPDATA%\bughub\product-credentials\lattice.json`(symlinkでなく、DACLが本人・SYSTEM・
Administratorsだけ。他のaccountが読めれば`credential_unsafe`・理由`acl_not_owner_only`)。
Windowsで収集を有効にするのはこの送信設定で、dotagentsがWindowsで使う設定の置き場は読まない。
- 秘密は通信に載せない。`Authorization: BugHub-HMAC-SHA256 key_id=…, ts=…, sig=…`
(`sig = HMAC-SHA256(secret, ts + "\n" + SHA-256(送るバイト列))`)。
- 本文は`schema_version`・`report_id`・`product_id`・`installed_version`・`observed_at`・`runtime_errors`・
Expand All @@ -241,11 +251,10 @@ native Windowsでは`HOST_PLATFORM_UNSUPPORTED`を返し、設定やstateへ書
- 送る時機: 故障を記録した直後と、以後のCLI実行(`hooks`を除く)の終わりに、切り離した子processで送る。
1分に1回まで、同じ中身の送り直しは1時間に1回まで。手で打つ`report`はこの制限を見ない。
- `LATTICE_RUNTIME_ERROR_REPORTING=0`は、既定の置き場の送信設定を読まない(試験と自動化の口)。
- Windowsは収集に対応しないので、送信も`unsupported`と答える。
- `diagnostics.collection`は`enabled`・`disabled`・`unsupported`の3値。`unsupported`は「このOSでは収集に
対応しない」という製品の答えで、Windowsが返す(storeの所有者と権限をPOSIXの形で確かめられない)。
設定が有効でも記録は作らない。`status`・`cursor`・配列・`diagnostics`のキーは`disabled`の時と同じ形
(`not_applicable`・すべて0・空)。受け側の前提はdotagents `49709de`以降。
対応しない」という製品の答えで、storeを本人だけに絞る方法を持たないOS(macOS・Linux・Windows以外)が返す。
送信も`unsupported`と答える。設定が有効でも記録は作らない。`status`・`cursor`・配列・`diagnostics`の
キーは`disabled`の時と同じ形(`not_applicable`・すべて0・空)。受け側の前提はdotagents `49709de`以降。
- 各記録は任意の`safe_context`を持つ: `command_kind`(落ちたCLIの面。`run.list`・`todo.start`等、
Latticeが持つ一覧の語だけ。無ければ`other`)、`error_kind`(例外の種類。一覧に無ければ`other`)、
`cause_code`(Nodeが付けるerror code。無ければ`none`)。付ける時は3つを必ずそろえる。
Expand Down
3 changes: 2 additions & 1 deletion docs/adr/0193-product-owned-runtime-error-reporting.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ADR 0193: runtime errorの送信をLattice自身が持つ

- Status: accepted
- Status: accepted(Decision 9 と、Consequences の「Windowsの端末からは送れない」は
[ADR 0194](0194-runtime-error-store-on-windows.md) が置き換える)
- Date: 2026-10-03
- Supersedes: runtime error storeの「reporting(BugHub送信)はdotagents adapter所有」
(`docs/01_integration-package.md` 5.5、`src/runtime-errors.mjs`冒頭)
Expand Down
83 changes: 83 additions & 0 deletions docs/adr/0194-runtime-error-store-on-windows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# ADR 0194: runtime error storeをWindowsでも本人だけが触れる形で置く

- Status: accepted
- Date: 2026-10-03
- Supersedes: [ADR 0193](0193-product-owned-runtime-error-reporting.md) の Decision 9
(Windowsは収集に対応しない)と、`src/runtime-errors.mjs`冒頭の「POSIX専用」

## Context

runtime error storeは、本人だけが触れる形でしか使わない。POSIXではフォルダが0700、fileが0600、所有者が
本人であることを確かめ、確かめられなければ`store_unsafe`で止める。Windowsにはmodeもuidも無く、
確かめる方法を持たなかったので、Windowsでは記録を1件も作らなかった(0.73.0からは`unsupported`と答える)。
オーナーの端末にはWindowsが1台あり、そこで起きたLatticeの故障は誰にも届かなかった。オーナーはこれを
残っている不具合として扱うと裁定した。

Windowsで権限を表すのはDACL(誰に何を許すかの一覧)である。実機(Windows 11)で確かめたこと:

- `icacls <path> /save <file>`は、DACLをSDDLで書き出す。SDDLは表示言語に依らない。
`icacls <path>`の画面表示は、accountの名前が表示言語で変わる。
- `whoami /user /fo csv /nh`は、自分のSIDを返す。
- 既定の`%LOCALAPPDATA%`の下に作ったフォルダは、親の権限を継ぐ。確かめた端末では、親が別のローカル
accountへ読み取りを継承で許していた。置くだけでは本人だけにならない。
- フォルダの継承を切って本人・SYSTEM・Administratorsだけに絞ると、中に作るfileは同じ権限を引き継ぎ、
renameで置き換えた後も保たれる。後から他のaccountへ権限を足すと、SDDLに現れる。
- どちらのcommandも20ms前後で返る。PowerShellの`Get-Acl`は400ms前後かかり、起動のしかたによっては失敗した。

## Decision

1. Windowsを収集の対象にする。`diagnostics.collection`は、Windowsでも設定に従って`enabled`か`disabled`を
返す。`unsupported`は、storeを本人だけに絞る方法を持たないOS(macOS・Linux・Windows以外)の答えとして残す。
2. Windowsの「本人だけ」は、**DACLが本人・SYSTEM・Administratorsへの許可だけで出来ていること**とする。
SYSTEMとAdministratorsは、その端末のどのfileも読める立場なので、数に入れる。拒否・条件つき・object用の
ACE、DACLの無い形、SDDLとして読めない出力は、意味を確かめずにすべて通さない。
3. DACLは`icacls /save`で、自分のSIDは`whoami /user`で読む。どちらも`%SystemRoot%\System32`の実物を
絶対pathで呼ぶ。`icacls`はfileへしか書き出せないので、出力はstoreのフォルダへ置いてすぐ消す。
他のaccountが書ける場所(利用者の一時フォルダ等)へは置かない。
4. storeは`%LOCALAPPDATA%\Lattice\runtime-errors\`という専用のフォルダへ置く。`%LOCALAPPDATA%\Lattice`には
他の機能のfileがあり、フォルダごと絞れない。
- フォルダは、継承を切り、本人・SYSTEM・Administratorsだけに絞る。空のフォルダ(作った直後)を絞る時は、
所有者も本人にする。
- 他のaccountが触れる形なのに中身があるフォルダは、絞らずに`store_unsafe`で止める。中身を信用できない。
- 絞った後に、誰かがフォルダやfileへ権限を足した時も`store_unsafe`で止める。Latticeは足された権限を消さない。
- 確認は、POSIXと同じく読む時と書く時の毎回行う。
5. 設定は`%LOCALAPPDATA%\Lattice\runtime-error-reporting.json`、合鍵はBugHubの契約どおり
`%LOCALAPPDATA%\bughub\product-credentials\lattice.json`から読む。`XDG_CONFIG_HOME`・`XDG_STATE_HOME`を
明示した時は、どのOSでもそちらを使う。
6. 合鍵のfileも同じ判定で確かめる。他のaccountが読める形なら`credential_unsafe`(理由`acl_not_owner_only`)、
確かめる場所(storeのフォルダ)を用意できなければ理由`acl_unverifiable`を返す。合鍵のフォルダへは何も書かない。
7. Windowsで収集を有効にするのは、Lattice自身の送信設定(`runtime-errors reporting enable`)である。
工場の設定は`${XDG_CONFIG_HOME:-~/.config}/dotagents/factory-reporter.json`だけを読み、dotagentsが
Windowsで使う置き場(`%LOCALAPPDATA%\dotagents\factory-reporter\config.json`)は読まない。
エラーを上げるのは製品の責務(ADR 0193)なので、工場の設定への依存を新しく足さない。
8. 自動送信の判定は、送るものがあるかを先に見て、合鍵は最後に確かめる。Windowsでは合鍵の確認が
外のprogramを起こすので、送るものが無い時のCLI実行に載せない。

## Consequences

- **所有者は確かめない。** `icacls`は所有者を返さない。所有者は、DACLが本人だけでも、後から自分へ権限を
足せる。これが効くのは、別のaccountが`%LOCALAPPDATA%\Lattice`へ書けて、storeのフォルダを先に作れる
端末だけである。そういう端末では、同じaccountがその利用者のprogramを差し替えられるので、所有者を
確かめても守れるものが増えない。足された権限は、次の確認で`store_unsafe`になる。
- storeのfileが在る端末では、送信を有効にしている間、送るものが無い時でもCLIの実行ごとに`icacls`が2回と
`whoami`が1回走る(確かめた端末で合わせて50ms前後)。故障を1件も記録していない端末では走らない。
- `icacls`か`whoami`が無い・失敗する端末では`store_unsafe`になり、記録を作らない。`diagnostics`は
`status: unavailable`を返す。本人のSIDがSDDLで別名(組み込みのAdministratorの`LA`等)で書かれる
accountも、本人と見分けられないので同じ扱いになる。
- 0.73.0で`unsupported`を返していたWindowsの端末は、送信を有効にするまで`disabled`を返す。
受け側(dotagents)は3値とも受ける。
- 「CLIが契約の外で落ちると記録される」試験は、Windowsでは走らない。試験が使う入力(`.lattice`がfile)は、
Windowsでは`lstat`がENOENTを返し、契約内のerrorで返る。記録・置き場・権限は、Windowsの実機の試験が確かめる。

## Acceptance

Windowsの実機(CIの`windows-native`)で確かめる。

- 他のaccountへ継承で読み取りを許すフォルダの下で記録すると、storeのフォルダは継承を切られ、
フォルダもfileも本人・SYSTEM・Administratorsだけになる。記録は読め、置き換えの後も権限が保たれる。
- storeのfileかフォルダへ他のaccountの権限を足すと、読むのも書くのも`store_unsafe`で止まる。
- 親の権限を継いだままの空のフォルダは絞って使い、中身のあるものは使わない。
- 合鍵へ他のaccountの読み取りを足すと`credential_unsafe`(`acl_not_owner_only`)になり、送らない。
- 既定の置き場(`%LOCALAPPDATA%`)で、CLIから送信を有効にし、記録を読み、解決にできる。
- 送信を有効にした端末で記録が出来ると、次のCLI実行の後に子processが届け、ackが進む。
- SDDLの判定は、どのOSでも走る試験で固定する(`test/windows-owner-only.test.mjs`)。
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@quolu/lattice",
"version": "0.74.0",
"version": "0.75.0",
"description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
"author": {
"name": "Quo / クオ at kitepon.dev",
Expand Down
5 changes: 5 additions & 0 deletions scripts/run-product-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ async function collectTests(directory, prefix = '') {
return files;
}

// 端末の本物のhost設定の置き場を指す変数(`src/setup-hosts.mjs`が読む)。試験は一時のHOMEを渡すが、
// これらが残っていると、CLIはHOMEでなくそちらを使い、利用者の本物の設定を書き換える。
export const HOST_CONFIG_ENV = Object.freeze(['CLAUDE_CONFIG_DIR', 'CODEX_HOME', 'GROK_HOME']);

export function productTestEnvironment(parentEnv = process.env) {
// product gateはsuite単位ですでに全CPU並列である。各integration fixtureが
// sensor init用WASM poolまで最大8本prewarmするとnested oversubscriptionになり、
Expand All @@ -96,6 +100,7 @@ export function productTestEnvironment(parentEnv = process.env) {
const env = { ...parentEnv, LATTICE_DASHBOARD_AUTOSTART: '0',
LATTICE_SENSOR_PARSE_WORKERS: '1', LATTICE_RUNTIME_ERROR_REPORTING: '0' };
delete env.FORCE_COLOR;
for (const name of HOST_CONFIG_ENV) delete env[name];
return env;
}

Expand Down
Loading
Loading