Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2443Done

The no-ungoverned-operation walk reads a RETURN TYPE's braces as the method body — and never follows a `this.` hop

Found while running MOTIR-2355: the gate added to aiChatService.submitDiscoveryTurn was invisible to the guard, so /api/ai/chat stayed in the PENDING bucket after it was demonstrably gated.

Two independent blind spots in tests/permissions/noUngovernedOperation.test.ts, both of the MOTIR-2292 family (a brace that is not the body's):

  1. A RETURN TYPE's braces are captured as the method body. methodBody walks the parameter list by paren depth (the MOTIR-2292 repair) and then takes src.indexOf('{', cursor) — but the very next thing after ) is the return annotation, and ): Promise<{ jobId: string }> { puts a { there. Reproduced directly:

    BODY CAPTURED: "{ jobId: string }"
    

    Every service method whose return type contains a brace therefore reports UNGOVERNED however plainly it asserts. MOTIR-2292 fixed WHERE the walk starts looking and left this half untouched, exactly as MOTIR-2304 fixed WHAT it recognises and left the same half untouched.

  2. A this.someMethod( call is not followed. serviceCalls matches someService.method( only, so a method that delegates to a sibling on the SAME object — getOrCreateForProjectthis.getOrCreateForScope, which asserts — reads as ungated. The walk already hops ACROSS services and (since MOTIR-2304) into module-local functions; the same-object hop is the one direction it still cannot see.

This blocks the close-out card: PENDING cannot reach 0 while gated operations are counted as pending, so it is fixed inside this story rather than logged for later.

Acceptance criteria

  • methodBody skips a return-type annotation before taking the body: a { that sits inside <…> or immediately follows a : belongs to the TYPE, not the body.
  • A SYNTHETIC control pins the failure shape — the same pattern the MOTIR-2292 and MOTIR-2304 controls use — so the regression cannot return once real services stop exhibiting it. Both halves: the fix finds the gate in ): Promise<{ … }> {, and still reports NO gate when the body genuinely has none.
  • bodyIsGated follows a this.method( call to the sibling method of the same service, with its own positive AND negative synthetic control.
  • The pinned PENDING and CLAIMED_BUT_UNVERIFIED counts are re-derived by RUNNING the guard, and the comment on each records that the fall is a measurement correction, not gates being added — the distinction MOTIR-2292 and MOTIR-2304 both had to write down.
  • pnpm test tests/permissions/ is green.

Context refs

  • tests/permissions/noUngovernedOperation.test.tsmethodBody, serviceCalls, bodyIsGated, and the two existing synthetic controls to mirror.
  • lib/services/aiChatService.tssubmitDiscoveryTurn, the reproduction.
  • lib/services/planChangeSessionsService.tsgetOrCreateForProject, the this. hop.
  • docs/decisions/permission-inventory.md — the GATE TODAY, MEASURED section, which records each instrument correction.