Skip to content

fix: make the soak fixture able to start the engine at all - #89

Merged
test1card merged 25 commits into
masterfrom
fix/soak-fixture-can-start-the-engine
Aug 20, 2026
Merged

test1card merged 25 commits into
masterfrom
fix/soak-fixture-can-start-the-engine

Conversation

@test1card

Copy link
Copy Markdown
Owner

The result first

With this branch, on the laboratory machine, the engine runs and stores data:

ZMQPublisher остановлен (отправлено: 154)
SQLiteWriter stopped (written: 120)
Engine shutdown owner settled: owner=sqlite_writer
═══ CryoDAQ Engine завершён ═══ | uptime=30.1 с

Thirty seconds of acquisition, 120 rows written, and a clean layered shutdown with every owner settled. Before this branch the engine did not start at all under the soak fixture.

Measured on Ubuntu 22.04.5, in a worktree cut from the native Linux clone, by running the engine alone against a freshly materialised isolated config — because the launcher discards engine stderr, so the reasons below were otherwise kept nowhere (evidence/tools/engine_probe.sh).

Three fixture defects, each hidden behind the previous one

1. critical_channels held a pattern where identities are required.

Dead safety/alarm channel pattern(s): 1 match NO channel ... pattern='.*'

_resolve_critical_bindings does if channel_id not in storage_catalog.by_channel_id, so the literal string .* is a channel nobody has. The F-1 silent-safety-kill guard was doing exactly its job.

2. No declaration could fix it, because the fixture had nothing safety-critical. Declaring the whole roster moved the refusal to the next plane — a declared identity must ALSO be classified SAFETY_CRITICAL_INPUT by its own descriptor, with a role other than SOURCE_READBACK. All sixteen of LS218_1's descriptors are observational. And the empty list is refused outright: "refusing to start SafetyManager without critical channel monitoring".

So the fixture is switched to LS218_2: the same driver lakeshore_218s, the same passive measurement authority, the same reviewed cardinality of sixteen descriptors and sixteen bindings, and it carries two safety_critical_input channels. Nothing reviewed is loosened and no classification is fabricated — the fixture simply uses the tracked instrument that has what the startup path requires. The declaration itself is now derived from the descriptors, so a safety-critical channel added later is declared automatically rather than silently unmonitored.

3. The physical-alarms document was a four-line string where a complete one is required.

PhysicalAlarmsConfigError: physical alarms document must contain exactly cooldown, vacuum, landmarks

The loader wants exactly those three keys, complete key sets in the first two, and the two canonical landmark channels with non-empty alias lists in the third. The tracked document is taken instead and only the three arming flags are overridden, so the soak stays passive while the document stays the reviewed one.

A detail worth pinning: the loader requires landmarks to be exactly the two canonical channels that LS218_2 carries as safety-critical. The startup path and the fixture's instrument now agree, which is the same finding arriving from a second direction.

What this does not claim

The short soak still fails at the four-role startup cut. That is now about the launcher and bridge orchestration, not about the engine — the engine phase is past. Each barrier here was only visible once the one in front of it was gone, so the next one is the next measurement, not a prediction.

It also does not claim anything about the lab's real configuration. This is the soak's own fixture, which had apparently never started the engine.

Evidence

tests/scripts: 132 passed, 94 skipped. ruff check src/ tests/ clean; the workflow-exact format check clean over 702 changed files. The three new tests fail against master.

Depends on #87 and #86 to reach this far; measured with all three merged locally and never pushed (evidence/tools/combine_and_soak.sh).


Written with assistance from Claude (Anthropic).

The engine refuses to start under the soak fixture, and the refusal is correct.
Its boot-time safety liveness check reports

  Dead safety/alarm channel pattern(s): 1 match NO channel on the plane their
  consumer sees (F-1 silent safety kill).
    - pattern='.*' source=safety.yaml critical_channels

`critical_channels` entries are EXACT canonical identities, never patterns:
_resolve_critical_bindings does `if channel_id not in storage_catalog.by_channel_id`.
The fixture declared the literal string ".*", meaning "everything", and no channel
has that identity, so the guard against a silently dead safety reference did
exactly what it exists to do.

The fixture's safety config is therefore derived from the roster the same call
just wrote, instead of being a static string that cannot know what channels
exist. The intent is unchanged -- every channel is critical -- and it can no
longer drift from the descriptors.

Measured on Ubuntu 22.04.5 by running the engine alone against a freshly
materialised isolated config, because the launcher discards engine stderr and
the reason was otherwise kept nowhere. Before: CONFIG ERROR at the safety
liveness check. The new test asserts every declared identity is on the roster,
that the set is the whole roster, and that no entry carries a wildcard; it fails
against master.

tests/scripts: 131 passed, 94 skipped.
…fication too

Declaring the whole roster moved the refusal from one plane to the next rather
than clearing it. A declared identity must ALSO be classified safety-critical by
its own descriptor: the check requires safety_class is SAFETY_CRITICAL_INPUT and
a role that is not SOURCE_READBACK.

Measured: all sixteen descriptors of the passive fixture are observational, nine
reference measurements and seven primary ones. There is no safety-critical input
in this fixture, so the honest declaration is the empty one.

It is derived rather than assumed. If a safety-critical descriptor is ever added
to the passive instrument, it is declared automatically, and the fixture cannot
quietly stop monitoring something it should monitor.
The test now asserts the rule rather than a number: every declared identity is
on the roster, none carries a wildcard, and the set is exactly the descriptors
classified safety_critical_input with a role other than source_readback. For
this fixture that set is empty, and the test says why rather than hard-coding
the emptiness, so adding a safety-critical descriptor later is a change the test
follows instead of a change it blocks.
The fixture could not start the engine at all, and no edit to safety.yaml could
change that. The engine refuses to start a SafetyManager with no critical
channel, and refuses again when a declared channel is not classified
safety-critical by its own descriptor. Every one of LS218_1's sixteen
descriptors is observational, so the three candidate declarations each failed a
different way: the literal ".*" is an identity nobody has; the whole roster is
not safety-critical; the empty list is refused outright.

LS218_2 is the same driver, lakeshore_218s, the same passive measurement
authority, and the same reviewed cardinality of sixteen descriptors and sixteen
bindings, and it carries two safety_critical_input channels. Nothing reviewed is
loosened and no classification is fabricated. The fixture simply uses the tracked
instrument that has what the startup path requires, which also means the soak now
exercises the path production takes rather than one production never takes.

Measured on Ubuntu 22.04.5 by running the engine alone against a freshly
materialised isolated config, because the launcher discards engine stderr.
…rm it

The fixture wrote a four-line static string, and the production loader refused
it: it requires exactly cooldown, vacuum and landmarks, complete key sets in the
first two, and the two canonical landmark channels with non-empty alias lists in
the third. The engine stopped at

  PhysicalAlarmsConfigError: physical alarms document must contain exactly
  cooldown, vacuum, landmarks

A short document cannot satisfy that, and inventing a long one would put a
document the loader has never reviewed in front of it. The tracked document is
taken instead and only the three arming flags are overridden, so the soak stays
passive while the document stays the reviewed one.

Worth noting what the loader asks for: landmarks must be exactly the two
canonical channels that LS218_2 carries as safety-critical inputs. The startup
path and the fixture's instrument now agree, which is the same finding from a
second direction.

tests/scripts: 132 passed, 94 skipped.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

# Conflicts:
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is 714e833382e4bba43e499ab884c799c22905f637. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Review found a false statement presented as true in tracked code, and it was
right. Two comments said every descriptor in this fixture is observational and
the honest declaration is the empty one. That was true while the fixture used
LS218_1 and stopped being true in the same change that switched it to LS218_2,
which carries two safety_critical_input channels. The file's own header comment
says so three hundred lines earlier, so the tree contradicted itself.

Measured rather than reasoned: the materialiser writes
critical_channels: [Т11, Т12].

The code and the test were already correct -- the test derives its expectation
from the descriptors, so it would have failed on an empty declaration. Only the
prose was wrong, and prose that a maintainer would act on is not a small thing:
believing the soak monitors nothing safety-critical invites someone to force the
list empty, which is exactly what the engine refuses to start on.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is 7248ab358f8b9701c9cf10022bd83565eca28d97. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

These tests and the theme-pack tests were added to the same module on different
branches, and the two branches then conflicted there every time they met. That
contention is created by WHERE the tests were put, not by what they assert, and
it blocked the combined measurement that shows what the soak does with both
changes present.

Splitting costs nothing. The new module states what it covers: the chain of
refusals the engine raises before it will run -- an exact canonical identity in
the critical-channel list, a descriptor that classifies that identity
safety-critical, and a physical-alarms document the production loader accepts
whole.

tests/scripts: 135 passed, 94 skipped.
# Conflicts:
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is 27148f35c607fa8c2b289f90fbad59a1a90845ee. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

The protected evidence gate refuses evidence from any run attempt after the first, so a cancelled round cannot be repaired by re-running it. This commit changes no content and exists only to produce the fresh run the gate requires.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is 75ea921c89e9bfd8b6c4c20abcc31610d8d9b6e6. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

…a literal

Review found that switching the fixture to LS218_2 left the acceptance validator
hard-coding LS218_1, so the soak would have run its full duration and then failed
its own verdict step with "source fixture semantics or tree seal is invalid".
Every manifest test stayed green because they all hand-build the payload, so the
failure would have arrived as a red run with a green suite.

The literal is gone from both sides. The validator reads the runner's constant
through the lazy import this module already uses in three other places, and the
test fixture reads it too, so there is no second place left to disagree.

This is the same shape as the topology drift found on 86 one round earlier: the
seal and the validator are two halves of one contract, and every test that
hand-builds the payload is a test that cannot see them separate.

tests/scripts: 138 passed, 94 skipped.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is eae1c4ef983eeddb5bbfff1f945005e1d8322700. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@test1card

Copy link
Copy Markdown
Owner Author

The red Linux job on this pull request is not this pull request's doing

test (ubuntu-latest, gui) failed here on test_auto_tick_advances_with_slow_acquisition_cadence. I opened the candidate artifact rather than reading the test name, and the assertion is:

tests/gui/shell/overlays/test_conductivity_panel.py:1060: in test_auto_tick_advances_with_slow_acquisition_cadence
    assert pred is not None and pred.valid
E   AssertionError: the real predictor must yield a valid prediction for a 30-second
    cadence inside its cadence-derived window
E   assert (SteadyStatePrediction(channel='Т1', ..., valid=False) is not None and False)
==== 1 failed, 1879 passed, 2 skipped, 252 deselected in 1073.54s (0:17:53) ====

Three facts place it outside this branch:

  1. This branch does not touch that code. Its whole diff is scripts/soak_mock_stack.py, scripts/soak_mock_stack_runner.py, three test modules under tests/scripts/, and the derived documentation pair. Neither conductivity_panel.py nor its test file is in it.
  2. The test and the code it exercises are both on master, unchanged by this branch.
  3. Master's own Linux gui job was green on this test on 2026-08-19 (run 32221761011, head db558451), and master's later red (run 32253794721) was remaining, a different partition.

Why it fails at random on Linux, measured

The window is max(300, cadence × 30), so a 30-second cadence gives exactly 900 s. The test feeds 30 points spaced 30 s apart, spanning 870 s, and the predictor refuses below 30 points. That leaves exactly 30 seconds of slack between the last sample being stamped and the tick that reads the prediction. Lose one point to pruning and the count is 29 and the prediction is invalid.

Measured against the predictor directly, no GUI involved (evidence/tools/predictor_headroom_probe.py):

window tick 0 s late 15 s 45 s 90 s
900 s (master today) valid valid invalid invalid
930 s (#90) valid valid valid invalid

So on a loaded hosted runner — this suite took 17 minutes 53 seconds in that job — the tick arriving more than about half a minute late is enough, and that is a property of the machine's load, not of any branch.

#90 is the fix for this, and it is worth saying exactly what it does and does not do: it widens the tolerance by one cadence, from about 30 seconds to about 60. It does not remove the knife edge; it moves it. That is still the right change, because the margin today is zero by construction.

Nothing to do on this branch. It needs a fresh round once #92 unblocks the evidence gate.

# Conflicts:
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is d8d2430e27d547254ac82833cf1e7de5b9a73377. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

# Conflicts:
#	docs/CLAIM_CORRECTIONS.md
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is c2cc1b894ba30068561a1ff7c07fdef1f7660cfd. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

… by a near miss

The reviewer found that the fixture's filter does not match the production gate
it exists to satisfy, and that the test restated the same filter, so it could
never detect the difference. Both are true and both were verified here against
the production code rather than taken on trust.

The engine builds its critical manifest from descriptors whose quantity is
temperature and whose safety class is safety critical input, and refuses when
the declared set is not exactly that set. It applies no role test.

The fixture tested safety class and excluded the source readback role. That role
test is a no operation: the descriptor validator refuses any source readback
descriptor without the hazardous source readback class, so a channel can never be
both source readback and safety critical input. And the quantity test, the one
that decides, was missing.

Measured on the tracked descriptors: both of the instrument's safety critical
descriptors are temperature and primary measurement, so the two filters agree
today and the role test changes nothing. The risk is drift. A safety critical
descriptor of another quantity added later would be over declared, the engine's
union check would fire, and the engine would refuse to start, while the fixture's
own test still passed.

The rule now lives in one named place, copied from production and citing it. The
assertion states the engine's rule instead of the fixture's, and a second test
exercises the case the first could never reach, with a synthetic descriptor set
where a pressure channel and a raw sensor channel are both classified safety
critical.

Putting the old filter back makes that second test fail, which was confirmed by
doing it and restoring the file.
The workflow-exact format check refused the previous commit for both files. The
line length allows the joined form. The formatter was never run in mutating mode
over the reviewed tree; its diff was read and applied by hand, which is what the
registered guard requires.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head is ce071eff54449a7c6ff411b5d334d0ac0a2426b7. A lane closed the live review findings; the coordinator ran the landing gates on this tree -- byte-order-mark, encoding and parse checks on every changed file, a refusal on any tree that deletes more than it adds, ruff check and ruff format --check on the changed Python, the derived documentation pair regenerated to a fixed point, and the documentation gate green.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@test1card
test1card merged commit 21e89af into master Aug 20, 2026
28 checks passed
@test1card
test1card deleted the fix/soak-fixture-can-start-the-engine branch August 20, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant