|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(spec)!: retire the incident-response, training and change-management families whole — nineteen defs and every name they exported — and the `ESignatureConfig` deadline pair (#15513, #14477, ADR-0049) |
| 6 | + |
| 7 | +<!-- adr-0087: registered incident-response-family-retired, training-family-retired, change-management-family-retired, esignature-config-deadline-keys-retired --> |
| 8 | + |
| 9 | +**BREAKING** — published exported symbols leave `@objectstack/spec/system`, and |
| 10 | +two authorable keys leave `data/ESignatureConfig` — landing after the v17.0.0 |
| 11 | +cut (the lockstep launch-window convention ships it as `minor`; the |
| 12 | +registrations live under protocol major 18, where `os migrate meta` users will |
| 13 | +look). Maintainer ruling 2026-09-05 on #15513 (decision batch #40, ruled A: |
| 14 | +retire the three compliance-shaped families whole via `RETIRED_DEFS_BY_MAJOR`, |
| 15 | +the `integration/ErrorMappingConfig` precedent; none of the three is |
| 16 | +roadmapped) and, in the same stroke, the answer the 2026-09-02 ruling on #14477 |
| 17 | +had held open (no roadmapped e-signature consumer ⇒ the pair retires with the |
| 18 | +rest). ADR-0049 enforce-or-remove decides it — declared-but-unenforced surface |
| 19 | +with zero measured readers comes off. |
| 20 | + |
| 21 | +## What leaves the public surface — the three families, whole |
| 22 | + |
| 23 | +Nineteen defs (the card counted fifteen; the manifest counts nineteen — the |
| 24 | +ruling names the families, the number is the files' reading), forty-five |
| 25 | +exported names, roughly a hundred declared keys, and the generated reference |
| 26 | +pages `references/system/incident-response`, `training` and |
| 27 | +`change-management`: |
| 28 | + |
| 29 | +| file | defs (`json-schema.manifest/system.json` spelling) | |
| 30 | +|:--|:--| |
| 31 | +| `system/incident-response.zod.ts` | `system/Incident`, `system/IncidentCategory`, `system/IncidentNotificationMatrix`, `system/IncidentNotificationRule`, `system/IncidentResponsePhase`, `system/IncidentResponsePolicy`, `system/IncidentSeverity`, `system/IncidentStatus` | |
| 32 | +| `system/training.zod.ts` | `system/TrainingCategory`, `system/TrainingCompletionStatus`, `system/TrainingCourse`, `system/TrainingPlan`, `system/TrainingRecord` | |
| 33 | +| `system/change-management.zod.ts` | `system/ChangeImpact`, `system/ChangePriority`, `system/ChangeRequest`, `system/ChangeStatus`, `system/ChangeType`, `system/RollbackPlan` | |
| 34 | + |
| 35 | +With them: every `*Schema` const, every `z.input` alias (`Incident`, |
| 36 | +`IncidentResponsePolicy`, `TrainingCourse`, `ChangeRequest`, …) and the six |
| 37 | +`*Parsed` aliases (`IncidentNotificationRuleParsed`, |
| 38 | +`IncidentNotificationMatrixParsed`, `IncidentResponsePolicyParsed`, |
| 39 | +`TrainingCourseParsed`, `TrainingPlanParsed`, `ChangeRequestParsed`). |
| 40 | + |
| 41 | +**Why.** The schemas were exported from `@objectstack/spec/system`, mounted by |
| 42 | +no `stack.zod.ts` key, registered as no metadata type, absent from the 2026-06 |
| 43 | +liveness ledgers, and **read by nothing**: the reader census over every package |
| 44 | +outside `packages/spec` (tests and changelogs excluded), over `examples/**` and |
| 45 | +`skills/**`, and over objectui at the pinned sha (`a472b07`) returned zero hits |
| 46 | +for every one of the forty-five names, with a lit control on the same pattern |
| 47 | +(`ObjectSchema` / `FieldSchema`: 336, 200 and 342 hits per leg). Several keys |
| 48 | +were boolean capability claims of exactly the shape ADR-0049 names — |
| 49 | +`IncidentNotificationRule.notifyRegulators`, |
| 50 | +`IncidentResponsePolicy.requirePostIncidentReview`, `TrainingCourse.mandatory`, |
| 51 | +`TrainingPlan.trackCompletion` / `sendReminders`, |
| 52 | +`ChangeRequest.approval.required`, |
| 53 | +`ChangeRequest.securityImpact.requiresSecurityApproval` — so an author writing |
| 54 | +`notifyRegulators: true` held a compliance promise the platform never kept, |
| 55 | +with no error and no feedback, and the reference docs advertised a compliance |
| 56 | +subsystem that does not exist. Tagging the families |
| 57 | +`[EXPERIMENTAL — not enforced]` was the fallback the ruling did not take: it is |
| 58 | +a human-only signal, and an AI generating from the schema still writes the key |
| 59 | +and believes it. |
| 60 | + |
| 61 | +**What happened to the fourteen #14477 deadline-key tombstones** (PR #15514, |
| 62 | +merged 2026-09-04): they leave with their defs' source. Their fourteen |
| 63 | +`RETIRED_KEYS_BY_MAJOR[18]` entries and three D3 entries stay as history — gate |
| 64 | +(b2) of `build-schemas.ts` accepts an entry naming a key the build no longer |
| 65 | +emits, and the 17→18 upgrade guide still owes the reader those prescriptions. |
| 66 | +`deadline-keys-retirement.test.ts`, whose every pin needed the schemas to exist, |
| 67 | +is replaced by `compliance-families-retirement.test.ts`. |
| 68 | + |
| 69 | +## What is refused — the `ESignatureConfig` pair |
| 70 | + |
| 71 | +Authoring `expirationDays` or `reminderDays` on an `ESignatureConfig`, with any |
| 72 | +value, on the base schema and through `Document.eSignature`. The schema is not |
| 73 | +`.strict()`, so each key is a `retiredKey()` tombstone rather than a bare |
| 74 | +deletion (a deletion would have stripped it in silence): authoring it is a `tsc` |
| 75 | +error (`never`) and a parse error carrying the prescription (`invalid_type` at |
| 76 | +the path of the key). Both carried defaults (30 days, 7 days) that were |
| 77 | +materialized into every parsed configuration without ever being consulted; |
| 78 | +parsed configurations no longer carry them. `provider`, `enabled` and `signers` |
| 79 | +stay, byte-identical. Census for the pair: zero hits for `expirationDays`, |
| 80 | +`reminderDays`, `eSignature` and the `ESignatureConfig` names on all three legs, |
| 81 | +control lit inside `packages/spec` (`document.zod.ts` 9, `document.test.ts` 24). |
| 82 | + |
| 83 | +**Unmeasured, verbatim:** `cloud` and real customer configurations are |
| 84 | +UNMEASURED for both the families and the pair — this census covers this repo |
| 85 | +and objectui at the pin. |
| 86 | + |
| 87 | +## FROM → TO |
| 88 | + |
| 89 | +```ts |
| 90 | +// before — imported and parsed green; no engine ever read a single key |
| 91 | +import { IncidentResponsePolicySchema, type IncidentResponsePolicy } from '@objectstack/spec/system'; |
| 92 | +const policy: IncidentResponsePolicy = { |
| 93 | + notificationMatrix: { rules: [{ severity: 'critical', channels: ['pagerduty'], recipients: ['security_team'], notifyRegulators: true }] }, |
| 94 | + defaultResponseTeam: 'security_team', |
| 95 | + requirePostIncidentReview: true, |
| 96 | +}; |
| 97 | +IncidentResponsePolicySchema.parse(policy); |
| 98 | + |
| 99 | +const signing: ESignatureConfig = { |
| 100 | + provider: 'docusign', |
| 101 | + signers: [{ email: 'client@example.com', name: 'John Doe', role: 'Client', order: 1 }], |
| 102 | + expirationDays: 30, |
| 103 | + reminderDays: 7, |
| 104 | +}; |
| 105 | + |
| 106 | +// after — the import is TS2305 and there is no replacement to point at, because |
| 107 | +// no incident-response, training-management or change-management engine exists. |
| 108 | +// A compliance record the organisation keeps is ordinary object data, declared |
| 109 | +// as an object with its own fields and enforced by the object engine; an |
| 110 | +// approval that must actually gate something is a flow (ADR-0018) with an |
| 111 | +// approval node. |
| 112 | +// |
| 113 | +// The e-signature pair: delete the keys. `ESignatureConfig` itself stays. |
| 114 | +const signing: ESignatureConfig = { |
| 115 | + provider: 'docusign', |
| 116 | + signers: [{ email: 'client@example.com', name: 'John Doe', role: 'Client', order: 1 }], |
| 117 | +}; |
| 118 | +``` |
| 119 | + |
| 120 | +One-line fix: delete the import (families) or the key (pair) wherever it is |
| 121 | +authored. There is no `os migrate meta` edit list — none of the schemas is a |
| 122 | +stack collection member and `document` is no metadata type, so the conversion |
| 123 | +chain has no seam to walk (the `MetadataPluginConfig.additionalTypes` |
| 124 | +precedent); the tombstone prescriptions, the `tsc` refusals and the protocol-18 |
| 125 | +upgrade guide are the channels. |
| 126 | + |
| 127 | +The retirement kit: |
| 128 | + |
| 129 | +- the three schema files and their tests deleted whole; the survivor notes in |
| 130 | + `packages/spec/src/system/index.ts` record what each module declared and why |
| 131 | + nothing ever read it |
| 132 | +- ADR-0087 registration: nineteen `RETIRED_DEFS_BY_MAJOR[18]` entries |
| 133 | + (`entries/retired-defs/18.system__*.ts`) and three D3 semantic entries, one |
| 134 | + per family; for the pair, `data/ESignatureConfig:expirationDays` and |
| 135 | + `data/ESignatureConfig:reminderDays` in `RETIRED_KEYS_BY_MAJOR[18]` plus the |
| 136 | + D3 entry `esignature-config-deadline-keys-retired`; the step-18 `rationale` |
| 137 | + extended |
| 138 | +- no liveness-ledger row: none of the families and neither `document` nor |
| 139 | + `ESignatureConfig` is an enrolled ledger type, so there is no row to keep or |
| 140 | + drop |
| 141 | +- pin tests: `compliance-families-retirement.test.ts` (zero holders of the |
| 142 | + forty-five names on every public entry via `export-origins/`, the deletion |
| 143 | + probe, the in-package importer walk, the runtime namespace, the shards' |
| 144 | + absence, the ADR-0087 registration, the #15514 history kept, and a |
| 145 | + tree-scoped absence leg whose walk radius is DECLARED in |
| 146 | + `scripts/cross-package-test-inputs.mjs` / `turbo.json` — the playbook rule |
| 147 | + #15566 added after PR #15514); `esignature-deadline-keys-retirement.test.ts` |
| 148 | + (refusal pins asserting issue path, code and prescription on the base schema |
| 149 | + and through `Document.eSignature`; the tsc `never` channel; no-materialize |
| 150 | + pins for the two former defaults; the ADR-0087 registration); the thirteen |
| 151 | + isomorphism pins the three modules held leave `type-alias-convention.pin.test.ts` |
| 152 | +- generated baselines and docs follow the schema: `json-schema.manifest/` |
| 153 | + loses nineteen keys (the manifest-deletion gate adjudicates whole-def |
| 154 | + removals against the merge base), `api-surface/`, `declaration-map/`, |
| 155 | + `export-origins/`, `authorable-surface/` and `authorable-defaults/` lose the |
| 156 | + families' rows, `authorable-surface/data.json` gains two `[RETIRED]` rows and |
| 157 | + `authorable-defaults/data.json` loses two, the three system reference pages |
| 158 | + are removed and `references/system/index.mdx`, `references/index.mdx` and |
| 159 | + `references/data/document.mdx` regenerated, `spec-changes.json` and the |
| 160 | + upgrade guide carry the four new registrations at the 18 cut |
| 161 | +- hand-written docs: the `Change Management` row leaves |
| 162 | + `getting-started/quick-reference.mdx` |
| 163 | +- zero authored occurrences in this repo's examples, skills and hand-written |
| 164 | + docs beyond that row, and zero hits in objectui at `a472b07`, so no sibling |
| 165 | + change and no pin bump ride along |
0 commit comments