@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,90 @@
1
+ # Connect a school to its signing provider
2
+
3
+ Give one tenant its own account at DocuSign or inSign, so its contracts are
4
+ signed through **the school's** account instead of a deployment-wide one, and
5
+ make that account the one its contracts go out through. The day-to-day signing
6
+ commands: [README.md](./README.md).
7
+
8
+ ## When to use it
9
+
10
+ - A school is going to send contracts for electronic signature for the first time.
11
+ - A school switches provider, rotates its password / HMAC key, or its
12
+ connection stopped working and you need to see **why**.
13
+
14
+ ## Prerequisites
15
+
16
+ - **`_platform.config.manage`** for `set`, `activate` and `remove`;
17
+ `finanzen_vertraege.contracts.manage` for `show` and `test`. Setting up a
18
+ connection opens a commercial relationship with a provider in the school's
19
+ name — a platform act, like Stripe Connect.
20
+ - The installation holds `SIGNING_CREDENTIALS_KEY` (otherwise every connection
21
+ reads `platform_unconfigured` and no secret can be stored). For DocuSign also
22
+ the platform app: `DOCUSIGN_OAUTH_HOST`, `DOCUSIGN_INTEGRATION_KEY`,
23
+ `DOCUSIGN_PRIVATE_KEY`. Setting these needs a platform operator; the CLI cannot.
24
+ - The school's account data: DocuSign — the sender's API user id (GUID) and,
25
+ for callbacks, the Connect HMAC key; inSign — base URL, API user, password,
26
+ owner id (`foruser`).
27
+
28
+ ## Steps
29
+
30
+ ```bash
31
+ # 1. Where does this school stand?
32
+ plexus-cli signing connection show --env staging --tenant <slug>
33
+
34
+ # 2a. inSign. The password comes from stdin — NEVER as an argument.
35
+ printf %s "$INSIGN_PASSWORD" | plexus-cli signing connection set --provider insign \
36
+ --base-url https://insign.<anbieter>.de --username <api-user> --owner-id <owner> \
37
+ --secret-stdin --env staging --tenant <slug>
38
+
39
+ # 2b. DocuSign. The HMAC key from a file (or stdin); without one, no callback is
40
+ # registered and the 15-minute poll is the only return path.
41
+ plexus-cli signing connection set --provider docusign --user-id <GUID> \
42
+ --redirect-to admin@plexus.swop.schule --secret-file ./hmac.txt \
43
+ --env staging --tenant <slug>
44
+
45
+ # 3. Reach the account. DocuSign: on consent_required open the printed
46
+ # consentUrl ONCE as the school's sender, then test again.
47
+ plexus-cli signing connection test --provider insign --env staging --tenant <slug>
48
+
49
+ # 4. Make it the provider the school's contracts go out through.
50
+ plexus-cli signing connection activate --provider insign --env staging --tenant <slug>
51
+
52
+ # 5. Prove it.
53
+ plexus-cli signing connection show --env staging --tenant <slug>
54
+ # E-Signatur: inSign · state=ready active=yes
55
+ ```
56
+
57
+ Change a field later with the same `set`; give only what changes plus the
58
+ required fields. A secret not given stays as it is. Remove one with
59
+ `--clear-secret hmacKey` (DocuSign) or `--clear-secret password` (inSign).
60
+
61
+ ## Verify
62
+
63
+ `state=ready` (or `failing` after an earlier success) and `active=yes` is what
64
+ `signing start` needs. Then send a test revision —
65
+ [README.md](./README.md) → "Send a revision for signature".
66
+
67
+ ## Gotchas
68
+
69
+ - **Secrets go in through stdin or a file only.** An argument would land in the
70
+ shell history and in `ps`. `show` prints the secret's name, a fingerprint and
71
+ when it was set; nothing ever prints the value.
72
+ - **A `production` account is refused outside prod.** inSign: any base URL but
73
+ the vendor's sandbox host counts as production; DocuSign: the deployment's
74
+ OAuth host decides.
75
+ - **Demo needs a redirect, production forbids it.** A sandbox account must carry
76
+ `--redirect-to` (nobody may address a real family from a sandbox whose
77
+ credentials are public); a production account must not (every contract would
78
+ land in one mailbox). `set` refuses either mistake.
79
+ - **Switching keeps open Vorgänge where they are.** `activate` on another
80
+ provider deactivates the old connection in the same step, but every Vorgang
81
+ already started keeps talking to the account it started on — poll, reconcile,
82
+ void and its callbacks included. `remove` is refused while one is open.
83
+ - **The account is part of an open Vorgang.** While one is open, changing
84
+ DocuSign's `--user-id` or inSign's `--base-url` / `--owner-id` is refused;
85
+ rotating the password or HMAC key is allowed.
86
+ - **`state=unverified` after every change.** Any `set` invalidates the last
87
+ test; run `test` again before the next contract goes out.
88
+ - **`state=platform_unconfigured`** is about the installation, not the school:
89
+ `SIGNING_CREDENTIALS_KEY` (or DocuSign's platform app) is missing there.
90
+ - **`remove` is a dry run** unless `--confirm`; the secrets go with the row.
@@ -0,0 +1,41 @@
1
+ # Rotate the signing credentials key
2
+
3
+ ## When to use it
4
+ `SIGNING_CREDENTIALS_KEY` (the platform key every school's signing secrets are
5
+ sealed under) must change — routine rotation or a suspected leak.
6
+
7
+ ## Prerequisites
8
+ - Platform right `_platform.config.manage` on the target environment.
9
+ - A platform operator who can change the deployment's environment
10
+ (`SIGNING_CREDENTIALS_KEY` and its previous slot) and deploy — the CLI cannot.
11
+
12
+ ## Steps
13
+ 1. Needs a platform operator: move the current key into the previous slot, set a
14
+ new current key (`openssl rand -base64 32`), deploy. Nothing breaks in between:
15
+ envelopes under the old key open through the previous slot.
16
+ 2. Re-seal every connection under the new key:
17
+ ```bash
18
+ plexus-cli signing connection reseal --env <env>
19
+ ```
20
+ 3. Check the slots:
21
+ ```bash
22
+ plexus-cli signing status --env <env>
23
+ ```
24
+
25
+ ## Verify
26
+ `signing status` shows `sealedPrevious: 0`, `sealedUnknown: 0` and
27
+ `previousSlot: nothing sealed under it — can be removed`. Then the platform
28
+ operator clears the previous slot from the deployment's environment and deploys.
29
+
30
+ ## Idempotency / gotchas
31
+ - `reseal` is idempotent; a second run reports `resealed: 0`. It exits 1 while
32
+ anything is left under the previous or an unknown key.
33
+ - Never clear the previous slot before `reseal`: every envelope still under it
34
+ becomes `incomplete` („mit Schlüssel … versiegelt, den … nicht trägt") and
35
+ DocuSign callbacks of those schools fail until the old key is back.
36
+ - Fingerprints change with the key (they are keyed by it); the secrets do not.
37
+ - A save during the run wins: its envelope is left alone (`changedMeanwhile`).
38
+
39
+ ## Related
40
+ - [Signing commands](./README.md)
41
+ - [Connect a school to its signing provider](./connect-a-school.md)
@@ -0,0 +1,68 @@
1
+ # Hard-delete a person staff deleted
2
+
3
+ **When to use:** a school (or a data-subject erasure request) needs a person
4
+ gone **for good** — the row, everything that belongs to it, its value history
5
+ and the stored files only it holds. In the app staff can only **soft**-delete
6
+ („Person löschen“ moves the person to „Gelöschte“, restorable); the hard delete
7
+ exists only here.
8
+
9
+ ## Prerequisites
10
+ - **Env + tenant:** `--env <local|staging|prod> --tenant <slug>`. Prod also needs
11
+ `--allow-prod` for the writing run.
12
+ - **Auth:** your own platform token with `_platform.config.manage` (the right that
13
+ commits a tenant wipe). Check with `plexus-cli --env <env> whoami`.
14
+ - **The person is soft-deleted first.** Staff deletes it in the app (Personen →
15
+ person → trash icon), or it already sits under „Gelöschte“. A person that is
16
+ not deleted is refused — the two steps are deliberate.
17
+ - **The party id.** List the deleted persons of the tenant:
18
+ ```bash
19
+ plexus-cli --env <env> --tenant <slug> stammdaten persons --deleted
20
+ ```
21
+
22
+ ## Steps
23
+ 1. **Plan** (no write — the default):
24
+ ```bash
25
+ plexus-cli --env <env> --tenant <slug> stammdaten persons purge <partyId>
26
+ ```
27
+ Prints the person, when staff deleted it, the rows per table (the party, its
28
+ persons row, roles, values and their links, placements, enrollments, search
29
+ entries, `value_history`, …) and every receipt that refuses the commit.
30
+ 2. **Commit** once the plan shows no `Refused` block:
31
+ ```bash
32
+ plexus-cli --env <env> --tenant <slug> stammdaten persons purge <partyId> --confirm [--allow-prod]
33
+ ```
34
+
35
+ ## Verify
36
+ - `plexus-cli --env <env> --tenant <slug> stammdaten persons --deleted` no longer
37
+ lists the person.
38
+ - Re-running the plan fails with „Der Datensatz wurde nicht gefunden.“
39
+
40
+ ## Idempotency / gotchas
41
+ - **Not reversible.** The soft delete is the reversible step; this one is not.
42
+ - **Refusals (both plan and commit):** a person an application in flight names as
43
+ Antragsteller:in or binds („gehört zu einer laufenden Bewerbung“) — finish or
44
+ discard the application first; a person linked to a user account — remove the
45
+ account's access first.
46
+ - **Receipts block the commit:** a contract, invoice, signature or other
47
+ `restrict` holder is listed as `table:id ← table:id (column)`. They are records
48
+ the school must keep; the person then stays soft-deleted.
49
+ - **One transaction.** A person with more rows than the deletion engine's plan
50
+ limit fails loud instead of half-deleting.
51
+ - **Files:** the plan lists every file the person's values and value history name,
52
+ and why it stays (`kept … held by`). A file goes only when the upload ledger
53
+ proves it a party file of this school and nothing else holds it. It stays as
54
+ `unproven` (no ledger row names this school — a file staff uploaded into a
55
+ person before upload stamping existed carries no school stamp, nor does one saved
56
+ more than two hours after its upload; such a file is never re-stamped without
57
+ proof), `upload:<surface>` (staged for a template, library,
58
+ brand file, import, chat …), `ledger` (staged for another school), `shared` (claimed again elsewhere after it
59
+ came in — a logo, a form, a feedback attachment), `form` /
60
+ `artifact` / `snapshot` / `signature` / `return` (a receipt that holds the file) or
61
+ `party` (another person's value names it, any school). Note `form`: while the
62
+ school has ANY application left, a form might hold the file (the fieldMap copy),
63
+ so it stays — expected, not a bug.
64
+ - `--json` prints the raw report (`rows`, `blockers`, `applied`).
65
+
66
+ ## Related
67
+ - Deleting every person past a school's deletion rule at once:
68
+ [Delete persons past their school's deletion rule](../retention/erase-due-parties.md).
@@ -0,0 +1,107 @@
1
+ # Connect a school to its own Stripe account
2
+
3
+ Give one tenant a Stripe account of its own, so its families' money lands with
4
+ **the school** rather than in a shared account.
5
+
6
+ ## When to use it
7
+
8
+ - A school is going to collect money through Plexus for the first time.
9
+ - A school's connection stopped working and you need to see **why** (there are
10
+ eight distinct reasons, and they need different people to act).
11
+
12
+ ## Prerequisites
13
+
14
+ - **The school sets Stripe up itself.** A member holding
15
+ `finanzen_vertraege.contracts.manage` **and** `finanzen_vertraege.payments.connect`
16
+ presses „Stripe einrichten" on Administration → Integrationen → Stripe, in
17
+ their own browser. Whoever finishes Stripe's form becomes the owner of the
18
+ school's Stripe login, so it should be the school's legal representative
19
+ (Geschäftsführung), and that person needs a Plexus account. **The CLI cannot
20
+ start it, and neither can a platform admin** (refused even with a
21
+ membership). Stripe forbids handing onboarding links on, which is why
22
+ `stripe connect` / `stripe onboarding-link` are gone.
23
+ - The deployment holds the **platform's** Stripe key with Connect enabled (set by
24
+ a platform operator; the CLI cannot).
25
+ Accounts are created through **Accounts v2** (`/v2/core/accounts`); a
26
+ *claimable sandbox* key (`rkcs_…`) cannot create accounts at all.
27
+ - A **real mailbox** for the school, entered in the dialog. Stripe writes to it
28
+ itself (onboarding, receipts, dispute notices), and that post bypasses
29
+ Plexus entirely, so `MAIL_DELIVERY_MODE` cannot intercept or redirect it.
30
+ - A school that **already uses Stripe** signs into the form with its existing
31
+ Stripe login and reuses its company details (networked onboarding). It still
32
+ gets a new connected account.
33
+
34
+ ## Steps
35
+
36
+ ```bash
37
+ # 1. Where does this school stand?
38
+ plexus-cli stripe status --env staging --tenant <slug>
39
+ # state=unconnected → nothing exists yet.
40
+
41
+ # 2. The school's representative signs in to Plexus and presses
42
+ # „Stripe einrichten" (Integrationen → Stripe): e-mail for Stripe's post and
43
+ # country, then „Weiter zu Stripe". Plexus creates the account and the
44
+ # browser goes straight to Stripe's form. Back in Plexus, the page asks
45
+ # Stripe at once. If the form is left half done, „Angaben bei Stripe
46
+ # vervollständigen" on the same page continues it.
47
+ # Testing URL for the representative (member persona, never the platform
48
+ # admin deep login, which is refused here):
49
+ # https://<host>/login?as=<member e-mail>&redirect=/<slug>/admin/integrations/stripe
50
+
51
+ # 3. Watch it land. The v2.core.account* events refresh the mirror by themselves:
52
+ plexus-cli stripe status --env staging --tenant <slug>
53
+ # connectedBy=<name> <when> → who started it.
54
+ # card=restricted: requirements_past_due + due=[…] → Stripe still waits.
55
+ # card=pending → Stripe is checking.
56
+ # card=active → Stripe has cleared it.
57
+ # sepa=… (!) → card works, SEPA-Lastschrift does not: the school enables
58
+ # it in its own Stripe dashboard (payment methods).
59
+
60
+ # 4. The school's own consent. Nothing collects without it, however happy
61
+ # Stripe is (page: „Einzug freigeben", or with the school's say-so):
62
+ plexus-cli stripe allow --env staging --tenant <slug>
63
+
64
+ # 5. Prove it:
65
+ plexus-cli stripe status --env staging --tenant <slug>
66
+ # state=ready
67
+ ```
68
+
69
+ ## Verify
70
+
71
+ `state=ready` is the only state a payment can start from. Then request one
72
+ through the real primitive — see
73
+ [Request a Stripe payment and follow it](./request-and-verify-a-payment.md).
74
+
75
+ ## Gotchas
76
+
77
+ - **`asked` empty means "never asked".** A seeded connection carries no Stripe
78
+ verdict on purpose; the first account event or `stripe refresh` fills it.
79
+ - **`error` set means the last read failed** (`stripe refresh` or an event's
80
+ re-read). It is shown, never interpreted — even "does not have access" does
81
+ not flip the state to revoked by itself.
82
+ - **A stale `no` is the expensive direction.** The mirror is refreshed by
83
+ webhook, and a missed delivery leaves a school unable to collect although it
84
+ may. `plexus-cli stripe refresh` is the way out — there is no equivalent
85
+ problem in the other direction, because Stripe refuses a charge on an account
86
+ it has since restricted.
87
+ - **`state=revoked` cannot be fixed here.** The school removed Plexus's access
88
+ in their own Stripe dashboard. Connecting again is not built;
89
+ `stripe allow` refuses rather than pretending.
90
+ - **One school, one account, forever.** Once a school has an account, „Stripe
91
+ einrichten" only continues Stripe's form for it and never creates a second
92
+ one, because replacing the reference would orphan every open payment pointing
93
+ at the first account. A double click is harmless: one idempotency key per
94
+ tenant gives back the same account.
95
+ - **The link died** (reopened, older than a few minutes, opened by a chat
96
+ preview): Stripe sends the person back with `?onboarding=expired`. The page
97
+ asks before sending them in again. It never redirects on its own.
98
+ - **`state=platform_unconfigured`** is about the installation, not the school —
99
+ clearing `stripe_mode` on a deployment removes every `STRIPE_*` variable while
100
+ the schools' rows stay behind.
101
+ - **`state=closed`** — Stripe closed the account. Onboarding cannot reopen it;
102
+ only a new account helps, which means replacing the connection (not built).
103
+ - **Two destinations, two secrets** (set by a platform operator): without
104
+ `STRIPE_WEBHOOK_SECRET` no account event (thin) verifies, without
105
+ `STRIPE_CONNECT_WEBHOOK_SECRET` no school payment (snapshot) — `/stripe/webhook`
106
+ answers **503** naming the variable (not 400 — "cannot check" is not "did not
107
+ check out"); payments then settle only through the 5-minute poll.
@@ -0,0 +1,78 @@
1
+ # Eine Stripe-Zahlung anfordern und nachverfolgen
2
+
3
+ Wofür: eine Einmalzahlung über Stripe auslösen und ihren Weg bis `succeeded`
4
+ verfolgen — lokal, auf Staging oder in Produktion. Voraussetzung ist eine
5
+ verbundene Schule: [Schule mit Stripe verbinden](./connect-a-school.md).
6
+
7
+ Für die Hand statt fürs Skript gibt es dasselbe unter **Administration →
8
+ Integrationen** (`/$tenant/admin/integrations`) — gleiche Funktionen, gleiche
9
+ Rechte, nur mit Knöpfen. Dieses Playbook ist der skriptbare Weg.
10
+
11
+ Für Zahlungen hat `plexus-cli stripe` (noch) keine eigenen Befehle — die Gruppe
12
+ verbindet nur die Schule; der Zahlungsadapter wird über `plexus-cli call`
13
+ bedient (`plexus-cli functions --grep stripe` listet die Funktionen).
14
+
15
+ ## 0. Verbindung prüfen
16
+
17
+ ```bash
18
+ plexus-cli --env local --json call stripe:status client=<tenant>
19
+ ```
20
+
21
+ `configured: false` nennt in `missing`/`invalid` genau die Variablen, an denen es
22
+ liegt. `mode` sagt, ob echtes Geld im Spiel ist.
23
+
24
+ ## 1. Zahlung anfordern
25
+
26
+ ```bash
27
+ plexus-cli --env local --json call stripe:create --kind mutation \
28
+ client=<tenant> \
29
+ amountMinor=18000 \
30
+ currency=eur \
31
+ description="Anmeldegebuehr Schuljahr 2026/27" \
32
+ customerEmail=eltern@beispiel.example \
33
+ requestKey=admissionFee:<applicationId> \
34
+ returnPath=/<tenant>/e/bewerbung/<applicationId>
35
+ ```
36
+
37
+ - **`amountMinor` sind CENT**, nicht Euro. 180 € sind `18000`.
38
+ - **`requestKey` leitet sich aus der Sache ab**, für die gezahlt wird — nie aus
39
+ einem Zeitstempel. Derselbe Schlüssel gibt dieselbe Zeile zurück
40
+ (`reused: true`), solange ein Versuch läuft oder die Forderung bezahlt ist.
41
+ - **`returnPath` ist ein app-relativer Pfad.** Eine absolute URL wird abgewiesen.
42
+
43
+ ## 2. Zustand lesen
44
+
45
+ ```bash
46
+ plexus-cli --env local --json call stripe:get client=<tenant> stripePaymentId=<id>
47
+ ```
48
+
49
+ `checkoutUrl` ist die Zahlungsseite (nur solange `status: open`). `events` zeigt
50
+ die von Stripe zugestellten, signaturgeprüften Rückrufe.
51
+
52
+ ## 3. Wenn nichts passiert
53
+
54
+ Zuerst: **das ist normal, bis jemand zahlt.** Danach der Reihe nach:
55
+
56
+ ```bash
57
+ # von Hand abgleichen (der Cron tut es alle 5 Minuten von selbst)
58
+ plexus-cli --env local --json call stripe:reconcileNow --kind mutation \
59
+ client=<tenant> stripePaymentId=<id>
60
+ ```
61
+
62
+ - `status: requires_session` länger als ein paar Sekunden ⇒ die Aktion ist
63
+ gescheitert. `error` auf der Zeile nennt Stripes Grund; nach zehn Minuten
64
+ markiert der Reaper sie als `failed`.
65
+ - `status: processing` ⇒ SEPA. Stripe selbst rät zu **mindestens sechs
66
+ Werktagen** Geduld, bevor man eine Lastschrift als erfolgreich betrachtet.
67
+ - Keine `events`, obwohl gezahlt wurde ⇒ der Webhook kommt nicht an (das richtet
68
+ der Plattformbetreiber ein); der Abgleich-Cron holt es trotzdem ein.
69
+
70
+ ## 4. Zurückziehen
71
+
72
+ ```bash
73
+ plexus-cli --env local --json call stripe:cancel --kind mutation \
74
+ client=<tenant> stripePaymentId=<id> reason="Platz anderweitig vergeben"
75
+ ```
76
+
77
+ Schließt erst die Seite bei Stripe, dann die Zeile. Eine bereits eingegangene
78
+ Zahlung wird dabei **nie** zurückgezogen — der Aufruf gleicht sie stattdessen ab.
@@ -0,0 +1,69 @@
1
+ # Drive a test application through test mode
2
+
3
+ **When to use:** walk a test application through an offer from the terminal — load a form
4
+ scenario and submit it, fill the file fields, submit a form — exactly what the cockpit's
5
+ test-mode popover („Testmodus") does. Replaces raw `call workflow:listTestActions` /
6
+ `runTestAction`.
7
+
8
+ ## Prerequisites
9
+ - `--tenant` set, and `--env` named on the command line for `run` (it writes; prod also
10
+ needs `--allow-prod`); the application is a **test application** (started in test
11
+ mode) and the caller holds `testActions.run`. Otherwise both verbs refuse with that reason.
12
+ - The application id — start one with `applications start-test`
13
+ ([Start a test application](../applications/start-a-test-application.md)) or list them with
14
+ `applications list --offer <slug> --test-only`.
15
+ - The form's scenarios exist — see [Author a form's test scenarios](../offer/author-form-scenarios.md).
16
+
17
+ ## Steps
18
+ ```bash
19
+ # What the application's current node(s) offer:
20
+ plexus-cli --env staging --tenant test test-actions list --application <applicationId>
21
+
22
+ # Load a scenario and submit (files are stubbed with openable PDFs first):
23
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> \
24
+ --action load-scenario:joint-reduced-fee
25
+
26
+ # Only load the values, no submit:
27
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> \
28
+ --action load-scenario:joint-reduced-fee --no-auto-submit
29
+
30
+ # Fill every file field, then submit once the fill has landed:
31
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> --action auto-fill-files
32
+ plexus-cli --env staging --tenant test test-actions list --application <applicationId> # submit-form no longer disabled?
33
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> --action submit-form
34
+
35
+ # An action that asks a question first (the popover's dialog) runs with its answer as a param —
36
+ # upload the signed copy with the contract terms confirmed:
37
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> \
38
+ --action upload-signed-document --param confirmed=true
39
+
40
+ # Application-wide actions (listed under „(Bewerbung)“), e.g. become responsible:
41
+ plexus-cli --env staging --tenant test test-actions run --application <applicationId> --action takeResponsibility
42
+ ```
43
+
44
+ Without `--node` the CLI asks every current node of the application and runs the action
45
+ on the one that offers it. Two current nodes offering the same action (parallel branches)
46
+ → it stops and asks for `--node`.
47
+
48
+ ## Verify
49
+ `run` prints the action and the node it ran on. File fills — and the submit a scenario
50
+ does after them — run as a scheduled action and land a moment later; `test-actions list`
51
+ then shows what the next node offers.
52
+
53
+ ## Idempotency / gotchas
54
+ - A disabled action is refused with its reason before the server is asked
55
+ (`submit-form … disabled: missingRequired (3)` — a required field is still empty).
56
+ - `load-scenario:<id>` submits by default, like the popover's „Automatisch absenden" checkbox;
57
+ that switch is `--no-auto-submit`, never `--param autoSubmit=…` (refused).
58
+ - `--param key=value` (repeatable) sends the action's params; each value is JSON where it
59
+ parses (`true`, `3`, `{"a":1}`), else text — quote a number meant as text (`--param note='"007"'`).
60
+ An action that declares a question (`upload-signed-document`, the contract terms) is refused
61
+ by the server without `--param confirmed=true`, and refused WITH it when it asks nothing.
62
+ - Not a test application, or no `testActions.run`: refused with that reason (never an empty
63
+ list). An unknown `--node`: refused with the node id.
64
+ - The UI label of `auto-fill-files` is „File-Felder mit Dummy füllen“.
65
+ - Real applications are never driven this way; the server refuses test actions outside
66
+ test mode.
67
+
68
+ ## Related
69
+ - [Author a form's test scenarios](../offer/author-form-scenarios.md) · [Use a tenant's test account](../users/use-test-account.md)
@@ -0,0 +1,59 @@
1
+ # Add a platform admin
2
+
3
+ **When to use:** you need a **tenant-less platform operator** (full `_platform.*`
4
+ set, reaches every tenant) with a working login — e.g. onboarding a new operator or
5
+ agent.
6
+
7
+ ## Prerequisites
8
+ - **Env:** target with `--env <local|staging|prod>`. **No `--tenant`** — a platform
9
+ admin is tenant-less by definition.
10
+ - **Auth:** your **own platform-admin token** (needs `_platform.users.manage`); a
11
+ Convex admin key is **not** sufficient. Confirm with
12
+ `plexus-cli --env <env> whoami`.
13
+ - **Inputs:** the new admin's email + display name. The email must **not** already
14
+ have tenant access (the `_platform.* XOR tenant-access` invariant).
15
+ - **Tooling:** an installed `plexus-cli`; `plexus-cli users --help` lists the user
16
+ commands.
17
+
18
+ ## Steps
19
+ ```bash
20
+ plexus-cli --env <env> users provision-platform-admin <email> \
21
+ --name "Vorname Nachname" \
22
+ --output creds.csv --quiet
23
+ ```
24
+
25
+ - The server **generates the password and returns it once**. `--output creds.csv`
26
+ captures the `email,password` row; `--quiet` keeps it out of stdout. To set a known
27
+ password, pass `--password "…"`.
28
+
29
+ ## Verify
30
+ ```bash
31
+ plexus-cli --env <env> users show <email>
32
+ ```
33
+
34
+ Success: a `platform: …` line lists the full `_platform.*` permission set and the
35
+ user has **no** tenant rows. `plexus-cli --env <env> platform users` lists them too.
36
+
37
+ ## Hand the credentials over
38
+
39
+ Same three-line format as every other credential handover — URL, email, password,
40
+ bare, one block per account. See
41
+ [Add a tenant admin](./add-tenant-admin.md) → "Hand the credentials over".
42
+
43
+ ## Idempotency / gotchas
44
+ - **Idempotent — re-running rotates the password** (does not duplicate the account).
45
+ - **Refuses an email that already has tenant access** (the `_platform.* XOR
46
+ tenant-access` invariant). For a per-tenant account use
47
+ [Add a tenant admin](./add-tenant-admin.md) instead.
48
+ - Contrast `users create <email> --platform-admin`, which also mints a credential
49
+ (no verb leaves an identity that cannot log in) but takes **no display
50
+ name** and never rotates an existing password. `provision-platform-admin` is the
51
+ named path, and re-running it deliberately resets the login.
52
+ - Runtime-only account — **not** part of the demo seed (real personal passwords
53
+ never are); a destructive reseed won't recreate it. Re-run if ever reseeded.
54
+
55
+ ## Related
56
+ - [Add a tenant admin](./add-tenant-admin.md) — the per-tenant twin (`users provision`).
57
+ - Command reference: `plexus-cli users provision-platform-admin --help` and
58
+ `plexus-cli users create --help` (the create-vs-provision distinction above).
59
+ - Connection and token: `plexus-cli config show`, `plexus-cli whoami`.
@@ -0,0 +1,59 @@
1
+ # Add a platform user
2
+
3
+ **When to use:** you need a **tenant-less platform operator** with a limited,
4
+ explicit `_platform.*` permission set and a working login.
5
+
6
+ ## Prerequisites
7
+ - **Env:** target with `--env <local|staging|prod>`. **No `--tenant`** — a platform
8
+ user is tenant-less by definition.
9
+ - **Auth:** your **own platform-admin token** (needs `_platform.users.manage`); a
10
+ Convex admin key is **not** sufficient. Confirm with
11
+ `plexus-cli --env <env> whoami`.
12
+ - **Inputs:** the user's email, display name, and exact platform permissions. The
13
+ email must **not** already have tenant access (the `_platform.* XOR tenant-access`
14
+ invariant).
15
+ - **Tooling:** an installed `plexus-cli`; `plexus-cli users --help` lists the user
16
+ commands.
17
+
18
+ ## Steps
19
+ ```bash
20
+ plexus-cli --env <env> users provision-platform-user <email> \
21
+ --name "Vorname Nachname" \
22
+ --permission _platform.schema.read \
23
+ --output creds.csv --quiet
24
+ ```
25
+
26
+ - Repeat `--permission` for every `_platform.*` permission the user should hold.
27
+ - The server **generates the password and returns it once**. `--output creds.csv`
28
+ captures the `email,password` row; `--quiet` keeps it out of stdout. To set a known
29
+ password, pass `--password "..."`.
30
+
31
+ ## Verify
32
+ ```bash
33
+ plexus-cli --env <env> users show <email>
34
+ ```
35
+
36
+ Success: a `platform: ...` line lists exactly the requested `_platform.*`
37
+ permissions and the user has **no** tenant rows.
38
+
39
+ ## Hand the credentials over
40
+
41
+ Same three-line format as every other credential handover — URL, email, password,
42
+ bare, one block per account. See
43
+ [Add a tenant admin](./add-tenant-admin.md) → "Hand the credentials over".
44
+
45
+ ## Idempotency / gotchas
46
+ - **Idempotent — re-running rotates the password** and resets the platform
47
+ permission list to exactly the repeated `--permission` values.
48
+ - **Refuses an email that already has tenant access** (the `_platform.* XOR
49
+ tenant-access` invariant). A limited platform user cannot also be a tenant user.
50
+ - For full platform operators, use [Add a platform admin](./add-platform-admin.md)
51
+ instead. That command grants the full `_platform.*` set.
52
+ - Runtime-only account — **not** part of the demo seed (real personal passwords
53
+ never are); a destructive reseed won't recreate it. Re-run if ever reseeded.
54
+
55
+ ## Related
56
+ - [Add a platform admin](./add-platform-admin.md) — full `_platform.*` operators.
57
+ - [Set (rotate) a user's password](./set-password.md) — rotate an existing
58
+ credential without changing access.
59
+ - Connection and token: `plexus-cli config show`, `plexus-cli whoami`.
@@ -0,0 +1,75 @@
1
+ # Add a tenant admin
2
+
3
+ **When to use:** a staff member should administer **one tenant** with a working
4
+ login — a ready-to-use account (credential + full tenant-admin access), not just a
5
+ permission grant.
6
+
7
+ ## Prerequisites
8
+ - **Env / tenant:** target with `--env <local|staging|prod>` **and** `--tenant <slug>`
9
+ (this is tenant-scoped). Run `plexus-cli --env <env> tenants` to see the slugs you
10
+ reach.
11
+ - **Auth:** a token whose identity may manage users in that tenant (a platform-admin
12
+ token, or tenant access with `_platform.users.manage`). Confirm with
13
+ `plexus-cli --env <env> whoami`.
14
+ - **Inputs:** the new user's email + display name.
15
+ - **Tooling:** an installed `plexus-cli`; `plexus-cli users --help` lists the user
16
+ commands.
17
+
18
+ ## Steps
19
+ ```bash
20
+ plexus-cli --env <env> --tenant <slug> users provision <email> \
21
+ --name "Vorname Nachname" --group tenant_admin \
22
+ --output creds.csv --quiet
23
+ ```
24
+
25
+ - `--group tenant_admin` is the **default**, so it may be omitted; it's shown here to
26
+ be explicit. Pass a different permission-group key (see
27
+ `plexus-cli --env <env> --tenant <slug> users groups`) for a non-admin staff account.
28
+ - The server **generates the password and returns it once**. `--output creds.csv`
29
+ appends the `email,password` row to a file; `--quiet` keeps it out of stdout. To set
30
+ a known password instead, pass `--password "…"` (and drop `--quiet` if you want to
31
+ see it echoed).
32
+
33
+ ## Verify
34
+ ```bash
35
+ plexus-cli --env <env> users show <email>
36
+ ```
37
+
38
+ Success: the user lists the `<slug>` tenant with groups `tenant_admin, applicant`.
39
+ (`users show` is a global lookup across every tenant the user belongs to — no
40
+ `--tenant` needed; the email match is case-insensitive.)
41
+
42
+ ## Hand the credentials over — always this format
43
+
44
+ Three bare lines, nothing else: URL, email, password. No table, no markdown link,
45
+ no labels. One block per account, so the recipient can copy each line straight
46
+ into the login form.
47
+
48
+ ```
49
+ https://plexus.swop.schule/
50
+ ada@example.com
51
+ <the generated password>
52
+ ```
53
+
54
+ Use the app URL of the env you provisioned in. Write the URL bare: a markdown link renders as a terminal escape
55
+ sequence, and the address never reaches the visible text.
56
+
57
+ ## Idempotency / gotchas
58
+ - **Re-running rotates the password** (it re-provisions the credential) and re-emits
59
+ it — re-run only if you intend to reset the login.
60
+ - The password is shown **once**; if you didn't capture it, re-run with `--output …`
61
+ to mint a fresh one.
62
+ - This both **grants tenant access and creates a login credential** — contrast
63
+ `users add` / `users set-group`, which only adjust access and group membership for
64
+ a user who already exists. `users add` REFUSES an unknown address (it
65
+ has no name or password channel, so any identity it created could never log in);
66
+ provision the account first, here.
67
+ - The backend lowercases the email; the login address is the lowercased form.
68
+ - Runtime-only account — it is **not** part of the demo seed, so a destructive reseed
69
+ won't recreate it; re-run this playbook if the tenant is ever reseeded.
70
+
71
+ ## Related
72
+ - [Add a platform admin](./add-platform-admin.md) — the tenant-less platform twin
73
+ (`users provision-platform-admin`).
74
+ - Command reference: `plexus-cli users --help`.
75
+ - Connection and token: `plexus-cli config show`, `plexus-cli whoami`.