Triage real-server API adversarial findings from #642 final round #664

Open
opened 2026-10-02 04:59:22 +00:00 by kayg · 9 comments
Owner

Real-server API-only adversarial round on 2026-10-02, HEAD bf3ad5f29d1108c34e87c0e9d20b1dbe67ffc5c3.

Command: ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 ADVERSARIAL_SERVER_BIN=/mnt/hdd/targets/jobs/blaze-settings/debug/calternal-server bash tests/adversarial/run.sh.

The server remained alive at the end. The round reported these non-SLOW observations:

  • A valid uploaded PDF was not returned by the search query for its embedded sentinel after the probe's 80 polls at 250 ms intervals.
  • Cross-source tag rename returned 409. After restoring the deliberately corrupted folder metadata, tag reconcile returned 409; after restoring the deliberately corrupted XMP, reconcile also returned 409. After deleting the rebuildable tag index rows, reconcile returned 409 and the tag page was empty.
  • A valid CalDAV PUT updating an existing Journal resource returned 204, while the probe expected 201. The probe then retained the old ETag, so its later cross-date PUT and DELETE returned 412. Those latter two results are cascade effects of the stale probe ETag, not independent server findings. The status expectation for the successful update needs owner review; the existing expectation was not changed.
  • The API-only selector invoked mcp_probe.py, but setup.mjs did not create mcp-passwords.json unless ADVERSARIAL_MCP_ONLY=1 or XUSER_MATRIX_WITH_MCP=1. MCP coverage therefore stopped at a fixture FileNotFoundError; the MCP API probe did not run.

Most other findings were explicitly marked SLOW while the host load average was about 19. The round did not report a server crash or a 5xx response. These observations need triage; no existing test expectation was changed.

Real-server API-only adversarial round on 2026-10-02, HEAD `bf3ad5f29d1108c34e87c0e9d20b1dbe67ffc5c3`. Command: `ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 ADVERSARIAL_SERVER_BIN=/mnt/hdd/targets/jobs/blaze-settings/debug/calternal-server bash tests/adversarial/run.sh`. The server remained alive at the end. The round reported these non-SLOW observations: - A valid uploaded PDF was not returned by the search query for its embedded sentinel after the probe's 80 polls at 250 ms intervals. - Cross-source tag rename returned 409. After restoring the deliberately corrupted folder metadata, tag reconcile returned 409; after restoring the deliberately corrupted XMP, reconcile also returned 409. After deleting the rebuildable tag index rows, reconcile returned 409 and the tag page was empty. - A valid CalDAV PUT updating an existing Journal resource returned 204, while the probe expected 201. The probe then retained the old ETag, so its later cross-date PUT and DELETE returned 412. Those latter two results are cascade effects of the stale probe ETag, not independent server findings. The status expectation for the successful update needs owner review; the existing expectation was not changed. - The API-only selector invoked `mcp_probe.py`, but `setup.mjs` did not create `mcp-passwords.json` unless `ADVERSARIAL_MCP_ONLY=1` or `XUSER_MATRIX_WITH_MCP=1`. MCP coverage therefore stopped at a fixture `FileNotFoundError`; the MCP API probe did not run. Most other findings were explicitly marked SLOW while the host load average was about 19. The round did not report a server crash or a 5xx response. These observations need triage; no existing test expectation was changed.
Author
Owner

Starting #664 on branch job/advfind-664, based on origin/dev at c4a61e8cf0. I am tracing the tag reconcile/rename and PDF indexing paths, and will update the API-only probes for the confirmed DAV and Appearance contracts from #708.

Starting #664 on branch job/advfind-664, based on origin/dev at c4a61e8cf090170f35b1bed3350d9de20c83ecd5. I am tracing the tag reconcile/rename and PDF indexing paths, and will update the API-only probes for the confirmed DAV and Appearance contracts from #708.
Author
Owner

