Skip to content

feat(lifecycle): retention.onlyWhen status predicate + declarative automation run-history retention + i18n bundle ownership guard (#2834)#2837

Merged
os-zhuang merged 2 commits into
mainfrom
claude/adr-0057-data-lifecycle-azuf45
Jul 11, 2026
Merged

feat(lifecycle): retention.onlyWhen status predicate + declarative automation run-history retention + i18n bundle ownership guard (#2834)#2837
os-zhuang merged 2 commits into
mainfrom
claude/adr-0057-data-lifecycle-azuf45

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Two follow-ups from the ADR-0057 tracking issue #2834: item ⑤ (i18n extract-config drift) and the retention.onlyWhen status predicate that lets the last per-plugin age sweep retire.

retention.onlyWhen — retention for mixed tables

sys_automation_run interleaves live workflow state (suspended approval runs, which may legitimately stay paused for months) with terminal run history (telemetry that should age out). A blanket retention.maxAge would reap paused runs and strand in-flight approvals — which is why #2835 removed the declaration and left the age sweep to a bespoke plugin loop.

This PR closes the expressiveness gap instead:

  • spec: lifecycle.retention.onlyWhen — a row filter (per-field equality or { $in: [...] }) the retention window is scoped to. Rows outside the filter are retained regardless of age. superRefine rejects combining it with rotation storage (the Rotator DROPs whole shards and would destroy rows the filter protects) or archive (the Archiver moves rows by age alone). No new exports — the API-surface snapshot is unchanged.

  • objectql: the LifecycleService Reaper merges onlyWhen into every retention delete — the plain pass, each tenant-override pass, and the global-remainder pass ($or with NULL-org rows).

  • service-automation: sys_automation_run now declares

    lifecycle: {
      class: 'telemetry',
      retention: { maxAge: '30d', onlyWhen: { status: { $in: ['completed', 'failed'] } } },
    }

    and the platform Reaper owns AGE retention — paused/running rows never match, same protection the old loop gave. Retired: ObjectStoreSuspendedRunStore.pruneHistory, the DEFAULT_RUN_HISTORY_RETENTION_DAYS export, and the runHistoryRetentionDays / runHistorySweepMs plugin options (launch-window breaking-as-minor, noted in the changeset). The write-time per-flow overflow cap (runHistoryMaxPerFlow) stays — a count bound the declarative contract can't express.

  • docs + skill: onlyWhen row in the objects.mdx key table, mixed-table example, validation example, and a decision-tree entry in skills/objectstack-data/rules/lifecycle.md. The Studio retention form deliberately does not grow an onlyWhen editor — it's a code-authored escape hatch with no sensible widget.

⑤ i18n bundle ownership (first commit)

sys_audit_log / sys_activity / sys_comment / sys_presence moved to plugin-audit / service-realtime (ADR-0029 D8), but their translation copies were left in platform-objects bundles — where the extractor prunes anything outside the config's import set, silently deleting curated zh-CN strings on the next regen (the incident that bit round ③). The leftover copies are now pruned (verified line-by-line against the owning plugins' bundles — zero translation loss) and a bundle-ownership.test.ts guard turns any future stray into a red build, in both directions (stray object in bundle / owned object missing).

Tests

  • spec: onlyWhen accept (scalar + $in) / reject (unknown operators, empty $in, strict-object extras, rotation combo, archive combo) — 6703 passed
  • objectql: Reaper where-merge assertions incl. tenant-override interplay — 840 passed
  • service-automation: pruneHistory tests removed with the code — 254 passed
  • platform-objects: bundle-ownership guard both directions

Refs #2834 (⑤ + onlyWhen item). Changeset: @objectstack/spec minor, @objectstack/objectql minor, @objectstack/service-automation minor.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3


Generated by Claude Code

claude added 2 commits July 11, 2026 09:57
…hip guard (#2834 ⑤)

The committed *.objects.generated.ts bundles still carried sys_audit_log /
sys_activity / sys_comment / sys_presence — objects that moved to
plugin-audit / service-realtime (ADR-0029 K2/D8) along with their OWN
translation bundles and extract configs. The leftovers made every
`os i18n extract` re-run a translation-loss hazard: the extractor prunes
objects outside its config's import set, deleting curated zh-CN content.

- Regenerated the four objects bundles: the moved objects' blocks are gone
  (verified line-by-line — all 131 deleted zh-CN lines exist either
  relocated in the new files or in the owning plugin's bundles: ZERO loss),
  and pending drift keys (e.g. sys_user failed_login_count) are absorbed.
- New bundle-ownership guard test: an object present in the bundle but not
  owned by the extract config turns the build red instead of dying silently
  on the next regeneration; the inverse direction catches dropped imports.
- Extract-config comments updated from "until the next regeneration" to the
  completed state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
…tomation run-history retention (#2834)

Mixed tables — live workflow state interleaved with prunable history —
could not use declarative retention: the Reaper's age cutoff would reap
suspended approval runs. This closes that gap and retires the last
per-plugin age sweep.

- spec: `lifecycle.retention.onlyWhen` — row filter (per-field equality
  or {$in: [...]}) the retention window is scoped to; rows outside it
  are retained regardless of age. superRefine rejects combining it with
  rotation storage (shard DROPs ignore filters) or archive (the
  Archiver moves rows by age alone).
- objectql: the Reaper merges onlyWhen into every retention delete,
  including the tenant-override and global-remainder passes.
- service-automation: sys_automation_run declares
  retention { maxAge: '30d', onlyWhen: { status: { $in: ['completed',
  'failed'] } } } — the platform Reaper owns AGE retention; paused rows
  never match. Retired the plugin's own sweep loop:
  ObjectStoreSuspendedRunStore.pruneHistory, the
  DEFAULT_RUN_HISTORY_RETENTION_DAYS export, and the
  runHistoryRetentionDays / runHistorySweepMs options are removed
  (launch-window breaking-as-minor). The write-time per-flow overflow
  cap (runHistoryMaxPerFlow) stays — a count bound the declarative
  contract can't express.
- docs + skill: onlyWhen in the objects.mdx key table, mixed-table
  example and decision-tree entry in skills/objectstack-data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 11, 2026 10:22am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/xl labels Jul 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/objectql, @objectstack/platform-objects, packages/services, @objectstack/spec.

103 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via packages/services, @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/platform-objects, packages/services, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects, @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 11, 2026 10:31
@os-zhuang
os-zhuang merged commit 33ebd34 into main Jul 11, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0057-data-lifecycle-azuf45 branch July 11, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants