Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions docs/validation/viewer-retirement-mounted-routes-20261004.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
Select mounted producer-bearing routes for viewer retirement
===========================================================

Source checkpoint: main01044edbc13c9a234a7ec52f772acca8015ccc99,
2026-10-04. Singer owns this narrow viewer-guide correction. Current open522
retirement/selection and Planck's independent point-highlight investigation
are coordinated; open697 final summaries and693 analytical colour rendering
are disjoint. No production, dependency, runtime or scientific files change.

Determining relation
-------------------

Viewer state deliberately includes declared routes without a mounted layer.
NapariViewerProjectionABC.route_keys unions route titles and component groups;
layer_state_for projects mounted from the actual layer and producer_identities
from the original NapariComponentGroupStore.producer_identities_for. An
unmounted, zero-item route can correctly have an empty producer array.

ViewerLayerRetirementControlOptions rejects empty producer tuples before
dispatch. Native NapariComponentAwareDisplayCoordinator.retire_layers also
requires every selected route to be mounted and its exact producer set to
match before any removal. This protects whole-set atomic admission, not a
ban on null invocation_key. Original PolyStore StreamProducerIdentity declares
invocation_key optional and its from_payload decoder preserves None; the
request annotation delegates to that decoder through BeforeValidator.

The retained refused request selected eleven routes: eight populated producer
arrays plus three empty arrays. An earlier readback marks those three unmounted
and zero-item; the latest readback before retirement instead has them mounted,
with one item and populated producer identities. The request retained their
older empty arrays. The generic error is consistent with the empty-set guard,
not evidence that null invocation_key or a valid fresh mounted producer is
rejected. The first offline evidence assertion expected complete latest-state
parity and failed; that determining failure is preserved, not weakened or
misclassified as a product failure. Eight supplied populated identities match
the latest state exactly; the three stale empty entries do not.
The original request, error and subsequent scene remain untouched. No replay
or author feedback occurs. No unsupported retirement fallback is introduced.

Owner and change
----------------

The declared manifest source for openhcs_viewer_qa is the existing packaged
references/viewer-qa.md; this guide has no RST backer. Its retirement procedure
now explicitly chooses mounted entries with nonempty returned producer arrays,
omits unmounted placeholders, and preserves null optional identity fields.
The declaration/decoder/coordinator remain the same owners; no copied identity
codec, route selector, store, registry, guard or alternate guide is added.

Applicable review: BOUND-2 (use the existing producer decoder), IDEN-7 (a
declared route is not necessarily a mounted layer), and IDEN-6 (exact route and
producer incarnation, not title or port). Skill-creator limits this to the
missing operational distinction; Diataxis keeps it in the existing how-to
step, rather than creating a protocol reference or a new universal gate.

Qualification checkpoint
------------------------

Offline SDK acceptance used the ORIGINAL installed receiving04 target and paired
interpreter, not a modified client or native process. build_server constructed
the actual registered retirement Tool's argument model. It decodes the original
eleven-entry request, after which the original request/control-options owner
rejects its three empty arrays with the exact retained error. Independently
constructing the eight populated, mounted entries preserves every returned
producer field through SDK -> original DTO -> as_tool_arguments, including
all null invocation_key values. No tool.run, transport send, native request,
client startup or viewer mutation was issued: dispatches0.

sdk01 retained the first evidence assertion failure described above. sdk02 is
terminal0,7.36s,377876KiB maxRSS,Swap0. Both journal hashes remain unchanged;
their complete original paths/hashes, imported module paths/hashes and outcomes
are retained in the archive. Relevant owner ASTs are equal between current
source and this original installed target: retirement control-options,
request, result and native retire_layers. This is actual SDK/request admission,
not a new public TCP retirement or survivor remount.

The existing audit Package/Repository AST owners parsed all701 production and
705 test modules at source766e2b9d3, zero omissions,32.83s/990724KiB/Swap0.
The selected direct consumers are capability declaration, request/result DTO,
original viewer-window service/gateway, CLI command and native coordinator;
two existing native/SDK test modules consume them. The original component-group
producer projection and PolyStore identity/optional-field decoding were read
semantically. This is complete source parse plus selected-family evidence,
not a full NRA/R1 detector or dynamic-resolution proof. No structural refactor
or production code change is made; no R0 improvement is claimed.

