`DRAFT` is the first system tag not assignable to accounts.
- **client** — filter it out of `tagsAvailable` in [create-or-update-account-dialog.component.ts:94](../apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts#L94)
- **client** — filter it out of `tagsAvailable` in [create-or-update-account-dialog.component.ts](../apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts)
- **server** — no guard exists today. `tagService.validateTagIds` only checks ownership, and
[account.service.ts:173](../apps/api/src/app/account/account.service.ts#L173) and
so it passes the date down rather than the service issuing a second query.
### 5 — Switch reads to the tag
The DB filter at [activities.service.ts:639](../apps/api/src/app/activities/activities.service.ts#L639)
becomes `tags: { none: { id: TAG_ID_DRAFT } }`.
becomes `WHERE_ACTIVITY_NOT_DRAFT`.
> **Gotcha:**`where.tags` is already assigned at
> [:757](../apps/api/src/app/activities/activities.service.ts#L757) and `where.OR` at
> [:755](../apps/api/src/app/activities/activities.service.ts#L755). A second assignment
> [:766](../apps/api/src/app/activities/activities.service.ts#L766) and `where.OR` at
> [:764](../apps/api/src/app/activities/activities.service.ts#L764). A second assignment
> silently clobbers the first, so this must go through the existing `andConditions` array.
Field reads to convert:
- [activities-table.component.ts:280](../libs/ui/src/lib/activities-table/activities-table.component.ts#L280) and [:360](../libs/ui/src/lib/activities-table/activities-table.component.ts#L360)
- badge and ICS gate at [activities-table.component.html:180](../libs/ui/src/lib/activities-table/activities-table.component.html#L180) and [:511](../libs/ui/src/lib/activities-table/activities-table.component.html#L511)
- gather gate at [activities.controller.ts:290](../apps/api/src/app/activities/activities.controller.ts#L290)
- badge and ICS gate at [activities-table.component.html:180](../libs/ui/src/lib/activities-table/activities-table.component.html#L180) and [:516](../libs/ui/src/lib/activities-table/activities-table.component.html#L516)
- the synthetic `isDraft: false` literals at [activities.service.ts:530](../apps/api/src/app/activities/activities.service.ts#L530) and [import.service.ts:168](../apps/api/src/app/import/import.service.ts#L168)
- 5 occurrences in [activities-table.component.stories.ts](../libs/ui/src/lib/activities-table/activities-table.component.stories.ts)
That is a fix, though it will look like a change to anyone affected.
`Activity extends Order` loses `isDraft` with the schema. It is dropped from the response
rather than recomputed — the client already receives `tags` and already derives
`isExcludedFromAnalysis` that way, so deriving `isDraft` identically is the consistent end
state.
## Release sequence
Changelog, following the `isExcluded` precedent:
The visibility boundary is the `Tag` row, not the code:
[tag.service.ts:72-81](../apps/api/src/services/tag/tag.service.ts#L72-L81) returns every tag
with `userId IS NULL`, so the moment that row exists the tag appears in every user's selector
and in the admin tag management. Everything else can ship dark.
```markdown
### Changed
- Removed the deprecated `isDraft` attribute of the activity in favor of the _Draft_ tag including a data migration
| **A + B** | steps 1, 2, 3, 4, 6a, and the Basic-subscriber fix from step 8 | a new _Draft_ tag, auto-assigned; no behaviour change, `isDraft` still governs every read |
| **C** | steps 5, 7, 9, rest of step 8 | behaviour moves to the tag |
| **D** | step 6b | nothing |
## Behavioural changes to call out in the pull request
A and B are combined deliberately. On its own, A (the config constant, the i18n label,
`isDraftActivity`, and the account guards) is genuinely invisible — `isSystemTag` cannot match
a row that does not exist, the label names a tag nobody can select, the helper has no callers,
and the guards reject a tag ID no row carries. But it is also entirely unreferenced code, so
shipping it alone buys nothing. B is where the de-risking actually happens: the tag row and
the backfill land while `isDraft` still governs every read, which makes C a pure deploy that
reverts without touching the database.
**Every existing draft becomes visible and removable.** Nothing recomputes the column today,
so a future-dated activity whose date has since passed is excluded from the portfolio
**permanently**. After the backfill those rows carry the _Draft_ tag and are still excluded —
but the user can now see why, and remove it. That is a fix, though it will look like a change
to anyone affected.
The property that makes A + B safe is that **step 4 does not modify the existing `isDraft`
computation** — see the warning there. If that refactor leaks into this release, the release
is no longer inert.
**`activitiesCount` starts including drafts**, which changes a visible number in the accounts
table and enables the **Delete account** guard for accounts holding only drafts.
C and D can be merged if fewer releases are preferred, at the cost of the rollback property
described in 6b.
**A `MANUAL` + `BUY` activity can now become a draft**, where the accidental carve-out