fix(iris_production_item): save the production class, not only the live config - #413
Open
PYDuquesnoy wants to merge 1 commit into
Open
PYDuquesnoy wants to merge 1 commit into
PYDuquesnoy wants to merge 1 commit into
Conversation
…ve config
add/remove/set_settings/enable/disable all ended in tProd.%Save(), which writes the
extent (Ens_Config.Item) and is the LIVE configuration, then optionally
Ens.Director.UpdateProduction to apply it. None called SaveToClass(), the step the
Portal performs, so the class's XData ProductionDefinition kept the old item list.
SaveToClass appeared 0 times in crates/ (control: iris_production_item appeared 66).
Measured on a disposable IRIS, staleness turns out to be the mild half. The XData is
what a COMPILE of the production class replays into the extent:
after Items.Insert + %Save() extent [Added, First, Ghost] XData 2 items
recompile the stale class extent [Added, First] XData 2 items
Zz408.Ghost is gone. No error, and the tool that added it already reported
success: true. So any later iris_compile of a production class silently undoes an
iris_production_item mutation — and the skills plugin's drift guidance ("iris_doc get
the production class and write it to src/") writes that stale class to disk first,
making it authoritative.
A failed class save must not fail the action: by the time it runs the live change has
landed, so reporting failure would tell the caller to retry a mutation that already
happened. Swallowing it is the same defect in new clothes. Third state — the action
reports OK with class_saved: false, the reason, and a warning naming the consequence
(a recompile reverts this) and the way out. The marker rides on its own line AFTER the
OK line, because a %Status chain's text is multi-line and a marker written first would
displace the production name, which is the truncation #347 fixed elsewhere.
enable/disable's codegen is extracted into build_set_enabled_code. It was the one
mutating action whose code was an inline format! rather than a named function, so it
was the one a parity assertion over the builders structurally could not see — and
#409, in this same file, was exactly a guard one sibling had and the other did not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7fLbq3ftb82ub45QCpsPB
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 #408.
add,remove,set_settings,enableanddisableall ended intProd.%Save(), which writes theextent (
Ens_Config.Item) — the live configuration — and then optionallyEns.Director.UpdateProductionto apply it. None calledSaveToClass(), the step the ManagementPortal performs, so the class's
XData ProductionDefinitionkept the old item list.SaveToClassappeared 0 times in
crates/(control:iris_production_itemappeared 66, so the search works).Staleness turned out to be the mild half
Reproduced on a disposable IRIS. The
XDatablock is what a compile of the production classreplays into the extent, so the stale class is not merely out of date — it is authoritative the moment
anyone compiles it:
Zz408.Ghostis gone. No error, no warning, and the tool that added it had already reportedsuccess: true.So the impact is worse than the issue states. It is not only that the plugin's
[IIS-DRIFT]guidancewrites a stale class to
src/— any lateriris_compileof a production class silently undoes aniris_production_itemmutation, and compiling a production class is a normal step in this workflow.The fix, on the same object, in the same run:
Ens_Config.Item(live)XData ProductionDefinitionItems.Insert+%Save()SaveToClass()The third row is also the control the second needs: the same reader that reported 1 reports 2
immediately afterwards, so "the XData still has one item" is a measurement rather than a broken
instrument.
Measured API facts
An instance method with an optional single item, so it is called on the object the codegen already
has open — no re-open. It returned success, and left the extent untouched. It is emitted after
%Save()succeeded and beforeUpdateProduction, which is the Portal's order.A failed class save is a third state, not a failure
By the time
SaveToClassruns,%Save()has succeeded and the change may already be live. Reportingfailure would tell the caller to retry a mutation that has happened. Swallowing it is the same defect
in new clothes — this repo's recurring shape. So the action still reports OK and carries
class_saved: false,class_error, and awarningthat names the consequence (a recompile revertsthis) and the trap: the obvious next step,
iris_doc(mode=get)then writing the class tosrc/,is exactly what makes the stale class authoritative.
The
CLASS_NOT_SAVEDmarker rides on its own line after the OK line. A%Statuschain's text ismulti-line; a marker written first would push the OK line past where the parser looks, which is the
truncation #347 fixed elsewhere. With it last, an arbitrarily long reason cannot corrupt the
production name.
read_class_save_markerreturnsNoneonly when the class WAS saved, and an emptyreason still returns
Some— decaying toNonewould report a class that was not saved as saved.build_set_enabled_codeenable/disablewas the one mutating action whose codegen was an inlineformat!rather than anamed function, so it was the one a parity assertion over the builders structurally could not see. It
is now a named function like its four siblings, and
every_mutating_action_saves_the_production_classiterates all five. #409, in this same file, wasexactly a guard that one sibling had and the other did not; five arms is five chances to repeat it.
Verification
--checkrc=0cargo clippy --workspace --all-targets -- -D warningsrc=0cargo build --workspacerc=0cargo test --workspacewith the IRIS env unset rc=0 — 1855 passed, 0 failed, 69 ignored across66 binaries (66
test result:lines, so the total is a measurement and not an empty grep)Mutation check: 15 mutants over the 13 assertions, 15/15 killed by the predicted assertion, file
restored byte-identical (sha checked) and the post-restore baseline green. Two of the fifteen are the
ones worth naming, because they are what a weaker test would have missed:
%Statusdiscarded. Acontains("tProd.SaveToClass()")assertionpasses here while every failure is lost — the class stays stale and the envelope still says
class_saved: true. The assertion also requires$$$ISERR(tSCC), so the verdict must be examined.This was added before the first run, from the lesson in fix(iris_interop_query): pin search_table to the extent's document class #412: its parity assertion passed with its
subject deleted, because it matched a string that also appeared in the projection.
the_class_verdict_is_reported_after_the_ok_line, which asserts position rather than presence.The new assertions live in their own file rather than being appended to the
#[cfg(test)] modininterop.rs: measured over 13 PRs in this repo, every cross-branch conflict was an appended testmodule, and several open PRs touch this file.
Scope note
The e2e tests that would exercise this against a live production are
ignoredwithout IRIS env set,per the gate. The live evidence above was gathered by driving
Ens.Config.Productiondirectly on adisposable instance rather than through the tool, so what is proven is the mechanism and the API
contract, not the assembled tool path. The five codegen sites and the four response arms are covered
by the unit assertions.
🤖 Generated with Claude Code
https://claude.ai/code/session_01N7fLbq3ftb82ub45QCpsPB