Finding: the API-only runner invokes sidecar_probe.py before attack.py. The Sidecar fixture leaves a .jpg.xmp sidecar containing arbitrary non-XMP bytes beside a JPEG. Tag reconciliation reads that source through the strict edit reader, which returns InvalidXmp; the Tag API maps that error to 409. The cross-source rename starts with reconciliation, so this explains the rename conflict without a stale ETag or lock race. I am adding a reconcile-only recovery path that warns and skips the damaged source, while keeping strict reads for edits.

Finding from #708: the DAV alarm PUT updates the existing Journal resource with If-Match. The server's 204 is valid. The old probe expected creation status 201 and therefore reused the old ETag for the following move/delete. The probe now accepts 200/204, requires and adopts a fresh response ETag, and has 17 passing DAV probe unit tests. No server change was needed.

Finding: the API-only runner invokes `sidecar_probe.py` before `attack.py`. The Sidecar fixture leaves a `.jpg.xmp` sidecar containing arbitrary non-XMP bytes beside a JPEG. Tag reconciliation reads that source through the strict edit reader, which returns `InvalidXmp`; the Tag API maps that error to 409. The cross-source rename starts with reconciliation, so this explains the rename conflict without a stale ETag or lock race. I am adding a reconcile-only recovery path that warns and skips the damaged source, while keeping strict reads for edits. Finding from #708: the DAV alarm PUT updates the existing Journal resource with `If-Match`. The server's 204 is valid. The old probe expected creation status 201 and therefore reused the old ETag for the following move/delete. The probe now accepts 200/204, requires and adopts a fresh response ETag, and has 17 passing DAV probe unit tests. No server change was needed.
Author
Owner

Implemented on branch job/advfind-664. Head: 884cc8ba6e1d275d14844187ed16e65f4b20a354.

Built

  • Tag reconciliation now skips invalid folder metadata and XMP Sidecars with a content-free WARN. It keeps damaged source bytes intact and rebuilds Tag rows from the remaining valid sources.
  • Tag rename now rewrites only source paths that still carry the old Tag in the rebuilt Index. This lets a cross-source rename proceed when an unrelated malformed Sidecar exists.
  • The API probe now expects successful reconciliation for damaged rebuildable sources, checks the warning and byte preservation, and checks that valid Tag rows remain. The existing deleted-row rebuild check remains.
  • The DAV update probe accepts 200/204 for an existing resource, requires a new ETag, and uses that ETag for follow-up requests. The server was not changed.
  • The existing Tag rename benchmark profile now records its two reconciliation passes per rename; its baseline includes the same workload field.

Files

  • crates/calternal-tags/src/source.rs
  • crates/calternal-tags/src/index.rs
  • crates/calternal-tags/src/rename.rs
  • crates/calternal-tags/src/lib.rs
  • tests/adversarial/attack.py
  • tests/adversarial/dav_probe_contracts.py
  • tests/adversarial/test_dav_probe.py
  • bench/tag-rename-525.sh
  • docs/perf/baseline.json

Commits: ff592fc23, 9bc77938b, 7c00ac62b, 884cc8ba6.

Gates run

cargo fmt --check: exit 0, no output.

cargo clippy -p calternal-tags --all-targets -- -D warnings output:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.01s

cargo test -p calternal-tags output:

    Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 04s
     Running unittests src/lib.rs (/mnt/hdd/targets/jobs/advfind-664/debug/deps/calternal_tags-2466b143a19391bc)

running 13 tests
test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok
test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok
test source::tests::valid_xmp_packet_is_still_readable ... ok
test tests::tags_are_bounded_and_nested_without_empty_segments ... ok
test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok
test tests::markdown_index_updates_use_the_callers_transaction ... ok
test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok
test tests::nested_queries_stop_at_tag_segment_boundaries ... ok
test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok
test tests::hidden_sources_do_not_create_tag_suggestions ... ok
test tests::reconcile_skips_corrupt_folder_metadata_and_keeps_it_untouched ... ok
test tests::reconcile_skips_corrupt_xmp_sidecar_and_keeps_it_untouched ... ok
test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 42.99s

   Doc-tests calternal_tags
 nrunning 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

