feat(vector-sync): discover office and Outlook documents, not only PDFs - #1266
feat(vector-sync): discover office and Outlook documents, not only PDFs#1266cbcoutinho wants to merge 7 commits into
Conversation
|
Claude finished @cbcoutinho's task in 2m 56s —— View job Review of PR #1266
This PR has already been through seven review rounds (visible in the thread above), and each substantive finding — the sequential per-type SEARCHes, the empty-allowlist inversion bug, the mislabeled size cap, the submodule pointer leak, the undocumented upgrade cost, the stale "PDF" wording — was fixed and tested. I re-read the full diff against Correctness
Test coverage
Minor / non-blocking
No blocking issues found. Nice use of |
75d2829 to
cc2adf2
Compare
…he setting Review follow-up on #1266. Tagged-folder expansion issued one `find_all_by_type` SEARCH per MIME type, sequentially awaited — six round-trips at the default type list where it used to cost one, with discovery latency scaling by the number of tagged folders. Extracted to `_walk_tagged_dir` and run through an `anyio` task group per CLAUDE.md, keeping the partial-failure tolerance. Results are collected by index rather than appended, so output order does not depend on which request finished first and discovery stays deterministic for the same corpus. Tested by counting requests actually in flight, not by inspecting state afterwards. Document `VECTOR_SYNC_INDEXABLE_MIME_TYPES` in the environment-variable reference — the PR's "narrowing it back to PDF alone is a one-line config change" is only true if an operator can find the name. Also: `_as_mime_tuple` returns from one place (python:S8495); `preflight_oversize_result`'s docstring and the `max_pdf_mb` local now say "document", matching the messages already changed in the parent commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cc2adf2 to
566e151
Compare
|
Round-1 findings addressed in
On the 🟢 pptx omission — confirmed deliberate. It is an explicit allowlist, and 3384 unit tests pass; ruff/ty green. |
…he setting Review follow-up on #1266. Tagged-folder expansion issued one `find_all_by_type` SEARCH per MIME type, sequentially awaited — six round-trips at the default type list where it used to cost one, with discovery latency scaling by the number of tagged folders. Extracted to `_walk_tagged_dir` and run through an `anyio` task group per CLAUDE.md, keeping the partial-failure tolerance. Results are collected by index rather than appended, so output order does not depend on which request finished first and discovery stays deterministic for the same corpus. Tested by counting requests actually in flight, not by inspecting state afterwards. Document `VECTOR_SYNC_INDEXABLE_MIME_TYPES` in the environment-variable reference — the PR's "narrowing it back to PDF alone is a one-line config change" is only true if an operator can find the name. Also: `_as_mime_tuple` returns from one place (python:S8495); `preflight_oversize_result`'s docstring and the `max_pdf_mb` local now say "document", matching the messages already changed in the parent commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
d6c8767 to
16c458a
Compare
|
Round-2 findings addressed in
3393 unit tests pass; ruff/ty green. Note on timing: this round and the CI behind it were both caught by yesterday's GitHub Actions outage (major outage 15:22–~00:10 UTC, runner acquisition failing). CI has been re-run since. |
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
16c458a to
e20df82
Compare
|
Round-3 finding addressed in
Rebased onto the updated lower PRs. 3399 unit tests pass; ruff/ty green. |
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
e20df82 to
4dc62bc
Compare
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4dc62bc to
df4fdb6
Compare
|
Round-4 — the submodule observation was a real defect, not a nice-to-check. Fixed in
Merging that would have pinned this repo to a commit that does not exist outside a PR branch, so Removed by amending the offending commit and replaying the tip; Also confirming the two other observations need no change:
3401 unit tests pass; ruff/ty green. |
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
df4fdb6 to
ef17b9a
Compare
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ef17b9a to
0e9916d
Compare
Tagged-file discovery hard-filtered on `application/pdf` in two places, so a tagged .docx was never enqueued no matter which processor could read it. That filter, not the absence of a converter, was what kept these formats out of the index. Which types count is now `settings.indexable_mime_types` -- PDF plus the office and Outlook formats by default. It is an explicit setting rather than "whatever the processor registry can parse" because enabling an optional processor (unstructured claims pptx, epub and images) would otherwise silently widen the corpus and its embedding bill behind the operator. Narrowing it back to PDF alone is a one-line config change. `find_files_by_tag` accepts one type or several. Nextcloud's SEARCH takes a single content type per query, so a tagged folder is expanded once per type; one type failing no longer discards the types that succeeded, which previously would have skipped the whole folder. Verify-on-read needs no change of its own -- it calls the same `_discover_tagged_files`, so the read-side filter cannot drift from the write-side one and drop indexed documents at query time. The pre-flight size cap now also lands on the new formats. That is deliberate: it guards bytes, and rendering a huge .doc holds the source and the rendered PDF at once, so it is more dangerous at size than a PDF. Its wording was updated from "PDF" to "document" to match, as the code comment there asked of whoever broadened discovery. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tagged-folder expansion issued one `find_all_by_type` SEARCH per MIME type, sequentially awaited — six round-trips at the default type list where it used to cost one. Extracted to `_walk_tagged_dir` and run through an `anyio` task group per CLAUDE.md, keeping the partial-failure tolerance. Results are collected by index rather than appended, so output order does not depend on which request finished first and discovery stays deterministic for the same corpus. Tested by counting requests actually in flight, not by inspecting state afterwards. Scope, stated precisely: the per-*type* SEARCHes within one folder now overlap; the folders themselves are still walked in sequence, so discovery remains O(n) round-trips in the number of tagged folders. Making those concurrent too would multiply the two fan-outs together against Nextcloud, which wants a bound rather than a task group, and is not attempted here. Document `VECTOR_SYNC_INDEXABLE_MIME_TYPES` in the environment-variable reference — the "narrowing it back to PDF alone is a one-line config change" claim is only true if an operator can find the name. Also spell out that `DOCUMENT_MAX_PDF_SIZE_MB` now caps every indexed document despite its PDF-specific name, which is the setting someone will hunt for when a large .docx is rejected as oversize. Also: `_as_mime_tuple` returns from one place (python:S8495); `preflight_oversize_result`'s docstring and the `max_pdf_mb` local now say "document"; and the directly-tagged-file filter — the path most office documents will actually take — is covered, where only the tagged-folder expansion and the tuple helper were before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Round-3 review follow-up on #1266. An empty `VECTOR_SYNC_INDEXABLE_MIME_TYPES` produced two contradictory behaviours at once. `find_files_by_tag` reads an empty tuple as "no filter", so directly-tagged files of *every* content type were indexed — images, video, anything carrying the tag — while `if mime_types and tagged_dirs` skipped folder expansion entirely, so folder-tagged files were indexed not at all. That inverts what the allowlist is for: it exists so a config change cannot silently widen the corpus, and its emptiest setting widened it the most. It is also reachable by an ordinary reading of this config — `vector_sync_keyword_tag` documents "set empty to disable the second tag entirely", so an operator reaching for the same idiom here to pause indexing would instead open discovery to every type in their Nextcloud. `_discover_tagged_files` now returns nothing and warns, naming the variable and how to re-enable, so an operator who empties it finds an explanation rather than silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0e9916d to
13a5bdc
Compare
…lter
CI caught what `-m unit` could not. `_discover_tagged_files` reads
`settings.indexable_mime_types`, and `tests/integration/test_index_mode_discovery.py`
builds its settings as a `SimpleNamespace` with only the two tag fields, so the
attribute was missing entirely:
AttributeError: 'types.SimpleNamespace' object has no attribute
'indexable_mime_types'
I updated the unit test's MagicMock when the setting went in and missed the
integration stub next to it, because I only ran the unit tier locally. The stub
now carries the real default -- not a placeholder -- since an empty value means
"index nothing" and would leave the fixture's tagged PDFs undiscovered, turning
a missing attribute into a silently empty result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ent wording Round-5 review follow-up on #1266. The default indexable set widens from PDF-only to PDF plus the Word, Excel and Outlook formats, so an instance upgrading on default config starts indexing -- and paying to embed -- file types it previously ignored. Nothing is removed and no API changes, so this is not a `BREAKING CHANGE:` per the repo's rule, but it does change spend on upgrade without any code-level signal, which belongs in the release notes rather than only in a PR thread. Narrowing back is one line: `VECTOR_SYNC_INDEXABLE_MIME_TYPES=application/pdf`. Also finishes the wording sweep the reviewer spotted as half-done: several comments in `vector/scanner.py` and `_reconcile_tag_event`'s docstring still said "tagged PDFs" and "PDF descendants" where discovery now covers every type in the allowlist -- the same staleness already fixed in `preflight_oversize_result`. Comment-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Round-5 — both nits addressed in
3406 unit tests pass; ruff/ty green. On CI for the record: the earlier red on this PR was a genuine bug of mine — |
…d it Fair catch on the previous commit: it was titled "note the upgrade cost" and then did not add one. The note lived in the commit message and the PR body, neither of which becomes a released artifact -- commitizen's changelog entry is the subject line, so the callout would have existed only in git metadata and a web thread. Adds a "Which file types get indexed" section to docs/configuration.md next to the tag configuration, stating plainly that upgrading from a PDF-only release makes already-tagged Word/Excel/Outlook files -- including everything under a tagged folder -- eligible on the next scan, with the one-line opt-out and the warning that an empty value means "index nothing" rather than "no filter". Also fixes the line the wording sweep missed: scanner.py:1044 still said a folder tag "applies to every PDF beneath it" while the two lines around it were edited in the same hunk. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Round-6 — both findings addressed in
CI for the record: the previous run on 3406 unit tests pass; ruff/ty green. |
Round-7 review follow-up on #1266. The reviewer noted the integration fixture only ever creates PDFs, so the multi-type fan-out and the office-file filter were exercised against a mocked webdav client alone. That gap hid an unchecked premise. Matching is `content_type.startswith(indexable_mime_types)`, so every entry has to be the exact string *Nextcloud* reports for that extension -- and those strings were chosen from the specification, never compared against the server that emits them. Had Nextcloud labelled a .docx differently, discovery would have returned nothing and every unit test would still have passed, because they assert the matching logic against the same strings the code declares. Two cases, run against the dev stack before committing: the reported content types for an uploaded .pdf/.docx/.xlsx are each matched by the configured default, and a tagged *folder* containing all three plus a .png expands to exactly the three indexable files. The .png is there so a green result cannot come from the filter having been dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Round-7 addressed in
Left as-is, both explicitly non-blocking:
On CI: the two red lanes on the previous commit were both |
|
|
Round-8: no blocking issues, and I am treating this as the end of the loop for this PR. CI is complete and green — 28/28 checks, Your three notes, none actioned, with reasons:
Summary of what these eight rounds actually caught in my code, since it is worth recording: a layering violation ( |



Tagged-file discovery hard-filtered on
application/pdfin two places, so atagged .docx was never enqueued no matter which processor could read it. That
filter, not the absence of a converter, was what kept these formats out of the
index.
Which types count is now
settings.indexable_mime_types-- PDF plus theoffice and Outlook formats by default. It is an explicit setting rather than
"whatever the processor registry can parse" because enabling an optional
processor (unstructured claims pptx, epub and images) would otherwise silently
widen the corpus and its embedding bill behind the operator. Narrowing it back
to PDF alone is a one-line config change.
find_files_by_tagaccepts one type or several. Nextcloud's SEARCH takes asingle content type per query, so a tagged folder is expanded once per type;
one type failing no longer discards the types that succeeded, which previously
would have skipped the whole folder.
Verify-on-read needs no change of its own -- it calls the same
_discover_tagged_files, so the read-side filter cannot drift from thewrite-side one and drop indexed documents at query time.
The pre-flight size cap now also lands on the new formats. That is deliberate:
it guards bytes, and rendering a huge .doc holds the source and the rendered
PDF at once, so it is more dangerous at size than a PDF. Its wording was
updated from "PDF" to "document" to match, as the code comment there asked of
whoever broadened discovery.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Stack created with GitHub Stacks CLI • Give Feedback 💬
Upgrade impact
This widens the default indexable set from PDF-only to PDF plus the Word,
Excel and Outlook formats. An instance upgrading on default config will start
indexing — and paying to embed — file types it previously ignored, wherever
those carry the
vector-index/keyword-indextag.Nothing is removed and no API changes, so this is not a
BREAKING CHANGE:under the repo's versioning rule. Narrowing back is one line:
This PR was generated with the help of AI, and reviewed by a Human