Skip to content

Commit de53a27

Browse files
committed
fix(spec): activityMilestones[].type describe states the real default — an unset type keeps the update row's kind, updated (#20494)
`object.zod.ts` described the field's default as `"completed"`; the runtime never wrote that. `audit-writers.ts` starts `activityType` from `activityTypeFor(action)`, and a milestone can only fire on the UPDATE branch, so an unset `type` has always emitted `updated`. Rewrote the describe to say that and regenerated `content/docs/references/data/object.mdx` with `gen:docs` (never hand-edited). Corrected the plugin-audit pin's title and docblock (`activity-type-vocabulary-enforcement.test.ts`) so they stop describing a divergence and stop saying the finding was "filed separately" — its assertions are unchanged. No schema shape change, no runtime change. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4a1df19 commit de53a27

4 files changed

Lines changed: 21 additions & 10 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
`activityMilestones[].type`'s `.describe()` now states the real default: an unset `type` keeps the update row's kind, `updated` (#20494)
6+
7+
Clause-②: no
8+
9+
No behaviour changes and no schema shape change. `object.zod.ts`'s `activityMilestones[].type` field described its default as `"completed"`; the runtime never wrote that. `audit-writers.ts` starts `activityType` from `activityTypeFor(action)`, and a milestone can only fire on the UPDATE branch (`create` / `delete` return their own summary before the milestone match ever runs), so an unset `type` has always emitted `updated`. `milestone.type` overrides it only when the author actually sets it — that half of the describe was correct and is unchanged.
10+
11+
The corrected string is the published half: it ships in `packages/spec/dist/*.d.ts`, in the JSON Schema under `packages/spec/json-schema/`, and in the generated `content/docs/references/data/object.mdx` (regenerated with `gen:docs`, never hand-edited). A repo-wide search for the old wording found no other hand-written copy; `object.form.ts`'s `activityMilestones.type` help text ("Unset: updated.", shipped with PR #20485) already stated the real default and is unchanged.
12+
13+
`packages/plugins/plugin-audit/src/activity-type-vocabulary-enforcement.test.ts` already measured the runtime's real answer — its title and docblock are corrected in the same PR to stop describing a divergence and stop saying the finding was "filed separately" (this card, #20494, is where it was filed). Its assertions are byte-for-byte unchanged.

‎content/docs/references/data/object.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ const result = ApiMethod.parse(data);
354354
| **field** | `string` | ✅ | Field to watch (typically a status/stage select). |
355355
| **value** | `string` | ✅ | The value the field must transition INTO to fire the milestone. |
356356
| **summary** | `string` | ✅ | Activity summary template; `{field}` tokens interpolate the record value. e.g. "Deal won: `{name}`". |
357-
| **type** | `string` | optional | Activity type for the emitted row (default "completed"). |
357+
| **type** | `string` | optional | Activity type for the emitted row — left unset, it keeps the update row's kind, "updated" (a milestone only fires on an update). |
358358

359359
### Nested Shape: `Object.listViews[string]`
360360

‎packages/plugins/plugin-audit/src/activity-type-vocabulary-enforcement.test.ts‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,13 @@ describe('[#8203] sys_activity.type — the writers emit declared values', () =>
266266
});
267267

268268
/**
269-
* The real default when a milestone omits `type`. Pinned because the spec's
270-
* own field description says otherwise — `object.zod.ts` documents
271-
* `activityMilestones[].type` as 'Activity type for the emitted row (default
272-
* "completed")', while the code default is `activityTypeFor(action)` and a
273-
* milestone can only fire on the UPDATE branch, making it `updated`.
274-
* Filed separately; pinned here so the divergence is measured rather than
275-
* argued from either side's prose.
269+
* The real default when a milestone omits `type`: `activityTypeFor(action)`,
270+
* and a milestone can only fire on the UPDATE branch, so the emitted row is
271+
* `updated`. `object.zod.ts`'s `activityMilestones[].type` describe states
272+
* this default (#20494); pinned here so the runtime behaviour stays
273+
* measured, not just described.
276274
*/
277-
it('a milestone without `type` emits `updated` — not the "completed" the spec text claims', async () => {
275+
it('a milestone without `type` keeps the update row\'s kind, `updated`', async () => {
278276
const { engine, storeFor } = await boot();
279277
await engine.insert('biz_ticket', { id: 't3', title: 'Three', stage: 'open' });
280278
await moveStage(engine, 't3', 'plain');

‎packages/spec/src/data/object.zod.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2120,7 +2120,7 @@ const ObjectSchemaBase = strictObject(
21202120
field: z.string().describe('Field to watch (typically a status/stage select).').meta({ title: 'Field' }),
21212121
value: z.string().describe('The value the field must transition INTO to fire the milestone.').meta({ title: 'Value' }),
21222122
summary: z.string().describe('Activity summary template; {field} tokens interpolate the record value. e.g. "Deal won: {name}".').meta({ title: 'Summary' }),
2123-
type: z.string().optional().describe('Activity type for the emitted row (default "completed").').meta({ title: 'Type' }),
2123+
type: z.string().optional().describe('Activity type for the emitted row — left unset, it keeps the update row\'s kind, "updated" (a milestone only fires on an update).').meta({ title: 'Type' }),
21242124
})).optional().describe('Declarative semantic activity milestones — emit a templated timeline row when a field transitions into a value, no hook code (ADR-0052 §5b.2).'),
21252125

21262126
// ADR-0020: record state machines are not a separate `stateMachines` map —

0 commit comments

Comments
 (0)