python3 -m unittest test_dav_probe test_appearance_probe: 21 tests passed. python3 -m py_compile tests/adversarial/attack.py tests/adversarial/dav_probe_contracts.py: exit 0, no output. The later DAV doc-comment-only update was followed by another 17-test test_dav_probe run, which passed.

Gaps

The full real-server API-only adversarial round was not run. The calternal-server clippy/test gates were not run. The uploaded-PDF search result and queue timing therefore remain unverified on this head. The extended benchmark profile was not run, so there are no new p50/p95, CPU, or RSS measurements to compare with docs/perf/baseline.json.

The Appearance system observation from #708 was not re-run against a fresh server. The current source and dedicated fresh-User probe define the default as no saved Auto setting (null); this job keeps that expectation and changes no product behavior.

Decisions

  • Rebuild treats invalid folder metadata or an invalid XMP Sidecar as absent for that source, logs WARN without source content, and never overwrites the damaged bytes.
  • Rename uses paths from the reconciled Index as its rewrite set; unrelated sources are not opened for editing.
  • DAV update status 200 or 204 is accepted, and follow-up requests use the response ETag.
  • No UI changed, so visual screenshots and UX-gap review do not apply.
Implemented on branch `job/advfind-664`. Head: `884cc8ba6e1d275d14844187ed16e65f4b20a354`. ## Built - Tag reconciliation now skips invalid folder metadata and XMP Sidecars with a content-free WARN. It keeps damaged source bytes intact and rebuilds Tag rows from the remaining valid sources. - Tag rename now rewrites only source paths that still carry the old Tag in the rebuilt Index. This lets a cross-source rename proceed when an unrelated malformed Sidecar exists. - The API probe now expects successful reconciliation for damaged rebuildable sources, checks the warning and byte preservation, and checks that valid Tag rows remain. The existing deleted-row rebuild check remains. - The DAV update probe accepts 200/204 for an existing resource, requires a new ETag, and uses that ETag for follow-up requests. The server was not changed. - The existing Tag rename benchmark profile now records its two reconciliation passes per rename; its baseline includes the same workload field. ## Files - `crates/calternal-tags/src/source.rs` - `crates/calternal-tags/src/index.rs` - `crates/calternal-tags/src/rename.rs` - `crates/calternal-tags/src/lib.rs` - `tests/adversarial/attack.py` - `tests/adversarial/dav_probe_contracts.py` - `tests/adversarial/test_dav_probe.py` - `bench/tag-rename-525.sh` - `docs/perf/baseline.json` Commits: `ff592fc23`, `9bc77938b`, `7c00ac62b`, `884cc8ba6`. ## Gates run `cargo fmt --check`: exit 0, no output. `cargo clippy -p calternal-tags --all-targets -- -D warnings` output: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.01s ``` `cargo test -p calternal-tags` output: ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 04s Running unittests src/lib.rs (/mnt/hdd/targets/jobs/advfind-664/debug/deps/calternal_tags-2466b143a19391bc) running 13 tests test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok test source::tests::valid_xmp_packet_is_still_readable ... ok test tests::tags_are_bounded_and_nested_without_empty_segments ... ok test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok test tests::markdown_index_updates_use_the_callers_transaction ... ok test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok test tests::nested_queries_stop_at_tag_segment_boundaries ... ok test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok test tests::hidden_sources_do_not_create_tag_suggestions ... ok test tests::reconcile_skips_corrupt_folder_metadata_and_keeps_it_untouched ... ok test tests::reconcile_skips_corrupt_xmp_sidecar_and_keeps_it_untouched ... ok test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... ok test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 42.99s Doc-tests calternal_tags nrunning 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `python3 -m unittest test_dav_probe test_appearance_probe`: 21 tests passed. `python3 -m py_compile tests/adversarial/attack.py tests/adversarial/dav_probe_contracts.py`: exit 0, no output. The later DAV doc-comment-only update was followed by another 17-test `test_dav_probe` run, which passed. ## Gaps The full real-server API-only adversarial round was not run. The `calternal-server` clippy/test gates were not run. The uploaded-PDF search result and queue timing therefore remain unverified on this head. The extended benchmark profile was not run, so there are no new p50/p95, CPU, or RSS measurements to compare with `docs/perf/baseline.json`. The Appearance `system` observation from #708 was not re-run against a fresh server. The current source and dedicated fresh-User probe define the default as no saved Auto setting (`null`); this job keeps that expectation and changes no product behavior. ## Decisions - Rebuild treats invalid folder metadata or an invalid XMP Sidecar as absent for that source, logs WARN without source content, and never overwrites the damaged bytes. - Rename uses paths from the reconciled Index as its rewrite set; unrelated sources are not opened for editing. - DAV update status 200 or 204 is accepted, and follow-up requests use the response ETag. - No UI changed, so visual screenshots and UX-gap review do not apply.
Author
Owner

