Skip to content

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:

MethodPathPurpose
GET/api/v1/graph/viewsList supported views and limits.
POST/api/v1/graph/views/course-neighborhoodCourse prerequisite and unlock neighborhood.
POST/api/v1/graph/views/course-universeBounded universal course relation graph for the active published index.
POST/api/v1/graph/views/course-pathwaysRecursive upstream and downstream course pathways around selected courses.
POST/api/v1/graph/views/credential-requirementsCredential requirement structure.
POST/api/v1/graph/views/unlock-overlayState-dependent unlock status for visible nodes.
POST/api/v1/graph/views/target-relevanceRelevance of visible courses to selected credentials.
POST/api/v1/graph/views/expand-nodeExpand 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:

FieldTypeMeaning
view_typestringNamed graph view type.
projection_versionstringResponse contract version.
scopeobjectInput scope normalized by backend.
boundsobjectApplied bounds.
nodesarrayTyped nodes.
edgesarrayTyped edges.
groupsarrayOptional grouping records.
metricsobjectView-level metrics.
omittedobjectOmitted 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:

BoundDefaultHard maximum
max_nodes2502500
max_edges6007500
max_depth24
max_courses1002500
max_credentials525

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:

FieldTypeMeaning
node_idstringNode id within the response.
node_typestringTyped node kind.
academic_object_idstring or nullBacking academic object id.
labelstringShort display label.
source_reference_idsarraySource ids backing this node.
metricsobjectNode-level metrics.
layout_hintsobjectOptional 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 typeMeaning
course_listingVisible course listing such as MATH 135.
course_creditCredit identity shared by linked listings.
credentialDegree, major, minor, specialization, option, or plan target.
requirement_groupParsed non-leaf requirement expression.
requirement_conditionParsed leaf requirement condition.
unparsed_requirementOpaque source requirement fragment.
subject_groupDisplay grouping by subject.
level_groupDisplay grouping by course level.
source_referenceSource 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:

FieldTypeMeaning
edge_idstringEdge id within the response.
edge_typestringTyped edge kind.
source_node_idstringSource node in this response.
target_node_idstringTarget node in this response.
direction_semanticsstringMeaning of edge direction.
source_reference_idsarraySource 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 typeMeaning
requiresTarget requirement needs source item, with direction clarified by view.
unlocksSource course or condition contributes to target availability.
excludesAntirequisite or incompatibility relation.
equivalent_creditListings share or map to one credit identity.
satisfies_requirementCourse or state fact satisfies a requirement condition.
part_of_requirementRequirement group contains subexpression or condition.
belongs_to_credentialRequirement source belongs to a credential.
belongs_to_subjectCourse listing belongs to a subject grouping.
belongs_to_levelCourse listing belongs to a level grouping.
cites_sourceNode 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-neighborhood

Request 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, and excludes edges;
  • 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-universe

Request 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, and equivalent_credit edges when represented by the index;
  • metrics.projection_source, which should be published_artifact when 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-pathways

Request 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-requirements

Request 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, and satisfies_requirement edges 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-overlay

Request 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-relevance

Request 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-node

Request 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_type and projection_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.