@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.
- package/README.md +863 -2
- package/dist/cli.mjs +5432 -0
- package/dist/skill/.managed-by-plexus-cli +1 -0
- package/dist/skill/SKILL.md +224 -0
- package/dist/skill/references/accounts/email-changes.md +46 -0
- package/dist/skill/references/agents/use-shared-tools.md +52 -0
- package/dist/skill/references/applications/inspect-process-store.md +61 -0
- package/dist/skill/references/applications/start-a-test-application.md +66 -0
- package/dist/skill/references/audit/answer-access-request.md +115 -0
- package/dist/skill/references/config/attach-a-template.md +117 -0
- package/dist/skill/references/config/init-tenant.md +121 -0
- package/dist/skill/references/config/onboard-tenant.md +71 -0
- package/dist/skill/references/config/rename-program-stage.md +99 -0
- package/dist/skill/references/config/set-tenant-branding.md +150 -0
- package/dist/skill/references/config/set-tenant-terms.md +50 -0
- package/dist/skill/references/config/set-up-billing-catalog.md +73 -0
- package/dist/skill/references/config/update-tenant-config.md +174 -0
- package/dist/skill/references/config/wipe-tenant-config.md +107 -0
- package/dist/skill/references/export/create-named-export.md +47 -0
- package/dist/skill/references/feedback/create-and-triage-feedback.md +69 -0
- package/dist/skill/references/feedback/manage-feedback-tags.md +57 -0
- package/dist/skill/references/finance/change-contract-grant.md +36 -0
- package/dist/skill/references/finance/record-submission-date.md +33 -0
- package/dist/skill/references/interessenten/list-interessenten.md +54 -0
- package/dist/skill/references/marketing/demo-video-links.md +101 -0
- package/dist/skill/references/notifications/debug-mail-delivery.md +77 -0
- package/dist/skill/references/notifications/mail-level.md +90 -0
- package/dist/skill/references/notifications/reword-a-letter.md +155 -0
- package/dist/skill/references/notifications/trace-a-login.md +50 -0
- package/dist/skill/references/offer/add-workflow-node.md +268 -0
- package/dist/skill/references/offer/addresses.md +129 -0
- package/dist/skill/references/offer/appointment-task.md +79 -0
- package/dist/skill/references/offer/assignment-rule.md +195 -0
- package/dist/skill/references/offer/attach-form-to-workflow.md +79 -0
- package/dist/skill/references/offer/author-form-scenarios.md +97 -0
- package/dist/skill/references/offer/author-manifest-meta.md +94 -0
- package/dist/skill/references/offer/author-smartform.md +102 -0
- package/dist/skill/references/offer/bind-node-input.md +76 -0
- package/dist/skill/references/offer/completeness-check.md +137 -0
- package/dist/skill/references/offer/countersign-node.md +91 -0
- package/dist/skill/references/offer/create-pedagogy-unit.md +55 -0
- package/dist/skill/references/offer/declare-node-reads.md +99 -0
- package/dist/skill/references/offer/edit-live-tenant-offer.md +144 -0
- package/dist/skill/references/offer/edit-smartform.md +121 -0
- package/dist/skill/references/offer/edit-workflow-node.md +126 -0
- package/dist/skill/references/offer/engage-push-node.md +147 -0
- package/dist/skill/references/offer/insert-and-rewire-node.md +67 -0
- package/dist/skill/references/offer/node-tasks.md +162 -0
- package/dist/skill/references/offer/optional-assessment.md +65 -0
- package/dist/skill/references/offer/publish-offer-bundle.md +117 -0
- package/dist/skill/references/offer/set-offer-capacities.md +78 -0
- package/dist/skill/references/offer/staff-input-if-node.md +112 -0
- package/dist/skill/references/offer/task-appointment-result.md +69 -0
- package/dist/skill/references/offer/task-family-request.md +69 -0
- package/dist/skill/references/offer/task-result-files.md +52 -0
- package/dist/skill/references/offer/template-document-node.md +113 -0
- package/dist/skill/references/offer/workflow-default-channels.md +69 -0
- package/dist/skill/references/resolver/manage-contract-resolver.md +161 -0
- package/dist/skill/references/retention/erase-due-applications.md +99 -0
- package/dist/skill/references/retention/erase-due-parties.md +107 -0
- package/dist/skill/references/roles/migrate-parent-assignments.md +31 -0
- package/dist/skill/references/signing/README.md +113 -0
- package/dist/skill/references/signing/connect-a-school.md +90 -0
- package/dist/skill/references/signing/rotate-the-key.md +41 -0
- package/dist/skill/references/stammdaten/purge-deleted-person.md +68 -0
- package/dist/skill/references/stripe/connect-a-school.md +107 -0
- package/dist/skill/references/stripe/request-and-verify-a-payment.md +78 -0
- package/dist/skill/references/test-actions/drive-a-test-application.md +69 -0
- package/dist/skill/references/users/add-platform-admin.md +59 -0
- package/dist/skill/references/users/add-platform-user.md +59 -0
- package/dist/skill/references/users/add-tenant-admin.md +75 -0
- package/dist/skill/references/users/change-login-email.md +55 -0
- package/dist/skill/references/users/cli-access-for-tenant-admin.md +117 -0
- package/dist/skill/references/users/delete-permission-group.md +76 -0
- package/dist/skill/references/users/grant-cross-tenant-access.md +57 -0
- package/dist/skill/references/users/grant-feedback-assignable.md +43 -0
- package/dist/skill/references/users/set-password.md +57 -0
- package/dist/skill/references/users/use-test-account.md +79 -0
- package/dist/skill/references/worker-ops/debug-worker-jobs.md +90 -0
- package/dist/skill/references/worker-ops/verify-migrations.md +51 -0
- package/dist/spec.snapshot.json +156821 -0
- package/package.json +53 -4
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Personalised demo video links with viewing statistics
|
|
2
|
+
|
|
3
|
+
**When to use:** send a named person a link to the public demo video page — to one
|
|
4
|
+
video or to the whole page — and later see whether they opened it, when, how often
|
|
5
|
+
and how long they watched each video. One sentence such as "Link für Erika
|
|
6
|
+
Mustermann zum Video Vorlagen" becomes one `demo link create`.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- A token of a platform operator with `_platform.demoLinks.manage` (every full
|
|
10
|
+
platform admin holds it). A school's own token cannot create links — needs a
|
|
11
|
+
platform operator.
|
|
12
|
+
- Getting such a token: an operator holding `_platform.apiTokens.manage` mints one
|
|
13
|
+
for themselves with `plexus-cli tokens create <name>`, or for you with
|
|
14
|
+
`plexus-cli tokens create <name> --for <your email>` (then you need
|
|
15
|
+
`_platform.apiTokens.manage` too — needs a platform operator).
|
|
16
|
+
- `--env prod` for real recipients; every writing command against production also
|
|
17
|
+
needs `--allow-prod`.
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Which videos exist (id, date, title, length) — the id is what --video takes.
|
|
22
|
+
plexus-cli --env prod demo videos
|
|
23
|
+
|
|
24
|
+
# 2a. One recipient, one video → prints the finished URL.
|
|
25
|
+
plexus-cli --env prod demo link create --allow-prod \
|
|
26
|
+
--to "Erika Mustermann" --email erika@example.com --video vorlagen --campaign herbst
|
|
27
|
+
|
|
28
|
+
# 2b. Without --video the link opens the whole page.
|
|
29
|
+
plexus-cli --env prod demo link create --allow-prod --to "Erika Mustermann"
|
|
30
|
+
|
|
31
|
+
# 2c. Many recipients from a CSV (header row: name, email or E-Mail, video — email and
|
|
32
|
+
# video optional; a row's video wins over --video). Writes the links as a CSV that
|
|
33
|
+
# German Excel opens directly (";" separated, umlauts intact).
|
|
34
|
+
plexus-cli --env prod demo link create --allow-prod \
|
|
35
|
+
--from recipients.csv --video vorlagen --campaign herbst --out links.csv
|
|
36
|
+
|
|
37
|
+
# Machine-readable (code + url per recipient):
|
|
38
|
+
plexus-cli --env prod demo link create --allow-prod --to "Erika Mustermann" --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Send each recipient **their own** URL. The link's `?r=<code>` names the recipient;
|
|
42
|
+
the rest of the URL is the page's usual jump to the video.
|
|
43
|
+
|
|
44
|
+
The recipient CSV must be **UTF-8**: in Excel, *Save as → „CSV UTF-8 (durch
|
|
45
|
+
Trennzeichen getrennt) (*.csv)"*. A file in the older „CSV (Trennzeichen-getrennt)"
|
|
46
|
+
format is refused instead of turning „Müller" into „M�ller". `,` and `;` both work;
|
|
47
|
+
an unknown column or a row with more values than the header (an unquoted comma in a
|
|
48
|
+
name) is refused, naming the row.
|
|
49
|
+
|
|
50
|
+
## Verify
|
|
51
|
+
```bash
|
|
52
|
+
plexus-cli --env prod demo link list --campaign herbst
|
|
53
|
+
plexus-cli --env prod demo link stats --campaign herbst # or: demo link stats <code>
|
|
54
|
+
```
|
|
55
|
+
`stats` shows one row per recipient and video:
|
|
56
|
+
- `besuche` — page loads a person engaged with (see the gotchas),
|
|
57
|
+
- `erster` / `letzter` — first and latest visit, German time,
|
|
58
|
+
- `gesehen` — seconds actually played, summed over all visits, / video length,
|
|
59
|
+
- `%` — how much of the video the best single visit actually played. Skipping ahead
|
|
60
|
+
to the end does not count: someone who drags the slider to the end and stops shows
|
|
61
|
+
a few seconds and a low `%`.
|
|
62
|
+
|
|
63
|
+
A recipient who never opened the link shows `0` and `—`.
|
|
64
|
+
|
|
65
|
+
To stop counting a link (e.g. it was posted publicly by mistake):
|
|
66
|
+
```bash
|
|
67
|
+
plexus-cli --env prod demo link revoke --allow-prod <code>
|
|
68
|
+
```
|
|
69
|
+
The page keeps working for everyone; the statistics recorded so far stay readable.
|
|
70
|
+
|
|
71
|
+
To erase a link on request (the name, the address and everything recorded):
|
|
72
|
+
```bash
|
|
73
|
+
plexus-cli --env prod demo link delete --allow-prod <code> # shows what would go
|
|
74
|
+
plexus-cli --env prod demo link delete --allow-prod <code> --confirm # deletes, cannot be undone
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Idempotency / gotchas
|
|
78
|
+
- **When a visit counts.** Only after the page was visible for 5 seconds or the
|
|
79
|
+
visitor scrolled, tapped, clicked, typed or pressed play. Mail scanners that only fetch
|
|
80
|
+
the link (Outlook Safe Links and the like) never count; a scanner that renders the
|
|
81
|
+
page for a moment rarely does. **A play is the hard signal**: `gesehen` above
|
|
82
|
+
`0:00` means a person watched. `besuche = 0` right after sending is expected.
|
|
83
|
+
- **A forwarded link counts for the original recipient.** The link knows only whom it
|
|
84
|
+
was created for; if they forward it, the colleague's viewing is counted under their
|
|
85
|
+
name. Create a separate link per person.
|
|
86
|
+
- **Every page load is a visit**, a reload too. Switching view or video on the page
|
|
87
|
+
is not a new visit.
|
|
88
|
+
- **Watching time is counted from playback**, every 10 seconds and when the viewer
|
|
89
|
+
pauses or leaves: closing the tab abruptly can lose up to 10 seconds.
|
|
90
|
+
- **No cookie, nothing stored in the browser, no session replay** for pages opened
|
|
91
|
+
through such a link. Visits and playback are deleted after 12 months; the link
|
|
92
|
+
itself (name, address) stays until `demo link delete`.
|
|
93
|
+
- `create` is not idempotent: running it twice gives the same person two links.
|
|
94
|
+
`revoke` is: revoking twice keeps the first time.
|
|
95
|
+
- An unknown video id is refused, and the error lists every valid id. A CSV batch is
|
|
96
|
+
created all or nothing (at most 500 recipients per call).
|
|
97
|
+
- A heavily shared link records at most 30 page loads an hour; beyond that its
|
|
98
|
+
visits are not counted.
|
|
99
|
+
|
|
100
|
+
## Related
|
|
101
|
+
- `plexus-cli demo link stats --json` for further processing (e.g. `jq`).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Debug a mail delivery
|
|
2
|
+
|
|
3
|
+
**When:** "no mail arrived", a red `deadLastHour` badge in the platform
|
|
4
|
+
console, or a `backend:convex` GlitchTip issue tagged `area=notifications`.
|
|
5
|
+
|
|
6
|
+
## 1. Is the sweeper alive, and is anything dead?
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
plexus-cli --env staging notifications sweeper-status
|
|
10
|
+
plexus-cli --env staging notifications list --status dead
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`ranAt` older than a few minutes ⇒ the cron is not firing (deploy broke?).
|
|
14
|
+
`deadLastHour > 0` ⇒ go to step 3 with an id from the list.
|
|
15
|
+
|
|
16
|
+
## 2. Find the delivery for a person
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
plexus-cli --env staging notifications list --to parent@example.org --since 2d
|
|
20
|
+
plexus-cli --env staging notifications list --template account.loginLink --since 24h
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For a sign-in problem start with `users login-trail` instead — see
|
|
24
|
+
[Trace why someone could not sign in](./trace-a-login.md).
|
|
25
|
+
|
|
26
|
+
Statuses: `queued` (waiting / backoff), `sending` (in flight), `sent` (relay
|
|
27
|
+
accepted — NOT "in the inbox"), `skipped` (a successful non-send: recipient
|
|
28
|
+
gone, precondition no longer true, reserved TLD — in `live` every RFC 2606
|
|
29
|
+
address, in `redirect` only `.invalid`), `dead` (gave up).
|
|
30
|
+
|
|
31
|
+
## 3. Read the evidence
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
plexus-cli --env staging notifications show <deliveryId>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| `errorCode` | Means | Do |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `delivery_mode_invalid` / `config_missing` | deployment env is incomplete | needs a platform operator: complete the deployment's mail settings, then `retry` |
|
|
40
|
+
| `smtp_auth` | relay rejected the credentials | needs a platform operator: rotate the relay password in the deployment's settings, then `retry` |
|
|
41
|
+
| `smtp_policy` | relay rejected OUR sender at `MAIL FROM` | `MAIL_FROM_ADDRESS` not allowed for this account — ask the mail admin |
|
|
42
|
+
| `smtp_content` with 4xx at `DATA` | milter (DKIM) hiccup — retried automatically | if it stays: the relay's DKIM key/permissions — ask the mail admin |
|
|
43
|
+
| `smtp_recipient_rejected` | 5xx at `RCPT TO` | the address does not exist — nothing to retry |
|
|
44
|
+
| `recipient_revoked` / `recipient_changed` / `recipient_deleted` | identity changed after enqueue | expected; re-raise the event if still wanted |
|
|
45
|
+
| `never_claimed` | the dispatch action fails BEFORE it can claim | bundle/import problem — needs a platform operator (deployment logs of the action), fix, `retry` |
|
|
46
|
+
| `unknown_outcome` | action died after SMTP began | the recipient MAY have received it; decide, then `retry` |
|
|
47
|
+
|
|
48
|
+
`lastSmtpCommand` / `lastSmtpResponseCode` / `lastSmtpResponse` carry the raw
|
|
49
|
+
relay line. `possibleDuplicate: true` means attempt N may already have landed.
|
|
50
|
+
|
|
51
|
+
## 4. Prove the configuration end to end
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
plexus-cli --env staging notifications self-test --to <your-address>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Verifies the connection + credentials, sends one mail, returns the SMTP
|
|
58
|
+
transcript. On staging the mail is redirected like every other; on prod it
|
|
59
|
+
really goes to `--to`.
|
|
60
|
+
|
|
61
|
+
## 5. Retry
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
plexus-cli --env staging notifications retry <deliveryId>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Only `dead` rows. It is a fresh attempt through the normal claim path — the
|
|
68
|
+
retry budget is widened by exactly one.
|
|
69
|
+
|
|
70
|
+
## Gotchas
|
|
71
|
+
|
|
72
|
+
- `sent` = accepted by the relay. The last hop (relay → GMX) is invisible; on
|
|
73
|
+
staging the platform operator reads the catch-all mailbox.
|
|
74
|
+
- A `451` is not always transient: the relay says `451 4.7.1` for a
|
|
75
|
+
sender-policy violation and for a broken DKIM key. Look at the COMMAND.
|
|
76
|
+
- Locally nothing is ever sent (`dry`): the mail is in the Dev Rail →
|
|
77
|
+
`notifications:listDevPreviews`.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Choose which mail a school sends (mail level)
|
|
2
|
+
|
|
3
|
+
> **Version check first.** A deployment on a release older than the mail level
|
|
4
|
+
> ignores `--field mailLevel` WITHOUT an error and keeps reading the retired
|
|
5
|
+
> `tenant.enabledMailCategories` (`[]` = sign-in only, every category = all).
|
|
6
|
+
> If `config export` shows no `mailLevel`, stop and ask the platform operator.
|
|
7
|
+
> The same skew means a tenant scaffolded with a newer CLI against an older
|
|
8
|
+
> deployment starts at sign-in mail only.
|
|
9
|
+
|
|
10
|
+
**When to use:** a school wants only the sign-in links for FAMILIES for now
|
|
11
|
+
(„Anmeldelinks ja, alles andere nein"), or is ready for every mail.
|
|
12
|
+
|
|
13
|
+
**The level is family mail only.**
|
|
14
|
+
Letters to the school's own staff (`audience: "internal"`: new tasks, passed
|
|
15
|
+
deadlines, family messages, mentions) go out at BOTH levels; each staff member
|
|
16
|
+
decides them in „Einstellungen" (personal categories, runbook „Which mail a
|
|
17
|
+
staff member wants"). Do not use the level to quiet staff mail.
|
|
18
|
+
|
|
19
|
+
**Current stance:** every tenant — prod, staging and the `test` demo tenant —
|
|
20
|
+
runs `loginOnly` until the platform operator decides otherwise. Switching a real
|
|
21
|
+
school to `all` is that decision, not a routine step. Locally, a reseeded `test` tenant is at `loginOnly` too; to see a
|
|
22
|
+
rendered admission, family-task or school-chat letter set it to `all` on your
|
|
23
|
+
own deployment (`--env local`). Staff letters render at both levels.
|
|
24
|
+
|
|
25
|
+
## What it is
|
|
26
|
+
|
|
27
|
+
- A school has ONE setting, its **mail level** (`tenant.mailLevel`, required in
|
|
28
|
+
every tenant config):
|
|
29
|
+
- `loginOnly` — „An Familien nur Anmeldelinks": to families, sign-in links,
|
|
30
|
+
the access a school hands out, address confirmations („Bewerbung
|
|
31
|
+
begonnen/geparkt" included) and security notices about one's own account
|
|
32
|
+
(e.g. a changed login address).
|
|
33
|
+
- `all` — „Alle E-Mails": to families additionally admission, family task
|
|
34
|
+
reminders and the school's chat messages.
|
|
35
|
+
- Every letter still declares a **category** in code (`login`, `security`,
|
|
36
|
+
`admission`, `tasks`, `schoolMessages`; for staff the personal `staffTasks`,
|
|
37
|
+
`staffAppointments`, `staffDeadlines`, `familyMessages`, `mentions`). It names
|
|
38
|
+
the letter in a skipped delivery's reason; a school cannot switch single
|
|
39
|
+
categories.
|
|
40
|
+
- The `notifications` module must be enabled for `all`; with the module off only
|
|
41
|
+
sign-in and security mail go out whatever the level says.
|
|
42
|
+
- Refused by `config set` / `config apply`: a missing or unknown level
|
|
43
|
+
(`[notification/mailLevel]`), `all` without the `notifications` module
|
|
44
|
+
(`[notification/levelWithoutModule]`), and the retired spellings
|
|
45
|
+
`tenant.enabledMailCategories` (`[notification/mailCategoriesRetired]`)
|
|
46
|
+
and `tenant.mailEnabled` (`[notification/mailEnabledRetired]`) — a scratch
|
|
47
|
+
export from before the mail level must be rewritten, not applied.
|
|
48
|
+
- `MAIL_DELIVERY_MODE` of the deployment stays the outer boundary. Never change
|
|
49
|
+
it for this.
|
|
50
|
+
|
|
51
|
+
## Set it (dry run first)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# sign-in mail only
|
|
55
|
+
plexus-cli --env staging config set --tenant <slug> --target tenant \
|
|
56
|
+
--key <slug> --field mailLevel --value loginOnly
|
|
57
|
+
plexus-cli --env staging config set --tenant <slug> --target tenant \
|
|
58
|
+
--key <slug> --field mailLevel --value loginOnly --confirm
|
|
59
|
+
|
|
60
|
+
# everything (what the introduction of the level wrote for every school that sent mail)
|
|
61
|
+
plexus-cli --env staging config set --tenant <slug> --target tenant \
|
|
62
|
+
--key <slug> --field mailLevel --value all --confirm
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Through a scratch export instead: `config export` → set
|
|
66
|
+
`"tenant": { …, "mailLevel": "loginOnly" }` → `config apply --from snapshot:…`.
|
|
67
|
+
|
|
68
|
+
## Verify
|
|
69
|
+
|
|
70
|
+
- `plexus-cli --env <env> config export --tenant <slug>` shows `mailLevel`
|
|
71
|
+
(always emitted).
|
|
72
|
+
- Platform rail → **Mandanten** (needs `_platform.config.read`): the column
|
|
73
|
+
„E-Mails" says „Nur Anmeldelinks" (amber) or „Alle E-Mails" — as the send
|
|
74
|
+
path answers it, so module off reads „Nur Anmeldelinks" too.
|
|
75
|
+
- Dashboard of the school: banner „Hinweis zur Konfiguration" on top of the
|
|
76
|
+
page, gap `mailDisabled` („Diese Schule versendet nur Anmeldelinks …") —
|
|
77
|
+
unless the school hides that notice: `config set --tenant <slug> --target tenant
|
|
78
|
+
--key <slug> --field hideMailLevelNotice --value true --confirm` (mail-module setting, shown under
|
|
79
|
+
„notifications" in Platform → Mandanten; the test tenant does). Only this
|
|
80
|
+
notice goes; a configuration error always shows.
|
|
81
|
+
- A suppressed letter is a row: `plexus-cli --env <env> notifications …` lists it
|
|
82
|
+
`skipped`, `tenant_delivery_disabled`, last error
|
|
83
|
+
`the school's mail level does not send "<category>" mail`.
|
|
84
|
+
|
|
85
|
+
## Gotchas
|
|
86
|
+
|
|
87
|
+
- A skipped delivery is never re-sent when the level goes back to `all` — the
|
|
88
|
+
setting decides at enqueue AND at send time, not retroactively.
|
|
89
|
+
- `applicant.accessSent` („Zugang versenden → Per Mail senden") is `login`: it
|
|
90
|
+
goes out at `loginOnly`.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Reword a letter for one school
|
|
2
|
+
|
|
3
|
+
**When to use:** a school wants one of the mails Plexus sends in its name to say
|
|
4
|
+
something else — a different opening paragraph, its own sign-off — and you want
|
|
5
|
+
to prove the new words actually reach a rendered mail before anyone relies on it.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- `--tenant <slug>` on every command: a letter is reworded **per school**, never
|
|
10
|
+
platform-wide.
|
|
11
|
+
- Permissions `notifications._self.access` (read) and
|
|
12
|
+
`notifications.mail.configure` (write). The local CLI bootstrap has both.
|
|
13
|
+
- Locally: a running local development deployment (`--env local`). **Nothing is
|
|
14
|
+
ever sent from a local deployment** (`MAIL_DELIVERY_MODE=dry`) — the rendered mail
|
|
15
|
+
lands in `notification_previews`, which is exactly what makes this verifiable.
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 1. What can this school reword, and what has it already changed?
|
|
21
|
+
plexus-cli --env local --tenant test notifications letters list
|
|
22
|
+
|
|
23
|
+
# 2. One letter, block by block, with the placeholders the LETTER may use
|
|
24
|
+
# printed above them — they belong to the letter, not to one paragraph, so a
|
|
25
|
+
# school may move a value into whichever block it likes. They are an
|
|
26
|
+
# ALLOWLIST: an undeclared {{x}} is refused, never silently printed. One
|
|
27
|
+
# marked "(Pflicht)" must appear somewhere in the BODY or the save is refused
|
|
28
|
+
# — that is what replaced the lock on the two sign-in letters.
|
|
29
|
+
plexus-cli --env local --tenant test notifications letters show applicant.tasksOpen --locale de
|
|
30
|
+
|
|
31
|
+
# 3. Reword ONE block, in ONE language. Untouched blocks are not written and
|
|
32
|
+
# keep following the platform text of that language; the other language is
|
|
33
|
+
# not touched at all (a German rewording never words the English letter).
|
|
34
|
+
plexus-cli --env local --tenant test notifications letters set applicant.tasksOpen --locale de \
|
|
35
|
+
--slot intro="bei {{schoolName}} warten {{taskCount}} Aufgaben auf Sie."
|
|
36
|
+
plexus-cli --env local --tenant test notifications letters set applicant.tasksOpen --locale en \
|
|
37
|
+
--slot intro="{{taskCount}} tasks at {{schoolName}} are waiting for you."
|
|
38
|
+
|
|
39
|
+
# A paragraph with line breaks belongs in a file, not in a shell argument:
|
|
40
|
+
# plexus-cli --env local --tenant test notifications letters set applicant.tasksOpen --locale de \
|
|
41
|
+
# --slot-file intro=./intro.txt
|
|
42
|
+
|
|
43
|
+
# 4. The shared letter frame (greeting, sign-off, sender name) is separate —
|
|
44
|
+
# it is the same frame for EVERY letter this school sends, worded in both
|
|
45
|
+
# languages at once (each letter carries its own language's line).
|
|
46
|
+
plexus-cli --env local --tenant test notifications chrome show
|
|
47
|
+
plexus-cli --env local --tenant test notifications chrome set \
|
|
48
|
+
--closing-de="Herzliche Grüße
|
|
49
|
+
Ihr Grundschul-Team" \
|
|
50
|
+
--closing-en="Warm regards
|
|
51
|
+
Your primary school team"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Verify
|
|
55
|
+
|
|
56
|
+
Reading back the catalog only proves the row was stored. What has to be proven is
|
|
57
|
+
that the words reached a **rendered mail**:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# a) The stored deviation
|
|
61
|
+
plexus-cli --env local --tenant test notifications letters list # "reworded: de, en"
|
|
62
|
+
plexus-cli --env local --tenant test notifications letters show applicant.tasksOpen --locale en
|
|
63
|
+
|
|
64
|
+
# b) Trigger the letter (for applicant.tasksOpen: open an external task on an
|
|
65
|
+
# application — e.g. a workflow task addressed to the family), then find its
|
|
66
|
+
# delivery and read the body that was actually rendered:
|
|
67
|
+
plexus-cli --env local notifications list --limit 20
|
|
68
|
+
plexus-cli --env local notifications show <deliveryId> --json | grep -A12 '"letter"'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`letter.text` (and `letter.html`) carry the reworded sentence, and
|
|
72
|
+
`letter.version.reworded` names the blocks in which the school's wording stood,
|
|
73
|
+
`letter.version.locale` the language it went out in — the RECIPIENT ACCOUNT's
|
|
74
|
+
(`users.language`). To see the English letter go out, the recipient's account
|
|
75
|
+
must have chosen English; the preview in Administration → Benachrichtigungen
|
|
76
|
+
shows either language without one. The same rows are the Dev Rail →
|
|
77
|
+
**Mail** panel, which is the human version of the same check (and the place to
|
|
78
|
+
follow a sign-in link this deployment "sent").
|
|
79
|
+
|
|
80
|
+
Roll back — one block, or the whole letter:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
plexus-cli --env local --tenant test notifications letters reset applicant.tasksOpen --locale de --slot intro
|
|
84
|
+
plexus-cli --env local --tenant test notifications letters reset applicant.tasksOpen --locale en --all
|
|
85
|
+
plexus-cli --env local --tenant test notifications chrome set --closing-de="" --closing-en="" # back to the platform sign-off
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The next rendered mail carries the platform text again — no republish, no deploy.
|
|
89
|
+
|
|
90
|
+
## Idempotency / gotchas
|
|
91
|
+
|
|
92
|
+
- **A deviation is a delta, not a copy.** Only the blocks a school rewrote are
|
|
93
|
+
stored. A block left alone keeps following the code, so a later platform
|
|
94
|
+
improvement to it still reaches that school. Setting a block to *exactly* the
|
|
95
|
+
platform text therefore stores nothing — the command's `deviating blocks` line
|
|
96
|
+
is the backend's answer, and it will not list that block.
|
|
97
|
+
- **`set` merges.** Passing `--slot intro=…` does not wipe a deviation on
|
|
98
|
+
`outro`. The way back is `reset`, which names what to drop.
|
|
99
|
+
- **`reset` without `--slot` needs `--all`.** Dropping every deviation of a
|
|
100
|
+
letter must be typed, not be what happens when a flag is forgotten.
|
|
101
|
+
- **Some letters have no blocks at all.** The two sign-in letters
|
|
102
|
+
(`applicant.magicLink`, `applicant.verifyEmail`) are category `login`: the mail
|
|
103
|
+
that carries a login link is not rewordable by anyone, so it declares nothing
|
|
104
|
+
to reword. They show `— (not editable)` in the list.
|
|
105
|
+
- **An unknown block or placeholder throws, naming every finding.** Nothing is
|
|
106
|
+
written — there is no partial save.
|
|
107
|
+
- **`--locale de|en` is required** on `show`, `set` and `reset`: every
|
|
108
|
+
letter exists in German and English, each worded on its own, and a command
|
|
109
|
+
that silently picked one would reword the letter you were not looking at. A
|
|
110
|
+
recipient gets the language of their own account; a school that rewords only
|
|
111
|
+
the German letter sends its English families the platform's English text —
|
|
112
|
+
the workshop says so on the letter.
|
|
113
|
+
- **This is configuration, not operational state.** A change made here is
|
|
114
|
+
exported, diffed and promoted like any other tenant setting — see
|
|
115
|
+
"Promote the wording to another environment" below. On staging/prod prefer
|
|
116
|
+
the config route so the change is reviewable; the direct `letters set` is for
|
|
117
|
+
local exploration and for a one-off correction with the platform operator watching.
|
|
118
|
+
|
|
119
|
+
## Promote the wording to another environment
|
|
120
|
+
|
|
121
|
+
The deviations live in the tenant config under `notificationLetters` (identity
|
|
122
|
+
`templateId:locale:scopeKey`) and the frame under
|
|
123
|
+
`tenant.notificationChrome`, so the ordinary config-as-code verbs carry them:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Export → edit → dry-run → confirm, exactly as for any other tenant setting.
|
|
127
|
+
plexus-cli --env staging config export --tenant test --out /tmp/test.json
|
|
128
|
+
# …edit `notificationLetters` / `tenant.notificationChrome` in /tmp/test.json…
|
|
129
|
+
plexus-cli --env staging config apply --tenant test --from snapshot:/tmp/test.json
|
|
130
|
+
plexus-cli --env staging config apply --tenant test --from snapshot:/tmp/test.json --confirm
|
|
131
|
+
|
|
132
|
+
# One block, without an export round trip. The --key IS the identity triple
|
|
133
|
+
# (templateId:locale:scopeKey) — a letter is worded per language and per scope.
|
|
134
|
+
plexus-cli --env staging config set --tenant test \
|
|
135
|
+
--target notification-letters --key "applicant.tasksOpen:de:tenant" \
|
|
136
|
+
--field slots.intro --value "…" --confirm
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Two things follow from "a deviation is a delta":
|
|
140
|
+
|
|
141
|
+
- A letter **absent** from `notificationLetters` is a letter this school does not
|
|
142
|
+
deviate from, so applying a config without it takes that letter back to the
|
|
143
|
+
platform text. Removing an entry is how you undo a rewording in config.
|
|
144
|
+
- `config set` **edits an existing entry**; it does not create one. The first
|
|
145
|
+
deviation for a letter comes from `notifications letters set` or from an
|
|
146
|
+
edited export.
|
|
147
|
+
|
|
148
|
+
## Related
|
|
149
|
+
|
|
150
|
+
- [Debug a mail delivery](./debug-mail-delivery.md) — when the mail did not
|
|
151
|
+
arrive at all (the operator's log, not the school's wording).
|
|
152
|
+
- [Update a tenant's config](../config/update-tenant-config.md) — the export →
|
|
153
|
+
edit → dry-run → apply loop these letters travel in.
|
|
154
|
+
- `plexus-cli notifications letters show --help` (and `set`/`reset`) — the letter /
|
|
155
|
+
block / override commands; a letter showing `— (not editable)` is one a school may not reword.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Trace why someone could not sign in
|
|
2
|
+
|
|
3
|
+
**When:** „I asked for a sign-in link and got nothing", „the link said already
|
|
4
|
+
used", „my new password does not work" — on any environment, after the fact.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
A platform token with `_platform.users.manage` (sessions name IP addresses) and
|
|
9
|
+
`_platform.notifications.monitor` (the delivery list).
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Everything the platform still knows about the address, oldest first.
|
|
15
|
+
plexus-cli --env prod users login-trail ada@example.com --since 24h
|
|
16
|
+
|
|
17
|
+
# Only the login page's letters to that address, with the relay's answer.
|
|
18
|
+
plexus-cli --env prod notifications list --template account.loginLink --to ada@example.com --since 24h
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`login-trail` prints one time-ordered list:
|
|
22
|
+
|
|
23
|
+
| Line | Means |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `abgelehnt: <reason>` | the login page sent this KNOWN account no link (`platformAdmin`, `unprovenAddress`, `noSchoolWithOption`) |
|
|
26
|
+
| `Brief account.loginLink: sent — Relay: 250 … queued as <id>` | our relay accepted the mail; the queue id finds it in the relay's log |
|
|
27
|
+
| `Sitzung von <ip> — <browser>` | a sign-in happened. A session seconds after the letter from a foreign network is a **mail scanner** (Exchange Safe Links), not the person |
|
|
28
|
+
| `Passwort gesetzt (zuletzt)` | the credential was last changed then — compare with the failed attempts |
|
|
29
|
+
|
|
30
|
+
## Reading it (an invented example)
|
|
31
|
+
|
|
32
|
+
- letter `sent` 09:12:05 → session from `203.0.113.7` at 09:12:29, a network the school
|
|
33
|
+
does not use → the person got „link already used": the scanner spent it. A link now opens a page first and only
|
|
34
|
+
its button spends the token, so a scanner no longer uses it up.
|
|
35
|
+
- password set 09:20:41, session from the school's network 09:20:42, failed
|
|
36
|
+
attempts before: the earlier resets went to **another** account (check the
|
|
37
|
+
`Passwort gesetzt` line of the account the person actually uses).
|
|
38
|
+
|
|
39
|
+
## Gotchas
|
|
40
|
+
|
|
41
|
+
- Not recorded at all (the command lists it under „Nicht erfasst"): rate-limited
|
|
42
|
+
requests, requests for an address without an account (both only in the
|
|
43
|
+
function log, minutes), anything older than 30 days, ended sessions, and what
|
|
44
|
+
happened after our relay (inbox / spam / quarantine / bounce).
|
|
45
|
+
- `sent` in every list means „our relay said 250", never „in the inbox". The
|
|
46
|
+
console's word for it is „Versendet".
|
|
47
|
+
|
|
48
|
+
## Related
|
|
49
|
+
|
|
50
|
+
- [Debug a mail delivery](./debug-mail-delivery.md)
|