Blocked-by: #14637
Filed by the domain:services execution seat at the contract review of PR #14905 (#14637), which is where the duplication is introduced. Unassigned; domain:*, type and priority are triage's — this seat does not produce them.
Named reader: the domain:spec execution seat. The change lands in packages/spec, which is single-owner, so this card exists rather than a rider on #14905.
What lands, and why it is not a rider
PR #14905 gates the share-link route probe on the object's publicSharing.enabled policy at two probe sites. To do that it needs one boolean predicate at both, and it ships two copies:
packages/plugins/plugin-sharing/src/share-link-service.ts — isPublicSharingEnabled(schema), exported from the module but deliberately not from the package entry point;
packages/runtime/src/domains/share-links.ts — a documented private copy of the same predicate.
That copy is correct and was the right call for that card, which could not open packages/spec. It is still a contract defect: one policy read, two spellings, held equal only by tests and a comment.
⚠️ The PR's stated justification for the copy does not survive measurement — do not inherit it
PR #14905's body argues the copy is forced because "importing it would invert the dependency direction", @objectstack/plugin-sharing being a dev dependency of packages/runtime. Verified: that dev-only relationship is real (runtime's package.json lists @objectstack/plugin-sharing under devDependencies as workspace:*, with no runtime src import of it).
But it is true only of that specific home. Measured at the contract review: five packages are already in the dependencies of both plugin-sharing and runtime — @objectstack/spec, @objectstack/types, @objectstack/core, @objectstack/objectql, @objectstack/metadata-core. A shared home exists today and needs no new edge. So the question is not "is a shared home reachable" (it is) but "which one is right", and the answer is packages/spec, beside the schema that declares the key.
Suggested shape (for triage and the spec seat, not a ruling)
Either of:
Then make both current sites consume it and delete the runtime copy.
The pins that must stay green
packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts (the [#14637] block, around :1155)
packages/runtime/src/domains/share-links-enforcement-context.test.ts (around :727)
Both assert the same observable answer on both surfaces rather than trusting the copy, so they are exactly the instrument that proves a de-duplication did not change behaviour. Re-derive the line numbers; they move.
⚠️ Fail-closed must survive the move: an absent block, an absent schema, and an engine that cannot answer getSchema at all are one answer, false. That is what resolveToken and createLink already do, and a shared predicate must not quietly turn any of those three into a different answer.
Dedup
Searched at the contract review: no existing card covers this. Nearest is #14703 (open, domain:spec) which concerns the TSDoc of the same key and does not cover the duplicated predicate — adjacent, not a duplicate.
⚠️ Channel caveat, recorded rather than hidden: search_issues free-text matching in this repo is known to return zero for terms present in open issue titles (#14743), so a zero here is weaker evidence than usual. Re-check with a label-filtered listing before treating this as certainly novel.
Why the review overturned the dev's decision not to file this
The dev's report gave its reason as "#14637 already records this exact duplication". That is wrong on two counts, both checkable: #14637's "duplicated twin of the same probe" is the pre-existing two-surface probe, not the predicate copy PR #14905 introduces; and #14637 closes when #14905 merges, so the copy would be left with no open card naming it.
Refs: #14637 (the card whose fix introduces the copy) · PR #14905 · #14703 (adjacent, TSDoc of the same key) · #14743 (the dedup-channel caveat)
Blocked-by: #14637
Filed by the
domain:servicesexecution seat at the contract review of PR #14905 (#14637), which is where the duplication is introduced. Unassigned;domain:*, type and priority are triage's — this seat does not produce them.Named reader: the
domain:specexecution seat. The change lands inpackages/spec, which is single-owner, so this card exists rather than a rider on #14905.What lands, and why it is not a rider
PR #14905 gates the share-link route probe on the object's
publicSharing.enabledpolicy at two probe sites. To do that it needs one boolean predicate at both, and it ships two copies:packages/plugins/plugin-sharing/src/share-link-service.ts—isPublicSharingEnabled(schema), exported from the module but deliberately not from the package entry point;packages/runtime/src/domains/share-links.ts— a documented private copy of the same predicate.That copy is correct and was the right call for that card, which could not open
packages/spec. It is still a contract defect: one policy read, two spellings, held equal only by tests and a comment.PR #14905's body argues the copy is forced because "importing it would invert the dependency direction",
@objectstack/plugin-sharingbeing a dev dependency ofpackages/runtime. Verified: that dev-only relationship is real (runtime'spackage.jsonlists@objectstack/plugin-sharingunderdevDependenciesasworkspace:*, with no runtimesrcimport of it).But it is true only of that specific home. Measured at the contract review: five packages are already in the
dependenciesof bothplugin-sharingandruntime—@objectstack/spec,@objectstack/types,@objectstack/core,@objectstack/objectql,@objectstack/metadata-core. A shared home exists today and needs no new edge. So the question is not "is a shared home reachable" (it is) but "which one is right", and the answer ispackages/spec, beside the schema that declares the key.Suggested shape (for triage and the spec seat, not a ruling)
Either of:
packages/spec, beside thepublicSharingdeclaration atpackages/spec/src/data/object.zod.ts(the key is declared around line 1384 — re-verify, this PR-era line number will rot); orIShareLinkServicecontract, e.g.sharingEnabledFor(objectName), which is the home PR fix(sharing): gate the share-link route probe onpublicSharing.enabled, at both probe sites #14905's own body nominates.Then make both current sites consume it and delete the runtime copy.
The pins that must stay green
packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts(the[#14637]block, around:1155)packages/runtime/src/domains/share-links-enforcement-context.test.ts(around:727)Both assert the same observable answer on both surfaces rather than trusting the copy, so they are exactly the instrument that proves a de-duplication did not change behaviour. Re-derive the line numbers; they move.
getSchemaat all are one answer,false. That is whatresolveTokenandcreateLinkalready do, and a shared predicate must not quietly turn any of those three into a different answer.Dedup
Searched at the contract review: no existing card covers this. Nearest is #14703 (open,
domain:spec) which concerns the TSDoc of the same key and does not cover the duplicated predicate — adjacent, not a duplicate.search_issuesfree-text matching in this repo is known to return zero for terms present in open issue titles (#14743), so a zero here is weaker evidence than usual. Re-check with a label-filtered listing before treating this as certainly novel.Why the review overturned the dev's decision not to file this
The dev's report gave its reason as "#14637 already records this exact duplication". That is wrong on two counts, both checkable: #14637's "duplicated twin of the same probe" is the pre-existing two-surface probe, not the predicate copy PR #14905 introduces; and #14637 closes when #14905 merges, so the copy would be left with no open card naming it.
Refs: #14637 (the card whose fix introduces the copy) · PR #14905 · #14703 (adjacent, TSDoc of the same key) · #14743 (the dedup-channel caveat)