You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Fix: file-read fail-loud for config + deps — unreadable-but-present files 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
* Update: sync deps/config specs with fail-loud behavior (review changes)
Addresses Kyntrin's CHANGES_REQUESTED on #325:
- deps.spec.md: Error Cases table now reflects hard-error behavior for
unreadable declared source AND spec files (was "Skipped during import
extraction"); added unreadable-spec row. Version bump 2->3 + Change Log.
- config.spec.md: Error Cases table made explicit that a present-but-
unreadable config warns loudly before falling back. Version bump 1->2 +
Change Log.
specsync check --strict passes (60/60).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: specs/config/config.spec.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
module: config
3
-
version: 1
3
+
version: 2
4
4
status: stable
5
5
files:
6
6
- src/config.rs
@@ -60,6 +60,7 @@ The configuration file supports the following top-level sections:
60
60
6. Root-level source files (no subdirectories) produce `["."]` as source dirs
61
61
7. TOML parsing is zero-dependency — uses line-by-line string parsing, not a TOML library
62
62
8. The reader accepts both TOML string kinds for scalar and array values: basic `"..."` strings (backslash escapes decoded) and literal `'...'` strings (taken verbatim, no escape processing); a `#`, `,`, `[`, or `]` appearing inside either kind is treated as content, not as a comment or array structure
63
+
9. A config file that is absent is expected — defaults apply silently. But a config file that **exists yet cannot be read** (e.g. not valid UTF-8) fails loud: a warning naming the file is printed and built-in defaults are used, rather than silently reverting to defaults (which would downgrade enforcement — strict→warn, exit 1→0 — with no signal). The same applies to the optional local override file (`config.local.toml`)
63
64
64
65
## Behavioral Examples
65
66
@@ -85,7 +86,8 @@ The configuration file supports the following top-level sections:
85
86
86
87
| Condition | Behavior |
87
88
|-----------|----------|
88
-
| Config file unreadable | Falls back to `SpecSyncConfig::default()`|
89
+
| Config file exists but unreadable (e.g. not valid UTF-8) | Prints a warning naming the file to stderr, then falls back to `SpecSyncConfig::default()` (fail-loud, not silent) |
Copy file name to clipboardExpand all lines: specs/deps/deps.spec.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
module: deps
3
-
version: 2
3
+
version: 3
4
4
status: stable
5
5
files:
6
6
- src/deps.rs
@@ -45,6 +45,7 @@ Cross-module dependency validation. Parses `depends_on` declarations from spec f
45
45
4. Cross-project refs (containing `/`) in `depends_on` are skipped — only local deps are validated
46
46
5. Undeclared imports (found in source but not in `depends_on`) are reported as warnings, not errors
47
47
6. Module names in `depends_on` paths are extracted from the path's directory component
48
+
7. A spec or declared source file that exists but cannot be read as UTF-8 is a hard error (not a silent skip): an unreadable spec would otherwise be dropped from the graph — defeating cycle and missing-dependency detection for that module — and an unreadable source would contribute no imports, hiding undeclared-import violations. Both are recorded in `report.errors`, so `cmd_deps` exits 1 rather than under-validating silently
48
49
49
50
## Behavioral Examples
50
51
@@ -70,7 +71,8 @@ Cross-module dependency validation. Parses `depends_on` declarations from spec f
70
71
71
72
| Condition | Behavior |
72
73
|-----------|----------|
73
-
| Source file unreadable | Skipped during import extraction |
74
+
| Declared source file exists but unreadable as UTF-8 | Hard error recorded in `report.errors`; `cmd_deps` exits 1 (not skipped during import extraction) |
75
+
| Spec file exists but unreadable as UTF-8 | Hard error recorded in `report.errors`; node is not silently dropped from the graph; `cmd_deps` exits 1 |
| No specs found in specs_dir | Returns empty graph and clean DepsReport |
76
78
@@ -94,5 +96,6 @@ Cross-module dependency validation. Parses `depends_on` declarations from spec f
94
96
95
97
| Date | Change |
96
98
|------|--------|
99
+
| 2026-07-06 | Documented fail-loud behavior for unreadable declared source and spec files: added invariant 7 and updated Error Cases table so an existing-but-non-UTF-8 file is a hard error gating `cmd_deps` rather than a silent skip |
97
100
| 2026-04-10 | Populated requirements.md with user stories, acceptance criteria, constraints, and out-of-scope items |
0 commit comments