Correction to the cargo test -p calternal-tags transcript in my previous comment: the doc-test portion is exactly:

   Doc-tests calternal_tags

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Correction to the `cargo test -p calternal-tags` transcript in my previous comment: the doc-test portion is exactly: ```text Doc-tests calternal_tags running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```
Author
Owner

PDF trace for the remaining search finding: production wires SubprocessPdfTextExtractor::current_executable(), and indexed_file extracts text for .pdf files within MAX_PDF_BYTES. The recursive Home watcher queues an Upsert and signals a rescan if its bounded queue is full. I did not run the final API-only real-server round, so I cannot confirm whether the reported 20-second miss remains or distinguish extractor failure from queue delay; no PDF code change is claimed.

PDF trace for the remaining search finding: production wires `SubprocessPdfTextExtractor::current_executable()`, and `indexed_file` extracts text for `.pdf` files within `MAX_PDF_BYTES`. The recursive Home watcher queues an Upsert and signals a rescan if its bounded queue is full. I did not run the final API-only real-server round, so I cannot confirm whether the reported 20-second miss remains or distinguish extractor failure from queue delay; no PDF code change is claimed.
Author
Owner

Independent read-only review complete.

Reviewed head: 884cc8ba6e1d275d14844187ed16e65f4b20a354 (job/advfind-664).
Review head: 711175909f4a756a20e39089f8c9e3902fd81d18 (job/rev2-advfind-664).
Review commits: bdce58b66, 711175909.

P1: #938 — hash-based recovery can route a skipped damaged Sidecar into the weaker XMP writer and replace its bytes. Other damaged sources can still stop reconciliation through strict writer errors. This is a static code finding, not a runtime reproduction. It needs a focused regression and fix before merge.

P2: #939 — the new warnings include private User folder/file names. The warning probe checks the body only and can accept warnings from earlier operations.

P2, existing scope gap: #654 — the API-only MCP setup fix is absent from the reviewed tree. I added evidence to #654 rather than creating a duplicate. Include its fix in the merge round. No P3 defect was identified.

Files committed: audit-findings.md, review-advfind-664.md. No product code changed. No builds, tests, servers, browsers, merges, pushes, or deployments ran. git diff --check HEAD~2 HEAD exited 0; output was empty. There is no build/test gate output to quote under the LIGHT rule. The worktree is clean.

Known gaps: runtime confirmation for #938, private-name log regression for #939, and the PDF search miss remain for the merge round. No independent defect was found in the new Tag rename path selection. No product design decision was made. UX gaps were not assessed because this was a source review.

Full report follows, including the exact merge-round commands.

Independent review of #664

Target: job/advfind-664 at
884cc8ba6e1d275d14844187ed16e65f4b20a354.
Reviewer branch: job/rev2-advfind-664.
Reviewer base: 440e19dce23040ac8ebaae88f0469b6535b1afcb.

Read CLAUDE.md, CONTEXT.md, DESIGN §2, §4, §31, and §32, the #664 body and
comments, and the complete nine-file branch diff. The review used source
reading only. No product files changed.

Findings

