@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,69 @@
|
|
|
1
|
+
# Author a workflow's default chat channels
|
|
2
|
+
|
|
3
|
+
**When to use:** every application of an offer should start with a chat channel
|
|
4
|
+
for a team combination (e.g. „Bewerbungsteam + Schulleitung"), in addition to the
|
|
5
|
+
automatic channel each participating team already gets.
|
|
6
|
+
|
|
7
|
+
Two kinds of channel exist in every application of a published version:
|
|
8
|
+
|
|
9
|
+
| kind | where it comes from | editable? |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| automatic | one per team the process gives work to (`assign`, composed tasks, …) | no — follows the graph |
|
|
12
|
+
| default | `graph.channels` in `workflow.json` | yes — this playbook |
|
|
13
|
+
|
|
14
|
+
Both are read from the application's **pinned** workflow version at chat time. No
|
|
15
|
+
row is written per application, so a new default channel reaches applications
|
|
16
|
+
started on the next published version; running applications keep the channels
|
|
17
|
+
of their version.
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
- An offer bundle on disk with a `workflow.json` (`offer export …` or `offer init …`).
|
|
21
|
+
- Authoring is offline; `offer apply --publish` needs a token with the tenant's offer-configuration rights (a tenant admin has them).
|
|
22
|
+
- Members are **permission-group keys** of the tenant (`plexus-cli --env <env> --tenant <t> users groups`
|
|
23
|
+
→ `key`); the applicant group is not a team.
|
|
24
|
+
|
|
25
|
+
## Steps
|
|
26
|
+
```bash
|
|
27
|
+
# What exists: automatic (read-only) + authored default channels.
|
|
28
|
+
plexus-cli offer workflow channels list --dir <bundle>
|
|
29
|
+
|
|
30
|
+
# Add one — the key is minted from the German label ONCE (here:
|
|
31
|
+
# bewerbungsteam-schulleitung). The label is German AND English.
|
|
32
|
+
plexus-cli offer workflow channels add --dir <bundle> \
|
|
33
|
+
--label "Bewerbungsteam + Schulleitung" --label-en "Admissions team + school leadership" \
|
|
34
|
+
--team bewerbungsteam --team schulleitung
|
|
35
|
+
|
|
36
|
+
# Rename / re-staff in place — pass the key; a rename never duplicates. A German
|
|
37
|
+
# rename always needs --label-en (the English is never copied from the German,
|
|
38
|
+
# and a channel name without English is refused).
|
|
39
|
+
plexus-cli offer workflow channels add --dir <bundle> --key bewerbungsteam-schulleitung \
|
|
40
|
+
--label "Aufnahme" --label-en "Admissions" --team bewerbungsteam --team schulleitung
|
|
41
|
+
|
|
42
|
+
# Remove an authored one by key (verb: `channels rm`).
|
|
43
|
+
|
|
44
|
+
# Release it.
|
|
45
|
+
plexus-cli offer validate --dir <bundle>
|
|
46
|
+
plexus-cli --env <env> offer apply --publish --dir <bundle>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The same section is editable in the workflow IDE: „Team-Verantwortliche" → tab
|
|
50
|
+
„Kanäle" → „Kanal hinzufügen" (the same team picker as the cockpit chat's „+").
|
|
51
|
+
|
|
52
|
+
## Verify
|
|
53
|
+
- `offer workflow channels list` shows the entry with source `Standardkanal`.
|
|
54
|
+
- After publish, start an application (e.g. convert an Interessent) and open its
|
|
55
|
+
cockpit: the chat rail carries a tab with the authored label.
|
|
56
|
+
|
|
57
|
+
## Gotchas
|
|
58
|
+
- **A single team the process already assigns is refused** (`[channels/duplicatesDerived]`)
|
|
59
|
+
— it has its automatic channel.
|
|
60
|
+
- **Unknown team** (`[channels/unknownTeam]`) is only caught at `offer apply --publish`
|
|
61
|
+
(the offline validator has no tenant); the applicant group counts as unknown.
|
|
62
|
+
- A channel's chat identity is its **team set**: a manual „+" channel on the same
|
|
63
|
+
teams is the same thread, and the authored label wins.
|
|
64
|
+
- A permission group named by a default channel cannot be deleted while any
|
|
65
|
+
stored workflow version names it (`referenced-by-workflow`).
|
|
66
|
+
|
|
67
|
+
## Related
|
|
68
|
+
- [Edit a workflow node](./edit-workflow-node.md)
|
|
69
|
+
- `plexus-cli offer workflow channels add --help` · `channels list` · `channels rm --key <key>`
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Manage a contract generator
|
|
2
|
+
|
|
3
|
+
**When to use:** Create or revise a tenant's TypeScript contract generator — or a combined generator over several of them — review scenarios, publish it, and pin an offer to that revision.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- A configured environment and tenant, with `finanzen_vertraege.contracts.manage`.
|
|
8
|
+
- A contract template with an attached DOCX. Use `generator template upload` if needed.
|
|
9
|
+
- A real tenant's source and configuration live in that tenant's data; work on them in a private scratch directory.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
Create a blank draft, or copy a concrete published revision:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
plexus-cli --env local --tenant test generator template list
|
|
17
|
+
plexus-cli --env local --tenant test generator create --key school-contract --name "Schulvertrag" --name-en "School contract" --contract-template-id <id>
|
|
18
|
+
plexus-cli --env local --tenant test generator clone school-contract@2
|
|
19
|
+
plexus-cli --env local --tenant test generator scaffold school-contract --out /tmp/generator.ts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For an existing source, export both the TypeScript and its manifest. Edit the source and the explicit article/grant ID lists in the manifest. Saving checks the exported revision number and refuses concurrent changes.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
plexus-cli --env local --tenant test generator source export school-contract@3 --out /tmp/generator.ts
|
|
26
|
+
plexus-cli --env local --tenant test generator source save school-contract@3 --file /tmp/generator.ts --manifest /tmp/generator.ts.json
|
|
27
|
+
plexus-cli --env local --tenant test generator scenarios test school-contract@3 --input /tmp/input.json --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A blank draft can use a manifest containing `id`, `expectedSourceRevision: 0`, `billingItemDefinitionIds` and `grantTemplateIds`. Read its ID with `generator show`. Source must be self-contained and export one default object with `inputSchema`, `parties`, `resolve(config, input)`, `previewDocument(config, input)` and `signatureSpots(config, input)`. Imports and host capabilities are unavailable. The metadata describes conditional fields; the functions may use arbitrary synchronous TypeScript logic.
|
|
31
|
+
|
|
32
|
+
Review the result, including its signature declarations. Write `/tmp/reviewed.json` with the returned `sourceRevision` as `expectedSourceRevision`, `artifactHash`, and `scenarios: [{ key, input, expected }]`. `expected` is the entire returned `output`, including `contract` and `signatureSpots`. Keep explicit expected results: a test command never approves its observations automatically.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
plexus-cli --env local --tenant test generator scenarios approve school-contract@3 --file /tmp/reviewed.json
|
|
36
|
+
plexus-cli --env local --tenant test generator seal school-contract@3
|
|
37
|
+
plexus-cli --env local --tenant test generator assign school-contract@3 --offer-version-id <wip-id> --node contract
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Publish the WIP offer through the normal offer workflow. Existing applications retain their pinned generator revision. Archiving a revision hides it from new selections while existing pins remain executable.
|
|
41
|
+
|
|
42
|
+
For one-file export/import on the same environment:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
plexus-cli --env local --tenant test generator export school-contract@3 --out /tmp/generator.json
|
|
46
|
+
plexus-cli --env local --tenant test generator apply /tmp/generator.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The export includes source, manifests and reviewed scenarios. Apply saves a draft and checks imported expected results; it does not publish. IDs in this export belong to its environment.
|
|
50
|
+
|
|
51
|
+
### Offer setup by stable catalog keys
|
|
52
|
+
|
|
53
|
+
The existing `contract/resolver-setup.json` filename and `resolver` property remain the bundle format. A source-backed setup adds these fields under `resolver`:
|
|
54
|
+
|
|
55
|
+
- `source`: a TypeScript file relative to the setup file.
|
|
56
|
+
- `billingItemKeys`: every article the generator may emit, including conditional branches.
|
|
57
|
+
- `grantTemplateKeys`: every grant template it may use. These must already exist in the catalog.
|
|
58
|
+
- `scenarios`: reviewed `{ key, input, expected }` cases. A new revision cannot publish without them.
|
|
59
|
+
|
|
60
|
+
Config and scenario values can use `{ "$billingItem": "article-key" }` and `{ "$grantTemplate": "grant-key" }` to reference declared entries. Prices retain the existing `{ "$config": "preise.key" }` reference. Template names and generator names are `{ de, en }`; `documentPlaceholderKeys` must match the DOCX.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
plexus-cli --env local --tenant test generator setup plan --file /tmp/offer/contract/resolver-setup.json
|
|
64
|
+
plexus-cli --env local --tenant test generator setup apply --file /tmp/offer/contract/resolver-setup.json
|
|
65
|
+
plexus-cli --env local --tenant test offer plan --dir /tmp/offer
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A setup that still names `implementation` can only copy a source already stored in that tenant's database. Ambiguous keys require cloning a concrete revision. New generators need no platform release.
|
|
69
|
+
|
|
70
|
+
**Known gap — the first revision of a new setup file.** `generator setup apply` refuses a new revision without reviewed `scenarios`, yet their `expected` output only exists once the source is saved on a draft and evaluated there — and the setup file needs it with `{ "$billingItem": … }` / `{ "$grantTemplate": … }` references, not the IDs `generator scenarios test` prints. There is no intended path through the setup file. Today's workaround: give the file one scenario with a placeholder `expected`; `setup apply` creates the draft and saves the source, then the scenario approval re-executes the scenario and refuses the mismatch — the draft and its source stay. Evaluate each scenario input on that draft (`generator scenarios test <key>@<draft revision> --input <input.json> --json`), review the `output`, write it into the file as `expected` with every article/grant ID replaced by its reference, and run `setup apply` again — it overwrites that draft and seals. Alternatively take the documented manual route above (create → `source save` → `scenarios test` → `scenarios approve` → `seal`) and write the setup file from the sealed revision afterwards. Proposed fix: `generator setup apply --draft` (stop after the source save, no scenario requirement) plus `generator setup scenarios --write` (evaluate every scenario input on that draft and write `expected` back into the file with references, for review).
|
|
71
|
+
|
|
72
|
+
## Combined generator
|
|
73
|
+
|
|
74
|
+
A combined generator is a declaration, not code: one input mask and ordered parts, each pinning a **sealed** document revision by `key@revision`, mapping every part field to a mask field, an expression or a fixed value, optionally running only `when` an expression holds. It has no source, template, manifest or scenarios.
|
|
75
|
+
|
|
76
|
+
Seal the part revisions first (the steps above). Then write the declaration file — easiest by exporting an existing combined revision (the test tenant has `school-with-care`) and editing it. The shape (abridged: a real part maps every field its generator requires — the server names each one missing):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
plexus-cli --env local --tenant test generator combined export school-with-care@1 --out /tmp/combined.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"formatVersion": 1,
|
|
85
|
+
"kind": "combined",
|
|
86
|
+
"key": "school-with-lunch",
|
|
87
|
+
"name": { "de": "Schulvertrag mit Mittagessen", "en": "School contract with lunch" },
|
|
88
|
+
"description": { "de": "", "en": "" },
|
|
89
|
+
"inputSchema": { "fields": [
|
|
90
|
+
{ "key": "startDate", "label": { "de": "Beginn", "en": "Start" }, "type": "date", "required": true },
|
|
91
|
+
{ "key": "ganztag", "label": { "de": "Mit Hort", "en": "With after-school care" }, "type": "boolean", "defaultValue": false },
|
|
92
|
+
{ "key": "lateCare", "label": { "de": "Spätbetreuung", "en": "Late care" }, "type": "boolean", "defaultValue": false, "visibleIf": "ganztag == true" }
|
|
93
|
+
] },
|
|
94
|
+
"parts": [
|
|
95
|
+
{ "partKey": "school", "generator": "school-contract@1",
|
|
96
|
+
"inputs": [ { "field": "startDate", "source": { "from": "field", "field": "startDate" } },
|
|
97
|
+
{ "field": "admissionFeeMode", "source": { "from": "fixed", "value": "standard" } } ] },
|
|
98
|
+
{ "partKey": "care", "generator": "after-school-care@1", "when": "ganztag == true",
|
|
99
|
+
"inputs": [ { "field": "pickupTime", "source": { "from": "expression", "expression": "if (lateCare == true) '18:00' else '16:00'" } } ] }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A file **without `id`** creates the draft of a new key and saves the declaration in one step; a refused declaration removes that empty draft again, so fix the file and save once more. A file **with `id`** (every export) targets exactly that revision, guarded by its `expectedSourceRevision`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
plexus-cli --env local --tenant test generator combined save /tmp/combined.json
|
|
108
|
+
plexus-cli --env local --tenant test generator combined plan school-with-lunch@1 --input /tmp/mask-input.json --strict
|
|
109
|
+
plexus-cli --env local --tenant test generator seal school-with-lunch@1
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
To revise a sealed combined revision, clone it, export the draft, edit, save, seal:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
plexus-cli --env local --tenant test generator clone school-with-care@1
|
|
116
|
+
plexus-cli --env local --tenant test generator combined export school-with-care@2 --out /tmp/combined.json
|
|
117
|
+
plexus-cli --env local --tenant test generator combined save /tmp/combined.json
|
|
118
|
+
plexus-cli --env local --tenant test generator seal school-with-care@2
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`generator combined create --key … --name … --name-en …` makes an empty draft (no mask, no parts) when you prefer to start from the workspace's editor. `plan` is the sandbox's „Positionen“ tab: which parts run for a mask input, the fields each still misses, and its roster issues; a refused input (a `when` that is no boolean) exits with the refusal. Like `generator test` it is a sandbox and exits 0 with fields missing; `--strict` exits 1 when the input would not generate (a missing field, an undecided part, a roster issue, a part's refusal), each named on stderr — for scripts and checks. The PDFs per part come from the workspace.
|
|
122
|
+
|
|
123
|
+
Pin it on a contract node like a document generator: the node's `resolver: { key, revision }` in the bundle (or `generator assign`), the reserved Träger as the node literal `inputs.traeger`, then `offer plan` and `offer apply --publish`. Write only `key` and `revision`: `offer plan|apply` stamp the pin from the environment the way publish does (mask, capacities, `parts`), so the node's `$node.<n>.contracts` resolves offline — no hand-made stamp. `offer validate` alone cannot know the kind and warns `[resolver/pinNotStamped]`. One „Erzeugen“ makes one contract per running part (a generation group), issued and signed together in one Vorgang.
|
|
124
|
+
|
|
125
|
+
- **No change, no write.** `save` of an unedited export prints `outcome: unchanged` and writes nothing; only the names changed → `renamed` (names are renamable at any status); otherwise `saved` (a new `sourceRevision`).
|
|
126
|
+
- **The server judges the declaration.** `save` and `seal` refuse with every gap named — `[generator/combinedDeclaration] …` (a required part field without a source, a type or option that does not fit, a `requiredIf` the mask cannot settle, an impossible roster, a priced mask field no part reads), `[generator/kindChange]` (a key that is already a document generator), the concurrent-save conflict. The CLI prints them verbatim; it does not pre-check.
|
|
127
|
+
- **Parts are pinned, never "newest".** `"generator": "school-contract"` without `@revision` is refused by the file reader. A part must be a sealed document revision when the combined one seals; an archived part blocks the seal, and a part used by a combined revision cannot be archived (`[generator/partInUse]`).
|
|
128
|
+
- **IDs belong to their environment.** An exported `id` only saves on the environment it came from; to bring a declaration to another environment, drop `id`/`expectedSourceRevision`/`revision`/`status` and save it there as a new key (or clone + export there and copy the `inputSchema` and `parts`).
|
|
129
|
+
- `generator export` / `generator source export` on a combined revision refuse and point to `generator combined export`; `show`, `list`, `usages`, `runs`, `clone`, `seal`, `archive`, `restore` and `delete-draft` take either kind.
|
|
130
|
+
|
|
131
|
+
## Verify
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
plexus-cli --env local --tenant test generator list
|
|
135
|
+
plexus-cli --env local --tenant test generator show school-contract@3 --json
|
|
136
|
+
plexus-cli --env local --tenant test generator usages school-contract@3
|
|
137
|
+
plexus-cli --env local --tenant test generator runs school-contract@3
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Also inspect the marked PDF and positions in the contract-template workspace. Scenario evaluation does not require the PDF converter; PDF preview does.
|
|
141
|
+
|
|
142
|
+
## Idempotency / gotchas
|
|
143
|
+
|
|
144
|
+
- Source is compiled in a Node action and executed in QuickJS-WASM inside the Convex transaction. No database, network, filesystem, environment, timers, randomness or ambient clock reaches the guest. Input and output are bounded JSON; CPU, memory and stack are bounded.
|
|
145
|
+
- Published source, compiler artifact, interpreter identity, bootstrap hash and execution limits are frozen. A compiler/runtime change requires a reviewed new revision or an explicit audited migration. Display names remain renamable.
|
|
146
|
+
- Saving source/configuration or removing a scenario invalidates approval. A changed DOCX requires renewed review. Publishing rechecks every expected result against current resolved inputs.
|
|
147
|
+
- Optional `articleDecisions` output entries explain inclusion/omission. When supplied, they must cover the declared article manifest exactly and agree with actual lines. The editor shows when explanations are absent.
|
|
148
|
+
- Article reverse links come from the finite manifest, so conditional possibilities remain visible even when an example omits them.
|
|
149
|
+
- Signature captions must exist in the PDF. Publication checks declarations; rendering verifies their locations.
|
|
150
|
+
- Prices still come from the pricing kernel. Override lines need a price version for currency. Grants, parties and payer allocations pass the normal tenant and commercial validation before materialization.
|
|
151
|
+
- Historical signed snapshots are never rewritten during the table/source migration. An unavailable historical source remains explicitly unavailable; it is never re-pinned to different code.
|
|
152
|
+
- Production writes need an explicit environment and `--allow-prod`; this playbook does not authorize them.
|
|
153
|
+
- To build on an existing generator's code (a second school's variant of a contract), start from `generator clone` or `generator source export`. If you author it as several modules, bundle them into the one self-contained source first (e.g. esbuild with `bundle: true, format: "esm", target: "es2020"`) — `generator source save` refuses imports. The guest has no `Intl` and throws on it: replace every `toLocaleString` with hand-written German formatting. Run `generator scenarios test` on the draft before approving.
|
|
154
|
+
- Fee groups are part of a source manifest (`feeGroups`, with bilingual names and article IDs). In a setup file use `resolver.feeGroups: [{ key, name: { de, en }, billingItemKeys }]`; every member must also be in `resolver.billingItemKeys`. Source export/apply round-trips groups. The catalog has no `plans` section; declare article availability in `billingItems[].programs`. Workflow prices use `pricedGroup` and `derive.group`, resolved through pinned generator revisions.
|
|
155
|
+
|
|
156
|
+
## Related
|
|
157
|
+
|
|
158
|
+
- `plexus-cli generator <verb> --help` for every verb above
|
|
159
|
+
- [Set up a billing catalog and Word templates](../config/set-up-billing-catalog.md)
|
|
160
|
+
- [Edit a live tenant offer](../offer/edit-live-tenant-offer.md)
|
|
161
|
+
- [Release an offer bundle](../offer/publish-offer-bundle.md)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Erase applications past their retention period
|
|
2
|
+
|
|
3
|
+
**When to use:** on the monthly erasure date (below), or as soon as the weekly
|
|
4
|
+
GlitchTip warning „[retention] applications past their retention period await
|
|
5
|
+
erasure" names a tenant. Plexus erases nothing on its own: an
|
|
6
|
+
application whose retention period has ended stays, with all its personal data,
|
|
7
|
+
until someone runs this recipe.
|
|
8
|
+
|
|
9
|
+
> **The DSGVO duty behind it.** Storage limitation (Art. 5(1)(e) DSGVO) requires
|
|
10
|
+
> deleting personal data once its purpose and retention period have ended. Since
|
|
11
|
+
> erasure is no longer automatic, it happens only if a person does it, deliberately
|
|
12
|
+
> and regularly:
|
|
13
|
+
>
|
|
14
|
+
> - **Who:** the Plexus platform operator (the maintainer, or whoever holds a
|
|
15
|
+
> `_platform.tenants.accessAll` token for prod), on behalf of the schools.
|
|
16
|
+
> - **How often:** **monthly**, on the first working day, for **every** tenant on
|
|
17
|
+
> prod; additionally within a week whenever the weekly warning appears. The
|
|
18
|
+
> shortest retention period is 180 days, so a monthly run keeps any application at
|
|
19
|
+
> most about a month past its date.
|
|
20
|
+
> - **Record:** keep the `--json` output of each `apply` run (date, tenant, ids) in
|
|
21
|
+
> your operations log, one directory per run (e.g. `retention-<date>/`). Plexus
|
|
22
|
+
> stores no "who erased" row yet: the application only carries `redactedAt`.
|
|
23
|
+
|
|
24
|
+
## What is due
|
|
25
|
+
|
|
26
|
+
The rule lives in one place on the server (`retentionDueAt`):
|
|
27
|
+
an application is due when it has ENDED (`endedAt` set) and `RETENTION_DAYS` of its
|
|
28
|
+
classification have passed since. The classification follows its outcome:
|
|
29
|
+
|
|
30
|
+
| outcome | classification | kept for |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| approved | admitted | 3650 days (10 years) |
|
|
33
|
+
| rejected | rejected | 180 days |
|
|
34
|
+
| withdrawn | withdrawn | 365 days |
|
|
35
|
+
| expired | expired | 180 days |
|
|
36
|
+
| waitlist | waitlist | 730 days |
|
|
37
|
+
| none | unknown | 365 days |
|
|
38
|
+
|
|
39
|
+
Test applications and soft-deleted ones („gelöscht") are listed too — both still hold
|
|
40
|
+
personal data. A still-running application is never due, even if soft-deleted.
|
|
41
|
+
|
|
42
|
+
## Prerequisites
|
|
43
|
+
|
|
44
|
+
- `plexus-cli` with a platform-operator token for the target env (check the
|
|
45
|
+
connection with `plexus-cli --env prod config show` and `plexus-cli --env prod whoami`);
|
|
46
|
+
both functions need `_platform.tenants.accessAll`.
|
|
47
|
+
- The tenant slug (`plexus-cli --env prod platform tenants`).
|
|
48
|
+
|
|
49
|
+
## Steps
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 1. Read-only: what is due in this tenant. Note the number of rows.
|
|
53
|
+
plexus-cli --env prod --tenant <slug> retention plan
|
|
54
|
+
|
|
55
|
+
# 2. Dry run of the erasure — prints the same list, erases nothing.
|
|
56
|
+
plexus-cli --env prod --tenant <slug> retention apply
|
|
57
|
+
|
|
58
|
+
# 3. Erase. --expect is the count from step 1: if the server now finds a different
|
|
59
|
+
# number of due applications, nothing is erased and you read the plan again.
|
|
60
|
+
mkdir -p retention-$(date +%F)
|
|
61
|
+
plexus-cli --env prod --allow-prod --tenant <slug> retention apply --confirm --expect <n> --json \
|
|
62
|
+
> retention-$(date +%F)/<slug>.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Repeat for every tenant `platform tenants` lists.
|
|
66
|
+
|
|
67
|
+
## Verify
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
plexus-cli --env prod --tenant <slug> retention plan # → no rows
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
An erased application keeps its row and its decision log (`application_events`);
|
|
74
|
+
its answers, documents, chat texts, access log and stored mail bodies are gone, and
|
|
75
|
+
`redactedAt` is set (server function `_redactApplicationCore` holds the full list).
|
|
76
|
+
|
|
77
|
+
## Gotchas
|
|
78
|
+
|
|
79
|
+
- **Erasure cannot be undone.** Read the plan first; there is no restore.
|
|
80
|
+
- **The server decides, not the list.** `retention:eraseDue` refuses an application
|
|
81
|
+
that is not due, belongs to another tenant, or is already erased. A refusal stops the
|
|
82
|
+
run; its message (stderr, not the `--json` file) names the ids erased before it —
|
|
83
|
+
copy them into the record, rerun `plan` and continue.
|
|
84
|
+
- **A different count erases nothing.** Someone ended an application between `plan` and
|
|
85
|
+
`apply`, or another operator erased some — read `plan` again and confirm the new count.
|
|
86
|
+
- **Writes need `--env` named; prod needs `--allow-prod`** (`retention apply` is a
|
|
87
|
+
`confirm` command, its dry run counts as a read).
|
|
88
|
+
- **One request for one person** (Art. 17, a family asks to be erased now) is not this
|
|
89
|
+
recipe: that is „Bewerbung löschen" on the single application
|
|
90
|
+
(`admission.redactApplication`), independent of any period.
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
|
|
94
|
+
- Weekly report: `retentionDueReport` (server cron „retention due report",
|
|
95
|
+
Mondays 03:00 UTC) — a GlitchTip `warning`, `area: admission.retention`, tags
|
|
96
|
+
`tenantSlug`, `dueCount`, `testDueCount`; a tenant whose read failed reports as an
|
|
97
|
+
`error` with `errorClass: scanFailed`.
|
|
98
|
+
- What an erasure removes from the access log is defined in the platform's logging
|
|
99
|
+
concept (Protokollierungskonzept, § 6); ask the platform operator for it.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Delete persons past their school's deletion rule
|
|
2
|
+
|
|
3
|
+
**When to use:** together with the monthly erasure of applications
|
|
4
|
+
([erase-due-applications.md](./erase-due-applications.md)), or as soon as the
|
|
5
|
+
weekly GlitchTip warning „[retention] persons past their tenant's deletion rule
|
|
6
|
+
await erasure" names a tenant. Plexus deletes nobody on its own: a
|
|
7
|
+
school switches a deletion rule on („Rollen" → „Löschregel"), and a person the
|
|
8
|
+
rule reaches stays, with all its data, until someone runs this recipe.
|
|
9
|
+
|
|
10
|
+
> **The duty behind it.** A school that switched the rule on has decided that
|
|
11
|
+
> master data without an active role is kept only for its waiting period
|
|
12
|
+
> (storage limitation, Art. 5(1)(e) DSGVO). Since deletion is not automatic,
|
|
13
|
+
> it happens only if a person does it:
|
|
14
|
+
>
|
|
15
|
+
> - **Who:** the Plexus platform operator (whoever holds a
|
|
16
|
+
> `_platform.config.manage` token for prod — the right of the other person
|
|
17
|
+
> hard delete, `stammdaten persons purge`).
|
|
18
|
+
> - **How often:** **monthly**, on the first working day, for every tenant with a
|
|
19
|
+
> rule; additionally within a week whenever the weekly warning appears.
|
|
20
|
+
> - **Record:** keep the `--json` output of each `apply` run in
|
|
21
|
+
> your operations log, one directory per run (e.g. `retention-<date>/`). A deleted person
|
|
22
|
+
> leaves a `party_role_events` row per role it held (actor: the operator) —
|
|
23
|
+
> and nothing else; the run's output is the record of who it was.
|
|
24
|
+
|
|
25
|
+
## What is due
|
|
26
|
+
|
|
27
|
+
The rule (`tenants.partyRetention`: waiting period in days, optionally a former
|
|
28
|
+
leaf role) and the deadline live in one place on the server (`retentionDeadlineFor`): a person with no active or future
|
|
29
|
+
role, not a draft, is due the waiting period after the latest of its creation,
|
|
30
|
+
its last role change, its last role end and the rule's save time.
|
|
31
|
+
|
|
32
|
+
Due is not deletable. The hard delete (rule `retention`) refuses a person that
|
|
33
|
+
|
|
34
|
+
- any application names or binds — finished and erased ones included,
|
|
35
|
+
- is linked to a user account,
|
|
36
|
+
- a receipt holds (contract, invoice, signature, …),
|
|
37
|
+
- has more than 1000 history rows or 200 role periods.
|
|
38
|
+
|
|
39
|
+
`plan` asks the server this for every due person and shows the reason as
|
|
40
|
+
`blocked: …`; `apply` skips those. Blocked persons stay due — the weekly warning
|
|
41
|
+
counts them apart (`blockedCount`) and raises nothing for them alone.
|
|
42
|
+
|
|
43
|
+
## Prerequisites
|
|
44
|
+
|
|
45
|
+
- `plexus-cli` with a platform-operator token holding `_platform.config.manage`
|
|
46
|
+
(check with `plexus-cli --env prod whoami`).
|
|
47
|
+
- The tenant slug (`plexus-cli --env prod platform tenants`). A tenant without a
|
|
48
|
+
switched-on rule answers `plan` with „hat keine eingeschaltete Löschregel".
|
|
49
|
+
|
|
50
|
+
## Steps
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 1. Read-only: who is due, and who of them may be deleted. Count the
|
|
54
|
+
# `would-erase` rows (blocked ones do not count).
|
|
55
|
+
plexus-cli --env prod --tenant <slug> retention parties plan
|
|
56
|
+
|
|
57
|
+
# 2. Dry run — the same list, deletes nothing.
|
|
58
|
+
plexus-cli --env prod --tenant <slug> retention parties apply
|
|
59
|
+
|
|
60
|
+
# 3. Delete. --expect is the would-erase count from step 1: if the server now
|
|
61
|
+
# finds a different number of deletable persons, nothing is deleted.
|
|
62
|
+
mkdir -p retention-$(date +%F)
|
|
63
|
+
plexus-cli --env prod --allow-prod --tenant <slug> retention parties apply --confirm --expect <n> --json \
|
|
64
|
+
> retention-$(date +%F)/<slug>-parties.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Verify
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
plexus-cli --env prod --tenant <slug> retention parties plan # → only blocked rows, or none
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A deleted person is gone with its values, relationships, unit placements, value
|
|
74
|
+
and role history and the stored files only it held (the party-file rule; a file another person or a form still
|
|
75
|
+
names stays).
|
|
76
|
+
|
|
77
|
+
## Gotchas
|
|
78
|
+
|
|
79
|
+
- **Deletion cannot be undone.** Read the plan first; there is no restore.
|
|
80
|
+
- **The server decides, not the list.** `partyRetention:eraseDue` refuses a person
|
|
81
|
+
of another tenant, one not (yet) due, a switched-off rule, and every block above.
|
|
82
|
+
A refusal stops the run; its message (stderr) names the persons deleted before it
|
|
83
|
+
— copy them into the record, rerun `plan` and continue.
|
|
84
|
+
- **A different count deletes nothing — but the count is not the list.** A role ended,
|
|
85
|
+
the rule changed or another operator ran it between `plan` and `apply` — read `plan`
|
|
86
|
+
again. If one person dropped out and another became due in between, the count still
|
|
87
|
+
matches and `apply` deletes the newcomer: the server still checks each one is due.
|
|
88
|
+
- **One person too large for one transaction stops the whole plan.** A deletion plan
|
|
89
|
+
above the relation engine's budget (`MAX_PLAN_ROWS` rows or the index-read cap) is
|
|
90
|
+
a bug-class error, not a block — `plan` aborts with `[relations] deletion plan
|
|
91
|
+
exceeds …` naming the person, and the weekly report marks the tenant `scanFailed`.
|
|
92
|
+
Review that person by hand (`stammdaten persons purge` after a soft delete, or
|
|
93
|
+
split its data) before the rest can run.
|
|
94
|
+
- **Soft-deleted persons („gelöscht")** are due like any other and marked so.
|
|
95
|
+
- **One person now** (staff deleted someone and the family asks for erasure) is
|
|
96
|
+
not this recipe: [`stammdaten persons purge`](../stammdaten/purge-deleted-person.md).
|
|
97
|
+
- **Writes need `--env` named; prod needs `--allow-prod`.**
|
|
98
|
+
|
|
99
|
+
## Related
|
|
100
|
+
|
|
101
|
+
- Weekly report: `partyRetentionDueReport` (server cron „party retention
|
|
102
|
+
due report", Mondays 03:20 UTC) — a GlitchTip `warning`, `area: stammdaten.retention`,
|
|
103
|
+
tags `tenantSlug`, `dueCount` (deletable), `blockedCount`, `testDueCount` (persons only
|
|
104
|
+
test applications name or bind; today always blocked); a tenant whose read failed
|
|
105
|
+
reports as an `error` with `errorClass: scanFailed`. It shares its shape with the
|
|
106
|
+
applications' report.
|
|
107
|
+
- The rule and its UI: „Rollen" → „Löschregel" in the school's settings.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Migrate direct assignments from a parent role
|
|
2
|
+
|
|
3
|
+
Use this when a tenant already has direct assignments to a role that has children.
|
|
4
|
+
The party detail marks these assignments invalid. Choose a descendant leaf whose
|
|
5
|
+
meaning fits the existing records; create a neutral leaf through the tenant
|
|
6
|
+
configuration first if the existing leaves imply facts you cannot verify.
|
|
7
|
+
|
|
8
|
+
1. On staging, inspect `admin:roleConfiguration` and obtain the source and
|
|
9
|
+
destination role IDs. Check the source count with
|
|
10
|
+
`admin:roleAssignmentMigrationImpact` (counts above 2,000 are capped).
|
|
11
|
+
2. Dry-run and apply the tenant configuration for any new destination role using
|
|
12
|
+
[Update a tenant's config](../config/update-tenant-config.md). Add a search
|
|
13
|
+
filter option if staff need to find the migrated parties.
|
|
14
|
+
3. Once the app revision containing `admin:migrateRoleAssignmentsBatch` is live,
|
|
15
|
+
call it repeatedly until it returns `{"migrated":0}`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
plexus-cli call admin:migrateRoleAssignmentsBatch --kind mutation --env staging \
|
|
19
|
+
client=<tenant> sourceRoleId=<parent-role-id> targetRoleId=<leaf-role-id> --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each call moves at most 50 assignments, preserves dates and unit scope, and
|
|
23
|
+
writes role-change events. It rejects a target outside the source subtree,
|
|
24
|
+
a target with children, or overlapping assignments already on the target.
|
|
25
|
+
If a call fails, inspect the conflict rather than retrying it unchanged.
|
|
26
|
+
4. Verify source count is zero, spot-check affected party details and search,
|
|
27
|
+
then repeat the configuration and migration against production. Export
|
|
28
|
+
production's own configuration first; never apply a staging snapshot blindly.
|
|
29
|
+
|
|
30
|
+
The command requires `administration.roles.manage` for the tenant. Run it under
|
|
31
|
+
an authenticated operator so each event records who made the change.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Signing (e-signature) — `plexus-cli signing …`
|
|
2
|
+
|
|
3
|
+
The CLI surface of the **Signaturvorgang**: one frozen contract revision
|
|
4
|
+
(`contract_snapshots`) ↔ one process at a provider, driven by a fixed backend
|
|
5
|
+
state machine. Two remote providers, `docusign` and `insign`, plus the paper
|
|
6
|
+
path. Setting up a school's provider account:
|
|
7
|
+
[connect-a-school.md](./connect-a-school.md); rotating the platform key:
|
|
8
|
+
[rotate-the-key.md](./rotate-the-key.md).
|
|
9
|
+
|
|
10
|
+
Every command needs `--tenant <slug>` and a caller holding
|
|
11
|
+
`finanzen_vertraege.contracts.manage` on that tenant (the platform operator
|
|
12
|
+
tokens do; `plexus-cli whoami` shows who you are).
|
|
13
|
+
|
|
14
|
+
## The school's connection
|
|
15
|
+
|
|
16
|
+
A provider account belongs to the SCHOOL, not to the deployment: each
|
|
17
|
+
tenant has at most one connection per provider and at most ONE active one — the
|
|
18
|
+
provider its contracts go out through. Setting one up is a platform act; the
|
|
19
|
+
recipe is [connect-a-school.md](./connect-a-school.md). The `test` tenant gets an
|
|
20
|
+
inSign sandbox connection out of the box, active, redirected to
|
|
21
|
+
`admin@plexus.swop.schule`.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
plexus-cli signing connection show --env local --tenant test # no network
|
|
25
|
+
plexus-cli signing connection test --provider insign --env local --tenant test # reaches the account
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`show` prints `E-Signatur: <Anbieter>|keine`, then one row per provider: `state`
|
|
29
|
+
(`platform_unconfigured ▸ incomplete ▸ unverified ▸ failing ▸ ready`), whether it
|
|
30
|
+
is active, the environment, the non-secret settings, the redirect, which secrets
|
|
31
|
+
are held (name · fingerprint · when — never the value), the last test and what
|
|
32
|
+
keeps it from `ready`. `test` reaches the account (DocuSign: JWT grant → token →
|
|
33
|
+
userinfo, and on `consent_required` the URL the school's sender must open once;
|
|
34
|
+
inSign: server version + owner), records the result on the connection and exits
|
|
35
|
+
1 when it fails. A contract goes out only through a connection a test has
|
|
36
|
+
reached since its last change (`ready`, or `failing` after an earlier success).
|
|
37
|
+
|
|
38
|
+
## Send a revision for signature
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
plexus-cli signing list --env local --tenant test # existing requests
|
|
42
|
+
plexus-cli signing start --env local --tenant test --snapshot <contract_snapshots id>
|
|
43
|
+
plexus-cli signing show <signingRequestId> --env local --tenant test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`start` validates (an active connection that has been tested, PDF, latest
|
|
47
|
+
revision, every declared signer has an e-mail — and for inSign a DISTINCT one
|
|
48
|
+
each, no open request on the revision, not already signed), inserts the request
|
|
49
|
+
as `pending` with the connection stamped on it, and schedules the provider call.
|
|
50
|
+
A few seconds later `show` reads `sent` + the `providerRef`; a refusal reads
|
|
51
|
+
`failed` with the provider's own message (DocuSign: `ANCHOR_TAB_STRING_NOT_FOUND`
|
|
52
|
+
when the PDF lacks the `/unterschrift:<role>:<n>/` markers; inSign: "refused the
|
|
53
|
+
signer invitation").
|
|
54
|
+
|
|
55
|
+
There is no `--provider`: the school's active connection decides, and a Vorgang
|
|
56
|
+
keeps talking to the account it started on even after the school switches.
|
|
57
|
+
|
|
58
|
+
On a demo/sandbox account every recipient is redirected to the connection's
|
|
59
|
+
redirect address (`show` prints the original address per signer). For inSign the
|
|
60
|
+
redirect is sub-addressed per recipient (`…+s1@`, `…+s2@`), because it
|
|
61
|
+
identifies an external user BY the address.
|
|
62
|
+
|
|
63
|
+
## Bring the answer back
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
plexus-cli signing reconcile <signingRequestId> --env local --tenant test
|
|
67
|
+
plexus-cli signing show <signingRequestId> --env local --tenant test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`reconcile` re-reads the process from its provider and applies it — the return
|
|
71
|
+
path on local dev (no public webhook). On staging/prod the callback + the
|
|
72
|
+
15-minute poll cron do this without anyone; `reconcile` is for "now". A
|
|
73
|
+
`completed` process stores the signed PDF + the completion certificate
|
|
74
|
+
(DocuSign's Certificate of Completion, inSign's Signaturprotokoll — URLs in
|
|
75
|
+
`show`) and writes one `signatures` row per signer, dated by the PROVIDER.
|
|
76
|
+
|
|
77
|
+
## Correct course
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
plexus-cli signing void <signingRequestId> --reason "Neuer Vertrag folgt" --env local --tenant test
|
|
81
|
+
plexus-cli signing resend <signingRequestId> --party <partyId> --env local --tenant test
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`void` ends the request (`voided`) and the row keeps YOUR reason, whether or not
|
|
85
|
+
the provider records one of its own. The contract may then be regenerated /
|
|
86
|
+
re-frozen and sent again. While a request is open, freezing a new revision is
|
|
87
|
+
refused (regeneration freeze).
|
|
88
|
+
|
|
89
|
+
`resend` reminds one recipient on DocuSign. inSign has no per-recipient resend —
|
|
90
|
+
it reminds every open recipient of the session at once, which is what the caller
|
|
91
|
+
means anyway.
|
|
92
|
+
|
|
93
|
+
## Verify
|
|
94
|
+
|
|
95
|
+
- `signing show` → `status`, per-signer `status`/`signedAt`, artefact URLs, the
|
|
96
|
+
provider webhook `receipt …` rows recorded for the request (evidence, not business
|
|
97
|
+
events — the contract's history is `contract_events`).
|
|
98
|
+
- `plexus-cli call contractSnapshots:listRevisions '{"client":"test","contractId":"…"}'`
|
|
99
|
+
→ `signerCount` / `signedAt` reflect the written `signatures`.
|
|
100
|
+
|
|
101
|
+
## Gotchas
|
|
102
|
+
|
|
103
|
+
- `--snapshot` takes the **contract_snapshots** id (the revision), not the
|
|
104
|
+
contract id. `signing list --contract <id>` shows the requests of a contract.
|
|
105
|
+
- A request on an older revision (not the contract's latest) is refused — freeze first, then start.
|
|
106
|
+
- The DocuSign demo account watermarks documents ("Demonstration Document Only")
|
|
107
|
+
and may sit in a US region: never put real family data through it. inSign's
|
|
108
|
+
sandbox is worse in one respect — its credentials are PUBLISHED by the vendor,
|
|
109
|
+
which is why a sandbox connection cannot be saved without a redirect address.
|
|
110
|
+
- `signing start` answers "hat keinen Anbieter … aktiviert" or "nicht erfolgreich
|
|
111
|
+
getestet": run `signing connection show`, then `signing connection test`.
|
|
112
|
+
- An inSign process that has expired still reads `sent`: inSign cannot report
|
|
113
|
+
expiry, and guessing would end a Vorgang that is still live. Withdraw it.
|