Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@argszero/cordis-plugin-session-audit

A proactive session-log completeness audit for the deepseek-harness (dsh) agent harness. It never fixes a log — it diagnoses the full corruption surface and prints a report before replay crashes or silently drops history.

Why this exists

The dsh harness persists every agent interaction to an append-only JSONL session log. Two in-tree mechanisms already exist, but neither is a full diagnostic:

  • repair.ts (interruptedTurnClosers) is narrow — it only synthesizes replay-time closers for a crash tail (open-turn truncation), one mechanism.
  • validateStoredEvents (storage-contract) is a fail-closed load-time check — it refuses an unrecognized/structurally-invalid log outright, but tells you only "this log is bad", not which of the many corruption classes is present.

Upstream, the session-log corruption family has produced 14+ distinct bug reports (seq gaps, index-reuse, format-version drift, empty-id duplicates, orphan tool_calls, turn-reason loss). This plugin audits for all of them at once and classifies each finding as error (replay would fail / needs manual) vs warn (data may have silently dropped) vs info (recoverable — e.g. the open-turn tail the in-tree repair already handles).

Detection surface

Code Severity What it means
HEADER_MISSING error First line is not a session header; log unusable
FORMAT_VERSION_DRIFT error Log format vN differs from the harness's SESSION_FORMAT_VERSION (written by newer/older harness)
UNPARSABLE_LINE error A committed line is not a decodeable session record
SEQ_GAP error Events are missing between seqs (contiguity break)
SEQ_DUPLICATE warn Same seq appears twice (index reuse)
SEQ_REORDERED warn An event appears below the contiguous watermark (out-of-order / reused seq)
UNKNOWN_REQUIRED_TYPE error A type outside the known vocabulary and not marked ignorable. The harness read path refuses a log containing an unmarked unknown type (SessionFormatUnsupportedError), so a reload of this session will hard-fail — this is the #5769 failure mode (a plugin wrote a custom event type that the log then rejects). Fix: mark the event ignorable, or the session may be unrecoverable.
TOOL_CALL_NO_ID / TOOL_RESULT_NO_ID warn tool/call or tool/result carries no usable call identity
ORPHAN_TOOL_CALL info A tool was requested (tool/call) but its outcome was never durably recorded (tool/result)
OPEN_TURN info A turn opened but never closed (crash interruption; the in-tree repair synthesizes closers for this)
EMPTY_TEXT_BLOCK warn A persisted assistant/message carries an empty/whitespace-only {type:'text', text:''} content block. Harmless under lenient models (GLM/Gemini), but a strict provider (Claude) refuses it and the session becomes permanently unusable after a model switch (#5773). Strip these blocks before replaying at a new provider.
UNREADABLE_ARTIFACT error The artifact is a Zstandard container whose structure is broken — a bad frame magic, a reserved frame-header bit, a reserved block type. This is the class that takes the whole application down: the harness's artifact listing aborts on it, so dsh cannot boot at all until the file is moved out of the sessions root (#7161). The finding names the byte offset, in the harness's own wording.
UNDECODABLE_FRAME error The container structure is intact, so the frames can be located, but a frame's content failed validation (checksum or entropy decode). The session cannot be read; unlike UNREADABLE_ARTIFACT this does not abort a boot, because the failure happens after the listing.
TORN_FINAL_FRAME info The final Zstandard frame is incomplete — a crash tail from a process killed mid-append. The harness tolerates this class while listing and repairs what it can, so it is reported as info and never as needs-manual. The tail's plaintext is recovered on a best-effort basis and reported separately; it is never folded into the audited content, which covers the complete frames only.
SESSION_PATH_MISMATCH error The artifact does not sit in the directory its own header names. The backend re-derives the path from the header on every read (assertStoredIdentity, session-persistence-jsonl) and refuses the read — corrupt session log "…": header id "…" and cwd identify "…" — and that refusal escapes the artifact listing, so it aborts the boot exactly like UNREADABLE_ARTIFACT (#7161, the renamed-directory row). It fires when a session directory was renamed or moved by hand. The session directory must be the header id under the backend's lossless path encoding; the check reports a path shape, so if both names resolve to one file (a symlink or hard link) the backend accepts the artifact and the finding is advisory.
PROJECT_PATH_MISMATCH warn The artifact sits under a project directory that the header's recorded cwd does not derive. The backend's project naming is deliberately lossy (separator runs collapse to one -, the slug is truncated to fit a filesystem component), so a disagreement is advisory — it usually means the layout predates a naming revision, not that the read is refused. A header with no cwd is compared against the _no-cwd bucket, which is where the backend puts such a session.
EMPTY_ARTIFACT info Zero bytes. The harness skips an empty artifact rather than refusing it, so this is not corruption.
REPETITIVE_STREAM warn A run of many identical consecutive assistant/chunk deltas within one stream — the signature of a degenerate decode loop (the model emits the same short token(s) repeatedly with no tool call between them, e.g. '\n' × 701). No tool-call repeat guard can see this because no tool call is made; the harness itself places no turn-length bound.

Usage

As a mounted Cordis plugin (slash command)

Add it to a cordis.yml resolution manifest:

- insert:
    - id: session-audit
      name: '@argszero/cordis-plugin-session-audit'

Then /session-audit audits every stored session artifact — .jsonl and the compressed .jsonl.zstd container — under the JSONL root, and /session-audit id=<substring> narrows to matching session ids. Configure the root via plugin config:

- insert:
    - id: session-audit
      name: '@argszero/cordis-plugin-session-audit'
      config:
        root: !!js dshHomePath('sessions')

As a one-shot CLI (no harness needed)

The package ships a session-audit bin that runs under plain Node and needs zero dsh runtime:

# audit a whole sessions root
npx @argszero/cordis-plugin-session-audit /path/to/sessions

# narrow to one session id
npx @argszero/cordis-plugin-session-audit /path/to/sessions --id <substring>

# default root from DSH_HOME
DSH_HOME=/path/to/home npx @argszero/cordis-plugin-session-audit

Exit code. The CLI exits 1 when any artifact needs manual attention, 0 otherwise — so it can gate a launch:

npx @argszero/cordis-plugin-session-audit "$DSH_HOME/sessions" && dsh web

Notes

  • Both containers are read. .jsonl and .jsonl.zstd are audited alike, so a machine with compression enabled gets the same findings as one without. The container is chosen by artifact name, which is how the harness chooses it — a .jsonl.zstd file whose bytes are not Zstandard is reported as the structural defect it is, not as a malformed header.
  • Per-frame, deliberately. Node's zstdDecompressSync accepts a concatenated container and decodes only the first frame, with no error. The harness's container is concatenated frames, one per durable batch, so a one-shot decode silently returns the first batch and drops the rest — which would make a badly truncated session read as clean. This tool scans the frame structure first and decodes each frame, which is also what lets it tell a torn crash tail apart from a structural defect.
  • Two-phase, so the important verdict survives a weak runtime. The structural scan is plain Buffer arithmetic and needs no node:zlib; decompression is the optional second phase. On a Node without zstdDecompressSync the audit still reports whether an artifact would abort a boot.
  • Non-mutating. This plugin never appends to, truncates, or rewrites a session log.
  • It is a diagnostic companion, not a replacement for repair.ts or a fix. Use it to understand what is wrong, then decide whether the in-tree repair or a manual migration is appropriate.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages