mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-27 07:45:08 +08:00
roadmap: #278 filed
This commit is contained in:
16
ROADMAP.md
16
ROADMAP.md
@@ -17125,3 +17125,19 @@ Gap. There is no typed `channel_resolution` preflight in the dogfood delivery pa
|
||||
Required fix shape: (a) add a channel-target preflight before dogfood reminder send that resolves the configured target to a canonical channel id/guild/provider tuple; (b) emit a typed `delivery_target_resolution_failed { provider, configured_target, guild_id, reason, directory_freshness_ms, fallback_targets }` event instead of bare `Unknown Channel`; (c) distinguish `not_found`, `permission_denied`, `wrong_provider`, `wrong_guild`, `cache_stale`, and `deleted_or_archived`; (d) include the resolved target tuple in successful delivery receipts (#269 sibling) so later reports can prove which channel was used; (e) add regression coverage where a stale channel id fails at preflight with a typed diagnostic and does not attempt message send. Acceptance: dogfood cron never surfaces a naked `Unknown Channel`; it reports the exact configured target, resolution failure class, and safe next action.
|
||||
|
||||
**Status:** Open. No source code changed. Filed 2026-04-26 16:32 KST. Branch: feat/jobdori-168c-emission-routing. HEAD: `cad7bb1` before filing. Cluster delta: dogfood-delivery-target-resolution +1; sibling to #269 (payload/delivery receipt) and #246 (cron timeout ambiguity), distinct pre-send channel identity layer. Concrete delta this cycle: ROADMAP-only pinpoint appended from live `Unknown Channel` cron failure evidence.
|
||||
|
||||
## Pinpoint #278 — `Session::from_jsonl` and `Session::from_json` parse the `version` field, store it verbatim, but never compare it against `SESSION_VERSION`, so a session file with any `u32` version (past, future, or corrupted) loads successfully and is silently treated as the current schema with default-filled-or-dropped fields
|
||||
|
||||
Dogfooded 2026-04-26 16:34 KST on `feat/jobdori-168c-emission-routing` at HEAD `4e4edc8` (post fast-forward onto gaebal-gajae's #277 channel-resolution preflight pinpoint). Static audit of `rust/crates/runtime/src/session.rs` shows `const SESSION_VERSION: u32 = 1;` at line 12, and three call sites that touch the field: `Session::new` at line 162 sets `version: SESSION_VERSION` on creation; `Session::from_json` at lines 338-343 parses `version` via `required_u32` and returns `SessionError::Format("version out of range")` only when `u32::try_from` fails; `Session::from_jsonl` at lines 406+445 initializes a local `let mut version = SESSION_VERSION;` and overwrites it from the `session_meta` record's `version` field via `required_u32`. In every loader path the parsed value is stored in the returned `Session.version` field without any comparison against `SESSION_VERSION`. `grep -rn "SESSION_VERSION" rust/crates/runtime/src/session.rs` returns exactly three hits — declaration + two assignment sites — and zero comparison/migration/reject sites. `grep -iE "migrat|incompat|upgrade|downgrade|reject|mismatch|future|forward" rust/crates/runtime/src/session.rs` returns zero matches.
|
||||
|
||||
Concrete failure mode: a session JSONL produced by a future claw-code release with `version: 2` and new schema fields (e.g. an unforeseen `tool_call_render_kind` per #274, a `workspace_provenance` block per #275, a `health_check_history` array, or a renamed `compaction` shape) is loaded by an older claw-code build. The older build silently accepts `version: 2`, drops every unknown record type via `"unsupported JSONL record type at line {}: {other}"` (which is structurally fine for forward-compat), but stores `self.version = 2` in memory, then on next save writes back a `session_meta` record with `version: 2` mixed with v1-shape data. Symmetrically, a corrupted or hand-edited session with `version: 999` or `version: 0` loads without warning and round-trips as if it were the live schema. There is no operator-visible signal — no warning log, no typed error, no `--strict-version` opt-in — that the on-disk schema does not match the binary's expectations. Combined with #259/#271/#273/#275 (provenance fragmentation across dogfood/status/doctor surfaces), this means a session file's schema-of-record is not auditable from any product surface either.
|
||||
|
||||
Gap. There is no version-mismatch policy. The on-disk `version` field is treated as an opaque tag rather than a contract. A correct loader for a versioned format must either (i) accept only `version == SESSION_VERSION` and reject everything else with a typed `SchemaVersionMismatch { found, expected }` error, (ii) maintain an explicit migration table that upgrades older versions to the current shape and refuses unknown future versions, or (iii) document a forward-compat policy with explicit field-level handling rules. claw-code does none of these; the field is parsed for storage only. This is structurally identical to a database without a schema_version column being silently bumped — the data still loads, but downstream consumers cannot tell whether they got the schema they expected.
|
||||
|
||||
Distinct from #259 (dogfood report provenance — runtime emission, not on-disk persistence). Distinct from #271 (repo-identity guard — workspace remote provenance, not session schema). Distinct from #273/#275 (status/doctor diagnostic-surface provenance fragmentation — product surface field layout, not session loader semantics). Distinct from #266 (typed-error-kind for credentials missing — error-kind for one specific runtime decision, not a missing-error-kind for schema versioning). Distinct from the entire help-contract-drift cluster (#263+#270+#276 — CLI surface vs dispatcher), the turn-budget triangle (#262+#264+#272 — parse/primitive/spec layers), the MCP-axis cluster (#254+#268+#274+#275 — MCP runtime/catalog/rendering/doctor), and the provenance quartet (#259+#271+#273+#275 — provenance surfacing). #278 founds a NEW `persisted-schema-version-policy` cluster on the persistence-layer axis — the first pinpoint to target what the loader does (or fails to do) with the `version` field on disk rather than what the diagnostic surface emits about state.
|
||||
|
||||
Discovery-pattern continuation: extends the structural-gap-without-source-change discovery-pattern (silent-fallback cluster style — accept input that should be rejected) into the on-disk persistence layer. Pairs structurally with the silent-fallback cluster: silent-fallback accepts malformed CLI/runtime input without a typed error; #278 accepts wrong-version on-disk state without a typed error. Both are structurally-absent-error-kind gaps but in different layers (input vs persisted state). Pairs orthogonally with the provenance quartet: provenance is about emitting state-of-record at runtime; #278 is about validating state-of-record at load. Together they form a state-of-record cross-axis bundle (emission × validation).
|
||||
|
||||
Required fix shape: (a) add a typed `SessionError::SchemaVersionMismatch { found: u32, expected: u32, policy: VersionPolicy }` variant where `VersionPolicy` is `Strict | MigrationAvailable | ForwardCompatibleReadOnly`; (b) at the top of `from_json` and after the `session_meta` parse in `from_jsonl`, compare the parsed `version` against `SESSION_VERSION` and short-circuit when not equal under the configured policy; (c) introduce a small `migrate_session(version: u32, raw: &JsonValue) -> Result<Session, SessionError>` table even if the only entry today is `1 -> 1` identity, so future versions land with one well-known extension point; (d) when loading a future version under a `ForwardCompatibleReadOnly` policy, refuse to write back to the same path (preserve the original) and surface a one-time warning; (e) extend tests to cover `version: 0` (rejected/migrated), `version: 2` (rejected or read-only), `version: 999` (rejected), and a missing `version` field (rejected with a clear message rather than silently defaulting to `SESSION_VERSION`); (f) add a session-version provenance line to `claw status` and `claw doctor` output (closes the #273/#275 surface gap for this specific schema-of-record dimension) so operators can inspect on-disk schema age vs binary schema age without scraping JSONL. Acceptance: a session file with any version other than `SESSION_VERSION` produces a typed, surface-visible diagnostic before any in-memory state is mutated; the migration table is the single extension point for future bumps; `claw status` and `claw doctor` show `session_schema_version` as a first-class provenance field.
|
||||
|
||||
**Status:** Open. No source code changed. Filed 2026-04-26 16:34 KST. Branch: feat/jobdori-168c-emission-routing. HEAD: `4e4edc8` before filing (post fast-forward onto gaebal-gajae's #277 channel-resolution preflight). Cluster delta: founds NEW `persisted-schema-version-policy` cluster (1 member) on the persistence-layer axis. Cross-axis bundle with silent-fallback cluster (input vs persisted state, structurally-absent-error-kind) and with provenance quartet #259+#271+#273+#275 (emission vs load-validation of state-of-record). Concrete delta this cycle: ROADMAP-only pinpoint appended after static audit of `Session::from_json`/`Session::from_jsonl` version-handling arms — three call sites for `SESSION_VERSION` (1 declaration + 2 assignments, 0 comparisons), zero migration/mismatch sites in the entire `runtime/src/session.rs` file. Concurrent-dogfood-rebase parity will be confirmed local==origin==fork at HEAD `4e4edc8+#278` after push.
|
||||
|
||||
Reference in New Issue
Block a user