Skip to content

fix(sieve): resolve case-exact file probes and deduplicate contents (fixes #645) - #658

Merged
Marc-cn merged 1 commit into
darnitdevorg:mainfrom
i-am-paradox:fix/645-case-insensitive-file-probes
Nov 6, 2026
Merged

Marc-cn merged 1 commit into
darnitdevorg:mainfrom
i-am-paradox:fix/645-case-insensitive-file-probes

Conversation

@i-am-paradox

@i-am-paradox i-am-paradox commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #645.

On case-insensitive filesystems (such as macOS APFS and Windows NTFS), os.path.exists() and open() match lowercase file probes against uppercase files in the repository. As reported in #645, this causes sieve handlers to:

  1. Match candidate probes that don't match the repository's actual case (e.g., matching lowercase docs/architecture.md when only docs/ARCHITECTURE.md exists).
  2. Read duplicate file contents in llm_eval_handler and llm_extract_handler when files_to_include or files specify both case variants (such as README.md and readme.md).
  3. Cite paths in audit evidence that do not exist under exact case in the repository.

Key Changes

  1. Case-exact Directory Entry Validation:
    • Added resolve_case_exact_path(path, root=None) in darnit.core.utils to traverse each path component and verify it exists verbatim in actual directory entries (os.listdir), ensuring exact-case matching across all platforms.
    • Cached directory entries via public dir_entries(dir_path) keyed by st_mtime_ns.
    • Cleared directory entries cache on remediation apply and in orchestrator _recheck.
    • Added get_file_identity(full_path) returning (st.st_dev, st.st_ino) for physical file deduplication across paths, symlinks, or aliases.
  2. Sieve Handler Updates:
    • file_exists_handler: verifies exact case for globs, max_depth walks, and direct path lookups while filtering broken symlinks and preserving repository containment.
    • _resolve_regex_files & _regex_exclude_evidence: ensures exact case matches, filters broken symlinks, and deduplicates by physical file identity.
    • llm_eval_handler & llm_extract_handler: enforces case-exact paths and deduplicates opened files before adding to file_contents.
  3. Config & Baseline Alignment:
    • discover_files: verifies case-exact paths before returning mapped references.
    • openssf-baseline.toml: updated OSPS-SA-01.01 and OSPS-SA-02.01 to probe docs/ARCHITECTURE.md and docs/API.md (and .rst variants) in discover and files.
  4. Cross-Platform Test Verification:
    • Added unit tests in tests/darnit/core/test_utils.py for resolve_case_exact_path and get_file_identity.
    • Added regression tests in tests/darnit/sieve/test_builtin_handlers.py for file_exists_handler, regex_handler, depth-limited walks, and llm_eval_handler case rejection & deduplication.
    • Path assertions in test_builtin_handlers.py and test_discovery.py use Path(...) and os.path.normpath for cross-platform compatibility on Windows (\) and POSIX (/).

Type of Change

  • Bug fix (non-breaking change fixing an issue)
  • New feature (non-breaking change adding functionality)
  • Breaking change (fix or feature causing existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)

Framework Changes Checklist

If this PR modifies the darnit framework (packages/darnit/):

  • Updated framework spec (docs/architecture/framework-design.md) if behavior changed
  • Ran uv run python scripts/validate_sync.py --verbose and it passes

Control/TOML Changes Checklist

If this PR modifies controls or TOML configuration:

  • Control metadata defined in TOML (not Python code)
  • SARIF fields (description, severity, help_url) included where appropriate
  • Ran validation to confirm TOML schema compliance

Testing

  • Tests pass locally (uv run pytest tests/ -v)
  • Added tests for new functionality (if applicable)
  • Linting passes (uv run ruff check .)

All unit and regression tests pass locally, and scripts/validate_sync.py --verbose succeeds. Path assertions use Path(...) and os.path.normpath to hold across both Windows and Linux/macOS.

AI assistance

  • No AI assistance was used
  • AI assistance was used

Assisted-by: Claude & Gemini for cross-platform reproduction analysis, path normalization investigation, and test fixture construction. All implementations, tests, and diffs were independently reviewed, executed, and verified locally.

Additional Notes

Addresses review feedback from @Marc-cn:

  • Updated test_nested_case_exact_rejection and test_glob_case_exact_match in test_builtin_handlers.py to compare with Path(...) to handle Windows path separators (\).
  • Updated test_case_mismatch_not_discovered in test_discovery.py to use os.path.normpath for Windows compatibility.
  • Adopted the full PR template structure including AI assistance disclosure.

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the approach is the one #645 asks for and the core of it reads right: case-exact traversal against os.listdir, the listing cache keyed by directory mtime, dedup by (st_dev, st_ino). 160 tests pass here.

Two things before merge:

  1. Please drop the unrelated reformatting. In core/utils.py about 60 of the 169 changed lines are untouched code rewritten by a formatter (quotes, joined argument lists, split f-strings), and sieve/builtin_handlers.py has the same. The project isn't formatted with ruff format, and these hunks hide the real change and conflict with every other PR touching those files.
  2. The TOML change isn't only about case: to stay at 5 entries, files_to_include drops docs/design.md (OSPS-SA-01.01) and swagger.json (OSPS-SA-02.01), so the LLM step stops reading those. @mlieberman85, that's a content call for you; if those files should stay, the uppercase variants could go into discover/files only.

Note for Mike: this edits discover_files in config/discovery.py, which #630's notes mark for removal after #622.

@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Thanks @Marc-cn! Addressed both points in 0c37b6c:

  1. Dropped unrelated reformatting: Reverted untouched code formatting across core/utils.py, sieve/builtin_handlers.py, and config/discovery.py back to original repository style, eliminating all formatting churn.
  2. Preserved baseline files_to_include: Restored docs/design.md in OSPS-SA-01.01 and swagger.json in OSPS-SA-02.01 in files_to_include (leaving files_to_include identical to upstream main). The uppercase variants remain in discover and files (pattern).

All 160 tests in test_utils.py, test_discovery.py, and test_builtin_handlers.py pass cleanly.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from 0c37b6c to c91e235 Compare October 25, 2026 23:10
@Marc-cn

Marc-cn commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Both of my points are addressed: the formatting churn is gone and files_to_include matches main. The case-exact walk only filters candidates, the reads still go through the #635 containment, so the two compose.

@mlieberman85: the uppercase variants added to the Baseline discover/files lists, and whether a 400-line change for case-insensitive filesystems is wanted given darnit mostly runs in Linux containers. The dedup of contents is the part I'd keep regardless.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from c91e235 to 79ae049 Compare October 27, 2026 22:45
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Rebased cleanly onto latest main (3822f28). The CHANGELOG.md conflict with #682 is resolved, and the branch is squashed into a single clean commit on top of main. All 160 unit and sieve tests in test_utils.py, test_discovery.py, and test_builtin_handlers.py pass cleanly.

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right approach for #645: each component checked against the real directory listing, dedupe by inode before reading into evidence; tests cover it. Four things:

  1. The three test files carry a lot of unrelated reformatting (string wrapping, quotes). Please drop it so the diff is only the fix.
  2. builtin_handlers.py imports the private _dir_entries from core.utils; make it public now that it's cross-module.
  3. The listing cache is keyed on st_mtime_ns. On coarse-mtime filesystems (HFS+, FAT, some network mounts) a file written in the same tick as the listing stays invisible, and the re-check after a remediation apply is exactly that pattern. clear_dir_entries_cache() exists but nothing on the apply/re-check path calls it. Either clear it after an apply or don't cache across that boundary.
  4. The depth-walk branches now test pattern in dir_entries(dirpath) without os.path.exists, so a broken symlink entry counts as found where it didn't before.

Needs a rebase too. @mlieberman85 worth alook since it changes file resolution for every control.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from 79ae049 to 0dc46f8 Compare October 28, 2026 22:53
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Thanks @Marc-cn! Addressed all four points in 0dc46f8 and rebased cleanly onto latest main:

  1. Dropped unrelated test reformatting: Reverted all string-wrapping and quote alterations across tests/darnit/core/test_utils.py, tests/darnit/config/test_discovery.py, and tests/darnit/sieve/test_builtin_handlers.py. The diff in the test files now consists strictly of the new regression tests and required imports.
  2. Made dir_entries public: Renamed _dir_entries to dir_entries in darnit.core.utils and imported dir_entries directly in darnit.sieve.builtin_handlers.
  3. Invalidate cache across apply/re-check boundary: Called clear_dir_entries_cache() in RemediationExecutor.execute on apply completion, in RemediationExecutor._write upon file write, and at the start of orchestrator._recheck(), ensuring coarse-timestamp filesystems see newly written remediation files during immediate post-apply re-checks.
  4. Symlink handling in depth-walk branches: Added os.path.exists(candidate) checks in the depth-limited walk branches of file_exists_handler and _regex_exclude_evidence, ensuring broken symlinks matching the candidate pattern are not falsely counted as found. Added test_max_depth_broken_symlink_not_found covering this behavior.

All 2,063 unit tests pass cleanly, and Ruff linting is green.

Marc-cn
Marc-cn previously approved these changes Oct 29, 2026

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All four addressed: reformat reverted, dir_entries public, cache cleared on apply/write/re-check, os.path.exists back in the depth walks with the broken-symlink test. Please rebase on main to pick up #694; the Test failure shown is the old runner issue. @mlieberman85 one last check since it changes file resolution for every control, then merge.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from 0dc46f8 to a758c39 Compare October 29, 2026 22:45
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Rebased cleanly onto latest main (e12e086), incorporating the upstream venv symlink fix (#694) and recently merged PRs (#656, #632). All 161 unit tests pass and lint checks are green.

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is right: checking each component against os.listdir gives Linux semantics on macOS and Windows, the listing cache is keyed by directory mtime and cleared after remediation writes, dedup by (st_dev, st_ino) covers case aliases and symlinks, containment is still enforced by _skip_outside, and the docs/ARCHITECTURE.md / docs/API.md TOML variants keep what macOS used to match by accident. Linux: ruff, validate_sync and the sieve/baseline/core suites pass (1834).

Since the Linux tests can't fail for a case mismatch (the OS already rejects it), I also ran the new tests on a Windows checkout, where it matters. The case-exact rejections pass there — good. Two of the new tests fail on Windows for an unrelated reason: they assert relative_path == "docs/ARCHITECTURE.md" and Windows gives docs\ARCHITECTURE.md:

  • TestFileExistsHandler::test_nested_case_exact_rejection (test_builtin_handlers.py:125)
  • TestFileExistsHandler::test_glob_case_exact_match (test_builtin_handlers.py:136)

Please compare with Path(...) or os.path.normpath so the tests hold on the platforms the fix is for. And please use the PR template (.github/pull_request_template.md), including the AI-assistance section. Happy to approve after those two.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from e34fd28 to a361b93 Compare October 31, 2026 22:55
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Thanks @Marc-cn! Addressed both points in a361b93:

  1. Cross-platform path comparisons in tests: Updated test_nested_case_exact_rejection and test_glob_case_exact_match in test_builtin_handlers.py to compare with Path(...) (assert Path(res.evidence["relative_path"]) == Path("docs/ARCHITECTURE.md")), ensuring tests pass on Windows (\) as well as POSIX (/). Also updated test_case_mismatch_not_discovered in test_discovery.py to use os.path.normpath.
  2. PR template & AI assistance: Updated the PR description to adopt the full PR template (.github/pull_request_template.md), including framework/control checklists, verification notes, and the AI-assistance disclosure section.
  3. Rebased onto latest main: Rebased cleanly onto latest main (075556e).

All 5,139 tests across the repository pass cleanly locally, scripts/validate_sync.py --verbose succeeds, and ruff check . is completely green.

@mlieberman85 mlieberman85 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed a361b93. The case-exact walk and the dedup work for ordinary paths, the shipped controls are unaffected, and the template and the Windows assertion changes from Marco's review are in. I can't run Windows and PR CI is Linux only, so nothing here confirms those two tests pass there. Requesting changes for three regressions that case-exactness introduced, plus two smaller items. Item 1 can produce a false PASS.

All of the results below come from darnit audit -f <framework>.toml on a throwaway repo, comparing main (075556e) with this PR.

1. max_depth > 0 with a path entry no longer finds the file (every filesystem).

builtin_handlers.py:304 (file_exists), :809 (_regex_exclude_evidence) and :899 (_resolve_regex_files) do pattern in dir_entries(dirpath). dir_entries() returns the names in one directory, so an entry like dist/app.exe never matches, not even at the root. Main joined the path and checked it on disk. framework-design.md:414 and :525 don't restrict max_depth to basenames.

On a repo where dist/app.exe exists:

step main this PR
pattern, exclude_files = ["dist/app.exe"], max_depth = 1, expr = 'output.files_found == 0', existence = true FAIL PASS, dispositive
same without max_depth FAIL FAIL
file_exists, files = ["dist/app.exe"], max_depth = 1, existence = true PASS FAIL

The first row has the shape of OSPS-QA-05.01: a forbidden file is present and the control passes. No shipped TOML hits this today, because QA-05.01's entries are globs and BR-05.01 and QA-02.01 use basenames, but an operator custom control or another framework would.

Suggested fix: resolve os.path.join(os.path.relpath(dirpath, root), pattern) with resolve_case_exact_path in those three branches, then drop the unused dir_entries import. With that applied, all three rows match main and tests/darnit/sieve passes. Please add tests with a slash-containing entry plus max_depth, including the exclude case.

2. Paths outside the repository are no longer recorded (spec 3.0.4).

resolve_case_exact_path returns None for a path outside the repository, so _skip_outside never sees it:

step main skipped_outside_repository this PR
file_exists, files = ["../outside.md"] outside.md none
pattern, files = ["../outside.md"] outside.md none
llm_eval, files_to_include = ["../outside.md"] outside.md none
llm_eval, files_to_include = ["dangling.md"], a symlink pointing outside the repo that dangles dangling.md none

Nothing is read either way, and statuses don't change, but the spec requires the record so an auditor can see the attempt. It needs to be recorded before the case-exact check rejects the path.

3. A file name starting with two dots is rejected.

utils.py:760 tests rel.startswith(".."), so ..b.md counts as outside the repository. file_exists, files = ["..b.md"], existence = true: main PASS, this PR FAIL. Testing for rel == ".." or rel.startswith(".." + os.sep) should fix it, though I haven't run that.

4. SA-01.01 and SA-02.01: the LLM step isn't given the document the pattern step found.

The new TOML variants keep what macOS used to match by accident for the pattern step, but not for the LLM step. The uppercase names went into discover/files but not files_to_include, which still names only lowercase docs/architecture.md / docs/api.md for those documents. With case-exact resolution those no longer open docs/ARCHITECTURE.md on macOS. The resolved_files fallback only runs when nothing was read, so a repo with a README.md doesn't reach it. On wtf-frontend (which has docs/ARCHITECTURE.md and docs/API.md):

control main file_contents this PR file_contents
OSPS-SA-01.01 README.md, docs/architecture.md README.md
OSPS-SA-02.01 README.md, docs/api.md README.md

The statuses don't change in that run (PENDING both ways), and file_contents matches what main reads on a case-sensitive volume, so it fails safe. But the pattern step now cites a document the judgment isn't given, on every filesystem. Suggestion: use $RESOLVED_FILES in files_to_include in place of those doc entries, listed after README.md, because the 5-file cap can otherwise push README out.

5. Spec. The file_exists, regex/pattern, llm_eval and llm_extract steps and discover_files are now case-exact on every filesystem, and repeated paths to one file are deduplicated. Plugin authors can observe that, so a sentence in those sections of framework-design.md would cover it. It shouldn't say every probe: on macOS, core.utils.file_exists still matches README.md for readme.md. The template's framework-spec box is ticked, but that file isn't in the diff.

Minor, non-blocking:

  • No test pins the clear_dir_entries_cache() calls in executor._write, execute and orchestrator._recheck: with all of them removed, the remediation, core and sieve tests still pass.
  • With get_file_identity made to return a unique value, only test_resolve_regex_files_deduplicates_same_file and that function's own unit test fail in the sieve and core tests, so llm_eval and llm_extract dedup aren't pinned.
  • From reading the code, not run: the direct file_exists branch now reports relative_path through relpath, so on Windows it would hold backslashes, and control_loader.py:183 feeds that value to project_update. The Path(...) assertions wouldn't catch it.

On a361b93, which is based on 075556e: full suite 5146 passed, ruff and validate_sync pass. A real-repo audit of wtf-frontend shows no status changes against 075556e, and on a case-insensitive volume the statuses and file_contents match main on a case-sensitive volume.


Drafted with AI assistance (Claude), reviewed and filed by me.

@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from a361b93 to 1490162 Compare November 1, 2026 23:00
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Addressed all review feedback in 1490162:

  1. max_depth with slash-containing path entries: Replaced pattern in dir_entries(dirpath) in file_exists, _regex_exclude_evidence, and _resolve_regex_files with resolve_case_exact_path on the constructed relative candidate path (os.path.join(os.path.relpath(dirpath, root), pattern)), and dropped the unused dir_entries import from builtin_handlers.py. Added tests covering slash-containing entries (dist/app.exe) with max_depth across file_exists and exclude_files.
  2. Spec 3.0.4 skipped outside repository records: Ensured candidate paths outside the repository are evaluated against _skip_outside before case-exact filtering rejects them, preserving skipped_outside_repository records across file_exists, regex/pattern, and llm_eval (including dangling symlinks pointing outside).
  3. Double-dot prefixed filenames (..b.md): Updated utils.py to test rel == ".." or rel.startswith(".." + os.sep) instead of rel.startswith(".."), so files starting with two dots inside the repository are recognized properly.
  4. OSPS-SA-01.01 and OSPS-SA-02.01 context passing: Switched files_to_include to ["README.md", "$RESOLVED_FILES"] per the suggestion, ensuring the LLM evaluation receives the exact document located by the pattern step.
  5. Framework Spec: Documented case-exact resolution and physical file deduplication in docs/architecture/framework-design.md sections 3.2 (file_exists), 3.3 (regex), 3.5 (llm_eval), and 11.1 (discover_files).
  6. Path normalization: Normalized relative_path evidence and file_contents keys to POSIX forward slashes (.replace(os.sep, "/")).

Full test suite (5,146 tests passed locally) and validate_sync.py pass cleanly.

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on 1490162. Both points addressed, and better than asked: relative_path is now normalised to "/" in the handler itself, so evidence is identical on every platform instead of the tests tolerating a difference. The max_depth lookups going through resolve_case_exact_path on the constructed relative path is the right generalisation, and the switch of SA-01.01/SA-02.01 to $RESOLVED_FILES matches the other eight controls that already use it. Linux: ruff, validate_sync, sieve/baseline/core suites and the corpus gate pass; the earlier Windows run confirmed the case-exact rejections where they matter.

@mlieberman85 mlieberman85 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1490162 merged into main at 155dcc1. The four behavior problems from my last review are fixed.

step main this PR
pattern, exclude_files = ["dist/app.exe"], max_depth = 1, existence = true FAIL FAIL
file_exists, files = ["dist/app.exe"], max_depth = 1, existence = true PASS PASS
file_exists, files = ["..b.md"], existence = true PASS PASS
file_exists, files = ["LOWERCASE.md"] on a case-insensitive volume, where the file is lowercase.md PASS FAIL
same on a case-sensitive volume FAIL FAIL

skipped_outside_repository matches main for file_exists, pattern and llm_eval with ../outside.md, and for llm_eval with a dangling symlink. All of the above ran on both a case-insensitive and a case-sensitive volume. On a repo with docs/architecture.md and docs/api.md, on a case-insensitive volume, SA-01.01 and SA-02.01 now give the judgment step the same files as main.

Real repositories, every control against main:

  • jonschlinkert/is-number and jonschlinkert/is-odd: no status changes on a case-insensitive volume.
  • sindresorhus/slash on a case-insensitive volume: four controls change, because its files are license and .github/security.md. OSPS-LE-01.01 goes WARN to FAIL, OSPS-LE-03.01 goes PASS to FAIL, and OSPS-VM-02.01 and STAGE1-REF-SECURITY-01 go PENDING to FAIL.
  • sindresorhus/slash on a case-sensitive volume: main and this PR give the same status on every control, including those four FAILs.

So this makes macOS match what Linux already reports, as intended. Those FAILs are still wrong for that repository, since GitHub detects MIT in license. That predates this PR, so I'll file it separately rather than hold this up.

The full suite passes on the merged tree (5175 passed, 11 skipped), and so do ruff and validate_sync.

One thing before merge: please add an Assisted-by trailer to the commit, as AI_POLICY.md asks. The description names Claude and Gemini, but the commit has only the sign-off. It still merges cleanly into main, so it can go in once that's pushed; I'll resolve the conflicts with my open PRs on my side.

I didn't recheck the two non-blocking test gaps from my last review.


Drafted with AI assistance (Claude), reviewed and filed by me. Measurements quoted were executed and verified.

…ixes darnitdevorg#645)

On case-insensitive filesystems (such as macOS APFS and Windows NTFS),
os.path.exists() and open() match lowercase file probes against uppercase
files in the repository. As reported in darnitdevorg#645, this causes sieve handlers to:
1. Match candidate probes that don't match the repository's actual case.
2. Read duplicate file contents in llm_eval_handler and llm_extract_handler
   when files_to_include or files specify both case variants.
3. Cite paths in audit evidence that do not exist under exact case in the repo.

Changes:
- Added resolve_case_exact_path(path, root=None) in darnit.core.utils.
- Cached directory entries via public dir_entries(dir_path) keyed by st_mtime_ns.
- Cleared directory entries cache on remediation apply and in orchestrator _recheck.
- Added get_file_identity(full_path) returning (st_dev, st_ino) for physical file deduplication.
- file_exists_handler: verifies exact case for globs, max_depth walks (including slash-containing entries like dist/app.exe), and direct path lookups while filtering broken symlinks, normalizing relative_path to POSIX forward slashes, and recording escaped paths in skipped_outside_repository per spec 3.0.4.
- _resolve_regex_files & _regex_exclude_evidence: ensures exact case matches, supports slash-containing entries in max_depth walks, filters broken symlinks, records outside paths, and deduplicates by physical file identity.
- llm_eval_handler & llm_extract_handler: enforces case-exact paths, records outside paths/dangling symlinks in skipped_outside_repository, and deduplicates opened files before adding to file_contents.
- utils.py: recognized filenames prefixed with two dots (e.g. ..b.md) inside repository without false outside-of-repo classification.
- discover_files: verifies case-exact paths before returning mapped references.
- openssf-baseline.toml: added docs/ARCHITECTURE.md and docs/API.md variants to discover and files, and updated files_to_include in OSPS-SA-01.01 and OSPS-SA-02.01 to ["README.md", "$RESOLVED_FILES"].
- docs/architecture/framework-design.md: documented case-exact resolution and physical file deduplication in sections 3.2, 3.3, 3.5, and 11.1.
- Unit and regression tests covering case-exact path validation, deduplication, broken symlink rejection, slash-containing max_depth entries, outside path recording, and ..b.md filenames.

Fixes darnitdevorg#645

Assisted-by: Claude:claude-3-5-sonnet
Assisted-by: Gemini:gemini-2.5-pro

Signed-off-by: Paradox <159635503+i-am-paradox@users.noreply.github.com>
@i-am-paradox
i-am-paradox force-pushed the fix/645-case-insensitive-file-probes branch from 1490162 to b1d3c1b Compare November 5, 2026 22:48
@i-am-paradox

Copy link
Copy Markdown
Contributor Author

Thanks @Marc-cn! Added the Assisted-by trailers to the commit per AI_POLICY.md (Assisted-by: Claude:claude-3-5-sonnet and Assisted-by: Gemini:gemini-2.5-pro) alongside the DCO sign-off, and rebased cleanly onto latest main (b7e6506). All unit, sieve, and discovery tests pass locally with validate_sync.py clean.

@Marc-cn Marc-cn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved on b1d3c1b (trailers added, rebased on main, same change Mike verified on both volume types). On the VM against main: tests/darnit/sieve + tests/darnit_baseline + tests/darnit/core 2146 passed, ruff and validate_sync clean.

@Marc-cn
Marc-cn merged commit e5bff3a into darnitdevorg:main Nov 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On a case-insensitive filesystem, file probes duplicate evidence and cite paths that are not in the repository

3 participants