@cosmicdrift/kumiko-bundled-features 0.236.0 → 0.237.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 (205) hide show
  1. package/package.json +9 -9
  2. package/src/admin-shell/feature.ts +4 -0
  3. package/src/agent-tools/__tests__/agent-doc-lint.test.ts +53 -1
  4. package/src/audit/feature.ts +4 -0
  5. package/src/audit/handlers/details.query.ts +2 -0
  6. package/src/audit/handlers/list.query.ts +2 -0
  7. package/src/auth-email-password/handlers/change-password.write.ts +2 -0
  8. package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +1 -0
  9. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -0
  10. package/src/auth-email-password/handlers/invite-accept.write.ts +1 -0
  11. package/src/auth-email-password/handlers/invite-create.write.ts +2 -0
  12. package/src/auth-email-password/handlers/invite-signup-complete.write.ts +1 -0
  13. package/src/auth-email-password/handlers/login.write.ts +2 -0
  14. package/src/auth-email-password/handlers/logout.write.ts +2 -0
  15. package/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -0
  16. package/src/auth-email-password/handlers/request-email-verification.write.ts +2 -0
  17. package/src/auth-email-password/handlers/request-password-reset.write.ts +2 -0
  18. package/src/auth-email-password/handlers/reset-password.write.ts +1 -0
  19. package/src/auth-email-password/handlers/self-registration-status.query.ts +2 -0
  20. package/src/auth-email-password/handlers/signup-confirm.write.ts +1 -0
  21. package/src/auth-email-password/handlers/signup-request.write.ts +2 -0
  22. package/src/auth-email-password/handlers/token-request-handler.ts +4 -0
  23. package/src/auth-email-password/handlers/verify-email.write.ts +1 -0
  24. package/src/auth-mfa/feature.ts +2 -0
  25. package/src/auth-mfa/handlers/disable.write.ts +3 -0
  26. package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +2 -0
  27. package/src/auth-mfa/handlers/enable-confirm.write.ts +2 -0
  28. package/src/auth-mfa/handlers/enable-start-preauth.write.ts +2 -0
  29. package/src/auth-mfa/handlers/enable-start.write.ts +2 -0
  30. package/src/auth-mfa/handlers/regenerate-recovery.write.ts +3 -0
  31. package/src/auth-mfa/handlers/status.query.ts +2 -0
  32. package/src/auth-mfa/handlers/verify.write.ts +2 -0
  33. package/src/auth-mfa/schema/user-mfa.ts +2 -0
  34. package/src/billing-foundation/handlers/create-checkout-session.write.ts +2 -0
  35. package/src/billing-foundation/handlers/create-portal-session.write.ts +2 -0
  36. package/src/billing-foundation/handlers/list-subscriptions.query.ts +2 -0
  37. package/src/billing-foundation/handlers/process-event.write.ts +1 -0
  38. package/src/cap-counter/entity.ts +2 -0
  39. package/src/cap-counter/feature.ts +7 -1
  40. package/src/cap-counter/handlers/get-counter.query.ts +2 -0
  41. package/src/cap-counter/handlers/increment-rolling.write.ts +1 -0
  42. package/src/cap-counter/handlers/increment.write.ts +1 -0
  43. package/src/cap-counter/handlers/mark-soft-warned.write.ts +1 -0
  44. package/src/cap-overview/handlers/caps-usage.query.ts +2 -0
  45. package/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -0
  46. package/src/cap-overview/handlers/tenant-options.query.ts +2 -0
  47. package/src/channel-in-app/handlers/inbox.query.ts +2 -0
  48. package/src/channel-in-app/handlers/mark-all-read.write.ts +2 -0
  49. package/src/channel-in-app/handlers/mark-read.write.ts +2 -0
  50. package/src/channel-in-app/handlers/unread-count.query.ts +2 -0
  51. package/src/compliance-profiles/handlers/for-tenant.query.ts +2 -0
  52. package/src/compliance-profiles/handlers/list-profiles.query.ts +2 -0
  53. package/src/compliance-profiles/handlers/needs-profile.query.ts +2 -0
  54. package/src/compliance-profiles/handlers/set-profile.write.ts +2 -0
  55. package/src/compliance-profiles/handlers/sub-processors.query.ts +2 -0
  56. package/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +2 -0
  57. package/src/config/handlers/cascade.query.ts +2 -0
  58. package/src/config/handlers/readiness.query.ts +2 -0
  59. package/src/config/handlers/reset.write.ts +3 -0
  60. package/src/config/handlers/schema.query.ts +2 -0
  61. package/src/config/handlers/set.write.ts +2 -0
  62. package/src/config/handlers/values.query.ts +2 -0
  63. package/src/crypto-shredding/handlers/forget-subject.write.ts +3 -0
  64. package/src/custom-fields/entity.ts +2 -0
  65. package/src/custom-fields/feature.ts +2 -0
  66. package/src/custom-fields/handlers/clear-custom-field.write.ts +2 -0
  67. package/src/custom-fields/handlers/define-system-field.write.ts +2 -0
  68. package/src/custom-fields/handlers/define-tenant-field.write.ts +2 -0
  69. package/src/custom-fields/handlers/delete-system-field.write.ts +3 -0
  70. package/src/custom-fields/handlers/delete-tenant-field.write.ts +3 -0
  71. package/src/custom-fields/handlers/set-custom-field.write.ts +2 -0
  72. package/src/custom-fields/handlers/update-tenant-field.write.ts +2 -0
  73. package/src/data-retention/handlers/policy-for.query.ts +2 -0
  74. package/src/delivery/handlers/log.query.ts +2 -0
  75. package/src/delivery/handlers/preferences.query.ts +2 -0
  76. package/src/delivery/handlers/set-preference.write.ts +2 -0
  77. package/src/feature-toggles/handlers/list.query.ts +2 -0
  78. package/src/feature-toggles/handlers/registered.query.ts +2 -0
  79. package/src/feature-toggles/handlers/set.write.ts +2 -0
  80. package/src/file-derivatives/handlers/public-variant.query.ts +1 -0
  81. package/src/folders/entity.ts +4 -0
  82. package/src/folders/feature.ts +35 -5
  83. package/src/folders/handlers/clear-folder.write.ts +2 -0
  84. package/src/folders/handlers/delete-folder.write.ts +3 -0
  85. package/src/folders/handlers/set-folder.write.ts +2 -0
  86. package/src/form-draft/handlers/discard.write.ts +3 -0
  87. package/src/form-draft/handlers/get.query.ts +2 -0
  88. package/src/form-draft/handlers/list.query.ts +2 -0
  89. package/src/form-draft/handlers/save.write.ts +2 -0
  90. package/src/inbound-mail-foundation/handlers/connect-account.write.ts +2 -0
  91. package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +3 -0
  92. package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -0
  93. package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +2 -0
  94. package/src/inbound-mail-foundation/handlers/list-messages.query.ts +2 -0
  95. package/src/inbound-mail-foundation/handlers/update-account.write.ts +2 -0
  96. package/src/jobs/feature.ts +4 -0
  97. package/src/jobs/handlers/catalog.query.ts +2 -0
  98. package/src/jobs/handlers/detail.query.ts +2 -0
  99. package/src/jobs/handlers/list.query.ts +2 -0
  100. package/src/jobs/handlers/retry.write.ts +2 -0
  101. package/src/jobs/handlers/trigger.write.ts +2 -0
  102. package/src/ledger/entity.ts +6 -0
  103. package/src/ledger/feature.ts +70 -10
  104. package/src/ledger/handlers/confirm-schedule-period.write.ts +2 -0
  105. package/src/ledger/handlers/create-transaction.write.ts +2 -0
  106. package/src/ledger/handlers/reports.query.ts +6 -0
  107. package/src/ledger/handlers/reverse-transaction.write.ts +2 -0
  108. package/src/managed-pages/feature.ts +4 -23
  109. package/src/managed-pages/handlers/branding.query.ts +2 -0
  110. package/src/managed-pages/handlers/by-slug.query.ts +2 -0
  111. package/src/managed-pages/handlers/by-tenant-published.query.ts +2 -0
  112. package/src/managed-pages/handlers/page-crud.ts +49 -0
  113. package/src/managed-pages/handlers/set.write.ts +2 -0
  114. package/src/managed-pages/table.ts +2 -0
  115. package/src/notes-history/entity.ts +2 -0
  116. package/src/notes-history/feature.ts +7 -1
  117. package/src/notes-history/handlers/add-note.write.ts +2 -0
  118. package/src/personal-access-tokens/feature.ts +2 -0
  119. package/src/personal-access-tokens/handlers/available-scopes.query.ts +2 -0
  120. package/src/personal-access-tokens/handlers/create.write.ts +2 -0
  121. package/src/personal-access-tokens/handlers/list.query.ts +2 -0
  122. package/src/personal-access-tokens/handlers/revoke.write.ts +3 -0
  123. package/src/rate-limiting/handlers/status.query.ts +2 -0
  124. package/src/readiness/handlers/status.query.ts +2 -0
  125. package/src/secrets/handlers/delete.write.ts +3 -0
  126. package/src/secrets/handlers/list.query.ts +2 -0
  127. package/src/secrets/handlers/set.write.ts +2 -0
  128. package/src/secrets/table.ts +2 -0
  129. package/src/seo/handlers/seo-config.query.ts +2 -0
  130. package/src/sessions/handlers/detail.query.ts +2 -0
  131. package/src/sessions/handlers/list.query.ts +2 -0
  132. package/src/sessions/handlers/mine.query.ts +2 -0
  133. package/src/sessions/handlers/revoke-all-for-user.write.ts +3 -0
  134. package/src/sessions/handlers/revoke-all-others.write.ts +3 -0
  135. package/src/sessions/handlers/revoke.write.ts +3 -0
  136. package/src/tags/entity.ts +4 -0
  137. package/src/tags/feature.ts +39 -6
  138. package/src/tags/handlers/assign-tag.write.ts +2 -0
  139. package/src/tags/handlers/create-tag.write.ts +2 -0
  140. package/src/tags/handlers/delete-tag.write.ts +3 -0
  141. package/src/tags/handlers/remove-tag.write.ts +2 -0
  142. package/src/tags/handlers/update-tag.write.ts +2 -0
  143. package/src/template-resolver/handlers/by-slug.query.ts +2 -0
  144. package/src/template-resolver/handlers/by-tenant.query.ts +2 -0
  145. package/src/template-resolver/handlers/collection-item.query.ts +4 -0
  146. package/src/template-resolver/handlers/collection-list.query.ts +4 -0
  147. package/src/template-resolver/handlers/collection-set.write.ts +4 -0
  148. package/src/template-resolver/handlers/find-by-id.query.ts +2 -0
  149. package/src/template-resolver/handlers/list.query.ts +2 -0
  150. package/src/template-resolver/handlers/set.write.ts +2 -0
  151. package/src/template-resolver/handlers/toggle-status.write.ts +12 -3
  152. package/src/template-resolver/handlers/upsert-system.write.ts +2 -0
  153. package/src/template-resolver/handlers/upsert-tenant.write.ts +2 -0
  154. package/src/tenant/feature.ts +15 -3
  155. package/src/tenant/handlers/active-tenant-ids.query.ts +2 -0
  156. package/src/tenant/handlers/add-member.write.ts +2 -0
  157. package/src/tenant/handlers/cancel-invitation.write.ts +2 -0
  158. package/src/tenant/handlers/create.write.ts +2 -0
  159. package/src/tenant/handlers/invitations.query.ts +2 -0
  160. package/src/tenant/handlers/list.query.ts +2 -0
  161. package/src/tenant/handlers/me.query.ts +2 -0
  162. package/src/tenant/handlers/members.query.ts +2 -0
  163. package/src/tenant/handlers/memberships.query.ts +2 -0
  164. package/src/tenant/handlers/remove-member.write.ts +3 -0
  165. package/src/tenant/handlers/resolve-user-ids.query.ts +1 -0
  166. package/src/tenant/handlers/team-list.query.ts +2 -0
  167. package/src/tenant/handlers/toggle-enabled.write.ts +3 -0
  168. package/src/tenant/handlers/update-member-roles.write.ts +2 -0
  169. package/src/tenant/handlers/update.write.ts +2 -0
  170. package/src/tenant/schema/tenant.ts +2 -0
  171. package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -0
  172. package/src/tenant-lifecycle/handlers/request-destruction.write.ts +3 -0
  173. package/src/tier-engine/entity.ts +2 -0
  174. package/src/tier-engine/feature.ts +25 -3
  175. package/src/tier-engine/handlers/active-tier.query.ts +2 -0
  176. package/src/tier-engine/handlers/get-tenant-tier.query.ts +2 -0
  177. package/src/tier-engine/handlers/set-tenant-tier.write.ts +2 -0
  178. package/src/user/handlers/create.write.ts +2 -0
  179. package/src/user/handlers/detail.query.ts +2 -0
  180. package/src/user/handlers/find-for-auth.query.ts +1 -0
  181. package/src/user/handlers/list.query.ts +2 -0
  182. package/src/user/handlers/me.query.ts +2 -0
  183. package/src/user/handlers/update.write.ts +2 -0
  184. package/src/user/schema/user.ts +2 -0
  185. package/src/user-data-rights/feature.ts +2 -0
  186. package/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -0
  187. package/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -0
  188. package/src/user-data-rights/handlers/download-attempt-list.query.ts +6 -1
  189. package/src/user-data-rights/handlers/download-by-job.query.ts +2 -0
  190. package/src/user-data-rights/handlers/download-by-token.query.ts +1 -0
  191. package/src/user-data-rights/handlers/export-job-detail.query.ts +2 -0
  192. package/src/user-data-rights/handlers/export-job-list.query.ts +2 -0
  193. package/src/user-data-rights/handlers/export-status.query.ts +2 -0
  194. package/src/user-data-rights/handlers/lift-restriction.write.ts +2 -0
  195. package/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -0
  196. package/src/user-data-rights/handlers/my-audit-log.query.ts +2 -0
  197. package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +2 -0
  198. package/src/user-data-rights/handlers/request-deletion.write.ts +3 -0
  199. package/src/user-data-rights/handlers/request-export.write.ts +2 -0
  200. package/src/user-data-rights/handlers/restrict-account.write.ts +3 -0
  201. package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -0
  202. package/src/user-data-rights/schema/download-attempt.ts +2 -0
  203. package/src/user-data-rights/schema/export-job.ts +2 -0
  204. package/src/user-profile/handlers/change-email.write.ts +2 -0
  205. package/src/workflow-runner/handlers/resume-run.write.ts +1 -0
