> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-unsw-v6.advance-uac.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-unsw-v6.advance-uac.com/_mcp/server.

# Curriculum structures

A curriculum is a tree of containers. Each container states a requirement, such as "Core
Courses", "Prescribed Electives" or "Choose 12 UOC from the following", and holds subject
entries, child containers, or both.

```
Academic Structure                    (root container)
├── Core Courses                      creditPoints: 72
│   ├── COMP1511  (6 UOC)
│   └── COMP1521  (6 UOC)
└── Prescribed Electives              creditPoints: 24, creditPointsMax: 36
    ├── Level 3 Electives             (child container)
    │   └── COMP3141  (6 UOC)
    └── COMP2511  (6 UOC)
```

We don't impose a shape on the tree. Depth, naming and how you divide requirements are
yours to decide, so build whatever describes your program.

## What a container carries

| Field             | Meaning                                                                               |
| ----------------- | ------------------------------------------------------------------------------------- |
| `title`           | The requirement's name, e.g. "Core Courses"                                           |
| `description`     | The requirement in prose, e.g. "Students must take 30 UOC of the following subjects." |
| `creditPoints`    | UOC that must be taken from this container                                            |
| `creditPointsMax` | Upper bound, where a range applies                                                    |
| `dynamicRules`    | Wildcard requirements ("any General Education subject"), for display only             |
| `items`           | The subject entries                                                                   |
| `children`        | Nested containers                                                                     |

> **Note**
>
> `dynamicRules` exists so we can render handbook text faithfully. Nothing
> evaluates it, and no assessment reads it. If a requirement has to affect an
> outcome, it needs to be an explicit entry.

## Draft first, then publish

Containers are created as `DRAFT`, which is what lets you assemble and review a whole tree
before anything depends on it.

Three operations are allowed only while a container is a draft:

* attaching it to a program or specialisation, with `PUT /structures/{structureId}/owner`
* nesting it under another container, with `PUT /structures/{structureId}/parent`
* deleting it

Publishing with `PUT /structures/{structureId}/status` sets the tree `LIVE`. Assessment reads
live trees, so the shape stops moving at that point, though you can still add and remove
subject entries.

To find work you started and left, `GET /structures/drafts` lists drafts that aren't yet
attached to anything.

## Ownership

A container is owned by at most one program or specialisation, never both. While it is still
a draft it can belong to neither, which is how you build a tree before deciding where it
goes.

Ownership applies to the root of a tree. Child containers belong to their parent and inherit
the owner from it.

Deleting a program or specialisation deletes its containers along with it. If you delete a
specialisation that some other container references, that entry stays where it is with its
link cleared, so you don't silently remove someone else's requirement.

## Adding and removing subjects

```
POST   /institution/catalogue/structures/{structureId}/subjects
DELETE /institution/catalogue/structures/{structureId}/subjects/{code}/{year}
```

A subject must already exist in your catalogue at that code and year before you can add it
to a container. Create the subject first, then reference it.

Entries carry a `resolved` flag. When it's `false`, the code isn't in the catalogue. This
only happens for entries that came from the original handbook import, never for anything you
add through the API.

> **Warning**
>
> Entries have an `itemType` of `Course`, `Major`, `Minor`, `Honours`,
> `Specialisation` or `Research`. A subject entry reports `itemType: "Course"`
> even though the endpoints call it a subject. The stored value predates the
> rename, and changing it would rewrite tens of thousands of historical rows, so
> match on `"Course"` rather than `"Subject"`.

Only `Course` entries can be added through the API. The rest came from the handbook import
and are read-only.

## Reading a curriculum

```
GET /institution/catalogue/programs/{code}/{year}/structure
GET /institution/catalogue/specialisations/{code}/{year}/structure
GET /institution/catalogue/structures/{structureId}
```

The first two return the whole tree, nested, with entries in sibling order. The third
returns a single container and everything beneath it. Both produce the same shape, so one
renderer will handle either.

## A typical build

1. `POST /structures` for the root, then again for each child. All of them start as `DRAFT`.
2. `PUT /structures/{id}/parent` to nest the children.
3. `POST /structures/{id}/subjects` to add entries.
4. `GET /structures/{rootId}` to review the assembled tree.
5. `PUT /structures/{id}/owner` to attach it to its program.
6. `PUT /structures/{id}/status` to publish it `LIVE`.

Steps 1 to 4 are freely reversible. From step 5 onwards the shape is settled.