fix(iris_production_item): add/enable/disable/set_settings could not find items of a production that never ran - #381
Open
PYDuquesnoy wants to merge 4 commits into
Open
PYDuquesnoy wants to merge 4 commits into
PYDuquesnoy wants to merge 4 commits into
Conversation
… trip Item 3 of issue 327. Measured over 31,009 parsed tool_use blocks: the tool arrives in bursts (53 runs of 2, 14 of 3, up to 8) and get_settings is 296 of the 513 action invocations inside them — the bursts are mostly reading several items' settings one at a time. 174 calls go away. ## One program, whatever the count `get_settings` now builds one framed program for one item or twenty. Not two paths: a second copy of the read is the #105 shape, where the copy nobody exercises keeps the old behaviour and looks more trustworthy for having a working sibling. The RESPONSE shape still differs — a call that passes no list reports exactly what it always did, so no existing caller changes. ## The framing, and why it is not just newline-separated The single-item program wrote bare `Name=Value` lines. With more than one item those cannot be attributed, and a value is free text. Read from a live production for the fixtures (`Comun.Produccion`, IRIS 2026.1, read-only over Atelier — 10 items, 65 settings): * `ReplyCodeActions` is `:?R=F,:?E=F,:~=S,:?A=C,:*=S,:I?=W,:T?=C`, so splitting on every `=` truncates a real setting at `:?R`. * 23 of the 65 settings are Adapter-targeted, and `Setting.Target` is not in the output. So each section declares how many settings to expect — the mechanism #347 used to make a short `%Status` chain detectable — and a line matching no marker is appended to the previous value rather than dropped. ## What a caller can now tell apart * `Missing` — the production holds no such item. * `Unreadable` — the section carried neither a count nor a marker. Neither of these is an item with no settings; reporting them that way answers a failure with a fact (#310). * `settings_incomplete` — fewer (or more) settings arrived than IRIS declared: the output was cut. * `duplicate_setting_names` — the rows that DID arrive collapsed in the map, because the program drops `Setting.Target`. Pre-existing, now counted instead of silent. `success` stays about the CALL; `all_found` and `missing` are about the answer, and a single short section is enough to make `all_found` false. Every name absent returns the same ITEM_NOT_FOUND envelope a single-item call has always given, built by the one reader that has always built it. ## No Quit in the missing branch Measured on IRIS 2026.1: a `Quit` inside an `If` block returns from the METHOD. One missing item would have ended the run, and every later item would have been absent from the output — silently, since absence is how a caller reads "no settings". That is why `item_not_found_block`, which ends in `Quit` by design, is not reused per item; its candidate block is emitted once. ## A defect this change had to fix in the existing guard `ACTIONS_NEEDING_AN_ITEM`'s refusal read `params.item` alone. A caller passing only `items: ["A","B"]` — the shape the new parameter invites — was told "nothing was sent to FindItemByConfigName" while having named two things: a true sentence about the wrong parameter, which is the failure mode this issue is about. The guard now asks whether ANY spelling named something, and a list of exactly one name is filled into `item` so every action accepts it. Only `get_settings` reads a list of several. On every other action a list of more than one is refused by name rather than acted on for the first and dropped for the rest. ## Verification fmt, clippy -D warnings, cargo build --workspace, and the gate with IRIS env unset: 5 result lines, 0 FAILED. 26 new tests, fixtures read from a live production rather than invented. 14 mutations applied one at a time — including "only the first item is written into the program", "the missing branch Quits", "the value is split on every equals sign" and "the handler reads `item` alone again" — each red on its named tests, restored green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l spelling
CI caught this: `all_four_sites_and_all_four_readers_use_the_shared_definitions` went red on the
previous commit, and it was right to. That test is the sibling-defect guard for ITEM_NOT_FOUND,
and the batch read changed the POPULATION it counts.
`get_settings` no longer interpolates `{not_found}`. It cannot: the shared block ends in `Quit`,
which returns from the METHOD, so one missing item would end the whole program and every later
item would be absent from the output. It emits the same declared-count candidate protocol
itself, once, after the last item.
So the instrument now counts what the claim is about — every generator that can report a missing
item emits a declared candidate count, and every reader routes through the one envelope builder —
rather than one particular call spelling. Both halves are still 4; the fourth member changed.
The reader count needed a real counter, not a substring match. `text.matches("item_not_found(")`
reads 5, because `interop.rs` has a test called `production_item_error_mapping_item_not_found` —
an identifier that merely ENDS with the name, which is the commonest false witness in a
source-reading guard. `call_sites` skips declarations and requires a non-identifier character
before the name, with three controls: a declaration counts 0, that test's name counts 0, and a
real call counts 1 — so the two negatives cannot be satisfied by counting nothing at all.
Also the reason this reached CI at all: the local gate ran the targets I expected to matter
rather than the targets that read what the change touched. The target list is now derived —
grep the tests directory for the symbols and files the diff moves — which finds this one and
nine others.
Mutations, both red on the guard and restored: get_settings stops emitting the protocol inline;
the batch reader stops routing through the shared envelope builder.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… that never ran
Ran the batch read against a real production for the first time. Every get_settings call — the new
batch path AND the pre-existing single-item path — reported ITEM_NOT_FOUND for items the production
demonstrably has. The refusal even contradicted itself:
Item not found: Probe.BS.Feed
The production has 3 item(s): Probe.BS.Feed, Probe.BO.Out, Probe.BO.NoSettings
## Measured, on IRIS 2026.1 against a configured-but-never-started production
%OpenId("IOProbe.Produccion") -> object, no error
tProd.Items.Count() -> 3, all three names present
tProd.FindItemByConfigName("Probe.BS.Feed") -> NO object, ERROR #00: (no error description)
^Ens.Runtime("DispatchName") -> does not exist, 0 entries
Ens.Director.GetProductionStatus -> running production '', state 2
walking tProd.Items and matching .Name -> resolves all three, settings and Targets intact
FindItemByConfigName resolves through the RUNTIME dispatch index, which is only populated once a
production has been started or updated. This fork's whole workflow is to configure items and then run
tests, so the items of a production being BUILT are exactly the ones it cannot find — and the %Status
it sets carries no text, so there was nothing to report.
The batch program now walks tProd.Items and matches .Name, which is config-level, needs no runtime
index, and is what build_list_items_code already did successfully.
NOT FIXED HERE: enable, disable, set_settings and add use the same call and fail the same way on a
never-started production. Filed separately rather than widened into this PR.
## Two more defects the live run found
* The wire marker leaked. `synthesise_not_found_payload` built "ERROR:ITEM_NOT_FOUND:Item not found:
…", and `item_not_found` takes its message from line 0 — so the caller's `error` began with the
marker. The single-item arm strips it before handing the payload over; the synthesised one must not
add it back. Same leak #358 fixed for INTEROP_ERROR.
* The response SHAPE regressed for every existing caller. `items` non-empty selects the batch
payload, and `item_names_arg` folds a single `item` into that same vector — so a plain `item=` call
came back as `results[]` instead of `{item, settings}`. Now keyed on `list_parameter_given`, which
asks whether a LIST key was supplied. The pure-function test could not see this: it sets the flag
by hand, and the WIRING computed it wrongly.
## One mutation survived, and it was the actual defect
Reverting the codegen to FindItemByConfigName passed all 25 tests in the file, because they feed the
PARSER hand-written marker text and cannot see the ObjectScript — the third time this week that
blindness hid a wrong premise about what IRIS does. The new program-level assertion is red against
it.
Its first control was also wrong — an exact count of GetAt references, which failed on correct code
because the trailing candidate block has one of its own. Replaced with a count of per-item walks.
Verified end to end after the fix: the 3-item batch returns 4, 3 and 0 settings with all_found true;
one-real-one-missing gives all_found false with the miss named; all-missing gives ITEM_NOT_FOUND with
no wire marker; `item=` returns the legacy shape and `items=[one]` the list shape.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…find items of a production that never ran Issue 379. The same defect #372 fixed for get_settings, at the three remaining ObjectScript sites — and one of them fails in the opposite, worse direction. ## Measured, driven through the real tool IRIS 2026.1, a production configured, compiled and never started: tProd.Items.Count() -> 1, the name present tProd.FindItemByConfigName("Dup.BO.Target") -> NO object, ERROR #00: (no error description) ^Ens.Runtime("DispatchName") -> does not exist, 0 entries enable on an item that exists -> ITEM_NOT_FOUND, and the message then lists that item set_settings on an item that exists -> the same add of an item that ALREADY exists -> success: true, and Ens_Config.Item then held TWO rows That last line is the reason all three move together. The duplicate guard is an EXISTENCE test, so the same broken lookup fails in the OPPOSITE direction: finding nothing reads as "no duplicate" and the write proceeds. It corrupts the production config rather than refusing, and a fix applied to two of the three would leave it looking more trustworthy than it is. FindItemByConfigName resolves through the runtime dispatch index, which is only populated once a production has been started or updated. This fork's workflow is to configure items and then run tests, so the items of a production being BUILT are exactly the ones it cannot find. ## The fix All three inline the same config walk, and ITEM_WALK_MARKER names it so a test can prove they all do and none has drifted. `remove` already walked the config correctly — that pattern pre-existed; these three simply did not use it. After the fix, against the same never-started production: disable succeeds, set_settings succeeds, add REFUSES with ITEM_EXISTS and the item count stays 1, and enable on a genuinely absent item still reports ITEM_NOT_FOUND. Positive and negative control both. Also corrected: the MISSING_PARAMETER text said "nothing was sent to FindItemByConfigName", which named a method nothing calls any more. ## One mutation survived twice, on a false witness each time Changing the walk's comparison to `.Name=""` — which makes it match nothing, reproducing the defect — survived two versions of the assertion: 1. `code.contains(".Name=")` is still true for `.Name=""`. 2. `code.contains(".Name=\"My.Item\"")` is satisfied by `add`'s own `Set tItem.Name="My.Item"`, the line that CREATES the item. The needle matched an assignment, not the comparison. It is now asserted on the walk LINE, one per item asked for. Red at the third attempt. The population check needed narrowing too: it flagged the caller-facing message that mentions the method in prose. `tProd.` is what makes an occurrence a call. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PYDuquesnoy
changed the base branch from
feat/production-item-batch-settings
to
master
September 28, 2026 09:14
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 #379
(Link added after the fact: this PR implements #379 — it replaces
FindItemByConfigNamewith an explicitFor zfi=1:1:tProd.Items.Count()walk in the four arms that issue names, anditem_lookup_without_runtime_index.rscovers it — but nothing in the PR referenced the issue, so merging would have left it open. Verified against the diff before linking.)Issue 379. The same defect #372 fixed for get_settings, at the three remaining ObjectScript sites —
and one of them fails in the opposite, worse direction.
Measured, driven through the real tool
IRIS 2026.1, a production configured, compiled and never started:
tProd.Items.Count() -> 1, the name present
tProd.FindItemByConfigName("Dup.BO.Target") -> NO object, ERROR #00: (no error description)
^Ens.Runtime("DispatchName") -> does not exist, 0 entries
enable on an item that exists -> ITEM_NOT_FOUND, and the message then lists that item
set_settings on an item that exists -> the same
add of an item that ALREADY exists -> success: true, and Ens_Config.Item then held TWO rows
That last line is the reason all three move together. The duplicate guard is an EXISTENCE test, so
the same broken lookup fails in the OPPOSITE direction: finding nothing reads as "no duplicate" and
the write proceeds. It corrupts the production config rather than refusing, and a fix applied to two
of the three would leave it looking more trustworthy than it is.
FindItemByConfigName resolves through the runtime dispatch index, which is only populated once a
production has been started or updated. This fork's workflow is to configure items and then run
tests, so the items of a production being BUILT are exactly the ones it cannot find.
The fix
All three inline the same config walk, and ITEM_WALK_MARKER names it so a test can prove they all do
and none has drifted.
removealready walked the config correctly — that pattern pre-existed; thesethree simply did not use it.
After the fix, against the same never-started production: disable succeeds, set_settings succeeds,
add REFUSES with ITEM_EXISTS and the item count stays 1, and enable on a genuinely absent item still
reports ITEM_NOT_FOUND. Positive and negative control both.
Also corrected: the MISSING_PARAMETER text said "nothing was sent to FindItemByConfigName", which
named a method nothing calls any more.
One mutation survived twice, on a false witness each time
Changing the walk's comparison to
.Name=""— which makes it match nothing, reproducing the defect —survived two versions of the assertion:
code.contains(".Name=")is still true for.Name="".code.contains(".Name=\"My.Item\"")is satisfied byadd's ownSet tItem.Name="My.Item", theline that CREATES the item. The needle matched an assignment, not the comparison.
It is now asserted on the walk LINE, one per item asked for. Red at the third attempt.
The population check needed narrowing too: it flagged the caller-facing message that mentions the
method in prose.
tProd.is what makes an occurrence a call.Co-Authored-By: Claude Opus 5 noreply@anthropic.com