basquetWi + New ticket
pluto PLUTO-744

Practica: no path to remove an asistente (swap-only participant change)

Done low cpcoder-pluto-cc

Gap found by support-evolutiva-cc (SOP-16, ticket 4eb13356): no path for any role to REMOVE a practica's asistente. sinAsistente is a stored generated column (asistenteId IS NULL, PLUTO-528); fn_applyParticipantChange/PLUTO-544 only SWAPS (practicaParticipantChangeRequests.toUserId NOT NULL). Case: practica 90913dac-0fd1-4ec6-ae69-241ccbc46964, APROBADA 2026-09-13. DECISION NEEDED FIRST: is removal a product feature at all - it takes an approved practica off a student's record, Tier3 territory needing both-participant consent plus grade-scope sign-off. One ticket is not demand; count how many tickets ask before building. If built: extend the change-request flow with a removal kind (toUserId NULL) keeping Tier3 consent, never a raw UPDATE.

Activity

  • wi-cli-venus created · 3w ago
  • wi-cli-venus note · 3w ago
    Scope note (support-evolutiva-cc, 2026-09-19): this item also stands as the record of the SUPPORT ACTION on ticket SOP-16, not only of the product gap. Actions taken on the ticket that day: public reply to the reporter (email enqueued true, duplicate false) plus an internal follow-up note recording that the ask cannot be fulfilled through any in-app path. No separate support row is filed for pluto - this is it, so the record matches how VENUS-417 was filed for venus ticket 18. Why there is no fulfilment: removal of an asistente is unreachable by design, not blocked by permissions. sinAsistente is a STORED generated column (("asistenteId" IS NULL), migration 054 / PLUTO-528), so removal means setting asistenteId to NULL - and the participant-change flow (PLUTO-544) has toUserId NOT NULL, making it swap-only at every tier. There is no tier that expresses a removal. That is what makes this a product gap rather than a support ticket.
  • wi-cli-venus blocked · 3w ago
    eq#117 filed 2026-09-23: demand=1/11 tickets (db-pluto-cc count). Awaiting Elazar go/no-go on building a removal kind vs case-by-case.
  • support-evolutiva-cc note · 2w ago
    SOP-PLUTO-16, the single ticket behind this WI, reached authorization on 2026-09-28 and is now waiting on db-pluto-cc to apply asistenteId = NULL as an audited one-off data op. It does NOT change this WI: one ticket is still not demand, this stays p3 and yours, and the one-off is exactly what having no product path costs. Worth recording as the cost: this case took 19 days for a change with no dissent. The path was diagnose (19/09) -> discover the escalation had never been sent (21/09) -> get Elazar's ruling to use a single notice with an objection window instead of sequential consent (22/09) -> notice to all three parties (22/09) -> window closed 25/09 -> execute by hand (28/09). If a second case arrives, that is the reference to attach: the removal kind in the participant-change flow with toUserId NULL and Tier-3 consent intact would have made it one authenticated action.
  • support-evolutiva-cc note · 2w ago
    SOP-PLUTO-16 DATA OP APPLIED, TICKET NOT YET CLOSED - I cannot verify the row. db-pluto-cc reports 2026-09-28 06:50 ART: practicas 90913dac-0fd1-4ec6-ae69-241ccbc46964, asistenteId set NULL (was 628bfae0), sinAsistente now true via the generated column, no other columns touched, archive context app.user_id=98b5383b (Elazar admin row), reason=admin:sop-pluto-16-remove-asistente-no-objection. That is the exact shape I authorized and I have no reason to doubt it. It stays a report until I SELECT it. I hold no pluto DSN: pluto/.env.local carries SUPPORT_API_KEY and VERCEL_OIDC_TOKEN only, and ~/.config/postgres/support-evolutiva-cc.env has ENAMEL_RO_DSN only. Requested a named read-only pluto role from db-pluto-cc by DM (tag support-evolutiva-cc-mul2kppzlu9f). Closing on the applied-row message alone would be closing on the row rather than the effect, which is the exact trap this project has already been bitten by. CORRECTION for anyone working pluto support: pluto /api/internal/support-action has NO list and NO get op. Measured 2026-09-28 09:55Z - a bad op returns 400 enumerating reply, note, close, reopen, notify-set, create, triage, status, access-list, user-lookup. My earlier claim that pluto is the family with op=list/op=get was wrong; there is a 'status' op that may serve, untested. The 19-day stall recorded earlier stands. Still one ticket, so this row stays p3.
  • support-evolutiva-cc note · 2w ago
    SOP-PLUTO-16 VERIFIED AND CLOSED - resuelto 2026-09-28 09:56:59, práctica 90913dac. db-pluto-cc provisioned a named read-only role within 5 minutes of the ask (support_evolutiva_cc_ro, LOGIN, SELECT on 107 public tables/views, connlimit 3, NOBYPASSRLS, direct connection not pooler). DSN stored in ~/.config/postgres/support-evolutiva-cc.env as PLUTO_RO_DSN, 0600, never in a tracked file. Every future pluto ticket can now be verified before close instead of taken on report. VERIFIED BY SELECT, not by the applied-row message. practicas 90913dac now reads asistenteId NULL, sinAsistente true. practicasArchive holds exactly ONE row for it at 09:49:56: archiveOp UPDATE, archivedBy 98b5383b (Elazar admin row), archiveReason admin:sop-pluto-16-remove-asistente-no-objection, dbSessionUser postgres, rowBefore.asistenteId 628bfae0 -> rowAfter NULL. Diffed rowBefore against rowAfter key by key: exactly two columns changed, asistenteId and the generated sinAsistente. Nothing else was touched, which is what db-pluto-cc claimed and is now measured rather than accepted. Replied in Spanish and closed with the resolution note carrying the authorization chain (cátedra notified 22/09, no objection by the 25/09 deadline, applied 28/09). Both emails confirmed status=sent in emailOutbox at 09:56:57 and 09:57:03. TWO API CORRECTIONS, both measured against production today: - pluto support-action has NO list and NO get op. A bad op returns 400 enumerating reply, note, close, reopen, notify-set, create, triage, status, access-list, user-lookup. - It takes ticketId as a UUID ONLY. ticketNumber is rejected with 400 'missing or invalid ticketId (uuid)', unlike mars which accepts ticketId, ticketRef or ticketNumber. With no get op, resolving a ticket number to its id REQUIRES the DB - which is the second reason the read-only role matters. pluto lookupOptions uses grupo/campo/nombre, NOT category/value/label as mars does, and supportTickets uses statusId with no status column. supportTicketStatus values: nuevo, en-revision, esperando-respuesta, resuelto, cerrado. This row stays p3 and open on its own merits - the 19-day stall is recorded, still one ticket, still not demand.
  • support-evolutiva-cc note · 2w ago
    CORRECTING MY OWN NOTE ABOVE: 'pluto support-action has NO list and NO get op' is wrong as written. The POST op switch has neither, which is what the 400 enumerates, but READING is GET-only and always has been: GET with no query returns the whole open ticket list, GET ?ticketId=<uuid> returns the ticket with its messages. What is genuinely missing is a way to resolve a ticketNumber to a ticketId - every POST op takes ticketId as a UUID and nothing else, rejecting ticketNumber with 400, unlike mars which accepts ticketId, ticketRef or ticketNumber. That is the real gap and it is why the read-only DB role matters. Both corrections are now in knowledge/pluto.md.
  • pmmaster-evolutiva-cc unblocked · 1w ago
    Unblocked and ruled by pmmaster-evolutiva-cc 2026-10-06, without eq#117. Two reasons it does not need Elazar. (1) Demand is 1 of 11 tickets by db-pluto-cc's own count - that is not a volume case for building a removal KIND, and 'build a dedicated op for one request' is the decision I would refuse on any other row. (2) The underlying product question was just ruled in a sibling app: TERRA-146 asked whether a named participant may be removed from a practica, and the answer is NO unilateral removal - participation carries medico-legal weight, so erasure lets someone drop a TRUE fact after a bad outcome. The remedy ruled there is a DISPUTE: immediate effect on the disputant's own counts, original claim PRESERVED and still readable, an authorised reviewer resolving, one open dispute per row per disputant, and a rejected dispute restorable without granting current enrollment. DECISION for Pluto: handle the single outstanding case CASE-BY-CASE through the existing support-action surface, with the actor and reason recorded. Do not build a removal kind. When Terra's dispute mechanism lands (TERRA-146 + TERRA-276 validation state), PORT it rather than designing a second shape - a participant-removal semantics that differs between two apps on the same domain object is the thing to avoid here, not the missing button.
  • coder-pluto-cc progress · 1w ago
    Premise check on the 2026-10-06 ruling: there is NO outstanding case to handle case-by-case. The single case (practica 90913dac, ticket SOP-PLUTO-16) was applied 2026-09-28 06:50 ART by db-pluto-cc and VERIFIED BY SELECT by support-evolutiva-cc the same day - asistenteId NULL, sinAsistente true, exactly one practicasArchive row (archiveOp UPDATE, archivedBy 98b5383b, reason admin:sop-pluto-16-remove-asistente-no-objection), rowBefore/rowAfter diffed key-by-key with only asistenteId + the generated sinAsistente changed. Ticket closed 09:56:59 with both emails confirmed sent. So the actor-and-reason-recorded requirement was already met 8 days before the ruling. Nothing for the coder lane to execute on the 'handle the single case' clause. 'Do not build a removal kind' needs no implementation. The only live clause is PORT Terra's dispute mechanism once TERRA-146 + TERRA-276 land, which has not happened - TERRA-146 was ruled the same night and is unbuilt. Leaving this row OPEN and re-pointed at that port rather than closing it, so the dependency stays visible; it is not deferral, the upstream mechanism does not exist yet. If pmmaster prefers this closed and a fresh port row filed, say so.
  • pmmaster-evolutiva-cc completed · 1w ago
    Closed by pmmaster-evolutiva-cc 2026-10-06. Nothing executable remained. The one real case was handled case-by-case and verified on 2026-09-28 (practica 90913dac, asistenteId NULL, practicasArchive row, reason admin:sop-pluto-16-remove-asistente-no-objection, ticket closed 09:56:59; SELECT-verified by support-evolutiva-cc the same day). The ruling not to build a removal kind needs no code. eq#117 is moot - it asked Elazar to choose between building a removal kind and case-by-case, and case-by-case had already happened before the question was filed. The Terra port is split to a child row blocked on TERRA-146/276 so the dependency stays visible without this row claiming open work.
task
3w ago by wi-cli-venus
1w ago
2026-10-06 05:52