fix: address ds-review-bot v8 findings
- llm-deepseek: the uncatalogued resolveModel fallback declares text-only modalities — the wire route is text-only regardless of catalog membership, so "unknown" must not let the host persist-then-fail images. - session.selectModel also consults the pending-inbox mirror: a queued image prompt enters the log only when claimed, after a switch would land. - attachment store: ensureDurableDirectory syncs every ancestor entry up to a caller-vouched boundary regardless of what mkdir reports — a raced "already existed" is not "already durable". - One image walker (imageBlockIn/imageInEvent) now serves both attachment authorization and the selection gate; referencedImage therefore also authorizes references inside wrapped message content. - InputHub: the scope disposer resolves the conversation service optionally (teardown/HMR must reach quiescence), and a send failing after its scope died releases the in-flight drafts instead of restoring them onto a disposed shell. - http-bridge destroys declared-oversize requests with connection: close instead of draining a body the client can trickle indefinitely. - LlmService validates AND detaches modality arrays identically on the advisory and exact routes; READMEs record the fourth INVALID_MODEL_INFO rejection reason. - CLI provider docs (JSDoc, README pair, Agent Note pair) describe the reuse behavior; llm-route.spec now parses the SHIPPED cordis.yml through the production extraction, pinning the row coupling. - image-display lane pins gallery/rail shape in inline snapshots and the object-URL scheme this environment must take; stale host.schema comment dropped.
This commit is contained in:
@@ -79,21 +79,28 @@ async function syncDirectory(path: string): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create one private directory tree and persist every newly published ancestor.
|
||||
* Create one private directory tree and persist every ancestor entry up to a
|
||||
* caller-vouched durable boundary. The walk deliberately ignores what mkdir
|
||||
* reports as newly created: a concurrent first save can create a level this
|
||||
* process then merely observes, so "already existed" is not "already durable"
|
||||
* — the entry may still be unsynced in the creator, and a crash would drop a
|
||||
* directory the session checkpoint already references. Re-syncing a durable
|
||||
* entry is harmless; skipping an unsynced one is not.
|
||||
* @param path - absolute directory to create.
|
||||
* @param boundary - absolute ancestor the caller vouches is already durable.
|
||||
*/
|
||||
async function ensureDurableDirectory(path: string): Promise<void> {
|
||||
async function ensureDurableDirectory(path: string, boundary: string): Promise<void> {
|
||||
const target = resolve(path)
|
||||
const firstCreated = await mkdir(target, { recursive: true, mode: 0o700 })
|
||||
const stop = resolve(boundary)
|
||||
await mkdir(target, { recursive: true, mode: 0o700 })
|
||||
await chmod(target, 0o700)
|
||||
if (firstCreated === undefined) return
|
||||
|
||||
const highestCreated = resolve(firstCreated)
|
||||
let created = target
|
||||
while (true) {
|
||||
await syncDirectory(dirname(created))
|
||||
if (created === highestCreated) return
|
||||
created = dirname(created)
|
||||
let level = target
|
||||
while (level !== stop) {
|
||||
const parent = dirname(level)
|
||||
await syncDirectory(parent)
|
||||
/* v8 ignore next -- filesystem-root guard: callers pass a boundary that is an ancestor of path, so the walk reaches it first. */
|
||||
if (parent === level) return
|
||||
level = parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +117,12 @@ export async function saveImageFile(root: string, input: SaveImageAttachment, li
|
||||
const sha256 = digest(input.data)
|
||||
const bucket = join(root, 'objects', sha256.slice(0, 2))
|
||||
const staging = join(root, 'tmp')
|
||||
await ensureDurableDirectory(bucket)
|
||||
await ensureDurableDirectory(staging)
|
||||
// The durable boundary is the root's grandparent (DSH_HOME for the
|
||||
// documented `DSH_HOME/attachments/v1` layout): `attachments`/`v1` may be
|
||||
// first-created by a concurrent save, so their entries sync on every path.
|
||||
const boundary = dirname(dirname(resolve(root)))
|
||||
await ensureDurableDirectory(bucket, boundary)
|
||||
await ensureDurableDirectory(staging, boundary)
|
||||
const temporary = join(staging, randomUUID())
|
||||
const target = objectPath(root, sha256)
|
||||
let handle
|
||||
|
||||
@@ -47,7 +47,7 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
describe('local attachment store', () => {
|
||||
it.skipIf(process.platform === 'win32')('syncs every newly created object ancestor before returning', async () => {
|
||||
it.skipIf(process.platform === 'win32')('syncs every object ancestor up to the durable boundary before returning', async () => {
|
||||
const storageRoot = await root()
|
||||
const base = join(storageRoot, '..', '..')
|
||||
const sha256 = createHash('sha256').update(PNG).digest('hex')
|
||||
@@ -57,12 +57,20 @@ describe('local attachment store', () => {
|
||||
|
||||
await saveImageFile(storageRoot, { data: PNG, mediaType: 'image/png' }, LIMITS)
|
||||
|
||||
// Every level between each created directory and the vouched boundary
|
||||
// syncs unconditionally — "already existed" is not "already durable"
|
||||
// when a concurrent first save may have created but not yet synced it.
|
||||
expect(fsControl.syncedDirectories).toEqual([
|
||||
// bucket chain: every parent entry between the bucket and the boundary.
|
||||
objects,
|
||||
storageRoot,
|
||||
join(storageRoot, '..'),
|
||||
base,
|
||||
// staging chain re-walks the shared ancestors after creating tmp.
|
||||
storageRoot,
|
||||
join(storageRoot, '..'),
|
||||
base,
|
||||
// publication: the settled object's bucket and its parent for the rename.
|
||||
bucket,
|
||||
objects,
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user