Environment
- OpenViking v0.4.5 (docker); code path unchanged in current
main (2026-07-13)
Bug
After any successful DELETE /api/v1/fs (regardless of the API's recursive query param), service/fs_service.py::rm enqueues a semantic refresh anchored at the deleted item's parent directory (_semantic_refresh_parent_uri). The message construction in _enqueue_delete_refresh does not pass recursive:
msg = SemanticMsg(
uri=root_uri, # parent of the deleted item — reasonable anchor
context_type=context_type,
... # recursive not passed!
changes={"deleted": [deleted_uri]}, # incremental hint is present but unused
)
and SemanticMsg.__init__ defaults recursive: bool = True. The semantic processor therefore performs a full post-order re-walk of the parent's entire subtree, regenerating every directory's L0/L1 via the VLM, even though nothing under the siblings changed.
The blast radius scales with where the deleted item sits:
- deleting a file in a leaf directory → only that directory (accidentally cheap, which is why this goes unnoticed)
- deleting a subdirectory of a large directory → all sibling subtrees re-walked
- deleting a top-level directory → parent is the namespace root → the whole tree
Production impact (measured)
Deleting one small test directory directly under viking://resources on a tree of ~138k files / ~11k directories triggered a silent full re-summarization: ~3.4 VLM calls/sec sustained, 65M+ prompt tokens and ~45k calls over 3.7 hours before we aborted it (projected 10+ hours total). All regenerated content was equivalent to what was already on disk. The job emits no per-node logs, exposes no progress, and cannot be cancelled through any API — the only way to stop it is to stop the server and delete the queue row while stopped.
Reproduction
- Create
viking://resources/tmp-dir/a.md (any small resource).
DELETE /api/v1/fs?uri=viking://resources/tmp-dir&recursive=true.
- Log shows
Processing semantic generation for: viking://resources (recursive=True) followed by a full-tree walk (VLM traffic proportional to the whole namespace, not to the deletion).
Expected behavior / suggested fix
_enqueue_delete_refresh should pass recursive=False. The deleted path is already carried in changes={"deleted": [...]}; a non-recursive incremental refresh of the parent correctly drops the deleted entry from the parent's L0/L1, and the processor's existing parent-refresh cascade propagates the update to the namespace root at a cost proportional to path depth (a handful of VLM calls) instead of subtree size.
We have been running this one-line change in production: deleting a top-level directory now produces exactly one viking://resources (recursive=False) incremental refresh (~2 VLM calls), with sibling subtrees untouched.
Related
Environment
main(2026-07-13)Bug
After any successful
DELETE /api/v1/fs(regardless of the API'srecursivequery param),service/fs_service.py::rmenqueues a semantic refresh anchored at the deleted item's parent directory (_semantic_refresh_parent_uri). The message construction in_enqueue_delete_refreshdoes not passrecursive:and
SemanticMsg.__init__defaultsrecursive: bool = True. The semantic processor therefore performs a full post-order re-walk of the parent's entire subtree, regenerating every directory's L0/L1 via the VLM, even though nothing under the siblings changed.The blast radius scales with where the deleted item sits:
Production impact (measured)
Deleting one small test directory directly under
viking://resourceson a tree of ~138k files / ~11k directories triggered a silent full re-summarization: ~3.4 VLM calls/sec sustained, 65M+ prompt tokens and ~45k calls over 3.7 hours before we aborted it (projected 10+ hours total). All regenerated content was equivalent to what was already on disk. The job emits no per-node logs, exposes no progress, and cannot be cancelled through any API — the only way to stop it is to stop the server and delete the queue row while stopped.Reproduction
viking://resources/tmp-dir/a.md(any small resource).DELETE /api/v1/fs?uri=viking://resources/tmp-dir&recursive=true.Processing semantic generation for: viking://resources (recursive=True)followed by a full-tree walk (VLM traffic proportional to the whole namespace, not to the deletion).Expected behavior / suggested fix
_enqueue_delete_refreshshould passrecursive=False. The deleted path is already carried inchanges={"deleted": [...]}; a non-recursive incremental refresh of the parent correctly drops the deleted entry from the parent's L0/L1, and the processor's existing parent-refresh cascade propagates the update to the namespace root at a cost proportional to path depth (a handful of VLM calls) instead of subtree size.We have been running this one-line change in production: deleting a top-level directory now produces exactly one
viking://resources (recursive=False)incremental refresh (~2 VLM calls), with sibling subtrees untouched.Related
processingare orphaned forever after a restart (no startup reclaim / visibility timeout), and recursive whole-namespace jobs run silently with no progress reporting or cancellation API.