diff --git a/docs/adr/0002-environment-database-isolation.md b/docs/adr/0002-environment-database-isolation.md
index 237278c747..83e246f78d 100644
--- a/docs/adr/0002-environment-database-isolation.md
+++ b/docs/adr/0002-environment-database-isolation.md
@@ -123,6 +123,10 @@ The migration is **non-destructive** and **idempotent**: each legacy org's datab
## References
+> **Path note (2026-09):** the three `packages/services/service-tenant/…` paths below are historical
+> and deliberately unlinked — the `packages/services/service-tenant/` package left this repository with the control-plane split to `objectstack-ai/cloud` (`473fce590`, 2026-05-21).
+> They are kept as written because they record where this decision landed at the time.
+
- `packages/spec/src/cloud/environment.zod.ts` — protocol schemas
- `packages/services/service-tenant/src/objects/sys-environment.object.ts` — merged control-plane environment object (includes DB addressing)
- `packages/services/service-tenant/src/environment-provisioning.ts` — provisioning service
diff --git a/docs/adr/0003-package-as-first-class-citizen.md b/docs/adr/0003-package-as-first-class-citizen.md
index 1b0c568dc1..18c2a9b9ab 100644
--- a/docs/adr/0003-package-as-first-class-citizen.md
+++ b/docs/adr/0003-package-as-first-class-citizen.md
@@ -191,6 +191,7 @@ The migration is non-destructive and idempotent. Steps 1–4 ship in v4.x as an
- `packages/spec/src/cloud/environment-package.zod.ts` — current installation schema (to be updated)
- `packages/services/service-tenant/src/objects/sys-package-installation.object.ts` — DB object (to be updated)
+ *(Path note, 2026-09: historical and deliberately unlinked — the `packages/services/service-tenant/` package left this repository with the control-plane split to `objectstack-ai/cloud` (`473fce590`, 2026-05-21).)*
- ADR-0002: `docs/adr/0002-environment-database-isolation.md` — Control Plane / Data Plane split
- Salesforce Unlocked Packages:
- Power Platform Solution Layers:
diff --git a/docs/adr/0004-cloud-multi-kernel.md b/docs/adr/0004-cloud-multi-kernel.md
index 42c3e99ea9..7a334271ac 100644
--- a/docs/adr/0004-cloud-multi-kernel.md
+++ b/docs/adr/0004-cloud-multi-kernel.md
@@ -134,4 +134,8 @@ Custom domains and multi-hostname binding (ACME certificates, `sys_domain` table
- Plan: `apps-server-sleepy-newell` (the plan this ADR crystallizes).
- Code: `packages/runtime/src/kernel-manager.ts`, `packages/runtime/src/project-kernel-factory.ts`, `packages/runtime/src/http-dispatcher.ts`, `apps/objectos/server/bootstrap.ts`, `apps/cloud/objectstack.config.ts`.
+ *(Path note, 2026-09: four of these five are historical and deliberately unlinked — the two runtime
+ paths for the reason the Path note in §2 gives (`packages/runtime/src/cloud/` was removed by #1600,
+ `60fc50186`), and `apps/objectos/` and `apps/cloud/` because both split out to `objectstack-ai/cloud`
+ on 2026-05-21 (`617016428` / `2a1fc869a`). `http-dispatcher.ts` is the only one still in this repository.)*
- Related guide: [Cloud vs Self-Hosted deployment](../../content/docs/deployment/index.mdx).
diff --git a/docs/adr/0005-metadata-customization-overlay.md b/docs/adr/0005-metadata-customization-overlay.md
index b94429bd5f..b4431d934d 100644
--- a/docs/adr/0005-metadata-customization-overlay.md
+++ b/docs/adr/0005-metadata-customization-overlay.md
@@ -25,7 +25,7 @@ Project concept entirely. As a consequence:
`branch`. The column physically named `project_id` on `sys_metadata`
is treated as a legacy alias and will be renamed/dropped in the
ADR-0008 PR-10 migration.
-- All references to `this.projectId` in `packages/objectql/src/protocol.ts`
+- All references to `this.projectId` in `packages/metadata-protocol/src/protocol.ts`
are deprecated. New code must consult `organization_id` (and, in M1,
the branch ref) only.
- The `(type, name, project_id)` UNIQUE index is superseded by
@@ -143,7 +143,7 @@ Three different things were tangled into one place:
### Storage shape
-`sys_metadata` schema (`packages/platform-objects/src/metadata/sys-metadata.object.ts`) is the storage substrate. No schema changes needed in Phase 1:
+`sys_metadata` schema (`packages/metadata-core/src/objects/sys-metadata.object.ts`) is the storage substrate. No schema changes needed in Phase 1:
| column | role |
|---|---|
@@ -194,7 +194,9 @@ Five files in `packages/platform-objects/src/metadata/` duplicate a Zod schema t
`sys-metadata.object.ts` and `sys-metadata-history.object.ts` are retained — they are the storage substrate, not duplicates of any metadata type.
-`MetadataProjector` (`packages/metadata/src/projection/metadata-projector.ts`) becomes dead code under this ADR. It is left in place for one release as a no-op safety net, and removed in the next major along with the deprecated objects.
+`MetadataProjector` (`packages/metadata/src/projection/metadata-projector.ts` — *Path note, 2026-09:
+the module was deleted in `516f48ca9`, the same change that implemented this ADR; the name survives
+only in `packages/metadata/src/migrations/drop-projection-tables.ts`*) becomes dead code under this ADR. It is left in place for one release as a no-op safety net, and removed in the next major along with the deprecated objects.
## Consequences
@@ -231,7 +233,7 @@ Five files in `packages/platform-objects/src/metadata/` duplicate a Zod schema t
## References
-- `packages/objectql/src/protocol.ts` — `getMetaItem`, `saveMetaItem`, `deleteMetaItem`, `loadMetaFromDb` (this ADR's primary site)
+- `packages/metadata-protocol/src/protocol.ts` — `getMetaItem`, `saveMetaItem`, `deleteMetaItem`, `loadMetaFromDb` (this ADR's primary site)
- `packages/rest/src/rest-server.ts` — `PUT/GET/DELETE /api/v1/meta/:type/:name` routes
- `packages/spec/src/api/protocol.zod.ts` — `ObjectStackProtocol` interface (`deleteMetaItem` added)
- `packages/spec/src/kernel/metadata-plugin.zod.ts` — `MetadataTypeRegistryEntrySchema.supportsOverlay` (future hook for the whitelist)
@@ -247,7 +249,7 @@ Two implementation issues were discovered during browser E2E verification with t
### 1. List endpoint did not include overlay rows in project kernels
-`getMetaItems(type)` in `packages/objectql/src/protocol.ts` was gated by
+`getMetaItems(type)` in `packages/metadata-protocol/src/protocol.ts` was gated by
`if (this.projectId === undefined)` before consulting `sys_metadata`. Project
kernels — which are precisely where overlays live — therefore returned only
artifact entries. `GET /api/v1/meta/view` listed 16 artifact items and zero
@@ -315,7 +317,7 @@ change, any JSON shape was accepted and stored verbatim, surfacing as
runtime errors only at read time when the merged effective metadata was
fed into the UI engine.
-Implementation (`packages/objectql/src/protocol.ts`):
+Implementation (`packages/metadata-protocol/src/protocol.ts`):
- `resolveOverlaySchema(type, item)` dispatches by metadata type:
- `view` → `ListViewSchema` or `FormViewSchema` (picked by the `type`
@@ -359,7 +361,7 @@ types fall through, plural type strings normalize correctly.
### Registry-driven opt-in (was: hard-coded whitelist)
-`packages/objectql/src/protocol.ts` previously gated `PUT/DELETE
+`packages/metadata-protocol/src/protocol.ts` previously gated `PUT/DELETE
/api/v1/meta/:type/:name` against a **hard-coded** `Set` of allowed types
(`OVERLAY_ALLOWED_TYPES = new Set(['view', 'dashboard'])`). Any new metadata
type that wanted to participate in the overlay system had to find and edit
@@ -453,7 +455,7 @@ idempotent migration is provided and run automatically by
> place: the `allowOrgOverride` boolean on its
> `DEFAULT_METADATA_TYPE_REGISTRY` entry.
> 3. The **overlay validator** lives in exactly one place:
-> `resolveOverlaySchema()` in `packages/objectql/src/protocol.ts`.
+> `resolveOverlaySchema()` in `packages/metadata-protocol/src/protocol.ts`.
>
> Do **not** re-declare the same shape as a `*.object.ts` (the
> projection-table pattern is removed; see Addendum 2026-05-16 (b)).
@@ -652,7 +654,7 @@ coverage:
### Admin UX: field-level Code-vs-Effective diff
-The Layers tab in the metadata editor (`packages/app-shell/.../LayeredDiff.tsx`
+The Layers tab in the metadata editor (`objectui:packages/app-shell/src/views/metadata-admin/LayeredDiff.tsx`
in `objectui`) now defaults to a **Diff** view that compares
`layered.code` (artifact baseline) against `layered.effective` (merged)
field-by-field. Each top-level key renders as a row with a colour-coded
@@ -680,7 +682,7 @@ the present), so this section is where the present tense lives.
| Addendum (d) says | Today | Superseded by |
|:---|:---|:---|
-| "`addSysMetadataOverlayIndex(driver)` — exported from `@objectstack/metadata/migrations`" | **Deleted.** The export and its module are gone; `packages/metadata/src/migrations/index.ts` carries a tombstone in their place that records the measurement and forbids re-introducing a producer for `idx_sys_metadata_overlay_active` in that package. | #6771 (PR #6824, merged 2026-08-08); `.changeset/overlay-index-single-producer.md` |
+| "`addSysMetadataOverlayIndex(driver)` — exported from `@objectstack/metadata/migrations`" | **Deleted.** The export and its module are gone; `packages/metadata/src/migrations/index.ts` carries a tombstone in their place that records the measurement and forbids re-introducing a producer for `idx_sys_metadata_overlay_active` in that package. | #6771 (PR #6824, merged 2026-08-08); `.changeset/overlay-index-single-producer.md` (since consumed by the release, `24c1b91e4`) |
| "a new idempotent migration is provided and run automatically by `DatabaseLoader.ensureSchema()`" | **No overlay-index DDL is issued from that method at all**, on either of its two paths — both call sites went with the export. What `ensureSchema()` still runs is the `project_id` → `environment_id` forward migration, which is a different concern. | #6771 (PR #6824) |
| "Drivers ignore `indexes` declarations on synced tables today" | **False** — and this one is *not* a consequence of #6771. `SqlDriver.syncDeclaredIndexes` materializes every declared index, through knex's `table.unique(fields, { indexName })` / `table.index(fields, name)`, skipping by name for idempotence. | The driver itself. The spec records the same fact where the `IDataDriver` capability bit `indexes` was retired for having no reader: "Declared indexes are materialised by the driver itself during schema sync (`SqlDriver.syncDeclaredIndexes`)". |
@@ -745,7 +747,7 @@ form from being re-imposed over it.
### Anchors
-Both files above are registered in `scripts/adr-anchors.json` against ADR-0005, so
+Both files above are registered in `scripts/adr-anchors/` against ADR-0005, so
the next author to edit either one is told which decision they are standing on.
That is the recurrence guard Prime Directive #13 names and the one thing this
amendment adds beyond prose: the producer that was deleted had no anchor, and
diff --git a/docs/adr/0006-project-environment-split.md b/docs/adr/0006-project-environment-split.md
index 2fb00459b1..a695f415d5 100644
--- a/docs/adr/0006-project-environment-split.md
+++ b/docs/adr/0006-project-environment-split.md
@@ -177,6 +177,8 @@ The toggle is sticky per user; once on, it stays on across sessions.
later (different semantics) when Builder UX ships. SDK consumers
(none in production yet) must rename calls.
3. **`apps/cloud/server/index.ts` and `worker.ts` change** their hostname
+ *(Path note, 2026-09: historical and deliberately unlinked — `apps/cloud/` split out to
+ `objectstack-ai/cloud` on 2026-05-21, `2a1fc869a`.)*
resolution from `sys_project` to `sys_environment`.
### Neutral
diff --git a/docs/adr/0006-project-environment-split.v2.md b/docs/adr/0006-project-environment-split.v2.md
index ca09495d9a..46b20c1a03 100644
--- a/docs/adr/0006-project-environment-split.v2.md
+++ b/docs/adr/0006-project-environment-split.v2.md
@@ -219,5 +219,7 @@ Each phase is independently shippable behind a feature flag
§12.3 — original three-layer-model recommendation
- `packages/services/service-tenant/src/objects/sys-project.object.ts`
— current single-table model; relabelled in Phase 0
+ *(Path note, 2026-09: historical and deliberately unlinked — the object was renamed to
+ `sys-environment.object.ts` by `0ee839c53`, and then the `packages/services/service-tenant/` package left this repository with the control-plane split to `objectstack-ai/cloud` (`473fce590`, 2026-05-21).)*
diff --git a/docs/adr/0006-project-environment-split.v4.md b/docs/adr/0006-project-environment-split.v4.md
index c9ccab7e21..4ea2a89bd0 100644
--- a/docs/adr/0006-project-environment-split.v4.md
+++ b/docs/adr/0006-project-environment-split.v4.md
@@ -189,7 +189,9 @@ installed where" goes through Packages → version history → installations.
1. `sys_package_version.published_from_project_id` (introduced in v3) is
removed as a field — provenance lives in `sys_package_version.metadata`
if needed.
-2. `ProjectArtifactSchema` (`packages/spec/src/cloud/project-artifact.zod.ts`)
+2. `ProjectArtifactSchema` (`packages/spec/src/cloud/project-artifact.zod.ts` — *Path note, 2026-09:
+ the follow-up rename this paragraph anticipates happened; the file is
+ `packages/spec/src/cloud/environment-artifact.zod.ts` today, renamed by `944f18758`, 2026-05-24*)
is the envelope returned by `GET /cloud/projects/:id/artifact`. The
route name is kept for BC; the response shape continues to wrap the
compiled `ObjectStackDefinitionSchema`. The "Project" in the schema
@@ -202,7 +204,7 @@ installed where" goes through Packages → version history → installations.
| Phase | Scope | Status |
|:---|:---|:---|
-| **A — Drop Project from the protocol** | Remove `packages/spec/src/cloud/project.zod.ts`; update `index.ts`; trim Project tests from `environment.test.ts`; mark `sys_environment_revision` as `@deprecated transitional` | ✅ This commit |
+| **A — Drop Project from the protocol** | Remove `packages/spec/src/cloud/project.zod.ts` (since removed, as this phase prescribes — `d4eed33ab`); update `index.ts`; trim Project tests from `environment.test.ts`; mark `sys_environment_revision` as `@deprecated transitional` | ✅ This commit |
| **B — Rewire CLI publish onto Package** | `objectstack publish` resolves implicit `sys_package`; calls `POST /cloud/packages/:id/versions`; upserts `sys_package_installation`. Old `/cloud/projects/:envId/metadata` becomes a thin BC shim that internally walks the new path. | Next |
| **C — Split CLI commands** | `objectstack push` (version only) + `objectstack deploy` (installation upsert) + `objectstack promote` + `objectstack rollback`. `publish` stays as a `push && deploy` alias. | Next+1 |
| **D — Remove transitional revision table** | After Phase B is shipping and verified: drop `sys_environment_revision` schema, delete `_DEPRECATED` route handlers, wipe table from any seeded control planes. | After C |
diff --git a/docs/adr/0007-settings-manifest-and-kv-store.md b/docs/adr/0007-settings-manifest-and-kv-store.md
index b2d9c0d96a..0089ff3842 100644
--- a/docs/adr/0007-settings-manifest-and-kv-store.md
+++ b/docs/adr/0007-settings-manifest-and-kv-store.md
@@ -446,6 +446,7 @@ Review existing `sys_oauth_application`, `sys_webhook`, `sys_two_factor` — kee
- `packages/platform-objects/src/identity/sys-user-preference.object.ts` — original config-resolution comment
- `packages/services/service-tenant/src/objects/sys-environment-credential.object.ts` — encrypted-creds reference impl
+ *(Path note, 2026-09: historical and deliberately unlinked — the `packages/services/service-tenant/` package left this repository with the control-plane split to `objectstack-ai/cloud` (`473fce590`, 2026-05-21).)*
- `packages/spec/src/data/field.zod.ts` — existing field type catalogue
- `packages/platform-objects/src/apps/setup.app.ts` — Setup app shell
- Apple "Implementing an iOS Settings Bundle" — https://developer.apple.com/documentation/foundation/userdefaults/implementing_an_ios_settings_bundle
diff --git a/docs/adr/0008-metadata-repository-and-change-log.md b/docs/adr/0008-metadata-repository-and-change-log.md
index c5f4584abf..879c5e5a24 100644
--- a/docs/adr/0008-metadata-repository-and-change-log.md
+++ b/docs/adr/0008-metadata-repository-and-change-log.md
@@ -482,7 +482,7 @@ The plan is intentionally staged so each milestone is **independently shippable*
- [ ] All existing tests pass; new contract suite passes for both `InMemoryRepository` and `FileSystemRepository`
- [ ] Cold start of the [HotCRM reference app](https://github.com/objectstack-ai/hotcrm) is ≤ current baseline (no eager full-load)
- [ ] `MetadataEvent` stream visible in `apps/studio/Logs` panel
-- [ ] No code in `packages/objectql/src/protocol.ts` reads files or imports from `@objectstack/metadata-fs`
+- [ ] No code in `packages/metadata-protocol/src/protocol.ts` reads files or imports from `@objectstack/metadata-fs`
**Out of scope for M0**
diff --git a/docs/adr/0010-nl-to-flow-authoring.md b/docs/adr/0010-nl-to-flow-authoring.md
index 6b6d35ff5f..092c3e8b73 100644
--- a/docs/adr/0010-nl-to-flow-authoring.md
+++ b/docs/adr/0010-nl-to-flow-authoring.md
@@ -139,6 +139,8 @@ HotCRM has a placeholder `live_data` skill that grounds Copilot answers in the l
1. Add `'flow_author'` to `sales_copilot.skills[]`.
2. Drop the explicit cut we made when removing the in-CRM `flow_designer` draft (commit retained in branch `feat/wow-2-flow-designer-draft`).
3. Update `content/docs/ai-copilot/live-schema.mdx` with a Wow #2 sibling page.
+ *(Path note, 2026-09: this is a HotCRM path, not a path in this repository — no file has ever
+ existed at `content/docs/ai-copilot/` here. It is kept as the record of the planned edit.)*
No CRM data model changes.
@@ -154,3 +156,5 @@ Pending platform review. HotCRM v1 launch (W9) will ship without Wow #2 unless t
* ADR-0003 — package-as-first-class-citizen (target for `register_flow`).
* ADR-0008 — metadata repository + change log (provides idempotency + audit).
* HotCRM `content/docs/ai-copilot/live-schema.mdx` — Wow #1 marketing copy that sets up Wow #2.
+ *(Path note, 2026-09: a HotCRM path, not a path in this repository — no file has ever existed at
+ `content/docs/ai-copilot/` here.)*
diff --git a/docs/adr/0011-actions-as-ai-tools.md b/docs/adr/0011-actions-as-ai-tools.md
index d29631108a..a7c6e781c8 100644
--- a/docs/adr/0011-actions-as-ai-tools.md
+++ b/docs/adr/0011-actions-as-ai-tools.md
@@ -314,3 +314,5 @@ ships next.
* `packages/spec/src/ui/action.zod.ts` — `ActionSchema` + `ActionAiSchema`
* `packages/spec/src/contracts/ai-service.ts` — `AIToolDefinition`
* `packages/services/service-ai/src/tools/action-tools.ts` — the bridge
+ *(Path note, 2026-09: historical and deliberately unlinked — `service-ai` was removed whole under
+ ADR-0025 S2, `21d4f8901`, 2026-06-25; the open edition is MCP-only.)*
diff --git a/docs/adr/0013-bidirectional-messaging.md b/docs/adr/0013-bidirectional-messaging.md
index 1f43a34ccf..6652087691 100644
--- a/docs/adr/0013-bidirectional-messaging.md
+++ b/docs/adr/0013-bidirectional-messaging.md
@@ -550,6 +550,11 @@ No new ADR-0011 surface is needed. The Action Registry, agent runtime, and tool-
The following are clean enough to copy verbatim; each goes into `packages/messaging-utils/` with a NOTICE entry:
+> **Path note (2026-09):** the three `packages/messaging-utils/src/…` paths in the *Our path* column
+> are **proposed** destinations, not citations — no `packages/messaging-utils/` package has ever
+> existed in this repository's history. They record where this ADR intended the borrowed artifacts
+> to land.
+
| openclaw source | Our path | Why borrowed |
|:---|:---|:---|
| `plugin-sdk/security-runtime.ts::safeEqualSecret` | `packages/messaging-utils/src/safe-equal.ts` | Constant-time secret compare; well-tested, no objectstack-specific shape needed |
diff --git a/docs/adr/0014-record-form-field-type.md b/docs/adr/0014-record-form-field-type.md
index 150870f50c..48a05bd537 100644
--- a/docs/adr/0014-record-form-field-type.md
+++ b/docs/adr/0014-record-form-field-type.md
@@ -16,7 +16,7 @@ internal `name` mirrors the key.
The Studio metadata editor, however, treated `fields` as an **array** all
the way down:
-1. Hand-crafted JSON Schema in `packages/objectql/src/protocol.ts`
+1. Hand-crafted JSON Schema in `packages/metadata-protocol/src/protocol.ts`
declared `{ type: 'array', items: { … } }`.
2. The form spec in `packages/spec/src/data/object.form.ts` declared
`{ type: 'repeater', widget: 'grid' }`.
diff --git a/docs/adr/0015-external-datasource-federation.md b/docs/adr/0015-external-datasource-federation.md
index 9dc622686d..ca6cbe1a95 100644
--- a/docs/adr/0015-external-datasource-federation.md
+++ b/docs/adr/0015-external-datasource-federation.md
@@ -5,6 +5,16 @@
**Builds on**: [ADR-0005](./0005-metadata-customization-overlay.md) (one Zod source of truth per metadata type)
**Consumers**: `@objectstack/spec`, `@objectstack/runtime`, `@objectstack/objectql`, `@objectstack/plugins/driver-sql`, `@objectstack/services/service-ai`, `@objectstack/cli`, `../objectui`
+> **Path note (2026-09), paths only — no decision changes.** Five paths this record cites are
+> historical and deliberately unlinked: `packages/spec/src/data/external-lookup.zod.ts` (retired
+> under ADR-0049 enforce-or-remove, `29488ccae`, 2026-08-12), `packages/spec/src/automation/sync.zod.ts`
+> (the L1 "Simple Sync" layer was retired whole, `891d345a7`, 2026-08-03), and
+> `packages/services/service-ai/src/tools/query-data.tool.ts` +
+> `packages/services/service-ai/src/schema-retriever.ts` (`service-ai` removed under ADR-0025 S2,
+> `21d4f8901`, 2026-06-25). `packages/spec/src/shared/error-map.ts` is a **proposed** path — no file
+> has ever existed there. Each is kept as written because it records this decision's surface at the
+> time it was taken.
+
---
## 0. Context
diff --git a/docs/adr/0016-studio-package-authoring-and-publish.md b/docs/adr/0016-studio-package-authoring-and-publish.md
index c28e0530c2..c8de176450 100644
--- a/docs/adr/0016-studio-package-authoring-and-publish.md
+++ b/docs/adr/0016-studio-package-authoring-and-publish.md
@@ -454,8 +454,9 @@ enabled), so disable state would otherwise be lost. Persistence is local-first:
- `../objectui/.../layout/UnifiedSidebar.tsx` — `active_package` selector (authoring target)
- `packages/runtime/src/http-dispatcher.ts` — `assemblePackageManifest` + `GET /packages/:id/export` (§9.2)
- `packages/runtime/src/cloud/marketplace-install-local-plugin.ts` — inline-manifest import + register-before-persist (§9.2–9.3)
+ *(Path note, 2026-09: historical and deliberately unlinked — `packages/runtime/src/cloud/` was removed by ADR-0006 Phase 4 (`15fcd360d`, 2026-06-11), which took the cloud plugins out of the framework runtime.)*
- `packages/objectql/src/registry.ts` — `isPackageDisabled` + `listItems` disabled-package filter (§9.5); `setInitialDisabledPackageIds` + `installPackage` disable seeding (§9.7)
-- `packages/objectql/src/protocol.ts` — `getMetaItems` final-merge disabled-package filter (§9.5)
+- `packages/metadata-protocol/src/protocol.ts` — `getMetaItems` final-merge disabled-package filter (§9.5)
- `packages/objectql/src/engine.ts` — `registerApp` consumes `tools` / `skills` (§9.6)
- `packages/spec/src/shared/metadata-collection.zod.ts` — `PLURAL_TO_SINGULAR` gains `tools` / `skills` (§9.6)
- `packages/spec/src/stack.zod.ts` — top-level `tools` on `ObjectStackDefinition` (§9.6)
diff --git a/docs/adr/0017-object-has-many-view.md b/docs/adr/0017-object-has-many-view.md
index 9846cac62a..326f21ef5e 100644
--- a/docs/adr/0017-object-has-many-view.md
+++ b/docs/adr/0017-object-has-many-view.md
@@ -116,7 +116,7 @@ lists `view`, keeps `v.viewKind && v.object === object`, sorts by
rows on top.
### 3.4 Runtime storage — `sys_view_definition`
-`packages/platform-objects/src/metadata/sys-view-definition.object.ts` —
+`packages/metadata-core/src/objects/sys-view-definition.object.ts` —
a `sys_`-prefixed **system object** (auto-provisioned). Columns:
`name`, `object`, `view_kind`, `label`, `is_default`, `view_order`,
`scope` (`shared`|`personal`), `owner`, `hidden`, `config` (JSON),
@@ -137,7 +137,7 @@ bound to that object, sorted, handling both array and `{items:[]}`
response shapes.
### 3.6 Runtime switcher — `../objectui`
-`packages/data-objectstack/src/index.ts` — the adapter's runtime-view
+`objectui:packages/data-objectstack/src/index.ts` — the adapter's runtime-view
CRUD (`listViews` / `createView` / `updateView` / `deleteView`) is
repointed from the legacy metadata `view` overlay onto
`sys_view_definition` via the generic data API:
@@ -150,7 +150,7 @@ repointed from the legacy metadata `view` overlay onto
the tab-bar consumes and the typed columns (config nested; identity
hoisted).
-`packages/app-shell/src/views/ObjectView.tsx` — the console switcher
+`objectui:packages/app-shell/src/views/ObjectView.tsx` — the console switcher
aggregates two sources it already merged: **package** views via
`objectDef.listViews` (the dual-read aggregated container, surfaced by
`MetadataProvider`) and **shared/personal** via `dataSource.listViews`.
diff --git a/docs/adr/0025-plugin-package-distribution.md b/docs/adr/0025-plugin-package-distribution.md
index 52e1e36240..e4be85f1c4 100644
--- a/docs/adr/0025-plugin-package-distribution.md
+++ b/docs/adr/0025-plugin-package-distribution.md
@@ -46,6 +46,10 @@ to build, no dependencies to resolve, and no trust boundary to cross.
`packages/runtime/src/cloud/marketplace-install-local-plugin.ts` and
ADR-0016 §9.
+> **Path note (2026-09):** every `packages/runtime/src/cloud/…` path in this record is historical and
+> deliberately unlinked — `packages/runtime/src/cloud/` was removed by ADR-0006 Phase 4 (`15fcd360d`, 2026-06-11), which took the cloud plugins out of the framework runtime.
+> They are kept as written because they record where this decision landed at the time.
+
The next step is **plugins**: distributable units that contain **executable
code and npm dependencies**, not just metadata. The repository already has the
*authoring* half of this story:
@@ -509,6 +513,9 @@ the developers and operators who compose Apps and provision runtimes.
- `packages/runtime/src/sandbox/quickjs-runner.ts` — QuickJS-WASM sandbox (T1)
- `packages/runtime/src/cloud/marketplace-install-local-plugin.ts` — local inline install (ADR-0016 §9)
- `packages/runtime/src/cloud/marketplace-proxy-plugin.ts` — marketplace browse proxy
+ *(Path note, 2026-09: both `marketplace-install-local-plugin.ts` and `marketplace-proxy-plugin.ts`
+ are historical and deliberately unlinked — `packages/runtime/src/cloud/` was removed by ADR-0006
+ Phase 4, `15fcd360d`, 2026-06-11.)*
- `packages/runtime/src/package-state-store.ts` — per-environment disable-state persistence
- `packages/cli/src/commands/package/publish.ts` — existing package publish pipeline
- `packages/drivers/driver-memory/objectstack.config.ts` — example plugin manifest (`ObjectStackManifest`)
diff --git a/docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md b/docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md
index 5516d832b6..9a503be95f 100644
--- a/docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md
+++ b/docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md
@@ -189,7 +189,7 @@ contributions follow ADR-0028's naming model (`sys.audit_log`, etc.).
A kernel object is more than its schema: it has **localized labels, field help,
and list-view names**. Today these live in `platform-objects` as generated
bundles (`src/apps/translations/*.objects.generated.ts`, produced by
-`os i18n extract` against `scripts/i18n-extract.config.ts`, loaded at runtime by
+`os i18n extract` against `packages/platform-objects/scripts/i18n-extract.config.ts`, loaded at runtime by
the `platform-objects` plugin). The generated entries are keyed by **object
name** (`sys_webhook: {...}`) and loaded globally, so they keep working at
runtime regardless of which package owns the object — but their **source of
@@ -202,7 +202,7 @@ Therefore object migration must carry the i18n resources, not just the schema:
bundle(s), and contributes them at runtime (e.g. via `i18n.loadTranslations`
or `manifest.translations`), exactly as `platform-objects` does today.
- When an object leaves `platform-objects`, it is removed from
- `scripts/i18n-extract.config.ts`; **regenerating before the plugin owns its
+ `packages/platform-objects/scripts/i18n-extract.config.ts`; **regenerating before the plugin owns its
extraction would silently drop locales** — so the plugin-side i18n extraction
must land in the same step (or the object stays in the extract set
transitionally, explicitly tracked).
@@ -783,7 +783,7 @@ does to it:
### 8. Anchors
`packages/objectql/src/registry.ts` is registered against ADR-0029 in
-`scripts/adr-anchors.json` by this change. It was **unanchored**, which is the
+`scripts/adr-anchors/` by this change. It was **unanchored**, which is the
recurrence shape Prime Directive #13 names and the same one #6825's amendment
found for ADR-0005: the file that implements D3 (the `own` splice at `packages/objectql/src/registry.ts`,
`assertSingleOwnerPerObject` at `#assertSingleOwnerPerObject`, the extender guard at) never said
diff --git a/docs/adr/0045-additive-materialization-and-visibility-gate.md b/docs/adr/0045-additive-materialization-and-visibility-gate.md
index a6f58dacff..956221d6cb 100644
--- a/docs/adr/0045-additive-materialization-and-visibility-gate.md
+++ b/docs/adr/0045-additive-materialization-and-visibility-gate.md
@@ -206,4 +206,4 @@ Stored `sys_metadata` app rows carrying `hidden: true` were written under the ol
### Anchors
-The two load-bearing implementation sites are pinned in `scripts/adr-anchors.json` — `packages/rest/src/rest-server.ts` (the gate) and `packages/runtime/src/domains/packages.ts` (the flip). Neither carried an anchor before, which is the recurrence shape Prime Directive #13 names: the files that implemented this ADR's §3 never said which decision they were standing on, so the next author could not have known that changing them was changing a decision.
+The two load-bearing implementation sites are pinned in `scripts/adr-anchors/` — `packages/rest/src/rest-server.ts` (the gate) and `packages/runtime/src/domains/packages.ts` (the flip). Neither carried an anchor before, which is the recurrence shape Prime Directive #13 names: the files that implemented this ADR's §3 never said which decision they were standing on, so the next author could not have known that changing them was changing a decision.
diff --git a/docs/adr/0057-erp-authorization-core-business-units-and-scope-depth.md b/docs/adr/0057-erp-authorization-core-business-units-and-scope-depth.md
index 1e022cc0e8..8929007ccf 100644
--- a/docs/adr/0057-erp-authorization-core-business-units-and-scope-depth.md
+++ b/docs/adr/0057-erp-authorization-core-business-units-and-scope-depth.md
@@ -2,6 +2,12 @@
**Status**: Accepted in part (2026-06-21) — D1–D3/D8 implemented (scope-depth enum + evaluator, `sys_business_unit` tree, hierarchy-resolver seam, conformance rows); **D4–D7 superseded by ADR-0090 D3 / ADR-0095**: the `sys_role`/`sys_user_role`/`role_and_subordinates` vocabulary never shipped — the code realizes them as flat `sys_position`/`sys_user_position` with rollup on the BU tree. This ADR's own implementation-status table predates that supersession; read it with the rename applied.
**Deciders**: ObjectStack Protocol Architects
+**Path note (2026-09), paths only — no decision changes**: this record cites the **pre-rename**
+vocabulary, and those four paths no longer resolve. `sys-department.object.ts` and `department-graph.ts`
+became `sys-business-unit.object.ts` / `business-unit-graph.ts` in this ADR's own landing (`e16f2a830`);
+`sys-role.object.ts` and `role-graph.ts` became `sys-position.object.ts` / `position-graph.ts` in the
+ADR-0090 P1 position rename (`6d83431cf`, 2026-07-09). The historical spellings are kept below because
+the argument is *about* the pre-rename shape.
**Builds on**: [ADR-0010](./0010-metadata-protection-model.md) (metadata protection / object ownership),
[ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove),
[ADR-0054](./0054-runtime-proof-for-authorable-surface.md) (runtime proof),
diff --git a/docs/adr/0058-expression-and-predicate-surface.md b/docs/adr/0058-expression-and-predicate-surface.md
index 60c54e1013..7b614b6973 100644
--- a/docs/adr/0058-expression-and-predicate-surface.md
+++ b/docs/adr/0058-expression-and-predicate-surface.md
@@ -290,7 +290,7 @@
> appendix will not presume the answer. The engine half and #5846 settle it
> together, in one edit to one ordering, and record it as an amendment.
> **→ Settled in Amendment II.1 below.**
-> - **`scripts/adr-anchors.json`'s `hook-wrappers.ts` invariant still describes
+> - **`scripts/adr-anchors/`'s `hook-wrappers.ts` invariant still describes
> the batch dispatch.** It is TRUE today and must move with the engine half,
> not before it. **→ Moved with #5574's engine half.**
diff --git a/docs/adr/0062-external-datasource-runtime.md b/docs/adr/0062-external-datasource-runtime.md
index 1a55705fe9..99533a32c7 100644
--- a/docs/adr/0062-external-datasource-runtime.md
+++ b/docs/adr/0062-external-datasource-runtime.md
@@ -35,7 +35,7 @@ This ADR defines the **complete external-datasource runtime contract** and the d
### The structural gap, precisely (R2)
-`os dev` (standalone) connects exactly **one** driver — the `default` library (`.objectstack/data/standalone.db`), built in `packages/runtime/src/standalone-stack.ts` by detecting the driver kind from the DB URL. A `defineStack({ datasources: [...] })` entry is only registered as metadata by `packages/runtime/src/app-plugin.ts` (`registerInMemory('datasource', …)`). ObjectQL routes queries by **driver name** (`engine.getDriver(object) → this.drivers.get(object.datasource)`) and throws `Datasource 'x' is not registered` when absent. So the only way to make a federated object queryable today is the app's `onEnable` hook calling `ctx.drivers.register(driver)` (= `engine.registerDriver`) — see `examples/app-showcase/src/datasources/external-fixture.ts`. That bridge is framework plumbing leaking into every federation app.
+`os dev` (standalone) connects exactly **one** driver — the `default` library (`.objectstack/data/standalone.db`), built in `packages/runtime/src/standalone-stack.ts` by detecting the driver kind from the DB URL. A `defineStack({ datasources: [...] })` entry is only registered as metadata by `packages/runtime/src/app-plugin.ts` (`registerInMemory('datasource', …)`). ObjectQL routes queries by **driver name** (`engine.getDriver(object) → this.drivers.get(object.datasource)`) and throws `Datasource 'x' is not registered` when absent. So the only way to make a federated object queryable today is the app's `onEnable` hook calling `ctx.drivers.register(driver)` (= `engine.registerDriver`) — see `examples/app-showcase/src/system/datasources/external-fixture.ts`. That bridge is framework plumbing leaking into every federation app.
### Why one consolidating ADR (not N small PRs)
diff --git a/docs/adr/0067-commit-history-and-rollback-for-ai-authoring.md b/docs/adr/0067-commit-history-and-rollback-for-ai-authoring.md
index 5afae61270..628ac4b6a9 100644
--- a/docs/adr/0067-commit-history-and-rollback-for-ai-authoring.md
+++ b/docs/adr/0067-commit-history-and-rollback-for-ai-authoring.md
@@ -31,7 +31,7 @@ A commit-history model usually means "build a versioning system." Here, most of
| Capability | Where | Note |
|---|---|---|
| Per-item version history, **full body per version** | `sys_metadata_history` (`packages/metadata-core/src/objects/sys-metadata-history.object.ts`) | append-only; `operation_type ∈ {create,update,publish,revert,delete}`; `event_seq` (per-org monotonic, orders *all* changes); `version` (per-item monotonic); `recorded_by` (incl. `ai:claude`) |
-| Read a prior version / **single-item revert** | `SysMetadataRepository.history()` / `restoreVersion(ref, targetVersion)` (`packages/objectql/src/sys-metadata-repository.ts`) | revert already lands as a forward `operation_type='revert'` event |
+| Read a prior version / **single-item revert** | `SysMetadataRepository.history()` / `restoreVersion(ref, targetVersion)` (`packages/metadata-protocol/src/sys-metadata-repository.ts`) | revert already lands as a forward `operation_type='revert'` event |
| Per-write ACID (metadata row + history row atomic) | `withTxn()` / `engine.transaction()` (same file + `engine.ts`) | exists **per item**; not across items |
| Full-bundle package snapshot + atomic rollback-by-install | `sys_package_version` + `snapshotBundleAsManifest()` + install pointer swap (cloud `service-tenant` / `service-cloud`) | downgrade already detected ("Rolled back v2→v1") |
| Soft vs hard delete | `keepData` / `dropStorage` flags (`engine.ts` teardown) | **default keeps data**; hard drop is opt-in — soft-drop is already the default posture |
diff --git a/docs/adr/0071-dataset-semantic-layer-depth.md b/docs/adr/0071-dataset-semantic-layer-depth.md
index aa57456b7d..7d69d4210a 100644
--- a/docs/adr/0071-dataset-semantic-layer-depth.md
+++ b/docs/adr/0071-dataset-semantic-layer-depth.md
@@ -12,12 +12,12 @@ The first merged draft listed **matrix / across pivoting** as an open gap (a
proposed "P1"). That was wrong — a check of the current code found matrix
pivoting **already implemented end-to-end**:
-- **Render** — `packages/plugin-report/src/DatasetReportRenderer.tsx` →
+- **Render** — `objectui:packages/plugin-report/src/DatasetReportRenderer.tsx` →
`DatasetMatrixTable` does a true cross-tab: one dataset query over
`[...rows, ...columns]`, pivoted client-side into row × column × measure cells,
with server-supplied row/column subtotals + grand total and drill-down. Covered
by `DatasetReportRenderer.test.tsx`.
-- **Author** — `packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx`
+- **Author** — `objectui:packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx`
offers `type: 'matrix'` and a dedicated "columns across" dimension editor;
`packages/spec/src/ui/report.form.ts` reveals `columns` when `type == 'matrix'`.
diff --git a/docs/adr/0077-authoring-surface-boundary-hook-flow-validation.md b/docs/adr/0077-authoring-surface-boundary-hook-flow-validation.md
index 87bb41fdd7..3b334593d8 100644
--- a/docs/adr/0077-authoring-surface-boundary-hook-flow-validation.md
+++ b/docs/adr/0077-authoring-surface-boundary-hook-flow-validation.md
@@ -89,7 +89,7 @@ The genuine overlap is `after-*` side effects (write succeeded → notify / audi
### 4. Loud-not-silent — the two new `os build` lints (the only code in v1)
-Authored alongside the existing flow lints in `packages/cli/src/utils/lint-flow-patterns.ts` (which already ships `flow-runas-unscoped`, `flow-double-brace-interpolation`, etc.):
+Authored alongside the existing flow lints in `packages/lint/src/lint-flow-patterns.ts` (which already ships `flow-runas-unscoped`, `flow-double-brace-interpolation`, etc.):
- **`flow-record-before-cannot-mutate`** — *error*. A flow bound to `record-before-*` that contains a `create_record`/`update_record` node targeting the **triggering object/record**, or otherwise reads as expecting to change the in-flight record. Message points to **hook** (rewrite) or **validation rule** (veto).
- **`flow-record-before-cannot-veto`** — *error*. A `record-before-*` flow whose shape implies it intends to stop the write (e.g. a decision branch ending in an error/`end` node presented as rejection). Message: *"record-change flows cannot abort the triggering write — its errors are isolated by design; use a validation rule to reject, or a hook to throw."*
diff --git a/docs/adr/0080-ai-authored-ui-jsx-source.md b/docs/adr/0080-ai-authored-ui-jsx-source.md
index e9107d23e9..c05b64e27e 100644
--- a/docs/adr/0080-ai-authored-ui-jsx-source.md
+++ b/docs/adr/0080-ai-authored-ui-jsx-source.md
@@ -35,7 +35,7 @@ The SDUI runtime is **not** the thing that needs to change:
| Recursive tree interpreter (`type` → component, children recurse) | `@object-ui/react` `SchemaRenderer` → `ComponentRegistry.get()` → `React.createElement` |
| Recursive node schema (`{ type, props, children }`, `className`/`style`, `visibleOn`) | `@object-ui/types` `BaseSchema` / discriminated `LayoutSchema` (`objectui:packages/types/src/layout.ts`, literal `type` discriminants) |
| Per-component prop declaration | `ComponentInput[]` on `register()` (`objectui:packages/core/src/registry/Registry.ts`) |
-| Conflict-free styling channels | typed primitive props → safelisted class map (`ResponsiveGrid.tsx`), and **scoped per-node CSS** (ADR-0065, `packages/core/src/styling/scoped-styles.ts`) |
+| Conflict-free styling channels | typed primitive props → safelisted class map (`ResponsiveGrid.tsx`), and **scoped per-node CSS** (ADR-0065, `objectui:packages/core/src/styling/scoped-styles.ts`) |
| Enumerable catalog across ~40 plugins | `ComponentRegistry.getAllConfigs()` / `getAllTypes()` |
| An HTML escape hatch (today unsafe) | `renderers/basic/html.tsx` — raw `dangerouslySetInnerHTML`, no sanitize |
diff --git a/docs/adr/0082-react-component-contract-governance.md b/docs/adr/0082-react-component-contract-governance.md
index ab13996ff5..d650244c0f 100644
--- a/docs/adr/0082-react-component-contract-governance.md
+++ b/docs/adr/0082-react-component-contract-governance.md
@@ -3,7 +3,7 @@
**Status**: Accepted (2026-06-30)
**Deciders**: ObjectStack Protocol Architects
**Builds on**: [ADR-0080](./0080-ai-authored-ui-jsx-source.md) (AI authors UI; the component registry `inputs` are the contract; **capability ≠ contract** — curate a small public surface, not the full capability set), [ADR-0081](./0081-trusted-react-page-tier.md) (the `kind:'react'` tier executes real React; its safety boundary is **trust + review**, and its prop ceiling is the **injected scope**), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI writes metadata via draft-gated review), [ADR-0054](./0054-runtime-proof-for-authorable-surface.md) (ratchet a snapshot; flag regressions, not the accepted baseline), [ADR-0078](./0078-no-silently-inert-metadata.md) (no silently-inert metadata — a prop the author writes must be honored or rejected, never silently dropped).
-**Consumers**: `@objectstack/spec` (`packages/spec/src/ui/react-blocks.ts` — the block→schema index + React overlay; `scripts/build-react-blocks-contract.ts` — the generator; `scripts/check-react-blocks-declaration-parity.ts` + `react-declaration-parity.baseline.json` — the ratchet), `@objectstack/lint` (`validate-react-page-props.ts` — the authoring prop gate), `@objectstack/cli` (`os validate` wires the gate), `scripts/gen-sdui-manifest.sh` (`pnpm sdui:manifest` — dumps the manifest and runs the ratchet; **this is the producer**, corrected here in #5960: the ratchet moved out of `scripts/build-console.sh` in #4472 and this row still named the old file), `../objectui` (the component registry `inputs` are the projected surface the ratchet checks against).
+**Consumers**: `@objectstack/spec` (`packages/spec/src/ui/react-blocks.ts` — the block→schema index + React overlay; `packages/spec/scripts/build-react-blocks-contract.ts` — the generator; `packages/spec/scripts/check-react-blocks-declaration-parity.ts` + `react-declaration-parity.baseline.json` — the ratchet), `@objectstack/lint` (`validate-react-page-props.ts` — the authoring prop gate), `@objectstack/cli` (`os validate` wires the gate), `scripts/gen-sdui-manifest.sh` (`pnpm sdui:manifest` — dumps the manifest and runs the ratchet; **this is the producer**, corrected here in #5960: the ratchet moved out of `scripts/build-console.sh` in #4472 and this row still named the old file), `../objectui` (the component registry `inputs` are the projected surface the ratchet checks against).
**Premise**: ADR-0081 gave authors (and AI) a `kind:'react'` page tier whose blocks are the curated public data components (``, ``, charts, record:* panels). For AI to author those blocks *correctly* it must know each block's props — and for that knowledge to be trustworthy, the props must come from an authoritative, machine-readable, **non-drifting** source. The problem: **there is no single such source.** Three prop surfaces exist for the same components, and nothing keeps them in lockstep:
@@ -36,7 +36,7 @@ They drift silently: a component can accept a prop the spec never declared (an u
`packages/spec/src/ui/react-blocks.ts` is a **block→schema index**: each curated public block (`` → `FormViewSchema`, `` → `ListViewSchema`, `` → `RecordDetailsProps`, `` → `ChartConfigSchema`, …) names its spec schema, plus a per-block `dataProps` allowlist that curates *which* schema props to surface (ADR-0080: capability ≠ contract — `ListView` has 45 schema props; the contract surfaces ~10 high-signal ones).
-`scripts/build-react-blocks-contract.ts` generates the AI-facing contract (`skills/objectstack-ui/references/react-blocks.md`) by reading the spec schemas (`z.toJSONSchema`, with `OS_EAGER_SCHEMAS=1` to resolve lazy schemas), taking each prop's spec-authored `.describe()`, and merging the React overlay. **Hand-authoring is rejected** — a hand-written contract drifts into fiction; a generated one is zero-drift by construction. *(#14296 item 3 = A, 2026-09-02: this generator also emitted a second, machine-readable rendering of the same table at `contracts/react-blocks.contract.json` — same four blocks, same prop counts, byte-identical `note`. It had zero consumers in either repo and charged the published skill bundle for a second copy of one table, so it is retired: the markdown is the single AI-facing output, and `check:react-blocks` verifies one artifact.)*
+`packages/spec/scripts/build-react-blocks-contract.ts` generates the AI-facing contract (`skills/objectstack-ui/references/react-blocks.md`) by reading the spec schemas (`z.toJSONSchema`, with `OS_EAGER_SCHEMAS=1` to resolve lazy schemas), taking each prop's spec-authored `.describe()`, and merging the React overlay. **Hand-authoring is rejected** — a hand-written contract drifts into fiction; a generated one is zero-drift by construction. *(#14296 item 3 = A, 2026-09-02: this generator also emitted a second, machine-readable rendering of the same table at `contracts/react-blocks.contract.json` — same four blocks, same prop counts, byte-identical `note`. It had zero consumers in either repo and charged the published skill bundle for a second copy of one table, so it is retired: the markdown is the single AI-facing output, and `check:react-blocks` verifies one artifact.)*
### 2. Registry `inputs` are a projection, not the protocol
@@ -61,7 +61,7 @@ These are declared in the `react-blocks.ts` overlay with a `kind` of `binding`/`
>
> **Corrected by #5960 — see addendum 2.** This section shipped saying the ratchet runs *inside `build-console.sh`*. That was true when it was written and stopped being true at #4472, which moved it into `scripts/gen-sdui-manifest.sh`; the file names below are corrected in place, because a reader who followed this ADR opened the wrong file. #5960 also answers the question this decision left open — "not every PR" never said *when*, and the answer is **at the objectui pin bump**.
-`scripts/check-react-blocks-declaration-parity.ts` compares the spec props (per block, via `z.toJSONSchema`) against the registry-inputs manifest (`sdui.manifest.json`). The manifest **only exists once a real browser has enumerated the built console registry** — the registry is a browser app pulling browser-only deps, so a framework PR has no manifest to check against. Running it on every PR is therefore not worth it.
+`packages/spec/scripts/check-react-blocks-declaration-parity.ts` compares the spec props (per block, via `z.toJSONSchema`) against the registry-inputs manifest (`sdui.manifest.json`). The manifest **only exists once a real browser has enumerated the built console registry** — the registry is a browser app pulling browser-only deps, so a framework PR has no manifest to check against. Running it on every PR is therefore not worth it.
Instead, it runs **inside `scripts/gen-sdui-manifest.sh`** (`pnpm sdui:manifest`), immediately after that script dumps the manifest (near-zero marginal cost *there*, because the browser is already open), as a **baseline ratchet** modeled on ADR-0054:
diff --git a/docs/adr/0087-metadata-protocol-upgrade-contract.md b/docs/adr/0087-metadata-protocol-upgrade-contract.md
index 89fe5da680..3c3a894c94 100644
--- a/docs/adr/0087-metadata-protocol-upgrade-contract.md
+++ b/docs/adr/0087-metadata-protocol-upgrade-contract.md
@@ -57,7 +57,7 @@ concrete gaps, then the assumption that reshapes the solution:
loader, installer, or CLI command reads it. Under ADR-0078, an authorable field the
runtime ignores is a bug class of its own: enforce it or remove it.
-2. **Change information is human-readable only.** `packages/spec/api-surface.json`
+2. **Change information is human-readable only.** `packages/spec/api-surface/`
records the full export surface and its diff gates every PR — then the diff is
thrown away. Releases ship a prose CHANGELOG and (for 11) a hand-written upgrade
guide. Nothing machine-consumable maps version N → N+1.
diff --git a/docs/adr/0104-field-runtime-value-shape-contract.md b/docs/adr/0104-field-runtime-value-shape-contract.md
index 38309b0d3c..408d1c5872 100644
--- a/docs/adr/0104-field-runtime-value-shape-contract.md
+++ b/docs/adr/0104-field-runtime-value-shape-contract.md
@@ -161,7 +161,7 @@ type lists):
- `driver-sql` derives `JSON_COLUMN_TYPES` / `NUMERIC_SCALAR_TYPES` membership
from the spec classes (DDL column choice remains the driver's decision; the
*classification* moves to the spec).
-- `packages/verify/read-coercion.ts` grows from a 3-type probe to asserting
+- `packages/verify/src/read-coercion.ts` grows from a 3-type probe to asserting
the full matrix: for every field type, a stored-form write round-trips to a
stored-form read on every driver.
diff --git a/docs/adr/0107-withdrawn-hook-body-write-set-static-gap.md b/docs/adr/0107-withdrawn-hook-body-write-set-static-gap.md
index 6948c37bf5..8ee4868454 100644
--- a/docs/adr/0107-withdrawn-hook-body-write-set-static-gap.md
+++ b/docs/adr/0107-withdrawn-hook-body-write-set-static-gap.md
@@ -74,7 +74,8 @@ state the invariant its ADR decided, and this number decided nothing.
The citations that exist today all discuss the withdrawal itself:
-- `.changeset/withdraw-adr-0107-drop-writes-proposal.md` — the changeset that withdrew it;
+- `.changeset/withdraw-adr-0107-drop-writes-proposal.md` — the changeset that withdrew it (since
+ consumed by the release, `24c1b91e4`);
- `docs/audits/2026-07-app-metadata-reference-integrity-assessment.md` §5 D4 — the audit whose open decision it recorded, which carries the revision;
- `scripts/check-adr-anchors.mjs` — the gate, describing this case.
diff --git a/docs/adr/0113-required-write-contract-vs-column-constraint.md b/docs/adr/0113-required-write-contract-vs-column-constraint.md
index 97725a6522..56790fed3a 100644
--- a/docs/adr/0113-required-write-contract-vs-column-constraint.md
+++ b/docs/adr/0113-required-write-contract-vs-column-constraint.md
@@ -16,7 +16,7 @@ verified sites:
| Meaning | Where it lives today |
|---|---|
| a write must provide the value | `objectql/validation/record-validator.ts` |
-| the COLUMN is `NOT NULL` | `packages/drivers/driver-sql/src/sql-driver.ts` — `if (field.required) col.notNullable()` |
+| the COLUMN is `NOT NULL` | **At decision time (2026-07-30):** `packages/drivers/driver-sql/src/sql-driver.ts#createColumn` — `if (field.required) col.notNullable()`. **Superseded by this ADR's P0** (2026-09 note, #14193): the physical constraint now keys off the explicitly-authored `storage.notNull` at that same `#createColumn` site; `field.required` no longer drives the DDL. |
| required-vs-nullable divergence is DRIFT | `packages/drivers/driver-sql/src/schema-drift.ts` — metadata-required + nullable column ⇒ expected `NOT NULL`, and imposing `NOT NULL` over possibly-null data is the classifier's `destructive` class |
> ⚠️ **Anchor note (#13556).** The three anchors above were line numbers into
@@ -30,6 +30,14 @@ verified sites:
> deliberately **not** repaired here: this record's Context describes the
> PRE-decision state, and rewriting it inside an anchor migration would edit a
> decision record's substance under cover of a formatting change.
+>
+> **Resolved (2026-09, #14193).** The two Context sites — the table row above and
+> the prose sentence under *The tri-binding, concretely* — are now annotated in
+> place as the state **at decision time**, each naming the `storage.notNull` site
+> that superseded it, and both citations are re-anchored by symbol
+> (`packages/drivers/driver-sql/src/sql-driver.ts#createColumn`) rather than by line. ⛔ No decision text changed:
+> the Context still describes the pre-P0 tri-binding, which is what motivated the
+> decision — it is now dated, so it cannot be read as a description of today.
Because all three ride one flag, **tightening any invariant on a deployed
object is a destructive migration, blocked by the very legacy nulls that
@@ -56,7 +64,9 @@ becomes a recognized posture: *new writes must provide; old rows may rest.*
### The tri-binding, concretely
Authoring `required: true` on a field of a **new** object is unremarkable: the
-column is created `NOT NULL` (packages/drivers/driver-sql/src/sql-driver.ts), the validator enforces
+column is created `NOT NULL` (`packages/drivers/driver-sql/src/sql-driver.ts#createColumn` — *at
+decision time; superseded by this ADR's P0, where that same site keys the constraint off the
+explicit `storage.notNull` instead*), the validator enforces
presence, the form shows the marker, drift never fires. The knob works —
until the object has deployed data.
diff --git a/docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md b/docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md
index bfa93dc865..24191df938 100644
--- a/docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md
+++ b/docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md
@@ -443,7 +443,7 @@ split this ADR exists to close (PD #10):
enforcement — the #5030 probe graduates into this suite), and for D2 (nine-key
inventory unchanged). The *"`'global'` is a synonym of `true`"* pin retires at 18
together with bare `true`.
-7. ADR anchors: `scripts/adr-anchors.json` entries for `normalizeDeclaredIndex`,
+7. ADR anchors: `scripts/adr-anchors/` entries for `normalizeDeclaredIndex`,
`uniqueIndexesFromFields`, the conversion entry, and R10/R-new (PD #13 corollary —
leave the id in the code).
diff --git a/docs/adr/0126-packaged-metadata-customization-model.md b/docs/adr/0126-packaged-metadata-customization-model.md
index a81732d321..9523336bd3 100644
--- a/docs/adr/0126-packaged-metadata-customization-model.md
+++ b/docs/adr/0126-packaged-metadata-customization-model.md
@@ -100,7 +100,9 @@ Three real mechanisms operate across those tiers, each invented separately:
object, or nav items to another package's app, merged at boot. Package-grain, additive,
upgrade-safe.
-And one **paper** mechanism: `packages/spec/src/kernel/metadata-customization.zod.ts` declares a
+And one **paper** mechanism: `packages/spec/src/kernel/metadata-customization.zod.ts` (*Path note,
+2026-09: the module was retired and deleted whole on 2026-08-29, `9e0ba21a1` — the disposition §6.4
+records; the path is kept as the record of what this section measured*) declares a
three-layer (system/platform/user) overlay protocol with field-level change tracking — exported,
published in the reference docs, and consumed by **zero** runtime packages
([#12057](https://github.com/objectstack-ai/objectstack/issues/12057)). §6.4 records its