feat(engineering): V1-A — Q-001 subsystem-scoped + pillar query integration test
Phase V1-A of the Engineering V1 Completion Plan. Scope was kept tight
per the plan: a single Q-001 shape fix and an integration test that
proves the four pillar queries work end-to-end against one seed graph.
Code change:
- subsystem_contents() in src/atocore/engineering/queries.py returns
{subsystem, contains: [{id, type, entity_type, name, status}]} by
walking inbound part_of edges (the inverse of CONTAINS), filtered
to active children. `type` matches the catalog spec; `entity_type`
preserves parity with the rest of this module's response shape.
- GET /entities/Subsystem/{id}?expand=contains route in routes.py
matches the Q-001 spec invocation verbatim; 400 on unsupported
expand, 404 on missing-or-wrong-type.
- Aliased under /v1.
- The existing project-wide /engineering/projects/{name}/systems
route stays as-is for Q-004 (whole-project tree).
V1-A acceptance test (tests/test_v1_a_pillar_queries.py):
- Q-001 subsystem-scoped: Optics → Primary Mirror + Diverger Lens
- Q-005 requirements_for: Primary Mirror has its single satisfied req
- Q-006 orphan_requirements: orphan surfaces, satisfied does not
- Q-017 evidence_chain: supported claim has the FEA result via
'supports'; unsupported claim does not
- Q-009 risky_decisions / Q-011 unsupported_claims also asserted
against the same seed (Codex P2: don't seed data you don't assert)
Plus targeted tests for the Q-001 helper: missing id, wrong type,
nested child subsystems, inactive-child filtering, real /v1 GET
behavior.
Reviewed by Codex twice: GO WITH CONDITIONS at b575773, GO at 0e83525
after the dual-key emit + Q-009/Q-011 + /v1-behavior + nested/inactive
amends folded in.
Tests: 596 -> 605 (+9). Full local suite green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,70 @@ def system_map(project: str) -> dict:
|
||||
return out
|
||||
|
||||
|
||||
def subsystem_contents(subsystem_id: str) -> dict | None:
|
||||
"""Q-001 subsystem-scoped variant: a single subsystem and its
|
||||
direct ``CONTAINS`` children.
|
||||
|
||||
Spec: ``GET /entities/Subsystem/<id>?expand=contains`` per
|
||||
``docs/architecture/engineering-query-catalog.md`` Q-001.
|
||||
|
||||
Differs from :func:`system_map` (Q-004) which returns the
|
||||
project-wide tree. The subsystem-scoped form is what individual
|
||||
operator queries actually need: "what's inside this one subsystem?"
|
||||
rather than "show me the whole project."
|
||||
|
||||
The relationship walk uses inbound ``part_of`` edges (the inverse
|
||||
of ``CONTAINS``) so both child Components and child Subsystems
|
||||
surface uniformly. Filters to active children only — superseded
|
||||
or invalid rows do not belong in a "current contents" answer.
|
||||
|
||||
Returns:
|
||||
``{"subsystem": {id, name, project, status, description},
|
||||
"contains": [{id, entity_type, name, status}]}``
|
||||
or ``None`` when the entity does not exist or is not a subsystem.
|
||||
"""
|
||||
with get_connection() as conn:
|
||||
ss = conn.execute(
|
||||
"SELECT * FROM entities WHERE id = ?",
|
||||
(subsystem_id,),
|
||||
).fetchone()
|
||||
if ss is None or ss["entity_type"] != "subsystem":
|
||||
return None
|
||||
rows = conn.execute(
|
||||
"SELECT e.id, e.entity_type, e.name, e.status "
|
||||
"FROM relationships r "
|
||||
"JOIN entities e ON e.id = r.source_entity_id "
|
||||
"WHERE r.relationship_type = 'part_of' "
|
||||
"AND r.target_entity_id = ? "
|
||||
"AND e.status = 'active' "
|
||||
"ORDER BY e.entity_type, e.name",
|
||||
(subsystem_id,),
|
||||
).fetchall()
|
||||
|
||||
return {
|
||||
"subsystem": {
|
||||
"id": ss["id"],
|
||||
"name": ss["name"],
|
||||
"project": ss["project"],
|
||||
"status": ss["status"],
|
||||
"description": ss["description"] or "",
|
||||
},
|
||||
"contains": [
|
||||
{
|
||||
"id": r["id"],
|
||||
# V1-A spec uses `type` per engineering-query-catalog.md Q-001;
|
||||
# `entity_type` is duplicated for parity with the rest of
|
||||
# this module's response shape (see `_entity_dict`).
|
||||
"type": r["entity_type"],
|
||||
"entity_type": r["entity_type"],
|
||||
"name": r["name"],
|
||||
"status": r["status"],
|
||||
}
|
||||
for r in rows
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def decisions_affecting(project: str, subsystem_id: str | None = None) -> dict:
|
||||
"""Q-008: decisions that affect a subsystem (or whole project).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user