---
title: "Task Delivery"
url: https://docs.growamal.com/en/guides/task-delivery/
language: en
---

# Task Delivery

The task delivery service (WP-DTE-BE, `modules/delivery`) selects a stored, render-ready item from the item bank and serves it **exactly as stored**. The platform never composes, templates, or generates content at runtime.

## Item-bank-only model

All items are authored and validated offline, then imported into the item bank through an import pipeline that runs Arabic agreement checks (gender, number, case), metadata validation, language-safety screening, and difficulty-prior (`delta_prior`) computation at import time. By the time an item enters the bank it is ready to be served without any runtime transformation.

When the delivery service runs, it does exactly three things: **filter**, **serve**, and **log**. No content is assembled, no language model is invoked, and no template is expanded.

## Selection filters

Each serve request specifies a target context. The engine applies the following predicates in combination:

| Filter | Description |
|---|---|
| `subSkillId` | Must match the sub-skill being practiced or assessed |
| `taskMode` | `practice`, `quick_check`, or `progress_monitoring_probe`; each has distinct UX rules |
| `scaffoldTier` | `Level_1_Foundation`, `Level_2_Targeted`, or `Level_3_Independence` (`progress_monitoring_probe` skips tier) |
| `difficultyBand` | Difficulty band anchored to `delta_prior` |
| `dialect` | `MSA` / `LEV`; items whose dialect tags are absent are treated as dialect-agnostic |
| Recently served | Items served to the same student in the recent-serve window are excluded |

When multiple items pass all filters the engine applies a fully deterministic tie-break: closest difficulty band first, then `delta_prior` ascending, then `itemBankId` lexicographic. The selection is byte-identical for identical inputs, which satisfies the V-6 determinism rule.

## Scaffold tier resolution

The scaffold tier is per (student × skill × bundle × task type), never a global student-level attribute. For tiered task modes (practice or quick_check within an active bundle) the delivery service looks up the student's `student_scaffold_assignment` row. If no assignment exists the serve returns `no_eligible_item`. A random fallback tier is never applied.

Probes (diagnostic `quick_check` outside a bundle) select tier-agnostically.

## Serve outcomes

Every serve response is HTTP `200`. When no item is served, `served` is `null` and `reason`
carries one of the non-ok outcome codes. The caller is responsible for choosing the correct UX
for each case.

| Outcome | `served` | Meaning | What to do |
|---|---|---|---|
| `ok` | item payload | Item selected and passed all checks | Render the item |
| `no_eligible_item` | `null` | No item passed the filter combination | Surface the gap UX; the outcome writes a gap-queue row |
| `blocked_by_visibility_rule` | `null` | An item was selected but failed the student-visibility wrapper at serve time | Treat as a content-pipeline error; surface the same gap UX as `no_eligible_item` |
| `failed` | `null` | Unexpected internal failure | Retry once; if it persists, escalate |

### `no_eligible_item`

Every `no_eligible_item` outcome writes a gap-queue row. The admin gap summary endpoint
aggregates these rows by (sub\_skill × tier × mode) so that offline content authoring teams know
exactly which combinations need new items.

> **Warning:** `no_eligible_item` is not an error. It is a gap signal. Never treat it as a 4xx or 5xx. Surface it to the content pipeline so the gap is filled offline.

### `blocked_by_visibility_rule`

The visibility wrapper runs a final check on the selected item's content payload immediately
before the serve is written. If the check finds an internal label that must not reach a student,
the serve is aborted and logged as `blocked_by_visibility_rule`. The import gate is designed to
prevent such items from reaching operational status, so this outcome indicates a content-pipeline
gap that needs investigation.

From a caller perspective, `blocked_by_visibility_rule` should be treated the same as
`no_eligible_item`: `served` is `null`, the UX should reflect the absence of an item, and the
incident appears in the gap summary so the content team can investigate.

## Served-task-instance audit

Every successful serve writes a `served_task_instance` row with a UUID primary key. This UUID is the join token for printed QR worksheets (session-gen and paper-scan flows). UUIDs are non-enumerable and globally unique, making them safe to embed on physical paper, unlike auto-increment integers.

The audit row pins the following for permanent reproducibility:

- Item-bank snapshot version and selection-policy version
- Scaffold tier and rule version used
- The recently-served exclusion window
- Request context (sub-skill, mode, band, dialect)

## Dry-run replay (V-6 verification)

`POST /api/admin/delivery/dry-run-replay` re-runs item selection against the same pinned versions. It writes nothing. The response reports whether each replayed request returns the byte-identical item. A `false` result is a determinism regression.

## API reference

| Method | Path | Permission | Notes |
|---|---|---|---|
| `POST` | `/api/delivery/task` | `VIEW_OWN_CLASSES` or `RUN_BACKOFFICE` | Serve one item; `served:null` is 200 |
| `GET` | `/api/delivery/log/{taskInstanceId}` | `RUN_BACKOFFICE` | Full serve audit for one instance |
| `GET` | `/api/admin/delivery/gap-summary` | `RUN_BACKOFFICE` | Gap counts by (sub\_skill × tier × mode) |
| `POST` | `/api/admin/delivery/dry-run-replay` | `RUN_BACKOFFICE` | V-6 determinism assertion; read-only |

> **Note:** There is no student-facing delivery endpoint. All delivery routes require a teacher or admin permission. Students never make direct calls to this service (B.17).

## What delivery does not do

- It does not generate or compose content at runtime.
- It does not invoke a language model at any point.
- It does not fall back to a substitute item when the requested tier has no match.
- It does not validate Arabic agreement at serve time (that check runs at import).
- It does not expose a student-facing endpoint.

## Related

- [Skills Taxonomy](https://docs.growamal.com/en/guides/skills-taxonomy/): sub-skill IDs used as selection keys
- [Student Profiles](https://docs.growamal.com/en/guides/student-profiles/): scaffold tier source
- [Intervention Bundles](https://docs.growamal.com/en/guides/intervention-bundles/): bundle context that governs tier selection
- [Language Safety](https://docs.growamal.com/en/guides/language-safety/): import-time Arabic agreement and safety gate
