Skip to content

Fix: file-read fail-loud for config + deps (unreadable-but-present files silently degraded)#325

Open
0xLeif wants to merge 1 commit into
mainfrom
fix/file-read-fail-loud
Open

Fix: file-read fail-loud for config + deps (unreadable-but-present files silently degraded)#325
0xLeif wants to merge 1 commit into
mainfrom
fix/file-read-fail-loud

Conversation

@0xLeif

@0xLeif 0xLeif commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Audit Batch 3 (config & deps half) — three defects of the same shape: a file that exists but can't be read as UTF-8 was silently degraded instead of failing loud.

Fixed

  • [medium] Unreadable config → silent defaults. load_toml_config / load_json_config mapped a read failure to SpecSyncConfig::default() with no signal, downgrading enforcement (strict→warn, exit 1→0) on a config that was present but not valid UTF-8. Callers reach these only after an .exists() check, so a None on a still-existing file now warns loudly (naming the file) before falling back. Same for the optional local override (config.local.toml). An absent config stays silent (expected).
  • [low] deps drops an unreadable source's imports. check_undeclared_imports did Err(_) => continue, so a non-UTF-8 declared source contributed no imports and deps --strict could pass while hiding undeclared-import violations. Now a hard error (mirrors validator.rs's source-read policy); cmd_deps exits 1.
  • [medium] deps drops an unreadable spec from the graph. build_dep_graph did Err(_) => continue, silently removing the node — defeating cycle and missing-dependency detection for that module. Refactored into an internal build_dep_graph_checked that also returns read-failure messages; validate_deps extends report.errors. build_dep_graph keeps its signature for the non-gating visualization / topological-order callers.

Reproduced

  • non-UTF-8 config → loud exists but could not be read warning (was silent);
  • non-UTF-8 declared source → deps error + exit 1 (was exit 0);
  • non-UTF-8 spec → deps error + exit 1 (was exit 0, node silently dropped).

Tests

3 integration regressions + config spec invariant #9 + deps spec invariant #7. 750 unit + 180 integration, fmt / clippy / self-check 100%.

Scope note

The schema-discovery fail-open (build_schema / get_schema_table_names silently skipping unreadable migration files, weakening DB-column validation) is the same theme but needs signature changes across schema.rs + validator.rs + the check command — it ships as its own follow-up PR to keep this one focused.

Part of the post-v4.7.1 audit sweep (Batch 3).

🤖 Generated with Claude Code

…iles no longer silently degrade

Audit Batch 3 (config & deps half) — three "a file that EXISTS but can't be read
must fail loud, not silently degrade" defects:

- **[medium] Unreadable config silently reverts to defaults.** load_toml_config /
  load_json_config mapped a read failure to SpecSyncConfig::default() with no
  signal, downgrading enforcement (strict→warn, exit 1→0) on a config that was
  present but not valid UTF-8. Now, since callers reach these only after an
  `.exists()` check, a None on a still-existing file warns loudly (naming the file)
  before falling back. Same for the optional local override (config.local.toml).
