Skip to content

Commit 29e4671

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-14848-object-conflict-merge-refuses-collections
2 parents 3871913 + c463d03 commit 29e4671

74 files changed

Lines changed: 3768 additions & 4155 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: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
The environment artifact's `checksum` now states its own coverage boundary, and `grantedPermissions` states that it sits outside the digest by design.
6+
7+
Describe text only — no key, value schema or accept-set change on `EnvironmentArtifactSchema`, and the digest itself is computed and verified by the control plane, not here.
8+
9+
- **`checksum`** carried the shared `Sha256DigestSchema` describe ("SHA-256 digest (64 hex chars)"), which says what the value *is* and nothing about what it *covers*. It now has its own field-level describe: the SHA-256 digest of the canonical JSON serialization of the `metadata` block (stable key ordering), computed by the control plane when assembling the GET response — and coverage stops there, so no other key on the envelope is under the digest. The shared `Sha256DigestSchema` describe is unchanged, so every other digest field still inherits it.
10+
- **`grantedPermissions`** gains one sentence group at the end of its describe: it sits beside `metadata`, outside the digest, and integrity of the granted consent set rests on the carrier — the artifact is environment-local and control-plane served (ADR-0003 / cloud ADR-0007) — an accepted boundary of this envelope rather than an oversight. Its five existing clauses (the manifest-`id` keying, the `sys_package_installation` source, the enforcer consumer, absent ≠ `{}`) are unchanged.

