[@layout]: ../docs.layout.md

# Completion

Declare completion targets and show progress for Markdown Sources.

Completion owns target membership and flat progress UI. Path, Input, Assessment,
and Work keep their own hierarchy and policy.

---

[@include](../extend/state.reference.md#storage-requirements)

---

## Save a personal checklist

Start with the [local Ada account and writable Home](../accounts/auth.reference.md#local-actor)
in an initialized project. Install the package:

```sh
bun add @pathmx/completion@beta
```

Create `plugins/completion.plugin.ts`:

```ts
import { createCompletionPlugin } from "@pathmx/completion"

export default createCompletionPlugin({ storage: "actor" }).plugin
```

Create `paths/preparation.page.md`:

```md
# Prepare your workshop

- [ ] Choose a topic {#topic}
- [ ] Write a worked example {#example}
- [ ] Rehearse the lesson {#rehearse}

<x-completion-progress />
<x-completion-outline />
```

Run `bun run dev`, sign in as Ada, and open `/preparation.page`. The initial
progress is `0/3`. Check “Choose a topic”: it becomes `1/3`. Reload and confirm
that progress remains. Uncheck it to return to `0/3`. Ada's Home stores the record;
the authored checklist stays unchanged. Signed-out readers cannot change her progress.

For an offline browser publication, keep the same Plugin and configure its
[local Actor and writable Home](../extend/state.reference.md#save-input-and-progress-in-the-browser).
An ordinary static export has no writable Source host. If controls are read-only,
check admission and storage before trying to change the checklist.

---

## Configure storage

Install `@pathmx/completion@beta` in the project root with `bun add`.
Completion requires one explicit storage policy. To switch the preceding example
to shared authored progress, replace its Plugin file with the following and grant
the acting reader write access to the checklist Source through the
[Source access policy](../extend/core.reference.md#choose-the-source-access-policy):

```ts
// plugins/completion.plugin.ts
import { createCompletionPlugin } from "@pathmx/completion"

export default createCompletionPlugin({ storage: "source" }).plugin
```

- `source` stores shared state in authored Markdown and topmatter.
- `actor` stores a `completed` key list below each Actor Home.

Both policies expose the same asynchronous View-bound reader. A browser Source
host uses `storage: "actor"` with its local Actor and writable Home; progress is
stored in ordinary [State Sources](../extend/state.reference.md).

---

## Author targets

Every Markdown task is a completion target; an id is optional:

```md
- [ ] Install dependencies
- [ ] [Review the guide](./guide.md)
- [ ] Ship the release {#ship}
```

Identity uses an explicit `{#id}`, then one exact local Source/fragment link,
then normalized visible text. Duplicate identities are lint errors.

Removing a target leaves its saved Actor completion key as history. Lint reports
`completion/state.target-missing` as a warning; current progress counts only
declared targets, and new completion writes to a removed target are rejected.
You do not need to edit learner records to remove an obsolete task. Reusing the
same key reconnects its saved completion, so give a different task a new id.
Malformed declarations, invalid saved state and missing origin Sources remain
errors.

The presence of `completion` declares a Source target:

```md
---
completion:
  label: Finish this lesson
---
```

It declares a Block target when that Block has an authored id:

```md
<!--
id: practice
completion:
  label: Finish the practice
-->
```

Source storage changes task `[ ]`/`[x]` or the target's `complete` field. Actor
storage does not rewrite authored content.

---

## Progress UI

Layouts and Sources can render the represented Source's flat projection:

```md
<x-completion-progress />
<x-completion-outline />
```

Progress renders a short native meter with a compact count such as `2/3`.
Outlines use aligned status indicators; authorized manual requirements have
checkboxes, while derived and read-only requirements remain indicators.
Manual controls submit existing Completion Actions. Without JavaScript, a
labeled submit button provides the same action.

Both components omit visible headings by default. Supply `label="Requirements"`
to show a label (progress) or heading (outline). Progress retains an accessible
count and outlines retain an accessible name when `label` is omitted. Sources
with no requirements render neither component.

Presentation uses Core's `.pmx-progress`, `.pmx-progress-count`, `.pmx-task-row`,
and `.pmx-task-status` styles. Existing `.pmx-completion-*` classes and status
attributes remain available for local layout overrides.

---

## Target another Source or Block

Both components accept a `source` reference to another Source or an exact Block:

```md
<x-completion-progress source="./project.project.md#practice" />
<x-completion-outline source="#practice" />
```

Omitting `source` uses the represented page, including in a layout. An explicit
reference resolves beside the authoring file. A fragment must identify a Block,
not an arbitrary heading. Cross-Source outline links and manual Actions point
at the selected Source and respect its permissions.

Block summaries count requirements associated with that Block. Source summaries
count all requirements once, including requirements without a Block. Neither
summary adds a target or stores separate rollup state. `completion:` on a Source
or Block still declares an independent manual requirement, not rollup settings.
Provider `block` values must identify existing Blocks. Single-Block Input
responses carry their Block; a response spanning Blocks remains Source-scoped.

---

## Read progress in a Plugin

The same reader supports Block scope through a canonical reference:

```ts
await completion.reader.summary(view, { id: "/project.project#practice" })
```

To present the requirements themselves, read their items:

```ts
const items = await completion.reader.items(view, { id: "/project.project?iid=retry" })
// [{ ref: { source: "/project.project?iid=retry", key: "task/submit" },
//    label: "Submit your project", block: "block-0",
//    interaction: "manual", complete: false, settable: true }]
```

`items()` accepts the same Source, instance and Block references as `summary()`
and returns the leaf requirements in authored order. `settable` is true when this
View may change the item with `completion.set`. Derived items and unreadable
Sources are never settable; Source storage cannot set a named instance, because its
instances share the authored file. A control posts the item's `ref` as
`_target` and `key` to `/actions/completion.set`.

Custom components read this same View-bound summary during rendering.
Readers admit their target and backing State Sources through the active View, so contextual restrictions and query invalidation also apply to derived completion.
The normal View refresh updates their markup after Actions and live changes.

Authored task checkboxes are also projected as read-only or actionable controls
for the current View. Derived targets remain read-only and transition
through their owning UI.

---

## Composition

Use this composition in place of the preceding Completion Plugin when Input
submission should also contribute progress. `view` and `source` in the reader
calls come from your host or component render context; the calls illustrate that
API and are not additional top-level project setup. Manual and derived domains
use separate seams:

```ts
import { createInputPlugin } from "@pathmx/core/plugins"
import { createCompletionPlugin } from "@pathmx/completion"

const input = createInputPlugin()
const completion = createCompletionPlugin({
  storage: "actor",
  derived: [input.completion],
})

await completion.reader.isComplete(view, {
  source: "/lessons/setup.page",
  key: "task/install",
})

await completion.reader.summary(view, source)
// { status: "in-progress", complete: 2, incomplete: 1, total: 3 }
```

`targets` adds manual declarations for Actor storage. Source storage takes its
manual declarations from authored tasks and Source/Block `completion` props so
each target has a direct mutation representation. `derived` works with either
storage policy and adds declarations plus `isComplete(view, ref)`, which may
return a boolean or a Promise, while its owner retains the backing state.

`summary()` projects authored tasks, Source/Block completion, manual provider
targets, and derived provider targets through the same reader. It returns
`status: "none"` when a Source declares no targets; an empty target set is not
implicitly complete. Contributors should declare leaf requirements rather
than rollups that double-count their own children.


`reader.manualTarget(view, { id })` returns the unfinished, directly authored
Source/Block acknowledgment when all other requirements in that scope are complete.
It returns no target for derived requirements or ordinary task-only Sources.
The returned `{ source, key }` retains instance identity. Before presenting a
control, find that item in `items()` and check `settable`.
