Skip to content

Commit be31bd7

Browse files
committed
fix(knowledge): re-index a file when an ancestor folder is renamed
Cursor Bugbot, medium severity. The hash already carried folderId so a move re-indexes, but the resolved folderPath is what gets stored as the tag, and renaming an ancestor folder rewrites that path while writing only the folder table. contentUpdatedAt, originalName and folderId all stay put, so classifyExternalDoc saw no change and the document kept the old folder tag indefinitely. folderPath now participates in the hash. Both listDocuments and getDocument resolve it from the same pathById map, so the listing and hydration phases still produce byte-identical hashes. Mutation-checked: dropping it from the hash fails the new test.
1 parent 176c84a commit be31bd7

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

apps/sim/connectors/sim-files/sim-files.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ describe('fileRowToStub', () => {
127127
expect(after).toBe(before)
128128
})
129129

130+
/**
131+
* Renaming an ancestor folder rewrites the stored path tag but writes only the
132+
* `folder` table — `contentUpdatedAt`, `originalName` and `folderId` all stay put,
133+
* so without the path in the hash the document keeps the old folder tag forever.
134+
*/
135+
it('changes the hash when an ancestor folder rename moves the path', () => {
136+
expect(fileRowToStub(BASE_ROW, 'ws-1', 'Documentation/Specs').contentHash).not.toBe(
137+
fileRowToStub(BASE_ROW, 'ws-1', 'Docs/Specs').contentHash
138+
)
139+
})
140+
130141
/** listDocuments and getDocument must produce byte-identical hashes for one row. */
131142
it('is deterministic for the same row', () => {
132143
expect(fileRowToStub(BASE_ROW, 'ws-1', 'Docs/Specs').contentHash).toBe(

apps/sim/connectors/sim-files/sim-files.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ export function buildFileListingFilters(args: {
186186
* `originalName` and `folderId` participate because a rename or a move alters what
187187
* we index (title, folder tag) without touching `contentUpdatedAt`, which advances
188188
* only on content writes.
189+
*
190+
* `folderPath` participates for the same reason one level up: renaming an ANCESTOR
191+
* folder rewrites the path we store as a tag but writes only the `folder` table, so
192+
* every other hashed field stays put and the document would keep the old path
193+
* forever. Both `listDocuments` and `getDocument` resolve it from the same
194+
* `pathById` map, so the two phases still agree.
189195
*/
190196
export function fileRowToStub(
191197
row: FileRow,
@@ -201,7 +207,7 @@ export function fileRowToStub(
201207
sourceUrl: `${getBaseUrl()}/workspace/${workspaceId}/files${
202208
row.folderId ? `?folderId=${encodeURIComponent(row.folderId)}` : ''
203209
}`,
204-
contentHash: `simfile:${row.id}:${row.contentUpdatedAt.toISOString()}:${row.originalName}:${row.folderId ?? ''}`,
210+
contentHash: `simfile:${row.id}:${row.contentUpdatedAt.toISOString()}:${row.originalName}:${row.folderId ?? ''}:${folderPath ?? ''}`,
205211
metadata: {
206212
folderPath: folderPath ?? '',
207213
contentType: row.contentType,

0 commit comments

Comments
 (0)