- **[low] deps drops an unreadable source's imports.** check_undeclared_imports did
  `Err(_) => continue`, so a non-UTF-8 declared source contributed no imports and
  `deps --strict` could pass while hiding undeclared-import violations. Now pushes a
  hard error (mirrors validator.rs's source-read policy); cmd_deps exits 1.
- **[medium] deps drops an unreadable spec from the graph.** build_dep_graph did
  `Err(_) => continue`, silently removing the node — defeating cycle and
  missing-dependency detection for that module. Refactored into an internal
  build_dep_graph_checked that also returns read-failure messages; validate_deps
  extends report.errors with them (build_dep_graph keeps its signature for the
  non-gating visualization / topological-order callers). cmd_deps exits 1.

Reproduced: non-UTF-8 config → loud "exists but could not be read" warning (was
silent); non-UTF-8 declared source → deps error + exit 1 (was exit 0); non-UTF-8
spec → deps error + exit 1 (was exit 0, node silently dropped).

Tests: 3 integration regressions (deps unreadable source, deps unreadable spec,
config unreadable warning). Documented as config spec invariant #9 and deps spec
invariant #7. 750 unit + 180 integration, fmt / clippy / self-check 100%.

(The schema-discovery fail-open — build_schema / get_schema_table_names silently
skipping unreadable migration files — is the same theme but needs signature changes
across schema.rs + validator.rs + check; it ships as its own follow-up PR.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m
@0xLeif 0xLeif requested a review from a team as a code owner July 4, 2026 16:25
@0xLeif 0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux July 4, 2026 16:25
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the configuration and dependency validation logic to fail loudly when files exist but cannot be read (e.g., due to invalid UTF-8), rather than silently skipping them or reverting to defaults. Specifically, it updates the specifications, modifies the configuration loaders to emit warnings, updates the dependency graph builder and import checker to collect and report errors, and adds integration tests to cover these scenarios. There are no review comments, and I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Found a shiny new spec!"

CI Summary

Check Status
Validate action.yml ✅ Passed
Dependency Audit ✅ Passed
Code Coverage ✅ Passed
Format Check ✅ Passed
Docs Site ✅ Passed
Spec Validation ✅ Passed
Tests (build, test, clippy) ✅ Passed
VS Code Extension ✅ Passed
📋 Spec Validation Details

✅ SpecSync: Passed

Metric Value
Specs checked 60
Passed 60
Errors 0
Warnings 0
File coverage 100% (76/76)
LOC coverage 100% (38512/38512)

Generated by specsync · Run specsync check --format github to reproduce


Powered by corvid-pet

@0xLeif

0xLeif commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Adversarial review: READY_WITH_NITS, 93 — no defects found.

  • False-positive check (the critical one): CLEAN. No config → zero warnings; normal readable config (all 4 file forms + local override) → zero warnings; only a present-but-unreadable config warns. Every loader call site is .exists()-guarded before reaching the new warning gate (load_config precedence chain + both migrate.rs load_config_from_path sites), so absent stays silent and present-unreadable warns — verified structurally and empirically.
  • Security: CLEAN. git diff touches zero ai_command lines; the on-disk ai_command stripping + local-merge refusal are byte-for-byte untouched.
  • Deps: CLEAN. non-UTF-8 source → exit 1 (no false positive on readable); non-UTF-8 spec → exit 1 in text+JSON; --strict and --strict --mermaid gate.
  • Backward compat: CLEAN. deps text/mermaid/dot/json output byte-identical between main and branch; validate_deps calls build_dep_graph_checked once (no double I/O); non-gating callers unaffected.
  • 750 unit + 180 integration pass; self-check 100%; build_dep_graph_checked is private (no new public API).

Non-blocking nit (point 6): the config warning is stderr-only and CI gates on exit code, so an unreadable enforcement config downgrades to defaults and still exits 0. This is consistent with the pre-existing JSON-parse-error branch (also warn+default), so it ships as-is; escalating an unreadable config to a hard non-zero exit for gating commands is noted as a candidate future hardening.

@Kyntrin Kyntrin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the focused regression coverage here. The code change looks directionally right and CI is green, but the spec updates need to be completed before merge.

Requested changes:

  1. specs/deps/deps.spec.md now has a new invariant saying unreadable declared source/spec files are hard errors, but the Error Cases table still says Source file unreadable | Skipped during import extraction. That contradicts the new behavior in validate_deps / cmd_deps; please update the Error Cases row to match the hard-error behavior, and add an unreadable-spec row if you want the table to cover both new cases.

  2. Both changed specs add new behavioral contract, but neither spec was version-bumped or given a Change Log entry. Per this repo's AGENTS.md workflow, after spec changes we should increment the spec version field and add a dated Change Log entry. Please update specs/config/config.spec.md and specs/deps/deps.spec.md accordingly.

Non-blocking note: specs/config/config.spec.md could also make the Error Cases table more explicit that unreadable config files now warn loudly before falling back to defaults. I’m not blocking on the existing fallback wording as long as the version/changelog update lands, but tightening it would prevent future ambiguity.

Validation I ran locally: cargo run --quiet -- check --strict passed with 60 specs checked, 60 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.

2 participants