Priority Finding Issue
P1 Hash recovery can write a damaged Sidecar after the new reader skips it. Some malformed XMP can pass the writer's weaker parser and be replaced. Other damaged sources can stop the rebuild through strict writer errors. #938
P2 New warnings include private User folder and file names. The probe checks source bodies but misses those name fields. #939
P2 API-only MCP setup still omits the password fixture in the reviewed tree. This is an existing gap with a separate fix branch. #654

The P1 needs a fix before merge because damaged-byte loss blocks a merge.
No P3 defect was identified. Evidence, expected behavior, rules, and focused
test ideas are in audit-findings.md. All runtime outcomes
are static inferences; no regression test ran in this LIGHT review.

Checks from source reading

  • The direct skip branches do not replace damaged bytes. The gap is the
    later hash-recovery writer call, which the added fixtures do not cover.
  • Tag rename obtains its source paths after reconciliation and uses that
    set in normal execution and restart recovery. No separate defect was found
    in the new path selection. Strict edit readers still reject damaged XMP.
  • The DAV alarm update adopts the new response ETag for a successful 200 or
    204. This addresses the documented stale-token cascade when the response
    includes a fresh ETag.
  • The warning probe scans the whole log. An earlier warning with the same
    message can satisfy its check. The #939 regression should inspect the new
    events from each operation.
  • The PDF search finding remains unverified. This diff makes no PDF change.
  • The benchmark adds a workload field to an existing baseline. It supplies
    no new measurements. Performance measurement is outside this LIGHT job.

Work and files

Created this review and the append-only findings record. Filed #938 and #939
after issue searches. Added current evidence to #654. Posted one summary on
#664. No fixes, dependency changes, UI changes, pushes, deployments, or merges.

Files: audit-findings.md and review-advfind-664.md.

Verification

Build and test gates: not run, as required by the LIGHT job.
Document check: git diff --check exited 0 with no output.
Its output is empty; there is no build or test transcript from this review.

No build output was created. No cargo cleanup or web-output deletion was
needed. The target branch was not modified or merged.

For the merge round

Run the focused #938 damaged-source regression, then:

cargo fmt --check
cargo clippy -p calternal-tags --all-targets -- -D warnings
cargo test -p calternal-tags
cd tests/adversarial
python3 -m unittest test_dav_probe test_appearance_probe

These checks must prove damaged-byte preservation on hash recovery and the
DAV response-token contract. Use the prescribed small-build environment.
For one real-server API-only round, from the repository root:

ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 bash tests/adversarial/run.sh

The round must include the #654 setup fix, reach the MCP probe, retain damaged
bytes, exclude private names from logs, complete cross-source Tag rename and
rebuild, and check the remaining PDF search finding. No new runtime evidence
is claimed by this review.

Decisions and UX gaps

No product design decision was made. The LIGHT instructions take precedence
over the shared build, test, merge, and cleanup workflow. The target HEAD is
fixed above so later branch changes need a separate review.

UX gaps closed: none; this job changes review documents only.
UX gaps left: not assessed; no UI change or browser work was in scope.

