Rewritten after checking the cloud repo — the original framing was wrong. It read the closed agent flags as an oversight and proposed opening them. They are deliberate, and the real finding is narrower. The original text is preserved at the bottom.
What is actually true
agent is the only metadata type with both allowOrgOverride: false and allowRuntimeCreate: false, and packages/spec/src/kernel/metadata-plugin.zod.ts says why, in as many words:
ADR-0063 §2 — `*.agent.ts` is CLOSED to third parties. The kernel ships
exactly two platform-owned agents (`ask`/`build`); tenants extend the
platform by authoring skills + tools, never agents. Hence
allowRuntimeCreate:false (no runtime "create agent") and
allowOrgOverride:false (no per-org agent fork).
Confirmed against the population: the two agents are ask and build, both shipped from @objectstack/service-ai-studio in the cloud repo (agents/ask-agent.ts, agents/metadata-assistant-agent.ts — BUILD_AGENT_NAME = 'build').
So migrateStoredMetadata skipping agent is correct and permanent, not a gap awaiting a write path. And the saveMetaItem legacy raw-engine branch — the one that records no history and forces state: 'active' — has, for this type, no callers at all: nothing authors an agent through it, by design. The silent-draft-publish hazard I originally described is latent, not live.
The real finding
The two agents do not reach sys_metadata through saveMetaItem. AIStudioPlugin.registerMeta calls metadataService.register(...):
const stored = await withTimeout(metadataService.get(type, name));
if (stored != null && JSON.stringify(stored) === JSON.stringify(item)) return; // up to date
await withTimeout(metadataService.register(type, name, item, { notify: false }));
ctx.logger.info(stored
? `[AI Studio] built-in ${type} ${name} refreshed (shipped definition changed)`
: `[AI Studio] ${type} ${name} registered`);
MetadataManager.register persists by calling loader.save(...) on any writable datasource: loader, and DatabaseLoader.save writes the sys_metadata row directly — computing a fresh checksum, skipping the write when the checksum is unchanged, and appending no sys_metadata_history row.
That log line is the finding: refreshed (shipped definition changed) is a real mutation of a live behavioural surface — an agent's instructions, tools and knowledge — recorded nowhere durable. Consequences:
- no answer to "what did the
build agent's instructions say in 17.0.0-rc.1"; the previous body is simply gone;
- no rollback for a shipped agent definition that regresses behaviour;
- nothing for a Studio history tab or an audit timeline to render;
- and the platform's own migration reports these rows as
skipped, so no operator-facing surface shows their state either.
Agent definitions are arguably the type where "what changed, when, and can we go back" matters most, and they are the one type that cannot answer.
Scope note on the general shape
register() bypassing the history path is not agent-specific — it is how every plugin-shipped metadata record is written. For most types that is harmless because the same record is also authorable through saveMetaItem, which does record history. agent is the case where the bypass is the only path, so it is where the missing change log is total rather than partial.
Sketch
Deliberately not "open the type" — ADR-0063 closed it on purpose and this issue should not reopen that.
- Decide whether platform-shipped metadata deserves history at all. It may be defensible that a code-owned record needs no change log because the code is the record (the definition is in git, in
cloud). If that is the answer, write it down next to the ADR-0063 comment and close this — the honest resolution.
- If not: have
MetadataManager.register (or the DatabaseLoader.save path) append a history row with a source marking it a platform refresh, so the durable record exists without routing platform writes through saveMetaItem's authoring gate.
- Independently, make the latent branch honest.
saveMetaItem's raw-engine branch silently upgrades mode: 'draft' to a publish. Nothing hits it for agent today, but a future type declaring the same flag pair would inherit the behaviour silently. A refusal naming the type beats a silent upgrade.
Downgraded P2 → P3: no live victim, no regression, and option 1 may be a legitimate close.
Original text as filed (superseded)
The original body claimed the flags were possibly set "by omission" and proposed flipping them as the preferred fix, and framed the silent draft-publish as an active hazard. Both are wrong: ADR-0063 §2 closes the type deliberately, and no caller reaches that branch for agent. The observation that survives is the missing history, via a different write path than the one originally named.
Refs #4327 (PR #4464), #4454 (PR #4492), #4498 (PR #4504), ADR-0063 §2, ADR-0033, ADR-0087.
Rewritten after checking the
cloudrepo — the original framing was wrong. It read the closedagentflags as an oversight and proposed opening them. They are deliberate, and the real finding is narrower. The original text is preserved at the bottom.What is actually true
agentis the only metadata type with bothallowOrgOverride: falseandallowRuntimeCreate: false, andpackages/spec/src/kernel/metadata-plugin.zod.tssays why, in as many words:Confirmed against the population: the two agents are
askandbuild, both shipped from@objectstack/service-ai-studioin thecloudrepo (agents/ask-agent.ts,agents/metadata-assistant-agent.ts—BUILD_AGENT_NAME = 'build').So
migrateStoredMetadataskippingagentis correct and permanent, not a gap awaiting a write path. And thesaveMetaItemlegacy raw-engine branch — the one that records no history and forcesstate: 'active'— has, for this type, no callers at all: nothing authors an agent through it, by design. The silent-draft-publish hazard I originally described is latent, not live.The real finding
The two agents do not reach
sys_metadatathroughsaveMetaItem.AIStudioPlugin.registerMetacallsmetadataService.register(...):MetadataManager.registerpersists by callingloader.save(...)on any writabledatasource:loader, andDatabaseLoader.savewrites thesys_metadatarow directly — computing a freshchecksum, skipping the write when the checksum is unchanged, and appending nosys_metadata_historyrow.That log line is the finding:
refreshed (shipped definition changed)is a real mutation of a live behavioural surface — an agent's instructions, tools and knowledge — recorded nowhere durable. Consequences:buildagent's instructions say in 17.0.0-rc.1"; the previous body is simply gone;skipped, so no operator-facing surface shows their state either.Agent definitions are arguably the type where "what changed, when, and can we go back" matters most, and they are the one type that cannot answer.
Scope note on the general shape
register()bypassing the history path is not agent-specific — it is how every plugin-shipped metadata record is written. For most types that is harmless because the same record is also authorable throughsaveMetaItem, which does record history.agentis the case where the bypass is the only path, so it is where the missing change log is total rather than partial.Sketch
Deliberately not "open the type" — ADR-0063 closed it on purpose and this issue should not reopen that.
cloud). If that is the answer, write it down next to the ADR-0063 comment and close this — the honest resolution.MetadataManager.register(or theDatabaseLoader.savepath) append a history row with asourcemarking it a platform refresh, so the durable record exists without routing platform writes throughsaveMetaItem's authoring gate.saveMetaItem's raw-engine branch silently upgradesmode: 'draft'to a publish. Nothing hits it foragenttoday, but a future type declaring the same flag pair would inherit the behaviour silently. A refusal naming the type beats a silent upgrade.Downgraded P2 → P3: no live victim, no regression, and option 1 may be a legitimate close.
Original text as filed (superseded)
The original body claimed the flags were possibly set "by omission" and proposed flipping them as the preferred fix, and framed the silent draft-publish as an active hazard. Both are wrong: ADR-0063 §2 closes the type deliberately, and no caller reaches that branch for
agent. The observation that survives is the missing history, via a different write path than the one originally named.Refs #4327 (PR #4464), #4454 (PR #4492), #4498 (PR #4504), ADR-0063 §2, ADR-0033, ADR-0087.