Skip to content

Commit ba5bfe9

Browse files
docs(spec): describe the history event source as an open code-path label, not surface labels no writer stamps (#24334)
Fixes #24328 Clause-②: no ## What changes One `.describe()` in `packages/spec`, the page it generates, and a prose-only `patch` changeset. The field is `events[].source` in `HistoryMetaItemResponseSchema` (`packages/spec/src/api/protocol.zod.ts:1884`), the body of `GET /api/v1/meta/:type/:name/history`. Before: > Origin label of the write: "fs", "studio", "rest", "ai", "git-import", … After: > Which code path wrote the event: an open label the server stamps, not an enum an author or client picks from (e.g. `protocol.saveMetaItem`, `protocol.publishMetaItem`, `migrate-stored`). The history row's stored value is passed through; `sys-metadata-repo` is the repository's own default and the answer for a row that stored none. - `content/docs/references/api/protocol.mdx:2030`: regenerated with `pnpm --filter @objectstack/spec gen:docs`. `check:generated` proved it the one stale artifact (`1 of 14 artifact(s) stale: content/docs/references/**`). - `.changeset/24328-history-source-describe.md`: `'@objectstack/spec': patch`, with `Clause-②: no`. The field stays `z.string()`. No key, type, export, accepted value or message text changes, and the door answers exactly what it answered before. The wording follows the audit twin's `source` describe in the same file (`:1728`, "Which code path recorded the event (e.g. `protocol.deleteMetaItem`)"). ## What each claim rests on All line numbers are on this branch's merge base `117301705`. The merge brought no change under `packages/metadata-*`. **Which rows this door reads.** `historyMetaItem` (`packages/metadata-protocol/src/protocol.ts:24594`) reads `this.getOverlayRepo().history(ref, opts)`. `getOverlayRepo()` (`:8215`) always builds a `SysMetadataRepository`, so the events are `sys_metadata_history` rows. It is the only implementation of the verb in this repository (`git grep historyMetaItem` over non-test `packages/**`: the implementation, the REST door `meta-item-history-routes.ts:305`, the spec interface). **"the history row's stored value is passed through; `sys-metadata-repo` ... the answer for a row that stored none".** The history projection, `sys-metadata-repository.ts:1821`: `source: (row.source as string | undefined) ?? 'sys-metadata-repo'`. **"`sys-metadata-repo` is the repository's own default".** The history insert in `put()`, `sys-metadata-repository.ts:1121`: `source: opts.source ?? 'sys-metadata-repo'`. The delete path has the same default (`:1271`). **The three labels named:** - `protocol.saveMetaItem`: `protocol.ts:22655`, `const writeSource = request.source ?? 'protocol.saveMetaItem';`, which the save hands to `repo.put` (`:23890`, `source: writeSource` at `:23896`). - `protocol.publishMetaItem`: `promoteDraftForPublish` (`protocol.ts:25012`) calls `repo.promoteDraft` with `source: 'protocol.publishMetaItem'` (`:25338`–`:25342`). Both `publishMetaItem` and `publishPackageDrafts` promote through it. - `migrate-stored`: `migrateStoredMetadata` saves with `source: 'migrate-stored'` (`protocol.ts:24536`), and so does the flow credential migration (`packages/services/service-automation/src/flow-credential-migration.ts:228`). **"an open label ... not an enum".** The list is not closed, so the describe gives examples and says the value passes through. Other labels the same writers stamp: `protocol.rollbackMetaItem` (`protocol.ts:29816`), `protocol.deleteMetaItem` (`:30636`), `protocol.revertCommit` (`:29319`, `:29465`), `sys-metadata-repo.publish` (`sys-metadata-repository.ts:1467`), `sys-metadata-repo.revert` (`:1619`), and `os migrate security-catalog-overlays` (`packages/cli/src/utils/security-catalog-overlays.ts:261`). The column is `Field.text` with `maxLength: 64` (`packages/metadata-core/src/objects/sys-metadata-history.object.ts:140`–`:145`), so a stored row can carry anything up to that length. **"the server stamps, not ... an author or client picks".** The save's own comment at `protocol.ts:22647`–`:22654`: the REST layer never forwards a client-supplied `source`. The spec already leaves `source` off `SaveMetaItemRequestSchema` for that reason (`protocol.zod.ts:774`–`:781`). **The labels the old text listed.** A `git grep` over non-test `packages/**` finds no writer stamping `'studio'`, `'rest'`, `'ai'` or `'git-import'` as a `source`, and no consumer comparing `source` against any of them. **Why `fs` is not named, although the card's example list has it.** `'fs'` is real, but it is not this door's. The `FileSystemRepository` in `@objectstack/metadata-fs` stamps it on its own JSONL change log and broker events (`packages/metadata-fs/src/repository.ts:421`, `:483`, `:674`, `:1045`). The metadata plugin attaches that repository as "a supplementary event source", with "No write mirroring yet" (`packages/metadata/src/plugin.ts:852`–`:855`). No non-test code passes `source: 'fs'` to `saveMetaItem` or a `SysMetadataRepository`. Naming `fs` here would repeat the defect this card fixes: a label the door's producers never write. **Not read:** cloud's writers. The describe says "e.g." and "open", so it makes no claim about them. ## Verification Final head: `8deed12d3`, the merge of `main` at `117301705`. That merge brought one `packages/spec` change (`src/kernel/public-auth-features.ts`), so the build, `check:generated`, the tests and the typecheck were run again after it. Build and test ran through `scripts/pm/os-verify-lock.sh`, and every exit code was captured before any pipe. - `pnpm --filter @objectstack/spec build`: `VERDICT command-exit 0`. - `pnpm --filter @objectstack/spec check:generated`: before `gen:docs`, exit 1 with `1 of 14 artifact(s) stale: content/docs/references/**`. After it, exit 0 with `All 14 generated artifacts are up to date`. On the merged head it is still exit 0 with all 14 up to date. - `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2`: `Test Files 694 passed (694)`, `Tests 21041 passed | 1 todo (21042)`, `VERDICT command-exit 0`. - `pnpm --filter @objectstack/spec typecheck`: `VERDICT command-exit 0`, ending `check:test-typecheck: OK`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on `8deed12d3` derived 104 commands from 3 paths against merge base `117301705`. 103 exit 0. The `--ran` reconciliation reads `104 derived, 103 run, 1 NOT-MEASURED, 0 UNRUN`. - Five of them first exited 3 (`PREREQUISITE NOT MET`): `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples`, `check:docs-transcript-drift` and `check:lean-entry-closure`. Each exited 0 after `turbo run build` of `@objectstack/formula`, `@objectstack/lint`, `@objectstack/objectql`, `@objectstack/client` and `@objectstack/client-react` (`VERDICT command-exit 0`). - `check:docs`: `227 generated files in sync with packages/spec`. - `check:doc-authoring`: clean, no internal issue-id references in the describe. - `check-adr-0087-registration`: `this PR adds no declared-breaking changeset`. `check-empty-changeset`: green. - `check:nul-bytes`: OK. - **NOT MEASURED: `check:dual-build-cjs-loads`** (exit 3, `PREREQUISITE NOT MET`). It reads every workspace package's `dist`, and I did not run a whole-repo build locally. That is a declared narrowing. CI's `Lint & Repo Gates` runs this gate. - `git grep -n "Origin label of the write"`: 0 hits on this head, 2 on the base (`protocol.zod.ts:1885`, `protocol.mdx:2030`). No test, ledger row or snapshot pinned the old text. - No control byte in any changed file (`grep -naP` over the three files). - No ablation and no reverse verification: the diff changes no tested behaviour and no type. - Left to CI: `test:repo` (`Test Core`), `pnpm lint`, the type-check lanes, and the families outside the derived set. ## Acceptance notes - **The same wrong list, one package down.** `@objectstack/metadata-core`'s `MetadataEventSchema.source` (`packages/metadata-core/src/types.ts:148`) is `.describe('Origin label: "fs", "studio", "rest", "ai", "git-import", …')`. That package is published (`files`: `dist`). This schema is shared by both repositories, so there `'fs'` is right and `'studio'`, `'rest'`, `'ai'`, `'git-import'` are not. It is outside this card's file surface, so it is handed to the seat as an out-of-scope finding, not fixed here. - **Two code comments in the same family**, in `packages/metadata-core/src/objects/sys-metadata-history.object.ts`: `:35`–`:36` ("'sys-metadata-repo', 'fs', 'studio', …") and `:136`–`:137` ("'sys-metadata-repo', 'fs', 'studio', 'api', …"). No writer stamps `'fs'` into that table, or `'studio'` or `'api'`. They are comments, not runtime text. Carrier: none, unless the finding above becomes a card. - **ADR-0008 §2.4's sketch** (`docs/adr/0008-metadata-repository-and-change-log.md:226`) has the same example list in a comment on `source: string`. The ADR decides the type, `string`, and this PR keeps it. The example list is illustration, and the ADR is a Tier H surface. Carrier: none. - **The spec's history fixture** (`packages/spec/src/api/protocol.test.ts:2141`–`:2162`) calls itself "a verbatim-shaped capture of a real `historyMetaItem` return", but its update event carries `source: 'studio'`. A real save stamps `protocol.saveMetaItem`. The test is still correct, because `z.string()` accepts both, and the file is outside this card's file surface. Carrier: none. ## Deviations - `main` was merged once before this PR opened, as AGENTS.md section 10 asks. - The new text leaves out the card's example `fs`, for the reason given above. - Labels: none written. --- _Generated by [Claude Code](https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 51ad8e4 commit ba5bfe9

3 files changed

Lines changed: 20 additions & 3 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Clause-②: no
6+
7+
The `source` describe on `HistoryMetaItemResponseSchema`'s events, the body of
8+
`GET /api/v1/meta/:type/:name/history`, no longer lists `"fs"`, `"studio"`, `"rest"`, `"ai"` and
9+
`"git-import"` as its labels: none of the write paths in `@objectstack/metadata-protocol` whose history
10+
rows this door reads stamps any of them. It now says what the field is: an open code-path label the
11+
server stamps, not an enum an author or client picks from, for example `protocol.saveMetaItem`,
12+
`protocol.publishMetaItem` or `migrate-stored`, with `sys-metadata-repo` as the repository's own default
13+
and the answer for a row that stored none. The field stays `z.string()`, so no key, type, export or
14+
accepted value changes, and the door answers exactly what it answered before.

‎content/docs/references/api/protocol.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
20272027
| **actor** | `string \| null` | ✅ | Who wrote this. `null` = system-initiated (boot sync, migration, scheduled job) — never a sentinel string: consumers that resolve this against `sys_user` must be able to tell "nobody" from "a user id". |
20282028
| **message** | `string` | optional | Optional commit message recorded with the write. |
20292029
| **ts** | `string` | ✅ | When the write happened (ISO-8601 string). |
2030-
| **source** | `string` | ✅ | Origin label of the write: "fs", "studio", "rest", "ai", "git-import", … |
2030+
| **source** | `string` | ✅ | Which code path wrote the event: an open label the server stamps, not an enum an author or client picks from (e.g. `protocol.saveMetaItem`, `protocol.publishMetaItem`, `migrate-stored`). The history row's stored value is passed through; `sys-metadata-repo` is the repository's own default and the answer for a row that stored none. |
20312031

20322032

20332033
---

‎packages/spec/src/api/protocol.zod.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,8 +1882,11 @@ export const HistoryMetaItemResponseSchema = lazySchema(() => z.object({
18821882
message: z.string().optional().describe('Optional commit message recorded with the write.'),
18831883
ts: z.string().describe('When the write happened (ISO-8601 string).'),
18841884
source: z.string().describe(
1885-
'Origin label of the write: "fs", "studio", "rest", "ai", '
1886-
+ '"git-import", …',
1885+
'Which code path wrote the event: an open label the server stamps, not '
1886+
+ 'an enum an author or client picks from (e.g. `protocol.saveMetaItem`, '
1887+
+ '`protocol.publishMetaItem`, `migrate-stored`). The history row\'s '
1888+
+ 'stored value is passed through; `sys-metadata-repo` is the '
1889+
+ 'repository\'s own default and the answer for a row that stored none.',
18871890
),
18881891
})).describe(
18891892
'The durable change-log for the item, oldest first. See the '

0 commit comments

Comments
 (0)