MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3509

Planning bug: a card RELOCATED a surface without checking that its destination is reachable by everyone the source was

Done
Description

Filed by motir run MOTIR-3500 (parent-run, guard #4 — its second firing on that parent) on 2026-08-25. The plan defect is already corrected: plan cmt9byzjm0049i2n87lgclmp9 proposes the decision card and blocks MOTIR-3502. This card is the telemetry.

The defect

MOTIR-3502 hid a surface and moved its contents somewhere else:

make /settings/workspace itself notFound() at count 1 … replace WorkspaceConfigCard's link-out with the folded-in Name / Members / Danger-zone sections on /settings/organization

Both halves are individually right. The card checked that the destination page exists, that it renders in the collapsed state, and that the design asset specifies the fold-in. What no gate asked is whether the destination is reachable by the same actors as the source.

It is not. /settings/organization returns the forbidden EmptyState to anyone who is not an org owner/admin (settings/organization/page.tsx:53-56), while /settings/workspace is gated on workspace membership. A workspace invitee is auto-joined to the org as role: ORGANIZATION_ROLE.member (organizationsService.ts:481) — so for a plain teammate the move closes the source and refuses the destination.

The capability that vanishes is Leave workspace. leaveWorkspaceAction has exactly one consumer in the tree (DangerZoneCard.tsx:29), and that component renders on exactly the two routes involved. Following the card as written would ship a product where a teammate who accepted an invite cannot get out — which is MOTIR-3500's own defect, one role over.

Why it survived authoring

Every existing gate the card had to pass, it passed:

  • Gate 2 (preconditions) — every path, symbol and component the card names exists. The fold-in target renders, and it renders specifically in the collapsed state.
  • The design gate — the card even pre-cleared it in its own body, correctly: the asset specifies the fold-in and delegates the drawing.
  • The advisory channelvalidate_work_item returned one likely-over-gate-sizing entry, disposed of on the record.

The relocation was expressed as two independent facts ("this route 404s" and "that page hosts the cards"), and the defect lives only in the RELATION between them. A gate that reads a card criterion by criterion cannot see a relation.

And the design asset states both halves of the contradiction four lines apart (design/org-admin/design-notes.md:147-150): the workspace surface is "folded into the org Settings page at one workspace", and those routes are "All org-owner/admin gated". A reader checking either sentence finds it true.

The general shape

A card whose verb is move / fold / relocate / replace X with Y / hide X, it lives at Y now is making a claim about a SET — everyone who could reach X can reach Y — and the claim is nowhere in its criteria. It reads as two facts about two surfaces. The gap surfaces at run time, as a role nobody enumerated.

The nearest existing rule is plan-rules/core.md gate 8's MIGRATION limb, which already knows that a migration's referrers are call sites rather than cards and demands they be greped and listed. This is the same limb on the AUDIENCE axis rather than the CALLER axis: not what still calls the old path, but who could reach it. The sibling rule is that a disclosure or hiding rule may remove a CONCEPT and may not remove a CAPABILITY.

Acceptance criteria

  1. plan-rules/core.md gate 8's migration limb gains an AUDIENCE arm — or a sibling gate is added — firing on a card whose verb is move / relocate / fold / replace-with / hide-it-lives-at, and requiring the card to enumerate who can reach the source and who can reach the destination, verified against each surface's actual gate rather than its existence.
  2. The arm names the discriminator that makes this cheap: compare the GATES, not the routes — the role, permission or membership each surface asserts. Two surfaces in the same area routinely assert different ones, which is exactly the case that reads as safe.
  3. It states the capability rule the fixture turned on: a disclosure rule may hide a CONCEPT and may not remove a CAPABILITY. The check is to list what the source surface uniquely carries and confirm each has a home after the move.
  4. It notes that a DESIGN asset is not authority for this and can carry the contradiction itself, citing that an asset stated both halves four lines apart — so the check runs against the shipped gate, not the spec.
  5. Stated generically enough to fire outside this tenancy model — any relocation between differently-gated surfaces.

Context refs

  • MOTIR-3502 — the mis-specified card, its blocking comment carrying the full evidence, and the re-scope
  • Plan cmt9byzjm0049i2n87lgclmp9 — the correction awaiting approval
  • motir-meta/prompts/plan-rules/core.md gate 8 (the migration limb the arm attaches to), gate 2
  • motir-core app/(authed)/settings/organization/page.tsx:53-56, lib/services/organizationsService.ts:481
  • motir-core app/(authed)/settings/workspace/_components/DangerZoneCard.tsx:29
  • motir-core design/org-admin/design-notes.md:147-150
  • MOTIR-3506 — the sibling planning bug from the same run, on the §5 arm

Repo: the fix is a rule change in motir-meta (prompts/plan-rules/core.md). No criterion names a path in another repository — the motir-core refs are evidence, not deliverables.