Skip to content

An artefact belongs to a single package: enforce it at the database #524

Description

@vincent-psarga

The gap

PR #518 added server-side checks on the two paths that place an artefact into a package — AddArtefactsToPackageUseCase and CreatePackageUseCase — and MoveArtefactsToPackageUseCase empties every other package in the space before writing the target.

All three read, then write. Two requests that interleave between the read and the write still place one artefact twice:

  • two packages move of the same unassigned artefact to different targets: both read an empty source list, both add;
  • the same interleaving on add and on package creation.

Live updates narrow the window; they do not close it, and they are not there at all when the SSE connection drops. Raised by Greptile on #518 (P1) and deferred deliberately: the checks that PR adds are worth having on their own — they close the wide window, the one measured in minutes, that the clients' fetched-once lists left open — but the last few milliseconds need a different mechanism.

What would close it

Uniqueness enforced where the write happens, so that the loser of a race is rejected by the database rather than by a check that has already gone stale:

  • one artefact may appear in at most one package per space, across the three join tables (package_commands, package_standards, package_skills — names to confirm);
  • a package is scoped to a space, so the constraint has to reach through the package to its space, which a plain unique index on the join table cannot express. A partial/functional index, a denormalised space_id on the join rows, or an exclusion constraint are the candidates;
  • the three use cases then catch the violation and answer the same 409 they already raise, so the error contract does not change.

Worth knowing before starting

  • UpdatePackageUseCase deliberately does not check, so that a package holding an artefact shared with another one stays editable rather than becoming frozen. A database constraint would not make that distinction, so existing shared memberships have to be found and resolved before it can be applied — a migration question, not only a schema one.
  • Two test suites now seed a shared artefact through the update path precisely because it is the one route left that allows it (packages/integration-tests/src/package-removal-from-target.spec.ts, apps/cli-e2e-tests/src/packages-membership.spec.ts). They will need another route, or the scenario retired.

Follow-up to #518.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions