Graph View Response Specification
Graph View Response Specification
Status: Draft v0.1.
Project: UWScrape.
Directory: docs/specs.
Audience: backend implementers, frontend implementers, solver implementers, and graph-view reviewers.
Last reviewed: 2026-05-11.
Primary architecture document: docs/reference/architecture/backend-runtime-architecture/.
Related documents:
docs/specifications/backend-api-spec/docs/specifications/query-evaluation-semantics-spec/docs/specifications/atlas-engine-and-wasm-spec/docs/reference/architecture/frontend-runtime-architecture/docs/reference/architecture/solver-system-architecture/docs/decisions/0009-rest-and-typed-graph-view-endpoints/
1. Purpose
This document specifies version 1 graph view request and response contracts.
Graph view endpoints serve bounded typed projections for frontend-heavy screens.
The atlas engine consumes these projections as visual inputs, as specified in the frontend runtime architecture and atlas engine spec.
Graph view endpoints do not define academic truth.
Graph view endpoints do not replace query evaluation.
Graph view endpoints do not expose a general graph query language.
Graph view endpoints are named views with documented inputs, outputs, bounds, source references, and uncertainty.
2. Endpoint List
Version 1 graph view endpoints:
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/graph/views | List supported views and limits. |
POST | /api/v1/graph/views/course-neighborhood | Course prerequisite and unlock neighborhood. |
POST | /api/v1/graph/views/course-universe | Bounded universal course relation graph for the active published index. |
POST | /api/v1/graph/views/course-pathways | Recursive upstream and downstream course pathways around selected courses. |
POST | /api/v1/graph/views/credential-requirements | Credential requirement structure. |
POST | /api/v1/graph/views/unlock-overlay | State-dependent unlock status for visible nodes. |
POST | /api/v1/graph/views/target-relevance | Relevance of visible courses to selected credentials. |
POST | /api/v1/graph/views/expand-node | Expand one node within a named view. |
All responses use the common envelope from docs/specifications/backend-api-spec/.
3. Shared Response Shape
data shape:
{ "view_type": "course_neighborhood", "projection_version": "1.0.0", "scope": {}, "bounds": {}, "nodes": [], "edges": [], "groups": [], "metrics": {}, "omitted": { "nodes": 0, "edges": 0, "groups": 0, "reason": null }, "state_overlay": null}Required fields:
| Field | Type | Meaning |
|---|---|---|
view_type | string | Named graph view type. |
projection_version | string | Response contract version. |
scope | object | Input scope normalized by backend. |
bounds | object | Applied bounds. |
nodes | array | Typed nodes. |
edges | array | Typed edges. |
groups | array | Optional grouping records. |
metrics | object | View-level metrics. |
omitted | object | Omitted counts caused by bounds. |
state_overlay is null for catalog-only views.
state_overlay is present for state-dependent views.
4. Bounds
Every graph view enforces server-side bounds.
Default bounds:
| Bound | Default | Hard maximum |
|---|---|---|
max_nodes | 250 | 2500 |
max_edges | 600 | 7500 |
max_depth | 2 | 4 |
max_courses | 100 | 2500 |
max_credentials | 5 | 25 |
The backend may use lower defaults for expensive views.
If a request exceeds hard maximums, return 400 bad_request.
If a response is truncated because defaults or requested soft bounds were reached, return 200 OK with omitted counts and graph_view_truncated warning.
Truncation is not a transport error.
Truncation means the projection is incomplete.
5. Node Shape
Node shape:
{ "node_id": "node:course_listing:CS:246", "node_type": "course_listing", "academic_object_id": "course_listing:CS:246", "label": "CS 246", "title": "Object-Oriented Software Development", "catalog_version_id": "uw_undergrad_2026_2027", "status": null, "metrics": {}, "source_reference_ids": [], "unknown_reason_ids": [], "conflict_reason_ids": [], "layout_hints": {}}Required fields:
| Field | Type | Meaning |
|---|---|---|
node_id | string | Node id within the response. |
node_type | string | Typed node kind. |
academic_object_id | string or null | Backing academic object id. |
label | string | Short display label. |
source_reference_ids | array | Source ids backing this node. |
metrics | object | Node-level metrics. |
layout_hints | object | Optional deterministic visual placement hints produced by the backend or index build. |
node_id may include view-local prefixing.
academic_object_id is the stable academic id when one exists.
The frontend must not infer academic semantics from node_id formatting.
layout_hints may include a position object with numeric x, y, and z
coordinates. These hints are visual projections only. They are preferred for
dense universal Canva graphs so weak client hardware does not need to recompute
catalog-wide layout, but they never define academic truth.
6. Node Types
Allowed initial node_type values:
| Node type | Meaning |
|---|---|
course_listing | Visible course listing such as MATH 135. |
course_credit | Credit identity shared by linked listings. |
credential | Degree, major, minor, specialization, option, or plan target. |
requirement_group | Parsed non-leaf requirement expression. |
requirement_condition | Parsed leaf requirement condition. |
unparsed_requirement | Opaque source requirement fragment. |
subject_group | Display grouping by subject. |
level_group | Display grouping by course level. |
source_reference | Source provenance node when a view requests source expansion. |
Node types are operational.
The frontend may render them however it wants.
The backend must not expose internal solver variable nodes as stable graph nodes.
7. Edge Shape
Edge shape:
{ "edge_id": "edge:req_1", "edge_type": "requires", "source_node_id": "node:course_listing:CS:246", "target_node_id": "node:course_listing:CS:136", "academic_relation_id": "requirement_condition:req_cond_1", "direction_semantics": "source_requires_target", "status": null, "metrics": {}, "source_reference_ids": [], "unknown_reason_ids": [], "conflict_reason_ids": []}Required fields:
| Field | Type | Meaning |
|---|---|---|
edge_id | string | Edge id within the response. |
edge_type | string | Typed edge kind. |
source_node_id | string | Source node in this response. |
target_node_id | string | Target node in this response. |
direction_semantics | string | Meaning of edge direction. |
source_reference_ids | array | Source ids backing this edge. |
The response must name edge direction semantics.
Clients must not infer edge meaning from direction alone.
8. Edge Types
Allowed initial edge_type values:
| Edge type | Meaning |
|---|---|
requires | Target requirement needs source item, with direction clarified by view. |
unlocks | Source course or condition contributes to target availability. |
excludes | Antirequisite or incompatibility relation. |
equivalent_credit | Listings share or map to one credit identity. |
satisfies_requirement | Course or state fact satisfies a requirement condition. |
part_of_requirement | Requirement group contains subexpression or condition. |
belongs_to_credential | Requirement source belongs to a credential. |
belongs_to_subject | Course listing belongs to a subject grouping. |
belongs_to_level | Course listing belongs to a level grouping. |
cites_source | Node or edge cites a source reference. |
9. Group Shape
Groups are optional visual or semantic clusters.
Shape:
{ "group_id": "group:subject:CS", "group_type": "subject_group", "label": "CS", "node_ids": ["node:course_listing:CS:246"], "metrics": {}, "source_reference_ids": []}Allowed group types:
subject_group;level_group;credential_group;requirement_group;result_group;
Groups are not academic truth by themselves.
Groups help the frontend organize projections.
10. State Overlay Shape
State-dependent views include state_overlay.
Shape:
{ "state_mode": "persisted", "state_version": 4, "state_catalog_version_id": "uw_undergrad_2026_2027", "active_catalog_version_id": "uw_undergrad_2026_2027", "catalog_mismatch": false, "node_statuses": {}, "edge_statuses": {}, "unknowns": [], "conflicts": []}node_statuses maps node_id to academic status summaries.
edge_statuses maps edge_id to academic status summaries.
The overlay must not include raw state tokens.
The overlay must not include token verifiers.
The overlay must not include user notes unless a future endpoint explicitly requests notes.
11. Status Summary Shape
Shape:
{ "status": "satisfied", "completeness": "complete", "academic_result_id": "academic_result:local_1", "unknown_reason_ids": [], "conflict_reason_ids": [], "explanation_summary": "Completed CS 135 satisfies this prerequisite."}Status values use the academic vocabulary:
satisfied;not_satisfied;partial;unknown;conflict;not_applicable.
Graph status summaries are compact.
Full explanations come from query endpoints or explicit expansion.
12. Metrics
Metric values are numeric or categorical facts computed by the backend.
Course metrics may include:
direct_unlock_count;transitive_unlock_count;unlock_subject_count;unlock_subject_entropy;credential_relevance_count;conflict_count;unknown_dependency_count;bottleneck_score;bridge_score;optionality_score.
Credential metrics may include:
requirement_group_count;remaining_requirement_count;unknown_requirement_count;choice_group_count;satisfied_requirement_count.
Metrics are not display sizes.
The frontend chooses visual mapping.
The backend should document each metric’s formula when implemented.
13. GET /api/v1/graph/views
Returns supported graph view types and limits.
Data shape:
{ "views": [ { "view_type": "course_neighborhood", "path": "/api/v1/graph/views/course-neighborhood", "state_dependent": false, "supports_state_overlay": true, "default_bounds": { "max_depth": 2, "max_nodes": 250, "max_edges": 600 } }, { "view_type": "course-universe", "path": "/api/v1/graph/views/course-universe", "state_dependent": false, "supports_state_overlay": false, "default_bounds": { "max_depth": 2, "max_nodes": 250, "max_edges": 600 } }, { "view_type": "course-pathways", "path": "/api/v1/graph/views/course-pathways", "state_dependent": false, "supports_state_overlay": true, "default_bounds": { "max_depth": 2, "max_nodes": 250, "max_edges": 600 } } ]}This endpoint is catalog-only.
It does not require authentication.
14. Course Neighborhood View
Endpoint:
POST /api/v1/graph/views/course-neighborhoodRequest shape:
{ "center": { "course_codes": ["CS 246"] }, "directions": ["prerequisites", "unlocks"], "bounds": { "max_depth": 2, "max_nodes": 250, "max_edges": 600 }, "include_state_overlay": false}Output includes:
- center course listing nodes;
- prerequisite nodes;
- unlock target nodes;
- requirement group and condition nodes when requested or needed;
requires,unlocks,part_of_requirement, andexcludesedges;- omitted counts when bounded.
When include_state_overlay is true, request must include state_mode.
State overlay may mark prerequisites as satisfied, missing, unknown, or conflicting.
15. Course Universe View
Endpoint:
POST /api/v1/graph/views/course-universeRequest shape:
{ "bounds": { "max_depth": 3, "max_nodes": 2500, "max_edges": 7500, "max_courses": 2500 }}Output includes:
- course listing nodes from the active published index;
- source-backed relation edges derived from parsed requirement conditions;
unlocks,excludes, andequivalent_creditedges when represented by the index;metrics.projection_source, which should bepublished_artifactwhen the published index includes a valid precomputed graph projection;- omitted counts when bounded.
This view is catalog-only.
It must not use student state to decide which courses exist in the universe.
The frontend may overlay Kanban, Advisory, hover, and selection state locally, but those overlays do not change the catalog graph.
16. Course Pathways View
Endpoint:
POST /api/v1/graph/views/course-pathwaysRequest shape:
{ "scope": { "course_codes": ["CS 136"] }, "bounds": { "max_depth": 2, "max_nodes": 250, "max_edges": 600 }}Output includes:
- selected course nodes;
- upstream prerequisite paths into the selected course;
- downstream unlock paths from the selected course;
- source-backed relation metadata on edges;
- omitted counts when bounded.
This view is catalog-only unless a later state overlay flag is explicitly added.
It distinguishes “course A appears in course B’s requirements” from “course A alone satisfies course B”; academic sufficiency still belongs to query evaluation.
17. Credential Requirements View
Endpoint:
POST /api/v1/graph/views/credential-requirementsRequest shape:
{ "credential_ids": ["credential:math_honours"], "bounds": { "max_depth": 4, "max_nodes": 500, "max_edges": 1200 }, "include_course_options": true, "include_state_overlay": false}Output includes:
- credential node;
- requirement group nodes;
- requirement condition nodes;
- referenced course listing nodes;
- unparsed requirement nodes;
part_of_requirement,belongs_to_credential, andsatisfies_requirementedges when state overlay is included.
The view must preserve cardinality metadata on requirement group nodes.
The view must not flatten all choices into simple course edges.
18. Unlock Overlay View
Endpoint:
POST /api/v1/graph/views/unlock-overlayRequest shape:
{ "state_mode": "persisted", "visible_nodes": [ { "node_type": "course_listing", "academic_object_id": "course_listing:CS:246" } ], "include_explanation_summaries": true}This endpoint is state-dependent.
It requires authorization when state_mode = "persisted" or state_mode = "persisted_with_changes".
Output contains compact status summaries for visible nodes.
It should not return full requirement trees unless explicitly requested later.
19. Target Relevance View
Endpoint:
POST /api/v1/graph/views/target-relevanceRequest shape:
{ "visible_courses": ["course_listing:CS:246"], "target_credentials": ["credential:math_honours"], "bounds": { "max_courses": 100, "max_credentials": 5 }}Output includes relevance metrics per visible course:
- whether the course is required;
- whether the course is one option in a choice group;
- whether the course credit identity contributes;
- credential relevance count;
- unknown contribution count;
- conflict count.
Relevance is source-derived.
It is not a recommendation by itself.
20. Expand Node View
Endpoint:
POST /api/v1/graph/views/expand-nodeRequest shape:
{ "view_type": "course_neighborhood", "node": { "node_type": "course_listing", "academic_object_id": "course_listing:CS:246" }, "expansion_kind": "requirements", "bounds": { "max_depth": 1, "max_nodes": 100, "max_edges": 250 }}Expansion kinds:
requirements;unlocks;source_references;credential_mentions;equivalences;conflicts.
The expansion response uses the same shared graph view shape.
21. Source References
Nodes and edges derived from source facts must include source reference ids.
Unparsed requirement nodes must include source reference ids.
Edges derived from requirement conditions must include source reference ids.
Graph views may use compact source reference summaries in the envelope.
The frontend can fetch full source references through source reference endpoints.
22. Unknowns and Conflicts
Graph views must surface uncertainty when it affects projection meaning.
Examples:
- an edge exists because a requirement is partly parsed and partly opaque;
- a node is included because it may satisfy an unparsed or unsupported condition;
- a state overlay cannot determine unlock status because a grade is missing;
- an antirequisite conflict affects a visible course.
Unknowns and conflicts should appear in:
- top-level envelope arrays when response-wide;
- node fields when node-specific;
- edge fields when edge-specific;
- state overlay when state-dependent.
23. Caching
Catalog-only graph views may be cached by:
index_id;view_type;- normalized request body;
projection_version.
State-dependent graph views must use Cache-Control: no-store.
Graph views with supplied state should also use Cache-Control: no-store.
The backend must not include raw state token values in cache keys, logs, or responses.
24. Test Scenarios
Graph view tests should cover:
- view registry response;
- course neighborhood prerequisites only;
- course neighborhood unlocks only;
- course neighborhood both directions;
- credential requirement view preserves nested groups;
- unparsed requirement node included with source reference;
- graph bounds truncate with omitted counts;
- hard bound violation returns
400 bad_request; - unlock overlay requires auth for persisted state;
- unlock overlay works with supplied state;
- target relevance distinguishes listing and credit identity;
- expand node returns same response shape;
- state overlay excludes notes and tokens;
- catalog-only view can use cacheable headers;
- state-dependent view uses
no-store.
25. Acceptance Criteria
An implementation satisfies this spec when:
- every graph endpoint returns common envelopes;
- every graph response declares
view_typeandprojection_version; - every node and edge has a typed semantic kind;
- every response reports applied bounds;
- truncation is visible through
omitted; - state-dependent views never leak tokens;
- source-derived nodes and edges preserve source references;
- requirement groups are not flattened into misleading simple edges;
- frontend layout remains a projection, not backend academic truth.