The original project_knowledge_assets -> KnowledgeBaseService -> AgentSkillBundle
-> sync_skills owner chain passes1 focused control: source/package full bytes,
rendered whole viewer-QA/lifecycle content, full retirement subsection at the
original MAX_DOCUMENT_CHARS bound, search discovery, managed guide bytes and
unchanged second sync. Terminal0,9.69s whole invocation/7.21s pytest,
330708KiB/Swap0. The new assertion compares the COMPLETE source span with the
retrieved subsection; it does not match a phrase as behavioral proof.
Existing plugin-free asyncio-configuration warnings remain in raw output.
Skill-creator quick_validate reports Skill is valid, terminal0. The original
source bootstrap and source-byte-qualified tabular ABI backing were reused;
C++15acc82b8ab64268bd1ea4f83fa7a68f527bf317f002e9f39e15be321e03b28e
is unchanged. No new build/install, native execution or live managed-skill sync.

Guide SHA256 bede1fa02bce04ed87ab31286327500df2771e8af3060db9f94cf43462acf4a7.
Byte-exact fifteen-member source/raw-log archive
viewer-retirement-mounted-routes-20261004.tar.gz is18762bytes, SHA256
0fa36297a868602299f4fb6ca0e879d627f0eed9c7be278aa34f44b61e3fa32c;
tar --compare passed. First failed assertion, all original warnings and later
accepted outputs are preserved, not rewritten as clean evidence. Source guide,
test and verification caller are included; originals remain in the owned
validation/viewer-retirement-mounted-routes699 directory. Eight foreign
gitlinks and all earlier untracked ledgers are preserved, unstaged and unchanged.

This documentation change does not claim a new native retirement, selected
geometry-survivor acceptance, autonomous gain or installed scientific update.
Existing active/frozen author packages remain unchanged. CI deferred.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,15 @@ contract is exposed:
2. Read fresh, unfiltered viewer state on the same viewer incarnation. Choose
only explicitly superseded routes; retain the current matched candidate,
raw/source domain and reference routes still needed for the next comparison.
Select only entries with `mounted=true` and a nonempty `producer_identities`
array. State can also list declared, unmounted routes with empty arrays;
omit those placeholders from the retirement mapping, rather than sending
`[]` or inventing an identity for them.
Build `expected_producers` by mapping each chosen **exact `route_key`** to
its **complete `producer_identities` array** from that readback, including
`invocation_key`. Do not shorten, fabricate or substitute identities from a
`invocation_key`; preserve `null` when that is the returned value. It is a
valid optional identity field, not a missing producer. Do not shorten,
fabricate or substitute identities from a
previous candidate, title or port. The receiving owner checks the whole set
before removal; a changed producer requires a fresh disposition/readback,
not a weaker identity check.
Expand Down
13 changes: 12 additions & 1 deletion tests/unit/agent/test_viewer_retirement_guide_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from openhcs.agent.dto.knowledge import KnowledgeBaseDocumentRequest, KnowledgeBaseSearchRequest
from openhcs.agent.services.knowledge_base_service import (
KnowledgeBaseService, load_document_specs_from_manifest,
MAX_DOCUMENT_CHARS, KnowledgeBaseService, load_document_specs_from_manifest,
)
from openhcs.agent.skill_bundle import AGENT_PLUGIN_MANIFEST_PATH, AgentSkillBundle
from openhcs.agent.skill_sync import sync_skills
Expand Down Expand Up @@ -38,6 +38,17 @@ def test_real_viewer_guide_projection_retrieval_and_skill_delivery(tmp_path):
# The original renderer joins source lines; compare all content, not a
# wording substring that would pretend to prove the guide's decisions.
assert reply.content == "\n".join((root / path).read_text().splitlines())
if document_id == "openhcs_viewer_qa":
section = next(item for item in reply.sections
if item.section_id == "keep-iterative-review-within-its-resource-budget")
excerpt = service.get_document(KnowledgeBaseDocumentRequest.from_fields(
document_id=document_id, section_id=section.section_id,
max_chars=MAX_DOCUMENT_CHARS,
))
assert not excerpt.errors and not excerpt.truncated
assert excerpt.content == "\n".join(
(root / path).read_text().splitlines()[section.start_line - 1:section.end_line]
)
paths.append(path)
found = service.search(KnowledgeBaseSearchRequest(query="viewer layer retirement", limit=5))
assert any(hit.document.document_id == "openhcs_viewer_qa" for hit in found.hits)
Expand Down
Loading