Independent read-only review complete. Reviewed head: `884cc8ba6e1d275d14844187ed16e65f4b20a354` (`job/advfind-664`). Review head: `711175909f4a756a20e39089f8c9e3902fd81d18` (`job/rev2-advfind-664`). Review commits: `bdce58b66`, `711175909`. P1: #938 — hash-based recovery can route a skipped damaged Sidecar into the weaker XMP writer and replace its bytes. Other damaged sources can still stop reconciliation through strict writer errors. This is a static code finding, not a runtime reproduction. It needs a focused regression and fix before merge. P2: #939 — the new warnings include private User folder/file names. The warning probe checks the body only and can accept warnings from earlier operations. P2, existing scope gap: #654 — the API-only MCP setup fix is absent from the reviewed tree. I added evidence to #654 rather than creating a duplicate. Include its fix in the merge round. No P3 defect was identified. Files committed: `audit-findings.md`, `review-advfind-664.md`. No product code changed. No builds, tests, servers, browsers, merges, pushes, or deployments ran. `git diff --check HEAD~2 HEAD` exited 0; output was empty. There is no build/test gate output to quote under the LIGHT rule. The worktree is clean. Known gaps: runtime confirmation for #938, private-name log regression for #939, and the PDF search miss remain for the merge round. No independent defect was found in the new Tag rename path selection. No product design decision was made. UX gaps were not assessed because this was a source review. Full report follows, including the exact merge-round commands. # Independent review of #664 Target: `job/advfind-664` at `884cc8ba6e1d275d14844187ed16e65f4b20a354`. Reviewer branch: `job/rev2-advfind-664`. Reviewer base: `440e19dce23040ac8ebaae88f0469b6535b1afcb`. Read CLAUDE.md, CONTEXT.md, DESIGN §2, §4, §31, and §32, the #664 body and comments, and the complete nine-file branch diff. The review used source reading only. No product files changed. ## Findings | Priority | Finding | Issue | | --- | --- | --- | | P1 | Hash recovery can write a damaged Sidecar after the new reader skips it. Some malformed XMP can pass the writer's weaker parser and be replaced. Other damaged sources can stop the rebuild through strict writer errors. | [#938](https://git.kayg.org/kayg/calternal/issues/938) | | P2 | New warnings include private User folder and file names. The probe checks source bodies but misses those name fields. | [#939](https://git.kayg.org/kayg/calternal/issues/939) | | P2 | API-only MCP setup still omits the password fixture in the reviewed tree. This is an existing gap with a separate fix branch. | [#654](https://git.kayg.org/kayg/calternal/issues/654) | The P1 needs a fix before merge because damaged-byte loss blocks a merge. No P3 defect was identified. Evidence, expected behavior, rules, and focused test ideas are in [audit-findings.md](audit-findings.md). All runtime outcomes are static inferences; no regression test ran in this LIGHT review. ## Checks from source reading - The direct skip branches do not replace damaged bytes. The gap is the later hash-recovery writer call, which the added fixtures do not cover. - Tag rename obtains its source paths after reconciliation and uses that set in normal execution and restart recovery. No separate defect was found in the new path selection. Strict edit readers still reject damaged XMP. - The DAV alarm update adopts the new response ETag for a successful 200 or 204. This addresses the documented stale-token cascade when the response includes a fresh ETag. - The warning probe scans the whole log. An earlier warning with the same message can satisfy its check. The #939 regression should inspect the new events from each operation. - The PDF search finding remains unverified. This diff makes no PDF change. - The benchmark adds a workload field to an existing baseline. It supplies no new measurements. Performance measurement is outside this LIGHT job. ## Work and files Created this review and the append-only findings record. Filed #938 and #939 after issue searches. Added current evidence to #654. Posted one summary on #664. No fixes, dependency changes, UI changes, pushes, deployments, or merges. Files: `audit-findings.md` and `review-advfind-664.md`. ## Verification Build and test gates: not run, as required by the LIGHT job. Document check: `git diff --check` exited 0 with no output. Its output is empty; there is no build or test transcript from this review. No build output was created. No cargo cleanup or web-output deletion was needed. The target branch was not modified or merged. ## For the merge round Run the focused #938 damaged-source regression, then: ```sh cargo fmt --check cargo clippy -p calternal-tags --all-targets -- -D warnings cargo test -p calternal-tags cd tests/adversarial python3 -m unittest test_dav_probe test_appearance_probe ``` These checks must prove damaged-byte preservation on hash recovery and the DAV response-token contract. Use the prescribed small-build environment. For one real-server API-only round, from the repository root: ```sh ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 bash tests/adversarial/run.sh ``` The round must include the #654 setup fix, reach the MCP probe, retain damaged bytes, exclude private names from logs, complete cross-source Tag rename and rebuild, and check the remaining PDF search finding. No new runtime evidence is claimed by this review. ## Decisions and UX gaps No product design decision was made. The LIGHT instructions take precedence over the shared build, test, merge, and cleanup workflow. The target HEAD is fixed above so later branch changes need a separate review. UX gaps closed: none; this job changes review documents only. UX gaps left: not assessed; no UI change or browser work was in scope.
Author
Owner

