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 HEAD → exit 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 vocabulary — StandardErrorCode 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
Filed by the
domain:servicesexecution 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 zeropackages/spec; both items below are one visit to the same package.1.
ApproverType.describe()asserts something PR #17993 falsifiespackages/spec/src/automation/approval.zod.ts— themanagerarm ofApproverType.describe()still says "that column has no product write surface."PR #17993 gives
sys_user.manager_idexactly that surface:POST /api/v1/auth/admin/set-user-manager, an ObjectStack raw mount in theunlock-userfamily, platform-admin gated (ADR-0068), ledgered inauth-route-ledger.ts. ⇒ the sentence becomes false the day that PR lands.Design §8.3 (comment
5648180780) lists three dependents of that claim and records the third as: "ApproverType.describe()… (landed since, in2eb472418/ #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 HEAD→ exit 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).describethat 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.mdxis 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.codeper refusal — the better shape, and it needs this packagePR #17993's endpoint enforces five refusals at the write (self-assignment, cycle, depth cap, cross-organization,
idp_provisioned).error.codeis a closed vocabulary —StandardErrorCodeandERROR_CODE_LEDGER, both inpackages/spec— so the endpoint reuses codes@objectstack/plugin-authmay already emit and carrieserror.details.reasonas 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.
managerrung stops resolving to nobody #17993 is correct as delivered and is not held for this.managerinstead of offering it as a bare allowed value #17640 was wrong — it landed a real improvement; it simply did not remove the sentence this card is about.needs-user-decision. Item 1 is a falsified sentence with one obvious repair. Item 2 is a shape question the taker can settle on the precedent inerror-code-ledger.zod.ts.Refs
#16678 / PR #17993 (the change that falsifies it) · design
5648180780§8.3 · ruling5651634638(option B) · #17640 /2eb472418· #17579 · #17909 + PR #17924 (the precedent for routing a spec prerequisite out of this lane)Generated by Claude Code