.changeset/lucky-doors-tickle.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@objectstack/objectql': patch
3+
'@objectstack/lint': patch
4+
---
5+
6+
Stop reporting a declarative `operation: 'update'` action as "a button wired to nothing"
7+
8+
The boot action-governance inventory (ADR-0110 D5) built its `unboundDeclarations`
9+
finding from a `type`-only test. The declarative single-record field write
10+
(`operation: 'update'` + `patch`, #14092) is exactly the shape that test mistakes
11+
for a dead button: `ActionSchema` refuses `target` and `body` beside it and keeps
12+
`type` at its default `script`, because the platform action route is where the
13+
write is performed. Every such action was named at every boot and every
14+
`metadata:reloaded` — with a prescription ("add a `body`, or register a handler
15+
under the declared `target`") that parse itself refuses.
16+
17+
Both readers now read `operation` before `type`, the precedence the runtime doors
18+
already use: the engine inventory, and the authoring-time AI tool-reference rule,
19+
which had diverged from the runtime's listing door and reported a resolvable
20+
`action_<name>` reference as fictional.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
"@objectstack/driver-memory": minor
3+
---
4+
5+
`driver-memory` analytics resolves `dateRange` on the UTC calendar, so `'today'` and `last N ...` stop being offset by the process timezone (#15825)
6+
7+
`MemoryAnalyticsService.query()` lowers a string `dateRange` through
8+
`parseDateRangeString()`, and that function built its window on the **local**
9+
calendar and rendered it as **UTC**. Two independent defects lived in it.
10+
11+
**1. The window boundary was local midnight.** `new Date(y, m, d)` constructs
12+
local midnight; `toISOString()` renders that instant in UTC. So in any process
13+
not sitting at UTC, the `'today'` bucket was the **local** day expressed as a
14+
UTC range. Measured 2026-09-05, with the clock at `2026-09-05T20:51Z`:
15+
16+
| `TZ` | `'today'` window produced | the UTC day it should be |
17+
|:---|:---|:---|
18+
| `UTC` | `2026-09-05T00:00Z``2026-09-06T00:00Z` | (agrees) |
19+
| `Asia/Shanghai` | `2026-09-05T16:00Z``2026-09-06T16:00Z` | `2026-09-05T00:00Z``2026-09-06T00:00Z` |
20+
| `America/Los_Angeles` | `2026-09-05T07:00Z``2026-09-06T07:00Z` | `2026-09-05T00:00Z``2026-09-06T00:00Z` |
21+
| `Europe/Berlin` | `2026-09-04T22:00Z``2026-09-05T22:00Z` | `2026-09-05T00:00Z``2026-09-06T00:00Z` |
22+
| `Asia/Kolkata` | `2026-09-05T18:30Z``2026-09-06T18:30Z` | `2026-09-05T00:00Z``2026-09-06T00:00Z` |
23+
24+
That is wrong on **every day of the year**, with no DST transition needed.
25+
26+
**2. The `last N ...` legs mixed two calendars.** `setDate(getDate() - n)` is
27+
local arithmetic and `toISOString()` is a UTC rendering. `setDate` preserves
28+
wall-clock time, so the instant moves `n × 24h` only while every local day in
29+
the window is 24 hours long; across a DST transition it moves 23h or 25h and
30+
the window start slips an hour. `setMonth` / `setFullYear` are the same class,
31+
and can move it by a whole day: at `America/New_York` with the clock at
32+
`2026-01-01T12:00Z`, `last 1 month` started at `2025-12-02T00:00Z` instead of
33+
`2025-12-01T00:00Z`.
34+
35+
**⛔ The two do not fix each other**, which is the easiest thing to get wrong
36+
here: `setUTCDate` alone leaves the local-midnight boundary in place, and
37+
`Date.UTC` alone leaves the arithmetic mixed. Both are repaired, each is pinned
38+
by its own file, and each was ablated on its own to prove the separation.
39+
40+
**Why UTC and not "any consistent calendar".** The rest of the platform
41+
resolves a bare date to the UTC day — `@objectstack/core`'s `{today}`
42+
filter-token macro builds its reference day as
43+
`new Date(Date.UTC(year, month - 1, day))` and falls back to UTC parts when the
44+
context carries no timezone, and `{TODAY()}` in flow templates resolves to the
45+
UTC day (#14852 repaired the identical two-calendar shape there). Before this
46+
change the same analytics question asked through the driver's `dateRange` and
47+
through a flow token could select **different rows in one deployment**. UTC is
48+
also the terminal fallback of the engine's own resolution chain
49+
(`selection.timezone ?? context.timezone ?? 'UTC'`, ADR-0053 Phase 2).
50+
51+
**What did not change.** The parser's vocabulary, its `[range, range]`
52+
fallback, and the shape of the emitted `$match` are untouched — this is a
53+
calendar repair, not a rewrite. `AnalyticsQuery.timezone` is still not consulted
54+
by this path; making the range tokens timezone-**aware** is a separate and
55+
larger question, which #14852 also declined.
56+
57+
**Who sees a difference.** Any deployment whose process is not at UTC: `'today'`
58+
and `last N ...` now select the UTC day they always claimed to, so charts built
59+
on a string `dateRange` shift by the process offset — toward agreement with
60+
`{today}` / `{TODAY()}` and with the same query run at `TZ=UTC`. Deployments
61+
already running at UTC are unaffected; the two spellings are indistinguishable
62+
there, which is exactly why CI never reddened on this.

content/docs/getting-started/quick-reference.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,14 @@ Plugin architecture, manifests, and kernel runtime.
8585
| **[Metadata Loader](/docs/references/kernel/metadata-loader)** | `metadata-loader.zod.ts` | MetadataLoaderContract | Metadata loading |
8686
| **[Package Registry](/docs/references/kernel/package-registry)** | `package-registry.zod.ts` | InstalledPackage, InstallPackageRequest | Package resolution |
8787

88-
## System Protocol (17 of 36 schemas)
88+
## System Protocol (16 of 33 schemas)
8989

9090
Runtime environment, logging, jobs, caching, and observability.
9191

9292
| Protocol | Source File | Key Schemas | Purpose |
9393
|:---------|:-----------|:------------|:--------|
9494
| **[Auth Config](/docs/references/system/auth-config)** | `auth-config.zod.ts` | AuthConfig | Authentication configuration |
9595
| **[Cache](/docs/references/system/cache)** | `cache.zod.ts` | CacheConfig | Caching layer |
96-
| **[Change Management](/docs/references/system/change-management)** | `change-management.zod.ts` | ChangeRequest, RollbackPlan | Change tracking |
9796
| **[Collaboration](/docs/references/system/collaboration)** | `collaboration.zod.ts` | Collaboration | Real-time collab |
9897
| **[Encryption](/docs/references/system/encryption)** | `encryption.zod.ts` | Encryption | Encryption & keys |
9998
| **[HTTP Server](/docs/references/system/http-server)** | `http-server.zod.ts` | RouteHandlerMetadata, MiddlewareConfig | Route + middleware metadata |

0 commit comments

Comments
 (0)