fix(host): classify backfill format refusals by the observable name family

The persistence seam translates the format edge's
SessionFormatUnsupportedMigrationError into SessionFormatUnsupportedError
(with the "source vN artifact remains unchanged" suffix) before the error
escapes, so the exact-name check never matched on any supported dsh
baseline: the skip summary never fired and every refused legacy session
kept its own WARN (issue #75). Classify by the SessionFormatUnsupported
prefix instead, and cover both faces of the translation in the fixture.
This commit is contained in:
bowenliang123 committed 2026-09-20 10:30:01 +08:00
1 parent 0b8175e721
commit af9c69bd09
2 files changed
+35 -16

No files matched your search

+17 -10
View File
@@ -93,8 +93,15 @@ interface ConnectionHostFace {
*/
const YIELD_MS = 100
/** The `name` dsh raises for a durable log its migration surface refuses. */
const UNSUPPORTED_MIGRATION = 'SessionFormatUnsupportedMigrationError'
/**
* The `name` family dsh raises for a durable log it cannot interpret. The
* format edge throws `SessionFormatUnsupportedMigrationError`, but the
* persistence seam translates it — together with every other "intact yet not
* interpretable" refusal — into `SessionFormatUnsupportedError` before the
* error escapes (issue #75): classify by the family prefix, since either
* exact name may be the one that arrives.
*/
const UNSUPPORTED_FORMAT = 'SessionFormatUnsupported'
function asRecord(value: unknown): Record<string, unknown> | null {
return value !== null && typeof value === 'object' ? value as Record<string, unknown> : null
@@ -170,15 +177,15 @@ function isLive(sessions: Record<string, unknown> | null, id: string): boolean {
}
/**
* Whether the cold read failed because the running harness refuses to
* migrate this log — a permanent, source-side refusal (the artifact is left
* unchanged, so retrying cannot succeed). Matched by the error's documented
* `name`, so no dsh symbol needs importing and every supported baseline
* classifies alike: an unknown error shape just falls to the warn path.
* Whether the cold read failed because the running harness refuses this log's
* format — a permanent, source-side refusal (the artifact is left unchanged,
* so retrying cannot succeed). Matched by the error's documented `name`, so no
* dsh symbol needs importing and every supported baseline classifies alike: an
* unknown error shape just falls to the warn path.
*/
function isUnsupportedMigration(error: unknown): boolean {
function isUnsupportedFormat(error: unknown): boolean {
try {
return asRecord(error)?.name === UNSUPPORTED_MIGRATION
return String(asRecord(error)?.name).startsWith(UNSUPPORTED_FORMAT)
} catch {
return false
}
@@ -271,7 +278,7 @@ export function watchActivityBackfill(ctx: Context): () => void {
;(cache as unknown as ProjectionCacheLike).coldSnapshot(log.header, log.inheritedEventCount, log.events)
folded++
} catch (error: unknown) {
if (isUnsupportedMigration(error)) {
if (isUnsupportedFormat(error)) {
// Permanent and source-side: the per-session detail drops to
// debug; the summary below is the pass's only above-debug word.
refused++
+18 -6
View File
@@ -59,10 +59,19 @@ function captureLogs(ctx: Context): LogLine[] {
return lines
}
/** The migration-refusal error dsh raises for a legacy log it cannot migrate. */
function unsupportedMigrationError(): Error {
const error = new Error('subagent/descriptor 0 uses unsupported descriptor version 2; source v0 artifact remains unchanged')
error.name = 'SessionFormatUnsupportedMigrationError'
/**
* One format-refusal face of the persistence seam. The format edge throws
* `SessionFormatUnsupportedMigrationError`, but the seam translates it — with
* the "source vN artifact remains unchanged (raw log: …)" suffix — into
* `SessionFormatUnsupportedError` before the error escapes (issue #75): the
* pass must classify BOTH faces as the same refusal.
*/
function unsupportedFormatError(name: string): Error {
const error = new Error(
'subagent/descriptor 0 uses unsupported descriptor version 2'
+ '; source v0 artifact remains unchanged (raw log: C:\\Users\\u\\.dsh\\sessions\\log.jsonl.zstd)',
)
error.name = name
return error
}
@@ -435,8 +444,11 @@ describe('watchActivityBackfill', () => {
{ header: { id: 'legacy-2', cwd: '/repo/legacy-2' } },
{ header: { id: 'good', cwd: '/repo/good' } },
])
state.failReads.set('legacy-1', unsupportedMigrationError())
state.failReads.set('legacy-2', unsupportedMigrationError())
// One refusal per face of the seam's translation: the translated name a
// real dsh delivers, and the untranslated format-edge name (either may
// arrive) — both classify alike, one summary for the pair.
state.failReads.set('legacy-1', unsupportedFormatError('SessionFormatUnsupportedError'))
state.failReads.set('legacy-2', unsupportedFormatError('SessionFormatUnsupportedMigrationError'))
const route = arm(ctx, state)
const lines = captureLogs(ctx)
const dispose = watchActivityBackfill(ctx)