40 — Tools, channels, routines and lenses
BuiltRendered from 40-tools-and-lenses-asbuilt.md in the public repository. Edits belong there.
MiCyte has three registers of things that act on an instance (tools, channels and automation routines) and one registry of lenses that format values for display.
Three registers
| Register | Module | An entry | Refuses |
|---|---|---|---|
| Tools | micyte/tools/_registry.py | A WorkbenchTool: builds a panel payload for a person | An object with a routine_id or channel_id |
| Channels | micyte/channels/__init__.py | A HostedChannel: a GET-only page a participant reaches (91) | Tools and routines |
| Routines | micyte/automation/ | An AutomationRoutine: writes or calls because a condition held | An object with build_panel_payload, route or applies_to_archetype |
Registered today: two channels (agnet, convention) and four routines (email_triage, email_verification_request, email_verification_sync, site_analytics_refresh). No routine runs on a schedule; each is declared, refused until granted, and run by hand.
The tool contract
A tool satisfies the runtime-checkable Protocol WorkbenchTool in micyte/tools/_contract.py:
| Member | Meaning |
|---|---|
tool_id, label, summary | Identity and palette text |
route | The route the palette dispatches; tools open on /portal/system/tools/workbench-ui?tools=<id> |
applies_to_archetype, applies_to_source_kind | When the tool is offered for the document in focus. Both empty means the tool is opened by address against the instance (a hub or an instrument) |
build_panel_payload(*, authority_db_file, sandbox_id, document_id, datum_address) | Returns the payload the workbench renders |
Optional attributes, read with defaults:
| Attribute | Meaning |
|---|---|
writes | A tuple of DeclaredWrite(document_kind, action). The write policy judges each write against these |
requires | A ToolRequirement: documents that must be held (documents, documents_any), fields the sandbox anchor must express, and sources rows read from other sandboxes. Install provisions them. A requirement is not a permission |
icon | The rail sprite symbol |
wants_surface_query | Receive the request's query arguments as extra_query |
wants_host_context | Receive host_context: private_dir and port, a callable that returns a bound adapter for a port_id or None |
Tools register themselves by calling register(...) at import; micyte/tools/__init__.py imports each module. all_tools() returns them sorted by id.
The registry today
MiCyte registers 45 tools: 20 declare writes, 24 are opened by address, 23 belong to a package and 22 belong to none. The host application registers its own tools beside them. Ask the registry rather than a list:
from micyte.tools import all_tools, declared_writes
[t.tool_id for t in all_tools()]fnd_app/tests/architecture/test_tool_registry_shape.py pins the registry as an exact set. Every registered tool must declare a write or name what it derives; a tool that only renders a document's own values is drawn by a viewscope instead (41).
A tool belongs to at most one package (packages_by_tool raises on a shared claim). A hub composes other tools as tabs; 46 lists the packages.
Eligibility
Two questions decide whether a tool is shown:
- Can this sandbox run it?
instance_can_use(46). - Does it apply to the selection?
recognize_applicable_toolsinmicyte/state_machine/portal_shell/tool_eligibility.pybuilds the document's archetype set from its metadata and from every row reached along the selected datum's hyphae chain, and matches it againstapplies_to_archetype; it also matches the source kind againstapplies_to_source_kind.test_palette_eligibility_purity.pykeeps this function free of I/O.
The palette runtime serves GET /portal/api/tools/eligible (for a selected document) and GET /portal/api/visualizers/for-sandbox (for a sandbox, ranked by how many documents each tool can render). An empty applies_to_* matches everything; no document context returns every tool.
Tools are not bound to a specific hyphae value or to a family's root datum; that binding is not built (60).
Lenses
A lens is a stateless codec (Lens in micyte/state_machine/lens/base.py) with decode, encode and validate_display.
| Lens id | Effect |
|---|---|
identity | Pass-through; the fallback |
trimmed_string | Strips whitespace |
samras_title | Decodes a SAMRAS-encoded title |
email_address | Lowercases and checks @ placement |
secret_reference | Shows a vault reference, never the value |
numeric_hyphen | Validates hyphenated digit groups (SAMRAS and HOPS addresses) |
fiat_cents | Shows a fiat magnitude as currency: 450 as $4.50 |
binary_text | Decodes a printable binary string to ASCII; otherwise shows {n} bits |
DatumLensRegistry (registry.py) chooses a lens by recognized family, then overlay kind, then value kind, defaulting to identity. The workbench resolves one lens per row and shows the result as the row's display value; the lens never changes the stored row. A lens can be disabled per instance through POST /portal/api/lenses/toggle; a disabled lens falls back to identity. The panel module for these toggles exists but no surface mounts it (81).
The workbench also has a workbench_lens query mode, interpreted (default) or raw, which switches the column set. It is separate from the lens registry.