@jini-ai/cms 0.3.10 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +47 -19
  3. package/dist/content-types/agent-tools.d.ts +3 -56
  4. package/dist/content-types/agent-tools.js +22 -6
  5. package/dist/content-types/cleanup.d.ts +18 -13
  6. package/dist/content-types/cleanup.js +16 -11
  7. package/dist/content-types/errors.d.ts +57 -24
  8. package/dist/content-types/errors.js +56 -39
  9. package/dist/content-types/field-defs.d.ts +8 -4
  10. package/dist/content-types/field-defs.js +19 -14
  11. package/dist/content-types/index-provisioning.d.ts +61 -47
  12. package/dist/content-types/index-provisioning.js +90 -66
  13. package/dist/content-types/index.d.ts +4 -3
  14. package/dist/content-types/lifecycle-dispatch.d.ts +11 -5
  15. package/dist/content-types/lifecycle-dispatch.js +5 -4
  16. package/dist/content-types/lifecycle.d.ts +23 -18
  17. package/dist/content-types/lifecycle.js +71 -64
  18. package/dist/content-types/repo.memory.d.ts +5 -4
  19. package/dist/content-types/repo.memory.js +3 -2
  20. package/dist/content-types/tool-registrations.d.ts +6 -8
  21. package/dist/content-types/tool-registrations.js +24 -28
  22. package/dist/content-types/types.d.ts +17 -13
  23. package/dist/content-types/types.js +11 -5
  24. package/dist/content-types/write-service.d.ts +20 -22
  25. package/dist/content-types/write-service.js +78 -75
  26. package/dist/core/authorization.d.ts +15 -0
  27. package/dist/core/authorization.js +2 -0
  28. package/dist/core/commands/change-set.d.ts +15 -7
  29. package/dist/core/commands/command.d.ts +45 -26
  30. package/dist/core/commands/command.js +73 -63
  31. package/dist/core/entity-liveness.d.ts +11 -7
  32. package/dist/core/entity-liveness.js +5 -4
  33. package/dist/core/index.d.ts +12 -5
  34. package/dist/core/index.js +10 -4
  35. package/dist/core/ports.d.ts +25 -24
  36. package/dist/core/ports.js +1 -0
  37. package/dist/core/tools/args.d.ts +49 -0
  38. package/dist/core/tools/args.js +46 -0
  39. package/dist/core/tools/index.d.ts +3 -0
  40. package/dist/core/tools/index.js +3 -0
  41. package/dist/entries/agent-tools.d.ts +1 -73
  42. package/dist/entries/agent-tools.js +19 -13
  43. package/dist/entries/errors.d.ts +28 -14
  44. package/dist/entries/errors.js +17 -11
  45. package/dist/entries/field-validation.d.ts +5 -3
  46. package/dist/entries/field-validation.js +7 -5
  47. package/dist/entries/index.d.ts +2 -2
  48. package/dist/entries/lifecycle-dispatch.d.ts +5 -2
  49. package/dist/entries/lifecycle-dispatch.js +2 -1
  50. package/dist/entries/list.d.ts +4 -2
  51. package/dist/entries/list.js +4 -3
  52. package/dist/entries/repo.memory.d.ts +12 -10
  53. package/dist/entries/repo.memory.js +11 -10
  54. package/dist/entries/tool-registrations.d.ts +6 -8
  55. package/dist/entries/tool-registrations.js +42 -41
  56. package/dist/entries/types.d.ts +0 -7
  57. package/dist/entries/write-service.d.ts +28 -29
  58. package/dist/entries/write-service.js +114 -105
  59. package/dist/http/settings/cms-adapter.d.ts +20 -0
  60. package/dist/http/settings/cms-adapter.js +124 -0
  61. package/dist/http/settings/contracts.d.ts +135 -0
  62. package/dist/http/settings/contracts.js +2 -0
  63. package/dist/http/settings/http.d.ts +10 -0
  64. package/dist/http/settings/http.js +211 -0
  65. package/dist/http/settings/index.d.ts +5 -0
  66. package/dist/http/settings/index.js +4 -0
  67. package/dist/http/settings/shared.d.ts +68 -0
  68. package/dist/http/settings/shared.js +54 -0
  69. package/dist/http/settings/stream.d.ts +24 -0
  70. package/dist/http/settings/stream.js +147 -0
  71. package/dist/media/agent-tools.d.ts +1 -66
  72. package/dist/media/agent-tools.js +5 -1
  73. package/dist/media/blob-gc-lock.d.ts +6 -2
  74. package/dist/media/blob-gc-lock.js +4 -2
  75. package/dist/media/blob-gc.d.ts +64 -51
  76. package/dist/media/blob-gc.js +62 -52
  77. package/dist/media/blob-key.d.ts +1 -1
  78. package/dist/media/content-type-sniffer.d.ts +3 -1
  79. package/dist/media/content-type-sniffer.js +1 -1
  80. package/dist/media/html-attributes.d.ts +9 -5
  81. package/dist/media/html-attributes.js +8 -6
  82. package/dist/media/image-transformer.js +1 -1
  83. package/dist/media/image-transformer.sharp.d.ts +6 -0
  84. package/dist/media/image-transformer.sharp.js +12 -6
  85. package/dist/media/import/fetch-image.d.ts +24 -0
  86. package/dist/media/import/fetch-image.js +137 -0
  87. package/dist/media/import/index.d.ts +3 -0
  88. package/dist/media/import/index.js +3 -0
  89. package/dist/media/import/ports.d.ts +38 -0
  90. package/dist/media/index.d.ts +7 -1
  91. package/dist/media/index.js +1 -0
  92. package/dist/media/media-service.d.ts +14 -10
  93. package/dist/media/media-service.js +57 -52
  94. package/dist/media/ports.d.ts +3 -2
  95. package/dist/media/rendition-service.d.ts +3 -3
  96. package/dist/media/rendition-service.js +38 -37
  97. package/dist/media/repo.memory.d.ts +18 -7
  98. package/dist/media/repo.memory.js +12 -6
  99. package/dist/media/tool-registrations.d.ts +16 -15
  100. package/dist/media/tool-registrations.js +84 -72
  101. package/dist/media/transform-lock.d.ts +22 -12
  102. package/dist/media/transform-lock.js +14 -13
  103. package/dist/media/transform-registry.d.ts +3 -3
  104. package/dist/media/transform-registry.js +19 -18
  105. package/dist/media/transform-types.d.ts +8 -3
  106. package/dist/media/transform-types.js +10 -5
  107. package/dist/media/types.d.ts +21 -4
  108. package/dist/media/types.js +15 -2
  109. package/dist/navigation/agent-tools.d.ts +1 -80
  110. package/dist/navigation/agent-tools.js +15 -3
  111. package/dist/navigation/contracts.d.ts +1 -1
  112. package/dist/navigation/index.d.ts +2 -2
  113. package/dist/navigation/menu-service.d.ts +57 -40
  114. package/dist/navigation/menu-service.js +80 -69
  115. package/dist/navigation/ports.d.ts +1 -1
  116. package/dist/navigation/read-model.d.ts +3 -1
  117. package/dist/navigation/read-model.js +2 -2
  118. package/dist/navigation/reconcile.d.ts +2 -2
  119. package/dist/navigation/reconcile.js +2 -1
  120. package/dist/navigation/repo.memory.d.ts +7 -3
  121. package/dist/navigation/repo.memory.js +4 -2
  122. package/dist/navigation/resolver.d.ts +4 -1
  123. package/dist/navigation/resolver.js +1 -1
  124. package/dist/navigation/tool-registrations.d.ts +6 -8
  125. package/dist/navigation/tool-registrations.js +44 -42
  126. package/dist/navigation/types.d.ts +1 -1
  127. package/dist/presentation/presentation.d.ts +11 -3
  128. package/dist/presentation/presentation.js +13 -6
  129. package/dist/presentation/repo.memory.d.ts +6 -2
  130. package/dist/presentation/repo.memory.js +3 -2
  131. package/dist/settings/agent-tools.d.ts +2 -75
  132. package/dist/settings/agent-tools.js +49 -45
  133. package/dist/settings/agent-value-write-tools.d.ts +22 -0
  134. package/dist/settings/agent-value-write-tools.js +154 -0
  135. package/dist/settings/agent-writable-preferences.js +8 -20
  136. package/dist/settings/agent-write-denylist.d.ts +19 -0
  137. package/dist/settings/agent-write-denylist.js +15 -0
  138. package/dist/settings/change-feed.d.ts +11 -2
  139. package/dist/settings/change-feed.js +4 -4
  140. package/dist/settings/definitions-dispatch.d.ts +8 -3
  141. package/dist/settings/definitions-dispatch.js +5 -5
  142. package/dist/settings/dictionaries/index.d.ts +5 -2
  143. package/dist/settings/dictionaries/index.js +2 -2
  144. package/dist/settings/dictionaries/settings-dialog.ar.js +0 -1
  145. package/dist/settings/dictionaries/settings-dialog.bn.js +0 -1
  146. package/dist/settings/dictionaries/settings-dialog.de.js +0 -1
  147. package/dist/settings/dictionaries/settings-dialog.es.js +0 -1
  148. package/dist/settings/dictionaries/settings-dialog.fa.js +0 -1
  149. package/dist/settings/dictionaries/settings-dialog.fr.js +0 -1
  150. package/dist/settings/dictionaries/settings-dialog.hi.js +0 -1
  151. package/dist/settings/dictionaries/settings-dialog.hu.js +0 -1
  152. package/dist/settings/dictionaries/settings-dialog.id.js +0 -1
  153. package/dist/settings/dictionaries/settings-dialog.it.js +0 -1
  154. package/dist/settings/dictionaries/settings-dialog.ja.js +0 -1
  155. package/dist/settings/dictionaries/settings-dialog.ko.js +0 -1
  156. package/dist/settings/dictionaries/settings-dialog.pl.js +0 -1
  157. package/dist/settings/dictionaries/settings-dialog.pt-BR.js +0 -1
  158. package/dist/settings/dictionaries/settings-dialog.ru.js +0 -1
  159. package/dist/settings/dictionaries/settings-dialog.th.js +0 -1
  160. package/dist/settings/dictionaries/settings-dialog.tr.js +0 -1
  161. package/dist/settings/dictionaries/settings-dialog.uk.js +0 -1
  162. package/dist/settings/dictionaries/settings-dialog.ur.js +0 -1
  163. package/dist/settings/dictionaries/settings-dialog.zh-CN.js +0 -1
  164. package/dist/settings/dictionaries/settings-dialog.zh-TW.js +0 -1
  165. package/dist/settings/ensure-definitions.d.ts +5 -5
  166. package/dist/settings/errors.d.ts +7 -1
  167. package/dist/settings/errors.js +7 -1
  168. package/dist/settings/index.d.ts +4 -1
  169. package/dist/settings/index.js +1 -0
  170. package/dist/settings/ports.d.ts +6 -5
  171. package/dist/settings/principal-lookup.d.ts +9 -0
  172. package/dist/settings/principal-lookup.js +2 -0
  173. package/dist/settings/purge-service.d.ts +5 -4
  174. package/dist/settings/purge-service.js +3 -2
  175. package/dist/settings/settings.d.ts +22 -7
  176. package/dist/settings/settings.js +47 -26
  177. package/dist/settings/tool-registrations.d.ts +25 -32
  178. package/dist/settings/tool-registrations.js +66 -56
  179. package/dist/settings/types.d.ts +1 -1
  180. package/dist/settings/ui-tab-definitions.d.ts +1 -1
  181. package/dist/settings/write-service.d.ts +32 -26
  182. package/dist/settings/write-service.js +26 -20
  183. package/dist/taxonomy/agent-tools.d.ts +1 -66
  184. package/dist/taxonomy/agent-tools.js +25 -15
  185. package/dist/taxonomy/content-lookup.js +1 -1
  186. package/dist/taxonomy/index.d.ts +2 -2
  187. package/dist/taxonomy/merge-term.d.ts +7 -4
  188. package/dist/taxonomy/merge-term.js +7 -5
  189. package/dist/taxonomy/repo.memory.d.ts +42 -19
  190. package/dist/taxonomy/repo.memory.js +14 -12
  191. package/dist/taxonomy/validation-chain.d.ts +39 -18
  192. package/dist/taxonomy/validation-chain.js +35 -25
  193. package/dist/taxonomy/write-service.d.ts +106 -45
  194. package/dist/taxonomy/write-service.js +221 -179
  195. package/dist/trash/adapters/record-store.d.ts +52 -0
  196. package/dist/trash/adapters/record-store.js +36 -0
  197. package/dist/trash/cursor.d.ts +20 -0
  198. package/dist/trash/cursor.js +36 -0
  199. package/dist/trash/follow-ups.d.ts +76 -0
  200. package/dist/trash/follow-ups.js +46 -0
  201. package/dist/trash/index.d.ts +8 -0
  202. package/dist/trash/index.js +8 -0
  203. package/dist/trash/ports.d.ts +308 -0
  204. package/dist/trash/ports.js +2 -0
  205. package/dist/trash/repo.memory.d.ts +50 -0
  206. package/dist/trash/repo.memory.js +114 -0
  207. package/dist/trash/sweeper.d.ts +77 -0
  208. package/dist/trash/sweeper.js +127 -0
  209. package/dist/trash/write-service.d.ts +100 -0
  210. package/dist/trash/write-service.js +242 -0
  211. package/dist/workspace/agent-tools.d.ts +1 -59
  212. package/dist/workspace/agent-tools.js +27 -12
  213. package/dist/workspace/create.d.ts +31 -6
  214. package/dist/workspace/create.js +18 -5
  215. package/dist/workspace/delete.d.ts +18 -13
  216. package/dist/workspace/delete.js +20 -12
  217. package/dist/workspace/index.d.ts +1 -1
  218. package/dist/workspace/repo.memory.d.ts +19 -4
  219. package/dist/workspace/repo.memory.js +26 -4
  220. package/dist/workspace/tool-registrations.d.ts +2 -2
  221. package/dist/workspace/tool-registrations.js +13 -13
  222. package/dist/workspace/update.d.ts +6 -5
  223. package/dist/workspace/update.js +9 -8
  224. package/package.json +44 -38
  225. package/dist/identity/admin-crud-service.d.ts +0 -280
  226. package/dist/identity/admin-crud-service.js +0 -468
  227. package/dist/identity/agent-tool-input.d.ts +0 -58
  228. package/dist/identity/agent-tool-input.js +0 -126
  229. package/dist/identity/agent-tools.d.ts +0 -101
  230. package/dist/identity/agent-tools.js +0 -290
  231. package/dist/identity/auth-service.d.ts +0 -118
  232. package/dist/identity/auth-service.js +0 -170
  233. package/dist/identity/authorize.d.ts +0 -90
  234. package/dist/identity/authorize.js +0 -94
  235. package/dist/identity/grant-service.d.ts +0 -198
  236. package/dist/identity/grant-service.js +0 -361
  237. package/dist/identity/hasher.d.ts +0 -39
  238. package/dist/identity/hasher.js +0 -39
  239. package/dist/identity/index.d.ts +0 -62
  240. package/dist/identity/index.js +0 -54
  241. package/dist/identity/password-policy.d.ts +0 -57
  242. package/dist/identity/password-policy.js +0 -66
  243. package/dist/identity/permission-migrations.d.ts +0 -86
  244. package/dist/identity/permission-migrations.js +0 -67
  245. package/dist/identity/permissions.d.ts +0 -35
  246. package/dist/identity/permissions.js +0 -603
  247. package/dist/identity/ports.d.ts +0 -201
  248. package/dist/identity/repo.memory.d.ts +0 -164
  249. package/dist/identity/repo.memory.js +0 -224
  250. package/dist/identity/seed.d.ts +0 -54
  251. package/dist/identity/seed.js +0 -429
  252. package/dist/identity/tool-registrations.d.ts +0 -50
  253. package/dist/identity/tool-registrations.js +0 -419
  254. package/dist/identity/types.d.ts +0 -185
  255. package/dist/identity/types.js +0 -64
  256. package/dist/identity/username.d.ts +0 -12
  257. package/dist/identity/username.js +0 -14
  258. /package/dist/{identity → media/import}/ports.js +0 -0
