Skip to content

fix: replay channel identity into a lazily built conductivity panel - #118

Draft
test1card wants to merge 43 commits into
codex/thermal-autosweep-persistence-20260827from
fix/conductivity-panel-learns-channel-identity
Draft

test1card wants to merge 43 commits into
codex/thermal-autosweep-persistence-20260827from
fix/conductivity-panel-learns-channel-identity

Conversation

@test1card

Copy link
Copy Markdown
Owner

The operator pressed Start on the automatic sweep and nothing happened, and
nothing was said.
The end-to-end thermal run reached that point and stopped.

Stacked on #110 — it depends on the identity guard that branch introduces.

What was wrong

The conductivity panel is built only when first opened. The authoritative
descriptor-qualified readings arrive before that. The shell forwards readings
only when the panel already exists, and lazy construction replays connection
state but not those readings. So the panel came up knowing no channel
identities, its Start button was enabled anyway, and the sweep's identity guard
refused.

The guard is right and is untouched. An automatic sweep must not persist
readings whose channel identity is unconfirmed — that is the data-misrepresented
failure this project exists to prevent. What was missing is the replay.

This is the same defect already repaired for the source panel in #111, one panel
over: a lazily opened panel missing state that was published once, before it
existed.

Freshness is the hard half

A snapshot from a superseded bridge instance or experiment generation is not
replayed. Handing the panel an identity it has no right to trust would be the
same defect wearing the opposite coat. The regression for that is the control
on this change rather than a demonstration of it — if it did not fail without
the change, the freshness claim would be unproven.

The four silent controls

Pressing Start with no Engine connection, during an active sweep, after an
unknown command outcome, or while a power command was in flight each produced
silence. Each now says what is wrong and what to do:

  • «Автоизмерение не запущено: нет связи с Engine. Дождитесь восстановления связи.»
  • «Автоизмерение уже выполняется. Дождитесь завершения или нажмите «Стоп».»
  • «Автоизмерение не запущено: исход предыдущей команды неизвестен. Нажмите «Стоп» для проверки отключения.»
  • «Автоизмерение не запущено: выполняется команда управления мощностью. Дождитесь подтверждения.»

A control that neither acts nor explains is worse than one that refuses out loud.

Verification

Reversion control, run independently by the coordinator: 8 nodes red without
the production change, 113 green with it.
The lane additionally showed that
selectively weakening the missing-descriptor refusal makes its guard test fail,
which is what proves the guard still has teeth.

No existing test was modified — assertions added 21, deleted 0.

An inherited red, stated deliberately

test_release_whole_tree_artifact_gate_is_reachable_and_pr_excluded fails on
this branch. It is not caused by this change — it fails identically on the
#110 base this is stacked on, where a governance receipt binds a guard file #110
edited. A separate lane is re-deriving that receipt on #110, and it must
re-reproduce the recorded red rather than copy a hash across. This branch goes
green when that lands and this rebases.

What would make accepting this red wrong: if the receipt turns out to be
invalidated by this branch rather than by #110. It is not — this branch
touches neither the governance registry nor the guard file the receipt binds.

Not determined

  • The socket acceptance probe (the lane's sandbox denied socket creation) and the
    coordinator's end-to-end run in the merged configuration.
  • The full conductivity panel module: it reaches a real ZMQ bind test that the
    sandbox refuses, so no whole-file green is claimed.

Disclosure

Written by an AI lane (gpt-5.6-sol) under an agent brief; root-caused by a
separate diagnosis lane; verified and landed by the coordinating agent. An
earlier attempt at this task was briefed against master by coordinator error and
correctly refused to edit anything, because master contains no identity guard at
all and adding replay there would have let a sweep start unguarded.

soak measurement and others added 30 commits August 26, 2026 01:44
The three findings on this pull request all said the same thing in different
places: the evidence for the fix was weaker than it looked.

The end-of-file budget now starts before the kill. The two-second measurement
began only after the kill call and the process wait had both returned, so time
spent reaping, or descheduled between engine death and the helper, was excluded.
The pump could take longer than the promised two seconds while the assertion
still saw a short interval and passed. The clock now starts immediately before
the kill, in both the live and the replay case, so the budget covers the whole
interval the promise is about.

A guard now fails when the production block is removed. None of the registered
guards invoked the production isolation block in `engine.main()`. The live
simulant called the helpers by hand and the production-entry guard covered
replay only, so deleting that block left every listed guard green. A guard that
has only ever been seen passing is not enforcement. A dedicated guard is now
registered against that block, and it was proven by removing the block and
watching the guard go red, then restoring the block and watching it pass.

The coverage escape has its own prevention. The failure after the descriptor
duplication was a deterministic defect found after the original suite had gone
green. Folding its new guards into the existing record left no independent
false-green identifier or baseline entry for the escape, which the repository's
two-obligation rule requires. It now has its own record and baseline entry.

Written with AI assistance.
…r-20260827

# Conflicts:
#	docs/CLAIM_CORRECTIONS.md
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
#	governance/agent_preventions.yaml
#	governance/agent_preventions_baseline.json
…ation

Fix Ubuntu launcher stderr inheritance
…control

The owner ruled on 2026-08-28: "nothing should disable start button. source may
be turned off if operator forgot, or if there is misbehaving, but not disable
the button".

Measured before this change: the end-to-end thermal run timed out because the
Source panel's Start action never became available. The cause was not the panel
wiring but `detector_warmup` holding `stop_source` at 77.5 K against a 10 K
threshold, which made Safety not-ready, which greyed out Start and the P/V/I
spin boxes and showed "Управление заблокировано".

The defect was never that the guard fired. It is that a guard could reach the
button at all.

`_safety_ready` carried two different meanings that a boolean cannot separate:
Safety authority is present and reports a current condition, and no
authoritative Safety state exists at all. Only the first is a guard judging the
operator. The second is the software being blind, which is a different question
and is still open with the owner.

So the cause is now explicit, as `SafetyGateCause`, and the two are treated
differently:

  AUTHORITATIVE_NOT_READY  - a condition. Start and the parameter controls stay
                             available, and the panel shows a WARNING naming the
                             condition and saying it may stop a running source.
  AUTHORITY_UNAVAILABLE    - blindness. Everything except emergency-off stays
                             disabled, exactly as before.

Nothing suppresses the guard itself. A `stop_source` interlock still stops a
source that is running; the operator may start it, be told why that is
questionable, and be stopped by the guard if the condition holds. His remedy is
to disable that interlock, which is the sibling change.

The decision is structural, not textual: no code branches on the wording of a
Safety reason, because that text is operator-facing prose that will be reworded.
Landed by the batch lander. The lane's own report and the coordinator's
verification are recorded on the pull request.
soak measurement added 2 commits August 28, 2026 06:43
The operator pressed Start on the automatic sweep and nothing happened, and
nothing was said. The end-to-end thermal run reached that point and stopped
there.

The conductivity panel is built only when it is first opened. The authoritative
descriptor-qualified readings arrive before that. The shell forwards readings
only when the panel already exists, and lazy construction replays connection
state but not those readings -- so the panel came up knowing no channel
identities, its Start button was enabled anyway, and the sweep's identity guard
refused.

The guard is right and is untouched. An automatic sweep must not persist
readings whose channel identity is unconfirmed; that is the data-misrepresented
failure this project exists to prevent. What was missing is the replay.

This is the same defect already repaired for the source panel, one panel over:
a lazily opened panel missing state that was published once, before it existed.

The replay carries freshness with it. A snapshot from a superseded bridge
instance or experiment generation is not replayed, because handing the panel an
identity it has no right to trust would be the same defect wearing the opposite
coat. There is a regression for exactly that, and it is the control on this
change rather than a demonstration of it.

The second half is the four early returns that said nothing at all. Pressing
Start with no Engine connection, during an active sweep, after an unknown
command outcome, or while a power command is in flight each produced silence.
Each now states what is wrong and what the operator can do about it. A control
that neither acts nor explains is worse than one that refuses out loud.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head under review: 4862df2af49516feb1e09a219ce1e86a932b7fff. Please bind your
verdict to that exact SHA. This is stacked on #110, not on master.

The useful review here is adversarial about the freshness half, not the replay:

  1. Stale identity is the failure mode this could introduce. A replayed
    snapshot must belong to the CURRENT bridge instance and experiment
    generation. Please look for any path where a snapshot from a superseded
    connection can reach the panel — that would hand it an identity it has no
    right to trust, and it would look exactly like success.
  2. The guard must still refuse. _snapshot_auto_selection() is untouched by
    design. Confirm nothing here lets an automatic sweep start with unconfirmed
    channel identity, which is the data-misrepresented failure the whole project
    exists to prevent.
  3. The four new operator messages. They must name the condition and what to
    do, and must appear when Start is pressed rather than only as ambient state.
    Please check they cannot be shown for the wrong condition.
  4. Footprint in main_window_v2.py. Several branches edit that file. The
    change should be additive; flag anything that restructures shared wiring.

Stated openly rather than left to be found: one documentation node is RED on this
branch and it is inherited from the #110 base, where a governance receipt binds a
guard file #110 edited. It is not caused by this change — this branch touches
neither the governance registry nor that guard file — and a separate lane is
re-deriving the receipt on #110 by re-reproducing the recorded red rather than
copying a hash. If you think that reasoning is wrong, say so; I would rather be
corrected than have it pass quietly.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4862df2af4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1483 to +1485
if new_exp_id != previous_exp_id:
self._conductivity_snapshot.clear()
self._conductivity_snapshot_binding = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invalidate the open panel at experiment boundaries

When the conductivity overlay is lazily opened during experiment A, replay installs its descriptors into the panel's _latest_channel_descriptors; an A→B status transition here clears only the shell cache, leaving the connected panel's descriptor generation unchanged. _snapshot_auto_selection() therefore still accepts experiment A's replayed identities, so pressing Start can begin a sweep for experiment B before any B-qualified reading arrives. Revoke the open panel's descriptor authority at this boundary as well as clearing the cache.

AGENTS.md reference: AGENTS.md:L468-L469

Useful? React with 👍 / 👎.

Comment thread src/cryodaq/gui/shell/main_window_v2.py Outdated
Comment on lines +719 to +720
self._conductivity_snapshot.clear()
self._conductivity_snapshot_binding = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Revoke panel identity during transport invalidation

After a lazily opened panel has received the replayed descriptors, a watchdog restart calls this method but clears only the shell cache. The open panel remains connected with those descriptors in its current _auto_connection_generation until a later _tick_status() changes its connection state; if the replacement bridge starts during that interval, Start can snapshot the superseded bridge's identities and send the first target through the new bridge. Invalidate the open conductivity panel synchronously at the same transport boundary.

AGENTS.md reference: AGENTS.md:L468-L471

Useful? React with 👍 / 👎.

Comment on lines +2257 to +2259
if not self._connected:
self.show_warning("Автоизмерение не запущено: нет связи с Engine. Дождитесь восстановления связи.")
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Start clickable for refusal explanations

All four refusal states still disable _auto_start_btn through _update_control_enablement() (start_ok requires connected, inactive, known outcome, and no pending token), and a disabled Qt button does not emit clicked. Consequently an operator pressing Start cannot enter this handler and will see none of the four new explanations; the new tests miss this because they invoke _on_auto_start() directly rather than clicking the button. Keep a clickable refusal affordance or otherwise route attempted activation to these messages.

AGENTS.md reference: AGENTS.md:L540-L542

Useful? React with 👍 / 👎.

Comment on lines +2266 to +2269
if self._auto_pending_token is not None:
self.show_warning(
"Автоизмерение не запущено: выполняется команда управления мощностью. Дождитесь подтверждения."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Name non-power pending commands accurately

_auto_pending_token is also set while experiment_attach_run_record is pending, including the initial RUNNING attachment before any power target is sent and the terminal metadata attachment after OFF. In those states this branch tells the operator that a power-control command is running, so the newly added message is displayed for the wrong condition and obscures whether the sweep is waiting for experiment binding or result publication. Track the pending command kind or use wording that accurately covers every command represented by the token.

Useful? React with 👍 / 👎.

Comment on lines +649 to +652
binding = self._current_conductivity_snapshot_binding()
metadata = reading.metadata
if binding is None or type(metadata) is not dict or metadata.get("bridge_instance_id") != binding[0]:
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind each cached reading to its source experiment

This assigns the cache binding from the shell's latest experiment status rather than from evidence carried by the reading; the only source check is the bridge ID. Because experiment-status replies and the reading queue are drained independently, an experiment-A reading already in flight can arrive after the A→B status callback clears the cache, be stored under (current_bridge, B), and then pass the lazy-open equality check as if it belonged to B. Carry and verify the reading's experiment generation, or enforce an ingress epoch barrier that prevents pre-boundary readings from repopulating the new experiment's snapshot.

AGENTS.md reference: AGENTS.md:L468-L469

Useful? React with 👍 / 👎.

soak measurement and others added 9 commits August 28, 2026 07:12
…ranch

#111 and #114 both edit the shell's Keithley safety wiring, for different
reasons, and conflicted in main_window_v2.py. Resolved by intent rather than by
hunk, keeping both properties:

  #111 - the panel must LEARN the source's confirmed state when it is opened
         lazily, after the state was already published.
  #114 - a current, authoritative Safety condition WARNS and leaves Start
         available; a missing Safety authority still disables it.

They are not in tension: one is about what the panel knows, the other about what
it permits. But they touch adjacent lines, and taking either side wholesale
would have compiled, passed most tests, and silently deleted a property.

Each conflicted hunk kept both sides. Where #114 sets a typed gate cause on
invalidation, #111's typed snapshot reset is kept alongside it: taking #114
alone would have retained invalid typed replay evidence, and taking #111 alone
would have lost the explicit fail-closed cause reset.

The three properties are pinned by named nodes, all passing: a current
authoritative condition leaves Start enabled; missing Safety authority leaves
Start disabled; a lazily opened panel reaches confirmed OFF.
… file

The integration combines both branches' edits to
tests/gui/shell/overlays/test_conductivity_panel.py, so the blob the four
receipts bind moved again. No recorded guard node was touched by either side.

Each red was RE-RUN at the merge commit before its hash moved, with the merged
guard file in place and each receipt's own defective source blob restored:

  auto_evidence_authority        3 passed fixed / 3 failed defective
  channel_identity_false_green   1 passed fixed / 1 failed defective
  runtime_authority              1 passed fixed / 1 failed defective
  unusable_feed_false_green      4 passed fixed / 4 failed defective

Every guard still catches the defect it records. No hash was copied across.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head under review: 6e4f3263893bb601135802d5766cba9977173003. Please bind your verdict to that exact SHA.

The interlock-severing branch has been taken in by merge, and that branch
already carried the source-panel replay. So this head reconciles three sets of
edits to the same shell wiring, and the resolution is what deserves review.

The conflict ledger, in short: both replay helper families were kept. Their
lifetimes intentionally DIFFER, and that is the subtle part —

  • source state belongs to the engine/Safety producer and survives experiment
    changes;
  • conductivity identity is bound to BOTH the current bridge instance and the
    current experiment generation.

Please check that distinction is right. If conductivity identity survived an
experiment change, a sweep could start on an identity it has no right to trust.

Three properties must all hold, and I would rather you tried to break them than
confirmed them: a current authoritative Safety condition warns and leaves Start
available; a missing Safety authority still disables it; a lazily built panel —
either panel — learns what was published before it existed.

The four conductivity red-reproduction receipts were REBOUND, not bumped: the
merged guard file changed, so each recorded red was re-run at this merge commit
with its own defective source restored. All four fail against the defect and
pass against the fix. No hash was copied across.

The changed-Python count was re-derived at the merged index and read out of the
guard — the merge deliberately took one side of that document, discarding the
other's count, which the resolution ledger flagged for me to fix rather than
inherit.

Docs gate green at this head; derived pair regenerated as the last commit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e4f326389

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cryodaq/gui/shell/main_window_v2.py Outdated
# The negative observation revokes readiness, not the
# still-current bridge/experiment identity binding.
self._typed_safety_ready = False
self._last_safety_gate_cause = SafetyGateCause.AUTHORITATIVE_NOT_READY

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep legacy SAFE_OFF from restoring Start authority

At 6e4f3263893bb601135802d5766cba9977173003, when a current typed UNKNOWN or transport-unavailable snapshot has already left matching bridge/experiment bindings, a subsequent legacy SAFE_OFF telemetry packet enters this branch and changes the gate from AUTHORITY_UNAVAILABLE to AUTHORITATIVE_NOT_READY. The panel consequently re-enables Start despite the typed Safety authority still being unavailable; legacy telemetry should be allowed to revoke typed authority but must not restore warning-permissive control authority merely because the bindings match.

AGENTS.md reference: AGENTS.md:L20-L22

Useful? React with 👍 / 👎.

Comment thread src/cryodaq/gui/shell/main_window_v2.py Outdated
and not self._replay_mode
and cut.mode is SnapshotMode.LIVE
and experiment_matches
and (ready or cause is SafetyGateCause.AUTHORITY_UNAVAILABLE)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Revoke cached OFF when typed Safety reports an active source

When the best-effort source-state ON publication is dropped, a later current typed snapshot with lifecycle RUNNING is classified as AUTHORITATIVE_NOT_READY and is excluded by this condition. The previously cached OFF therefore remains displayed, and because the warning-permissive gate enables controls, Start remains available for a channel whose authoritative Safety lifecycle says a source is active. Periodic cuts should revoke retained per-channel OFF for non-OFF lifecycles, while preserving it only for conditions that are compatible with verified OFF.

AGENTS.md reference: AGENTS.md:L533-L536

Useful? React with 👍 / 👎.

soak measurement added 2 commits August 28, 2026 08:24
Landed by the batch lander. The lane's own report and the coordinator's
verification are recorded on the pull request.
@test1card

Copy link
Copy Markdown
Owner Author

@codex review

Head under review: aca3b6954d5422e9237ff10f44f80aff81b23642. Please bind your verdict to that exact SHA.

This round fixes a regression the COORDINATOR introduced when integrating this
branch with the interlock-severing branch, not one the lane wrote:
self._conductivity_snapshot.clear() was placed in the transport-invalidation
path without the getattr(..., None) guard every neighbouring line there uses.
That path runs during PARTIAL CONSTRUCTION, so it raised AttributeError and
test_owner_anchor_sees_teardown_safe_fields_before_later_construction_failure
caught it — the node exists for exactly that.

Measured: the node passes on master, failed at the previous head, and passes now.
Coordinator's control at this head: 5 red without the production change, 72
green with it.

The thing worth pressing: the guard must not have turned the clear into a no-op.
The snapshot MUST still be invalidated when the transport dies, or a sweep could
later start on stale channel identity — this branch's own defect, inverted.
test_transport_invalidation_clears_lazy_snapshot_binding is the node for that;
please check it would fail if the clear stopped happening.

Docs gate green at this head; derived pair regenerated as the last commit.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: aca3b6954d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

test1card added a commit that referenced this pull request Aug 28, 2026
…and not only the plumbing

Integrates #107. Zero product conflicts with the candidate and zero with the warning-choice
rework in flight -- it touches only tools/thermal_conductivity_simulator.py and its test.

This is the owner's ONE named feature. His words: "стараться делать минимум новых фич (я бы
сказал что симулятор теплопроводности это единственная нужная фича)". The campaign handoff
listed #107 under "unrelated backlog"; that reading was rejected, and the consultants agreed it
was wrong. A rehearsal against a simulated specimen exercises the measurement path -- power
applied, temperatures responding, a point accepted and durably recorded -- rather than only the
transport and the panels.

Reversion control on this candidate: 38 passed with the change; the simulator reverted, 7 fail,
including test_external_simulator_answers_invalid_channels_with_explicit_error.

WHAT THIS DOES NOT ESTABLISH, and must never be reported as establishing: a simulator run is not
hardware qualification. Mock, simulator and software-only evidence never qualify the physical
instrument. It makes the end-to-end rehearsal meaningful; it does not make it a physical
acceptance.

Block B, simulator half. The persistence half (#110's run-record-before-power-advance and #118's
identity replay) follows the warning-choice rework, because both touch engine.py. Semantic delta
only; the merge's generated metrics were reverted to master and reconcile once at freeze.
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