Say so when a schema will not load, and never wait for one on the UI thread - #39
Merged
Merged
Conversation
…thread Closes #35, #36, #37 — one condition seen from three places. An `xsi:schemaLocation` hint that cannot be resolved used to be reported as a warning, counted as nothing, and retried forever. Measured against a host that swallows packets (192.0.2.1, unroutable by RFC 5737), `--validate` took 120 seconds — a validation pass resolves each hint twice, once by URI and again by namespace, at 60 seconds a go — and exited 0 saying "0 errors". In the editor that pass runs after every command. Remote schemas are worth using: one published on the web and referenced by every document beats a copy beside each file. Being briefly unable to reach one is therefore routine, and has to be survivable. #35 — the UI thread no longer goes to the network at all. `XmlProxyResolver` gets a thread-scoped `OfflineThread`, set for the life of the process in `BuildUi`; remote hints are resolved on a background thread by the new `Fux.Schemas`, which compiles them so nested includes are warmed too. The two never overlap — `Revalidate` does nothing while `Ui.SchemaPending` is set, and that is the whole of the mutual exclusion keeping both off the shared `SchemaCache`. A fetch is capped at 5 seconds (`--schema-timeout=N`) on one shared `HttpClient` rather than 60 on a fresh one per request, and a failure is remembered by `SchemaResolver` for the session instead of being retried after every keystroke. `--validate` stays synchronous: it is a CI entry point. #36 — `Checker` now records what did not load as `SchemaLoadFailure` rather than only as warning text, so "nothing checked this document" is answerable. The pane title and `--validate`'s first line say `Not validated: 1 schema unavailable` instead of `0 errors`, and `--validate` exits 3 for it. Errors still win the exit code when there are any. `XmlSchema.Read` returning null — a captive portal's sign-in page, an HTML 404 body — is now a failure too, not a silent pass. #37 — a dialog on open naming the schema and why it failed, offering Retry, Continue and Quit, with Quit last where MessageBox's Enter lands (#21): a reflexive Enter that quits costs a relaunch, one that dismisses leaves the user editing unvalidated. It fires once per condition, not once per validation, and re-arms rather than stacking on another dialog. The pane title is the persistent half. File > Reload Schemas reaches the same retry after the dialog is gone. Drill §14c, 28 checks, all local fixtures — the point of the fix is that the UI thread needs no route out. Mutation-tested: eleven mutations, each killing the checks it should and no others; two checks that survived the first pass were weak and are rewritten. CI gains an exit-3 regression check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146fhu1MJdAWimomPpjX6WY
| internal static int HintCount(XmlCache model) | ||
| { | ||
| int n = 0; | ||
| foreach (SchemaHint unused in Checker.GetSchemaHints(model?.Document)) n++; |
| // (a) A hint that resolves to nothing. "fux-drill-missing.xsd" and not | ||
| // "emp.xsd": Main copies every sibling .xsd into this directory, so the | ||
| // obvious name would resolve and the fixture would prove nothing. | ||
| var orphan = System.IO.Path.Combine(schemaScratch, "fux_drill_orphan.xml"); |
| // timed — a remote hint comes back recorded as *pending*, which can only | ||
| // happen if the fetch was declined before a socket was opened. | ||
| Check(XmlProxyResolver.OfflineThread, "the UI thread is barred from fetching a schema"); | ||
| var blackhole = System.IO.Path.Combine(schemaScratch, "fux_drill_blackhole.xml"); |
Comment on lines
+127
to
+133
| catch | ||
| { | ||
| // Every reason a schema did not load is already recorded — in the schema | ||
| // cache as an absence, in the resolver as a remembered failure. Nothing | ||
| // here is worth killing a background thread over, and the validation pass | ||
| // the callback triggers is what reports it. | ||
| } |
Comment on lines
+138
to
+142
| catch | ||
| { | ||
| // The app can be torn down while a fetch is in flight — quit during the | ||
| // five seconds this is allowed to take. There is then no UI to update. | ||
| } |
| // Recorded by the resolver; the next hint still deserves its chance. | ||
| } | ||
| } | ||
| try { set.Compile(); } catch { } |
Comment on lines
+188
to
+191
| foreach (var f in failures) | ||
| { | ||
| if (!f.Pending) uris.Add(f.ResolvedUri + "|" + f.Message); | ||
| } |
| var settled = new List<SchemaLoadFailure>(); | ||
| if (failures != null) | ||
| { | ||
| foreach (var f in failures) if (!f.Pending) settled.Add(f); |
Comment on lines
+431
to
+442
| foreach (var existing in this._schemaFailures) | ||
| { | ||
| if (existing.ResolvedUri == uri) | ||
| { | ||
| if (existing.Pending && !pending) | ||
| { | ||
| existing.Pending = false; | ||
| existing.Message = message; | ||
| } | ||
| return; | ||
| } | ||
| } |
Comment on lines
+179
to
+182
| foreach (var inner in agg.InnerExceptions) | ||
| { | ||
| if (IsIn(inner)) return true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #35, closes #36, closes #37 — one condition seen from three places.
What was wrong
sandbox/testdata/emp-invalid.xmlwith its hint retargeted at192.0.2.1(TEST-NET-1,unroutable by RFC 5737, so packets are dropped rather than refused):
Two minutes, not the 60 seconds #35 estimated: a validation pass resolves each hint
twice — once by URI in
LoadXsiSchemas, again by namespace inLoadSchemasForNamespace—and nothing cached the failure. In the editor that pass runs after every command. And
the answer, after two minutes, was
0 errors, exit 0: indistinguishable from a pass.Same fixture now:
#35 — the UI thread does not go to the network
Not "with a shorter timeout": at all. A fetch on the thread drawing the screen is a
freeze however short its budget.
XmlProxyResolver.OfflineThread, thread-scoped, set for the life of the process inBuildUi. A remote fetch from that thread raisesSchemaOfflineExceptionbefore asocket is opened — deliberately not a failure, since nothing has been learned.
Fux.Schemasresolves the document's remote hints on a background thread andCompile()s them, so a schema whose ownxs:includeis remote is warmed too ratherthan being left for a thread that may not fetch it.
Revalidatedoes nothing whileUi.SchemaPendingis set, andthat is the whole of the mutual exclusion keeping both threads off the shared
SchemaCache— plain dictionaries, no locking.HttpClientwith a 5-second per-request token, not a fresh client per fetchwith a 60-second timeout that was then disposed while still holding the stream it had
returned.
--schema-timeout=Nchanges it.SchemaResolverremembers a failure for the session.ClearFailuresis what Retry is.--validatestays synchronous and on-thread: it is a CI entry point and has to givethe same answer every run.
#36 — a document nothing checked does not report a pass
Checkerrecords what did not load asSchemaLoadFailure— location, resolved URI,reason, position — rather than only as warning text, so "was this document checked
against anything?" is answerable. A warning among warnings is not.
Validation: 0 errors, 1 warningNot validated: 1 schema unavailableValidation: 4 errors, 1 warningValidation: 4 errors, 1 warning — 1 schema unavailable--validateexit03(errors still win with1)XmlSchema.Readreturning null without throwing is now a failure too — that is acaptive portal's sign-in page or an HTML 404 body arriving where a schema was asked
for, and it used to come out as "no schema, no errors, all is well".
AggregateException'sOne or more errors occurred.is unwrapped, so the DNS orstatus text is what the user reads.
#37 — the person who opened the file is told
MessageBoxbinds Enter to the lastbutton — the trap Confirm before deleting a node that takes others with it #21 walked into — and of the two reflexes that is the safe one: a
reflexive Enter that quits costs a relaunch, one that dismisses leaves the user
editing a document nothing is checking.
xsi:schemaLocationnever prompts.Raised from a timeout after the loop is up, never from the load path —
Mainloads thedocument before the app exists, so nothing raised there would have had a UI to appear in.
MessageBoxdoes not wrap, and an untrimmed URL takesits own buttons off-screen.
dialog is gone, which is why it must not go back to counting errors.
Retry is a one-shot and a session that started offline stays unvalidated after the VPN
comes up.
Verification
thread needs no route out, so a check needing one would measure the runner. The remote
hint is asserted structurally: it comes back recorded as pending, which can only
happen if the fetch was declined before a socket opened.
Two that survived the first pass were weak —
!contains("0 errors")stayed green whena mutation restored a title reading
2 errors, 1 warning— and are rewritten to assertthat an unchecked document reports no count at all.
.xsdso it needs no network.https://schema.fread.now/work/4.0/fread-work.xsdopens and validates clean; theblack-holed one stays responsive to keystrokes throughout the wait, then prompts.
Left open
people who habitually work offline — is not decided here. It is currently per document
per session.
XmlSchemaSettreats an unresolvablexs:includeinside a schema as non-fatal and compiles the rest, so that goes unreported. Milder
than these issues (the document is still validated, against a partial schema), and a
different mechanism.
🤖 Generated with Claude Code
https://claude.ai/code/session_0146fhu1MJdAWimomPpjX6WY