# stapel-tasks 0.1.8

Generic tasks and kanban boards: Board/Column/Task/ChecklistItem/TaskComment, a REST surface, a full outbox event surface, and custom fields via stapel-attributes. Usable standalone (a team runs a board by hand) or as the substrate an external orchestrator projects onto through opaque origin_* handles, a MOVE_POLICY authorization seam, and comm Functions (tasks.get/list_board/create/move/comment).

Contract: axes 3 · extension points 5.
Generated from docs/capabilities.json by `stapel-llms-txt` — do not edit; drift-gated by `make contract-check`.

## Configuration axes — what a product switches on
Settings keys; `default` is what you get by saying nothing. Turning an axis off unmounts the operations it gates.
- MOVE_POLICY [enum, default "stapel_tasks.policy.AllowAllMovePolicy"] — Who/what may move a card between columns
  Decides whether a card may move from one column to another (drag-and-drop / tasks.move): allow / deny(reason_key) / defer (accepted but applied later by an external orchestrator). Default allows any move but honours a per-board transitions whitelist (conf.py, MODULE.md Extension point 2).
- SCOPE_PROVIDER [enum, default "stapel_tasks.scope.DefaultScopeProvider"] — Multi-tenant scoping and permissions
  Resolves the opaque workspace_id from a request, filters querysets by it, and answers viewer/member/admin permission checks. Default is a single global scope that allows everything; a stapel-workspaces-aware host swaps in a real provider — this module never imports stapel-workspaces (conf.py, MODULE.md Extension point 1).
- STORE_UNKNOWN_FEATURES [bool, default true] — Keep unknown custom fields when typed validation is off
  Controls what happens to custom card fields when stapel-attributes isn't installed: keep the raw submitted data, or discard it.

## Extension points — what a product replaces, fork-free
- BOARD_PRESETS [merge_registry]
  Open registry (STAPEL_TASKS['BOARD_PRESETS'] + register_board_preset(key, factory)) merged over the built-in 'simple' preset; None removes a built-in. Guarded by E005/E006 (MODULE.md Extension point 3).
- MOVE_POLICY [dotted_path]
  REPLACE seam: a MovePolicy subclass returning allow()/deny(reason_key)/defer() (policy.py). Guarded by E003/E004 (MODULE.md Extension point 2).
- SCOPE_PROVIDER [dotted_path]
  REPLACE seam: a ScopeProvider subclass with resolve()/filter()/can() (scope.py). Guarded by system checks E001/E002 (MODULE.md Extension point 1).
- custom_field_seam [attribute_bridge]
  Board.feature_defs is a stapel-attributes FeatureDef list; Task.features holds the normalized DAO via services.create_task/update_task -> features.validate_features/normalize_features. Add vertical field types with attributes' own register_feature_type (MODULE.md Extension point 4).
- serializer_seams [class_override]
  Every APIView mixes in SerializerSeamMixin (request_serializer_class/response_serializer_class + get_* methods); the DTOs in dto.py are the API models, never ORM instances (views.py:60-68, MODULE.md Extension point 5).

## Fits with — fleet dependencies
- stapel-attributes (optional) — soft integration for typed custom-field validation on cards; module runs without it (feature seam degrades to a pass-through governed by STORE_UNKNOWN_FEATURES) (pyproject.toml optional-dependencies 'attributes' extra)
- stapel-core (required) — comm bus (task.* emits, tasks.* Functions, user.deleted consume for GDPR anonymization) (pyproject.toml dependency)