@@ -1,10 +1,15 @@
1
- import { buildDomainRegistrations, indexCatalogById, optionalString, requireInputRecord, requireNoInput, requireString, requireToolPermission, withSchemaOnRejection, } from "../core/tools/registration-kit.js";
1
+ import { adaptLegacyAuthorize } from "../core/tools/index.js";
2
+ import { buildSettingsValueWriteHandler } from "./agent-value-write-tools.js";
3
+ import { AGENT_WRITE_CONFIRMATION_SETTINGS, AGENT_WRITE_DENIED_SETTINGS, findAgentWriteRule } from "./agent-write-denylist.js";
4
+ import { buildDomainRegistrations, indexCatalogById, optionalString, requireInputRecord, requireNoInput, requireString, withSchemaOnRejection } from "@jini-ai/core";
5
+ import { requireToolPermission } from "../core/tools/index.js";
2
6
  import { getSettingsAgentToolCatalog } from "./agent-tools.js";
3
7
  import { AGENT_PREFERENCE_WRITE_SCOPE, AGENT_WRITABLE_PREFERENCE_IDS, resolveAgentWritablePreference, } from "./agent-writable-preferences.js";
4
8
  import { DefinitionNotFoundError, DefinitionTombstonedError, ScopeNotAllowedError, ValueValidationFailedError, } from "./errors.js";
