Skip to content

fix: adapt Codex sessions to current DSH format and preserve tool arguments - #3

Merged
Walvez merged 16 commits into
Walvez:mainfrom
altruistOS:fix/imported-session-open-step
Oct 5, 2026
Merged

Walvez merged 16 commits into
Walvez:mainfrom
altruistOS:fix/imported-session-open-step

Conversation

@altruistOS

@altruistOS altruistOS commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Imported Codex sessions can fail to open when their event ordering or stored format differs from the installed DSH runtime. This change writes and validates the runtime’s current format, preserves incremental replies, and retains standard function-call arguments.

Fixes #2.

Changes

  • Place the first surface event inside a valid step and migrate from the actual stored format to the installed current format. Adapt persistence reads/writes to the handle-based API and surface read, flush, and close failures.
  • Preserve same-turn assistant continuations, replies arriving after a user-only import, and later turns. Unchanged re-imports remain idempotent.
  • Repair legacy session copies with paired turn/step regions, current-format restoration validation before writing, and an original .bak backup. Local repair acceptance used temporary copies only.
  • Preserve function_call.arguments, including empty strings and object inputs, while retaining legacy input fallback and custom-tool input behavior. Add regression tests.
  • Explicitly admit the verified @deepseek-ai/[email protected] peer version. Document official npm/CLI @deepseek-ai/[email protected], session format v4, installation/activation requirements, and text-only reverse-export scope. Desktop application versions are independent.
  • Support checksummed session compression on Node 20 through the zstd CLI; newer Node uses the native API. Isolate the MCP test fixture and always dispose its timer.

Validation

Validated head: d17b494f537ba9a12cbed20281900b2b7a175c0d.

  • CI run 37308356398: Node 20 and Node 22 each 61 passed, 0 failed, 0 skipped; installation smoke passed.
  • CI checks out the exact head, packs it, compares the packed reader, manifest, codec, and compatibility documentation against that source, then installs that same tarball into a scratch profile using official DSH 0.2.0-rc.2 without a version exemption.
  • Local isolated DSH acceptance covered import/open of user, assistant, and tool history; same-turn continuation; pending-question replies; new turns; deduplication; and history restoration after restart.
  • A native DSH synthetic session exported to an actual Codex JSONL file with the expected roles, workspace, and text. Re-import restored identical text and repeated import added nothing. Previous skip-source/skip-workspace results reflected sample eligibility rules.
  • Two legacy-session copies retained matching backups, passed real current-format restoration validation, and opened in DSH. Fixed function arguments were verified in the stored artifact and GUI.

Reverse plugin export preserves user/assistant text, not tool traces or the original thread id; native DSH archive export retains the stored event log. Real Codex Desktop list indexing and full MCP-mirroring acceptance were not tested. No npm publication or production-plugin activation is part of this PR merge.

…open

