Skip to content

feat(memoria): perfil curado v1.1 — UI no portal + injeção per-account (allowlist)#151

Merged
BrunooMoniz merged 5 commits into
mainfrom
feat/mem-portal
Jun 19, 2026
Merged

feat(memoria): perfil curado v1.1 — UI no portal + injeção per-account (allowlist)#151
BrunooMoniz merged 5 commits into
mainfrom
feat/mem-portal

Conversation

@BrunooMoniz

Copy link
Copy Markdown
Owner

O quê

Fecha o v1 do perfil curado de memória com UI funcional e pivota de owner-only para per-account governado por allowlist (decisão de produto 2026-06-19: o uso real é via conta friend/portal, não só owner/Claude Code). Atualização registrada no spec.

Mudanças

  • Injeção per-account + allowlist (resolveInstructions): carrega os fatos da conta da sessão (não DEFAULT) e injeta só se PROFILE_INJECT_ENABLED E a conta está em PROFILE_INJECT_ACCOUNTS. Fail-closed reforçado: cada conta só vê os próprios fatos; accountId ausente/fora do allowlist → base pura; usa getContext()?.accountId (não o getAccountId() com fallback pra 'bruno').
  • Curador per-account: itera todas as contas com fatos (listAccountsWithProfileFacts), isolado por conta, ainda só na tabela nova (fora do eval gate).
  • Rotas /portal/profile (per-account, session-authed): GET (lista + budget + injecting), POST (cria, guarda de segredo 400), PATCH (pin/editar), POST /:id/evidence (vale/não-vale → applied/violated + recomputa confiança), DELETE. :id sempre WHERE id AND account_id.
  • UI Next.js (web/app/(app)/perfil/): página Perfil com medidor de orçamento, status de injeção, formulário de adicionar fato, cards com banda de confiança/pin/contadores/badge "injetado", ações vale/não-vale/excluir. Item "Perfil" na nav. Reusa o design system (Card/Button/Tag/TextField/EmptyState/Skeleton).

Verificação

  • Engine: tsc limpo, 1346 testes (node:test) — inclui injeção allowlist fail-closed, curador multi-conta, 13 testes de rotas.
  • Web: bun run typecheck + bun run build + NEXT_PUBLIC_BASE_PATH=/app bun run build OK; 244 testes (vitest).
  • Fora do eval gate F8 (não toca search.ts/brain_chunks).

Deploy (depois do merge)

Engine: deploy de rotina. Web: build do Next na VPS (cd web && bun install && NEXT_PUBLIC_BASE_PATH=/app bun run build && rm -rf portal-next && cp -r web/out portal-next) — ver RUNBOOK. Enable: PROFILE_INJECT_ENABLED=true + PROFILE_INJECT_ACCOUNTS=<conta do Bruno> + MEMORY_CURATION_ENABLED=true.

🤖 Generated with Claude Code

BrunooMoniz and others added 5 commits June 19, 2026 08:37
…26-06-19)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pivo de owner-only para per-account: resolveInstructions agora recebe a conta
REAL da sessao + a allowlist (PROFILE_INJECT_ACCOUNTS) e so injeta o perfil
quando flag on E accountId nao-vazio E na allowlist. Cada conta carrega so os
proprios fatos (loadFacts(accountId), nunca DEFAULT). index.ts passa
getContext()?.accountId ?? null (sem o fallback 'bruno' de getAccountId),
fail-closed por desenho.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
listAccountsWithProfileFacts (SELECT DISTINCT account_id) + runMemoryCuration
agora itera todas as contas com fatos, cada uma isolada (loadFacts/upsertFact/
insertAudit sempre com o account_id da conta processada). deps.accountId
explicito processa so essa conta (testabilidade); o tick noturno passa
listAccounts e processa todas. Sem accountId/listAccounts cai no DEFAULT (compat).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
profile-portal.ts (helpers reusando profile-storage/computeConfidence/
profile-guard) + rotas GET/POST/PATCH/evidence/DELETE em routes.ts, todas
escopadas por res.locals.accountId (sessão, nunca input) e :id validado por
WHERE id=$1 AND account_id=$2 (cross-account → 404). POST recusa segredo
(looks_like_secret) e vazio/>500 chars; evidence recomputa confiança;
memory_audit com trigger portal-{create,edit,evidence,delete}. GET expõe
eligible + injecting (flag + allowlist) + usedChars (renderProfile real).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nova rota /perfil no portal logado (Next.js) onde o usuário cura os fatos
que o Zinom injeta nas sessões dele.

- header editorial com badge de status de injeção (ativo / não ativado)
- medidor de orçamento usedChars/budgetChars (barra, alerta perto/acima do limite)
- form de adicionar fato (select de categoria + textarea), trata 400
  looks_like_secret e invalid com mensagem clara
- cards de fato: conteúdo, chip de categoria, badge de banda de confiança,
  star de pinned (toggle), contadores applied/violated, badge 'Injetado agora'
  quando eligible; ações pin/unpin, 'ainda vale'/'não vale mais', excluir
  com confirmação
- estados loading (skeleton), empty e erro no padrão das outras páginas
- hooks React Query (useProfile + mutations) com refetch via invalidação
- item 'Perfil' registrado na navegação lateral (lib/nav.ts) + NavId
- contratos ProfileFact/ProfileResponse em lib/contracts.ts
- testes vitest: hooks (mutations + 400) e componentes (lista + form)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BrunooMoniz BrunooMoniz merged commit d1921e5 into main Jun 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant