@smartkomm/plexus-cli 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +863 -2
  2. package/dist/cli.mjs +5432 -0
  3. package/dist/skill/.managed-by-plexus-cli +1 -0
  4. package/dist/skill/SKILL.md +224 -0
  5. package/dist/skill/references/accounts/email-changes.md +46 -0
  6. package/dist/skill/references/agents/use-shared-tools.md +52 -0
  7. package/dist/skill/references/applications/inspect-process-store.md +61 -0
  8. package/dist/skill/references/applications/start-a-test-application.md +66 -0
  9. package/dist/skill/references/audit/answer-access-request.md +115 -0
  10. package/dist/skill/references/config/attach-a-template.md +117 -0
  11. package/dist/skill/references/config/init-tenant.md +121 -0
  12. package/dist/skill/references/config/onboard-tenant.md +71 -0
  13. package/dist/skill/references/config/rename-program-stage.md +99 -0
  14. package/dist/skill/references/config/set-tenant-branding.md +150 -0
  15. package/dist/skill/references/config/set-tenant-terms.md +50 -0
  16. package/dist/skill/references/config/set-up-billing-catalog.md +73 -0
  17. package/dist/skill/references/config/update-tenant-config.md +174 -0
  18. package/dist/skill/references/config/wipe-tenant-config.md +107 -0
  19. package/dist/skill/references/export/create-named-export.md +47 -0
  20. package/dist/skill/references/feedback/create-and-triage-feedback.md +69 -0
  21. package/dist/skill/references/feedback/manage-feedback-tags.md +57 -0
  22. package/dist/skill/references/finance/change-contract-grant.md +36 -0
  23. package/dist/skill/references/finance/record-submission-date.md +33 -0
  24. package/dist/skill/references/interessenten/list-interessenten.md +54 -0
  25. package/dist/skill/references/marketing/demo-video-links.md +101 -0
  26. package/dist/skill/references/notifications/debug-mail-delivery.md +77 -0
  27. package/dist/skill/references/notifications/mail-level.md +90 -0
  28. package/dist/skill/references/notifications/reword-a-letter.md +155 -0
  29. package/dist/skill/references/notifications/trace-a-login.md +50 -0
  30. package/dist/skill/references/offer/add-workflow-node.md +268 -0
  31. package/dist/skill/references/offer/addresses.md +129 -0
  32. package/dist/skill/references/offer/appointment-task.md +79 -0
  33. package/dist/skill/references/offer/assignment-rule.md +195 -0
  34. package/dist/skill/references/offer/attach-form-to-workflow.md +79 -0
  35. package/dist/skill/references/offer/author-form-scenarios.md +97 -0
  36. package/dist/skill/references/offer/author-manifest-meta.md +94 -0
  37. package/dist/skill/references/offer/author-smartform.md +102 -0
  38. package/dist/skill/references/offer/bind-node-input.md +76 -0
  39. package/dist/skill/references/offer/completeness-check.md +137 -0
  40. package/dist/skill/references/offer/countersign-node.md +91 -0
  41. package/dist/skill/references/offer/create-pedagogy-unit.md +55 -0
  42. package/dist/skill/references/offer/declare-node-reads.md +99 -0
  43. package/dist/skill/references/offer/edit-live-tenant-offer.md +144 -0
  44. package/dist/skill/references/offer/edit-smartform.md +121 -0
  45. package/dist/skill/references/offer/edit-workflow-node.md +126 -0
  46. package/dist/skill/references/offer/engage-push-node.md +147 -0
  47. package/dist/skill/references/offer/insert-and-rewire-node.md +67 -0
  48. package/dist/skill/references/offer/node-tasks.md +162 -0
  49. package/dist/skill/references/offer/optional-assessment.md +65 -0
  50. package/dist/skill/references/offer/publish-offer-bundle.md +117 -0
  51. package/dist/skill/references/offer/set-offer-capacities.md +78 -0
  52. package/dist/skill/references/offer/staff-input-if-node.md +112 -0
  53. package/dist/skill/references/offer/task-appointment-result.md +69 -0
  54. package/dist/skill/references/offer/task-family-request.md +69 -0
  55. package/dist/skill/references/offer/task-result-files.md +52 -0
  56. package/dist/skill/references/offer/template-document-node.md +113 -0
  57. package/dist/skill/references/offer/workflow-default-channels.md +69 -0
  58. package/dist/skill/references/resolver/manage-contract-resolver.md +161 -0
  59. package/dist/skill/references/retention/erase-due-applications.md +99 -0
  60. package/dist/skill/references/retention/erase-due-parties.md +107 -0
  61. package/dist/skill/references/roles/migrate-parent-assignments.md +31 -0
  62. package/dist/skill/references/signing/README.md +113 -0
  63. package/dist/skill/references/signing/connect-a-school.md +90 -0
  64. package/dist/skill/references/signing/rotate-the-key.md +41 -0
  65. package/dist/skill/references/stammdaten/purge-deleted-person.md +68 -0
  66. package/dist/skill/references/stripe/connect-a-school.md +107 -0
  67. package/dist/skill/references/stripe/request-and-verify-a-payment.md +78 -0
  68. package/dist/skill/references/test-actions/drive-a-test-application.md +69 -0
  69. package/dist/skill/references/users/add-platform-admin.md +59 -0
  70. package/dist/skill/references/users/add-platform-user.md +59 -0
  71. package/dist/skill/references/users/add-tenant-admin.md +75 -0
  72. package/dist/skill/references/users/change-login-email.md +55 -0
  73. package/dist/skill/references/users/cli-access-for-tenant-admin.md +117 -0
  74. package/dist/skill/references/users/delete-permission-group.md +76 -0
  75. package/dist/skill/references/users/grant-cross-tenant-access.md +57 -0
  76. package/dist/skill/references/users/grant-feedback-assignable.md +43 -0
  77. package/dist/skill/references/users/set-password.md +57 -0
  78. package/dist/skill/references/users/use-test-account.md +79 -0
  79. package/dist/skill/references/worker-ops/debug-worker-jobs.md +90 -0
  80. package/dist/skill/references/worker-ops/verify-migrations.md +51 -0
  81. package/dist/spec.snapshot.json +156821 -0
  82. package/package.json +53 -4
@@ -0,0 +1,55 @@
1
+ # Change a user's login e-mail
2
+
3
+ **When to use:** an account's login address has to move — above all a made-up
4
+ test account on a real, deliverable domain (`test100@<domain>.com`) that must go to a
5
+ reserved one (`test100@<domain>.test`) so no mail can ever reach a stranger.
6
+
7
+ ## Prerequisites
8
+ - A platform token with `_platform.users.manage` for the target env.
9
+ - The account exists (`plexus-cli --env <env> users show <email>`).
10
+ - The new address is free: the verb refuses one held by another account, in the
11
+ plan already.
12
+
13
+ ## Steps
14
+ ```bash
15
+ # 1. Plan — runs every check, writes nothing
16
+ plexus-cli --env <env> users set-email <old@domain> --to <new@domain>
17
+
18
+ # 2. Apply
19
+ plexus-cli --env <env> users set-email <old@domain> --to <new@domain> --confirm
20
+ ```
21
+
22
+ For a test account, keep the local part and replace the domain's TLD with
23
+ `.test` (RFC 2606/6761): `test100@<domain>.com` → `test100@<domain>.test`,
24
+ `ada@<schule>.de` → `ada@<schule>.test`.
25
+
26
+ ## Verify
27
+ ```bash
28
+ plexus-cli --env <env> users show <new@domain> # same userId, same tenants
29
+ plexus-cli --env <env> users show <old@domain> # → No user with email …
30
+ ```
31
+
32
+ ## Idempotency / gotchas
33
+ - Re-running with the address already moved prints "no change".
34
+ - One identity, every tenant: an account shared by several tenants moves in all
35
+ of them at once. (Staff editing the party's E-Mail field cannot do this — that
36
+ path refuses a shared identity and points here.)
37
+ - The password is untouched; set one afterwards with `users set-password` if needed.
38
+ - **Mailbox proof** is reset (the holder re-confirms via a mailed link before
39
+ they can move a process again) — except for a reserved test address (`.test`,
40
+ `.example`, `.invalid`, `.localhost`, `example.com/.net/.org`): no mailbox
41
+ exists there, so the proof stays as it was (`proof: unchanged`), and no mode ever transmits
42
+ such an address (`skipped: reserved_address`).
43
+ - **Config-declared accounts** (a staff roster in tenant config): re-export the
44
+ tenant config afterwards (`plexus-cli --env <env> --tenant <slug> config export`).
45
+ Applying an older export that still names the old address re-creates an
46
+ identity on it — config accounts are matched by e-mail.
47
+ - Only the login changes. Old addresses in history (sent deliveries, value
48
+ history, contract snapshots) stay as they were; a queued delivery to the old
49
+ address ends `skipped: recipient_changed`.
50
+ - An account-LESS party's e-mail (a contact, a second parent) is a stored value,
51
+ not a login — change it with `plexus-cli call parties:updateValue …` instead.
52
+
53
+ ## Related
54
+ - [Set (rotate) a user's password](./set-password.md)
55
+ - [Debug a mail delivery](../notifications/debug-mail-delivery.md)
@@ -0,0 +1,117 @@
1
+ # CLI access for a tenant admin
2
+
3
+ **When to use:** a school's own administrator (no platform rights) should run
4
+ `plexus-cli` for **exactly their tenant**: export, plan and apply its config
5
+ (`config export|apply|set`) and its admission offers (`offer …`). Example: the
6
+ administrator of one school group onboarding their own offers.
7
+
8
+ The person gets a `plx_…` token that acts **as them**. It reaches exactly the
9
+ tenants their own login reaches — no other tenant, no platform surface. **A token
10
+ works only while its owner holds `administration.apiTokens.manage`** (platform:
11
+ `_platform.apiTokens.manage`): every CLI call asks the right again, and the
12
+ change that takes the right away revokes the person's tokens.
13
+
14
+ ## Prerequisites
15
+ - **The person is a tenant member with a login.** No login yet →
16
+ [Add a tenant admin](./add-tenant-admin.md) first.
17
+ - **Every `tenant_admin` holds the three CLI rights** — `administration.apiTokens.manage`
18
+ (mint a token), `administration.config.read` / `.manage` (config, held
19
+ **tenant-wide**). They are part of the derived `tenant_admin` and reach existing
20
+ tenants on every deploy; nothing to grant. Someone outside `tenant_admin` needs a
21
+ group holding them.
22
+ - **Offer commands** need `bewerbungsmanagement.forms.configure` (in `tenant_admin`).
23
+ A config change to groups or members also needs
24
+ `administration.permissions.manage` / `.users.manage` (in `tenant_admin`).
25
+ - **Your auth (issuing for them):** a platform token with `_platform.users.manage`
26
+ **and** `_platform.apiTokens.manage` — the token acts as the person.
27
+ - `--env` is required for the write; prod also needs `--allow-prod`.
28
+
29
+ ## Steps
30
+
31
+ **0 — check the person is a `tenant_admin`:**
32
+
33
+ ```bash
34
+ plexus-cli --env <env> users show <email> # groups: tenant_admin, …
35
+ ```
36
+
37
+ **A — the administrator mints it themselves:** in the app, *Einstellungen →
38
+ API-Token* („Token erstellen"), or with a token they already have:
39
+ `plexus-cli --env <env> tokens create laptop`.
40
+
41
+ **B — the platform issues it for them** (to hand over directly):
42
+
43
+ ```bash
44
+ plexus-cli --env <env> tokens create cli --for <email> --output /tmp/token-<slug>.csv --quiet
45
+ ```
46
+
47
+ - The token is shown **once**. `--output` appends `email,token` to the CSV (a file
48
+ with a different header, e.g. a password file, is refused); `--quiet` keeps it out
49
+ of stdout. Without `--output`, it is printed.
50
+ - Refused when the address has no account (`kein Konto`), no login, or holds
51
+ `administration.apiTokens.manage` in no tenant (`in keinem Mandanten`) — including
52
+ platform operators, who are tenant-less.
53
+ - The token records who issued it.
54
+
55
+ **The administrator's setup** (`~/.config/agent-tools/plexus.yml`; see SKILL.md →
56
+ Connect, or `plexus-cli --help`). The platform operator provides the staging URLs:
57
+
58
+ ```yaml
59
+ plexus:
60
+ default_env: staging
61
+ default_tenant: <slug>
62
+ environments:
63
+ staging: { convex_url: "https://<staging-convex-url>", site_url: "https://<staging-site-url>", token: "plx_…" }
64
+ # Required even without a prod token: every write compares its target with
65
+ # the prod URL (refused otherwise: "names no environments.prod.convex_url").
66
+ prod: { convex_url: "https://db.plexus.swop.schule", site_url: "https://actions.db.plexus.swop.schule" }
67
+ ```
68
+
69
+ ## Verify
70
+
71
+ As the administrator's token:
72
+
73
+ ```bash
74
+ plexus-cli --env <env> whoami
75
+ plexus-cli --env <env> config export --tenant <slug> --out ./<slug>/config.json # passes
76
+ plexus-cli --env <env> config export --tenant <other-slug> --out /tmp/x.json # FORBIDDEN / NOT_FOUND
77
+ plexus-cli --env <env> config apply --tenant <slug> --from snapshot:./<slug>/config.json # dry run passes
78
+ ```
79
+
80
+ ## What a tenant-scoped apply refuses (FORBIDDEN, naming the field)
81
+ - a tenant that does not exist (no onboarding), `--from blueprint`, `--allow-destructive`;
82
+ - any `tenant.*` setting except `branding`, `brandingAssets`, `notificationChrome`
83
+ differing from live — modules (`enabledModules` decides the permission allowlist),
84
+ `defaultApplicantGroupKey`, `mailLevel`, `magicLinkLogin`, terms, …;
85
+ - the import `source`;
86
+ - a `foundation.users` address that is not an **active** member with a login (no new
87
+ accounts, no other tenant's people, no revoked member brought back);
88
+ - a `_platform.*` right in any permission group.
89
+
90
+ A change to the permission groups additionally needs `administration.permissions.manage`,
91
+ a change to `foundation.users` (who sits in which group) `administration.users.manage` —
92
+ the rights the app asks for the same act (`tenant_admin` holds both).
93
+
94
+ Copy those values from `config export` unchanged; a change to them is a platform task.
95
+ Platform-only regardless: `config wipe`, `config prune-offers`, `config apply --scope
96
+ platform`, setup mode (Load N / wipe), `offer hard-delete`.
97
+
98
+ ## Idempotency / gotchas
99
+ - Minting again makes a **second** token; revoke the old one with `tokens list` →
100
+ `tokens revoke <id>`. Without the right, the CLI refuses every call — listing and
101
+ revoking your own tokens then works in the app (Settings → API-Token) only.
102
+ - Taking the right away (group removed or narrowed, tenant access ended, user
103
+ deactivated, module dropped by `config apply` or the deploy, platform right
104
+ dropped) **revokes** the person's tokens in the same change. Giving the
105
+ right back does not revive them — mint a new one. The exchange then answers 401
106
+ „Das API-Token „…“ wurde am <Datum> widerrufen: <Grund>“. A right that lapses without a
107
+ write is still caught: every CLI call asks it and refuses with 403 „Ihr Konto hat
108
+ das Recht „API-Tokens: Verwalten“ nicht mehr …“.
109
+ - A tenant member cannot be made platform admin (`users set-platform-admin`):
110
+ platform rights and tenant access exclude each other — an inactive access row
111
+ counts too.
112
+ - A unit-scoped membership does not count for config: the right must be held
113
+ tenant-wide.
114
+
115
+ ## Related
116
+ - [Add a tenant admin](./add-tenant-admin.md) — the login itself.
117
+ - [Edit a live tenant's offer](../offer/edit-live-tenant-offer.md).
@@ -0,0 +1,76 @@
1
+ # Delete a tenant permission group
2
+
3
+ Remove a permission group that should not exist — typically one created by an
4
+ accidental click in Administration → Berechtigungen, which the admin UI can
5
+ create and rename but **not** delete.
6
+
7
+ ## When to use
8
+
9
+ - A stray group ("Neue Berechtigungsgruppe") with no members.
10
+ - A team that was retired and whose work has been re-pointed elsewhere.
11
+
12
+ Not for: taking someone's access away — that is `users unset-group` (leave the
13
+ group, keep the tenant) or `users remove` (revoke tenant access).
14
+
15
+ ## Prerequisites
16
+
17
+ - `bewerbungsmanagement`-independent: the mutation gates on the tenant's
18
+ **permissions-manage** grant, so a tenant admin or platform admin token.
19
+ - The group must be genuinely unreferenced (see *Guards*).
20
+
21
+ ## Commands
22
+
23
+ ```bash
24
+ # 1. See what the tenant has.
25
+ plexus-cli --env prod users groups --tenant <slug>
26
+
27
+ # 2. Dry-run — prints the member count it would be deleting against.
28
+ plexus-cli --env prod users groups delete neue_berechtigungsgruppe \
29
+ --tenant <slug>
30
+
31
+ # 3. Commit.
32
+ plexus-cli --env prod users groups delete neue_berechtigungsgruppe \
33
+ --tenant <slug> --confirm --allow-prod
34
+ ```
35
+
36
+ ## Verify
37
+
38
+ ```bash
39
+ plexus-cli --env prod users groups --tenant <slug> # key is gone
40
+ ```
41
+
42
+ Config is DB-authoritative, so the group is gone from the tenant's config too. A
43
+ platform operator can confirm it in a `config export` (`foundation.permissionGroups`).
44
+
45
+ ## Guards (the delete REFUSES when…)
46
+
47
+ | Refusal | Meaning | Fix first |
48
+ |---|---|---|
49
+ | `has-members` | Someone is still in the group (the message names them) | `users unset-group` each |
50
+ | `owns-teams` | A running application binds this group as a work owner | Re-assign those applications |
51
+ | `referenced-by-workflow` | A stored graph routes a decision/assign (or a not-yet-migrated legacy team-owner binding) to this key | Re-point the node, re-publish |
52
+ | `structural` | It is `tenant_admin` | — (never deletable) |
53
+ | `default-applicant` | It is the tenant's `defaultApplicantGroupKey` | Point the default at another group |
54
+ | `unknown` | No group with that key in this tenant | Check `users groups` |
55
+
56
+ ## Gotchas
57
+
58
+ - **Hard delete, on purpose.** There is no soft delete and no `--restore`. A
59
+ hidden-but-present group would need an `active` flag every reader honours, and
60
+ a reader that forgets one leaves a ghost — exactly the bug that let a removed
61
+ user keep a `user_tenant_access` row and made `config export` throw for that
62
+ tenant. Re-creating an empty group is one click; a ghost is a day of debugging.
63
+ - **The group's `custom_field_permissions` go with it.** Those grants are the
64
+ group's own field visibility settings and mean nothing without it. Everything
65
+ else refuses rather than cascades.
66
+ - **Key, not label.** `users groups delete` takes the stable `key`
67
+ (`neue_berechtigungsgruppe`), never the display label — renaming a group must
68
+ not change what the command targets.
69
+
70
+ ## Related
71
+
72
+ - [Add a tenant admin](./add-tenant-admin.md)
73
+ - [Update a tenant's config](../config/update-tenant-config.md) — the declarative
74
+ route: drop the group from the exported config and `config apply --confirm
75
+ --allow-destructive` sweeps it as an orphan. Equivalent outcome; use the CLI
76
+ verb for a one-off, the config route when the change belongs in the file.
@@ -0,0 +1,57 @@
1
+ # Grant every tenant's users access to another tenant
2
+
3
+ **When to use:** let **all existing users of every tenant** reach one shared tenant
4
+ — the canonical case is giving every client access to the **Test Tenant** (`test`)
5
+ so they can dogfood the product. One-time + manual: it grants the users that exist
6
+ **now**; new users are **not** auto-enrolled (re-run later to pick up newcomers).
7
+
8
+ > Pair this with the Test-Tenant feedback-privacy rule: the Test Tenant grants no
9
+ > `administration.feedback.viewAll` (part of the `test` tenant's config), so
10
+ > "Alle Rückmeldungen" is unavailable
11
+ > there and each client only sees their own feedback.
12
+
13
+ ## Prerequisites
14
+ - **Env:** target with `--env <local|staging|prod>` (this is a platform operation —
15
+ no `--tenant` needed). `plexus-cli --env <env> tenants` lists the reachable slugs.
16
+ - **Auth:** a platform-admin token with `_platform.users.manage`. Confirm with
17
+ `plexus-cli --env <env> whoami`.
18
+ - **Tooling:** an installed `plexus-cli` (`plexus-cli users grant-all --help`).
19
+
20
+ ## Steps
21
+ Run once per **source** tenant, pointing every one at the target (`test`):
22
+ ```bash
23
+ # Repeat for each non-test tenant slug:
24
+ plexus-cli --env <env> users grant-all --from <sourceSlug> --to test --group tenant_admin
25
+ ```
26
+
27
+ To enumerate the source slugs to loop over:
28
+ ```bash
29
+ plexus-cli --env <env> tenants # list slugs; run grant-all for each but `test`
30
+ ```
31
+
32
+ - `--group tenant_admin` (the default) lets the granted users explore every module in
33
+ the Test Tenant. Pass a different group key for a narrower role.
34
+ - Each user is granted idempotently; a user who already has access is a no-op.
35
+ - Platform operators (tenant-less identities) never appear in a tenant's user set and
36
+ are additionally rejected by the `_platform.* XOR tenant-access` invariant — they
37
+ show up under `skipped` with a reason rather than aborting the run.
38
+
39
+ ## Verify
40
+ ```bash
41
+ plexus-cli --env <env> users show <someEmailFromSource>
42
+ ```
43
+
44
+ Success: the user now lists the `test` tenant with group `tenant_admin, applicant`.
45
+ The command output also prints `Granted N/M users …` plus any `skipped` rows.
46
+
47
+ ## Idempotency / gotchas
48
+ - **Current users only** — there is no standing auto-grant; re-run to enroll users
49
+ added after the last run.
50
+ - Re-running is safe (idempotent per user); it neither rotates passwords nor creates
51
+ logins (contrast `users provision`).
52
+ - Runtime-only grants — a destructive reseed of the source/target tenant drops them;
53
+ re-run this playbook afterwards.
54
+
55
+ ## Related
56
+ - [Add a tenant admin](./add-tenant-admin.md) — single ready-to-log-in account.
57
+ - Command reference: `plexus-cli users grant-all --help`.
@@ -0,0 +1,43 @@
1
+ # Make a platform admin assignable to feedback issues
2
+
3
+ **When to use:** a platform admin should appear in the feedback-board assignee
4
+ picker (and be a valid `feedback assign` target). Being a platform admin is **not**
5
+ enough — assignability is the opt-in `_platform.feedback.assign` permission, granted
6
+ explicitly to the platform's real triage admins only.
7
+
8
+ ## Prerequisites
9
+ - `--env staging|prod` configured with a platform-operator token (the grant is gated
10
+ `_platform.users.manage`).
11
+ - The target already exists as a platform admin (`plexus-cli --env <env> platform users`).
12
+
13
+ ## Steps
14
+ ```bash
15
+ plexus-cli --env <env> users set-platform-permission <email> \
16
+ --permission _platform.feedback.assign --on
17
+ ```
18
+ Example — grant one triage admin on prod:
19
+ ```bash
20
+ plexus-cli --env prod users set-platform-permission --allow-prod ada@example.com \
21
+ --permission _platform.feedback.assign --on
22
+ ```
23
+
24
+ ## Verify
25
+ ```bash
26
+ # The assignee picker lists EXACTLY the assignable admins:
27
+ plexus-cli --env <env> feedback admins
28
+ ```
29
+
30
+ ## Idempotency / gotchas
31
+ - **Opt-in, not part of the full-admin grant.** Making someone a full platform admin
32
+ (`users set-platform-admin … --on`) does **not** grant assignability; this perm is
33
+ excluded from the default set on purpose. Re-affirming full-admin status *preserves*
34
+ an already-granted assign perm (it is never silently stripped).
35
+ - Revoke with `--off`. Removing the last permission from a row deletes the
36
+ `platform_admins` row entirely (the identity stays a normal user).
37
+ - `_platform.* XOR tenant-access`: the grant fails loud if the identity has tenant
38
+ access (platform operators must be tenant-less).
39
+ - Re-running `--on` is a no-op (the perm is added once).
40
+
41
+ ## Related
42
+ - [Add a platform admin](./add-platform-admin.md) · [Add a platform user](./add-platform-user.md)
43
+ - [Create + triage feedback from the CLI](../feedback/create-and-triage-feedback.md)
@@ -0,0 +1,57 @@
1
+ # Set (rotate) a user's password
2
+
3
+ **When to use:** an existing user — tenant user **or** platform admin — needs a new
4
+ login password (reset a lost one, or set a known value). This only changes the
5
+ password; it grants nothing and creates nothing.
6
+
7
+ ## Prerequisites
8
+ - **Env:** target with `--env <local|staging|prod>`. No `--tenant` — the command
9
+ finds the identity by email globally.
10
+ - **Auth:** a token with `_platform.users.manage` (your platform-admin token, no
11
+ Convex admin key). Confirm with `plexus-cli --env <env> whoami`.
12
+ - **Inputs:** the user's email. The identity **must already exist** (provision it
13
+ first with [add-tenant-admin](./add-tenant-admin.md) /
14
+ [add-platform-admin](./add-platform-admin.md)).
15
+ - **Tooling:** an installed `plexus-cli` (`plexus-cli users set-password --help`).
16
+
17
+ ## Steps
18
+ ```bash
19
+ # Set a known password:
20
+ plexus-cli --env <env> users set-password <email> --password 'NewPassw0rd!'
21
+
22
+ # …or let the server generate a strong one (returned once; capture without echoing):
23
+ plexus-cli --env <env> users set-password <email> --output creds.csv --quiet
24
+ ```
25
+
26
+ The server owns hashing + generation and returns the plaintext **once** (only the
27
+ hash is stored). `--output creds.csv` appends the `email,password` row; `--quiet`
28
+ keeps it off stdout.
29
+
30
+ ## Verify
31
+ ```bash
32
+ plexus-cli --env <env> users show <email> # identity unchanged (sanity)
33
+ ```
34
+ Then confirm the new password actually logs in at the env's `/login`.
35
+
36
+ ## Hand the credentials over
37
+
38
+ Same three-line format as every other credential handover — URL, email, password,
39
+ bare, one block per account. See
40
+ [Add a tenant admin](./add-tenant-admin.md) → "Hand the credentials over".
41
+
42
+ ## Idempotency / gotchas
43
+ - **Pure credential write** — it creates no identity, tenant access, person, or
44
+ permission, and changes no groups. Use `users provision` /
45
+ `users provision-platform-admin` to *create* a ready-to-log-in user;
46
+ `set-password` only rotates an **existing** one.
47
+ - **Fails loud, never repairs by creating:** throws if no Plexus identity exists for
48
+ the email, if the email has no live Better Auth account, or if that account
49
+ doesn't match the identity (an orphan → run `users relink-auth` first, then retry).
50
+ - The password is shown **once**; re-run to mint a fresh one if you lose it.
51
+ - Email match is case-insensitive (the backend lowercases it).
52
+
53
+ ## Related
54
+ - [Add a tenant admin](./add-tenant-admin.md) · [Add a platform admin](./add-platform-admin.md)
55
+ — both also accept `--password` at creation time.
56
+ - `users relink-auth <email>` — repair an orphaned identity before setting a password.
57
+ - Command reference: `plexus-cli users set-password --help`.
@@ -0,0 +1,79 @@
1
+ # Use a tenant's test account
2
+
3
+ **When to use:** you want to walk a school's actual admission process as an
4
+ applicant — on local, staging or prod — without registering a new address first.
5
+
6
+ ## Prerequisites
7
+
8
+ - The account exists already in every tenant with an applicant portal:
9
+ `config:bootstrap` provisions it on every deploy, `config apply` on every
10
+ tenant onboarding, and the local development seed. Nothing to create.
11
+ - Identity: **`test@example.com`** ("Test Bewerber", stable key `fixture_test`
12
+ on `user_tenant_access.configKey`), a member of the tenant's applicant group.
13
+ - Staging/prod: a platform admin (`_platform.users.manage`) to set its password
14
+ once per deployment.
15
+
16
+ ## Steps
17
+
18
+ ### 1. Check the account in the target tenant
19
+
20
+ ```bash
21
+ plexus-cli --env <local|staging|prod> users show test@example.com
22
+ ```
23
+
24
+ Expect one access row per tenant with an applicant portal, each in the
25
+ `applicant` group.
26
+
27
+ ### 2. Set a password (staging / prod, once per deployment)
28
+
29
+ A deploy provisions the account with a random password nobody holds. Set one:
30
+
31
+ ```bash
32
+ plexus-cli --env <staging|prod> --tenant <slug> users set-password test@example.com
33
+ ```
34
+
35
+ The identity is global, so this one password opens the account in **every**
36
+ tenant of that deployment. The plaintext is printed once — password manager,
37
+ not a ticket.
38
+
39
+ **Local** needs no password: the account carries the dev-login marker, so
40
+ `/login?as=test@example.com&redirect=/<slug>/e` signs in as it.
41
+
42
+ ### 3. Apply
43
+
44
+ Open `https://<host>/<slug>/e`, start the offer, fill the form — the same
45
+ surfaces a parent uses.
46
+
47
+ ## Verify
48
+
49
+ ```bash
50
+ # It is the test row of "Wer ist der Antragsteller?" (needs testActions.run).
51
+ plexus-cli --env <env> call admission:testApplicantParties client=<slug>
52
+ ```
53
+
54
+ ## Gotchas
55
+
56
+ - **Every application of it is a test application.** Started by the
57
+ account itself through the portal (any Testmodus checkbox is irrelevant) or
58
+ by staff from the internal „+ Bewerbung" dialog (its amber first row, for
59
+ anyone with `bewerbungsmanagement.testActions.run`; refused without it), the
60
+ server derives test mode from the key. Discard it like any test application.
61
+ - **Mail.** Never sent, in any mode (staging `redirect` included):
62
+ `example.com` is a reserved test address, so the delivery is rendered and
63
+ recorded and settles `skipped` / `reserved_address` — sign in with the
64
+ password, not a magic link.
65
+ - **A deploy never rotates the password.** An existing credential is preserved,
66
+ so a password you distributed survives every `config:bootstrap`.
67
+ - **Identity is the key, not the name.** Renaming "Test Bewerber" moves the row;
68
+ it never creates a second account.
69
+ - **A real account on `test@example.com` fails the deploy** (`config:bootstrap`
70
+ throws "not the test fixture"). Move that account to another address first.
71
+ - **Not in `config export`.** The account is platform-provisioned, so it is
72
+ never emitted as a config user.
73
+ - Skipped (and named in the bootstrap result under `fixtures.skipped`): tenants
74
+ without a `defaultApplicantGroupKey`, and the reserved `_platform` tenant.
75
+
76
+ ## Related
77
+
78
+ - [Drive a test application through test mode](../test-actions/drive-a-test-application.md)
79
+ - [Set (rotate) a user's password](./set-password.md)
@@ -0,0 +1,90 @@
1
+ # Debug a stuck / failed import or worker job
2
+
3
+ **When to use:** an import shows "running" but looks stuck, the Worker console
4
+ shows no active worker, a job failed and you need to know why, or you must clean
5
+ up a stranded import run. This is the **canonical agent debugging setup** for the
6
+ worker/job system: `plexus-cli` for the LIVE job/worker/import state +
7
+ `glitchtip-cli` for the captured stack traces.
8
+
9
+ ## Prerequisites
10
+ - `--env local|staging|prod`; the new worker/job commands need **platform-admin**
11
+ (the configured token already is, on staging/prod).
12
+ - For the stack traces: access to the platform's GlitchTip (the error tracker the
13
+ worker reports to), e.g. through `glitchtip-cli` (`glitchtip-cli info` shows the
14
+ server + auth). That is platform-operator access; without it, ask the operator.
15
+
16
+ ## Steps
17
+
18
+ ```bash
19
+ # 1. Live fleet: is any worker alive, and what is it running right now?
20
+ plexus-cli --env prod platform workers --json # lifecycle live|stale|dead, currentJob
21
+
22
+ # 2. The jobs themselves (generic queue). Filter to what's stuck/broken.
23
+ plexus-cli --env prod jobs list --status running # genuinely in flight
24
+ plexus-cli --env prod jobs list --status failed --kind import
25
+ plexus-cli --env prod jobs show <jobId> # lease, attempts, phase, error, run timeline
26
+ plexus-cli --env prod jobs logs <jobId> # captured worker stdout/stderr
27
+
28
+ # 3. Import-domain view (imports stuck "still running" live here, NOT in jobs).
29
+ plexus-cli --env prod imports status --tenant <slug>
30
+
31
+ # 4. Captured exceptions for the failure (stack trace + tags).
32
+ glitchtip-cli issues list
33
+ # GlitchTip event tags the worker attaches: errorSource=backend:worker,
34
+ # jobKind, jobId, importRunId, configKey. Filter by them via GlitchTip's API
35
+ # (the CLI `issues list` has no tag filter).
36
+
37
+ # 5. Is the reaper (the only authority that frees stuck leases) actually firing?
38
+ plexus-cli --env prod workers reaper-status --json # ranAt should be < ~1 min old
39
+ ```
40
+
41
+ ### The decision rule (why did the worker die?)
42
+
43
+ A job reaper-failed / vanished, **and**:
44
+ - **GlitchTip HAS an event** → it was a JS throw. Read the stack in the issue.
45
+ - **GlitchTip has NO event** → uncatchable **OOM/SIGKILL**. The only trace is the
46
+ reaper-failed job; its error message names the last phase + idle time (e.g.
47
+ *"vanished during phase 'importing' after N min"*). Confirming it needs a
48
+ platform operator with host access: the worker container's **exit code 137**
49
+ (or the kernel's OOM-killer log).
50
+
51
+ ## Recovery
52
+
53
+ ```bash
54
+ # Force a reaper pass now instead of waiting for the 1-min cron.
55
+ plexus-cli --env prod workers reap --allow-prod --json
56
+
57
+ # Clear import runs stuck 'running' with no active job (the orphan class).
58
+ # DRY-RUN first (no --confirm), then confirm. Marks failed, never deletes.
59
+ plexus-cli --env prod imports cleanup-orphaned --tenant <slug>
60
+ plexus-cli --env prod imports cleanup-orphaned --allow-prod --tenant <slug> --confirm
61
+ plexus-cli --env prod imports cleanup-orphaned --all-tenants # dry-run sweep
62
+
63
+ # Cancel a job that should not be running.
64
+ plexus-cli --env prod jobs cancel --allow-prod <jobId>
65
+ ```
66
+
67
+ ## Verify
68
+
69
+ ```bash
70
+ plexus-cli --env prod imports status --tenant <slug> # the stuck run now 'failed'
71
+ plexus-cli --env prod jobs list --status running # nothing unexpectedly in flight
72
+ ```
73
+
74
+ ## Idempotency / gotchas
75
+ - `cleanup-orphaned` and `jobs cancel` flip rows to **failed/canceled**, never
76
+ delete — history is preserved. `cleanup-orphaned` is dry-run unless `--confirm`.
77
+ - Since the terminal-state fix (the job registry's `onTerminal` hook), a job
78
+ that terminally fails now propagates to its `import_runs` row automatically —
79
+ **new** orphans shouldn't appear; `cleanup-orphaned` is for pre-fix strays and
80
+ as a backstop.
81
+ - The **reaper** only touches the generic `jobs` table (frees leases / fails
82
+ stalled jobs); it does **not** scan `import_runs`. A "running" import with all
83
+ workers idle = an orphaned domain row → use `cleanup-orphaned`.
84
+ - `jobs list --kind` filters client-side after paging; raise `--limit` if you page
85
+ past the row you want.
86
+
87
+ ## Related
88
+ - [Verify that a deploy's migrations ran](./verify-migrations.md)
89
+ - Command reference: `plexus-cli jobs list --help`, `plexus-cli workers reap --help`,
90
+ `plexus-cli imports cleanup-orphaned --help`.
@@ -0,0 +1,51 @@
1
+ # Verify that a deploy's migrations ran
2
+
3
+ **When to use:** after a staging or production deploy whose release lists
4
+ pending migrations (the release's `PENDING_MIGRATIONS` list), when you need the
5
+ state from the deployment itself rather than from the deploy log. The deploy job
6
+ already prints, by name, the dry run of the next migration, every migration that
7
+ ran (with its document count) and a final check that every pending entry is
8
+ `success` — it goes red if any is not; that log is the platform operator's.
9
+
10
+ ## Prerequisites
11
+ - `--env staging|prod` (or `local` for a local development backend).
12
+ - The CLI token's user holds `_platform.config.read` (every full platform
13
+ admin does). Anyone else gets `FORBIDDEN`.
14
+
15
+ ## Steps
16
+
17
+ ```bash
18
+ plexus-cli --env staging migrations status # pending migrations, exit 1 while one is not success
19
+ plexus-cli --env staging migrations status --recent # + the 20 most recently started of any name
20
+ plexus-cli --env staging migrations status --json # raw answer + `unfinished`
21
+ ```
22
+
23
+ A release script gates on the exit code instead of grepping the output:
24
+ `plexus-cli --env prod migrations status || exit 1`.
25
+
26
+ Underneath it reads `migrations:getStatus`, which returns two lists of `{ name, state, isDone, processed, error, latestStart, latestEnd }`:
27
+ - **`pending`**: every entry of `PENDING_MIGRATIONS`, in run order. After a good
28
+ deploy each one reads `state: "success"`, `isDone: true`. A name that reads
29
+ `unknown` never started on this deployment.
30
+ - **`recent`**: the 20 most recently started migrations of any name, newest
31
+ first. Use it to see what the last deploy actually touched.
32
+
33
+ ## Verify
34
+ - Every `pending` entry is `success`, and `latestEnd` is after the deploy's
35
+ workflow run started.
36
+ - `pending` is empty when the release carries no migration. Then there is
37
+ nothing to verify, and the migration step only pushed functions.
38
+
39
+ ## Gotchas
40
+ - `failed` or `canceled` with an `error`: `runPending` threw and the deploy
41
+ stopped before the config bootstrap. Read the error here; the host-side output
42
+ is in the deploy log (platform operator).
43
+ - `inProgress` long after the deploy means the migration is stuck. Hand it to a
44
+ platform operator; do not try to restart it from the CLI.
45
+ - A local development backend lists every pending migration as `unknown` (exit 1): the seed
46
+ builds data in the new shape and never runs migrations. Only staging/prod answer the question.
47
+ - „Could not find public function migrations:getStatus“: the CLI points at a deployment
48
+ without this release (often a local development backend that never received the
49
+ functions). Check `--env` (`plexus-cli config show`).
50
+ - The query shows names, state, progress and errors only; it never returns a
51
+ cursor or document data.