@@ -20,6 +20,8 @@ export const forTenantQuery = defineQueryHandler({
20
20
  name: "for-tenant",
21
21
  schema: z.object({}),
22
22
  access: { openToAll: true },
23
+ description:
24
+ "Returns the effective compliance profile for the caller's tenant with any tenant override merged in, falling back to minimal-no-region plus a no-profile-selected warning when the tenant has not picked one yet.",
23
25
  handler: async (query, ctx): Promise<EffectiveComplianceProfile> => {
24
26
  const row = (await fetchOne(ctx.db, tenantComplianceProfileTable, {
25
27
  tenantId: query.user.tenantId,
@@ -17,6 +17,8 @@ export const listProfilesQuery = defineQueryHandler({
17
17
  name: "list-profiles",
18
18
  schema: z.object({}),
19
19
  access: { openToAll: true },
20
+ description:
21
+ "Lists the compliance profiles a tenant can choose from (key, region, label, supervisory-authority contact, notification languages), for rendering the onboarding profile picker; the minimal-no-region fallback is excluded because it is not selectable.",
20
22
  handler: async (): Promise<{ profiles: readonly ComplianceProfileSummary[] }> => {
21
23
  return {
22
24
  profiles: SELECTABLE_PROFILE_KEYS.map(toSummary),
@@ -29,6 +29,8 @@ export function createNeedsProfileQuery(pickerReachableRoles: readonly string[])
29
29
  name: "needs-profile",
30
30
  schema: z.object({}),
31
31
  access: { roles: [ROLES.TenantAdmin] },
32
+ description:
33
+ "Answers whether the calling tenant admin still has to pick a compliance profile, for the onboarding banner in their own tenant; reports needsSelection false once a profile exists or when the caller's roles cannot reach the picker screen.",
32
34
  handler: async (query, ctx): Promise<NeedsProfileResponse> => {
33
35
  const row = (await fetchOne(ctx.db, tenantComplianceProfileTable, {
34
36
  tenantId: query.user.tenantId,
@@ -59,6 +59,8 @@ export const setProfileWrite = defineWriteHandler({
59
59
  // SystemAdmin kann Profile fuer Customer-Setup setzen (Plattform-
60
60
  // Operator-Pfad). TenantAdmin nur fuer eigenen Tenant.
61
61
  access: { roles: access.admin },
62
+ description:
63
+ "Sets or replaces a tenant's compliance profile key plus an optional JSON override, for onboarding or a later region change; a system admin may target a different tenant through tenantIdOverride.",
62
64
  handler: async (event, ctx) => {
63
65
  const tenantOverride = event.payload.tenantIdOverride;
64
66
  const overrideDenied = crossTenantOverrideDenied(
@@ -25,6 +25,8 @@ export const subProcessorsQuery = defineQueryHandler({
25
25
  name: "sub-processors",
26
26
  schema: z.object({}),
27
27
  access: { roles: ["anonymous", "Member", "User", "TenantAdmin", "SystemAdmin"] },
28
+ description:
29
+ "Returns the publicly readable Kumiko sub-processor list split into active and planned entries, for the GDPR Art. 28(2) disclosure a privacy policy or data-processing agreement links to.",
28
30
  handler: async (): Promise<SubProcessorListResponse> => {
29
31
  return {
30
32
  active: [...getActiveSubProcessors()],
@@ -20,6 +20,8 @@ export const tenantsMissingProfileQuery = defineQueryHandler({
20
20
  name: "tenants-missing-profile",
21
21
  schema: z.object({}),
22
22
  access: { roles: [ROLES.SystemAdmin] },
23
+ description:
24
+ "Lists every enabled tenant that has never selected a compliance profile, for the platform operator sweeping the whole installation; needs-profile answers the same question but only for the caller's own tenant.",
23
25
  handler: async (_query, ctx): Promise<TenantsMissingProfileResponse> => {
24
26
  if (!ctx.systemDb) {
25
27
  throw new InternalError({
@@ -11,6 +11,8 @@ import { hasConfigAccess } from "../write-helpers";
11
11
 
12
12
  export const cascadeQuery = defineQueryHandler({
13
13
  name: "cascade",
14
+ description:
15
+ "Shows, per config key the caller may read, the value at every cascade level (user, tenant, system, app override, default) and which level won; use it to explain where a setting's current value comes from.",
14
16
  schema: z.object({
15
17
  keys: z.array(z.string()).optional(),
16
18
  }),
@@ -127,6 +127,8 @@ export async function collectMissingRequiredConfig(
127
127
  // about secrets. readiness:query:status (readiness feature) rolls both up.
128
128
  export const readinessQuery = defineQueryHandler({
129
129
  name: "readiness",
130
+ description:
131
+ "Lists the required config keys that are still unset for the caller's tenant, each with its scope and type; use it to find out what is missing before a feature can run.",
130
132
  schema: z.object({}),
131
133
  // Per-key read access enforced via hasConfigAccess inside the handler.
132
134
  access: { openToAll: true },
@@ -18,6 +18,9 @@ const executor = createEventStoreExecutor(configValuesTable, configValueEntity,
18
18
 
19
19
  export const resetWrite = defineWriteHandler({
20
20
  name: "reset",
21
+ description:
22
+ "Removes the stored config value for one key at the given scope so the key falls back to the next cascade level, and permanently deletes the stored credential for secrets-backed keys.",
23
+ agent: { risk: "high" },
21
24
  schema: z.object({
22
25
  key: z.string(),
23
26
  scope: scopeEnum.optional(),
@@ -4,6 +4,8 @@ import { hasConfigAccess } from "../write-helpers";
4
4
 
5
5
  export const schemaQuery = defineQueryHandler({
6
6
  name: "schema",
7
+ description:
8
+ "Returns the definitions of all config keys the caller may read (scope, type, default, bounds, required flag) without any values; use it to discover which settings exist and how they may be set.",
7
9
  schema: z.object({}),
8
10
  // Per-key read access enforced via hasConfigAccess inside the handler.
9
11
  access: { openToAll: true },
@@ -36,6 +36,8 @@ const executor = createEventStoreExecutor(configValuesTable, configValueEntity,
36
36
 
37
37
  export const setWrite = defineWriteHandler({
38
38
  name: "set",
39
+ description:
40
+ "Stores a config value for one key at the requested scope (user, tenant or system) after type, bounds and pattern validation, encrypting it when the key declares that; use it to change a setting.",
39
41
  schema: z.object({
40
42
  key: z.string(),
41
43
  value: z.union([z.string(), z.number(), z.boolean()]),
@@ -12,6 +12,8 @@ import { hasConfigAccess } from "../write-helpers";
12
12
 
13
13
  export const valuesQuery = defineQueryHandler({
14
14
  name: "values",
15
+ description:
16
+ "Returns the effective value, scope and winning source for every config key the caller may read, with encrypted and secret-backed values masked; use it to inspect the current settings.",
15
17
  schema: z.object({}),
16
18
  // Per-key read access enforced via hasConfigAccess inside the handler.
17
19
  access: { openToAll: true },
@@ -187,6 +187,9 @@ export const forgetSubjectWrite = defineWriteHandler({
187
187
  name: "forget-subject",
188
188
  schema: forgetSubjectSchema,
189
189
  access: { roles: [ROLES.DataProtectionOfficer, ROLES.SystemAdmin] },
190
+ description:
191
+ "Irreversibly crypto-shreds one user or tenant subject by erasing its encryption key, nulling its blind indexes, purging its search documents and closing the user's login, for supervisory-authority requests and operator recovery outside the automated Art. 17 cleanup pipeline.",
192
+ agent: { risk: "high" },
190
193
  handler: async (event, ctx) => {
191
194
  const kms = configuredPiiSubjectKms();
192
195
  if (!kms) {
@@ -36,6 +36,8 @@ import {
36
36
  // columns + events.
37
37
  export const fieldDefinitionEntity = createEntity({
38
38
  table: "read_custom_field_definitions",
39
+ description:
40
+ "One custom-field definition: the host entity it extends, the field key, its type and required/searchable flags, a display order, and the serialized field builder that carries the type options, default value and labels. Rows owned by the system tenant apply to every tenant; all others belong to the tenant that defined them.",
39
41
  // softDelete is required, NOT cosmetic: the aggregate-id is deterministic
40
42
  // (uuidv5(tenantId|entityName|fieldKey)), so deleting a definition leaves a
41
43
  // (created+deleted) event stream under that id. A hard delete would force the
@@ -163,6 +163,8 @@ function registerCustomFields(
163
163
  r.queryHandler(
164
164
  defineEntityListHandler("field-definition", fieldDefinitionEntity, {
165
165
  access: { roles: variant.fieldDefinitionListRoles },
166
+ description:
167
+ "Lists the custom-field definitions of the caller's own tenant; use it to discover which extra fields an entity accepts before setting or clearing a value on it.",
166
168
  }),
167
169
  );
168
170
 
@@ -27,6 +27,8 @@ export const clearCustomFieldHandler: WriteHandlerDef = {
27
27
  name: "clear-custom-field",
28
28
  schema: clearCustomFieldPayloadSchema,
29
29
  access: { roles: DEFAULT_VALUE_WRITE_ROLES },
30
+ description:
31
+ "Removes the stored value of one custom field from a single host entity row after re-checking that field's per-field write roles; use it to blank a field the user emptied instead of writing a null value.",
30
32
  handler: async (event, ctx) => {
31
33
  const payload = event.payload as ClearCustomFieldPayload; // @cast-boundary engine-payload
32
34
 
@@ -19,6 +19,8 @@ export const defineSystemFieldHandler: WriteHandlerDef = {
19
19
  name: "define-system-field",
20
20
  schema: defineFieldPayloadSchema,
21
21
  access: { roles: ["SystemAdmin"] },
22
+ description:
23
+ "Creates a custom-field definition under the system tenant so every tenant inherits the field on the named entity; use it for platform-mandated fields that individual tenants may fill in but never edit or delete.",
22
24
  handler: async (event, ctx) => {
23
25
  const payload = event.payload as DefineFieldPayload; // @cast-boundary engine-payload
24
26
 
@@ -53,6 +53,8 @@ export function createDefineTenantFieldHandler(
53
53
  name: "define-tenant-field",
54
54
  schema: defineFieldPayloadSchema,
55
55
  access: { roles: opts.roles ?? DEFAULT_FIELD_DEFINITION_WRITE_ROLES },
56
+ description:
57
+ "Creates a custom-field definition owned by the caller's own tenant on the named entity, rejecting the write once the tenant's definition quota is reached; use it when one tenant needs an extra field the other tenants must not see.",
56
58
  handler: async (event, ctx) => {
57
59
  const payload = event.payload as DefineFieldPayload; // @cast-boundary engine-payload
58
60
  const tenantId = event.user.tenantId;
@@ -14,6 +14,9 @@ export const deleteSystemFieldHandler: WriteHandlerDef = {
14
14
  name: "delete-system-field",
15
15
  schema: deleteFieldPayloadSchema,
16
16
  access: { roles: ["SystemAdmin"] },
17
+ description:
18
+ "Deletes a system-tenant custom-field definition and cascades an event that strips the now-orphaned values out of every tenant's host rows; use it to retire a platform-wide field for all tenants at once.",
19
+ agent: { risk: "high" },
17
20
  handler: async (event, ctx) => {
18
21
  const payload = event.payload as DeleteFieldPayload; // @cast-boundary engine-payload
19
22
 
@@ -31,6 +31,9 @@ export function createDeleteTenantFieldHandler(
31
31
  name: "delete-tenant-field",
32
32
  schema: deleteFieldPayloadSchema,
33
33
  access: { roles: opts.roles ?? DEFAULT_FIELD_DEFINITION_WRITE_ROLES },
34
+ description:
35
+ "Deletes one of the caller's own tenant's custom-field definitions and cascades an event that strips its orphaned values out of that tenant's host rows; use it to retire a field this tenant defined itself.",
36
+ agent: { risk: "high" },
34
37
  handler: async (event, ctx) => {
35
38
  const payload = event.payload as DeleteFieldPayload; // @cast-boundary engine-payload
36
39
  const tenantId = event.user.tenantId;
@@ -50,6 +50,8 @@ export const setCustomFieldHandler: WriteHandlerDef = {
50
50
  name: "set-custom-field",
51
51
  schema: setCustomFieldPayloadSchema,
52
52
  access: { roles: DEFAULT_VALUE_WRITE_ROLES },
53
+ description:
54
+ "Stores one custom-field value on a single host entity row after validating it against the field definition's declared type and per-field write roles; use it to save what a user entered into a custom field.",
53
55
  handler: async (event, ctx) => {
54
56
  const payload = event.payload as SetCustomFieldPayload; // @cast-boundary engine-payload
55
57
 
@@ -43,6 +43,8 @@ export function createUpdateTenantFieldHandler(
43
43
  name: "update-tenant-field",
44
44
  schema: updateFieldPayloadSchema,
45
45
  access: { roles: opts.roles ?? DEFAULT_FIELD_DEFINITION_WRITE_ROLES },
46
+ description:
47
+ "Replaces a tenant-owned custom-field definition with a complete new spec while refusing any change to the field's type; use it to relabel or re-constrain an existing field rather than to create or remove one.",
46
48
  handler: async (event, ctx) => {
47
49
  const payload = event.payload as UpdateFieldPayload; // @cast-boundary engine-payload
48
50
  const tenantId = event.user.tenantId;
@@ -23,6 +23,8 @@ export const policyForQuery = defineQueryHandler({
23
23
  entityName: z.string().min(1).max(100),
24
24
  }),
25
25
  access: { openToAll: true },
26
+ description:
27
+ "Resolves the effective retention policy for one entity name in the caller's tenant (keep-for duration plus delete or anonymize strategy) by layering the entity default, tenant preset and tenant override, so a forget flow or cleanup job knows how that data may be removed.",
26
28
  handler: async (query, ctx): Promise<EffectiveRetentionPolicy> => {
27
29
  const entityName = query.payload.entityName;
28
30
 
@@ -75,6 +75,8 @@ export const logQuery = definePagedQueryHandler({
75
75
  sortDirection: z.enum(["asc", "desc"]).optional(),
76
76
  }),
77
77
  access: { roles: access.admin },
78
+ description:
79
+ "Pages through recorded delivery attempts (notification type, channel, recipient, status, error, priority) for the caller's tenant, or across all tenants for a SystemAdmin; use it to investigate whether and why a notification reached someone.",
78
80
  handler: async (query, ctx) => {
79
81
  if (!ctx.systemDb) {
80
82
  throw new InternalError({ message: "delivery log handler requires ctx.systemDb" });
@@ -8,6 +8,8 @@ export const preferencesQuery = defineQueryHandler({
8
8
  name: "preferences",
9
9
  schema: z.object({}),
10
10
  access: { openToAll: true },
11
+ description:
12
+ "Returns the calling user's notification preference rows for their own tenant; use it to show which notification types and channels that user has enabled or muted.",
11
13
  handler: async (query, ctx) => {
12
14
  // delivery runs in system-scope, so ctx.db is not auto-tenant-filtered —
13
15
  // userId alone is not tenant-unique, so a userId-only filter here leaked preferences cross-tenant.
@@ -12,6 +12,8 @@ export const setPreferenceWrite = defineWriteHandler({
12
12
  }),
13
13
  // Every user manages their own preferences; tenant+user scoping is on the WHERE.
14
14
  access: { openToAll: true },
15
+ description:
16
+ "Enables or disables one notification type and channel combination for the calling user, where either side may be the wildcard * as a catch-all; use it when a user changes their own notification settings.",
15
17
  handler: async (event, ctx) => {
16
18
  const { notificationType, channel, enabled } = event.payload;
17
19
  const { id: userId, tenantId } = event.user;
@@ -10,6 +10,8 @@ import { globalFeatureStateTable } from "../global-feature-state-table";
10
10
  // effective state (registered features + their current override, if any).
11
11
  export const listQuery = defineQueryHandler({
12
12
  name: "list",
13
+ description:
14
+ "Lists only the features that carry an explicit platform-wide on/off override, with who flipped it and when; use it to see which toggles were changed away from their default.",
13
15
  schema: z.object({}),
14
16
  access: { roles: ["SystemAdmin"] },
15
17
  handler: async (_event, ctx) => {
@@ -20,6 +20,8 @@ function formatFlag(value: boolean | null): string {
20
20
 
21
21
  export const registeredQuery = defineQueryHandler({
22
22
  name: "registered",
23
+ description:
24
+ "Lists every registered feature with whether it is toggleable, its default, its explicit override, its dependencies and its effective on/off state; use it as the full feature inventory.",
23
25
  schema: z.object({}),
24
26
  access: { roles: ["SystemAdmin"] },
25
27
  handler: async (_event, ctx) => {
@@ -34,6 +34,8 @@ import type { GlobalFeatureToggleRuntime } from "../toggle-runtime";
34
34
  export function createSetWriteHandler(getRuntime: (() => GlobalFeatureToggleRuntime) | undefined) {
35
35
  return defineWriteHandler({
36
36
  name: "set",
37
+ description:
38
+ "Turns one registered, toggleable feature on or off platform-wide and applies the flip immediately; use it to enable or disable a feature for the whole installation.",
37
39
  schema: z.object({
38
40
  featureName: z.string().min(1),
39
41
  enabled: z.boolean(),
@@ -75,6 +75,7 @@ export const publicVariantQuery = defineQueryHandler({
75
75
  variant: z.string().min(1).max(64),
76
76
  }),
77
77
  access: { roles: ["anonymous", "User", "TenantAdmin", "SystemAdmin"] },
78
+ agent: { expose: false },
78
79
  // ponytail: "ip" trusts the first x-forwarded-for hop (buildRequestContextData
79
80
  // in request-id-middleware.ts) — this is the ONLY throttle on an anonymous,
80
81
  // internet-facing render/storage-cost route, so it assumes the deployment's
@@ -7,6 +7,8 @@ import { createEntity, createTextField } from "@cosmicdrift/kumiko-framework/eng
7
7
  // tenant-scoped.
8
8
  export const folderEntity = createEntity({
9
9
  table: "read_folders",
10
+ description:
11
+ "One folder of a tenant's hierarchical catalog: a name plus an optional parentId pointing at another folder, so the rows together form a tree whose roots have no parent.",
10
12
  fields: {
11
13
  name: createTextField({ required: true, maxLength: 64 }),
12
14
  // Parent folder id, or absent for a root folder. No FK (event-sourced); a
@@ -32,6 +34,8 @@ export const folderEntity = createEntity({
32
34
  // - entities in a folder → list assignments filter { field: "folderId", op: "eq" }
33
35
  export const folderAssignmentEntity = createEntity({
34
36
  table: "read_folder_assignments",
37
+ description:
38
+ "The membership row recording which folder one host entity, addressed by entityType and entityId, is filed in. At most one row exists per entity, so filing it elsewhere changes this row's folderId rather than adding a second.",
35
39
  softDelete: true,
36
40
  fields: {
37
41
  folderId: createTextField({ required: true, maxLength: 64 }),
@@ -58,18 +58,48 @@ function registerFolders(
58
58
 
59
59
  // Folder catalog — plain CRUD, no custom logic. update is rename (and, in a
60
60
  // later stage, reparent: it accepts changes.parentId, optimistic-locked).
61
- r.writeHandler(defineEntityCreateHandler("folder", folderEntity, { access }));
62
- r.writeHandler(defineEntityUpdateHandler("folder", folderEntity, { access }));
61
+ r.writeHandler(
62
+ defineEntityCreateHandler("folder", folderEntity, {
63
+ access,
64
+ description:
65
+ "Creates a folder in the caller's tenant folder tree, optionally nested under a parent folder; use it to grow the folder catalog, not to file an entity into a folder.",
66
+ }),
67
+ );
68
+ r.writeHandler(
69
+ defineEntityUpdateHandler("folder", folderEntity, {
70
+ access,
71
+ description:
72
+ "Renames a folder or moves it under a different parent folder; use it to reorganise the catalog itself, which leaves the entities filed in that folder where they are.",
73
+ }),
74
+ );
63
75
  // Custom, not defineEntityDeleteHandler: blocks the delete when folder-
64
76
  // assignments still point at this folder (658/1) — see delete-folder.write.ts.
65
77
  r.writeHandler(createDeleteFolderHandler(access));
66
- r.queryHandler(defineEntityListHandler("folder", folderEntity, { access }));
67
- r.queryHandler(defineEntityDetailHandler("folder", folderEntity, { access }));
78
+ r.queryHandler(
79
+ defineEntityListHandler("folder", folderEntity, {
80
+ access,
81
+ description:
82
+ "Lists the caller's tenant folders with their names and parent ids so the whole folder tree can be reassembled; use it to render a folder tree or to let a user pick a folder.",
83
+ }),
84
+ );
85
+ r.queryHandler(
86
+ defineEntityDetailHandler("folder", folderEntity, {
87
+ access,
88
+ description:
89
+ "Reads one folder of the caller's tenant by id, returning its name and parent id; use it to confirm a folder id is real before filing something into it.",
90
+ }),
91
+ );
68
92
 
69
93
  // Single-membership assignment — hand-written (deterministic id + move/restore).
70
94
  r.writeHandler(createSetFolderHandler(access));
71
95
  r.writeHandler(createClearFolderHandler(access));
72
- r.queryHandler(defineEntityListHandler("folder-assignment", folderAssignmentEntity, { access }));
96
+ r.queryHandler(
97
+ defineEntityListHandler("folder-assignment", folderAssignmentEntity, {
98
+ access,
99
+ description:
100
+ "Lists the folder-membership rows of the caller's tenant; filter on entityId to learn which folder one entity sits in, or on folderId to list everything filed into a folder.",
101
+ }),
102
+ );
73
103
  }
74
104
 
75
105
  export const foldersFeature = defineFeature(FOLDERS_FEATURE_NAME, (r) =>
@@ -14,6 +14,8 @@ export function createClearFolderHandler(
14
14
  name: "clear-folder",
15
15
  schema: clearFolderPayloadSchema,
16
16
  access,
17
+ description:
18
+ "Unfiles a host entity so it sits in no folder at all, reporting success when it was already unfiled; use it to take an entity out of its folder without touching the folder itself.",
17
19
  handler: async (event, ctx) => {
18
20
  const payload = event.payload as ClearFolderPayload; // @cast-boundary engine-payload
19
21
  const id = folderAssignmentAggregateId(
@@ -20,6 +20,9 @@ export function createDeleteFolderHandler(
20
20
  name: "folder:delete",
21
21
  schema: deleteFolderPayloadSchema,
22
22
  access,
23
+ description:
24
+ "Deletes a folder from the tenant catalog and refuses while any entity is still filed in it; use it once that folder's contents have been unfiled or moved elsewhere.",
25
+ agent: { risk: "high" },
23
26
  handler: async (event, ctx) => {
24
27
  const payload = event.payload as { id: string }; // @cast-boundary engine-payload
25
28
  const assigned = await folderAssignmentExecutor.list(
@@ -30,6 +30,8 @@ export function createSetFolderHandler(
30
30
  name: "set-folder",
31
31
  schema: setFolderPayloadSchema,
32
32
  access,
33
+ description:
34
+ "Files a host entity into one existing folder and moves it out of whatever folder it was in, since an entity belongs to at most one folder; use it for both the first filing and every later move.",
33
35
  handler: async (event, ctx) => {
34
36
  const payload = event.payload as SetFolderPayload; // @cast-boundary engine-payload
35
37
  const id = folderAssignmentAggregateId(
@@ -27,6 +27,9 @@ export const discardDraftWrite = defineWriteHandler({
27
27
  name: "discard",
28
28
  schema: discardDraftPayloadSchema,
29
29
  access: FORM_DRAFT_ACCESS,
30
+ description:
31
+ "Deletes the calling user's draft for one draftKey and, when releaseFiles is set, hard-deletes the file references that draft alone uploaded; use it after a successful submit or when the user abandons the form.",
32
+ agent: { risk: "high" },
30
33
  handler: async (event, ctx) => {
31
34
  const ownerId = event.user.id;
32
35
  const existing = await lookupDraft(
@@ -14,6 +14,8 @@ export const getDraftQuery = defineQueryHandler({
14
14
  name: "get",
15
15
  schema: getDraftPayloadSchema,
16
16
  access: FORM_DRAFT_ACCESS,
17
+ description:
18
+ "Returns the calling user's saved draft blob (form values and step index) for one draftKey, or null when they have no such draft; use it to resume an in-progress form.",
17
19
  handler: async (query, ctx): Promise<GetDraftResult> => {
18
20
  const row = await lookupDraft(
19
21
  ctx.db,
@@ -29,6 +29,8 @@ export const listDraftsQuery = defineQueryHandler({
29
29
  name: "list",
30
30
  schema: listDraftsPayloadSchema,
31
31
  access: FORM_DRAFT_ACCESS,
32
+ description:
33
+ "Lists the calling user's open drafts for one screenId as id, draftKey, stepIndex and savedAt (newest first, without the form values); use it to let a user pick which draft to resume when the client lost the draftId.",
32
34
  handler: async (query, ctx): Promise<ListDraftsResult> => {
33
35
  const rows = await listDraftsByScreen(
34
36
  ctx.db,
@@ -35,6 +35,8 @@ export const saveDraftWrite = defineWriteHandler({
35
35
  name: "save",
36
36
  schema: saveDraftPayloadSchema,
37
37
  access: FORM_DRAFT_ACCESS,
38
+ description:
39
+ "Upserts the calling user's draft for one draftKey with the given form values and step index, stamping savedAt server-side and refusing a brand-new draft once the per-owner draft cap is reached; use it to persist an in-progress form before the real entity exists.",
38
40
  handler: async (event, ctx) => {
39
41
  const ownerId = event.user.id;
40
42
  const { draftKey, values, stepIndex } = event.payload;
@@ -57,6 +57,8 @@ export const connectAccountHandler: WriteHandlerDef = {
57
57
  name: "connect-account",
58
58
  schema: connectAccountSchema,
59
59
  access: { roles: ["SystemAdmin", "TenantAdmin"] },
60
+ description:
61
+ "Registers a mailbox for the tenant (provider, auth method, display name, encrypted address, shared or personal) and returns the new accountId that also keys its credential slot; use it to start a mail connection, credentials and the first connection test happen outside this call.",
60
62
  handler: async (event, ctx) => {
61
63
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
62
64
  const payload = event.payload as ConnectAccountPayload;
@@ -29,6 +29,9 @@ export const disconnectAccountHandler: WriteHandlerDef = {
29
29
  name: "disconnect-account",
30
30
  schema: disconnectAccountSchema,
31
31
  access: { roles: ["SystemAdmin", "TenantAdmin"] },
32
+ description:
33
+ "Puts a connected mailbox into the final disconnected state so the watch supervisor stops fetching it, keeping the stream for audit and requiring a fresh connect to resume; use it to stop mail ingestion for one mailbox.",
34
+ agent: { risk: "high" },
32
35
  handler: async (event, ctx) => {
33
36
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
34
37
  const payload = event.payload as DisconnectAccountPayload;
@@ -110,6 +110,7 @@ export const ingestMessageHandler: WriteHandlerDef = {
110
110
  name: "ingest-message",
111
111
  schema: ingestMessageSchema,
112
112
  access: { roles: ["SystemAdmin"] },
113
+ agent: { expose: false },
113
114
  handler: async (event, ctx) => {
114
115
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
115
116
  const payload = event.payload as IngestMessagePayload;
@@ -25,6 +25,8 @@ export const listAccountsQuery: QueryHandlerDef = {
25
25
  name: "account:list",
26
26
  schema: listAccountsSchema,
27
27
  access: { roles: ["SystemAdmin", "TenantAdmin", "User"] },
28
+ description:
29
+ "Lists the connected mailboxes of the caller's tenant with the mailbox address decrypted, hiding personal mailboxes the caller neither owns nor administers; use it for the mail connect and settings views.",
28
30
  handler: async (_query, ctx) => {
29
31
  const allRows = await selectMany(ctx.db.raw, mailAccountsProjectionTable, {
30
32
  tenantId: ctx.user.tenantId,
@@ -32,6 +32,8 @@ export const listMessagesQuery: QueryHandlerDef = {
32
32
  name: "message:list",
33
33
  schema: listMessagesSchema,
34
34
  access: { roles: ["SystemAdmin", "TenantAdmin", "User"] },
35
+ description:
36
+ "Lists received mail of the caller's tenant with sender, recipients, subject and snippet decrypted, optionally narrowed to one account, thread or folder scope and hiding messages of personal mailboxes the caller neither owns nor administers; use it to read an inbox or a conversation.",
35
37
  handler: async (query, ctx) => {
36
38
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
37
39
  const payload = query.payload as ListMessagesPayload;
@@ -37,6 +37,8 @@ export const updateAccountHandler: WriteHandlerDef = {
37
37
  name: "update-account",
38
38
  schema: updateAccountSchema,
39
39
  access: { roles: ["SystemAdmin", "TenantAdmin"] },
40
+ description:
41
+ "Records a status, watch-state or display-name change on a connected mailbox as a new snapshot with an audit reason, rejecting any change to an already disconnected mailbox; use it to flag an auth error or to re-activate a mailbox after fixing its credentials.",
40
42
  handler: async (event, ctx) => {
41
43
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
42
44
  const payload = event.payload as UpdateAccountPayload;
@@ -114,6 +114,8 @@ export function createJobsFeature(options: JobsFeatureOptions = {}): FeatureDefi
114
114
  id: JOB_RUNS_SCREEN_ID,
115
115
  type: "custom",
116
116
  renderer: { react: { __component: "JobRunsScreen" } },
117
+ description:
118
+ "Operator table of recent job runs with status filters, plus a panel to trigger a manual job with a payload; open it to monitor background jobs and start one.",
117
119
  access: systemAdminAccess,
118
120
  });
119
121
  // kumiko-lint-ignore app-feature-structure Phase-3 conversion tracked in #2312
@@ -121,6 +123,8 @@ export function createJobsFeature(options: JobsFeatureOptions = {}): FeatureDefi
121
123
  id: JOB_RUN_DETAIL_SCREEN_ID,
122
124
  type: "custom",
123
125
  renderer: { react: { __component: "JobRunDetailScreen" } },
126
+ description:
127
+ "Detail view of one job run with status, timings, error and log lines, and a retry action for failed runs; reached from a row of the job-runs list.",
124
128
  listScreenId: JOB_RUNS_SCREEN_ID,
125
129
  access: systemAdminAccess,
126
130
  });
@@ -22,6 +22,8 @@ function payloadSchemaJson(job: JobDefinition): Record<string, unknown> | null {
22
22
  /** SystemAdmin catalog of jobs that may be started via `jobs:write:trigger`. */
23
23
  export const catalogQuery = defineQueryHandler({
24
24
  name: "catalog",
25
+ description:
26
+ "Lists the jobs that may be started manually, each with its per-tenant flag and payload JSON Schema; use it to find out which job can be triggered and what payload it expects.",
25
27
  schema: z.object({}),
26
28
  access: { roles: ["SystemAdmin"] },
27
29
  handler: async (_query, ctx): Promise<{ rows: readonly ManualJobCatalogEntry[] }> => {
@@ -12,6 +12,8 @@ const KMS_POOL_CONCURRENCY = 4;
12
12
 
13
13
  export const detailQuery = defineQueryHandler({
14
14
  name: "details",
15
+ description:
16
+ "Returns one job run across all tenants by its run id with status, timings, decrypted payload, error and its full log lines; use it to investigate why a run failed.",
15
17
  // Post-ES: runId is the uuid aggregate-id of the jobRun event-stream.
16
18
  // Pre-ES callers passed the serial row-id; the migration is breaking
17
19
  // for API callers (intentional — jobs is framework-ops, no external
@@ -23,6 +23,8 @@ async function decryptRunRow<T extends Record<string, unknown>>(row: T): Promise
23
23
 
24
24
  export const listQuery = defineQueryHandler({
25
25
  name: "list",
26
+ description:
27
+ "Lists job runs across all tenants newest-first, optionally filtered by job name and status; use it to check whether a job ran and whether it succeeded.",
26
28
  schema: z.object({
27
29
  jobName: z.string().optional(),
28
30
  status: z.enum(["queued", "running", "completed", "failed"]).optional(),
@@ -23,6 +23,8 @@ type JobRunRow = {
23
23
 
24
24
  export const retryWrite = defineWriteHandler({
25
25
  name: "retry",
26
+ description:
27
+ "Re-dispatches a failed job run with its original payload and triggering user; use it to rerun a job that failed for a transient reason.",
26
28
  // Post-ES: runId is the uuid aggregate-id. See detail.query for the
27
29
  // rationale — jobs is framework-ops, callers are admin tooling only.
28
30
  schema: z.object({ runId: z.uuid() }),
@@ -13,6 +13,8 @@ import { isManualTrigger } from "../is-manual-trigger";
13
13
 
14
14
  export const triggerWrite = defineWriteHandler({
15
15
  name: "trigger",
16
+ description:
17
+ "Starts one manually-triggerable job with the given payload after validating it against the job's schema; use it to run a maintenance or import job on demand.",
16
18
  schema: z.object({
17
19
  jobName: z.string(),
18
20
  payload: z.record(z.string(), z.unknown()).optional(),
@@ -16,6 +16,8 @@ import { ACCOUNT_TYPES, SCHEDULE_INTERVALS, TRANSACTION_STATUS } from "./constan
16
16
  // tenantId is a base column set by the framework → each tenant has its own books.
17
17
  export const accountEntity = createEntity({
18
18
  table: "read_ledger_accounts",
19
+ description:
20
+ "One node of a tenant's chart of accounts: a name, an asset/liability/equity/income/expense type that decides how reports read its balance, an optional account code and an optional parent account forming the account tree; balances are derived from postings, never stored here.",
19
21
  fields: {
20
22
  name: createTextField({ required: true, maxLength: 120 }),
21
23
  type: createSelectField({ options: ACCOUNT_TYPES, required: true }),
@@ -38,6 +40,8 @@ export const accountEntity = createEntity({
38
40
  // `status` carries draft|posted for the later Soll/Ist work — Phase 0 posts only.
39
41
  export const transactionEntity = createEntity({
40
42
  table: "read_ledger_transactions",
43
+ description:
44
+ "One journal entry: a booking date, a narration, an optional reference (a reversal points at the entry it corrects), a draft/posted status and the embedded posting lines of accountId plus signed minor-unit amount that must sum to zero; posted entries are immutable and corrected only by a reversing entry.",
41
45
  fields: {
42
46
  date: createDateField({ required: true }),
43
47
  // Journal narration ("Miete Januar", "Storno: …") is accounting data, not
@@ -76,6 +80,8 @@ export const transactionEntity = createEntity({
76
80
  // amount is stored positive (minor units); the confirm handler assigns the signs.
77
81
  export const scheduleEntity = createEntity({
78
82
  table: "read_ledger_schedules",
83
+ description:
84
+ "One recurring booking template: book a positive minor-unit amount from a debit account to a credit account each interval between a start date and an optional open end; it holds no bookings itself, periods become real entries only when confirmed.",
79
85
  fields: {
80
86
  description: createTextField({
81
87
  required: true,