merge: compose current auth migration with Ticket case schema 91
This commit is contained in:
@@ -26,11 +26,96 @@ auto-read files, references, retained tail, and TaskStore context. Exceeding it
|
||||
warning and continues without discarding the generated summary or stripping auto-read/retained
|
||||
content. `0` disables this warning. Normal request-time safety checks still apply after activation.
|
||||
|
||||
## Large sessions
|
||||
## Compactor inputs and continuity policy
|
||||
|
||||
Large-session compaction should not send an entire prefix transcript as the summary input. Prefer bounded overview/index inputs plus exploration, then keep the retained tail small and explicit.
|
||||
The compactor receives the previous Host-produced summary in full as mandatory source
|
||||
material, separately from the bounded overview of this Segment's ordinary prefix. It must
|
||||
preserve ongoing user goals, directives, completion conditions, and unresolved work from
|
||||
that summary and from the current Segment's user messages. Recent progress and the
|
||||
TaskStore snapshot are supporting evidence, not replacements for the original request.
|
||||
Important wording or ambiguous completion state should be checked through exploration
|
||||
rather than reconstructed from recent activity.
|
||||
|
||||
This keeps compaction cost predictable and avoids turning a context-recovery mechanism into the largest prompt in the session.
|
||||
The first model input includes:
|
||||
|
||||
- `## Initial compaction summary (full text)`, when a prior summary exists, containing its
|
||||
complete body without overview truncation;
|
||||
- recently touched file references, when available;
|
||||
- the full current Session TaskStore snapshot;
|
||||
- `## Conversation overview/index`, a bounded index of the ordinary prefix selected for
|
||||
summarization, with exploration tools for omitted details.
|
||||
|
||||
The inherited summary is selected by `LoggedSessionHistoryOrigin::CompactionSummary`,
|
||||
not by message role or pasted text. Only the Host-produced opening compaction summary
|
||||
receives that origin. Auto-read contents, related-file context, and TaskStore snapshots
|
||||
remain `DerivedSummary` and follow ordinary prefix/retention policy. A user or system
|
||||
message containing `[Compacted context summary]` is not sufficient to qualify.
|
||||
|
||||
Restore recognizes one explicitly adopted legacy construction: the first history item of
|
||||
an `AnnotatedSegmentStart` with `compacted_from: Some(...)`, whose origin is
|
||||
`DerivedSummary`, whose role is System, and whose text starts with the exact Host summary
|
||||
marker. That item is interpreted as `CompactionSummary` in restored history. Restore does
|
||||
not promote later derived items, user messages, unknown origins, or marker-bearing items
|
||||
without compacted lineage, and does not rewrite the original durable record.
|
||||
|
||||
## Retained tail
|
||||
|
||||
The retained-token budget selects an ordinary history tail, at item granularity. Retained
|
||||
entries remain in the replacement Segment with their item and metadata preserved, except
|
||||
for documented binary-attachment projection. They are outside the prefix overview.
|
||||
|
||||
The previous `CompactionSummary` is an explicit exception: its full body is supplied to the
|
||||
compactor regardless of whether the cut lies before or after it, but the old summary is
|
||||
excluded from both the ordinary overview and the retained tail. The newly generated
|
||||
summary supersedes it. Keeping both would duplicate inherited context on every compaction.
|
||||
Other derived context is not removed merely because its origin is `DerivedSummary`.
|
||||
|
||||
## Limits and large sessions
|
||||
|
||||
Large-session compaction does not send an entire prefix transcript as the initial input.
|
||||
The ordinary prefix uses a bounded overview/index plus exploration. This keeps the
|
||||
transcript portion predictable without allowing an overview limit to erase inherited goals.
|
||||
|
||||
`overview_target_tokens` is a packing target, `overview_warning_tokens` emits a warning,
|
||||
and `overview_deadline_tokens` switches the ordinary overview to a coarse index. These
|
||||
limits and `compact.overview_tokens` accounting cover the overview only, not the separate
|
||||
full inherited summary. Small targets, warnings, and coarse fallback must not shorten or
|
||||
omit that summary.
|
||||
|
||||
The inherited summary still occupies the compactor's context. It counts against
|
||||
`worker_context_max_tokens`; moving it outside the overview does not exempt it from the
|
||||
hard input-occupancy check. If mandatory input exceeds that cap, compaction fails explicitly
|
||||
before sending the oversized request, leaving the source Segment active rather than
|
||||
silently clipping source material or activating an incomplete replacement. The budget guard
|
||||
is installed through the internal Worker's normal `WorkerInterceptor`, not an Engine
|
||||
interceptor that Worker startup would replace. Before a provider usage measurement exists,
|
||||
the estimate includes the full input, system prompt, and installed tool definitions. Later
|
||||
provider input measurements include that request shape already. A budget warning is an
|
||||
append-only history item and is also checked against the hard cap; if it cannot fit alongside
|
||||
the mandatory input, the run fails explicitly. Provider context-window failures likewise do
|
||||
not activate a replacement; estimates are not an exact model-token guarantee.
|
||||
|
||||
`summary_target_tokens` guides the new `write_summary` output. A nonzero
|
||||
`summary_max_tokens` is a hard output bound: an oversized result receives a rewrite request;
|
||||
if the rewritten output still exceeds the maximum, compaction fails without activation.
|
||||
This output bound is distinct from overview packing, the compactor input cap, and the
|
||||
advisory `result_context_max_tokens` warning on the assembled replacement.
|
||||
|
||||
## Assessment boundary
|
||||
|
||||
Deterministic integration tests inspect the actual `LlmClient::Request` to prove full
|
||||
inherited-summary transport, separation from overview packing, provenance-aware retention,
|
||||
shared history-restoration behavior, hard-limit rejection without activation, and inheritance
|
||||
through at least three chained compactions. Their `write_summary` outputs are predetermined
|
||||
mock responses: they do **not** prove that a model understood or semantically retained user
|
||||
goals, directives, or completion conditions. Shared restore-path coverage is not a complete
|
||||
cold Runtime restart assessment.
|
||||
|
||||
Live semantic assessment remains pending. It should use a real model on long, repeated
|
||||
compactions and compare the resulting summaries against the original request and explicit
|
||||
completion conditions, including facts near the end of a long inherited summary and facts
|
||||
absent from recent progress. Report semantic omissions separately from successful transport;
|
||||
a mock echo or a successful activation cannot establish semantic continuity.
|
||||
|
||||
## History integrity
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Compaction summary input validation (T-738)
|
||||
|
||||
## Baseline and scope
|
||||
|
||||
Implementation starts at `6a12b6cc346402b7a10208685038967171b09cb3`.
|
||||
Compared with the Ticket's inspected `3263d501`, the compaction input, capture and
|
||||
prompt contracts are unchanged; intervening Worker edits concern state-contract
|
||||
naming, resource/manage-worker projections, and shared state. The defects reported
|
||||
against `3263d501` therefore still apply to this checkout.
|
||||
|
||||
The change fixes mandatory previous-summary transport and the continuity policy.
|
||||
It does not add Journal integration (T-745), a separate directive ledger, exploration
|
||||
of historical Segments, or a comprehensive internal-compactor audit log. See
|
||||
[the current design](../design/compaction.md) for the input and retention contracts.
|
||||
|
||||
## Acceptance evidence
|
||||
|
||||
| Ticket condition | Deterministic evidence |
|
||||
| --- | --- |
|
||||
| 1: full initial input, long summary and recent activity | `full_initial_summary_survives_small_overview_budgets_and_both_retention_extremes` observes the actual first compactor `LlmClient::Request`; full body equality, one occurrence, and no clipped duplicate in overview. Summary exceeds 4,000 characters with directives/end sentinel after the long prefix, plus 80 recent messages. |
|
||||
| 2: small target/warning/fallback, context accounting, hard failure | Same matrix uses target=1 and warning/deadline settings 0/1. `inherited_summary_counts_against_hard_input_limit_and_failure_does_not_activate` compares the same overview with/without a mandatory summary above the cap: no compactor request, no replacement Segment, unchanged active metadata/history on rejection. `budget_warning_is_durable_and_keeps_full_summary_in_first_model_request` verifies the warning survives in the next request. Interceptor tests cover request-shape estimation, measured occupancy without double counting, and a warning that cannot fit. |
|
||||
| 3: absent summary, restore, retained boundary, other derived context | Matrix covers no retained tail and retaining all ordinary items. `pasted_markers_and_other_derived_context_remain_ordinary_overview_material` covers absent summary, auto-read, TaskStore, ordinary system, user paste and later derived markers. `restored_legacy_summary_requires_compacted_lineage_first_position_origin_role_and_marker` covers the exact adopted old Host seed boundary and its counterexamples. Invalid or duplicate typed Host summaries fail closed. |
|
||||
| 4: privacy and lightweight overview | Existing tool-argument/tool-result/reasoning omission tests remain. `host_summary_remains_private_to_compaction_not_observation_or_memory_evidence` verifies overview, search, read, and evidence exclude Host summary, other derived/system and reasoning bodies. The persisted retained-history paging fixture includes the new Host summary origin and still excludes derived content. Public provenance maps to the existing excluded `DerivedSummary` class; no public observation/Memory capability is added. |
|
||||
| 5: prompt and shortening policy | Resource policy test covers initial summary, segment user messages, prohibitions, completion conditions, changes/retractions, uncertainty, TaskStore distinction and retained tail. Shrink-policy and budget-warning tests cover the corresponding priorities. Output-hard-max integration test exercises the actual rewrite path and explicit failure if output remains oversized. |
|
||||
| 6: at least three chained compacts | `three_chained_compactions_transport_each_complete_predecessor_without_retaining_old_summaries` starts with a long summary carrying a late original-goal sentinel; each of three compacts receives a full predecessor and a new user directive containing temporary recovery/SubWorker collection instructions. It checks each actual initial request, one current typed summary, predecessor lineage and persisted result. |
|
||||
| 7: tests/check/format and current design | Commands below pass; design input, privacy, retention, limit and assessment descriptions are updated. |
|
||||
|
||||
## Executed validation
|
||||
|
||||
During implementation, the smallest filters were run first:
|
||||
|
||||
- `cargo test -p worker --lib build_summary_prompt_tests::overview`
|
||||
- `cargo test -p worker --lib compact_worker_`
|
||||
- `cargo test -p worker --lib session_history::`
|
||||
- `cargo test -p worker --lib session_capture::`
|
||||
- `cargo test -p worker --lib build_summary_prompt_tests::compact_prompt_and_shrink`
|
||||
- `cargo test -p worker --lib malformed_or_duplicate_host`
|
||||
- `cargo test -p worker --test compact_events_test inherited_summary_counts_against_hard_input_limit_and_failure_does_not_activate`
|
||||
- `cargo test -p worker --test compact_events_test` (25 tests)
|
||||
- `cargo test -p session-store --lib retained_history_pages_twelve_turns`
|
||||
|
||||
Final default-configuration validation:
|
||||
|
||||
- `cargo check` at workspace root (normal compile closure, including TUI/Server)
|
||||
- `cargo test -p worker` (library, integration targets, doctests)
|
||||
- `cargo test -p session-store` (library, integration targets, doctests)
|
||||
- `cargo fmt --all -- --check`
|
||||
- `git diff --check HEAD`
|
||||
- `env -u LD_LIBRARY_PATH deno run --allow-read --allow-run=git tools/check-state-contract-names.ts` (0 unapproved, 0 stale exceptions)
|
||||
|
||||
No added contract depends on an optional Worker feature. Full-workspace tests,
|
||||
`--all-targets`, live provider calls, E2E, Nix and Docker builds were not run.
|
||||
|
||||
## Unverified semantic and operational behavior
|
||||
|
||||
Predetermined mock `write_summary` results prove transport, input policy and
|
||||
replacement behavior, **not** a real model's semantic retention or comprehension.
|
||||
Live semantic evaluation remains pending: use a real model across at least three
|
||||
compacts, score original scope/prohibitions/completion/exception-reporting duties
|
||||
and explicit subsequent changes separately from transport success, and verify that
|
||||
temporary recovery procedures do not displace the continuing request. Do not credit
|
||||
an echo fixture as semantic evidence.
|
||||
|
||||
The restore fixture exercises public `rewind_to` and the shared typed restoration
|
||||
path over persisted annotated records. It is not a cold Runtime restart test.
|
||||
Manual and automatic compaction use the same `compact_impl`; existing automatic
|
||||
resume tests pass, but no separate live automatic-compaction semantic evaluation
|
||||
was run. Initial context accounting is an estimate, not exact provider tokenization;
|
||||
provider failure must still fail the run without activation.
|
||||
@@ -8,11 +8,11 @@ Never use the live dogfood Server or Runtime as the first startup test for a new
|
||||
|
||||
1. Build the production entrypoints: `cargo build -p worker-runtime --bin yoi-runtime -p yoi-workspace-server --bin yoi-server`.
|
||||
2. Run the focused and dependent tests for the changed contracts, followed by workspace-root `cargo check`, `cargo fmt --all -- --check`, and `git diff --check HEAD`.
|
||||
3. Exercise the provisioning and operational checks in [Workspace ↔ Runtime authentication](server-runtime-auth.md) against isolated Server DB, Runtime data, and ports. The Workspace owner must create the binding, the Runtime operator must install the Workspace issuer bundle, and the challenge proof must become verified.
|
||||
3. Exercise the provisioning and operational checks in [Workspace ↔ Runtime authentication](server-runtime-auth.md) against isolated Server DB, Runtime data, and ports. The Workspace owner must configure the Runtime endpoint and pinned Runtime key, the Runtime operator must install the Workspace issuer bundle, and actual signed requests must authenticate. A connection-test result describes only that attempt; no stored verification state is a readiness prerequisite.
|
||||
4. Have an external supervisor or operator restart Server and Runtime from the same validated source commit/build artifacts. A Worker hosted by the target Runtime must never terminate its own Runtime.
|
||||
5. Verify post-restart readiness through the Workspace Runtime projection, ping, Worker list/create, protocol subscription, and a Runtime-to-Server source-proof operation before treating the environment as healthy.
|
||||
|
||||
The former isolated startup shell harness depended on removed Server-global trust commands and is intentionally not a fallback smoke path. New automated startup coverage must provision the same Workspace-scoped binding and challenge authority used by production rather than recreating global trust or seeding private authority directly.
|
||||
The former isolated startup shell harness depended on removed Server-global trust commands and is intentionally not a fallback smoke path. New automated startup coverage must configure the same Workspace-scoped connection and explicit Runtime-side Workspace trust used by production, then exercise per-request authentication rather than recreating global trust or seeding a past verification result.
|
||||
|
||||
## What to record
|
||||
|
||||
@@ -25,7 +25,7 @@ A report is useful when it explains:
|
||||
- what design boundary was missing
|
||||
- what evidence was observed
|
||||
|
||||
For a Remote Runtime rollout, also record the source commit, binary artifact digest, Server schema version, Runtime `binding_id`, Workspace `key_id` and `workspace_trust_id`, and typed HTTP/WebSocket outcomes. A successful document response does not outweigh visible UI, console, or API errors.
|
||||
For a Remote Runtime rollout, also record the source commit, binary artifact digest, Server schema version, Runtime `binding_id` and pinned key fingerprint, the current Workspace `key_id` and public fingerprint, the Runtime-side trust registration/revocation, and typed HTTP/WebSocket outcomes. Do not copy a Runtime trust ID into the Server connection configuration or treat a successful connection test as persisted authentication. A successful document response does not outweigh visible UI, console, or API errors.
|
||||
|
||||
## Runtime command caveat
|
||||
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
# Workspace ↔ Runtime 認証
|
||||
|
||||
Yoi の Remote Runtime 認証は Workspace ごとの署名 identity を authority とする。
|
||||
認証の成否は実際の HTTP / WebSocket request ごとに決まり、保存済みの「verified」状態や過去の接続テスト結果を前提にしない。
|
||||
Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わない。
|
||||
|
||||
## Authority
|
||||
|
||||
- Server DB は Workspace ごとの signing identity と Runtime binding を保持する。
|
||||
- Runtime は `trust-workspace` で受理した `WorkspacePublicIdentityBundle` を保持する。
|
||||
- Server DB は Workspace ごとの signing identity と Runtime 接続設定(binding)を保持する。
|
||||
- binding は endpoint、固定した Runtime public key/fingerprint、`binding_id`、authentication mode、管理者による失効を保持する。Workspace key や Runtime 側の trust ID のコピー、認証成功状態は保持しない。
|
||||
- Runtime は管理者が `trust-workspace` で受理した `WorkspacePublicIdentityBundle` を保持する。
|
||||
- bundle は `workspace_id`、Workspace `key_id`、Workspace public key/fingerprint、Backend URL を固定する。
|
||||
- Server → Runtime の各 HTTP / WebSocket request は、対象 Workspace の signing identity で短命な capability token を発行する。
|
||||
- Runtime は request method、`path_and_query`、body digest、permission、Workspace、Runtime、key ID、現在の trust ID、expiry、JTI を検証する。
|
||||
- Runtime → Server の source proof は Runtime identity で署名し、対象 Workspace と bundle の Backend URL を audience に固定する。
|
||||
- Server → Runtime の各 HTTP / WebSocket request は、その時点の Workspace signing identity で短命な capability token を発行する。署名時には claims の Workspace・key ID・fingerprint が選択した identity と一致することを検証する。秘密鍵materialがない場合は fallback せず拒否する。
|
||||
- Runtime は現在の明示的な Workspace trust、署名、request method、`path_and_query`、body digest、permission、Workspace、Runtime、必要に応じ Worker、key ID/fingerprint、expiry、JTI を検証する。過去の handshake や verification record は不要である。
|
||||
- Runtime → Server の source proof は Runtime identity で署名し、対象 Workspace と bundle の Backend URL を audience に固定する。この proof の契約は変わらない。
|
||||
- Server は現在の Workspace Runtime binding、Runtime public key、Backend public URL、request target、body digest、permission、expiry、replay state を検証する。
|
||||
|
||||
旧 Server identity/trust 管理 command と旧 Runtime-side Server trust command、旧 Runtime auth key flags は廃止済みである。これらに相当する Server-global trust を fallback として使ってはならない。
|
||||
|
||||
## Provisioning
|
||||
|
||||
The enrollment input includes the exact Runtime-issued `workspace_trust_id` from
|
||||
`trust-workspace add/show`. Export the current Workspace public bundle first,
|
||||
accept it on the Runtime, then register the Runtime public bundle, endpoint and
|
||||
trust ID. Server verification binds that enrollment ID; it must never invent an
|
||||
ID from the key fingerprint or assume an initial counter value. Re-enrollment
|
||||
requires registering the newly issued trust ID, even when the key is unchanged.
|
||||
Runtime 側の Workspace trust 登録と Server 側の Runtime 接続設定は独立した管理操作である。Runtime が発行する `trust_id` を Server にコピーする必要はない。
|
||||
|
||||
1. Runtime identity を初期化する。
|
||||
|
||||
@@ -31,17 +28,16 @@ requires registering the newly issued trust ID, even when the key is unchanged.
|
||||
yoi-runtime identity show
|
||||
```
|
||||
|
||||
2. The Workspace owner exports the current Workspace issuer public bundle from Workspace identity settings.
|
||||
3. The operator accepts that bundle on the Runtime and reads the issued trust ID.
|
||||
2. Workspace owner が Workspace identity settings から現在の Workspace issuer public bundle を export する。
|
||||
3. Runtime 管理者が bundle を確認し、Runtime に明示的に登録する。
|
||||
|
||||
```sh
|
||||
yoi-runtime trust-workspace add --bundle <workspace-issuer-bundle.json>
|
||||
yoi-runtime trust-workspace show --workspace-id <workspace-id>
|
||||
```
|
||||
|
||||
4. The owner registers the Runtime public bundle, endpoint and exact `workspace_trust_id` in Settings > Runtimes.
|
||||
5. The Server sends a signed challenge bound to that trust ID, the Workspace key fingerprint and the binding ID, then verifies the Runtime proof and acknowledgement.
|
||||
6. Normal Workspace-signed requests become available only after the verified binding is committed. Revoking and re-enrolling the same key still requires the newly issued Runtime trust ID.
|
||||
4. Workspace owner が Settings > Runtimes に Runtime public bundle と endpoint を登録する。接続設定には Runtime key を固定するが、Workspace key や Runtime の trust ID は登録しない。
|
||||
5. 接続テストまたは通常操作を実行する。実際の Workspace-signed request が Runtime の現在の trust と照合される。接続テスト結果はその試行の診断であり、永続的な認証状態を作らず、以後の request の成功を保証しない。
|
||||
|
||||
同じ Runtime identity は異なる Workspace から独立して信頼できる。trust record、replay protection、binding、失効はすべて Workspace scope で評価する。
|
||||
|
||||
@@ -55,29 +51,41 @@ requires registering the newly issued trust ID, even when the key is unchanged.
|
||||
- 最大 8 MiB
|
||||
- 最大 4,096 records
|
||||
- exact Workspace / Runtime identity
|
||||
- `key_id` と public key fingerprint、opaque `trust_id`
|
||||
- `key_id` と public key fingerprint、管理 record の opaque `trust_id`
|
||||
- normalized Backend URL
|
||||
- 同じ `key_id` に異なる鍵materialを割り当てないこと。実際の置換・失効は新しい `trust_id` を発行する
|
||||
- 同じ `key_id` に異なる鍵materialを割り当てないこと。実際の置換・失効は新しい `trust_id` を発行するが、この ID は request token や Server binding に含めない
|
||||
- list は `offset` / `limit` 必須で、1 page 最大 100 records
|
||||
|
||||
## Local token
|
||||
|
||||
`--local-token` は明示的な local Runtime 呼び出し専用であり、Remote Workspace binding の代替ではない。Workspace issuer auth が有効な Remote Runtime request は Workspace capability token を使う。
|
||||
|
||||
## Rotation と失効
|
||||
## Key 変更と失効
|
||||
|
||||
Workspace signing key または Runtime key の変更は、現在 binding を置き換える明示的な provisioning 操作として行う。古い key/trust ID、古い Runtime key、revoked binding、失効済み token、replayed JTI は即時拒否する。
|
||||
Workspace signing identity は各 request の発行時に現在の authority から選択する。Workspace public key が変更された場合、Runtime 管理者が現在の public bundle を明示的に `trust-workspace replace` で受理する必要がある。Workspace key のコピーを更新するためだけに Server の Runtime 接続設定を置き換える操作は不要である。これは Workspace key rotation の新しい production API を定義するものではない。
|
||||
|
||||
Server の Runtime cache は現在の persisted binding 全体と照合する。endpoint、Runtime public key/fingerprint、`binding_id`、Workspace `key_id`、`workspace_trust_id` の変更を検知した場合、stale client を利用しない。
|
||||
Runtime 管理者は `trust-workspace revoke` で Workspace trust を失効できる。Runtime は現在の trust と一致しない key、失効済み trust、expired token、replayed JTI を拒否する。同じ key の再登録も管理者の明示的な操作で行い、request の自己申告による trust 登録は行わない。
|
||||
|
||||
Runtime key や endpoint の変更は Server の接続設定を明示的に置き換える。Server の Runtime cache と authorizer は現在の接続設定(endpoint、Runtime public key/fingerprint、`binding_id`、authentication mode、管理者による失効)と照合し、replaced/revoked binding を使う stale client を拒否する。接続設定の一致は送信先の選択であって、認証成功の証拠ではない。
|
||||
|
||||
接続先が到達不能・認証失敗でも、owner は観測した `binding_id` を指定して接続設定を修復できる。取得できた active Worker は置換を遮断するが、Worker 一覧の取得失敗を「idle の証明」とは扱わず、認証成功を修復の前提にもしない。接続設定の置換は Worker の停止・削除ではなく、Runtime 削除の資源確認とは別の操作である。
|
||||
|
||||
## 保存形式の移行
|
||||
|
||||
Server schema90 は既存 binding の endpoint、Runtime key、binding ID、明示的失効を保持し、古い認証状態と検証記録を履歴専用 archive に移す。履歴は要求の認可には参照しない。既存 Workspace identity や接続設定の作り直しは不要である。
|
||||
|
||||
Runtime の旧形式の Workspace trust は、検証済みの公開鍵と管理者の Active/Revoked 設定を保持して読み込む。形式の移行だけでは失効させず、すでに保存されている Revoked 設定を自動で解除することもしない。旧 verification file は認可に使用しない。消費済み token の replay 記録は引き継ぎ、同じ鍵の再登録でリセットしない。
|
||||
|
||||
API と capability claims の旧 handshake 契約は維持しないため、Backend・Runtime・Web は対応する版へ更新する。接続テストは更新後の実認証を確認するものであり、登録を有効化する手順ではない。
|
||||
|
||||
## 運用確認
|
||||
|
||||
Remote Runtime を有効化した後は次を確認する。
|
||||
Remote Runtime を設定した後は次を確認する。
|
||||
|
||||
1. `yoi-runtime trust-workspace show --workspace-id <workspace-id>` が期待する bundle を表示する。
|
||||
2. Workspace Settings の Runtime binding が `verified` で、現在の `binding_id`・`workspace_key_id`・`workspace_trust_id` と verification evidence を表示する。
|
||||
3. Runtime ping、Worker list/create、`worker.protocol` subscription が Workspace-signed token で成功する。
|
||||
4. wrong Workspace、wrong Runtime、wrong target/body、expired token、revoked/replaced binding、replayed JTI が拒否される。
|
||||
1. `yoi-runtime trust-workspace show --workspace-id <workspace-id>` が期待する active bundle を表示する。
|
||||
2. Workspace Settings の Runtime 接続設定が期待する endpoint と固定 Runtime key を示し、失効していない。
|
||||
3. 接続テストの今回の結果を確認し、Runtime ping、Worker list/create、`worker.protocol` subscription が実際の Workspace-signed request で成功することを確認する。保存済み「verified」表示や verification evidence は確認項目ではない。
|
||||
4. wrong Workspace、wrong Runtime、wrong target/body、expired token、revoked Runtime trust、replaced/revoked binding、replayed JTI が拒否される。
|
||||
5. Runtime → Server source proof が configured Backend public URL audience と一致し、spoofed headers だけでは認証されない。
|
||||
|
||||
Server / Runtime の再起動は live reload ではない authority 変更を反映するときだけ、通常の運用権限と migration gate に従って行う。実行中プロセスを開発 Worker が無断で停止してはならない。
|
||||
|
||||
@@ -31,7 +31,7 @@ closeは案件の実作業拘束を終える一方、T-715の終端担当責任
|
||||
|
||||
## 旧保存データの移行
|
||||
|
||||
独立T-739 sourceではTicket component schema7→9、Workspace schema89→90を使用する。component6からの既存移行も維持する。
|
||||
T-739 sourceではTicket component schema7→9を使用する。Workspaceは現developのauth/runtime connection移行89→90をそのまま保持し、案件移行90→91を追加する。旧89からはauth→案件の順に進み、現90からは案件移行だけを行う。component6からの既存移行も維持する。
|
||||
|
||||
| 旧workflow state | 現行案件状態 | 終了理由 |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# schema86→87のSSH移行: 異なる時刻形式の文字列比較でfingerprintがNULLになる
|
||||
|
||||
調査日: 2026-10-10
|
||||
報告された起動エラー:
|
||||
|
||||
`yoi-server: store error: workspace schema migration failed: sqlite error: NOT NULL constraint failed: workdir_create_credential_candidates.credential_fingerprint`
|
||||
|
||||
## 原因
|
||||
|
||||
`crates/workspace-server/src/frozen_repository_key_migration.rs:262–266`のcredential candidate移行は、旧credential IDと保存counterの組から`repository_key_cutover`へLEFT JOINし、`m.fingerprint`を新しいNOT NULL列に挿入する。
|
||||
|
||||
JOIN条件の`m.created_at <= o.updated_at`が、RFC3339の鍵作成時刻とepoch-millis文字列のWorkdir更新時刻を、そのままTEXTとして比較している。日時としての順序を判定していないため、有効な古い鍵のJOINが外れ、fingerprintがNULLになる。
|
||||
|
||||
- 鍵作成時刻: `repository_access.rs:2256`の`Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true)`。
|
||||
- Workdir作成・更新時刻: `server.rs:30936–30940`の`now_registry_timestamp()`はepochミリ秒の文字列。reserveでもこれを使用する(`server.rs:24272–24308`)。
|
||||
|
||||
同じ異形式比較はoperation本体のcredential/host trust移行にもある(migration L260–261)。candidateだけ直してもoperation snapshotのfingerprintが欠落する問題が残る。
|
||||
|
||||
## 実データによる確認
|
||||
|
||||
ユーザーから共有済みの`yoi.local/server/server.db`をNodeの`DatabaseSync({readOnly: true})`と`PRAGMA query_only=ON`で照合した。秘密鍵、暗号化envelope、master keyは取得していない。migrationやDB更新も実行していない。
|
||||
|
||||
- DBはschema86のまま。
|
||||
- 新candidateへ移される5件すべてがfailed Workdir createのcredential候補で、旧retentionが各1件あるため成功済み・unretainedのarchive経路には入らない。
|
||||
- 5件とも参照先の鍵は存在する。対応するmutation receiptも各1件あり、証拠欠落ではない。
|
||||
- 5件ともSQLiteのTEXT比較はfalse、実際の時刻比較はtrue。
|
||||
|
||||
代表例:
|
||||
|
||||
| 項目 | 値 |
|
||||
| --- | --- |
|
||||
| operation | `call_mgmyCNofdQMZ8uyI5tg0mBhv` |
|
||||
| credential | `workspace-default`、旧保存counter 1 |
|
||||
| 鍵created_at | `2026-09-16T18:44:56.736Z` |
|
||||
| Workdir updated_at | `1790173821030` |
|
||||
| Workdir updated_atを日時に変換 | `2026-09-23T14:30:21.030Z` |
|
||||
| SQLiteの文字列比較 | false |
|
||||
| 日時としての比較 | true |
|
||||
|
||||
他の4件では鍵作成が`2026-09-10T13:09:52.113Z`、Workdir更新は9/23または9/29で、同じ問題になる。
|
||||
|
||||
## 修正方針(この調査では未実装)
|
||||
|
||||
- frozen migration内で旧データの時刻形式を明示的に読み、同じ時刻型/epoch-millisへ正規化して比較する。文字列比較やSQLの安易なCASTに頼らない。
|
||||
- `created_at <= updated_at`の安全確認そのものは削除しない。旧IDと保存counterが削除後に再利用された鍵を、過去のcandidateに誤って結び付けることを防ぐ条件である。
|
||||
- candidateだけでなくoperation本体のcredential/host trust等、同じ境界の比較を確認する。
|
||||
- 正規化不能・不正な時刻や真に必要な証拠が欠落した場合は、NOT NULLエラーになる前に文脈付きで拒否し、transactionをrollbackする。
|
||||
|
||||
既存の`frozen_repository_key_tests.rs:35–42`の共通fixtureは、鍵とWorkdirの時刻をすべて`created`に揃えており、実データの異形式を再現していない。RFC3339鍵時刻+epoch-millis Workdir時刻での移行成功と、実時刻が操作より後の再作成鍵を拒否するケースが必要。テスト追加時は`docs/development/rust-testing-strategy.md`に従う。
|
||||
|
||||
## 影響・実施範囲
|
||||
|
||||
schema86→87はtransaction内で実行され、version87の記録とcommitは最後に行う(migration L29、L349–350)。今回のDBでもschema86と旧テーブルを確認した。ただし、それより前のschema更新まで含めた全起動処理が一括で元に戻るという意味ではない。
|
||||
|
||||
NOT NULLを外す、適当なfingerprintを入れる、candidate/retentionを消して通す、といったDB修復は行うべきではない。正しい既存データをmigrationが誤判定しているので、移行コード側を修正する。
|
||||
|
||||
調査時点で追加したのは本報告のみ。以下は、その後のユーザー指示による修正記録。
|
||||
|
||||
## 修正と検証(2026-10-10)
|
||||
|
||||
- RFC3339/epoch-millisをRustで読み、temporary tableのUTC秒・ナノ秒へ正規化して比較するよう修正。credential/host trustのoperation snapshot、candidate、旧receiptの時刻条件に適用した。
|
||||
- タイムゾーン・sub-millisecondの順序を維持し、元の履歴時刻やreceipt照合用の文字列、暗号化AADは書き換えない。永続schemaの変更・version bumpは不要。
|
||||
- 不正な時刻、欠けた鍵、操作より新しい再利用鍵は文脈付きで拒否しrollback。NOT NULLを緩めたり候補を削除したりしない。
|
||||
- 独立コード確認で見つかった、親operationのない旧candidateがINNER JOINから落ちて消える経路も、コピー前の拒否とrollbackテストで保護した。追修正に追加のblocking findingなし。
|
||||
- 偽時刻`created`や日付だけのfixtureは実際のRFC3339へ修正。新規回帰は混在形式・同時刻・offset・ナノ秒精度、各SSH参照での新しい鍵拒否、不正時刻、孤立候補の保持を検証する。
|
||||
- 最小の混在形式テスト、migrationモジュール、公開migration dry-run/applyテスト、孤立候補テストを順に実行して成功。最終の`cargo test -p yoi-workspace-server`はlibrary **815 passed / 1 ignored**、binary **11 passed**、doctest失敗なし。
|
||||
- root `cargo check`、`cargo fmt --all -- --check`、変更したincludeファイルの明示的`rustfmt --check`、`git diff --check HEAD`も成功。
|
||||
- 稼働DBでのmigration、ビルド済みバイナリの配備、サービスの停止・再起動は行っていない。更新はユーザー操作のまま。
|
||||
|
||||
作業中、テスト担当SubWorkerはcwd基準の読取で、scopeに付与した別ディレクトリのテスト規則を参照できなかった(絶対pathもinvalid logical filesystem pathで拒否)。編集なしを確認して停止し、親でテストを実装した。読取専用レビュー担当はroot cwdで規則・ソースを読めた。cwdとscopeにまたがる論理pathの案内・検証がツール上の改善点。
|
||||
@@ -0,0 +1,88 @@
|
||||
# Workdir 削除のマウント残留と結果不明の診断
|
||||
|
||||
## 調査範囲
|
||||
|
||||
ユーザーの「workdirを消せない原因を調査して」という依頼に対し、保存済み Server DB を
|
||||
`mode=ro` / `PRAGMA query_only=ON` で照合し、対象ディレクトリ・マウント・現行実装を
|
||||
観察した。削除再実行、アンマウント、DB 更新、サービス/プロセス操作、実装修正は行っていない。
|
||||
調査中にも operation の記録が更新されたため、以下は最後に読んだ状態であり、過去の全試行に
|
||||
同じ原因があったと断定するものではない。
|
||||
|
||||
Workspace: `0197a949-4b6b-7f2a-9d9a-1f87e3a4c5b6`、Runtime: `arcadia`。
|
||||
表示に含まれる次の2つの Workdir ID は別の対象である。
|
||||
|
||||
## T-721: 残留した JuiceFS マウントによる拒否(確認済み)
|
||||
|
||||
- Workdir: `001a11ab3e830000007` / `T-721 JuiceFS Drive foundation`
|
||||
- operation: `wdr_1c0840acc66f27b7e048bdfff8f80b76`
|
||||
- 最後に読んだ attempt: `01a12682-3b33-7e43-b4c6-4e57eb469912`
|
||||
- 更新時刻: `2026-10-10T15:50:31.515343065+00:00`
|
||||
- 結果: `failed` / `mount_present` / `retryable=1`
|
||||
|
||||
`findmnt` で、次の2つが `JuiceFS:yoi-t721` / `fuse.juicefs` のマウントとして残っていることを
|
||||
確認した(パスは `yoi.local` 以下の論理パス)。
|
||||
|
||||
```text
|
||||
workdirs/001a11ab3e830000007/checkout/.yoi/dev/juicefs-t721/mount
|
||||
workdirs/001a11ab3e830000007/checkout/.yoi/dev/juicefs-t721/mount2
|
||||
```
|
||||
|
||||
両エントリの属性参照は `Transport endpoint is not connected` で失敗した。
|
||||
`ps -C juicefs` に現在のプロセスはなかった。マウント解除されていない FUSE の残留状態と
|
||||
整合するが、プロセスが終了した経緯までは今回確認していない。
|
||||
|
||||
Registry の cleanliness は `clean` だが、これはマウントを安全に削除できる証明ではない。
|
||||
実ディレクトリの `materialization.json` は `cleanup_pending`。
|
||||
現行 `crates/worker-runtime/src/working_directory/cleanup.rs` は、削除前にマウント境界を
|
||||
検査し、外部/未知のマウントを自動解除しない。Server は
|
||||
`working_directory_cleanup_mount_present` を `mount_present` として扱う。
|
||||
これは安全性を維持する拒否であり、cleanliness 判定の不具合とは異なる。
|
||||
|
||||
解消には、所有者が保存先と利用状況を確認して両マウントを安全に解除することが必要。
|
||||
その後、マウントがないことを確認して通常の Workdir 削除を再実行する。
|
||||
マウント先を含む `rm -rf`、強制的なメタデータ改変、未知のプロセス停止は解消手段にしない。
|
||||
マウント解除後も ignored な実験データ等の保護により別の拒否が起こる可能性があり、今回の
|
||||
確認は次回の削除成功を保証しない。
|
||||
|
||||
## 別 ID: HTTP タイムアウトと物理削除の時間差が疑われる(未確定)
|
||||
|
||||
- Workdir: `001a111ca9d33000002`
|
||||
- operation: `wdr_fccf7c6e9ee6cbb931ea2f9b82198650`
|
||||
- attempt: `01a12682-411e-7720-b829-f9c9c0148886`
|
||||
- created: `2026-10-10T15:50:32.984845992+00:00`
|
||||
- updated: `2026-10-10T15:50:43.018819445+00:00`
|
||||
- 結果: `failed` / `provider_cleanup_failed` / `retryable=1`
|
||||
|
||||
要求から失敗まで約10.034秒。Registry には `corrupted` / cleanliness `unknown` として
|
||||
残っている一方、調査時点で `yoi.local/workdirs/001a111ca9d33000002` は存在しない。
|
||||
`.cleanup-authority` にも残存ファイルはなかった。
|
||||
|
||||
現行実装では `RemoteRuntimeConfig::new` の timeout は10秒で、DELETE はその共通
|
||||
blocking HTTP client を使う。Runtime の HTTP handler は `spawn_blocking` で同期的な
|
||||
検査/削除の完了を待つ。クライアントのタイムアウトは、この blocking 削除処理の取り消しや
|
||||
物理削除の失敗を意味しない。
|
||||
|
||||
このため「Server が10秒で応答待ちを打ち切った後、Runtime の物理削除が完了した」という
|
||||
説明と整合する。ただし、元の `remote_runtime_timeout` 等の診断を今回の観察面で取得できず、
|
||||
別の操作でディレクトリがなくなった可能性も排除できない。物理パスがないだけで Backend の
|
||||
削除完了を捏造してはいけない。通常の再試行で Runtime の厳密な not-found 応答を確認できれば、
|
||||
現行 Server は registry 削除を完了する経路を持つ。今回、その再試行は行っていない。
|
||||
|
||||
## 診断面の障壁と改善案
|
||||
|
||||
`crates/workspace-server/src/server.rs` の `workdir_cleanup_failure_category` は、既知の
|
||||
cleanup code を分類し、それ以外を `provider_cleanup_failed` に落とす。
|
||||
`crates/workspace-server/src/hosts.rs` は transport timeout を `remote_runtime_timeout`
|
||||
に区別するが、上記分類では失敗結果不明との区別が失われる。
|
||||
|
||||
また removal operation は bounded category のみを永続化し、元の diagnostic code を残さない。
|
||||
これは秘密情報を含み得る message を保存しない方針とは両立するが、現行の失敗ログも
|
||||
Workdir/operation/runtime/category のみで、ユーザー案内の「相関 Runtime diagnostics を調べる」
|
||||
を保存済み情報だけでは完遂できなかった。`runtime.json` の diagnostics にも対象 ID / operation
|
||||
を含む記録は見つからなかった。
|
||||
|
||||
改善案は、秘密を含む raw message を公開・永続化せず、transport timeout を「削除結果不明」と
|
||||
して区別し、許可された診断コードと operation/attempt/Workdir の相関を残すこと。
|
||||
さらに、完了まで時間のかかる guarded cleanup を通常の10秒 control request と同じ同期要求で
|
||||
扱う契約を見直すこと。単にタイムアウトを延長するだけでは結果不明の状態はなくならない。
|
||||
今回は調査報告のみで、これらの実装変更はしていない。
|
||||
Reference in New Issue
Block a user