feat(lifecycle): retention.onlyWhen status predicate + declarative automation run-history retention + i18n bundle ownership guard (#2834)#2837
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 4 package(s): 103 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 11, 2026 10:31
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two follow-ups from the ADR-0057 tracking issue #2834: item ⑤ (i18n extract-config drift) and the
retention.onlyWhenstatus predicate that lets the last per-plugin age sweep retire.retention.onlyWhen— retention for mixed tablessys_automation_runinterleaves live workflow state (suspended approval runs, which may legitimately stay paused for months) with terminal run history (telemetry that should age out). A blanketretention.maxAgewould 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.superRefinerejects combining it with rotationstorage(the Rotator DROPs whole shards and would destroy rows the filter protects) orarchive(the Archiver moves rows by age alone). No new exports — the API-surface snapshot is unchanged.objectql: the LifecycleService Reaper merges
onlyWheninto every retention delete — the plain pass, each tenant-override pass, and the global-remainder pass ($orwith NULL-org rows).service-automation:
sys_automation_runnow declaresand the platform Reaper owns AGE retention —
paused/runningrows never match, same protection the old loop gave. Retired:ObjectStoreSuspendedRunStore.pruneHistory, theDEFAULT_RUN_HISTORY_RETENTION_DAYSexport, and therunHistoryRetentionDays/runHistorySweepMsplugin 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:
onlyWhenrow in theobjects.mdxkey table, mixed-table example, validation example, and a decision-tree entry inskills/objectstack-data/rules/lifecycle.md. The Studio retention form deliberately does not grow anonlyWheneditor — it's a code-authored escape hatch with no sensible widget.⑤ i18n bundle ownership (first commit)
sys_audit_log/sys_activity/sys_comment/sys_presencemoved 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 abundle-ownership.test.tsguard turns any future stray into a red build, in both directions (stray object in bundle / owned object missing).Tests
onlyWhenaccept (scalar +$in) / reject (unknown operators, empty$in, strict-object extras, rotation combo, archive combo) — 6703 passedRefs #2834 (⑤ + onlyWhen item). Changeset:
@objectstack/specminor,@objectstack/objectqlminor,@objectstack/service-automationminor.🤖 Generated with Claude Code
https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
Generated by Claude Code