Started the independent review follow-up on branch job/advfind-664 at HEAD 884cc8ba6e1d275d14844187ed16e65f4b20a354, based on origin/dev SHA 440e19dce23040ac8ebaae88f0469b6535b1afcb. Scope: fix #938 and #939 with regression coverage.

Started the independent review follow-up on branch `job/advfind-664` at HEAD `884cc8ba6e1d275d14844187ed16e65f4b20a354`, based on `origin/dev` SHA `440e19dce23040ac8ebaae88f0469b6535b1afcb`. Scope: fix #938 and #939 with regression coverage.
Author
Owner

Completed the independent-review follow-up for #938 and #939.

Head: 6cfebf7f75f9265c7af7cf4959ee141ccc680c5f

Commits:

  • b45febe5f tags: preserve damaged sources during hash recovery
  • 0699665e1 tags: redact rebuild warning paths
  • 6cfebf7f7 tags: keep XMP fixture lint clean

Built:

  • Tag reconciliation keeps damaged-source state separate from a valid empty Tag set. Hash recovery skips damaged folder metadata and XMP Sidecars.
  • XMP writes validate XML before Adobe's parser can serialize a packet.
  • Rebuild warnings log the User ID and source kind. They omit folder and Sidecar paths.
  • Regression coverage seeds a missing old path and matching current content hash, checks exact source-byte preservation, and verifies other valid sources rebuild. The adversarial probe checks only new log events for private folder, filename and source-body markers.

Files: crates/calternal-tags/src/index.rs, crates/calternal-tags/src/source.rs, crates/calternal-tags/src/lib.rs, tests/adversarial/attack.py.

Gate output:

cargo fmt --check completed with exit status 0 and no output.

cargo clippy -p calternal-tags --all-targets -- -D warnings:

    Checking calternal-tags v0.1.0 (/home/kayg/Developer/calternal-wt/advfind-664/crates/calternal-tags)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 16s

cargo test -p calternal-tags:

    Finished `test` profile [unoptimized + debuginfo] target(s) in 7m 54s
     Running unittests src/lib.rs (/mnt/hdd/targets/jobs/advfind-664/debug/deps/calternal_tags-2466b143a19391bc)

running 15 tests
test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok
test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok
test source::tests::valid_xmp_packet_is_still_readable ... ok
test tests::tags_are_bounded_and_nested_without_empty_segments ... ok
test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok
test tests::nested_queries_stop_at_tag_segment_boundaries ... ok
test tests::markdown_index_updates_use_the_callers_transaction ... ok
test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok
test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok
test tests::hidden_sources_do_not_create_tag_suggestions ... ok
test tests::reconcile_skips_corrupt_xmp_sidecar_and_keeps_it_untouched ... ok
test tests::reconcile_skips_corrupt_folder_metadata_and_keeps_it_untouched ... ok
test tests::hash_recovery_skips_damaged_folder_metadata ... ok
test tests::hash_recovery_skips_damaged_xmp_sidecars ... ok
test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... ok

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 17.92s

   Doc-tests calternal_tags

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

python3 -c 'import ast, pathlib; ast.parse(pathlib.Path("tests/adversarial/attack.py").read_text())' and git diff --check both completed with exit status 0 and no output.

Known gap / for the merge round: the API-only adversarial round was not run under the verification policy. The probe extension needs that round to prove damaged bytes remain unchanged and private markers stay out of new warning events. The merged tree still has the separate #654 MCP fixture gap: setup.mjs creates mcp-passwords.json only for ADVERSARIAL_MCP_ONLY=1 or XUSER_MATRIX_WITH_MCP=1, not for API-only selection. Include the #654 setup fix, then run:

ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 bash tests/adversarial/run.sh

Decisions not set by DESIGN: if any paired XMP Sidecar is damaged, hash recovery is disabled for that media item; Tags from other valid paired sources remain indexable. Warning events use the immutable User ID and a source-kind field as their content-free identifiers, with no path or path hash.

UX gaps closed: not applicable; no UI changed. UX gaps left: not assessed; no UI changed. cargo clean removed 3982 files (1.6 GiB). No web build output existed.

Completed the independent-review follow-up for #938 and #939. Head: `6cfebf7f75f9265c7af7cf4959ee141ccc680c5f` Commits: - `b45febe5f` tags: preserve damaged sources during hash recovery - `0699665e1` tags: redact rebuild warning paths - `6cfebf7f7` tags: keep XMP fixture lint clean Built: - Tag reconciliation keeps damaged-source state separate from a valid empty Tag set. Hash recovery skips damaged folder metadata and XMP Sidecars. - XMP writes validate XML before Adobe's parser can serialize a packet. - Rebuild warnings log the User ID and source kind. They omit folder and Sidecar paths. - Regression coverage seeds a missing old path and matching current content hash, checks exact source-byte preservation, and verifies other valid sources rebuild. The adversarial probe checks only new log events for private folder, filename and source-body markers. Files: `crates/calternal-tags/src/index.rs`, `crates/calternal-tags/src/source.rs`, `crates/calternal-tags/src/lib.rs`, `tests/adversarial/attack.py`. Gate output: `cargo fmt --check` completed with exit status 0 and no output. `cargo clippy -p calternal-tags --all-targets -- -D warnings`: ```text Checking calternal-tags v0.1.0 (/home/kayg/Developer/calternal-wt/advfind-664/crates/calternal-tags) Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 16s ``` `cargo test -p calternal-tags`: ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 7m 54s Running unittests src/lib.rs (/mnt/hdd/targets/jobs/advfind-664/debug/deps/calternal_tags-2466b143a19391bc) running 15 tests test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok test source::tests::valid_xmp_packet_is_still_readable ... ok test tests::tags_are_bounded_and_nested_without_empty_segments ... ok test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok test tests::nested_queries_stop_at_tag_segment_boundaries ... ok test tests::markdown_index_updates_use_the_callers_transaction ... ok test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok test tests::hidden_sources_do_not_create_tag_suggestions ... ok test tests::reconcile_skips_corrupt_xmp_sidecar_and_keeps_it_untouched ... ok test tests::reconcile_skips_corrupt_folder_metadata_and_keeps_it_untouched ... ok test tests::hash_recovery_skips_damaged_folder_metadata ... ok test tests::hash_recovery_skips_damaged_xmp_sidecars ... ok test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... ok test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 17.92s Doc-tests calternal_tags running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `python3 -c 'import ast, pathlib; ast.parse(pathlib.Path("tests/adversarial/attack.py").read_text())'` and `git diff --check` both completed with exit status 0 and no output. Known gap / for the merge round: the API-only adversarial round was not run under the verification policy. The probe extension needs that round to prove damaged bytes remain unchanged and private markers stay out of new warning events. The merged tree still has the separate #654 MCP fixture gap: `setup.mjs` creates `mcp-passwords.json` only for `ADVERSARIAL_MCP_ONLY=1` or `XUSER_MATRIX_WITH_MCP=1`, not for API-only selection. Include the #654 setup fix, then run: ```sh ADVERSARIAL_API_ONLY=1 ADVERSARIAL_SKIP_WEB_BUILD=1 bash tests/adversarial/run.sh ``` Decisions not set by DESIGN: if any paired XMP Sidecar is damaged, hash recovery is disabled for that media item; Tags from other valid paired sources remain indexable. Warning events use the immutable User ID and a source-kind field as their content-free identifiers, with no path or path hash. UX gaps closed: not applicable; no UI changed. UX gaps left: not assessed; no UI changed. `cargo clean` removed 3982 files (1.6 GiB). No web build output existed.
Author
Owner

Correction to the clippy transcript above: the package version in the emitted Checking line is 0.0.1 (not 0.1.0). The exact successful output was:

    Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/advfind-664/crates/calternal-tags)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 16s
Correction to the clippy transcript above: the package version in the emitted `Checking` line is `0.0.1` (not `0.1.0`). The exact successful output was: ```text Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/advfind-664/crates/calternal-tags) Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 16s ```
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#664
No description provided.