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
{{ message }}
Repository navigation
fetch_schema rewrites src/osw/model/entity.py as a side effect of ordinary read/write calls #141
fetch_schema regenerates src/osw/model/entity.pyinside the installed package. It is not called explicitly by the user, it is reached transitively from ordinary operations, currently create_or_update_entity and export_entity_jsonld in the MCP server (#133), and soon from the planned CLI.
Why it matters
Under a normal pip install osw this writes into site-packages: wasteful and surprising, but contained.
In an editable/source checkout it modifies a tracked file in the developer's working tree. Running a tool that reads or writes one entity produces an unrelated diff in git status.
@SimonStier picking this up from the #133 follow-up triage. Two things I could
confirm while looking at it, and one of them makes the obvious fix harder than it
looks. Worth a broader design discussion before anyone writes code, because it
touches load_entity too, not just the MCP/CLI surface.
The corruption is unrecoverable, not just untidy
#125 shows fetch_schema writing a syntactically invalid entity.py and then
reloading it. The part worth stating explicitly is what that leaves behind. I
appended a single invalid line to src/osw/model/entity.py and ran osw --help:
File "src/osw/controller/__init__.py", line 8, in <module>
import osw.model.entity as model
File "src/osw/model/entity.py", line 30
broken: Foo | None = Field(default_factory=lambda :Foo.parse_obj(<object object at 0x1>))
^
SyntaxError: invalid syntax
So it is not only import osw.core that breaks, it is the CLI entry point
itself. src/osw/cli/main.py:24 imports osw.service.ops, which reaches osw.core, which does import osw.model.entity as model at src/osw/core.py:40.
(I restored the file; nothing is committed in that state.)
Consequence for a reset tool
A reset command that lives in the CLI or the MCP server cannot run in the exact
state it exists to repair. Whatever we build has to be reachable without
importing osw.core: a separate console script, a python -m osw.model.reset,
or a subcommand registered before the ops import. This is the main reason I did
not just implement the reset directly.
The good news
The pristine src/osw/model/entity.py is 28 lines, a pure re-export shim from opensemantic.core.v1 and opensemantic.base.v1. So "original state" is well
defined and trivial to restore. The catch is that the first fetch_schema
overwrites the only copy, so a pristine duplicate has to be shipped alongside it
or embedded.
Prevention may be cheaper than recovery
_fetch_schema writes entity.py and then reloads it
(src/osw/core.py:997-1001). Generating to a temp file, compile()-ing it, and
only moving it into place on success turns #125 from a bricked install into a
failed operation that leaves the previous working model untouched.
That also covers the load_entity path, which is how #125 is actually triggered:
lazy relation resolution -> resolver -> load_entity(autofetch_schema=True) -> fetch_schema. A reset tool alone would not help there, since the user has to
notice and run it.
Open questions for the discussion
Should fetch_schema keep writing into the installed package at all, or write
to a user cache dir? There is already a precedent to reuse in _default_state_dir() at src/osw/service/ledger.py:25-33, which needs no
extra dependency.
Should generation be validated before it replaces a working entity.py?
Should autofetch_schema still default to True on the resolver path, given
that is the path that bricks the install?
If we do add a reset, where does it live so it survives the broken state?
For context on the current blast radius: the MCP get_category_schema tool is not affected, it reads the jsonschema slot directly
(src/osw/service/ops/schema.py:28-31). Only create_or_update_entity
(src/osw/service/ops/entities.py:132-134, unconditional on every call) and export_entity_jsonld via autofetch_schema=True reach fetch_schema today.
Now observed through the CLI, which this issue anticipated.
An agent driving only the generic osw CLI ran osw entity put once against a live instance. src/osw/model/entity.py appeared as modified in git status immediately afterwards, in an editable checkout. The agent flagged it unprompted as surprising for a command whose stated job is a remote write.
Two further data points from the same session:
The regenerated file grows by roughly 5500 lines for a single category fetch, so the diff hides any real change to that file.
A clean src/osw/model/entity.py is a 28-line stub that re-exports from opensemantic.core.v1. No category class is shipped, so every model class exists only after fetch_schema has written that file. Any code wanting a category class in-process has no alternative today.
The interim mitigation suggested here is in place: the side effect is documented in the help text of the affected commands.
Activity
LukasGold commentedon Aug 28, 2026
@SimonStier picking this up from the #133 follow-up triage. Two things I could
confirm while looking at it, and one of them makes the obvious fix harder than it
looks. Worth a broader design discussion before anyone writes code, because it
touches
load_entitytoo, not just the MCP/CLI surface.The corruption is unrecoverable, not just untidy
#125 shows
fetch_schemawriting a syntactically invalidentity.pyand thenreloading it. The part worth stating explicitly is what that leaves behind. I
appended a single invalid line to
src/osw/model/entity.pyand ranosw --help:So it is not only
import osw.corethat breaks, it is the CLI entry pointitself.
src/osw/cli/main.py:24importsosw.service.ops, which reachesosw.core, which doesimport osw.model.entity as modelatsrc/osw/core.py:40.(I restored the file; nothing is committed in that state.)
Consequence for a reset tool
A reset command that lives in the CLI or the MCP server cannot run in the exact
state it exists to repair. Whatever we build has to be reachable without
importing
osw.core: a separate console script, apython -m osw.model.reset,or a subcommand registered before the ops import. This is the main reason I did
not just implement the reset directly.
The good news
The pristine
src/osw/model/entity.pyis 28 lines, a pure re-export shim fromopensemantic.core.v1andopensemantic.base.v1. So "original state" is welldefined and trivial to restore. The catch is that the first
fetch_schemaoverwrites the only copy, so a pristine duplicate has to be shipped alongside it
or embedded.
Prevention may be cheaper than recovery
_fetch_schemawritesentity.pyand then reloads it(
src/osw/core.py:997-1001). Generating to a temp file,compile()-ing it, andonly moving it into place on success turns #125 from a bricked install into a
failed operation that leaves the previous working model untouched.
That also covers the
load_entitypath, which is how #125 is actually triggered:lazy relation resolution -> resolver ->
load_entity(autofetch_schema=True)->fetch_schema. A reset tool alone would not help there, since the user has tonotice and run it.
Open questions for the discussion
fetch_schemakeep writing into the installed package at all, or writeto a user cache dir? There is already a precedent to reuse in
_default_state_dir()atsrc/osw/service/ledger.py:25-33, which needs noextra dependency.
entity.py?autofetch_schemastill default toTrueon the resolver path, giventhat is the path that bricks the install?
For context on the current blast radius: the MCP
get_category_schematool isnot affected, it reads the
jsonschemaslot directly(
src/osw/service/ops/schema.py:28-31). Onlycreate_or_update_entity(
src/osw/service/ops/entities.py:132-134, unconditional on every call) andexport_entity_jsonldviaautofetch_schema=Truereachfetch_schematoday.LukasGold commentedon Sep 21, 2026
Now observed through the CLI, which this issue anticipated.
An agent driving only the generic
oswCLI ranosw entity putonce against a live instance.src/osw/model/entity.pyappeared as modified ingit statusimmediately afterwards, in an editable checkout. The agent flagged it unprompted as surprising for a command whose stated job is a remote write.Two further data points from the same session:
src/osw/model/entity.pyis a 28-line stub that re-exports fromopensemantic.core.v1. No category class is shipped, so every model class exists only afterfetch_schemahas written that file. Any code wanting a category class in-process has no alternative today.The interim mitigation suggested here is in place: the side effect is documented in the help text of the affected commands.