Scoped procedure inventory over the graph backend.
The review queue and HTTP list need the stored node, including lifecycle and
custom properties. A retrieval projection loses those fields and may stop at
200 rows, so this read port queries the graph without a recall limit.
ProcedureAdminService
ProcedureAdminService(graph: Any)
List one scope's stored procedures without composing an agent.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| def __init__(self, graph: Any) -> None:
self._graph = GraphMemoryStore(graph)
|
amend_procedure
async
amend_procedure(scope: MemoryScope, node_id: str, *, steps: Any = None, properties: dict[str, Any] | None = None, active: bool | None = None) -> dict[str, Any] | None
Edit a visible procedure through the locked lifecycle transition.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def amend_procedure(
self, scope: MemoryScope, node_id: str, *, steps: Any = None,
properties: dict[str, Any] | None = None, active: bool | None = None,
) -> dict[str, Any] | None:
"""Edit a visible procedure through the locked lifecycle transition."""
scoped = tenant_scope(scope)
# The layer method guards review state; this port also rejects nodes
# outside the procedure surface before invoking that transition.
candidate = await self._graph.backend.get_node(scoped, NodeId(node_id))
if (candidate is None or candidate.layer != MemoryLayer.PROCEDURAL
or is_retracted(candidate.properties)):
return None
from symfonic.memory.layers.procedural.layer import ProceduralLayer
try:
node = await ProceduralLayer(self._graph).amend_skill(
scoped, NodeId(node_id), steps=steps, properties=properties, active=active,
)
except KeyError:
return None
props = node.properties or {}
status, node_active = procedure_lifecycle(props)
return {
"id": str(node.id), "label": node.label, "steps": props.get("steps", []),
"properties": props, "status": status, "active": node_active,
}
|
delete_procedure
async
delete_procedure(scope: MemoryScope, node_id: str) -> bool
Atomically delete one exact-owner procedural row, if supported.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def delete_procedure(self, scope: MemoryScope, node_id: str) -> bool:
"""Atomically delete one exact-owner procedural row, if supported."""
backend = self._graph.backend
delete = getattr(backend, "delete_procedure_atomic", None)
if not getattr(backend, "supports_atomic_procedure_delete", False) or not callable(delete):
raise NotImplementedError("atomic procedure deletion unavailable")
return await delete(tenant_scope(scope), NodeId(node_id))
|
get_procedure
async
get_procedure(scope: MemoryScope, node_id: str) -> dict[str, Any] | None
Read one procedural node in the authenticated tenant's scope.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def get_procedure(
self, scope: MemoryScope, node_id: str
) -> dict[str, Any] | None:
"""Read one procedural node in the authenticated tenant's scope."""
node = await self._graph.get_node(tenant_scope(scope), NodeId(node_id))
if node is None or node.layer != MemoryLayer.PROCEDURAL:
return None
return self._row(node)
|
list_draft_procedures
async
list_draft_procedures(scope: MemoryScope) -> list[dict[str, Any]]
Project draft rows to the legacy review-queue wire shape.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def list_draft_procedures(self, scope: MemoryScope) -> list[dict[str, Any]]:
"""Project draft rows to the legacy review-queue wire shape."""
rows = await self.list_procedures(scope, status="draft")
fields = (
"id", "label", "content", "steps", "status", "active", "importance", "created_at",
)
return [{field: row[field] for field in fields} for row in rows]
|
list_procedures
async
list_procedures(scope: MemoryScope, *, status: str | None = None) -> list[dict[str, Any]]
Preserve the legacy list row and status filter over all matching nodes.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def list_procedures(
self, scope: MemoryScope, *, status: str | None = None
) -> list[dict[str, Any]]:
"""Preserve the legacy list row and status filter over all matching nodes."""
nodes = await self._graph.query_nodes(
tenant_scope(scope),
layer=MemoryLayer.PROCEDURAL,
limit=None,
)
rows = []
for node in nodes:
props = node.properties or {}
node_status, active = procedure_lifecycle(props)
if (status is None and node_status != "approved") or (
status not in (None, "all") and status != node_status
):
continue
rows.append(self._row(node))
return rows
|
toggle_procedure
async
toggle_procedure(scope: MemoryScope, node_id: str) -> dict[str, Any] | None
Toggle a visible procedure under the same lock as review writes.
Source code in src/symfonic/capabilities/memory/procedure_admin.py
| async def toggle_procedure(
self, scope: MemoryScope, node_id: str,
) -> dict[str, Any] | None:
"""Toggle a visible procedure under the same lock as review writes."""
scoped = tenant_scope(scope)
async with review_lock_for(self._graph.backend):
candidate = await self._graph.backend.get_node(scoped, NodeId(node_id))
if (candidate is None or candidate.layer != MemoryLayer.PROCEDURAL
or is_retracted(candidate.properties)):
return None
node = await self._graph.backend.get_node(scoped, NodeId(node_id))
if (node is None or node.layer != MemoryLayer.PROCEDURAL
or is_retracted(node.properties)):
return None
props = dict(node.properties or {})
nested = props.get("record_metadata")
if isinstance(nested, dict):
reserved = {
"status", "active", "procedure_review_authority",
"procedure_review_actor", "procedure_lifecycle_origin",
}
props["record_metadata"] = {
key: value for key, value in nested.items() if key not in reserved
}
status, active = procedure_lifecycle(props)
props["active"] = not active if status == "approved" else False
props["_last_edited_by"] = "user_manual_edit"
try:
node = await self._graph.update_node(
scoped, NodeId(node_id), {
"properties": props,
"access_count": node.access_count + 1,
},
)
except KeyError:
return None
props = dict(node.properties or {})
status, active = procedure_lifecycle(props)
return {
"id": str(node.id), "label": node.label, "status": status,
"active": active, "properties": props,
}
|