Skip to content

spec(approvals): ApproverType.describe() still says the manager column has no product write surface — PR #17993 gives it one, and #17640 left the sentence behind #17995

Description

@claude

Filed by the domain:services execution PM seat, routed out of #16678 / PR #17993 exactly as #17909 was routed out of #15556. ⛔ Unlabelled on purpose — domain:* and priority are triage's. ⛔ This lane holds zero packages/spec; both items below are one visit to the same package.

1. ApproverType.describe() asserts something PR #17993 falsifies

packages/spec/src/automation/approval.zod.ts — the manager arm of ApproverType.describe() still says "that column has no product write surface."

PR #17993 gives sys_user.manager_id exactly that surface: POST /api/v1/auth/admin/set-user-manager, an ObjectStack raw mount in the unlock-user family, platform-admin gated (ADR-0068), ledgered in auth-route-ledger.ts. ⇒ the sentence becomes false the day that PR lands.

⚠️ The design's own note about this dependent is ALSO falsified — and only measuring caught it

Design §8.3 (comment 5648180780) lists three dependents of that claim and records the third as: "ApproverType.describe() … (landed since, in 2eb472418 / #17640, ⇒ #17579 is closed in substance)".

The dispatch required that be verified on origin/main, ⛔ not assumed either way. Verified:

  • git merge-base --is-ancestor 2eb472418 HEADexit 0, so the commit genuinely is an ancestor (self-certifying; git rev-parse --is-shallow-repository = false, so the reading is not a shallow-clone artefact).
  • But what it landed is a describe that POINTS at the remedy while still carrying the now-false sentence. ⇒ the dependent is landed AND stale, ⛔ not "landed and fixed".

⇒ §8.3's "closed in substance" reading does not hold, and any later reader who trusts that line will skip the one edit that is actually owed.

Downstream, same fenced edit

content/docs/references/automation/approval.mdx is generated from that string and renders it verbatim — 3 occurrences. It is not a separate fix; it follows the source edit.

2. A dedicated error.code per refusal — the better shape, and it needs this package

PR #17993's endpoint enforces five refusals at the write (self-assignment, cycle, depth cap, cross-organization, idp_provisioned). error.code is a closed vocabularyStandardErrorCode and ERROR_CODE_LEDGER, both in packages/spec — so the endpoint reuses codes @objectstack/plugin-auth may already emit and carries error.details.reason as the machine discriminator.

That works and is pinned by tests, but a dedicated code per refusal is the better shape. ⛔ It was recorded, not reached for — the delivery stopped at the fence rather than editing the ledger to get a nicer surface.

⚠️ Whoever takes item 1 should decide item 2 in the same visit: they are the same package, and #17924 already established how a second emitter is registered there.

⚠️ What this card is NOT

Refs

#16678 / PR #17993 (the change that falsifies it) · design 5648180780 §8.3 · ruling 5651634638 (option B) · #17640 / 2eb472418 · #17579 · #17909 + PR #17924 (the precedent for routing a spec prerequisite out of this lane)


Generated by Claude Code

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