fix(platform-objects): state sys_job's uniqueness boundary as 'global' and correct its published claim (#8578) - #8776
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
hotlong
marked this pull request as ready for review
August 15, 2026 00:16
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.
Fixes #8578
The reading, first — and it lands on
'global'The card deliberately asserted no defect and named the one question that settles the direction:
Nothing does. Five independent lines of evidence, all measured on
origin/main:DbJobAdapter(services/service-job) is the only thing that writes this table —upsertJobRowonschedule(),setActiveoncancel(),bumpJobafter every run. All three write underSYSTEM_CTX = { isSystem: true, positions: [], permissions: [] }, and all three locate their row withfind('sys_job', { where: { name }, limit: 1 })— keyed onnamealone. The adapter already assumes the global key; a per-organization constraint would make that lookup ambiguous rather than fix anything.jobmetadata type is closed to tenants on all three flags —allowOrgOverride: false,allowRuntimeCreate: false,supportsOverlay: false. The registry comment states the deciding half in words: "no per-org job fork".enable.apiMethodsis['get', 'list'](ADR-0103 engine-owned) — reads stay open for the Setup grid, every write verb is absent, for every caller.schedule()call site is registration-time and installation-scoped:AppPluginonkernel:ready,JobServicePluginreplaying those registrations, the schedule / time-relative flow triggers (keyed onflowName, andflowis itselfallowOrgOverride: false), the approvals and reports plugins' fixed names, and wait-node timers keyedflow-wait:{runId}:{nodeId}on a server-minted run id.sys_job.namefirst among the nine, with the After spelling'global'and "zero drift".types/src/unique-scope-install-gate.tsnames it twice more as platform-wide by construction.So the card's first branch wins:
sys_jobis tenant-scoped only incidentally (organization_idis kernel-injected, never authored), the installation-wide constraint is correct, and the remedy is to state it explicitly plus correct the description that published the bare claim.No third population turned up, so this did not need to go back to the decision box.
One thing worth flagging to the record
The card placed
sys_jobin category C (the defect class) as an open judgement case. ADR-0120's S5 inventory had already namedsys_job.nameas a platform-wide engine idempotency key — that is, the answer existed in an accepted ADR at filing time. This is not a criticism of the sweep (the lint rule fires on spelling alone and cannot see S5); it is a note that the next sweep of this class can cheaply pre-filter category C against ADR-0120 S5's nine-key list.What changed
packages/platform-objects/src/audit/sys-job.object.ts— declared index respelledunique: true→unique: 'global', and thenamefield'sdescriptioncorrected from the boundary-free "Unique job identifier (snake_case)" to text that names the installation-wide boundary. Both carry[#8578]comments with the reasoning.packages/platform-objects/src/apps/translations/*.objects.generated.ts— regenerated vianode scripts/check-i18n-bundles.mjs --write. Theenbundle is rewritten from source; the three translated locales still carried the old bare claim ("唯一任务标识" / "一意のジョブ識別子" / "Identificador único"), so theirhelpvalues were updated to match the corrected meaning — the same shape as the sys_position locale fix. Values only; no key added or dropped.packages/platform-objects/src/audit/sys-job.global-unique.test.ts— new pin (10 cases)..changeset/sys-job-global-unique-scope.md.Zero drift, no migration:
'global'is the semantics baretruealready materialized, so the physical index stays(name)verbatim (ADR-0120 D2).The pin guards the READING, not just the spelling
The card's bar asks for a test that fails if the opposite becomes true. Spelling assertions alone cannot do that — they would stay green on the day a per-organization write path opens, and the constraint would silently become wrong. So four of the ten cases pin the premises as doors that are currently shut:
enable.apiMethodscarries no write verb,jobis closed on all three registry flags,flowis closed too (flow-derived job names would otherwise fork per org), andorganization_idis genuinely injected (so the scope is a real choice, not an artifact of an absent column).Reverse verification — direction predicted before each run
Leg 1 — ablate the spelling (
'global'→ baretrue). Predicted: exactly 4 red (the three spelling assertions + the byte-identical shape), all four "doors" green. Observed, exactly:Leg 2 — ablate the READING (flip
job.allowOrgOverridetotruein spec). This is the load-bearing leg.@objectstack/specresolves throughexportsto builtdist/in this package's vitest config (only@objectstack/lintis aliased to src), so the ablation was rebuilt and the mutation proved in the artifact the test actually reads —dist/kernel/index.jsshowingallowOrgOverride: true— before running. Predicted: exactly 1 red, the reading pin. Observed:Both legs restored from the commit (
git checkout HEAD --with the path, nevergit stash), spec rebuilt, and the restore likewise proved indist(allowOrgOverride: false) so no mutated artifact survives in the worktree.Verification
All at final HEAD
d474a66fb, after the last commit:pnpm --filter @objectstack/platform-objects testpnpm --filter @objectstack/platform-objects typechecknode scripts/check-i18n-bundles.mjsnode scripts/check-nul-bytes.mjspnpm check:changeset-gate-self-tests·check:objectui-changesetcheck-adr-0087-registration·check-changeset-no-major·check-empty-changesetpnpm check:query-options-erasurepnpm check:type-check-coverageGate union re-derived with
node scripts/pm/dispatch-gates.mjs $(git diff --name-only origin/main...HEAD)(three-dot). Beyond the prompt's predictedcheck:i18nit surfaced the five changeset-family gates pluscheck:query-options-erasure,check:type-check-coverageandcheck:type-check-debt— all run above.check:type-check-debtdeserves its own line.platform-objectsexcludes**/*.test.tsfrom its tsconfig, sopnpm typechecknever saw the new test file; the TEST_DEBT ratchet is frozen at 3 for this package. Re-measured with the exclusion lifted: exactly 3 errors, all pre-existing infeature-gate-guard.test.ts(TS2339 x2, TS7006 x1) — the new file contributes zero, so the ratchet does not move.Deliberately not done
sys_setting, and categories A and B are untouched — the card's three-category triage is its durable product and is not re-derived here.unique: true; they are correct today and owned by parked v18 ADR-0120 协议 18 项:D2 conversion(声明索引 unique: true → 'global')+ 裸 true 硬拒 + synonym pin 退役 —— 挂 18 列车,现在勿动工 #5082.content/docs/releases/edit.sys-account.object.tsandplugin-auth/**untouched ([security]sys_account.passwordandprevious_password_hashesserialize on the generic data API — to admins cross-user, and to the user themselves #8676's surface).origin/mainhad not moved when this PR was opened, so the declared translation-bundle collision with [security]sys_account.passwordandprevious_password_hashesserialize on the generic data API — to admins cross-user, and to the user themselves #8676 has not materialized; if that card lands first this branch will need a plain regeneration round.Generated by Claude Code