5
- import { getEffective, resolveDefinition } from "./settings.js";
9
+ import { getEffective, resolveDefinition, resolveDefinitionRaw } from "./settings.js";
10
+ import { SCOPE_BIT } from "./types.js";
6
11
  import { set as setSettingValue } from "./write-service.js";
7
- const CATALOG_BY_ID = indexCatalogById(getSettingsAgentToolCatalog());
12
+ const CATALOG_BY_ID = indexCatalogById({ catalog: getSettingsAgentToolCatalog() });
8
13
  /** Permission gating a read that names a DIFFERENT principal than the caller — mirrors a host's
9
14
  * own HTTP admin layer's equivalent constant (kept as a literal here per this file's header; the
10
15
  * two are data, not logic, so duplication carries no drift risk beyond a string comparison). */
@@ -31,11 +36,7 @@ async function resolveOwnOrOtherPrincipalRead(routeDeps, required) {
31
36
  const target = requestedPrincipalId ?? callerPrincipalId;
32
37
  if (target === callerPrincipalId)
33
38
  return target;
34
- await requireToolPermission(routeDeps, {
35
- principalId: callerPrincipalId,
36
- permission: CROSS_PRINCIPAL_SETTINGS_READ_PERMISSION,
37
- entityType: "setting-value",
38
- });
39
+ await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: callerPrincipalId, permission: CROSS_PRINCIPAL_SETTINGS_READ_PERMISSION }, { entityType: "setting-value" });
39
40
  return requestedPrincipalId;
40
41
  }
41
42
  function layerValueOf(record) {
@@ -79,11 +80,15 @@ function isPreferenceShapeRejection(error) {
79
80
  /**
80
81
  * This wiring layer's OWN risk classification, authored from what each handler below actually
81
82
  * calls. See `DerivedRiskByToolId` in the kit for why it is independent of the catalog's own
82
- * `sideEffects` declaration — and note that the four excluded writes appear NOWHERE here, which is
83
+ * `sideEffects` declaration — and note that the two excluded bulk/schema writes appear NOWHERE here, which is
83
84
  * itself the strongest of the guards: an unclassified id cannot be wired at all.
84
85
  */
85
86
  export const settingsDerivedRisk = new Map([
86
- // -> settingsRepo.listActiveDefinitions() x2 (platform + this workspace): reads only.
87
+ // -> write-service.set(): one value override and one revision, with optional human confirmation.
88
+ ["settings_set_value", "mutates-durable-state"],
89
+ // -> write-service.clear(): one override marked cleared and one revision; no definition/delete.
90
+ ["settings_clear_value", "mutates-durable-state"],
91
+ // -> settingsRepo.listActiveDefinitions() x2 + resolveDefinitionRaw() for aliases: reads only.
87
92
  ["settings_list_definitions", "none"],
88
93
  // -> settingsRepo.listActiveDefinitions() x2 + getEffective() per key: reads only, no write path.
89
94
  ["settings_get_effective", "none"],
@@ -97,11 +102,6 @@ export const settingsDerivedRisk = new Map([
97
102
  ]);
98
103
  /** Settings catalog entries this pass does not wire, and why — see `agent-tools.ts`'s own per-entry comments for the full reasoning. */
99
104
  const UNWIRED_SETTINGS_TOOL_IDS = new Set([
100
- // EXCLUDED BY DESIGN: generic "set any setting key" — a typical human admin UI for this domain
101
- // is itself an uncurated free-text/raw-JSON editor, not a fixed named list (see agent-tools.ts
102
- // file header).
103
- "settings_set",
104
- "settings_clear",
105
105
  // EXCLUDED BY DESIGN: bulk variant — clears every value in an operator-named namespace at a
106
106
  // scope in one call, irreversible.
107
107
  "settings_reset",
@@ -112,27 +112,44 @@ const UNWIRED_SETTINGS_TOOL_IDS = new Set([
112
112
  ]);
113
113
  export function buildSettingsRegistrations(routeDeps) {
114
114
  const handlers = {
115
+ settings_set_value: buildSettingsValueWriteHandler(routeDeps, "settings_set_value"),
116
+ settings_clear_value: buildSettingsValueWriteHandler(routeDeps, "settings_clear_value"),
115
117
  settings_list_definitions: async (ctx) => {
116
- requireNoInput(ctx.input);
118
+ requireNoInput({ input: ctx.input });
117
119
  await routeDeps.settingsReady;
118
- await requireToolPermission(routeDeps, { principalId: ctx.principal.id, permission: "settings.read.definitions", entityType: "setting-definition" });
120
+ await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "settings.read.definitions" }, { entityType: "setting-definition" });
119
121
  const [platformDefs, siteDefs] = await Promise.all([
120
122
  routeDeps.settingsRepo.listActiveDefinitions({ workspaceId: null }),
121
123
  routeDeps.settingsRepo.listActiveDefinitions({ workspaceId: routeDeps.workspaceId }),
122
124
  ]);
123
- const data = [...platformDefs, ...siteDefs]
124
- .map((def) => ({ namespace: def.namespace, key: def.key, ownerKind: def.ownerKind, scopes: def.scopes, status: def.status, version: def.version }))
125
+ const deniedRules = [...AGENT_WRITE_DENIED_SETTINGS, ...(routeDeps.extraDeniedSettings ?? [])];
126
+ const confirmationRules = [...AGENT_WRITE_CONFIRMATION_SETTINGS, ...(routeDeps.extraConfirmationSettings ?? [])];
127
+ // Only aliases need an extra lookup. Their target's schema/scope and BOTH coordinates'
128
+ // key policies must agree with the write handler; dangling/tombstoned targets fail closed.
129
+ const data = (await Promise.all([...platformDefs, ...siteDefs].map(async (def) => {
130
+ const resolved = def.status === "alias"
131
+ ? await resolveDefinitionRaw({ repo: routeDeps.settingsRepo }, { namespace: def.namespace, key: def.key, workspaceId: routeDeps.workspaceId })
132
+ : def;
133
+ const coordinates = resolved ? [def, resolved] : [def];
134
+ const denied = coordinates.some((coordinate) => !!findAgentWriteRule(deniedRules, coordinate));
135
+ const confirmationRequired = coordinates.some((coordinate) => !!findAgentWriteRule(confirmationRules, coordinate));
136
+ const scopes = resolved?.scopes ?? def.scopes;
137
+ return { namespace: def.namespace, key: def.key, ownerKind: def.ownerKind, scopes, status: def.status, version: def.version,
138
+ schema: resolved?.schema ?? def.schema,
139
+ agentWritable: !!resolved && resolved.status !== "tombstone" && !denied && (!confirmationRequired || !!routeDeps.confirmWrite) && (scopes & (SCOPE_BIT.workspace | SCOPE_BIT.user)) !== 0,
140
+ confirmationRequired };
141
+ })))
125
142
  .sort((a, b) => a.namespace.localeCompare(b.namespace) || a.key.localeCompare(b.key));
126
143
  return { data };
127
144
  },
128
145
  settings_get_effective: async (ctx) => {
129
- const input = requireInputRecord(ctx.input);
130
- const namespace = requireString(input, "namespace");
146
+ const input = requireInputRecord({ input: ctx.input });
147
+ const namespace = requireString({ input: input, key: "namespace" });
131
148
  await routeDeps.settingsReady;
132
- await requireToolPermission(routeDeps, { principalId: ctx.principal.id, permission: "settings.read", entityType: "setting-value" });
149
+ await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "settings.read" }, { entityType: "setting-value" });
133
150
  const workspaceId = routeDeps.workspaceId;
134
151
  const principalId = await resolveOwnOrOtherPrincipalRead(routeDeps, {
135
- requestedPrincipalId: optionalString(input, "principalId"),
152
+ requestedPrincipalId: optionalString({ input: input, key: "principalId" }),
136
153
  callerPrincipalId: ctx.principal.id,
137
154
  });
138
155
  const [platformDefs, siteDefs] = await Promise.all([
@@ -149,14 +166,14 @@ export function buildSettingsRegistrations(routeDeps) {
149
166
  return { data };
150
167
  },
151
168
  settings_get_raw: async (ctx) => {
152
- const input = requireInputRecord(ctx.input);
153
- const namespace = requireString(input, "namespace");
154
- const key = requireString(input, "key");
169
+ const input = requireInputRecord({ input: ctx.input });
170
+ const namespace = requireString({ input: input, key: "namespace" });
171
+ const key = requireString({ input: input, key: "key" });
155
172
  await routeDeps.settingsReady;
156
- await requireToolPermission(routeDeps, { principalId: ctx.principal.id, permission: "settings.read.raw", entityType: "setting-value" });
173
+ await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "settings.read.raw" }, { entityType: "setting-value" });
157
174
  const workspaceId = routeDeps.workspaceId;
158
175
  const principalId = await resolveOwnOrOtherPrincipalRead(routeDeps, {
159
- requestedPrincipalId: optionalString(input, "principalId"),
176
+ requestedPrincipalId: optionalString({ input: input, key: "principalId" }),
160
177
  callerPrincipalId: ctx.principal.id,
161
178
  });
162
179
  const definition = await resolveDefinition({ repo: routeDeps.settingsRepo }, { namespace, key, workspaceId });
@@ -176,8 +193,8 @@ export function buildSettingsRegistrations(routeDeps) {
176
193
  };
177
194
  },
178
195
  settings_set_ui_preference: async (ctx) => {
179
- const input = requireInputRecord(ctx.input);
180
- const settingId = requireString(input, "setting");
196
+ const input = requireInputRecord({ input: ctx.input });
197
+ const settingId = requireString({ input: input, key: "setting" });
181
198
  // Re-checked here even though the published schema carries the same enum. See
182
199
  // `agent-writable-preferences.ts` on why: the enum is enforced by a caller's descriptor
183
200
  // validation, and this is the check that still holds if a stale or bypassed descriptor lets
@@ -201,40 +218,33 @@ export function buildSettingsRegistrations(routeDeps) {
201
218
  // No `requireToolPermission` call here, unlike every read above. That is deliberate and is
202
219
  // the opposite of the reads' situation: the read paths carry no `authorize()` of their own,
203
220
  // so this layer must supply it, whereas `write-service.set()` IS the authorization
204
- // chokepoint (INV-07) and derives the permission itself from the scope and target. Adding a
221
+ // chokepoint and derives the permission itself from the scope and target. Adding a. See docs/decisions/DR-003-settings-ledger-invariants.md.
205
222
  // pre-check would mean this file naming a permission string that the chokepoint might later
206
223
  // derive differently — the exact fail-open drift `deriveRequiredPermission`'s own header
207
- // (Red-Team RT-003) says must not be duplicated. Omitting `principalId` is what makes the
224
+ // (Red-Team ) says must not be duplicated. Omitting `principalId` is what makes the. See docs/decisions/DR-003-settings-ledger-invariants.md.
208
225
  // derived permission `settings.user.self.write`.
209
226
  const { value: stored, revisionSeq } = await withSchemaOnRejection({
210
227
  toolId: "settings_set_ui_preference",
211
228
  catalog: CATALOG_BY_ID,
212
- isShapeRejection: isPreferenceShapeRejection,
213
- }, () => setSettingValue({
214
- deps: toWriteDeps(routeDeps),
215
- input: {
216
- namespace: preference.namespace,
217
- key: preference.key,
218
- scope: AGENT_PREFERENCE_WRITE_SCOPE,
219
- value,
220
- workspaceId: routeDeps.workspaceId,
221
- // `principalId` is deliberately NOT passed. `write-service.set()` falls back to
222
- // `callerPrincipalId` for a user-scoped write, so the caller's own layer is the only
223
- // reachable target — and the self-vs-other permission derivation sees a self-write.
224
- callerPrincipalId: ctx.principal.id,
225
- authWorkspaceId: routeDeps.workspaceId,
226
- },
227
- }));
229
+ isShapeRejection: ({ error }) => isPreferenceShapeRejection(error), fn: () => setSettingValue({
230
+ deps: toWriteDeps(routeDeps),
231
+ input: {
232
+ namespace: preference.namespace,
233
+ key: preference.key,
234
+ scope: AGENT_PREFERENCE_WRITE_SCOPE,
235
+ value,
236
+ workspaceId: routeDeps.workspaceId,
237
+ // `principalId` is deliberately NOT passed. `write-service.set()` falls back to
238
+ // `callerPrincipalId` for a user-scoped write, so the caller's own layer is the only
239
+ // reachable target — and the self-vs-other permission derivation sees a self-write.
240
+ callerPrincipalId: ctx.principal.id,
241
+ authWorkspaceId: routeDeps.workspaceId,
242
+ },
243
+ })
244
+ });
228
245
  return { setting: settingId, value: stored, scope: AGENT_PREFERENCE_WRITE_SCOPE, revisionSeq };
229
246
  },
230
247
  };
231
- return buildDomainRegistrations({
232
- domain: "settings",
233
- catalogModule: "settings/agent-tools.ts",
234
- catalog: CATALOG_BY_ID,
235
- handlers,
236
- derivedRisk: settingsDerivedRisk,
237
- unwiredToolIds: UNWIRED_SETTINGS_TOOL_IDS,
238
- });
248
+ return buildDomainRegistrations({ domain: "settings", catalogModule: "settings/agent-tools.ts", catalog: CATALOG_BY_ID, handlers, derivedRisk: settingsDerivedRisk }, { unwiredToolIds: UNWIRED_SETTINGS_TOOL_IDS });
239
249
  }
240
250
  //# sourceMappingURL=tool-registrations.js.map
@@ -1,4 +1,4 @@
1
- import type { ISODateTime, JsonValue, UUID } from "../core/ports.js";
1
+ import type { ISODateTime, JsonValue, UUID } from "@jini-ai/core/primitives";
2
2
  /**
3
3
  * @file Settings domain types.
4
4
  *
@@ -1,4 +1,4 @@
1
- import type { UUID } from "../core/ports.js";
1
+ import type { UUID } from "@jini-ai/core/primitives";
2
2
  import { type EnsureSettingDefinitionsDeps } from "./ensure-definitions.js";
3
3
  /**
4
4
  * @file Boot-time definition registration for the settings-dialog tabs that
@@ -1,10 +1,10 @@
1
- import type { ClockPort, IdGeneratorPort, JsonValue, UUID } from "../core/ports.js";
2
- import type { PrincipalRepoPort } from "../identity/index.js";
1
+ import type { Clock, IdGenerator, JsonValue, UUID } from "@jini-ai/core/primitives";
2
+ import type { SettingsPrincipalLookupPort } from "./principal-lookup.js";
3
3
  import type { SettingsRepoPort } from "./ports.js";
4
4
  import { type DefinitionInput } from "./settings.js";
5
5
  import { type SettingScope, type SettingValueSchema } from "./types.js";
6
6
  /**
7
- * @file `SettingsWriteService` — the single write chokepoint (REQ-04).
7
+ * @file `SettingsWriteService` — the single write chokepoint.
8
8
  *
9
9
  * Purpose:
10
10
  * The ONLY value/definition-mutation path. Repo write methods
@@ -12,13 +12,14 @@ import { type SettingScope, type SettingValueSchema } from "./types.js";
12
12
  * be called from outside this file (Code Review enforces this as a file-
13
13
  * boundary check).
14
14
  *
15
- * Every export here: `authorize()` first (fail-closed, INV-07) -> validate ->
16
- * write value/definition row + revision in one transaction (INV-01).
15
+ * Every export here: `authorize` first (fail-closed) -> validate ->
16
+ * write value/definition row + revision in one transaction.
17
17
  *
18
18
  * `deriveRequiredPermission` is the sole source of truth for the self-vs-
19
- * other permission rule (behavior.spec.md §1.3) — closes Red-Team RT-003.
20
- * REQ-13's target-principal check reuses `identity.PrincipalRepoPort`
21
- * directly — no new port (closes RT-001/RT-002 at the implementation level).
19
+ * other permission rule (behavior.spec.md §1.3) — closes Red-Team.
20
+ * target-principal checks use a host-supplied active-principal lookup over core Principal;
21
+ * identity and membership policy remain at the host boundary.
22
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
22
23
  */
23
24
  /** Matches `core/commands/command.ts`'s `AuthorizeFn` shape structurally — no import, kept decoupled. */
24
25
  export type AuthorizeFn = (params: {
@@ -33,22 +34,23 @@ export type AuthorizeFn = (params: {
33
34
  }>;
34
35
  export interface SettingsWriteServiceDeps {
35
36
  repo: SettingsRepoPort;
36
- clock: ClockPort;
37
- ids: IdGeneratorPort;
37
+ clock: Clock;
38
+ ids: IdGenerator;
38
39
  authorize: AuthorizeFn;
39
- /** REQ-13 — reused directly from `identity`, not duplicated. */
40
- principals: PrincipalRepoPort;
40
+ /** Host lookup must hide disabled and out-of-workspace principals. See docs/decisions/DR-003-settings-ledger-invariants.md. */
41
+ principals: SettingsPrincipalLookupPort;
41
42
  }
42
43
  /**
43
- * behavior.spec.md §1.3 / REQ-06 `[internal-invariant]` — the self-vs-other
44
+ * behavior.spec.md §1.3 / `[internal-invariant]` — the self-vs-other
44
45
  * permission derivation. `targetPrincipalId` omitted or equal to the caller
45
46
  * -> `settings.user.self.write`; any other principal -> `settings.user.write`.
46
47
  * A divergence between this function and any other implementation of the
47
- * rule is a fail-open authorization bug (Red-Team RT-003) — this is the only
48
+ * rule is a fail-open authorization bug (Red-Team ) — this is the only
48
49
  * place the rule may be encoded.
49
50
  *
50
51
  * @complexity O(1), pure.
51
52
  * @overallScore 100
53
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
52
54
  */
53
55
  export declare function deriveRequiredPermission(input: {
54
56
  scope: SettingScope;
@@ -64,7 +66,7 @@ export interface RegisterDefinitionsRequired {
64
66
  authWorkspaceId: UUID;
65
67
  };
66
68
  }
67
- /** REQ-02/REQ-09 chokepoint write: authorize `settings.definitions.manage` -> validate -> write + revision, one per definition, each its own tx. */
69
+ /** chokepoint write: authorize `settings.definitions.manage` -> validate -> write + revision, one per definition, each its own tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
68
70
  export declare function registerDefinitions(required: RegisterDefinitionsRequired): Promise<{
69
71
  registered: string[];
70
72
  }>;
@@ -101,13 +103,14 @@ export interface SetValueRequired {
101
103
  * additive: omitted, behavior is unchanged (every pre-existing caller still gets the
102
104
  * scope-derived permission). When a domain-settings module supplies this, the chokepoint
103
105
  * authorizes THIS permission instead of the scope-derived one — still a mandatory
104
- * `authorize()` call before any write (INV-07 fail-closed discipline is not relaxed, only
106
+ * `authorize` call before any write ( fail-closed discipline is not relaxed, only
105
107
  * which permission string is checked).
106
- */
108
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
109
+ */
107
110
  requiredPermissionOverride?: string | undefined;
108
111
  };
109
112
  }
110
- /** REQ-04/REQ-13 chokepoint write: authorize -> validate -> value + revision, same tx. */
113
+ /** chokepoint write: authorize -> validate -> value + revision, same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
111
114
  export declare function set(required: SetValueRequired): Promise<{
112
115
  value: JsonValue;
113
116
  revisionSeq: number;
@@ -121,7 +124,7 @@ export interface ClearValueRequired {
121
124
  workspaceId?: UUID | undefined;
122
125
  principalId?: UUID | undefined;
123
126
  callerPrincipalId: UUID;
124
- /** Set by `resetNamespace` when looping `clear()` in its own reset-authorized context (R3-01) — bypasses the inner authorize() re-check, still writes a normal revision. */
127
+ /** Set by `resetNamespace` when looping `clear` in its own reset-authorized context — bypasses the inner authorize re-check, still writes a normal revision. See docs/decisions/DR-003-settings-ledger-invariants.md. */
125
128
  skipAuthorize?: boolean | undefined;
126
129
  /** Set by a caller that has ALREADY opened a transaction around this write —
127
130
  * see `resetNamespace`. Explicit rather than an ambient depth counter,
@@ -132,7 +135,7 @@ export interface ClearValueRequired {
132
135
  authWorkspaceId?: UUID | undefined;
133
136
  };
134
137
  }
135
- /** REQ-04/REQ-13 chokepoint write: authorize -> value + revision (state='cleared'), same tx. */
138
+ /** chokepoint write: authorize -> value + revision (state='cleared'), same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
136
139
  export declare function clear(required: ClearValueRequired): Promise<{
137
140
  revisionSeq: number;
138
141
  }>;
@@ -149,15 +152,16 @@ export interface ResetNamespaceRequired {
149
152
  };
150
153
  }
151
154
  /**
152
- * EC-09/R3-01 — an explicit, human-invoked orchestrator: authorize
153
- * the matching `settings.reset.*` permission once, then loop `clear()` for
155
+ * — an explicit, human-invoked orchestrator: authorize
156
+ * the matching `settings.reset.*` permission once, then loop `clear` for
154
157
  * every setting in the namespace in the reset-authorized internal context
155
158
  * (`skipAuthorize: true`) — the outer reset permission is sufficient on its
156
159
  * own; each inner clear still emits its own `op='clear'` revision.
157
160
  *
158
- * `revisionSeqs` collects each inner `clear()` call's own `revisionSeq` so a
161
+ * `revisionSeqs` collects each inner `clear` call's own `revisionSeq` so a
159
162
  * host's admin HTTP route can surface the full contract without a second
160
163
  * read. Existing callers that only read `.clearedCount` are unaffected.
164
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
161
165
  */
162
166
  export declare function resetNamespace(required: ResetNamespaceRequired, keysInNamespace: string[]): Promise<{
163
167
  clearedCount: number;
@@ -177,7 +181,7 @@ export interface RenameDefinitionRequired {
177
181
  };
178
182
  }
179
183
  /**
180
- * The rename mechanism (AC-09, EC-06): a same-tx pair —
184
+ * The rename mechanism : a same-tx pair —
181
185
  * (1) UPDATE the active definition row's `(namespace,key)` to the new name,
182
186
  * same `setting_id`/`version` (ledgered `op='alias'`); (2) INSERT a fresh v1
183
187
  * alias marker at the OLD name pointing at the new name. Because identity
@@ -191,6 +195,7 @@ export interface RenameDefinitionRequired {
191
195
  * Never touches `schema`/`defaultValue`/`scopes` -- a pure rename can never
192
196
  * trigger `RENAME_RETYPE_CONFLICT` by construction; that guard lives in
193
197
  * `retypeDefinition` ("no rename+retype in one op").
198
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
194
199
  */
195
200
  export declare function renameDefinition(required: RenameDefinitionRequired): Promise<{
196
201
  settingId: string;
@@ -204,7 +209,7 @@ export interface RetypeDefinitionRequired {
204
209
  workspaceId: UUID | null;
205
210
  schema: SettingValueSchema;
206
211
  defaultValue: JsonValue | null;
207
- /** A total coercer id/tag (EC-08 registry in `settings.ts`) for reading values recorded under any prior version. */
212
+ /** A total coercer id/tag ( registry in `settings.ts`) for reading values recorded under any prior version. See docs/decisions/DR-003-settings-ledger-invariants.md. */
208
213
  coercionTag: string;
209
214
  /**
210
215
  * Present only to detect a combined rename+retype request;
@@ -220,7 +225,7 @@ export interface RetypeDefinitionRequired {
220
225
  };
221
226
  }
222
227
  /**
223
- * The retype mechanism (AC-10, EC-05): a same-tx pair --
228
+ * The retype mechanism : a same-tx pair --
224
229
  * (1) UPDATE the prior active version's `status` to `deprecated` FIRST (else
225
230
  * the insert in step 2 collides with the one-active-row-per-slot
226
231
  * invariant); (2) INSERT the new `version+1` row as `active`, same
@@ -228,6 +233,7 @@ export interface RetypeDefinitionRequired {
228
233
  * (2..N) already carries a total coercer -- `coercionTag` is `null` only for
229
234
  * version 1 by construction (`types.ts`), so this walks 2..N and requires
230
235
  * each to be non-null.
236
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
231
237
  */
232
238
  export declare function retypeDefinition(required: RetypeDefinitionRequired): Promise<{
233
239
  settingId: string;
@@ -1,16 +1,18 @@
1
+ import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
1
2
  import { AliasDepthExceededError, DefinitionInvalidError, DefinitionNotFoundError, DefinitionTombstonedError, ForbiddenError, PrincipalNotFoundError, RenameRetypeConflictError, ScopeNotAllowedError, ValueValidationFailedError, } from "./errors.js";
2
3
  import { invalidateDefinitionNamespaceCache, resolveDefinitionRaw, validateDefinitionInput, validateValueAgainstSchema, } from "./settings.js";
3
4
  import { SCOPE_BIT } from "./types.js";
4
5
  /**
5
- * behavior.spec.md §1.3 / REQ-06 `[internal-invariant]` — the self-vs-other
6
+ * behavior.spec.md §1.3 / `[internal-invariant]` — the self-vs-other
6
7
  * permission derivation. `targetPrincipalId` omitted or equal to the caller
7
8
  * -> `settings.user.self.write`; any other principal -> `settings.user.write`.
8
9
  * A divergence between this function and any other implementation of the
9
- * rule is a fail-open authorization bug (Red-Team RT-003) — this is the only
10
+ * rule is a fail-open authorization bug (Red-Team ) — this is the only
10
11
  * place the rule may be encoded.
11
12
  *
12
13
  * @complexity O(1), pure.
13
14
  * @overallScore 100
15
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
14
16
  */
15
17
  export function deriveRequiredPermission(input) {
16
18
  if (input.scope === "global")
@@ -20,7 +22,7 @@ export function deriveRequiredPermission(input) {
20
22
  const isSelf = input.targetPrincipalId == null || input.targetPrincipalId === input.callerPrincipalId;
21
23
  return isSelf ? "settings.user.self.write" : "settings.user.write";
22
24
  }
23
- /** REQ-02/REQ-09 chokepoint write: authorize `settings.definitions.manage` -> validate -> write + revision, one per definition, each its own tx. */
25
+ /** chokepoint write: authorize `settings.definitions.manage` -> validate -> write + revision, one per definition, each its own tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
24
26
  export async function registerDefinitions(required) {
25
27
  const { deps, input } = required;
26
28
  const authResult = await deps.authorize({
@@ -38,7 +40,7 @@ export async function registerDefinitions(required) {
38
40
  if (!validation.valid)
39
41
  throw validation.error;
40
42
  await deps.repo.transaction(async () => {
41
- const now = deps.clock.nowIso();
43
+ const now = kernelNowIso({ clock: deps.clock });
42
44
  const settingId = deps.ids.newId();
43
45
  await deps.repo.saveDefinition({
44
46
  settingId,
@@ -128,10 +130,11 @@ function assertTargetWorkspaceMatchesAuth(input) {
128
130
  `but the write targets workspace '${input.workspaceId}'`);
129
131
  }
130
132
  /**
131
- * REQ-13/INV-09 — for scope=user writes targeting another principal, verify
133
+ * — for scope=user writes targeting another principal, verify
132
134
  * that principal resolves to an active user whose own `workspace_id` equals
133
135
  * the request's `workspaceId` (structural scoping — a principal
134
136
  * belongs to exactly one workspace, not a membership join).
137
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
135
138
  */
136
139
  async function assertTargetPrincipalInWorkspace(deps, input) {
137
140
  if (input.scope !== "user")
@@ -140,12 +143,12 @@ async function assertTargetPrincipalInWorkspace(deps, input) {
140
143
  return;
141
144
  if (!input.workspaceId)
142
145
  return;
143
- const principal = await deps.principals.findById({ workspaceId: input.workspaceId, id: input.principalId });
144
- if (!principal || principal.status === "disabled") {
146
+ const principal = await deps.principals.findActiveById({ workspaceId: input.workspaceId, id: input.principalId });
147
+ if (!principal) {
145
148
  throw new PrincipalNotFoundError(`principal '${input.principalId}' was not found in workspace '${input.workspaceId}'`, input.principalId, input.workspaceId);
146
149
  }
147
150
  }
148
- /** REQ-04/REQ-13 chokepoint write: authorize -> validate -> value + revision, same tx. */
151
+ /** chokepoint write: authorize -> validate -> value + revision, same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
149
152
  export async function set(required) {
150
153
  const { deps, input } = required;
151
154
  const permission = input.requiredPermissionOverride ??
@@ -171,7 +174,7 @@ export async function set(required) {
171
174
  }
172
175
  await assertTargetPrincipalInWorkspace(deps, input);
173
176
  const result = await deps.repo.transaction(async () => {
174
- const now = deps.clock.nowIso();
177
+ const now = kernelNowIso({ clock: deps.clock });
175
178
  const revisionSeq = await deps.repo.appendRevision({
176
179
  entityKind: "value",
177
180
  settingId: definition.settingId,
@@ -219,7 +222,7 @@ export async function set(required) {
219
222
  });
220
223
  return result;
221
224
  }
222
- /** REQ-04/REQ-13 chokepoint write: authorize -> value + revision (state='cleared'), same tx. */
225
+ /** chokepoint write: authorize -> value + revision (state='cleared'), same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
223
226
  export async function clear(required) {
224
227
  const { deps, input } = required;
225
228
  if (!input.skipAuthorize) {
@@ -243,7 +246,7 @@ export async function clear(required) {
243
246
  const definition = await resolveScopedDefinitionOrThrow(deps, input);
244
247
  await assertTargetPrincipalInWorkspace(deps, input);
245
248
  const runClear = async () => {
246
- const now = deps.clock.nowIso();
249
+ const now = kernelNowIso({ clock: deps.clock });
247
250
  const revisionSeq = await deps.repo.appendRevision({
248
251
  entityKind: "value",
249
252
  settingId: definition.settingId,
@@ -295,15 +298,16 @@ export async function clear(required) {
295
298
  return result;
296
299
  }
297
300
  /**
298
- * EC-09/R3-01 — an explicit, human-invoked orchestrator: authorize
299
- * the matching `settings.reset.*` permission once, then loop `clear()` for
301
+ * — an explicit, human-invoked orchestrator: authorize
302
+ * the matching `settings.reset.*` permission once, then loop `clear` for
300
303
  * every setting in the namespace in the reset-authorized internal context
301
304
  * (`skipAuthorize: true`) — the outer reset permission is sufficient on its
302
305
  * own; each inner clear still emits its own `op='clear'` revision.
303
306
  *
304
- * `revisionSeqs` collects each inner `clear()` call's own `revisionSeq` so a
307
+ * `revisionSeqs` collects each inner `clear` call's own `revisionSeq` so a
305
308
  * host's admin HTTP route can surface the full contract without a second
306
309
  * read. Existing callers that only read `.clearedCount` are unaffected.
310
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
307
311
  */
308
312
  export async function resetNamespace(required, keysInNamespace) {
309
313
  const { deps, input } = required;
@@ -392,7 +396,7 @@ async function resolveActiveDefinitionOrThrow(deps, input, verb) {
392
396
  return current;
393
397
  }
394
398
  /**
395
- * The rename mechanism (AC-09, EC-06): a same-tx pair —
399
+ * The rename mechanism : a same-tx pair —
396
400
  * (1) UPDATE the active definition row's `(namespace,key)` to the new name,
397
401
  * same `setting_id`/`version` (ledgered `op='alias'`); (2) INSERT a fresh v1
398
402
  * alias marker at the OLD name pointing at the new name. Because identity
@@ -406,6 +410,7 @@ async function resolveActiveDefinitionOrThrow(deps, input, verb) {
406
410
  * Never touches `schema`/`defaultValue`/`scopes` -- a pure rename can never
407
411
  * trigger `RENAME_RETYPE_CONFLICT` by construction; that guard lives in
408
412
  * `retypeDefinition` ("no rename+retype in one op").
413
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
409
414
  */
410
415
  export async function renameDefinition(required) {
411
416
  const { deps, input } = required;
@@ -426,7 +431,7 @@ export async function renameDefinition(required) {
426
431
  throw new DefinitionInvalidError(`rename target '${input.newNamespace}.${input.newKey}' is already in use`);
427
432
  }
428
433
  const result = await deps.repo.transaction(async () => {
429
- const now = deps.clock.nowIso();
434
+ const now = kernelNowIso({ clock: deps.clock });
430
435
  // Step 1: move the active row to the new name -- same setting_id, same version.
431
436
  await deps.repo.saveDefinition({ ...current, namespace: input.newNamespace, key: input.newKey, updatedAt: now });
432
437
  // Sequential rename: retarget every prior marker pointing at the OLD name to the NEW name.
@@ -488,7 +493,7 @@ export async function renameDefinition(required) {
488
493
  return result;
489
494
  }
490
495
  /**
491
- * The retype mechanism (AC-10, EC-05): a same-tx pair --
496
+ * The retype mechanism : a same-tx pair --
492
497
  * (1) UPDATE the prior active version's `status` to `deprecated` FIRST (else
493
498
  * the insert in step 2 collides with the one-active-row-per-slot
494
499
  * invariant); (2) INSERT the new `version+1` row as `active`, same
@@ -496,6 +501,7 @@ export async function renameDefinition(required) {
496
501
  * (2..N) already carries a total coercer -- `coercionTag` is `null` only for
497
502
  * version 1 by construction (`types.ts`), so this walks 2..N and requires
498
503
  * each to be non-null.
504
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
499
505
  */
500
506
  export async function retypeDefinition(required) {
501
507
  const { deps, input } = required;
@@ -514,7 +520,7 @@ export async function retypeDefinition(required) {
514
520
  }
515
521
  }
516
522
  const result = await deps.repo.transaction(async () => {
517
- const now = deps.clock.nowIso();
523
+ const now = kernelNowIso({ clock: deps.clock });
518
524
  const newVersion = current.version + 1;
519
525
  // Step 1: deprecate the prior active version first.
520
526
  await deps.repo.saveDefinition({ ...current, status: "deprecated", updatedAt: now });
@@ -606,7 +612,7 @@ export async function reconcileDefinitionDefault(required) {
606
612
  throw new ValueValidationFailedError(`cannot reconcile the default of '${input.namespace}.${input.key}': the source default does not satisfy the definition's own schema (type '${current.schema.type}')`);
607
613
  }
608
614
  await deps.repo.transaction(async () => {
609
- const now = deps.clock.nowIso();
615
+ const now = kernelNowIso({ clock: deps.clock });
610
616
  await deps.repo.saveDefinition({ ...current, defaultValue: input.defaultValue, updatedAt: now });
611
617
  await deps.repo.appendRevision({
612
618
  entityKind: "definition",
@@ -638,7 +644,7 @@ async function transitionDefinitionStatus(deps, input, target) {
638
644
  await authorizeDefinitionsManage(deps, input.callerPrincipalId, input.authWorkspaceId);
639
645
  const current = await resolveActiveDefinitionOrThrow(deps, input, target.revisionOp);
640
646
  const result = await deps.repo.transaction(async () => {
641
- const now = deps.clock.nowIso();
647
+ const now = kernelNowIso({ clock: deps.clock });
642
648
  await deps.repo.saveDefinition({ ...current, status: target.status, updatedAt: now });
643
649
  await deps.repo.appendRevision({
644
650
  entityKind: "definition",