feat(repl): analyze each loaded file as a workspace document of its own - #309
Merged
Merged
Conversation
Files loaded from the command line or by %load were joined into the transcript document, so a root-level import in one file served the others and two files declaring one root package were reported as duplicates. Each loaded file is now a workspace document under its own name, indexed with the others and analyzed on its own, as the editor and the corpus gates analyze it; the typed transcript stays one joined document. A differential test runs every multi-file directory of the fixtures and the OMG corpora through the command line and a workspace and asserts the same diagnostics. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…pt alone Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…ing skill Co-Authored-By: jason.han <hanhuijun@gmail.com>
This was referenced Sep 15, 2026
Co-Authored-By: jason.han <hanhuijun@gmail.com>
A loaded file is analyzed as a document of its own, so an error in it gates that file's deeper checks only. The blocker note on a clean prompt submission now skips diagnostics from loaded files, and a load's from the transcript. Co-Authored-By: jason.han <hanhuijun@gmail.com>
A load shares no document with the rest of the buffer, so nothing blocks it and it neither names nor forgets the error the transcript has already been told of. Co-Authored-By: jason.han <hanhuijun@gmail.com>
… interval A load still names no blocker, but when it leaves the transcript unblocked the recorded note is cleared, so the error is named again should a reload bring it back. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md
… document is left A file reloaded with its enclosure left open is masked and its workspace document removed; with no scoped document left, symbolIndex returned before taking the file's previous declarations back out of the session index, so a qualified lookup kept answering with what the session no longer held. The empty-document path now drops every indexed document, as a reset does, and keeps the standard library. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
Co-Authored-By: jason.han <hanhuijun@gmail.com>
… load order Two files declaring the same root package are ordered as the workspace orders documents, by name, so which declaration a reference reaches does not depend on the order the files were given in. A test pins both orders. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…er-file loading Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md
Co-Authored-By: jason.han <hanhuijun@gmail.com>
6 tasks
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
…ocuments Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # README.md # docs/project/spec-compliance.md
This was referenced Sep 17, 2026
docs(roadmap): record E1, the fUML referee and the state-executor findings as landed on develop
#355
Merged
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ad order Co-Authored-By: jason.han <hanhuijun@gmail.com>
The typed transcript is the workspace document <repl>, so a file of that name would share its key and each would overwrite the other. Every path loader (LoadPaths, LoadFile, LoadFilesSummary, %load) now reads through one helper that refuses such a file with a *ReservedNameError before anything is submitted; the CLI exits as it does for any read failure. Co-Authored-By: jason.han <hanhuijun@gmail.com>
… merge develop's sessionSourceFile still distinguished the joined KerML buffer; with each loaded file a document of its own only the transcript's spans need the snippet lookup. Co-Authored-By: jason.han <hanhuijun@gmail.com>
SubmitFiles takes SourceFile values without the path loaders, so a file named <repl> reached openDocuments under the transcript's workspace key and its findings and declarations were read twice. The submission is now refused whole before anything is accepted; Result.Refused carries the *ReservedNameError and the rendering is that one error line. Co-Authored-By: jason.han <hanhuijun@gmail.com>
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.
Description
What and why
Files loaded from the command line (
-validate,-satisfy,-check,-run, …) or by%loadwere joined into the transcript document (<repl>, with a synthetic<repl>.kermlfor KerML files) and analyzed as one text. That buffer had semantics of its own: a root-level import in one file served every other file, and two files declaring the same root package were reported as a duplicate. The editor, the corpus gates and the pilot tools analyze the same files throughmodel.Workspace, one document per file, and report the opposite —unresolved reference: Realin the file that does not import it, and no duplicate.Each loaded file is now a workspace document under its own name, indexed together with the others and analyzed on its own; typed submissions still form the one joined transcript document. Concretely, in
internal/frontend/repl:Readers of
s.ws.Document(docName)that assumed every declaration lived in<repl>—%calc,%sweep,%analyze,%print, the compound-expression path of%eval,promptScope,symbolsInLoadOrder(%view) — now go throughhasDeclarations()/sessionDocs()/sessionMembers()/rootScopeOf(sym); nothing re-joins the files behind the scenes.%printcan print a declaration of any loaded file rather than only of<repl>/<repl>.kerml.%saveis unchanged: it writes each snippet's own text back.Two user-visible changes for multi-file loads, both the behavior
model.Workspacealready had:%load a.sysml— the text typed at the prompt. Root packages remain reachable through the global namespace as before, and the prompt still evaluates in the last namespace declared, which may be one a loaded file declares.package Aare two root namespaces of that name, not a duplicate; a reference toAresolves to the declaration in the file whose name sorts first — the order the workspace gives documents, whatever order the files were given in on the command line (TestRepeatedRootPackageResolvesByDocumentNameNotLoadOrderpins both orders).The compound-expression path of
%evalparses the expression appended to the transcript with the loaded files masked out (as the transcript document has them), so a loaded file's text can no longer fail the parse of a prompt expression; the expression is still evaluated inpromptScope().This is the first of two PRs: the parallel batch pipeline over these documents (parallel parse, one index and one wildcard expansion per batch, a worker pool with a private context per worker, diagnostics in document order, the pool sized by the existing
-jobssetting, the-split-planesgenerator mode and the measurements) follows in #312, which is stacked on this branch. Merge order: this PR first, then #312; both targetdevelop.The loading API is unchanged:
Session.LoadFile,LoadFileSummaryandLoadFilesSummary(used bycmd/sysml,tests/perfand the sibling-file dependency loading ofwithDependencies) keep their signatures and still submit every path as one submission; underneath, each path is a workspace document of its own rather than a part of<repl>.Existing
internal/frontend/repltests were examined individually; none asserted the import leak or the duplicate, so none was changed.TestTargetlessPseudoViewSpansLoadedDocumentsandTestLoadFilesSummaryMatchesLoadingEachFilefailed during the migration and were fixed in the code (symbolsInLoadOrdersorting by buffer offset;Result.ownMembersfiltering by the buffer offset), not in the tests.Known behaviour
Two prompt evaluation rules predate this change and are kept as they are (documented under "Known behaviour" in the loading section of
docs/guide/04-repl.md); both are open to change. A prompt expression evaluates in the last namespace declared (promptScope), so when a loaded file declares it, the expression sees that file's root imports although a typed declaration does not —%eval 1.5 as Realresolves after loadingprivate import ScalarValues::*; package A { … }while a typedattribute y : Real;reportsRealunresolved; the alternative is a fallback to the transcript's own root. A qualified command argument (%eval A::y,%print A::y) goes through the single index-basedlookupSymbol, which holds every document's declarations, so with two loadedpackage Ait reaches the secondA's member that a model reference or a compound expression cannot; the alternatives are a resolver-based lookup for the evaluating commands, or rejecting a root that resolves ambiguously. Both were already so ondevelop(the second for a.kerml/.sysmlpair); per-file loading makes them reachable with any two files.Specification basis
KerML 8.2.3.3 and 8.2.4: a root-level
importsurfaces its names in the importing document's own root namespace (docs/project/spec-compliance.md, row "A root-levelimport X::*surfaces its names in the importing document's own root namespace"). KerML 8.2.3.5: two root namespaces of the same name are neither an ambiguity nor a duplicate, and resolution uses the first declaration (docs/project/pilot-xpect.md, "The global namespace…", item 1;TestNameResolutionPassResolvesARepeatedTopLevelNameToTheFirst). No compliance row moves; the command line converges on what those rows already certify for the workspace.How it was verified
New in
internal/frontend/repl/filedocs_test.go:TestLoadedFilesDoNotShareRootImports—a.sysmlwithprivate import ScalarValues::*;,b.sysmlusingReal: the command line reportsunresolved reference: Realinb.sysml, and its diagnostics equalmodel.Workspace's over the same files.TestLoadedFilesDeclaringOneRootPackageAreNotDuplicates— twopackage Afiles plus a file referencingA::X(first declaration) andA::Y(second): no duplicate,A::Xresolves, diagnostics equal the workspace's.TestPromptDoesNotSeeALoadedFilesRootImports— after%load a.sysml, typed text still reachesAbut not a bareReal.TestCommandLineLoadMatchesWorkspace— every multi-file directory undertests/testdata/,examples/and the four OMG corpus roots (examples/sysml-v2-training,examples/pilot-corpora/{kerml-examples,sysml-examples,sysml-validation}) run through the command-line load and throughmodel.Workspaceyields the same diagnostics. All four fail ondevelop.Gates, all run locally:
training_examples_expected.txtis unchanged and empty; no pilot ratchet moved.By hand:
sysml -validate a.sysml b.sysmlon the import case (unresolved reference: Realinb.sysml, exit 2) and on the same-name-package case (clean; the existing note thatAis opened by more than one loaded file still prints), and%loadof several files followed by%print,%view,%evalof a compound expression and%save(each file's text written back verbatim). The REPL's own name lookup (%print A,%view A) still reports a root name two loaded files declare as ambiguous, as it did before; model resolution (A::X) takes the first declaration.Checklist
make testandmake lintpass locallydocs/reference/cli.md"Multiple Files",docs/guide/04-repl.mdloading section)changes/unreleased/per-file-documents.changed.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved