Summary
Two hardening items in the new bind-text-block path
(robosystems/operations/roboledger/commands/text_blocks.py). Neither is a
security hole — both paths are tenant-scoped and write-gated — but both are
data-integrity / robustness looseness worth closing before the disclosure
authoring surface goes live in prod.
(a) Non-deterministic element resolution, no structure-membership check
_resolve_element resolves a qname via Element.qname == ... .limit(1) with no
ordering, then bind_text_block stamps the fact with structure_id = body.structure_id + element_id = element.id. Nothing verifies the element is
actually an arc member of that structure, and a qname duplicated across
taxonomies in the tenant schema resolves arbitrarily.
Result: a narrative can be attached to the wrong structure.
Fix: make the qname lookup deterministic/unique, and validate the element
participates in body.structure_id (via associations).
(b) item_type stamp can trip the library-immutability trigger
bind_text_block sets element.item_type = "text_block" on first bind
(text_blocks.py:145-149). If the target element is ever library-owned
(created_by = 'library-seeder'), the UPDATE elements on flush hits the
raise_library_immutable_elements trigger (migration 0002) → P0001, which
surfaces as an uncategorized InternalError → 500 (MCP: generic
command_failed).
Unreachable today: the only library text-block-CAP disclosure structures are
abstract (rejected by the is_abstract guard first), and the only concrete
library concepts are the 5 rs-metric ratios (not text-block). It becomes a live
crash the moment the library ships a concrete text-block concept.
Fix: only stamp item_type when created_by != 'library-seeder', drive the
write through the sanctioned library_resync path, or set item_type at
authoring time in create-taxonomy-block instead of lazily on bind.
Test gap
The bind_text_block command itself has no direct unit test (only the
envelope/dispatch and serialization paths are covered), so the item_type path is
untested.
Found during the v1.6.6 pre-release review.
Summary
Two hardening items in the new
bind-text-blockpath(
robosystems/operations/roboledger/commands/text_blocks.py). Neither is asecurity hole — both paths are tenant-scoped and write-gated — but both are
data-integrity / robustness looseness worth closing before the disclosure
authoring surface goes live in prod.
(a) Non-deterministic element resolution, no structure-membership check
_resolve_elementresolves a qname viaElement.qname == ... .limit(1)with noordering, then
bind_text_blockstamps the fact withstructure_id = body.structure_id+element_id = element.id. Nothing verifies the element isactually an arc member of that structure, and a qname duplicated across
taxonomies in the tenant schema resolves arbitrarily.
Result: a narrative can be attached to the wrong structure.
Fix: make the qname lookup deterministic/unique, and validate the element
participates in
body.structure_id(viaassociations).(b)
item_typestamp can trip the library-immutability triggerbind_text_blocksetselement.item_type = "text_block"on first bind(
text_blocks.py:145-149). If the target element is ever library-owned(
created_by = 'library-seeder'), theUPDATE elementson flush hits theraise_library_immutable_elementstrigger (migration 0002) →P0001, whichsurfaces as an uncategorized
InternalError→ 500 (MCP: genericcommand_failed).Unreachable today: the only library text-block-CAP disclosure structures are
abstract(rejected by theis_abstractguard first), and the only concretelibrary concepts are the 5
rs-metricratios (not text-block). It becomes a livecrash the moment the library ships a concrete text-block concept.
Fix: only stamp
item_typewhencreated_by != 'library-seeder', drive thewrite through the sanctioned
library_resyncpath, or setitem_typeatauthoring time in
create-taxonomy-blockinstead of lazily on bind.Test gap
The
bind_text_blockcommand itself has no direct unit test (only theenvelope/dispatch and serialization paths are covered), so the item_type path is
untested.
Found during the v1.6.6 pre-release review.