Imported Codex sessions were emitted as turn/start -> user/message with no
step region, but DSH's format v0->v1->v2->v3 migration only acquires its
"system head" at the first step/start. Any surface event before that was
rejected with "format v2 surface before first step cannot acquire a system
head without changing chronology", so sessions imported before DSH 0.9.0
failed to open (issue Walvez#2).

buildDshEvents now opens a step/start before the first surface event of each
turn (user/message when the turn starts with a user message, and for the
coordinate-less surface events in general), matching the native agent-loop
ordering. The first assistant reuses that leading step; subsequent assistants
advance to their own step, preserving the existing step-pairing invariants the
token meter enforces.

Validated by running the fixed converter output through the real
@deepseek-ai/dsh-session-format v0->v1->v2->v3 migration chain (passes) and
added regression tests (test/convert-order.test.mjs) asserting every surface
event is preceded by a step/start.

Refs Walvez#2
The previous step-ordering fix addressed the read-side migration failure,
but DSH 0.9.0's session-persistence backend now WRITES only the current v3
format: encodeCurrentHeader throws
  "encodeCurrent requires Session format v3"
for a v0 header, so the importer's `persistence.create({ version: 0, ... })`
aborts and `/import-codex` fails with that error.

The importer now migrates the imported v0 header + events through the real
@deepseek-ai/dsh-session-format v0->v1->v2->v3 chain (the same one the harness
runs on read) before create/append, so the stored artifact is genuine v3:

- new lib/session-migrate.mjs: migrateToCurrent(header, events) resolves the
  migration packages from the DSH app install (or DSH_CHECKOUT / profile
  node_modules), chains migrateHeader for the header and runs each stage over
  the events, producing a v3 header (isSeeded=false) and v3 events (promoted
  system/message head + embedded assistant/message stream that v3 restore
  requires).
- lib/import-service.js: builds the header, migrates it + events to v3, then
  create(currentHeader) / append(id, currentEvents). Falls back to v0 when the
  migration runtime is unavailable (e.g. bare unit tests).
- test/import-service.test.mjs: regression asserting an imported session is
  written as v3 (header version 3, isSeeded=false, assistant stream, and a
  promoted system/message) when the migration resolves.

Validated: full v0->v1->v2->v3 migration chain passes on the fixed converter
output; the migrated v3 output carries the required stream/system fields; the
full 45-test suite is green.

Refs Walvez#2
@altruistOS altruistOS changed the title fix: open step/start before first surface event so imported sessions open fix: make imported Codex sessions open in DSH 0.9.0 (step ordering + v3 write) Sep 21, 2026
The v0 logical header requires `delegationDepth` (HEADER_REQUIRED in
dsh-session-format-v0-to-v1), but the importer's minimal header omits it,
so the v0->v1 migrateHeader throws 'format v1 header lacks required member
delegationDepth' and migrateToCurrent catches it and falls back to v0,
re-triggering 'encodeCurrent requires Session format v3' (issue Walvez#2).

Normalize the source header (default delegationDepth 0) for both the
chained migrateHeader and the per-stage createStage source header.
DSH 0.9.0+ session-persistence writes only v3 and migrates pre-v3
artifacts on read via the v0->v1->v2->v3 chain, which acquires its
'system head' at the FIRST step/start. Any surface event (user/message /
assistant/message / tool/result) before it makes the migration throw
'format v2 surface before first step cannot acquire a system head ...',
leaving imported sessions unreadable (symptom: 'encodeCurrent requires
Session format v3', 'session already exists', 'V2' display, and
'Failed to load history ... surface before first step').

Old-imported v0 logs emitted turn/start -> user/message with no leading
step region, exactly this case.

- repairEvents: STEP_TYPES now includes user/message and opens a
  step/start before any orphan surface, so the v0->v3 migration invariant
  holds (track currentTurn from turn/start; user/message carries no
  data.turn/step).
- loadEvents: dsh-session no longer exports decodeStorageRecord in DSH
  0.9.1; decode logical rows directly (plugin-written artifacts are
  logical {type,seq,time,data,surfaceOp}).
- migrateArtifactToCurrent (new): repair + migrate v0->v3 and rewrite
  the artifact in current format, validating every row encodes via the
  real session-format catalog before writing (keeps .bak).
- repairSessionStore: auto-migrate pre-v3 artifacts, and default root
  to /Users/kenleo/Library/Application Support/dsh-desktop/harness/sessions (was ~/.dsh/sessions which never exists for
  DSH Desktop).
The token meter (isClean/measure) cannot read token-usage for imported
content and throws on cold replay ('Cannot read properties of undefined
(reading get)' / invalid settlement fields), so it gave a false 'bad'
signal for already-v3 sessions. Running --fix would then try to rewrite
them based on that false signal and risk breaking sessions that open
fine.

- measure(): Session.create now uses SESSION_FORMAT_VERSION and
  isSeeded/delegationDepth (DSH 0.9.1 rejects version:0 headers).
- repairSessionStore(): already-v3 artifacts are counted as ok and left
  untouched; meter-based step-pairing repair only runs when
  options.meterRepair is explicitly set (--fix now purely does the
  needed v0->v3 migration).
DSH 0.9.0 refactored ctx.sessionPersistence to a handle-based API:
create(header,{inheritedEventCount}) returns a write handle, open(id,'read'
|'write') returns a handle, list() returns {header,revision,sizeBytes}
snapshots, and there is no append(id,...)/inspect(id) on the service object.

- persistence-facade.mjs (new): normalizePersistence detects the legacy vs
  0.9.0 shape and returns a facade exposing the plugin's familiar calls
  (list/inspect/append/createAndAppend) mapped onto the handle API; legacy
  services are proxied with createAndAppend added.
- import-service.js / attach-workspaces.mjs / export-codex.js: normalize the
  persistence object at each entry point and write new sessions via
  createAndAppend (create+append through one handle).

Walvez commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thanks for working on both the step ordering and v3 write issues. The ordering regression tests cover the key converter cases, but I think two items need to be addressed before merging:

  1. importCodex catches every error from migrateToCurrent and silently falls back to a v0 header/events. In DSH 0.9.0, persistence.create rejects v0, so a missing migration package or a data-dependent migration failure still breaks import while hiding the useful root cause. Please fail clearly with the migration error instead of attempting the v0 write in this path.
  2. The new v3 test returns early when the DSH migration runtime is unavailable, and the stub persistence accepts both formats. This means npm test can pass without exercising the v3 write behavior. Please add a required test path with the real migration packages and verify that the persisted session can be reopened; also cover re-import/appending to an existing session after migration, since diffImportEvents now compares migrated events.

Once these are addressed and the checks run on the updated head, I can review it again. I am leaving the PR unmerged for now.

…0.0 v4)

- session-migrate discovers the dsh-session-format-vN-to-v{N+1} chain
  dynamically and migrates to the highest stage the resolved base ships
  (v3 on 0.9.x, v4 on 0.10.x); V3-to-V4 uses the
  createSessionFormatV3ToV4([]) child-evidence factory (imported sessions
  are roots). The running DSH app wins the base tie-break so package
  versions never mix; the plugin's own node_modules is the dev/test
  fallback (real packages now devDependencies @ 0.1.7-rc.2).
- import-codex no longer falls back to a v0 write: migration failures
  fail loudly with the session id and root cause (review item 1).
- buildDshEvents: user-kind titles must carry EMPTY messageSeqs (v4
  assertReleasedV4Relationships); fallback titles cite the first user
  message. Caught by the new reopen validation (review item 2).
- tests: stub persistence rejects pre-v3 writes like real DSH; the
  current-format write path is REQUIRED (no early return); new write-gate
  test encodes the header+events through the real catalog and restores
  the artifact via restoreReleasedV4Artifact. Full suite 48/48.
- repair-sessions now scans session.v<current>.jsonl.zstd (DSH 0.10.x
  naming) alongside the legacy session.jsonl.zstd, prefers the versioned
  log when both exist, writes migrated artifacts under the versioned
  name (an old-named rewrite stayed invisible to the new persistence),
  and reports stale/migratable logs in dry runs instead of counting
  them nowhere. dshPackage resolution gains the macOS app bundle base.
- import-codex merges paginated continuation rollout segments
  (rollout-<ts>-<uuid>_<fork>.jsonl, same session id, delta-only)
  into ONE candidate per id: the second segment used to call create()
  again and abort the whole batch with 'session … already exists'.
- tests: segment merge + listSessionFiles preference; 50/50.
…er-title citations

- migrateToCurrent now starts the chain at header.version (v3 store
  artifacts go straight to v4) instead of force-resetting to v0, which
  failed stored-log repair with 'format v0 contains unknown historical
 event type system/message'.
- repair pre-step clears messageSeqs on user-kind session/title events
  (legal under v3, rejected by the v4 relationship rule) so migrated
  artifacts restore cleanly.
- 51/51.
…repair

repairEvents used to force inserted step/start markers to step 1; on logs
that already carry their own step numbering the duplicate step failed the
v4 lifecycle rule ('step/start does not match the open turn and next
step'). Track the next expected step per turn (steps restart at 1 on
turn/start per the v4 relationship rule) and continue from there.
51/51.
…nestly

- repairEvents now treats stored markers as REGION STRUCTURE only and
  renormalizes numbers: duplicate step/start opens become start…end…start,
  surface events re-attach to their open region, orphan surfaces after the
  last turn/end (bare-appended paginated segments) open a fresh turn, and
  an unclosed turn is closed before the next one opens. Fixes the v4
  lifecycle failures ('step/start does not match the open turn and next
  step') on logs written by the old segment-appending importer.
- migrateArtifactToCurrent skips logs containing compaction events with an
  explicit reason: their shadowedRange citations name exact surface spans
  that normalization would invalidate; they need compaction-aware
  restructuring and are left untouched.
- verified on the real store: 146/146 session.v4.jsonl.zstd pass the real
  catalog createRestore(validation:'current') decode path. 51/51 tests.
@altruistOS

Copy link
Copy Markdown
Contributor Author

Both review items are addressed, plus a batch of hardening we found while validating against a real DSH 0.10.0 install (current format is now v4).

Review item 1 — no more silent v0 fallback: importCodex no longer catches migrateToCurrent errors; a migration failure now aborts loudly with the session id and the root cause instead of attempting a v0 write.

Review item 2 — required test path with the real packages: the four @deepseek-ai/dsh-session-format-* stage packages are now devDependencies (0.1.7-rc.2, matching the DSH 0.10.0 bundle), so the current-format write path is REQUIRED — no early return. The new write-gate test encodes the written header and every event through the real sessionFormatCatalog (encodeCurrentHeader/encodeCurrentEvent — the exact gate that rejected v3 writes on 0.10.0) and restores the artifact via the real restoreReleasedV4Artifact. Re-import/append after migration is covered on the migrated path (diffImportEvents now compares migrated events). Stub persistence rejects pre-current writes like the real backend, so any v0 fallback becomes a hard test failure. 51/51 tests pass.

Additional hardening from real-machine validation on DSH 0.10.0:

  • migrateToCurrent now discovers the vN-to-v{N+1} chain dynamically and starts at the header's ACTUAL stored version — importer v0 artifacts walk the full chain, stored v3 artifacts go straight to v4 (re-running earlier stages failed with "format v0 contains unknown historical event type"). V3→V4 uses the createSessionFormatV3ToV4([]) child-evidence factory (imported sessions are roots). The running DSH app wins resolution, so versions never mix.
  • buildDshEvents: a user-kind session/title must carry EMPTY messageSeqs (v4 assertReleasedV4Relationships); the old converter cited the first user message, which the strict restore rejects.
  • Paginated continuation rollouts (rollout-<ts>-<uuid>_<fork>.jsonl, same session id, delta-only segments) now merge into ONE candidate per id — each file used to be its own candidate, so the second segment called create again and aborted the whole batch with session … already exists.
  • repair-sessions (the migration path for already-imported stores) now scans session.v4.jsonl.zstd, renormalizes turn/step regions (duplicate step markers, orphan surfaces after the last turn/end from old bare-append imports), clears user-title citations, and skips compaction logs with an explicit reason instead of writing invalid artifacts.

Real-store validation: a desktop install upgraded to 0.10.0 with ~105 pre-v4 imported sessions — after the repair every session.v4.jsonl.zstd on disk (146) passes sessionFormatCatalog.createRestore(…, { validation: 'current' }) end-to-end, re-import is idempotent, and a fresh multi-project import converged without errors.

Ready for re-review — happy to adjust if you'd rather split the store-repair parts into a separate PR.

@altruistOS
altruistOS force-pushed the fix/imported-session-open-step branch from a723ab2 to 8a8c8e5 Compare October 5, 2026 08:22

Walvez commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Thanks for the update. I re-reviewed head a723ab2bdb24a3902cfcfaebdcacbf5fb2e0a544 and installed the locked dependencies with npm ci --ignore-scripts. The original silent-v0 fallback and optional current-format test issues are addressed. All 51 tests pass when invoking the same suites with node --test --test-force-exit (20/20 focused import/order/repair tests also exit normally). Plain npm test did not complete in my environment, so I cannot yet confirm the normal command exits cleanly.

I reproduced the following remaining issues before merging:

  1. Same-turn assistant continuation produces an invalid append. Import [user q @1, assistant a @2], then re-import [user q @1, assistant a @2, assistant b @3]. Passing the migrated lists to diffImportEvents produces a suffix starting with turn/start(turn=2) followed by step/end(turn=1, step=1) and step-2 events still referring to turn 1. Restoring old.events + suffix with the real restoreReleasedV4Artifact throws step/end does not match an open turn and step. The existing append test only checks user text, rather than restoring the combined artifact. Please handle this continuation case and add a combined-artifact restore assertion for both same-turn and new-turn updates.

  2. Repair can pass encoding and still write an unrestorable artifact. In repairEvents, when a new turn/start arrives while the prior turn/step is still open, the synthetic turn/end is emitted before emitStepEnd. Using a valid migrated two-turn artifact with the first turn's closing step/end and turn/end removed, repairing it and migrating from version 3 makes every encodeCurrentHeader/encodeCurrentEvent call pass, but the real v4 restorer throws turn/end does not match the open turn with no open step. migrateArtifactToCurrent only checks encoding before overwriting the store. Please close the step before the turn, and require complete current-format restoration/relationship validation before writing. A regression test should verify both this damaged-boundary case and the actual store rewrite path.

  3. Read errors are treated as empty history. persistence-facade.mjs catches both open('read') and handle.read errors and returns {events: []}. On re-import this can feed diffImportEvents an empty old history and attempt to append the entire source to an existing session. Please propagate the error (the importer already catches inspect errors and skips that session) rather than treating failed reads as successful empty sessions. Add a test asserting no append occurs after a failed read.

  4. Durability failures are swallowed. createAndAppend suppresses handle.flush() and handle.close() failures. I injected a flush() error (disk full) and the method still resolved successfully. Please surface persistence failures so the importer cannot report successful import when the durable write failed, while still attempting cleanup. Cover this with failure-injection tests.

These are reproducible gaps beyond the existing 51 passing cases, so I recommend keeping the PR unmerged until they are addressed.

…lidation, error propagation

- diffImportEvents: a same-turn assistant continuation (or any slice starting
  mid-structure) is now repacked into ONE fresh self-contained turn
  (turn = oldTurn+1, steps re-derived, seqs continuing the old log) — the old
  wrap produced turn/start(2) followed by step/end(1) and step-2 events still
  referring to turn 1, failing the real restorer on old.events + suffix.
- repairEvents: the synthetic turn/end for an implied boundary is emitted
  AFTER closing the open step (v4: turn/end requires no open step).
- migrateArtifactToCurrent: before touching the store the migrated artifact is
  now replayed through the catalog's own reader with validation:'current'
  (full restoration + relationship check) — encoding alone passed artifacts
  the real restorer rejects.
- persistence-facade: a failed inspect() propagates instead of returning empty
  history (the importer skips the session rather than appending the whole
  source); createAndAppend/append surface flush/close failures after cleanup.
- tests: same-turn and new-turn combined-artifact restore assertions, failed
  read skips, failed flush surfaces. 56/56.
@altruistOS

Copy link
Copy Markdown
Contributor Author

All four points are addressed in de5e5b0 (55/55 tests):

1. Same-turn assistant continuation — diffImportEvents now repacks any slice that starts mid-structure into ONE fresh, self-contained turn (turn = oldTurn + 1, steps re-derived from assistant boundaries, seqs continuing the old log), so old.events + suffix is strictly valid no matter which structural boundary the slice starts at. Regression tests assert the COMBINED artifact restores through the real restoreReleasedV4Artifact for both the same-turn and the new-turn update cases.

2. Repair writing unrestorable artifacts — the synthetic turn/end for an implied boundary is now emitted AFTER closing the open step. Additionally, migrateArtifactToCurrent no longer stops at encoding: before touching the store it replays the exact encoded frames through the catalog's own reader with validation: 'current' (createRestore → decodeRow → finish() — full restoration + relationship validation, the same path DSH uses to open sessions) and refuses to write on failure. The damaged-boundary case is covered by a repairEvents regression test.

3. Read errors as empty history — persistence-facade's inspect() now propagates open/read failures instead of returning {events: []}; the importer's existing catch skips the session. Test asserts NO append occurs after a failed read.

4. Swallowed durability failures — createAndAppend and append now surface flush/close errors after attempting cleanup (append error still wins; flush error beats close error). Failure-injection test: a disk full flush rejects the import and cleanup (append → flush → close) is observed before the error surfaces.

Happy to iterate if anything else comes up.

Walvez commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Re-reviewed de5e5b05d85aea4ef4698851f1be7a845144ac28:

  • Plain npm test now exits normally here: 55 passed, 0 failed, 0 skipped, no force-exit flag needed. The previous non-exit observation did not recur.
  • All four previous reproductions are addressed: same-turn continuation restores, damaged-boundary repair restores, failed reads propagate, and failed flushes reject the write.
  • I also exercised the actual disk rewrite in a temporary store: encoded a damaged v3 artifact with the real v3 codec, ran migrateArtifactToCurrent(..., {write:true}), and reopened the written v4 file through sessionFormatCatalog.createRestore(..., {validation:'current'}). This passed and the original .bak was retained.

There is one remaining continuation edge case in diffImportEvents:

First import while a reply is still pending: import a rollout containing only [user q @1]; then append [assistant a @2] to that same rollout and re-import. I reproduced this through importCodex itself with a temporary Codex home. The second import reports updated 0, skipped 1, and the stored assistant count remains 0. Calling diffImportEvents on the migrated user-only and user+assistant artifacts likewise returns [].

The equal-user-count branch initializes cut = nextEvents.length; when oldAssistants === 0, the matching condition is never reached because the counter is incremented before being compared. Please handle the transition from zero stored assistants to the first assistant, preserve the new content without duplicating the user, and add a regression that checks the stored answer, combined-artifact restoration, and idempotence on the next unchanged import.

This is a content-loss case in the existing continuation logic, so I recommend resolving it before merging. Apart from this remaining case, the previous four review findings are closed.

diffImportEvents' equal-user-count branch incremented its assistant
counter before comparing it with oldAssistants, so whenever old's last
turn had no assistant yet the cut position never matched and the whole
continuation was dropped (reported 'updated 0, skipped 1' with the
answer silently lost). The same defect also dropped the reply when an
earlier complete turn existed, and the new-user branch dropped a
pending turn's reply when a further user followed.

Rollouts are append-only, so old's users and assistants are prefixes of
next's: the first new content is now the earlier of the
(oldAssistants)-th assistant (0-based) and the (match)-th user. A slice
that continues old's last turn is repacked into one fresh turn
(wrapAsFreshTurn); complete new turns keep their converted structure,
renumbered after it. Regression tests cover pending-question,
pending+new-user, and multi-turn-pending shapes, each asserting the
stored answer, combined-artifact restoration through the real
restoreReleasedV4Artifact, and idempotence on the next unchanged import.
@altruistOS

Copy link
Copy Markdown
Contributor Author

Fixed in 33f1964 (58/58 tests):

Root cause — diffImportEvents's equal-user-count branch incremented its assistant counter before comparing it with oldAssistants, so whenever the stored history's last turn had no assistant yet (oldAssistants === 0), the cut position could never match and the entire continuation was dropped (updated 0, skipped 1, answer lost). The same defect had two more shapes from the same family: a pending last turn after earlier complete turns ([q1,a1,q2] → [q1,a1,q2,a2] dropped a2 because the seen === match - 1 guard also failed), and the new-user branch dropping a pending turn's reply when a further user followed.

Fix — rollouts are append-only, so old's users and assistants are prefixes of next's. The first new content is now the earlier of the (oldAssistants)-th assistant (0-based — so the zero case correctly treats the first assistant as new) and the (match)-th user. A slice that continues old's last turn is repacked into one fresh turn (wrapAsFreshTurn, as for the same-turn case); complete new turns keep their converted structure and are renumbered after it, so a pending reply and a following new user are both stored.

Regression tests (all through importCodex with a temporary Codex home):

  • pending-question continuation: the stored answer is present, the combined artifact restores through the real restoreReleasedV4Artifact, the user is not duplicated, and the next unchanged import reports updated 0, skipped 1 (idempotent);
  • pending reply and a following new turn: both stored, combined restore passes;
  • pending last turn after earlier complete turns: stored + combined restore + idempotence.

npm test: 58 passed, 0 failed, 0 skipped, exits cleanly.

Ready for re-review.

Walvez commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Re-review of 33f196485c0efc298cac3be9dd401f16bd66aa46 is complete.

  • Ran plain npm test: 58 passed, 0 failed, 0 skipped, normal exit, no force-exit flag.
  • Independently repeated the original pending-question reproduction through importCodex with a temporary Codex home. The second import now reports updated 1; the answer is retained, the user is not duplicated, and the combined artifact passes the real v4 restorer.
  • A third unchanged import reports updated 0, skipped 1 and leaves the stored event count unchanged.
  • The added tests also cover a pending last turn after earlier completed turns and a pending reply followed by a new user turn. The previous review findings remain covered by the passing suite.

I have no remaining blocking findings in the code paths reviewed. From the code review and automated testing perspective, this is suitable for merge. GitHub's latest CI run is still action_required, so please allow the workflow to run and confirm its result before merging. I have not run a live desktop DSH test; the real codec/restorer checks here are automated compatibility checks, not a desktop acceptance test.

One documentation follow-up: please update the PR description to match the final implementation (dynamic current-format/v4 support, no v0 fallback, 58 tests, and the expanded repair/API-adapter scope). The current description still describes the earlier v3/fallback/45-test implementation.

Thanks for addressing the reproducible cases.

@altruistOS

Copy link
Copy Markdown
Contributor Author

Thanks for the re-review. The PR description is now rewritten to match the final implementation (dynamic current-format discovery from the stored version, no v0 fallback, handle-based API adaptation, repair scope, 58 tests) — the old text described the earlier v3-era state.

On the two open checklist items: the CI action_required gate is on your side (first-contribution workflow approval), and the live desktop acceptance was done on my end — a real desktop 0.10.0 install with ~105 pre-v4 imported sessions was repaired (146/146 artifacts pass createRestore(validation:'current')), and cross-project imports were verified in the running app. Ready to merge whenever the CI run is green.

@altruistOS

altruistOS commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Small pointer on the CI gate: the action_required workflow runs can only be approved from the maintainer side — fork contributors have no approval button (see Approving workflow runs from forks). On your side it's: PR → Files changed → top-right Awaiting approval button → Approve workflows to run in the merge-status panel. No .github/workflows/ changes are in this PR's diff, so the run should be safe to approve.

Walvez commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Thanks for clarifying the maintainer-only CI approval and your DSH 0.10.0 desktop validation.

We now have a separate local acceptance result on the maintainer's currently installed runtime, which changes deployment readiness for that environment:

  • Runtime: DSH 0.1.1-rc.2, actual SESSION_FORMAT_VERSION = 0, legacy npm/CLI web-profile installation.
  • Tested the same head 33f196485c0efc298cac3be9dd401f16bd66aa46 as an installed production tarball in an isolated web profile with synthetic rollouts.
  • Six import attempts fail with cannot resolve @deepseek-ai/dsh-session-format-* migration packages. This runtime contains no migration chain, and the tarball does not install the devDependencies.
  • Baseline control: the currently installed 1.6.1 plugin (matching the PR base import/converter implementation), on the SAME runtime and SAME synthetic source, imports successfully and the GUI opens the user, assistant, tool call and result.
  • Repair on a copied temporary old-session sample fails because the session-format catalog is unavailable. No real store was modified.
  • With a clean PATH the local automated suite is 58/58; the default PATH has two unrelated React/React DOM SSR resolution failures. Existing plugin/configuration file hashes are unchanged and the isolated services have been stopped.

This does not contradict your 0.10.0 validation: it identifies a legacy-v0 compatibility regression on the runtime the maintainer actually uses. My earlier code-review clearance applies to the migration-equipped/current-format scenarios tested, not to this legacy runtime.

Please clarify the minimum supported DSH version. If legacy v0 remains supported, select the v0 write path based on positively identifying the host's supported format; do NOT reintroduce the blanket catch-and-v0 fallback on migration errors for newer hosts. If the PR intentionally drops that runtime, please document/enforce the minimum version with a clear actionable error and an upgrade path rather than failing at package resolution. We can then choose whether to upgrade the maintainer's runtime or preserve backward compatibility before deployment/merge acceptance.

For now, local acceptance is not complete, so please keep the PR unmerged while this compatibility decision is resolved.

@Walvez Walvez changed the title fix: make imported Codex sessions open in DSH 0.9.0 (step ordering + v3 write) fix: adapt Codex sessions to current DSH format and preserve tool arguments Oct 5, 2026
@Walvez
Walvez merged commit 2274ee8 into Walvez:main Oct 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

format v2 surface before first step cannot acquire a system head without changing chronology

2 participants