Skip to content

Commit c4bde18

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-14356-unchecked-response-literal-sites
2 parents 6d6b55c + 0c5e973 commit c4bde18

41 files changed

Lines changed: 3870 additions & 239 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): retire the fourteen inert deadline keys of the incident-response, training and change-management schemas (#14477, ADR-0049)
6+
7+
<!-- adr-0087: registered incident-response-deadline-keys-retired, training-deadline-keys-retired, change-management-duration-keys-retired -->
8+
9+
**BREAKING** accept-set narrowing, landing after the v17.0.0 cut (the lockstep
10+
launch-window convention ships it as `minor`; the migration prescriptions are
11+
registered under protocol major 18, where `os migrate meta` users will look).
12+
Maintainer ruling 2026-09-02 on the census card (ruled A: retire per family):
13+
ADR-0049 enforce-or-remove decides it — declared-but-unenforced deadline
14+
surface with zero measured readers comes off.
15+
16+
Fourteen hour/minute/day-shaped deadline, SLA and duration key sites — twelve
17+
distinct names, because `durationMinutes` and `estimatedMinutes` each occur at
18+
two sites — sat on the exported incident-response, training and
19+
change-management schemas and in the generated reference docs, and **nothing
20+
read them**: the schemas are exported from `@objectstack/spec/system`, mounted
21+
by no stack key, registered as no metadata type, absent from the 2026-06
22+
liveness ledgers, and the reader census over every package outside
23+
`packages/spec` (tests and changelogs excluded) and over objectui at the
24+
pinned sha returned zero hits for every key. An author could write
25+
`triageDeadlineHours: 4`, `validityDays: 365` or `regulatorDeadlineHours: 72`
26+
and reasonably expect the platform to escalate, expire or notify — it never
27+
did, and it never said so. Six of the keys carried defaults (30 minutes,
28+
1 hour, 2555 days; 365, 30 and 14 days) that were materialized into every
29+
parsed document without ever being consulted. A compliance-shaped deadline
30+
that fails silently is the worst form of the shape ADR-0049 names.
31+
32+
**What is refused:** authoring any of the keys below, with any value, on the
33+
base schema and through every carrier that nests it (`Incident.responsePhases[]`,
34+
`IncidentResponsePolicy.notificationMatrix`, `TrainingPlan.courses[]`,
35+
`ChangeRequest.impact` / `.rollbackPlan` / `.implementation`). None of the
36+
schemas is `.strict()`, so each key is a `retiredKey()` tombstone rather than a
37+
bare deletion (a deletion would have stripped it in silence): authoring it is a
38+
`tsc` error (`never`) and a parse error carrying the prescription
39+
(`invalid_type` at the path of the key).
40+
41+
| schema | retired keys |
42+
|:--|:--|
43+
| `IncidentResponsePhase` | `targetHours` |
44+
| `IncidentNotificationRule` | `withinMinutes`, `regulatorDeadlineHours` |
45+
| `IncidentNotificationMatrix` | `escalationTimeoutMinutes` (default 30) |
46+
| `IncidentResponsePolicy` | `triageDeadlineHours` (default 1), `retentionDays` (default 2555) |
47+
| `TrainingCourse` | `durationMinutes`, `validityDays` |
48+
| `TrainingPlan` | `recertificationIntervalDays` (default 365), `gracePeriodDays` (default 30), `reminderDaysBefore` (default 14) |
49+
| `ChangeImpact` | `downtime.durationMinutes` |
50+
| `RollbackPlan` | `steps[].estimatedMinutes` |
51+
| `ChangeRequest` | `implementation.steps[].estimatedMinutes` |
52+
53+
**What stays, byte-identical:** every other key of the three families with its
54+
default and its (absent) readers, and every export — no def leaves the public
55+
surface. Parsed documents no longer carry the six former defaults.
56+
57+
**Held, not touched:** the `ESignatureConfig` pair (`expirationDays`,
58+
`reminderDays` in `data/document.zod.ts`) — the ruling left that branch open
59+
pending the e-signature roadmap answer; it stays on the card.
60+
61+
## FROM → TO
62+
63+
```ts
64+
// before — parsed green; no engine ever read a single one of these numbers
65+
const policy: IncidentResponsePolicy = {
66+
notificationMatrix: {
67+
rules: [{ severity: 'critical', channels: ['pagerduty'], recipients: ['security_team'],
68+
withinMinutes: 15, notifyRegulators: true, regulatorDeadlineHours: 72 }],
69+
escalationTimeoutMinutes: 45,
70+
},
71+
defaultResponseTeam: 'security_team',
72+
triageDeadlineHours: 2,
73+
retentionDays: 3650,
74+
};
75+
const course: TrainingCourse = {
76+
id: 'COURSE-SEC-001', title: 'Security Fundamentals', description: '',
77+
category: 'security_awareness', targetRoles: ['all_employees'],
78+
durationMinutes: 60, validityDays: 365,
79+
};
80+
const rollback: RollbackPlan = {
81+
description: 'Restore from backup',
82+
steps: [{ order: 1, description: 'Restore backup', estimatedMinutes: 15 }],
83+
};
84+
85+
// after — delete the keys; there is no replacement because no incident-response,
86+
// training-management or change-management engine exists to keep a deadline.
87+
// Record retention is the object-level `lifecycle` block (ADR-0057), declared on
88+
// the object that stores the records.
89+
const policy: IncidentResponsePolicy = {
90+
notificationMatrix: {
91+
rules: [{ severity: 'critical', channels: ['pagerduty'], recipients: ['security_team'],
92+
notifyRegulators: true }],
93+
},
94+
defaultResponseTeam: 'security_team',
95+
};
96+
const course: TrainingCourse = {
97+
id: 'COURSE-SEC-001', title: 'Security Fundamentals', description: '',
98+
category: 'security_awareness', targetRoles: ['all_employees'],
99+
};
100+
const rollback: RollbackPlan = {
101+
description: 'Restore from backup',
102+
steps: [{ order: 1, description: 'Restore backup' }],
103+
};
104+
```
105+
106+
One-line fix: delete the key wherever it is authored. There is no
107+
`os migrate meta` edit list for these keys — none of the schemas is a stack
108+
collection member, so the conversion chain has no seam to walk (the
109+
`MetadataPluginConfig.additionalTypes` precedent); the tombstone prescription
110+
and the protocol-18 upgrade guide are the channels.
111+
112+
The retirement kit:
113+
114+
- `retiredKey()` tombstones at all fourteen sites (`packages/spec/src/system/
115+
incident-response.zod.ts`, `training.zod.ts`, `change-management.zod.ts`;
116+
each file's section comment records what the shape was and why no D2
117+
conversion exists)
118+
- ADR-0087 registration: fourteen `RETIRED_KEYS_BY_MAJOR[18]` entries (the
119+
three nested change-management sites spelled `ChangeImpact:downtime.durationMinutes`,
120+
`RollbackPlan:steps.estimatedMinutes`, `ChangeRequest:implementation.steps.estimatedMinutes`)
121+
and three D3 semantic entries, one per family
122+
- no liveness-ledger row: none of the three families is an enrolled ledger
123+
type, so there is no row to keep or drop
124+
- pin tests (`deadline-keys-retirement.test.ts`): a refusal pin per site
125+
asserting the issue path, code and prescription on the base schema and
126+
through the nesting carriers; the tsc `never` channel; no-materialize pins
127+
for the six former defaults; the ADR-0087 registration; and a tree-scoped
128+
absence pin over every authored source in the repo
129+
- generated baselines and docs follow the schema: `authorable-surface/` gains
130+
eleven `[RETIRED]` rows, `authorable-defaults/` loses six rows, the three
131+
system reference pages are regenerated, and the gitignored `json-schema/`
132+
output is re-emitted on the next build
133+
- `json-schema.manifest/` is unchanged, and correctly so: it ratchets def
134+
*names*, and retiring keys removes no def from the published surface
135+
- `spec-changes.json` and the protocol upgrade guide are unchanged too: both
136+
project the migration chain at the current protocol major (17), so these
137+
protocol-18 registrations reach them at the 18 cut
138+
- zero authored occurrences in this repo's examples, skills and hand-written
139+
docs, and zero hits in objectui at the pinned sha, so no in-repo source
140+
changes ride along beyond the three families' own unit tests

.github/workflows/lint.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3188,6 +3188,23 @@ jobs:
31883188
- name: Console-intercept disarm
31893189
run: node scripts/check-console-intercept-disarm.mjs --self-test && node scripts/check-console-intercept-disarm.mjs
31903190

3191+
# Declared registry log level (#15425, origin #13517). objectql's
3192+
# SchemaRegistry logs one `[Registry] Registered …` line per registered
3193+
# item per construction, at its shipped `info` default; a suite that boots
3194+
# app stacks pays items x boots of that on a shard log nobody reads.
3195+
# PR #13985 and PR #14016 declared `OS_REGISTRY_LOG` in four harnesses and
3196+
# removed ~47,900 lines — and nothing held them there, so deleting the
3197+
# line restored five figures of output with every suite still green. This
3198+
# asserts the declaration in every package whose OWN TEST SOURCES boot the
3199+
# engine, a DERIVED population (registry construction / `bootStack` /
3200+
# example-app import), not a roster: the tree's own audit
3201+
# (docs/audits/2026-09-test-log-volume-census.md) measures packages/rest at
3202+
# 528 residual `[Registry]` lines, so a roster of the four that already
3203+
# declared would have shipped a known-incomplete list. Self-test first,
3204+
# then the real scan; reads ~72 manifests and their test sources.
3205+
- name: Declared registry log level
3206+
run: node scripts/check-registry-log-declared.mjs --self-test && node scripts/check-registry-log-declared.mjs
3207+
31913208
# Live-server database isolation (#10382). CI provisions ONE Postgres and
31923209
# ONE MySQL for the whole temporal-conformance job and points every live
31933210
# leg at them, and every live suite in the repo issues a `drop` when it

content/docs/references/system/change-management.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ const result = ChangeImpactSchema.parse(data);
3030
| **level** | `Enum<'low' \| 'medium' \| 'high' \| 'critical'>` || Impact level |
3131
| **affectedSystems** | `string[]` || Affected systems |
3232
| **affectedUsers** | `number` | optional | Affected user count |
33-
| **downtime** | `{ required: boolean; durationMinutes?: number }` | optional | Downtime information |
33+
| **downtime** | `{ required: boolean }` | optional | Downtime information |
3434

3535
### Nested Shape: `ChangeImpact.downtime`
3636

3737
| Property | Type | Required | Description |
3838
| :--- | :--- | :--- | :--- |
3939
| **required** | `boolean` || Downtime required |
40-
| **durationMinutes** | `number` | optional | Downtime duration |
40+
| **durationMinutes** | `never` | optional | [REMOVED] `ChangeImpact.downtime.durationMinutes` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no engine scheduled a maintenance window or measured an outage against it, so the declared downtime was never enforced, announced or compared with what happened. Delete the key. There is no replacement, because no change-management engine exists. |
4141

4242

4343
---
@@ -84,22 +84,22 @@ const result = ChangeImpactSchema.parse(data);
8484
| **level** | `Enum<'low' \| 'medium' \| 'high' \| 'critical'>` || Impact level |
8585
| **affectedSystems** | `string[]` || Affected systems |
8686
| **affectedUsers** | `number` | optional | Affected user count |
87-
| **downtime** | `{ required: boolean; durationMinutes?: number }` | optional | Downtime information |
87+
| **downtime** | `{ required: boolean }` | optional | Downtime information |
8888

8989
### Nested Shape: `ChangeRequest.implementation`
9090

9191
| Property | Type | Required | Description |
9292
| :--- | :--- | :--- | :--- |
9393
| **description** | `string` || Implementation description |
94-
| **steps** | `{ order: number; description: string; estimatedMinutes: number }[]` || Implementation steps |
94+
| **steps** | `{ order: number; description: string }[]` || Implementation steps |
9595
| **testing** | `string` | optional | Testing procedure |
9696

9797
### Nested Shape: `ChangeRequest.rollbackPlan`
9898

9999
| Property | Type | Required | Description |
100100
| :--- | :--- | :--- | :--- |
101101
| **description** | `string` || Rollback description |
102-
| **steps** | `{ order: number; description: string; estimatedMinutes: number }[]` || Rollback steps |
102+
| **steps** | `{ order: number; description: string }[]` || Rollback steps |
103103
| **testProcedure** | `string` | optional | Test procedure |
104104

105105
### Nested Shape: `ChangeRequest.schedule`
@@ -177,7 +177,7 @@ const result = ChangeImpactSchema.parse(data);
177177
| Property | Type | Required | Description |
178178
| :--- | :--- | :--- | :--- |
179179
| **description** | `string` || Rollback description |
180-
| **steps** | `{ order: number; description: string; estimatedMinutes: number }[]` || Rollback steps |
180+
| **steps** | `{ order: number; description: string }[]` || Rollback steps |
181181
| **testProcedure** | `string` | optional | Test procedure |
182182

183183
### Nested Shape: `RollbackPlan.steps[number]`
@@ -186,7 +186,7 @@ const result = ChangeImpactSchema.parse(data);
186186
| :--- | :--- | :--- | :--- |
187187
| **order** | `number` || Step order |
188188
| **description** | `string` || Step description |
189-
| **estimatedMinutes** | `number` | | Estimated duration |
189+
| **estimatedMinutes** | `never` | optional | [REMOVED] `RollbackPlan.steps[].estimatedMinutes` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no engine executed, timed or summed rollback steps, so the estimate was never compared with anything. Delete the key from every step. There is no replacement, because no change-management engine exists. |
190190

191191

192192
---

0 commit comments

Comments
 (0)