# AD (Authoring & Delivery) knowledge graph

AD is Pearson's assessment platform. Authors write QTI 3.0 items, stimuli and forms. Host applications such as Navvy
and Abigail embed AD's editors and test player, register student sessions, and receive scores by callback. The
source is the `core-authoring-delivery` monorepo (main branch `dev`).

## Start here
1. [[adam-ad]]: the Go server (Gin + bun/Postgres). It holds the router, the auth middleware and the boot sequence.
2. [[accounts-auth]] and `permissions.md`: tenants, credentials, token kinds, and the `RequiresAccess("Bank", action)`
   model.
3. [[take-test]]: the main delivery journey, from registering a session to the score callback.
4. [[score-session]]: save-off, the single asynchronous scoring pipeline.
5. [[item]], [[form]], [[session]], [[response]]: the core entities. Each has a full Fields table.
6. [[qti-engine]]: QTI parsing, response processing, custom operators, import and export.
7. [[ad-authoring-ui]] and [[player-ui]]: the custom-element contracts that host apps embed.
8. [[feature-flags]] and [[core-env-config]]: every runtime switch.

## Main flows
- Delivery and scoring:
  - [[take-test]]
  - [[score-session]]
  - [[deliver-adaptive-section]]
  - [[evaluate-battery]]
  - [[human-score-response]]
  - [[score-with-oscar]]
  - [[score-qti-item-stateless]]
- Authoring:
  - [[author-item]]
  - [[save-form]]
  - [[import-qti-package]]
  - [[export-qti-package]]
  - [[print-with-xprt]]
  - [[translate-item-with-ai]]
  - [[upload-stat-set]]
- Platform:
  - [[onboard-tenant]]
  - [[authenticate-api-client]]
  - [[notify-host-of-content-change]]

Product features are listed as `capability` nodes (for example [[online-test-delivery]] and
[[embedded-item-authoring]]). The glossary is the set of `concept` nodes (for example [[save-off]],
[[bank-workflow-state]], [[token-issuer]] and [[scoring-test-map]]).

## Module layout
`adam_ad/` is the hub. `src/apis/{admin_api,data_api,delivery_api,scoring_api,job_api}` holds the HTTP handlers, grouped
as `/api/admin`, `/api/data`, `/api/delivery`, `/api/scoring` and `/api/jobs`. `src/services` holds the business logic
(one package, split by file into the graph's modules: [[item-authoring]], [[form-assembly]], [[delivery-engine]],
[[cat-engine]], [[scoring-engine]], [[item-statistics]], [[oscar-scoring]], [[metametrics-scoring]], [[ai-assist]],
[[xprt-orchestration]]). `src/models` holds the bun structs, `src/qti` holds [[qti-engine]], and `src/async` holds the
SQS consumers and crons ([[async-jobs]]). `db/migrations` holds the Postgres DDL.

The frontends are built separately, and each copies its bundle into `adam_ad/client/admin_ui/public/`, which adam_ad
serves at `/public/`:
- [[ad-authoring]] (Vue 3, `ad-authoring.js`)
- [[adam-player]] (Vue 3 + PrimeVue, `ad-test-player.js`)
- [[item-components]] (Lit, `qti-item-components-v1.0.0.js`)

`adam_ad/client/admin_ui` is the operator console ([[admin-ui]]). `adam_ad/client/explorer` is the internal workbench
([[api-explorer]]).

Separate services:
- [[scoring-api]]: Go. It imports adam_ad as a module.
- [[pymathengine]]: Python on Lambda.
- [[ad-cat]]: Java, the CAT engine.

[[adam-ad-scripts]] are offline tools. About half of them no longer compile.

## Conventions for implementers
- **New endpoint.** Add the handler in `adam_ad/src/apis/<group>/` and register it in the group's `Init`
  (`data_api.go` and so on) with `api_utils.RequiresAccess("Bank", "<Action>")`. The action string is registered
  automatically; grant it to credentials through the admin console. Add Swagger annotations and run `make swagger` /
  `make codegen` so the explorer's TypeScript clients regenerate. Changes to exported types in `src/models` can break
  `scoring-api`, so build it afterwards.
- **Migration.** Run `cd adam_ad/db && make migration NAME=<name>` (golang-migrate). Every migration needs both up and
  down scripts wrapped in `BEGIN; … COMMIT;`. Then update the bun struct in `src/models` and the entity node's Fields
  table.
- **Feature flag.** Add a `feature_flags` row (admin console) and read it through `FeatureFlagService` with a code
  default. Rows are cached for 60 s and a missing row returns the default. Add the key to [[feature-flags]].
- **Scoring changes** go through `Stm.Score` and save-off. Re-queue [[session-saveoff-requested]]; never write outcomes
  directly.
- **Interaction types** live in three lists that must stay in sync: item-components `Constants.registerInteraction`,
  `adam_ad/src/utils/qti_utils.go`, and `ad-authoring/src/interaction-type-registry.ts`.
- **Tests.**
  - adam_ad: `source ~/env/ad_local` first, then `make test`. Tests run serially (`-p 1`) against a real Postgres.
  - adam-player and ad-authoring: `npm test` (vitest).
  - item-components: `npm test` (web-test-runner + puppeteer), or `npm run storybook`.
  - E2E: `e2e-automation` (see [[e2e-tests]]).
- **Branches** are named `AD-NNNN-description` and cut from `dev`. Specs are in `shared/docs/spec/`.

## Known risks recorded in the graph
- **Secrets in the repo:** a hard-coded `XPRT_KEY` default in `options.go`, and credentials committed under
  `ad-automation/helper/env`.
- **One signing secret for every token kind:** all tokens are signed with `JWT_SECRET`, and the issuer checks are weak
  (see [[token-issuer]]). The player JWT also embeds the host's OAuth token.
- **Routes without a bank check:** resources, media get, label bundles and parts of `/xprt`. (AD-6126 scoped bank states, item test cases, file deletes, bank secrets and SaveBank child rows; see [[accounts-auth]].)
- **Unauthenticated services:** [[ad-cat-api]] and the OSCAR callback have no authentication.
- **Broken QTI packaging:** bank export (`packaging_api.go` empty `case "bank"`) and multi-file XML import.
