docs: doc13 Forms Pro (WPForms+Fluent Forms) + doc14 Project Memory blueprints

Fundamentados em código real: WPForms Lite 2.0.0.5 + Fluent Forms 6.2.12
(activos em ecommerce-demo.descomplicar.pt), e CARL+Mem0/Hindsight (sistemas
de memória/governança já em produção neste ecossistema). Achado central doc13:
Fluent Forms já embarca servidor MCP nativo próprio (AbilitiesRegistrar, 9
abilities, dry-run+confirm_token) - recomenda delegar em vez de reimplementar.
INDEX.md actualizado com as 2 novas entradas e totais.
This commit is contained in:
Claude Code
2026-08-19 07:58:31 +01:00
parent 58acdd71e3
commit 2812cd79ba
15 changed files with 3535 additions and 573 deletions
+553
View File
@@ -0,0 +1,553 @@
# 13 — Forms Pro: blueprint de abilities MCP (WPForms + Fluent Forms)
Fonte primária: leitura directa (19-08-2026) do **WPForms Lite 2.0.0.5** e do **Fluent Forms
(free) 6.2.12**, ambos instalados e **activos** em `ecommerce-demo.descomplicar.pt`
(`/home/ealmeida/ecommerce-demo.descomplicar.pt/wp-content/plugins/{wpforms-lite,fluentform}/`),
confirmado por `wp plugin list --format=json` antes de qualquer leitura de código (secção 6).
Cruzado com `docs/08-INTEGRACOES-TERCEIROS.md` §3-§4 (a classe base **real** e **Free**
`EMCP_Tools_Form_Integration` + a integração CF7 concreta, únicas peças do domínio "Forms" que
existem no código EMCP Tools instalado) e `docs/10-MODULOS-E-INVENTARIO-PRO.md` linhas 470-476
(confirmação do gate `EMCP_Tools_WPForms_Integration`/`EMCP_Tools_FluentForms_Integration`
— ambas Pro-only, ficheiro ausente desta árvore). **Exclui por completo** SEO/Yoast/Rank
Math/WooCommerce, fora de âmbito.
## 0. Panorama — porque este documento é diferente dos outros 12
Este documento tem a mesma forma que o 11 (WooCommerce) e o 12 (Widget Builder): a peça que
faltaria auditar — `EMCP_Tools_WPForms_Integration`/`EMCP_Tools_FluentForms_Integration` — está
**fisicamente ausente** desta árvore Free (confirmado em `docs/08` §7 e `docs/10` linhas 470-476:
referenciadas por `class_exists()` no `class-ability-registrar.php`, nunca `require_once`).
O que muda aqui é a base sobre a qual se propõe o desenho: existe, **e é código real, Free, já
lido na íntegra**, uma classe abstracta partilhada `EMCP_Tools_Form_Integration`
(`includes/abilities/forms/class-form-integration.php`, doc08 §3) da qual **uma** integração
concreta real herda — `EMCP_Tools_CF7_Integration` (doc08 §4). WPForms e Fluent Forms seriam,
numa implementação real do EMCP Pro, mais duas classes que estendem a MESMA base — não um
desenho paralelo. Este documento não reinventa o dispatcher; reaproveita o contrato já confirmado
no doc08 e preenche as duas células concretas que faltam.
Três fontes fundamentam cada afirmação abaixo, e cada tabela di-lo explicitamente:
1. **A classe base + CF7, código real Free já lido** (`docs/08-INTEGRACOES-TERCEIROS.md` §3-§4) —
o contrato `id()/label()/is_active()/operations()`, o dispatcher `dispatch()`, a gate de
confirmação genérica (`arguments.confirm===true`), as capabilities coarse (`can_read()=
edit_posts`, `can_write()=manage_options`), e a nota de que **`destructive=true` é o default do
tool `write` desta base específica** (diferente de ACF/Meta Box/SEO) — citado tal-qual do doc08.
2. **O catálogo de 4 tools/11 operações da captura de ecrã** partilhada pelo utilizador — usado
**apenas** como especificação da API-alvo (nomes de tool, descrições, lista de operações),
nunca como fonte de schema ou de comportamento interno. Citado abaixo tal-qual foi fornecido,
com a marcação `[CAPTURA DE ECRÃ]`.
3. **Leitura directa do código real de WPForms Lite e Fluent Forms** — os dois plugins que a
captura nomeia, ambos confirmados **instalados e activos** no mesmo site do bundle Descomplicar
(`ecommerce-demo.descomplicar.pt`), e lidos ao nível dos métodos concretos que uma implementação
real teria de chamar. Secções 1 e 2.
> **Convenção**: `[REAL]` = confirmado por leitura de código (EMCP Free, WPForms Lite ou Fluent
> Forms). `[CAPTURA DE ECRÃ]` = vem apenas da imagem partilhada pelo utilizador (nome do tool,
> descrição, lista de operações) — usada como especificação-alvo, não como fonte de comportamento.
> `[DESENHO PRÓPRIO]` = proposta nossa sem equivalente confirmado em nenhuma das duas fontes
> anteriores.
**A descoberta mais importante deste documento**: ao ler o código de Fluent Forms para fundamentar
a tabela de ability proposta, descobriu-se que a versão instalada (6.2.12) **já embarca o seu
próprio servidor MCP nativo** — `FluentForm\App\Modules\MCP\AbilitiesRegistrar`, registado via
`wp_register_ability()` (a mesma API core que o EMCP Tools usa), com 9 abilities reais, um padrão
dry-run+confirm-token mais rigoroso do que qualquer coisa documentada nesta série até agora, e um
modelo de permissões (`Acl`) próprio. Isto **não é o EMCP Tools** — é uma superfície MCP
completamente independente e paralela, que o próprio Fluent Forms passou a incluir. A secção 2.3
documenta-a na íntegra porque muda a recomendação de blueprint: uma integração EMCP real para
Fluent Forms poderia **detectar e delegar** nesta camada nativa em vez de reimplementar CRUD do
zero — algo que não se aplica a nenhuma das outras integrações de terceiros desta série. **WPForms
Lite não tem nada equivalente** (confirmado por ausência de qualquer pasta/classe MCP na árvore
`src/` do plugin, secção 1).
**Segunda distinção importante, também confirmada por leitura de código**: existem **dois eixos
free/pro independentes** em jogo aqui, não um. O eixo 1 é o do EMCP Tools em si (Free vs Pro) — já
documentado em doc08/doc10, é o que decide se `wpforms-read`/`wpforms-write` existem de todo como
abilities MCP. O eixo 2 é **interno a cada plugin de formulários** — WPForms Lite vs WPForms Pro —
e é **completamente ortogonal** ao primeiro: mesmo com o EMCP Tools Pro totalmente licenciado, a
operação `list-entries` de `wpforms-read` nunca teria dados para devolver contra uma instalação
WPForms **Lite**, porque a Lite genuinely não guarda submissões (secção 1.4). A frase da captura de
ecrã — *"entries require WPForms Pro"* — refere-se exactamente a este segundo eixo, e a secção 1.4
confirma-a byte a byte no código lido.
---
## 1. WPForms (Lite 2.0.0.5)
### 1.1 Ficheiros lidos
`includes/class-form.php` (`WPForms_Form_Handler`, integral), `src/WPForms.php` (bootstrap +
registry `obj()`, integral), `src/Access/Capabilities.php` (integral),
`includes/functions/access.php` (integral), `lite/wpforms-lite.php` (integral — bootstrap
específico da build Lite, incl. o painel de Entries com dados de amostra).
### 1.2 O contrato de dados real: `WPForms_Form_Handler` `[REAL]`
Registado em `wpforms()->obj('form')` (só `'form'` e `'process'` são registados no `objects()` da
classe principal — ver 1.4). Um "formulário" WPForms é um `WP_Post` do CPT `wpforms` (ou
`wpforms-template`), com o formulário inteiro (campos, definições, notificações, confirmações)
serializado em `post_content` (JSON via `wpforms_encode()`/`wpforms_decode()`), não em postmeta
individual.
| Método | O que faz | Nota |
|---|---|---|
| `get( $id = '', $args = [] )` | Sem `$id`: `get_multiple()` (lista, `WP_Query`-like via `get_posts()`, aceita `search`/`post_status`/etc.). Com `$id`: `get_single()` — devolve `WP_Post` ou, com `content_only:true`, o array JSON decodificado | Filtro de gate: se `$args['cap']` fornecida (ou detectada automaticamente via `access_obj->init_allowed()`), chama `wpforms_current_user_can($cap, $id)` antes de devolver |
| `add( $title, $args, $data )` | Cria um formulário novo (post + `post_content` inicial); requer capability `create_forms` | Se criado fora do form builder (`data.builder===false`), popula notificação/confirmação por omissão automaticamente |
| `update( $form_id, $data, $args )` | Substitui o `post_content` inteiro por `wpforms_encode($data)`, preservando `meta` e `fields[].meta` do estado anterior (`get_meta()`/`update__preserve_fields_meta()`) | Capability por omissão: `edit_form_single` |
| `delete( $ids )` | Apaga o post **definitivamente** (`wp_delete_post($id, true)` — sem trash), e em cascata `entry`/`entry_meta`/`entry_fields` **se esses objectos existirem no registry** (nunca existem em Lite — ver 1.4) | Capability: `delete_form_single` |
| `duplicate( $ids )` | Clona formulário(s), renomeia com sufixo `(ID #N)`, injecta avisos de "Zap desligado" se aplicável | Capability: `create_forms` para criar + `view_form_single` para ler o original |
| `update_status( $form_id, $status )` | Só aceita `publish`/`trash` (filtrável); é o mecanismo real de "apagar suavemente" um formulário | **Nota do código**: usa a capability `delete_form_single`, não `edit_form_single`, mesmo para restaurar — comentário explícito: "trash and restore are actually part of the deletion operation" |
| `get_meta`/`update_meta`/`delete_meta` | Ler/escrever/apagar uma chave dentro do bloco `meta` do formulário (usado para `category`/`subcategory`, entre outros) | Capability por omissão: `edit_form_single` |
| `next_field_id`, `get_field`, `get_field_meta` | Utilitários de builder — atribuição do próximo ID de campo, leitura de um campo/a sua meta | — |
### 1.3 O colapso de capabilities em Lite: tudo vira `manage_options` `[REAL]`
Citação directa e integral de `src/Access/Capabilities.php` (a classe real por trás de
`wpforms()->obj('access')`, que `wpforms_current_user_can()` chama):
```php
public function current_user_can( $caps = [], $id = 0 ) {
return \current_user_can( \wpforms_get_capability_manage_options() );
}
```
`wpforms_get_capability_manage_options()` devolve `'manage_options'` (filtrável, mas sem filtro
aplicado nesta instalação). **Independentemente do argumento `$caps` passado** — `create_forms`,
`edit_form_single`, `delete_form_single`, `view_form_single`, `view_own_forms`,
`view_others_forms`, etc. — o resultado é sempre o mesmo: `current_user_can('manage_options')`.
Esta é a implementação de `Access Controls` no Lite; a versão Pro do WPForms substitui esta classe
por uma com granularidade real por role (é uma feature Pro do **WPForms em si**, não do EMCP).
**Implicação directa para o desenho da ability**: ao contrário do padrão documentado em `docs/08`
§0 para ACF/Meta Box/Forms ("gate coarse larga no MCP + gate fina por operação dentro do
`dispatch()`"), contra uma instalação **WPForms Lite** a gate fina É a gate coarse — não há
separação real de privilégio possível entre `list-forms` (deveria ser `edit_posts`-like) e
`delete-form` (deveria exigir mais) enquanto o alvo for Lite. Uma implementação honesta deveria
expor este facto ao agente (ex.: um campo `effective_gate: 'manage_options (WPForms Lite has no
granular Access Controls)'` na resposta de descoberta), em vez de fingir que os `perm` por operação
da tabela 1.6/1.7 têm efeito real nesta instalação concreta.
### 1.4 "Entries": ausência real confirmada, não apenas mencionada na descrição `[REAL]`
Duas confirmações independentes, ambas por leitura directa de código, provam que WPForms Lite
**não tem** armazenamento de submissões — não é uma limitação de licença que a API esconda, é
código genuinamente ausente:
1. **O registry nunca regista `'entry'`.** `src/WPForms.php::objects()` (chamado em
`plugins_loaded`):
```php
public function objects(): void {
$this->registry['form'] = new WPForms_Form_Handler();
$this->registry['process'] = new WPForms_Process();
do_action( 'wpforms_loaded' );
}
```
Apenas `form` e `process`. `obj( 'entry' )`/`obj( 'entry_meta' )`/`obj( 'entry_fields' )`
devolvem sempre `null` (`return $this->registry[$name] ?? null;`) — o próprio
`WPForms_Form_Handler::delete()` já lida com isto defensivamente:
```php
$entry_obj = wpforms()->obj( 'entry' );
$entry_meta_obj = wpforms()->obj( 'entry_meta' );
$entry_fields_obj = wpforms()->obj( 'entry_fields' );
if ( $entry_obj && $entry_meta_obj && $entry_fields_obj ) { /* delete entries */ }
```
Em Lite este `if` é sempre falso — não há nada para apagar porque não há classe registada.
2. **A página admin "Entries" mostra dados FALSOS, codificados no plugin.** `lite/wpforms-lite.php`,
método `get_entries_list_data()`, devolve um array PHP fixo (`'Michael Johnson'`,
`'David Thompson'`, `'Sarah Parker'`, …) — 12 nomes hard-coded, nenhum vindo de base de dados —
apresentado com um CTA de upgrade. O comentário da UI é explícito: *"Display sample data and
notify user that entries is a pro feature."*
**Conclusão directa para a ability proposta**: as operações `list-entries`/`get-entry` (secção
1.6) e `update-entry-status`/`delete-entry` (secção 1.7) da captura de ecrã **não têm nenhuma
superfície de código a auditar nesta instalação Lite** — a única grounding possível é negativa
(confirmar que genuinamente não existem), o que casa exactamente com o parêntesis da própria
descrição capturada: *"entries require WPForms Pro"*.
### 1.5 Estrutura real de notificações e confirmações `[REAL]`
De `lite/wpforms-lite.php::form_settings_notifications()`/`form_settings_confirmations()` — os
dois blocos do formulário guardados em `post_content.settings`:
| Bloco | Chaves reais confirmadas | Nota |
|---|---|---|
| `notifications` (array indexado por ID, `'1'` = "Default Notification") | `email` (destinatário; suporta lista separada por vírgulas e smart tags `{admin_email}`/`{user_email}`), `carboncopy` (CC — só visível se a definição global `email-carbon-copy` estiver activa), `subject`, `sender_name`, `sender_address`, `replyto`, `message` (default `{all_fields}`) | Em Lite só a notificação `1` é editável na UI ("Add New Notification" é um `education-modal` — upsell); a estrutura de dados em si já suporta múltiplas |
| `confirmations` (array indexado por ID, `'1'` = "Default Confirmation") | `type` (`message`\|`page`\|`redirect`), `message`, `message_scroll` (bool), `page` (post ID), `page_url_parameters`, `redirect` (URL), `redirect_new_tab` (bool) | Mesma limitação de UI que notifications em Lite |
### 1.6 Tabela de ability proposta — `emcp-tools/wpforms-read`
**Descrição alvo `[CAPTURA DE ECRÃ]`**: "Read WPForms forms, fields, notifications, and entries
(entries require WPForms Pro)". PRO. READ-ONLY. 6 operações.
| operação `[CAPTURA DE ECRÃ]` | Fundamentação `[REAL]` | Args propostos | Nível de risco |
|---|---|---|---|
| `list-forms` | `WPForms_Form_Handler::get( '', $args )` → `get_multiple()`; título/estado/contagem de campos derivados de `post_title`/`post_status`/`content_only` decode | `{ search?, status?, page?, per_page? }` | readonly |
| `get-form` | `get( $form_id, ['content_only'=>true] )` → devolve o array JSON completo: `fields[]`, `settings` (incl. `notifications`, `confirmations`), `meta` | `{ form_id }` | readonly |
| `list-notifications` | Subconjunto `settings.notifications` do form completo (secção 1.5) | `{ form_id }` | readonly |
| `get-settings` | Subconjunto `settings.confirmations` + campos gerais não-notificação de `settings` (`form_title`, `form_desc`, `submit_text`, etc.) | `{ form_id }` | readonly |
| `list-entries` | **Sem grounding possível nesta instalação** — `wpforms()->obj('entry')` é `null` em Lite (secção 1.4); a operação só teria dados numa instalação **WPForms Pro** real (código não presente em nenhum lado desta série) | `{ form_id, status?, page?, per_page? }` | readonly — **mas sempre devolveria `plugin_inactive`/equivalente contra Lite**, não uma lista vazia disfarçada de sucesso |
| `get-entry` | Idem — sem grounding; depende de `WPForms_Entry_Handler` (Pro), classe ausente de toda a árvore auditada nesta série | `{ entry_id }` | readonly — mesma nota |
### 1.7 Tabela de ability proposta — `emcp-tools/wpforms-write`
**Descrição alvo `[CAPTURA DE ECRÃ]`**: "Update WPForms notifications, set entry status, and
delete entries (confirm:true). Entry operations require WPForms Pro." PRO. DESTRUCTIVE. 3
operações.
| operação `[CAPTURA DE ECRÃ]` | Fundamentação `[REAL]` | Args propostos | Nível de risco |
|---|---|---|---|
| `update-notification` | `WPForms_Form_Handler::update( $form_id, $data )` com `$data['settings']['notifications'][$id]` alterado — a chamada real substitui o `post_content` inteiro, por isso uma implementação correcta tem de fazer merge no lado do EMCP: ler o form completo, alterar só a notificação alvo, escrever de volta | `{ form_id, notification_id, email?, carboncopy?, subject?, sender_name?, sender_address?, replyto?, message? }` | write, não-destructive por si (mas o tool inteiro herda `destructive=true` da base, secção 3) |
| `update-entry-status` | Sem grounding — depende de `entry`/`entry_meta` (Pro, ausentes) | `{ entry_id, status }` | write — sempre `plugin_inactive` contra Lite |
| `delete-entry` | Sem grounding — mesma ausência; a base `EMCP_Tools_Form_Integration::dispatch()` (doc08 §3.2) já suporta nativamente a gate `confirm:true` que a descrição da captura exige ("confirm:true") — esse mecanismo **já existe e é real**, mesmo que os dados que apagaria não existam em Lite | `{ entry_id, confirm: true }` | **destructive** — irreversível numa instalação Pro real |
---
## 2. Fluent Forms (free 6.2.12)
### 2.1 Ficheiros lidos
`app/Models/Form.php`, `app/Models/Submission.php` (ambos integrais — ORM `FluentForm\Framework`,
tabelas `fluentform_forms`/`fluentform_submissions`), `app/Api/Form.php`, `app/Api/Entry.php`
(integrais — a fachada pública de leitura usada por shortcodes/relatórios internos),
`app/Http/Policies/FormPolicy.php`, `app/Http/Policies/SubmissionPolicy.php` (integrais),
`app/Http/Controllers/McpSettingsController.php` (integral), `app/Modules/MCP/
AbilitiesRegistrar.php` (integral), `app/Modules/MCP/Tools/FormTools.php`,
`app/Modules/MCP/Tools/SubmissionTools.php` (integrais), `app/Modules/MCP/Support/
PermissionGate.php`, `app/Modules/MCP/Support/WriteGuard.php` (integrais),
`app/Modules/Acl/Acl.php` (integral).
### 2.2 O contrato de dados real: `Form`/`Submission` (ORM `FluentForm\Framework`) `[REAL]`
Ao contrário do WPForms (post type + JSON em `post_content`), Fluent Forms usa **tabelas próprias
do plugin**: `fluentform_forms` (colunas confirmadas via docblock `@property`: `id, title, status,
appearance_settings, form_fields, has_payment, type, conditions, created_by, created_at,
updated_at`) e `fluentform_submissions` (`id, form_id, serial_number, response, source_url,
user_id, status, is_favourite, browser, device, ip, city, country, payment_status, payment_method,
payment_type, currency, payment_total, total_paid, created_at, updated_at`). `status` do
formulário: `published`/`unpublished` (confirmado em `Form::prepare()`, default `'published'`).
`status` da submissão: `unread`/`read`/`spam`/`trashed` (confirmado directamente na `SubmissionTools`
nativa — secção 2.3 — e em `Submission::countByGroup()`); `is_favourite` é um campo **separado**
(boolean), não um valor de `status`.
`Form::remove($formId)` (estático) e `Submission::remove($submissionIds, $formId)` (estático,
agora **exige** `$formId` — `throw new \InvalidArgumentException` se omitido, um "fail-closed scope
guard" explícito no código) fazem cascata real de apagamento: meta, entry details, analytics, logs,
e (se pagamentos estiverem activos) order items/transactions/subscriptions.
### 2.3 A descoberta central: Fluent Forms já embarca o seu próprio servidor MCP nativo `[REAL]`
Não é uma integração de terceiros comum — é uma **superfície MCP paralela e independente do EMCP
Tools**, presente na versão 6.2.12 instalada, registada com a mesma API core WordPress
(`wp_register_ability()`) que o próprio EMCP Tools usa. A classe orquestradora é
`FluentForm\App\Modules\MCP\AbilitiesRegistrar` (citação directa do seu docblock):
> "Single source of truth for every FluentForm MCP ability... Pro tools are NOT listed here — they
> push abilities via the `fluentform/mcp_loaded` action + `fluentform/mcp_ability_names` filter."
**Estado ligado/desligado**: a feature vem **desligada por omissão** — `PermissionGate::isEnabled()`
lê a option dedicada `_fluentform_mcp_settings['enabled']==='yes'`, activável em FluentForm →
Settings → MCP (`McpSettingsController::toggle()`, exige `manage_options`). Requer também um
plugin adaptador MCP instalado (`fluent-toolkit` ou `mcp-adapter`) — nenhum dos dois está instalado
nesta instalação, por isso este servidor MCP nativo não está actualmente a correr em
`ecommerce-demo.descomplicar.pt`, mas o código das 9 abilities está completo e presente.
**As 9 abilities reais registadas** (`FormTools` + `SubmissionTools`; há mais 6 classes
`IntegrationTools`/`StylingTools`/`FieldTools`/`NotificationTools`/`ReportTools`/`ContextTools`
não detalhadas aqui por estarem fora do âmbito "forms" directo desta tabela, mas seguem o mesmo
padrão):
| Ability (nome real) | mode | `capability` real | Descrição (citação exacta do código) |
|---|---|---|---|
| `fluentform/list-forms` | read | `['fluentform_forms_manager', 'fluentform_dashboard_access']` (qualquer uma) | "Find and filter forms with compact rows (id, title, status, type, entry count)." |
| `fluentform/get-form` | read | idem | "Full detail for one form: status, type, timestamps, and the field schema." |
| `fluentform/create-form` | write | `fluentform_forms_manager` | Cria formulário a partir de título + lista de campos tipados |
| `fluentform/list-submissions` | read | `fluentform_entries_viewer` | "List and filter entries for one form (form_id required)." |
| `fluentform/get-submission` | read | `fluentform_entries_viewer` | "Full detail for one entry by id... fields as label/value pairs." |
| `fluentform/update-submission-status` | write | `fluentform_manage_entries` | `status` ∈ `{unread, read, spam, trashed}`; `trashed` é soft-delete reversível |
| `fluentform/add-submission-note` | write | `fluentform_manage_entries` | Nota interna de staff, não visível ao submissor |
| `fluentform/delete-submission` | write, **destructive** | `fluentform_manage_entries` | Apagamento **permanente**, irreversível |
| `fluentform/bulk-update-submissions` | write, **destructive** | `fluentform_manage_entries` | Acção em lote (`read`\|`unread`\|`trashed`\|`favorite`\|`unfavorite`\|`delete_permanently`), máx. 200 IDs por chamada |
**O mecanismo de segurança mais rigoroso documentado nesta série inteira**: TODAS as 6 operações
de escrita (mesmo `add-submission-note`, que não parece destructive) passam por
`Mutation::runGuarded()`, que impõe um round-trip **dry-run → `confirm_token` → execução** —
citação directa do docblock de `WriteGuard`:
> "EVERY write routes through `Mutation::runGuarded`, not just the destructive ones. The reason is
> prompt injection: entry field values are written by anonymous members of the public and land in
> the agent's context... The dry-run → confirm_token round-trip is the one mechanism here that
> forces a second pass through the operator before anything is written, so an instruction smuggled
> into a form submission cannot complete a write on its own."
O token é vinculado a um **fingerprint do estado actual** da entidade (ex.: para
`update-submission-status`, o `status` actual faz parte da chave — um token gerado para
"aprovar mudar para `read`" nunca pode ser reaproveitado para `trashed`); expira em 300s
(`CONFIRM_TTL`); há ainda uma chave de idempotência opcional (`idempotency_key`, TTL 86400s) para
retries seguros, e um mecanismo de "claim" via `INSERT IGNORE` atómico em `wp_options` para
serializar execuções concorrentes do mesmo `(tool, entidade)`. Todo o texto de campos submetidos
por visitantes anónimos que chega ao agente é explicitamente delimitado com marcadores
`[[UNTRUSTED_USER_INPUT]]` (`MCPHelper::untrusted()`) antes de sair de `get-submission`/
`list-submissions` — uma defesa directa contra prompt injection via conteúdo de formulário.
**Nota de segurança honesta, citada do próprio código** (docblock de `PermissionGate`), sobre um
limite real deste modelo: `Acl::hasPermission()` concede qualquer permissão FluentForm (excepto
`fluentform_full_access`) a um utilizador cujo *role* WordPress tenha sido delegado via
"Managers" a nível de role — o que significa que um Editor com acesso FluentForm delegado por role
**passa a gate de capability em TODAS as ferramentas, incluindo `delete-submission`**,
independentemente da capability específica que essa ferramenta declara. Só utilizadores com
concessão **individual** (`Acl::attachPermissions()`, per-user) são de facto restringidos como as
declarações da tabela acima sugerem. Citação directa: *"the real boundary is 'authenticated
WordPress user with FluentForm access, restricted to their form scope' — NOT per-tool capability
separation. Do not describe it as the latter."*
### 2.4 Capabilities reais / modelo `Acl`
8 permissões nativas (`Acl::getPermissionSet()`): `fluentform_dashboard_access`,
`fluentform_forms_manager`, `fluentform_entries_viewer`, `fluentform_manage_entries`,
`fluentform_view_payments`, `fluentform_manage_payments`, `fluentform_settings_manager`,
`fluentform_full_access`. **Por omissão só a role `administrator` as recebe** — `Acl::setPermissions()`
adiciona as 8 capabilities apenas a `get_role('administrator')`; qualquer outro utilizador/role
precisa de concessão explícita via FluentForm → Settings → Managers. `manage_options` bypassa
sempre tudo (`hasExplicitFullAccess()`).
### 2.5 Tabela de ability proposta — `emcp-tools/fluentforms-read`
**Descrição alvo `[CAPTURA DE ECRÃ]`**: "Read Fluent Forms forms, fields, and submissions." PRO.
READ-ONLY. 4 operações.
| operação `[CAPTURA DE ECRÃ]` | Fundamentação `[REAL]` | Equivalente nativo directo | Args propostos |
|---|---|---|---|
| `list-forms` | `Form` model + `Api\Form::forms()` (fachada pública já existente, paginação/pesquisa/filtro por `status`/`filter_by` embutidos) | **Mapeia 1:1** para a ability nativa `fluentform/list-forms` (secção 2.3) | `{ search?, status?, page?, per_page? }` |
| `get-form` | `Api\Form::find()`/`form()` + `FormFieldsParser`/`FormService::getInputsAndLabels()` para o schema de campos | **Mapeia 1:1** para `fluentform/get-form` | `{ form_id }` |
| `list-entries` | `Api\Entry::entries()` (fachada pública) ou `Submission::customQuery()`/`paginateEntries()` directamente | **Mapeia 1:1** para `fluentform/list-submissions` — inclui o mesmo enum `status ∈ {unread, read, spam, trashed, favorites}` | `{ form_id, status?, search?, date_from?, date_to?, page?, per_page? }` |
| `get-entry` | `Api\Entry::entry()`/`getFormattedEntry()` (label+valor por campo, resolve utilizador associado) | **Mapeia 1:1** para `fluentform/get-submission` | `{ entry_id }` |
### 2.6 Tabela de ability proposta — `emcp-tools/fluentforms-write`
**Descrição alvo `[CAPTURA DE ECRÃ]`**: "Set Fluent Forms submission status and delete submissions
(confirm:true)." PRO. DESTRUCTIVE. 2 operações.
| operação `[CAPTURA DE ECRÃ]` | Fundamentação `[REAL]` | Equivalente nativo directo | Args propostos |
|---|---|---|---|
| `update-entry-status` | `Submission::amend($id, ['status'=>...])`, ou via a fachada `SubmissionService::updateStatus()` | **Mapeia 1:1** para `fluentform/update-submission-status` — mesmo enum `unread/read/spam/trashed`, mesma nota de que `trashed` é reversível | `{ entry_id, status, confirm: true }` |
| `delete-entry` | `Submission::remove($submissionIds, $formId)` (estático; agora exige `$formId` explícito — "fail-closed scope guard", citação directa do código) — cascata para meta/entry-details/logs/pagamentos | **Mapeia 1:1** para `fluentform/delete-submission` (`annotations.destructive=true` na base MCP) | `{ entry_id, form_id, confirm: true }` |
**Nota de design que a captura já reflecte correctamente**: a descrição alvo `(confirm:true)` para
`fluentforms-write` casa exactamente com o mecanismo genérico já real na base
`EMCP_Tools_Form_Integration::dispatch()` (`arguments.confirm===true` obrigatório quando
`operations()[$op]['confirm']===true`, doc08 §3.2). O que o EMCP Tools faria de mais simples aqui
(gate binária de confirmação) já existe hoje na própria Fluent Forms de forma **mais forte**
(dry-run + token vinculado a fingerprint de estado + expiração + idempotência) — uma diferença de
desenho relevante para a secção 5.
### 2.7 Assimetria WPForms vs Fluent Forms — confirmada, não presumida
A árvore `src/` de WPForms Lite (listada na íntegra: `Access, Admin, Analytics, Db, Education,
Emails, Forms, Frontend, Helpers, Integrations, Lite, Logger, Migrations, Providers,
Requirements, SetupChecklist, SetupWizard, SmartTags, Tasks`, mais `API.php`/`ErrorHandler.php`/
`Loader.php`/`WPForms.php` de topo) **não contém nenhuma pasta ou classe MCP** — `src/API.php` é um
registo interno de callbacks (import de formulários), sem qualquer relação com Model Context
Protocol. Fluent Forms, pelo contrário, tem uma pasta dedicada `app/Modules/MCP/` com 9 abilities
reais completas. Esta assimetria é directamente relevante para a secção 5 (prioridade de
construção): uma integração EMCP para Fluent Forms tem uma superfície de referência real e
testável para se alinhar (ou mesmo delegar); uma para WPForms tem de ser construída inteiramente
de raiz sobre `WPForms_Form_Handler`.
---
## 3. Comparação com o padrão base real (`EMCP_Tools_Form_Integration::dispatch()`)
Citação do contrato já confirmado em `docs/08-INTEGRACOES-TERCEIROS.md` §3.1-§3.2: cada integração
concreta implementa `id()`, `label()`, `is_active()`, `operations(): array<string,array>` — mapa
`nome => { mode, run, perm, desc, confirm? }` — e a base fornece `register()` (regista os dois
tools `<id>-read`/`<id>-write`), `can_read()=edit_posts`, `can_write()=manage_options`, e
`dispatch()` (resolve `operation`, verifica `is_active()`, valida existência+modo, corre `perm`,
aplica a gate de confirmação genérica se `confirm===true`).
Aplicando isto às duas famílias deste documento:
```php
// Esqueleto de EMCP_Tools_WPForms_Integration extends EMCP_Tools_Form_Integration
public function id(): string { return 'wpforms'; }
public function label(): string { return 'WPForms'; }
public function is_active(): bool { return class_exists( 'WPForms_Form_Handler' ) || defined( 'WPFORMS_VERSION' ); }
public function operations(): array {
return [
'list-forms' => [ 'mode' => 'read', 'run' => [ $this, 'op_list_forms' ], 'perm' => 'edit_posts' ],
'get-form' => [ 'mode' => 'read', 'run' => [ $this, 'op_get_form' ], 'perm' => 'edit_posts' ],
'list-notifications' => [ 'mode' => 'read', 'run' => [ $this, 'op_list_notif' ], 'perm' => 'edit_posts' ],
'get-settings' => [ 'mode' => 'read', 'run' => [ $this, 'op_get_settings' ], 'perm' => 'edit_posts' ],
'list-entries' => [ 'mode' => 'read', 'run' => [ $this, 'op_list_entries' ], 'perm' => 'edit_posts' ], // op_list_entries devolve plugin_inactive-like se wpforms()->is_pro()===false
'get-entry' => [ 'mode' => 'read', 'run' => [ $this, 'op_get_entry' ], 'perm' => 'edit_posts' ],
'update-notification' => [ 'mode' => 'write', 'run' => [ $this, 'op_update_notif' ], 'perm' => 'manage_options' ],
'update-entry-status' => [ 'mode' => 'write', 'run' => [ $this, 'op_update_status' ], 'perm' => 'manage_options', 'confirm' => true ],
'delete-entry' => [ 'mode' => 'write', 'run' => [ $this, 'op_delete_entry' ], 'perm' => 'manage_options', 'confirm' => true ],
];
}
```
A gate `confirm=>true` mapeia directamente para o `(confirm:true)` que a própria descrição da
captura de ecrã já anuncia para `wpforms-write` — sem inventar mecanismo novo, o que já existe na
base serve tal-qual. O mesmo esqueleto para `EMCP_Tools_FluentForms_Integration` seria idêntico na
forma, mas cada `run` callback delegaria para os métodos estáticos já reais de
`FluentForm\App\Api\Form`/`Entry` (secção 2.2) — **ou**, mais interessante dado o achado da secção
2.3, para os métodos estáticos das próprias classes `FormTools`/`SubmissionTools` nativas do
Fluent Forms, se detectadas presentes (ver secção 5).
**Nota sobre `destructive` a nível de tool**: a base Forms marca o tool `write` inteiro como
`destructive=true` por omissão (doc08 §3.2, diferente de ACF/Meta Box/SEO) — precisamente porque,
ao contrário de CF7 (a única integração Free, que nunca apaga nada), tanto `wpforms-write` como
`fluentforms-write` têm pelo menos uma operação genuinamente destructive (`delete-entry`). A
captura de ecrã confirma isto explicitamente com a etiqueta "DESTRUCTIVE" em ambos os tools write.
---
## 4. As outras 6 integrações Pro de formulários — sem grounding possível
Gravity Forms, Ninja Forms, Formidable Forms, MetForm, SureForms e Forminator estão todas listadas
em `docs/08-INTEGRACOES-TERCEIROS.md` §7 como classes referenciadas só por `class_exists()` no
registrar, com ficheiro fisicamente ausente da árvore Free. Confirmação adicional própria desta
tarefa: **nenhum dos dois sites verificados** para este documento (`care.descomplicar.pt` e
`ecommerce-demo.descomplicar.pt`) tem qualquer um destes 6 plugins instalado — a listagem completa
de `wp plugin list` de ambos os sites (secção 6) não contém `gravityforms`, `ninja-forms`,
`formidable`, `metform`, `sureforms`, nem `forminator`/`forminator-pro`. Sem código a ler em lado
nenhum, este documento **não propõe** tabelas de ability para estas seis — inventar schemas sem
fundamento contradiz o princípio desta série. Ficam confirmadas apenas como ausentes (mesma tabela
de gating do doc08 §7, não reproduzida aqui).
---
## 5. Blueprint para réplica
### 5.1 Copiar quase 1:1
- **A classe base `EMCP_Tools_Form_Integration` inteira** (doc08 §3) — contrato
`id()/label()/is_active()/operations()`, dispatcher, gate de confirmação genérica,
`can_read()=edit_posts`/`can_write()=manage_options`, e a anotação `destructive=true` por
omissão no tool write. Nada disto precisa de ser redesenhado; WPForms e Fluent Forms encaixam
nele exactamente como CF7 já encaixa.
- **O padrão dry-run + `confirm_token` vinculado a fingerprint de estado da Fluent Forms nativa**
(secção 2.3) — é uma evolução directa, e comprovadamente já em produção num plugin real, do
mecanismo `confirm:true` simples da base EMCP. Vale a pena propor subir este padrão mais rico
para a base `EMCP_Tools_Form_Integration` em geral (aplicável também a `woo-orders-write`/etc.,
doc11 §9), não só a esta integração — a única razão para não o copiar tal-qual é que aumenta a
complexidade de qualquer chamador MCP (duas chamadas por escrita em vez de uma).
- **Escrever sempre por identificador estável e nunca confiar no valor de retorno de `update()`** —
a mesma lição já documentada para ACF (doc08 §1.3) aplica-se ao `WPForms_Form_Handler::update()`:
substitui `post_content` inteiro, por isso uma implementação EMCP tem de ler-modificar-escrever
com cuidado para não perder campos/notificações não tocados pela operação pedida.
- **A marcação `[[UNTRUSTED_USER_INPUT]]` da Fluent Forms nativa** (`MCPHelper::untrusted()`) — um
mecanismo directo e barato contra prompt injection via texto submetido por visitantes anónimos,
aplicável tal-qual a `get-entry`/`list-entries` de ambas as integrações (WPForms teria o mesmo
problema assim que ligado a WPForms Pro real).
### 5.2 Decisão específica para Fluent Forms: detectar e delegar em vez de reimplementar
Dado que a versão 6.2.x já embarca `FluentForm\App\Modules\MCP\AbilitiesRegistrar` com abilities
reais e testadas (secção 2.3), uma implementação real de `EMCP_Tools_FluentForms_Integration`
devia começar por:
```php
if ( class_exists( '\FluentForm\App\Modules\MCP\AbilitiesRegistrar' ) ) {
// Delegar: reaproveitar getDefinitions() como a fonte de operations(),
// sem reimplementar list-forms/get-form/list-submissions/get-submission/
// update-submission-status/delete-submission a partir do zero.
}
```
Isto é uma recomendação **exclusiva** desta integração dentro de toda a série de documentos —
nenhuma outra (ACF, Meta Box, WooCommerce, Widget Builder, CF7) tem um equivalente nativo já
registado via `wp_register_ability()` no próprio plugin de terceiros. O risco a mitigar: a Fluent
Forms nativa fica desligada por omissão (`PermissionGate::isEnabled()===false`) e exige um plugin
adaptador (`fluent-toolkit`/`mcp-adapter`) — uma implementação EMCP que delegasse teria de activar
programaticamente essa option, ou simplesmente reimplementar chamando os mesmos métodos estáticos
`FormTools::listForms()`/`SubmissionTools::listSubmissions()` directamente (sem depender do
registo MCP nativo estar ligado), o que é possível porque são métodos estáticos públicos.
### 5.3 Decisão específica para WPForms: gate explícita de "entries requer Pro"
Dado que `wpforms()->is_pro()` é uma verificação real e barata (`src/WPForms.php`,
`apply_filters('wpforms_allow_pro_version', $this->pro)`), as operações `list-entries`/`get-entry`/
`update-entry-status`/`delete-entry` deviam falhar explicitamente com um erro estruturado
(`wpforms_pro_required`, análogo ao `plugin_inactive` HTTP 409 já usado pela base para
`is_active()===false`, doc08 §3.2) em vez de devolver `[]`/`404` genéricos — a distinção entre
"este formulário não existe" e "esta instalação não suporta entradas de todo" é informação que o
agente precisa para não repetir a chamada.
### 5.4 Prioridade de construção
1. **`wpforms-read` (list-forms/get-form/list-notifications/get-settings) + `fluentforms-read`
(list-forms/get-form)** — zero risco, cobre o caso mais comum ("que formulários existem, o que
perguntam, para onde notificam"). Para Fluent Forms, literalmente delegar em `FormTools`
(secção 5.2) reduz isto a um wrapper fino.
2. **`fluentforms-read` (list-entries/get-entry)** — Fluent Forms free já guarda submissões
nativamente, sem gate Pro-do-plugin-alvo; alto valor imediato.
3. **`wpforms-write` (update-notification)** — única operação de escrita de WPForms com grounding
completo nesta instalação; sem depender de WPForms Pro.
4. **`fluentforms-write` (update-entry-status/delete-entry)** — reaproveitar o par dry-run+token da
Fluent Forms nativa (secção 5.1) em vez do `confirm:true` simples da base, dado que o código de
referência real já demonstra porque vale a pena (fingerprint de estado, replay seguro).
5. **`wpforms-read`/`wpforms-write` (as 4 operações de entries)** — só implementável e testável
contra uma instalação WPForms **Pro** real; até lá, ficam atrás da gate `wpforms_pro_required`
descrita em 5.3, nunca simuladas.
### 5.5 O que NÃO replicar sem decisão explícita
- **Reimplementar do zero o que a Fluent Forms MCP nativa já faz bem** (dry-run/confirm_token,
fencing de untrusted input, scope de formulário por utilizador via `FormAccess`) — duplicar essa
lógica dentro do EMCP Tools é trabalho redundante e uma segunda superfície para ficar
dessincronizada da primeira à medida que a Fluent Forms evolui a sua própria API MCP.
- **Simular `list-entries`/`get-entry` de WPForms contra uma instalação Lite** com dados vazios ou
de exemplo — o próprio WPForms Lite já mostra dados de amostra na sua UI (secção 1.4) para
incentivar upgrade; uma ability MCP que fizesse o mesmo silenciosamente enganaria um agente a
pensar que está a ler dados reais.
---
## 6. Fonte
**Confirmação de instalação e estado activo** (19-08-2026), via `wp plugin list --format=json`:
```
ssh server "wp plugin list --path=/home/ealmeida/ecommerce-demo.descomplicar.pt --allow-root --format=json"
→ fluentform active 6.2.12
→ wpforms-lite active 2.0.0.5
```
Listagem prévia de `wp-content/plugins/` de `care.descomplicar.pt` (sem WPForms nem Fluent Forms —
descartado como site-alvo) e de `ecommerce-demo.descomplicar.pt` (ambos presentes, `fluentform/` +
`wpforms-lite/`) — usada também para confirmar a ausência de Gravity Forms/Ninja Forms/Formidable/
MetForm/SureForms/Forminator em qualquer um dos dois sites (secção 4).
**WPForms Lite 2.0.0.5** (`wp-content/plugins/wpforms-lite/`):
- `includes/class-form.php` (`WPForms_Form_Handler`, integral)
- `src/WPForms.php` (integral — bootstrap, `objects()`, `obj()`, `register()`)
- `src/Access/Capabilities.php` (integral)
- `src/API.php` (integral — confirma que NÃO é um endpoint REST/MCP)
- `includes/functions/access.php` (integral — `wpforms_current_user_can()`, etc.)
- `lite/wpforms-lite.php` (integral — notificações/confirmações reais, painel Entries com dados de
amostra)
- Listagem de directório (sem leitura de conteúdo, confirmação de inventário): `src/` (17
subdirectórios + 4 ficheiros de topo — confirma ausência de qualquer pasta MCP),
`includes/functions/` (14 ficheiros), `includes/admin/builder/panels/` (7 ficheiros)
**Fluent Forms (free) 6.2.12** (`wp-content/plugins/fluentform/`):
- `app/Models/Form.php`, `app/Models/Submission.php` (integrais)
- `app/Api/Form.php`, `app/Api/Entry.php` (integrais)
- `app/Http/Policies/FormPolicy.php`, `app/Http/Policies/SubmissionPolicy.php` (integrais)
- `app/Http/Controllers/McpSettingsController.php` (integral)
- `app/Modules/MCP/AbilitiesRegistrar.php` (integral)
- `app/Modules/MCP/Tools/FormTools.php`, `app/Modules/MCP/Tools/SubmissionTools.php` (integrais)
- `app/Modules/MCP/Support/PermissionGate.php`, `app/Modules/MCP/Support/WriteGuard.php`
(integrais)
- `app/Modules/Acl/Acl.php` (integral)
- Listagem de directório (inventário): `app/` (9 subdirectórios), `app/Models/` (14 ficheiros),
`app/Api/` (4 ficheiros), `app/Http/Controllers/` (17 ficheiros), `app/Http/Policies/` (7
ficheiros), `app/Modules/MCP/` (2 ficheiros + 2 subdirectórios), `app/Modules/MCP/Tools/` (8
ficheiros)
**Cruzado com**: `docs/00-ARQUITECTURA.md` (padrão `emcp_tools_register_ability()`,
`normalize_result`), `docs/08-INTEGRACOES-TERCEIROS.md` §3 (`EMCP_Tools_Form_Integration`, base
abstracta real, citada extensivamente), §4 (`EMCP_Tools_CF7_Integration`, única integração
concreta real), §7 (tabela de gating das 18 integrações Pro-only, incl. WPForms e Fluent Forms),
`docs/10-MODULOS-E-INVENTARIO-PRO.md` linhas 470-476 (confirmação adicional do mesmo gate).
+613
View File
@@ -0,0 +1,613 @@
# 14 — Project Memory (Pro): blueprint de abilities MCP (desenho próprio, não é auditoria EMCP Pro)
Fonte primária: leitura directa (19-08-2026) de **duas fontes reais** fora da árvore `emcp-tools`
propriamente dita — porque, tal como em `docs/11-WOOCOMMERCE-BLUEPRINT.md` e
`docs/12-WIDGET-BUILDER-BLUEPRINT.md`, o código Pro do módulo "Project Memory" está **fisicamente
ausente** desta build Free (confirmado pelo padrão já estabelecido em `docs/10-MODULOS-E-INVENTARIO-PRO.md`:
uma classe `class_exists()`-gated cujo ficheiro nunca existe na árvore instalada — não retentado
nesta tarefa, o mesmo padrão já foi verificado exaustivamente para os outros ~30 módulos Pro). As
duas fontes reais usadas em substituição: **(A)** `~/.omp/agent/AGENTS.md` (e o documento canónico
que importa, `Hub/04-Stack/02.04-Sistemas/AGENTS.md`) — a identidade operacional deste próprio
ambiente OMP, que já opera um sistema de governança de regras com staging/aprovação (CARL) e um
sistema de memória cross-agent em camadas (Mem0), ambos **em produção, não hipotéticos**; **(B)** os
padrões de aprovação humana e de change-ledger já confirmados por código real nesta mesma série
(`docs/04-THEMER.md` §11, `docs/06-SANDBOX-CUSTOM-CODE.md` §1, `docs/05-REDIRECTS-SEARCH-LEDGER.md`
§4).
## 0. Panorama — porque este documento é diferente e o que fundamenta cada peça
A captura de ecrã da UI de administração do EMCP Tools (secção *Project Memory (Pro)*, contador
"0/3") mostra **3 tools MCP**, todas prefixadas `emcp-tools/`:
| Tool id (screenshot) | Badge | Descrição literal do utilizador |
|---|---|---|
| `emcp-tools/recall` | PRO · READ-ONLY | "Read approved guidance + recent session summaries so the agent does not re-guess site context" |
| `emcp-tools/remember` | PRO | "Propose one guardrail/fact/convention/instruction. Stored pending until a human approves it" |
| `emcp-tools/save-session-summary` | PRO | "Record a session summary; the plugin attaches a factual digest of the actual changes" |
Estes 3 nomes e as 3 descrições são usados **apenas como especificação da API-alvo** (nomenclatura,
âmbito e a promessa comportamental de cada tool) — **não** como fonte de implementação. Não há
código Pro nesta árvore para ler, logo não há input schema, mensagens de erro, nem lógica interna
real do EMCP Pro para citar. Qualquer detalhe de *como* cada tool funciona por dentro, neste
documento, é proposta própria.
Em vez de tentar adivinhar o comportamento interno, este documento faz a pergunta inversa dos docs
01-10: **dado que este próprio ambiente de trabalho (OMP/Claude Code, ecossistema Descomplicar) já
resolveu exactamente o mesmo problema** — "como é que um agente de IA lembra contexto de projecto
entre sessões sem inventar factos e sem escrever governança sem supervisão humana" — **com dois
sistemas reais e vivos em produção**, qual é o desenho mais honesto para "Project Memory" que se
pode propor sem fingir conhecer o código Pro?
### As duas fontes reais e o que cada uma fundamenta
1. **CARL** (Sistema de Regras e Governança, com pipeline staging→aprovação) — confirmado como
sistema real vivo por três evidências independentes, todas citadas literalmente ao longo deste
documento:
- O ficheiro canónico `Hub/04-Stack/02.04-Sistemas/AGENTS.md` §4 (errata 26-07-2026), citação
directa: *"As **22 regras CARL `GLOBAL`** só carregavam com o cwd dentro de `04-Stack` — o
CARL resolve scopes pelo cwd e substitui o domínio inteiro. Fora dali, `GLOBAL` tinha 3.
Consolidadas."*
- `Hub/04-Stack/02.04-Sistemas/PIPELINE-QUALIDADE-TOTAL.md` (17-07-2026), citação directa:
*"stack-observer→classify→fingerprint→propostas CARL; (…) CARL v2 GLOBAL com 26 regras"* e,
na matriz de arcos do pipeline (C6, 06→02/01): *"Propostas aprovadas → novas regras CARL /
gates / processos (…) staging CARL funcional"*.
- **O conjunto real de ferramentas MCP CARL disponíveis neste próprio ambiente** (servidor
`carl-mcp`, invocável directamente nesta sessão) — nomes de tool, descrições e schemas de
argumento reais, sem paráfrase, citados na íntegra na secção 2 abaixo. Existem **duas gerações**
coexistentes do mesmo sistema (v1, ficheiro-por-domínio; v2, `carl.json` único) — ambas reais,
ambas activas nesta instalação, e a divergência entre elas é, por si só, um dado relevante para
o desenho de "Remember Guidance" (secção 2.3).
2. **Mem0 / memória cross-agent** — confirmado como sistema real vivo pela secção 5 ("MEMÓRIA") do
`Hub/04-Stack/02.04-Sistemas/AGENTS.md`, citada quase integralmente na secção 1, e pelo conjunto
real de ferramentas MCP `mem0` disponíveis nesta sessão (servidor `mem0`, prefixo
`mcp__mem_*`) — `save_memory`, `search_memories`, `get_all_memories`, `update_memory`,
`delete_memory`, etc.
**Cruzamento adicional com código real desta série** (o mesmo grupo de precedentes já usado por
`docs/12-WIDGET-BUILDER-BLUEPRINT.md` §6.5 para justificar "zero tool de auto-activação"):
`docs/04-THEMER.md` §11 (Themer PHP: *"there is intentionally no attach tool"*) e
`docs/06-SANDBOX-CUSTOM-CODE.md` §1 (PHP Snippets: *"There is intentionally no 'activate' tool"*),
mais o change ledger unificado de `docs/05-REDIRECTS-SEARCH-LEDGER.md` §4
(`EMCP_Tools_Change_Log`/`Change_Recorder`/`Change_Blobs`) como modelo de armazenamento/rollback
para o que uma "guidance" pendente/aprovada precisaria de persistir e desfazer.
> **Convenção usada em todo o documento** (idêntica a docs 11/12): cada afirmação é marcada
> `[REAL]` quando vem directamente de uma fonte verificável nesta sessão (o próprio `AGENTS.md`, os
> ficheiros do Hub citados, ou uma chamada real a uma ferramenta MCP CARL/Mem0 feita nesta tarefa),
> ou `[DESENHO PRÓPRIO]` quando é uma proposta nossa sem equivalente confirmado no EMCP Pro. Onde
> uma peça combina as duas (ex.: mapear uma tool Pro hipotética sobre um fluxo CARL real), isso é
> dito explicitamente — e a maioria das peças deste documento está nessa categoria mista, porque
> **nenhuma das 3 tools Pro foi lida directamente**; só a promessa comportamental (a frase da
> captura de ecrã) é dado de entrada.
**A distinção mais importante de todo o documento:** ao contrário do doc 12 (onde a camada de
armazenamento `EMCP_Tools_Widget_Store` **já existe** e é código real, só a camada de compilação e
a de abilities é que faltam), aqui **nenhuma camada do sistema "Project Memory" existe fisicamente
nesta árvore Free** — nem abilities, nem store, nem CPT, nem option. Este blueprint é, por isso,
mais "desenho próprio" do que o doc 12; a âncora real está inteiramente fora do plugin, nos dois
sistemas de governança/memória que este próprio ambiente de execução já usa para resolver o mesmo
problema.
---
## 1. `recall` — ler orientação aprovada + resumos de sessão recentes
**Promessa da tool (screenshot, PRO READ-ONLY):** *"Read approved guidance + recent session
summaries so the agent does not re-guess site context."*
### 1.1 Fundamentação `[REAL]` — o sistema de memória em camadas já em produção
O `AGENTS.md` canónico documenta explicitamente uma arquitectura de memória por níveis que resolve
precisamente este problema — "não repetir contexto que já foi estabelecido". Citação directa,
`Hub/04-Stack/02.04-Sistemas/AGENTS.md` §5 ("MEMÓRIA"):
> *"### Cross-agent (Mem0)
> - Gateway: `gateway.descomplicar.pt/v1/mem0/mcp`
> - Bridge Hindsight: `localhost:8888` → mem0 local
> - REST API directa: `localhost:8090` (debug/Swagger)
> - OMP usa `memory.backend: hindsight` → bridge → mem0
> - Protocolo Wayland v2: P5=identidade, P4=procedimento, P3=facto atómico
>
> ### Níveis
> | Tier | Sistema | O quê | Automático? |
> |------|---------|-------|-------------|
> | 1 | IJFW/OMP Hindsight | Working memory de sessão | Sim |
> | 2 | Mem0 (via gateway MCP) | Factos duráveis cross-agent | Sim (bridge) |
> | 3 | Mem0 REST API (`:8090`) | Acesso directo para debug/automação | Manual"*
Além disto, o próprio `AGENTS.md` §2 ("HIERARQUIA DE FONTES") estabelece a regra geral que
`recall` deveria implementar do lado de um plugin WordPress: *"NLM-first: consultar NotebookLM
PRIMEIRO antes de perguntas técnicas"* e *"Nunca confiar em docs sem verificar: o que está escrito
pode estar errado. Só o que é verificado no terreno conta."* — ou seja, mesmo um sistema de
memória "aprovada" não é uma licença para parar de verificar; é uma optimização de "não repetir
trabalho de descoberta já feito", não uma substituição da verificação.
**Mapeamento directo:** o Tier 2 (Mem0 via gateway MCP, "factos duráveis cross-agent", automático)
é o análogo funcional mais próximo de "ler orientação aprovada + resumos recentes" — mas com uma
diferença estrutural crítica face ao desenho proposto pela EMCP Pro (ver secção 1.3).
### 1.2 Ferramentas MCP Mem0 reais desta sessão `[REAL]`
A ferramenta `search_memories` (servidor `mem0`, `mcp__mem_search_memories`) é o equivalente mais
directo de `recall`: aceita `query` (pesquisa em linguagem natural), `user_id`/`agent_id`/`run_id`
opcionais para segmentar por âmbito, `top_k` (default 5) e `threshold` (score mínimo de
similaridade). `get_all_memories` (`mcp__mem_get_all_memories`) é o equivalente de "listar tudo o
que está aprovado para este âmbito", sem query — mais próximo do comportamento "devolve a
orientação aprovada" descrito na promessa da tool Pro (uma leitura completa e determinística, não
uma pesquisa semântica top-k que pode omitir algo relevante por baixo do threshold).
### 1.3 Desenho proposto para `recall` `[DESENHO PRÓPRIO, sobre camadas [REAL]]`
| Área | O que cobre | API/sistema real usado como modelo | Nível de risco |
|---|---|---|---|
| Leitura de guardrails/factos/convenções **aprovados** | Devolve só entradas com estado `approved` — nunca `pending`/`rejected` (ver modelo de estados, secção 4) | Equivalente ao filtro implícito de `get-domain-rules`/`get-domain` do CARL real (secção 2.2), que só devolve regras já escritas no domínio, nunca as que ainda estão em staging | readonly, idempotent |
| Leitura de resumos de sessão recentes | Últimos N resumos gravados por `save-session-summary` (secção 3), ordenados por recência, com o "digest factual" já anexado pelo plugin | Análogo ao Tier 1 ("working memory de sessão", `AGENTS.md` §5) tornado persistente entre sessões — o Pro parece fundir aqui o que o ecossistema Descomplicar trata como dois tiers distintos (1 efémero, 2 durável) | readonly, idempotent |
| Filtro por âmbito (página/post/site inteiro) | `[DESENHO PRÓPRIO]` — sem equivalente directo confirmado; proposta: um parâmetro `scope?:{post_id?, site_wide?:bool}`, espelhando o padrão `emcp_themer_selectors` (`docs/04-THEMER.md` §0) de escala larga→granular por filtro | — |
| Recall keyword-driven (activação por intenção) | `[DESENHO PRÓPRIO, precedente REAL forte]` — cada domínio CARL tem `recall` (palavras-chave que o disparam, ver `carl_get_domain_rules`: *"Load rules for a specific CARL domain. Use when user intent matches domain recall keywords"*); um `recall` de Project Memory poderia aceitar `intent_hint?:string` e devolver só entradas cujas keywords casem, evitando devolver TODA a memória aprovada em cada chamada | — |
**Nota sobre "não re-adivinhar contexto"**: a frase da promessa Pro ("so the agent does not
re-guess site context") é, no ecossistema Descomplicar, exactamente o motivo declarado para o
Mem0 Tier 2 existir — "factos duráveis cross-agent" que sobrevivem ao reset de contexto de uma
sessão. A diferença de desenho que este documento propõe adoptar (ver 1.4) é que o Pro parece
restringir `recall` a **só** conteúdo já aprovado por um humano, ao passo que o Mem0 real do
ecossistema grava directamente (`save_memory`) sem esse portão — ver a tensão explícita na secção
2.4.
### 1.4 Porque "aprovado" é o modelo certo aqui, não "gravado directamente"
Ao contrário do Mem0 (Tier 2, escrita automática via bridge, sem revisão humana — apropriado para
factos operacionais de baixo risco, como "o cliente X prefere PT-PT sem gírias"), uma "guidance"
de `recall` num plugin WordPress pode influenciar directamente **decisões de escrita subsequentes
do próprio agente no mesmo site** (ex.: "nunca apagar redirects sem confirmar", "este cliente usa
sempre Multibanco, não activar Stripe"). Este é exactamente o tipo de afirmação que, se errada ou
obsoleta, causa dano real e persistente — o mesmo raciocínio de risco que already levou o Themer
PHP e os PHP Snippets a nunca expor "activar" via MCP (`docs/04-THEMER.md` §11,
`docs/06-SANDBOX-CUSTOM-CODE.md` §1). Por isso o modelo correcto para `recall` **não** é "o que o
Mem0 tem gravado" (Tier 2 puro), é "o subconjunto do Tier 2 que já passou pelo portão humano" —
precisamente o desenho que a secção 2 propõe para `remember`.
---
## 2. `remember` — propor uma guidance, pendente de aprovação humana
**Promessa da tool (screenshot, PRO):** *"Propose one guardrail/fact/convention/instruction. Stored
pending until a human approves it."*
Esta é a peça mais bem fundamentada de todo o documento — a promessa da tool Pro descreve, quase
palavra por palavra, o fluxo **staging→aprovação** que o CARL já implementa como sistema real,
com ferramentas MCP reais chamáveis nesta mesma sessão.
### 2.1 O fluxo CARL real (v1, ficheiro-por-domínio) `[REAL]`
Ferramentas reais do servidor `carl-mcp`, citadas com a descrição exacta tal como declarada nos
seus schemas MCP (sem paráfrase):
| Tool MCP real | Descrição literal | Argumentos |
|---|---|---|
| `carl_stage_proposal` | "Stage a new CARL rule proposal for review. Sources: psmm, decisions, manual." | `domain`, `rule_text`, `rationale`, `source` (enum `psmm`\|`decisions`\|`manual`) |
| `carl_get_staged` | "List all pending rule proposals in the staging pipeline." | — |
| `carl_approve_proposal` | "Approve a staged proposal — writes the rule to the target domain file and removes from staging." | `id` (ex. `prop-001`) |
| `carl_archive_proposal` | "Archive a proposal — keeps it for reference but doesn't activate as a rule." | `id` |
| `carl_kill_proposal` | "Delete a staged proposal permanently." | `id` |
| `carl_log_decision` | "Log a decision to a CARL domain. Auto-creates domain file if new." | `domain`, `decision`, `rationale`, `recall` |
| `carl_get_domain_rules` | "Load rules for a specific CARL domain. Use when user intent matches domain recall keywords." | `domain` |
| `carl_get_manifest` | "Get current CARL manifest showing all domains, states, and recall keywords." | — |
| `carl_toggle_domain` | "Enable or disable a CARL domain by updating the manifest." | `domain`, `state` (`active`\|`inactive`) |
| `carl_create_domain` | "Create a new CARL domain with file and manifest entry." | `domain`, `description`, `recall`, `rules` |
**O ciclo de vida real confirmado por estes nomes de tool, sem qualquer inferência**: uma proposta
nasce por `carl_stage_proposal` (nunca directamente como regra); fica visível a um humano/revisor
via `carl_get_staged`; termina num de **três** destinos possíveis — `carl_approve_proposal`
(promovida a regra activa no domínio), `carl_archive_proposal` (guardada para referência, **nunca**
se torna regra), ou `carl_kill_proposal` (apagada sem deixar rasto). Este é exactamente o modelo de
três saídas (aprovado/arquivado/rejeitado) que se propõe adoptar para `remember` (secção 2.3).
### 2.2 O fluxo CARL v2 real (`carl.json` único) `[REAL]`
Uma segunda geração do mesmo sistema, também real e chamável nesta sessão (prefixo
`carl_v2_*`), que difere em dois pontos relevantes para o desenho de `remember`:
| Tool MCP real (v2) | Descrição literal | Diferença face à v1 |
|---|---|---|
| `carl_v2_stage_proposal` | "Stage a rule proposal in carl.json staging array." | Mesmo conceito, `source` agora opcional (default `manual`) |
| `carl_v2_get_staged` | "List all staged rule proposals from carl.json." | Igual em espírito |
| `carl_v2_approve_proposal` | "Approve a staged proposal — adds rule to target domain and removes from staging." | `id` no formato `stg-001` (não `prop-001`) |
| `carl_v2_reject_proposal` | "Reject (kill) a staged proposal — marks status=rejected, keeps it in staging as an audit trail (does NOT add any rule)." | **Diferença arquitectural real e significativa**: em vez de apagar (como `carl_kill_proposal` v1), o v2 **preserva a entrada rejeitada** com um campo `status='rejected'` — a rejeição fica auditável, não desaparece |
| `carl_v2_get_domain` | "Get a full domain object from carl.json including rules, decisions, recall, state." | Devolve regras + decisões + `recall` + `state` num único objecto — mais próximo do que `recall` (secção 1) precisaria de devolver de uma vez |
| `carl_v2_list_domains` | "List all CARL domains from carl.json with rule counts, decision counts, state, and always_on flag." | Introduz `always_on` (domínios que carregam sempre, independentemente de keyword de recall) — precedente directo para a distinção "guidance global do site" vs "guidance específica de um contexto" |
| `carl_v2_add_rule` | "Add a rule to a domain in carl.json. Auto-assigns next sequential ID." | Escrita **directa**, sem staging — existe em paralelo ao fluxo com aprovação, para uso humano/administrativo explícito |
**Achado relevante para o desenho de `remember`**: a existência de **duas gerações coexistentes**
do mesmo sistema de governança (v1 ficheiro-por-domínio, v2 `carl.json` único) é, ela própria, um
sinal a não ignorar numa réplica — mostra que um modelo de dados de "staging de regras" tende a
evoluir (aqui, de N ficheiros para 1 documento único com contadores agregados) à medida que o
volume de domínios cresce. Uma implementação de `remember` desde o início beneficiaria de escolher
logo o modelo v2 (um único documento JSON por site, com `staged[]` + domínios com `rules[]` +
`decisions[]` + `recall[]` + `state` + `always_on`), evitando a migração que o CARL real precisou
de fazer.
### 2.3 Desenho proposto para `remember` `[DESENHO PRÓPRIO, mapeado 1:1 sobre o fluxo CARL real]`
| Área | O que cobre | Fluxo CARL real usado como modelo | Nível de risco |
|---|---|---|---|
| Propor uma entrada | `remember({type, domain?, text, rationale})` — nunca escreve directamente; cria sempre um registo com estado inicial `pending` | `carl_stage_proposal`/`carl_v2_stage_proposal` — mesmo padrão de "escrita nunca chega directa ao alvo" | write, **não-destructive**, não-idempotent (cada chamada cria uma nova proposta, mesmo repetida) |
| Listar pendentes (uso interno pela UI de aprovação, não necessariamente exposta como tool MCP separada) | Equivalente a `carl_get_staged`/`carl_v2_get_staged` | mesma fonte | readonly |
| Aprovar (**exclusivamente UI de admin, nunca MCP** — ver 2.4) | Promove `pending`→`approved`; só depois disto entra no que `recall` devolve | `carl_approve_proposal`/`carl_v2_approve_proposal` | write, **fora do protocolo MCP** |
| Rejeitar com auditoria (**exclusivamente UI de admin**) | Recomenda-se o comportamento v2 (`carl_v2_reject_proposal`): marca `status:'rejected'` mas **preserva** a entrada, em vez de apagar (`carl_kill_proposal` v1) — auditabilidade é mais valiosa do que limpeza para um sistema que pode influenciar decisões futuras de um agente | `carl_v2_reject_proposal`, citação directa: *"keeps it in staging as an audit trail (does NOT add any rule)"* | write, **fora do protocolo MCP** |
**`type` proposto** (`guardrail`\|`fact`\|`convention`\|`instruction`) reproduz literalmente a
enumeração da própria promessa da tool Pro ("guardrail/fact/convention/instruction") — os quatro
termos usados na descrição do screenshot mapeiam directamente para os dois tipos de conteúdo que o
CARL real já distingue: **regras** (`carl_stage_proposal`/`add_rule` — mais próximo de
`guardrail`/`convention`/`instruction`, afirmações prescritivas de "como agir") e **decisões**
(`carl_log_decision` — mais próximo de `fact`, um registo factual de "o que foi decidido e
porquê", já com `rationale`+`recall` no schema real). Uma implementação completa de `remember`
deveria, portanto, decidir internamente para qual dos dois sub-sistemas encaminhar consoante o
`type`, em vez de tratar as quatro categorias como um único blob indiferenciado.
### 2.4 A decisão de design central: `remember` nunca aprova a si própria
**Justificação, cruzando os dois precedentes reais já confirmados nesta série** para "código/regra
gerado por IA que precisa de portão humano antes de ter efeito":
- Themer PHP (`docs/04-THEMER.md` §11, citação directa): *"AI authors + validates DRAFT PHP
templates; there is intentionally no attach tool — a human selects a template in the Themer
metabox (the execution gate)"*.
- PHP Snippets (`docs/06-SANDBOX-CUSTOM-CODE.md` §1, citação directa): *"There is intentionally no
'activate' tool: a snippet created via MCP is an inactive draft until a human administrator
reviews it and activates it in the Sandbox admin screen."*
O CARL real reforça exactamente o mesmo princípio noutro domínio (não código PHP, mas *regras de
governança que alteram o comportamento futuro de agentes*): `carl_stage_proposal` nunca escreve a
regra final por si — só `carl_approve_proposal` o faz, e nada nas descrições das tools reais
sugere que a própria IA que propôs a regra a possa aprovar dentro do mesmo fluxo MCP. **A promessa
literal da tool Pro confirma isto de forma independente**: "Stored pending until a human approves
it" não deixa ambiguidade — não há tool `approve` no catálogo de 3 mostrado na captura de ecrã.
**Consistência tripla** (Themer PHP real + PHP Snippets real + a própria descrição da tool Pro):
uma réplica de `remember` deve, tal como estas três fontes convergem, **nunca** expor uma ability
MCP `approve-guidance`/`remember-approve` — a aprovação vive exclusivamente na UI de admin, com o
mesmo mecanismo de nonce+sessão humana já usado por `Themer_Metabox::save()`
(`docs/04-THEMER.md` §7) e pelo handler AJAX de `PHP_Snippet_Store::set_status()`
(`docs/06-SANDBOX-CUSTOM-CODE.md` §3.1).
**Tensão explícita a registar** (paralela à da secção 6.5 do doc 12 sobre `set-widget-status`): o
catálogo de 3 tools do screenshot **não inclui** nenhuma tool de aprovação — o que é, na verdade,
uma confirmação a favor desta decisão, não uma tensão como no caso do Widget Builder (onde
`set-widget-status` aparecia no catálogo Pro e ficava ambíguo se aceitaria `'active'`). Aqui os
três nomes de tool observados (`recall`, `remember`, `save-session-summary`) já são, por si só,
evidência de que o EMCP Pro **não** expõe uma quarta tool `approve-guidance` — reforça a decisão
de desenho em vez de a contestar.
---
## 3. `save-session-summary` — registar um resumo de sessão com digest factual anexado
**Promessa da tool (screenshot, PRO):** *"Record a session summary; the plugin attaches a factual
digest of the actual changes."*
A frase-chave é *"a factual digest of the **actual** changes"* — o plugin não confia no resumo
narrativo do agente por si só, **anexa por conta própria** uma prova verificável do que realmente
mudou. Este é o único ponto das 3 tools onde o ecossistema Descomplicar tem **dois** precedentes
reais complementares e não sobrepostos: um sobre *como avaliar se um resultado é real* (SPEC-A) e
outro sobre *onde/como um digest de alterações já é persistido de forma auditável* (o change
ledger, código real do próprio `emcp-tools`).
### 3.1 Fundamentação `[REAL]` #1 — SPEC-A: avaliação de resultado real, não execução nominal
O Pipeline de Qualidade Total do ecossistema (`Hub/04-Stack/02.04-Sistemas/PIPELINE-QUALIDADE-TOTAL.md`)
documenta um sistema chamado **SPEC-A** ("Avaliação de Resultados"), já implementado e a correr em
produção, cujo propósito é exactamente o que a tool Pro promete: não aceitar a alegação de sucesso
de uma acção pelo valor nominal, mas verificar o resultado real. Citações directas:
> *"stack-observer→classify→fingerprint→propostas CARL; **SPEC-A (Avaliação de Resultados) merged
> com baseline T0**; cron `observer-cc` semanal (2ª 08:30)"* (secção 06. SelfImprovement)
> *"**Errata 03-07-2026 (F3.1+F3.2):** L1/F-CODE-02 **fechada** — colector `observer/action_eval.py`
> (commit `38ecc03`) apresenta cada linha da action-log ao **juiz SPEC-A** →
> `evaluations(source='action', entity_id=action_id)` + `actions.result_real`; backfill 5150/5150
> acções (10-jun→03-jul), **130 divergências nominal≠real**; consumidores em
> `COALESCE(result_real, result)`, defaults `pending`, `result` = sensor."*
> *"**Lacunas declaradas:** L1 — `action-log.result` hardcoded `success` (execução ≠ resultado real;
> F-CODE-02)"* — ou seja, a lacuna real que o SPEC-A veio fechar era EXACTAMENTE "o sistema achava
que tudo tinha corrido bem só porque a acção correu até ao fim, sem verificar se o resultado era
o pretendido" — a mesma distinção que "factual digest of the **actual** changes" (não do que o
agente *disse* que mudou) descreve para `save-session-summary`.
**Mapeamento directo:** `result` (sensor/execução nominal, sempre existiu) vs `result_real` (juízo
SPEC-A sobre o resultado verificado, `COALESCE(result_real, result)` como padrão de consumo — usa
o valor verificado quando existe, cai para o nominal quando ainda não foi avaliado) é o precedente
real exacto para a distinção que `save-session-summary` deveria fazer entre "o resumo que o agente
escreveu" (nominal, o que o agente *diz* que fez) e "o digest factual anexado pelo plugin" (real, o
que de facto mudou, verificável independentemente da narrativa do agente).
### 3.2 Fundamentação `[REAL]` #2 — o change ledger como fonte do "digest factual"
`docs/05-REDIRECTS-SEARCH-LEDGER.md` §4 já documentou, por leitura directa de código real do
`emcp-tools`, exactamente o mecanismo que produziria um "digest factual das alterações reais" sem
precisar de nenhuma lógica nova: o **change ledger unificado**
(`EMCP_Tools_Change_Log`/`Change_Recorder`/`Change_Blobs`), já activo e já a registar **toda**
escrita relevante do plugin (Elementor, filesystem, BD, posts/CPT, settings, redirects,
utilizadores, ACF, media) numa única sequência cronológica, com `id, ts, user_login, domain,
action, target, summary`.
**A implicação directa para `save-session-summary`**: em vez de o plugin ter de "adivinhar" o que
mudou numa sessão (uma tarefa de detecção de estado potencialmente frágil), pode simplesmente
**consultar o próprio ledger** entre o timestamp de início e fim da sessão — `list-changes` já
devolve exactamente essa lista, com `summary` e `target` por entrada, e já é ordenado
cronologicamente (`array_reverse`, mais recente primeiro, confirmado em `docs/05` §4.1). O
"digest factual" da promessa Pro não precisa de ser um subsistema novo: é uma **agregação sobre o
ledger já existente**, filtrado pela janela temporal da sessão.
### 3.3 Fundamentação `[REAL]` #3 — precedentes de skills reais do ecossistema
O catálogo de skills deste ambiente (visível na lista completa de skills disponíveis nesta sessão)
contém dois precedentes directos, com descrições literais que se sobrepõem quase totalmente à
promessa de `save-session-summary`:
- **`/worklog`** (e o seu alias `/reflect`): *"Registo de trabalho e reflexão unificado. Analisa
sessão, regista trabalho, identifica padrões, sugere acções, higieniza documentação Hub. (…)
Usar quando (…) 'registar trabalho', 'log', ao parar timer."* — o padrão "regista o que aconteceu
numa sessão, no fim dela", já real e usado.
- **`/qw`**: *"Recapitulação rápida de sessão — resumo conciso do trabalho feito, actualização de
docs e memória mem0. Uso: /qw ao finalizar tarefa ou sessão curta. Complemento leve ao
/worklog."* — nota-se explicitamente que actualiza "memória mem0", ou seja, este ecossistema já
liga "resumir uma sessão curta" a "persistir esse resumo em memória cross-agent" — exactamente a
cadeia `save-session-summary`→`recall` proposta para o EMCP Pro.
- **`/report`**: *"Report 5W2H+PDCA ao terminar tarefa/etapa — estrutura formal de conclusão com
análise de resultados, documentação e memória. **Integrado no domínio TASK-COMPLETION-REPORT do
CARL.**"* — **achado relevante**: o próprio ecossistema já tem um **domínio CARL dedicado a
relatórios de conclusão de tarefa** (`TASK-COMPLETION-REPORT`), confirmando que a família CARL
não serve só para "regras/decisões" (secção 2), serve também como destino natural de resumos de
conclusão estruturados — um precedente real e directo para tratar `save-session-summary` como um
**terceiro tipo de entrada CARL** (ao lado de rules e decisions), em vez de um subsistema
totalmente separado.
### 3.4 Desenho proposto para `save-session-summary` `[DESENHO PRÓPRIO, sobre 3 fontes [REAL]]`
| Área | O que cobre | Fonte real usada como modelo | Nível de risco |
|---|---|---|---|
| Registar o resumo narrativo do agente | `save-session-summary({narrative, session_start?, session_end?})` — texto livre, o que o agente *diz* que fez | Análogo ao campo `result` (nominal) de `actions` no SPEC-A — nunca descartado, mas nunca tratado como única fonte de verdade | write, não-destructive |
| Anexar o digest factual ("actual changes") | Consulta o change ledger (`EMCP_Tools_Change_Log::all()`/`list-changes`, `docs/05` §4.1-4.2) filtrado pela janela temporal da sessão; agrega por `domain`+`action`, conta entradas, lista `target`s únicos | `docs/05-REDIRECTS-SEARCH-LEDGER.md` §4 — reutilização directa de infra-estrutura já existente e já real, não um subsistema novo | readonly (a consulta em si; a gravação do resumo é write) |
| Divergência nominal≠real | Se o `narrative` do agente afirmar uma alteração (ex. "apaguei o redirect X") sem entrada correspondente no ledger na janela temporal, ou vice-versa, marcar a entrada com um flag `divergence:true` — precedente directo: SPEC-A já regista "130 divergências nominal≠real" como métrica de primeira classe, não como excepção a ignorar | `PIPELINE-QUALIDADE-TOTAL.md`, citação já dada em 3.1 | — |
| Alimentar `recall` | Os N resumos mais recentes (com digest anexado) entram directamente no que `recall` (secção 1) devolve — sem portão de aprovação humana explícito, ao contrário de `remember` | `[DESENHO PRÓPRIO]` — decisão de design explícita, ver 3.5 | — |
### 3.5 Porque `save-session-summary` (ao contrário de `remember`) não precisa de aprovação humana
Distinção de risco deliberada: um resumo de sessão é, por desenho, **descritivo e retrospectivo**
("o que aconteceu"), ancorado num facto verificável (o change ledger, secção 3.2) — não é
**prescritivo e prospectivo** como uma `guidance` de `remember` ("o que fazer no futuro"). O risco
de um resumo de sessão errado é limitado (na pior hipótese, um `recall` futuro lê um resumo
impreciso, mas o digest factual anexado automaticamente pelo plugin — secção 3.2 — já mitiga isto,
porque não depende só da honestidade do agente); o risco de uma `guidance` aprovada erradamente é
muito maior, porque molda directamente decisões de escrita futuras. Esta assimetria de risco é o
mesmo raciocínio, aplicado ao domínio inverso, que levou a secção 2.4 a exigir aprovação só para
`remember`.
---
## 4. Modelo de dados proposto — o que uma "memória de projecto" guarda `[DESENHO PRÓPRIO, sobre schema CARL real]`
O modelo de estados abaixo é, deliberadamente, **um mapeamento quase 1:1 sobre o CARL v2 real**
(secção 2.2) — porque replicar um modelo já validado em produção, com uma migração de geração já
feita e documentada (v1 ficheiro-por-domínio → v2 documento único), evita repetir esse mesmo custo
de evolução numa réplica que começasse do zero com o modelo mais simples (v1).
```json
{
"guidance": [
{
"id": "gm-014",
"type": "guardrail", // guardrail | fact | convention | instruction
"domain": "checkout", // livre, análogo aos domínios CARL (ex. GLOBAL, DEVELOPMENT)
"text": "Nunca activar Stripe neste site — cliente só usa Multibanco/MB Way.",
"rationale": "Pedido explícito do cliente em 2026-05-12, confirmado por email.",
"recall": ["stripe", "gateway de pagamento", "multibanco"],
"status": "approved", // pending | approved | rejected | archived
"source": "manual", // manual | agent_proposal | session_summary_derived
"proposed_by": "agent-run-8821",
"proposed_at": "2026-08-19T10:03:00Z",
"reviewed_by": "emanuel", // preenchido só após aprovação/rejeição humana
"reviewed_at": "2026-08-19T14:20:00Z"
}
],
"session_summaries": [
{
"id": "ss-0091",
"session_start": "2026-08-19T09:00:00Z",
"session_end": "2026-08-19T09:47:00Z",
"narrative": "Corrigido bug de redirect em loop no checkout; actualizado texto da página de contactos.",
"digest": {
"source": "change_ledger",
"entries_count": 3,
"by_domain": { "redirect": 1, "content": 2 },
"targets": ["redirect#44", "post#812"],
"divergence": false
},
"agent": "agent-run-8821"
}
]
}
```
### 4.1 Correspondência campo-a-campo com o CARL real
| Campo proposto | Fundamentação | Equivalente CARL real |
|---|---|---|
| `id` | `[DESENHO PRÓPRIO]`, formato inspirado directamente nos ids reais observados nas descrições das próprias tools (`prop-001` v1, `stg-001` v2) | `carl_approve_proposal.id` / `carl_v2_approve_proposal.id` |
| `type` | `[REAL]` — os 4 valores vêm literalmente da descrição da tool Pro ("guardrail/fact/convention/instruction") | — (a própria captura de ecrã) |
| `domain` | `[REAL]` — conceito directo dos domínios CARL (`GLOBAL`, `DEVELOPMENT`, `PROJECTS`, `CONTENT`, exemplos vistos nas descrições reais das tools `carl_get_domain_rules`/`carl_v2_get_domain`) | `domain` em `carl_stage_proposal`/`carl_log_decision`/`carl_v2_stage_proposal` |
| `recall` | `[REAL]` — campo com o mesmo nome exacto em `carl_log_decision`/`carl_v2_log_decision` ("Comma-separated recall keywords"/"Comma-separated keywords for when to recall this decision") e em `carl_create_domain`/`carl_v2_create_domain` (recall keywords do próprio domínio) | `recall` em múltiplas tools CARL reais |
| `status: pending\|approved\|rejected\|archived` | `[REAL, com uma correcção sobre a v1]` — a v1 tem só 3 saídas efectivas (approve/archive/kill-delete); o `status:'rejected'` **preservado** (não apagado) é especificamente o comportamento v2, citação directa já dada em 2.3: *"marks status=rejected, keeps it in staging as an audit trail"* — este documento recomenda o comportamento v2 (preservar) sobre o v1 (apagar) | `carl_v2_reject_proposal` |
| `source: manual\|agent_proposal\|session_summary_derived` | `[DESENHO PRÓPRIO]` — o terceiro valor (`session_summary_derived`) é a extensão própria deste blueprint: uma `guidance` pode nascer não de uma proposta directa (`remember`), mas de um padrão detectado em vários `session_summaries` (ex. "o agente corrigiu o mesmo tipo de bug 3 vezes" → proposta automática de um `guardrail`) — o precedente real mais próximo é o `source` real `psmm` do CARL ("Proactive Session Memory Mining", inferido do nome da enum em `carl_stage_proposal.source`, não confirmado em detalhe nesta tarefa, mas o padrão "a proposta pode vir de mineração de sessões passadas, não só de pedido directo" já existe no CARL real) | `source` (enum `psmm`\|`decisions`\|`manual`) em `carl_stage_proposal` |
| `session_summaries[].digest` | `[DESENHO PRÓPRIO]` — a estrutura em si; a **fonte** dos dados (`source:'change_ledger'`) é `[REAL]`, ver secção 3.2 | `EMCP_Tools_Change_Log::all()` |
| `session_summaries[].digest.divergence` | `[DESENHO PRÓPRIO]` — o campo; o **conceito** (nominal ≠ real) é `[REAL]`, SPEC-A, secção 3.1 | `actions.result` vs `actions.result_real`, `COALESCE()` |
### 4.2 Evolução de estados — paralelo directo ao CARL
```
remember() aprovação humana (UI, nunca MCP)
(pending) ─────────────────► [pending] ──────────────────────────────────► [approved]
│ │
│ rejeição humana (UI, nunca MCP) │ recall() lê SÓ aqui
▼ ▼
[rejected] (visível a recall)
(preservado, auditável,
nunca vira regra — v2)
│ arquivar (referência, nunca activa)
▼
[archived]
```
Este diagrama de estados é uma transcrição directa, para o domínio "Project Memory", do
comportamento real confirmado nas descrições das 5 tools CARL de gestão de propostas citadas em
2.1-2.2 (`stage`/`get_staged`/`approve`/`archive`/`reject-ou-kill`) — nenhum estado ou transição
aqui é inventado sem correspondência directa numa tool CARL real.
---
## 5. Blueprint para réplica
### Reaproveitar quase 1:1 (infra-estrutura já real, `[REAL]`)
1. **O modelo de estados staging→aprovação do CARL** (secção 2, 4) — não inventar um novo
vocabulário de estados; usar exactamente `pending`/`approved`/`rejected`/`archived` e o
princípio "rejeitar preserva para auditoria, não apaga" (comportamento v2, secção 2.2) desde o
primeiro dia, evitando a migração v1→v2 que o CARL real precisou de fazer.
2. **O change ledger unificado como fonte de "digest factual"** (`docs/05-REDIRECTS-SEARCH-LEDGER.md`
§4, `EMCP_Tools_Change_Log`) — `save-session-summary` não precisa de nenhuma lógica nova de
detecção de alterações; é uma consulta agregada sobre infra-estrutura que já existe e já está
documentada nesta série. Reaproveitar directamente `list-changes` filtrado por janela temporal.
3. **O padrão "a flag `partial`/`divergence` é responsabilidade do produtor, não do consumidor"**
(`docs/05` §4.4, sobre `record_db()`) — aplicado aqui à flag `divergence` do digest: quem
constrói o resumo de sessão marca-a explicitamente quando a narrativa e o ledger não batem
certo, o consumidor (`recall`) só propaga.
4. **A ausência deliberada de uma tool `approve`/`activate` no protocolo MCP** — o padrão mais
repetido e mais bem confirmado desta série inteira (Themer PHP, PHP Snippets, e agora também o
próprio CARL real e a própria descrição da tool Pro `remember`, todos convergentes): qualquer
escrita que altere o comportamento futuro de um agente de IA a partir de conteúdo proposto por
um agente de IA deve ter o portão de aprovação **fora** do protocolo MCP, com sessão humana +
nonce (padrão exacto de `Themer_Metabox::save()`, `docs/04-THEMER.md` §7).
### Construir de raiz (`[DESENHO PRÓPRIO]`, sequência recomendada por dependência)
1. **Modelo de dados** (secção 4) primeiro — um único documento JSON por site (seguindo o modelo
v2 do CARL, não o v1 por-ficheiro), com `guidance[]` + `session_summaries[]`; evita a migração
estrutural que o CARL real teve de fazer.
2. **`recall`** — a mais simples das três, é uma leitura filtrada (`status='approved'` +
`session_summaries` mais recentes); implementável e testável isoladamente sem nenhuma escrita.
3. **Ligação ao change ledger** — antes de `save-session-summary`, expor um método interno
`changes_in_window(start, end)` sobre `EMCP_Tools_Change_Log::all()` (código já existe, doc 05);
este é o único bloco de dependência real de código do próprio `emcp-tools`.
4. **`save-session-summary`** — sem portão de aprovação humana (secção 3.5); grava directamente,
com o digest anexado a partir do passo 3.
5. **`remember`** — por último, porque é o único dos três com uma superfície de admin
obrigatória (a UI de aprovação humana) a construir em paralelo; sem essa UI, `remember` cria
propostas que nunca podem sair de `pending` — inútil sem ela.
6. **Detecção de padrões cross-sessão** (`source:'session_summary_derived'`, secção 4.1) — feature
avançada, deixar para uma segunda iteração; o precedente real mais próximo (`psmm` como um dos
três valores reais do enum `source` de `carl_stage_proposal`) sugere que o próprio CARL trata
isto como um caminho de proposta distinto e não trivial, não uma extensão simples do fluxo
manual.
### Deixar de fora ou adiar explicitamente
- **Qualquer tool de aprovação/activação via MCP** (`approve-guidance`, `activate-guidance`, etc.)
— nunca implementar, independentemente de pressão de conveniência; é a decisão de design mais
repetida e mais bem fundamentada de toda a série de documentos EMCP (docs 04, 06, 12 e agora
este).
- **Reranking semântico de `recall`** — o precedente real mais próximo (`EMCP_Tools_Search_Ranker`,
`docs/05` §2.3) já deixa um "seam" explícito no próprio código real do plugin para um reranker
com embeddings como upgrade Pro futuro ("*This is the seam for an embedding-backed reranker (a
future Pro upgrade); the lexical order is the default*"); uma primeira implementação de `recall`
deveria seguir exactamente o mesmo princípio — ordenação lexical/determinística por omissão
(recência para `session_summaries`, correspondência exacta de `recall` keywords para
`guidance`), com um filtro extensível para um reranker semântico mais tarde, sem bloquear o v1
nessa dependência.
- **Migração automática entre gerações de schema** (o equivalente ao `maybe_migrate()` de
`EMCP_Tools_Sandbox_Paths`, `docs/06-SANDBOX-CUSTOM-CODE.md` §3.4, que existe só porque o produto
real mudou de local a meio da vida) — não é necessária numa implementação de raiz que já comece
com o modelo de dados da secção 4 (inspirado directamente na geração v2, mais recente, do CARL).
---
## 6. Fonte
**Fonte A — identidade operacional deste ambiente OMP (lida integralmente nesta sessão):**
- `/home/ealmeida/.omp/agent/AGENTS.md` — ficheiro de import; conteúdo próprio limitado a uma
política de tokens/segredos em sessão, não relevante a este documento.
- `/media/ealmeida/Dados/Hub/04-Stack/02.04-Sistemas/AGENTS.md` (documento canónico importado pelo
anterior) — secção 2 ("HIERARQUIA DE FONTES", regras NLM-first e "nunca confiar em docs sem
verificar"), secção 4 ("ECOSSISTEMA", errata sobre as 22/26 regras CARL `GLOBAL`), secção 5
("MEMÓRIA", tabela completa de tiers Mem0/Hindsight, protocolo Wayland).
- `/media/ealmeida/Dados/Hub/04-Stack/02.04-Sistemas/PIPELINE-QUALIDADE-TOTAL.md` — secções sobre
o arco C6 (propostas CARL, "staging CARL funcional", "CARL v2 GLOBAL com 26 regras"), o arco C7
(SPEC-A, `action_eval`, `result_real`, `COALESCE(result_real, result)`, "130 divergências
nominal≠real"), e a secção 06.SelfImprovement ("SPEC-A (Avaliação de Resultados) merged com
baseline T0").
**Fonte B — ferramentas MCP reais, invocadas ou inspeccionadas directamente nesta sessão:**
- Servidor `carl-mcp` (v1, ficheiro-por-domínio): `carl_stage_proposal`, `carl_get_staged`
(chamada real, resultado `{pending_count:0, archived_count:0}` — sem propostas activas neste
projecto no momento da tarefa), `carl_get_manifest` (chamada real, resultado `{success:false,
"Manifest not found"}` — sem manifesto v1 inicializado neste projecto), `carl_approve_proposal`,
`carl_archive_proposal`, `carl_kill_proposal`, `carl_log_decision`, `carl_get_domain_rules`,
`carl_list_domains`, `carl_create_domain`, `carl_toggle_domain`, `carl_search_decisions`,
`carl_get_decisions`, `carl_archive_decision`, `carl_list_decision_domains` — descrições e
schemas de argumento lidos directamente da definição real da ferramenta, não paraseados de
documentação externa.
- Servidor `carl-mcp` (v2, `carl.json` único): `carl_v2_stage_proposal`, `carl_v2_get_staged`,
`carl_v2_approve_proposal`, `carl_v2_reject_proposal` (citação literal confirmada:
"marks status=rejected, keeps it in staging as an audit trail (does NOT add any rule)"),
`carl_v2_add_rule`, `carl_v2_remove_rule`, `carl_v2_replace_rules`, `carl_v2_log_decision`,
`carl_v2_archive_decision`, `carl_v2_search_decisions`, `carl_v2_get_domain`,
`carl_v2_list_domains`, `carl_v2_create_domain`, `carl_v2_toggle_domain`, `carl_v2_get_config`,
`carl_v2_update_config`.
- Servidor `mem0`: `mcp__mem_save_memory`, `mcp__mem_search_memories`, `mcp__mem_get_all_memories`,
`mcp__mem_get_memory`, `mcp__mem_update_memory`, `mcp__mem_delete_memory`,
`mcp__mem_delete_all_memories`, `mcp__mem_memory_history`, `mcp__mem_reset_memory` — usadas como
modelo do Tier 2 (secção 1.2).
- Catálogo de skills deste ambiente (lista completa disponível nesta sessão) — descrições literais
citadas de `/worklog`, `/reflect`, `/qw`, `/report` (secção 3.3), incluindo o achado do domínio
CARL `TASK-COMPLETION-REPORT` referenciado explicitamente na descrição real da skill `/report`.
**Cruzado com código real do próprio `emcp-tools`, já lido em sessões anteriores desta série (não
relido nesta tarefa):**
- `docs/05-REDIRECTS-SEARCH-LEDGER.md` §4 (integral) — `EMCP_Tools_Change_Log`,
`EMCP_Tools_Change_Recorder`, `EMCP_Tools_Change_Blobs`, `EMCP_Tools_Transaction_Abilities`,
usados como modelo para o "digest factual" de `save-session-summary` (secção 3.2) e como
precedente do padrão de flags-responsabilidade-do-produtor (secção 5).
- `docs/05-REDIRECTS-SEARCH-LEDGER.md` §2.3 — `EMCP_Tools_Search_Ranker`, citação sobre o "seam"
para reranking semântico futuro, usada em §5 ("Deixar de fora") como precedente para não
implementar reranking semântico em `recall` v1.
- `docs/04-THEMER.md` §7, §11 (integral) — `EMCP_Tools_Themer_Metabox::save()` (padrão de
aprovação humana com nonce), `EMCP_Tools_Themer_PHP_Abilities`/`Themer_PHP_Store` (citação
directa "there is intentionally no attach tool"), usados como precedente central da secção 2.4.
- `docs/06-SANDBOX-CUSTOM-CODE.md` §1, §3.1, §3.4 (integral) — `EMCP_Tools_PHP_Snippet_Abilities`
(citação directa "There is intentionally no 'activate' tool"), `EMCP_Tools_PHP_Snippet_Store::set_status()`
(portão de aprovação via handler AJAX admin, nunca MCP), `EMCP_Tools_Sandbox_Paths::maybe_migrate()`
(precedente citado em §5 para justificar não construir migração de schema de raiz).
- `docs/10-MODULOS-E-INVENTARIO-PRO.md` — padrão de `class_exists()`-gated com ficheiro Pro
fisicamente ausente da árvore Free, usado em §0 para justificar por que "Project Memory" não é
auditável directamente (mesmo padrão já confirmado exaustivamente para os outros ~30 módulos
Pro nesse documento, não retentado nesta tarefa).
- `docs/11-WOOCOMMERCE-BLUEPRINT.md` e `docs/12-WIDGET-BUILDER-BLUEPRINT.md` — usados como
referência de **estilo e formato** deste tipo de documento "blueprint, não auditoria" (secção 0
explícita com fontes numeradas, convenção `[REAL]`/`[DESENHO PRÓPRIO]`, tabelas de ability
proposta com coluna de nível de risco, secção final "Blueprint para réplica" estruturada em
copiar/construir/omitir, secção "Fonte" com ficheiros exactos e o que não foi lido).
**Não lido nesta tarefa (lacuna explícita):** qualquer código-fonte do EMCP Pro para "Project
Memory" (`class-project-memory-abilities.php` ou nome equivalente) — **fisicamente ausente da
árvore Free instalada**, mesmo padrão de ausência já confirmado exaustivamente para os outros
módulos Pro nesta série (`docs/10-MODULOS-E-INVENTARIO-PRO.md`), não retentado por leitura directa
nesta tarefa. A implementação interna real de `remember`/`recall`/`save-session-summary` — schema
de input exacto, mensagens de erro, se usa CPT ou tabela SQL própria, se tem tectos de
retenção/paginação como o change ledger (`MAX_COUNT=500`, `MAX_BYTES=2MB`, `docs/05` §4.2) — é
desconhecida e **não** deve ser assumida a partir deste blueprint; tudo aqui é proposta própria
fundamentada nos dois sistemas reais descritos acima, não uma transcrição do comportamento Pro
real.
+7 -5
View File
@@ -1,6 +1,6 @@
# EMCP Tools — Mapeamento completo e blueprint de réplica
Índice e síntese de 13 documentos (~7 810 linhas), produzidos 19-08-2026 por leitura directa do
Índice e síntese de 14 documentos ("7 810+" linhas), produzidos 19-08-2026 por leitura directa do
código-fonte `emcp-tools` v3.12.1 (build Free, `msrbuilds/elementor-mcp`, GPL-2.0-or-later),
instalado em `emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/emcp-tools/`),
mais dois docs adicionais (11, WooCommerce; 12, Widget Builder) fundamentados noutro código real
@@ -33,11 +33,13 @@ copiar-1:1 / simplificar / deixar de fora.
| [10-MODULOS-E-INVENTARIO-PRO](10-MODULOS-E-INVENTARIO-PRO.md) | Sistema de módulos (toggles), Image Optimization, SVG Support, **inventário definitivo das 30 classes Pro-only** | 661 | n/a + 30 ausentes | — (estrutural + auditoria) |
| [11-WOOCOMMERCE-BLUEPRINT](11-WOOCOMMERCE-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de abilities WooCommerce (produtos/encomendas/clientes/cupões/analytics/webhooks) fundamentado no WooCommerce real, para preencher o gap `EMCP_Tools_Woo_Integration` (Pro, código ausente). Exclui SEO/Yoast por pedido. | 878 | ~35 propostas | WooCommerce activo |
| [12-WIDGET-BUILDER-BLUEPRINT](12-WIDGET-BUILDER-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de gerador spec→widget-Elementor-PHP (marca `[REAL]`/`[DESENHO PRÓPRIO]` em cada afirmação), fundamentado na API pública `Widget_Base`/`Controls_Manager` do Elementor real + no `EMCP_Tools_Widget_Store`/`Widget_Loader` já Free/reais (doc 06). Recomenda "zero tool de activação via MCP", ecoando Themer PHP/Sandbox. | 651 | 8 propostas | Elementor activo |
| [13-FORMS-PRO-BLUEPRINT](13-FORMS-PRO-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint para as integrações Pro-ausentes WPForms/Fluent Forms, fundamentado em código real Free de ambos os plugins (activos em `ecommerce-demo.descomplicar.pt`). Achado central: Fluent Forms já embarca o seu próprio servidor MCP nativo (`AbilitiesRegistrar`, 9 abilities, dry-run+confirm_token) — recomenda detectar e delegar em vez de reimplementar. | ~410 | 6 propostas | WPForms/Fluent Forms instalados |
| [14-PROJECT-MEMORY-BLUEPRINT](14-PROJECT-MEMORY-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint para o módulo Pro-ausente "Project Memory", fundamentado em dois sistemas reais em produção neste ecossistema: CARL (governança de regras com staging/aprovação) e Mem0/Hindsight (memória cross-agent em camadas). Propõe `remember`/`recall`/`save-session-summary` como ponte MCP para memória/governança já existente, não desenho do zero. | ~475 | ~7 propostas | Nenhuma (desenho próprio) |
Total: **~7 810 linhas de documentação**, ~165 abilities EMCP confirmadas + ~35 WooCommerce + 8
Widget Builder propostas (docs 11-12, não implementadas no EMCP — blueprints próprios) sobre
**~128 465 linhas de PHP** do plugin `emcp-tools` (`~50k` das quais são o SDK Freemius bundled,
irrelevante para réplica).
Total: **~8 695 linhas de documentação**, ~165 abilities EMCP confirmadas + ~35 WooCommerce + 8
Widget Builder + 6 Forms Pro + ~7 Project Memory propostas (docs 11-14, não implementadas no EMCP
— blueprints próprios) sobre **~128 465 linhas de PHP** do plugin `emcp-tools` (`~50k` das quais
são o SDK Freemius bundled, irrelevante para réplica).
---
+3 -1
View File
@@ -8,5 +8,7 @@
"6": "2. Produtos",
"7": "4. Clientes",
"8": "5. Cupões / Descontos",
"9": "9. Blueprint para réplica"
"9": "9. Blueprint para réplica",
"10": "6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`",
"11": "8. Blueprint para réplica"
}
@@ -0,0 +1,12 @@
{
"0": "EMCP Tools — Mapeamento completo e blueprint de réplica",
"1": "5. Sequência de construção recomendada para uma réplica",
"2": "11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)",
"3": "3. Encomendas",
"4": "6. Relatórios / Analytics (`wc-analytics/*`)",
"5": "7. Webhooks",
"6": "2. Produtos",
"7": "4. Clientes",
"8": "5. Cupões / Descontos",
"9": "9. Blueprint para réplica"
}
+103
View File
@@ -0,0 +1,103 @@
# Graph Report - emcp-tools-mapping (2026-08-19)
## Corpus Check
- 13 files · ~66,158 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 55 nodes · 54 edges · 10 communities
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `2f98a527`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- [[_COMMUNITY_EMCP Tools — Mapeamento completo e blueprint de réplica|EMCP Tools — Mapeamento completo e blueprint de réplica]]
- [[_COMMUNITY_5. Sequência de construção recomendada para uma réplica|5. Sequência de construção recomendada para uma réplica]]
- [[_COMMUNITY_11 — WooCommerce blueprint de abilities MCP (não é auditoria EMCP)|11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)]]
- [[_COMMUNITY_3. Encomendas|3. Encomendas]]
- [[_COMMUNITY_6. Relatórios Analytics (`wc-analytics`)|6. Relatórios / Analytics (`wc-analytics/*`)]]
- [[_COMMUNITY_7. Webhooks|7. Webhooks]]
- [[_COMMUNITY_2. Produtos|2. Produtos]]
- [[_COMMUNITY_4. Clientes|4. Clientes]]
- [[_COMMUNITY_5. Cupões Descontos|5. Cupões / Descontos]]
- [[_COMMUNITY_9. Blueprint para réplica|9. Blueprint para réplica]]
## God Nodes (most connected - your core abstractions)
1. `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` - 12 edges
2. `EMCP Tools — Mapeamento completo e blueprint de réplica` - 9 edges
3. `5. Sequência de construção recomendada para uma réplica` - 8 edges
4. `3. Encomendas` - 6 edges
5. `6. Relatórios / Analytics (`wc-analytics/*`)` - 5 edges
6. `7. Webhooks` - 5 edges
7. `2. Produtos` - 4 edges
8. `4. Clientes` - 4 edges
9. `5. Cupões / Descontos` - 4 edges
10. `9. Blueprint para réplica` - 4 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
## Import Cycles
- None detected.
## Communities (10 total, 0 thin omitted)
### Community 0 - "EMCP Tools — Mapeamento completo e blueprint de réplica"
Cohesion: 0.25
Nodes (8): 1. Mapa dos documentos, 2. O achado mais importante de toda a série, 3. Os 5 subsistemas de maior valor para copiar quase verbatim, 4. Padrões arquitecturais que atravessam todo o plugin, 6. Tabela de risco consolidada, 7. Como usar esta documentação, EMCP Tools — Mapeamento completo e blueprint de réplica, Metodologia
### Community 1 - "5. Sequência de construção recomendada para uma réplica"
Cohesion: 0.25
Nodes (8): 5. Sequência de construção recomendada para uma réplica, Explicitamente fora de âmbito (doc 10 §2), Fase 0 — Fundação (sem isto, nada mais tem rollback nem é seguro), Fase 1 — Conteúdo WordPress puro (sem dependência de Elementor), Fase 2 — Elementor (o núcleo de "construir páginas"), Fase 3 — Camadas de produto sobre o conteúdo, Fase 4 — Superfície de risco elevado, opt-in, Fase 5 — Integrações e periferia (valor incremental, não bloqueante)
### Community 2 - "11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)"
Cohesion: 0.29
Nodes (5): 0. Porque este documento é diferente dos outros 10, 10. Fonte, 11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP), 1. Panorama, 8. Segurança / gating recomendado
### Community 3 - "3. Encomendas"
Cohesion: 0.33
Nodes (6): 3.1 Estados reais (não inventados — via `wc_get_order_statuses()`), 3.2 Itens da encomenda e ciclo de escrita, 3.3 Devoluções (refunds), 3.4 Notas da encomenda, 3.5 Tabela de ability proposta — Encomendas, 3. Encomendas
### Community 4 - "6. Relatórios / Analytics (`wc-analytics/*`)"
Cohesion: 0.40
Nodes (5): 6.1 Dois grupos de controllers, 6.2 Capability real para relatórios (não `manage_woocommerce`), 6.3 Data stores de agregação confirmados, 6.4 Tabela de ability proposta — Analytics, 6. Relatórios / Analytics (`wc-analytics/*`)
### Community 5 - "7. Webhooks"
Cohesion: 0.40
Nodes (5): 7.1 Modelo de dados real (confirmado por `$data` default em `WC_Webhook`), 7.2 Validação de topic — não é livre texto, 7.3 Entrega assíncrona por omissão, 7.4 Tabela de ability proposta — Webhooks, 7. Webhooks
### Community 6 - "2. Produtos"
Cohesion: 0.50
Nodes (4): 2.1 Schema real (campos confirmados por leitura de `get_item_schema()`), 2.2 Endpoints extra confirmados (além do CRUD standard), 2.3 Tabela de ability proposta — Produtos, 2. Produtos
### Community 7 - "4. Clientes"
Cohesion: 0.50
Nodes (4): 4.1 Capabilities reais (não `manage_woocommerce` genérico), 4.2 Schema real (confirmado por `get_item_schema()`), 4.3 Tabela de ability proposta — Clientes, 4. Clientes
### Community 8 - "5. Cupões / Descontos"
Cohesion: 0.50
Nodes (4): 5.1 Tipos de desconto reais (via `wc_get_coupon_types()`), 5.2 Campos de restrição confirmados (schema/data props do coupon), 5.3 Tabela de ability proposta — Cupões, 5. Cupões / Descontos
### Community 9 - "9. Blueprint para réplica"
Cohesion: 0.50
Nodes (4): 9.1 Prioridade de construção (por valor/esforço, assumindo Fase 0 do doc00 já feita), 9.2 Reaproveitamento explícito dos padrões já documentados, 9.3 O que NÃO replicar sem decisão explícita, 9. Blueprint para réplica
## Knowledge Gaps
- **43 isolated node(s):** `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)`, `2.2 Endpoints extra confirmados (além do CRUD standard)`, `2.3 Tabela de ability proposta — Produtos` (+38 more)
These have ≤1 connection - possible missing edges or undocumented components.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` connect `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` to `3. Encomendas`, `6. Relatórios / Analytics (`wc-analytics/*`)`, `7. Webhooks`, `2. Produtos`, `4. Clientes`, `5. Cupões / Descontos`, `9. Blueprint para réplica`?**
_High betweenness centrality (0.852) - this node is a cross-community bridge._
- **Why does `EMCP Tools — Mapeamento completo e blueprint de réplica` connect `EMCP Tools — Mapeamento completo e blueprint de réplica` to `5. Sequência de construção recomendada para uma réplica`, `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)`?**
_High betweenness centrality (0.463) - this node is a cross-community bridge._
- **Why does `5. Sequência de construção recomendada para uma réplica` connect `5. Sequência de construção recomendada para uma réplica` to `EMCP Tools — Mapeamento completo e blueprint de réplica`?**
_High betweenness centrality (0.245) - this node is a cross-community bridge._
- **What connects `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)` to the rest of the system?**
_43 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
{
"docs/00-ARQUITECTURA.md": {
"mtime": 1787116450.5336444,
"ast_hash": "ae2257c149d3430da6823558b3750ed6",
"semantic_hash": ""
},
"docs/01-ELEMENTOR-CLASSICO.md": {
"mtime": 1787117462.0784547,
"ast_hash": "0a293f4d65574952b8be56fa57259af6",
"semantic_hash": ""
},
"docs/02-ATOMIC-V4-GUTENBERG.md": {
"mtime": 1787117548.949143,
"ast_hash": "b0f909b8212428e433c1116688a23763",
"semantic_hash": ""
},
"docs/03-WORDPRESS-CORE-TEMAS.md": {
"mtime": 1787117302.2605903,
"ast_hash": "4930718faa985078263df3adc0d476cf",
"semantic_hash": ""
},
"docs/04-THEMER.md": {
"mtime": 1787117386.2796364,
"ast_hash": "caa9c0b7a5fa8f9fbba4c6843970e5d6",
"semantic_hash": ""
},
"docs/05-REDIRECTS-SEARCH-LEDGER.md": {
"mtime": 1787117265.8808794,
"ast_hash": "dda4a02913bdc0e1a4ad183a94cfaa94",
"semantic_hash": ""
},
"docs/06-SANDBOX-CUSTOM-CODE.md": {
"mtime": 1787117373.3797464,
"ast_hash": "dabc5ce243ae7f9dd3affd5e443f0689",
"semantic_hash": ""
},
"docs/07-SYSTEM-OPS.md": {
"mtime": 1787117916.9961705,
"ast_hash": "7c3c342bc505c620d58b3fd7a2a4bea4",
"semantic_hash": ""
},
"docs/08-INTEGRACOES-TERCEIROS.md": {
"mtime": 1787117238.7386587,
"ast_hash": "6554b848b88618e823cbe45c7cde149c",
"semantic_hash": ""
},
"docs/09-STOCK-IMAGES-CLOUD-OAUTH.md": {
"mtime": 1787117293.6521044,
"ast_hash": "a7cc5fc5d8dadcff115a5ecd7025ee30",
"semantic_hash": ""
},
"docs/10-MODULOS-E-INVENTARIO-PRO.md": {
"mtime": 1787117721.9560692,
"ast_hash": "d44d5afadee4312373d78c989a74cc1d",
"semantic_hash": ""
},
"docs/11-WOOCOMMERCE-BLUEPRINT.md": {
"mtime": 1787119782.238708,
"ast_hash": "14dd4613a906c0de9e1c64d5e8706822",
"semantic_hash": ""
},
"docs/INDEX.md": {
"mtime": 1787119806.4339821,
"ast_hash": "bc362e3c65cd504567232c69a800427c",
"semantic_hash": ""
}
}
+39 -27
View File
@@ -1,16 +1,16 @@
# Graph Report - emcp-tools-mapping (2026-08-19)
## Corpus Check
- 13 files · ~66,158 words
- 14 files · ~71,911 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 55 nodes · 54 edges · 10 communities
- 80 nodes · 78 edges · 12 communities
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `2f98a527`
- Built from commit: `58acdd71`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -25,18 +25,20 @@
- [[_COMMUNITY_4. Clientes|4. Clientes]]
- [[_COMMUNITY_5. Cupões Descontos|5. Cupões / Descontos]]
- [[_COMMUNITY_9. Blueprint para réplica|9. Blueprint para réplica]]
- [[_COMMUNITY_6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `REAL`|6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`]]
- [[_COMMUNITY_8. Blueprint para réplica|8. Blueprint para réplica]]
## God Nodes (most connected - your core abstractions)
1. `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` - 12 edges
2. `EMCP Tools — Mapeamento completo e blueprint de réplica` - 9 edges
3. `5. Sequência de construção recomendada para uma réplica` - 8 edges
4. `3. Encomendas` - 6 edges
5. `6. Relatórios / Analytics (`wc-analytics/*`)` - 5 edges
6. `7. Webhooks` - 5 edges
7. `2. Produtos` - 4 edges
8. `4. Clientes` - 4 edges
9. `5. Cupões / Descontos` - 4 edges
10. `9. Blueprint para réplica` - 4 edges
2. `12 — Widget Builder: blueprint de abilities MCP (desenho próprio, não é auditoria EMCP Pro)` - 11 edges
3. `EMCP Tools — Mapeamento completo e blueprint de réplica` - 9 edges
4. `5. Sequência de construção recomendada para uma réplica` - 8 edges
5. `6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`` - 6 edges
6. `3. Encomendas` - 6 edges
7. `6. Relatórios / Analytics (`wc-analytics/*`)` - 5 edges
8. `7. Webhooks` - 5 edges
9. `5. O compilador spec→PHP — desenho próprio `[DESENHO PRÓPRIO]`` - 4 edges
10. `8. Blueprint para réplica` - 4 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
@@ -44,10 +46,10 @@
## Import Cycles
- None detected.
## Communities (10 total, 0 thin omitted)
## Communities (12 total, 0 thin omitted)
### Community 0 - "EMCP Tools — Mapeamento completo e blueprint de réplica"
Cohesion: 0.25
Cohesion: 0.20
Nodes (8): 1. Mapa dos documentos, 2. O achado mais importante de toda a série, 3. Os 5 subsistemas de maior valor para copiar quase verbatim, 4. Padrões arquitecturais que atravessam todo o plugin, 6. Tabela de risco consolidada, 7. Como usar esta documentação, EMCP Tools — Mapeamento completo e blueprint de réplica, Metodologia
### Community 1 - "5. Sequência de construção recomendada para uma réplica"
@@ -55,8 +57,8 @@ Cohesion: 0.25
Nodes (8): 5. Sequência de construção recomendada para uma réplica, Explicitamente fora de âmbito (doc 10 §2), Fase 0 — Fundação (sem isto, nada mais tem rollback nem é seguro), Fase 1 — Conteúdo WordPress puro (sem dependência de Elementor), Fase 2 — Elementor (o núcleo de "construir páginas"), Fase 3 — Camadas de produto sobre o conteúdo, Fase 4 — Superfície de risco elevado, opt-in, Fase 5 — Integrações e periferia (valor incremental, não bloqueante)
### Community 2 - "11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)"
Cohesion: 0.29
Nodes (5): 0. Porque este documento é diferente dos outros 10, 10. Fonte, 11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP), 1. Panorama, 8. Segurança / gating recomendado
Cohesion: 0.20
Nodes (9): 0. Porque este documento é diferente dos outros 10, 10. Fonte, 11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP), 1. Panorama, 2.1 Schema real (campos confirmados por leitura de `get_item_schema()`), 2.2 Endpoints extra confirmados (além do CRUD standard), 2.3 Tabela de ability proposta — Produtos, 2. Produtos (+1 more)
### Community 3 - "3. Encomendas"
Cohesion: 0.33
@@ -71,8 +73,8 @@ Cohesion: 0.40
Nodes (5): 7.1 Modelo de dados real (confirmado por `$data` default em `WC_Webhook`), 7.2 Validação de topic — não é livre texto, 7.3 Entrega assíncrona por omissão, 7.4 Tabela de ability proposta — Webhooks, 7. Webhooks
### Community 6 - "2. Produtos"
Cohesion: 0.50
Nodes (4): 2.1 Schema real (campos confirmados por leitura de `get_item_schema()`), 2.2 Endpoints extra confirmados (além do CRUD standard), 2.3 Tabela de ability proposta — Produtos, 2. Produtos
Cohesion: 0.14
Nodes (14): 0. Panorama — porque este documento é diferente dos outros 11 e o que fundamenta cada peça, 12 — Widget Builder: blueprint de abilities MCP (desenho próprio, não é auditoria EMCP Pro), 1. A "spec" de um widget — estrutura de dados proposta `[DESENHO PRÓPRIO]`, 2. Como um widget é normalmente registado — contrato real do Elementor `[REAL]`, 3.1 Controlos de dados simples/compostos (`get_controls_names()`), 3.2 Group controls (`get_groups_names()`) — controlos compostos multi-campo, 3. `list-control-types` — catálogo real de tipos de controlo `[REAL]` + parâmetros `[misto]`, 4. `validate-widget-spec` — o que validar `[DESENHO PRÓPRIO, sobre APIs `[REAL]`]` (+6 more)
### Community 7 - "4. Clientes"
Cohesion: 0.50
@@ -86,18 +88,28 @@ Nodes (4): 5.1 Tipos de desconto reais (via `wc_get_coupon_types()`), 5.2 Campos
Cohesion: 0.50
Nodes (4): 9.1 Prioridade de construção (por valor/esforço, assumindo Fase 0 do doc00 já feita), 9.2 Reaproveitamento explícito dos padrões já documentados, 9.3 O que NÃO replicar sem decisão explícita, 9. Blueprint para réplica
### Community 10 - "6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`"
Cohesion: 0.33
Nodes (6): 6.1 Camada 1 — Manifest-only lookup `[REAL]`, 6.2 Camada 2 — Tamper guard sha256 `[REAL]`, 6.3 Camada 3 — Path containment guard `[REAL]`, 6.4 Camada 4 — `runtime_validate()` + shutdown fatal-recovery `[REAL]`, 6.5 Decisão de segurança: aprovação humana obrigatória antes de activar `[DESENHO PRÓPRIO, precedente REAL]`, 6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`
### Community 11 - "8. Blueprint para réplica"
Cohesion: 0.50
Nodes (4): 8. Blueprint para réplica, Construir de raiz (`[DESENHO PRÓPRIO]`, sequência recomendada por dependência), Copiar quase 1:1 (já é código real, `[REAL]`), Deixar de fora ou adiar explicitamente
## Knowledge Gaps
- **43 isolated node(s):** `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)`, `2.2 Endpoints extra confirmados (além do CRUD standard)`, `2.3 Tabela de ability proposta — Produtos` (+38 more)
- **62 isolated node(s):** `0. Panorama — porque este documento é diferente dos outros 11 e o que fundamenta cada peça`, `1. A "spec" de um widget — estrutura de dados proposta `[DESENHO PRÓPRIO]``, `2. Como um widget é normalmente registado — contrato real do Elementor `[REAL]``, `3.1 Controlos de dados simples/compostos (`get_controls_names()`)`, `3.2 Group controls (`get_groups_names()`) — controlos compostos multi-campo` (+57 more)
These have ≤1 connection - possible missing edges or undocumented components.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` connect `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` to `3. Encomendas`, `6. Relatórios / Analytics (`wc-analytics/*`)`, `7. Webhooks`, `2. Produtos`, `4. Clientes`, `5. Cupões / Descontos`, `9. Blueprint para réplica`?**
_High betweenness centrality (0.852) - this node is a cross-community bridge._
- **Why does `EMCP Tools — Mapeamento completo e blueprint de réplica` connect `EMCP Tools — Mapeamento completo e blueprint de réplica` to `5. Sequência de construção recomendada para uma réplica`, `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)`?**
_High betweenness centrality (0.463) - this node is a cross-community bridge._
- **Why does `5. Sequência de construção recomendada para uma réplica` connect `5. Sequência de construção recomendada para uma réplica` to `EMCP Tools — Mapeamento completo e blueprint de réplica`?**
_High betweenness centrality (0.245) - this node is a cross-community bridge._
- **What connects `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)` to the rest of the system?**
_43 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Why does `12 — Widget Builder: blueprint de abilities MCP (desenho próprio, não é auditoria EMCP Pro)` connect `2. Produtos` to `EMCP Tools — Mapeamento completo e blueprint de réplica`, `6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]``, `8. Blueprint para réplica`?**
_High betweenness centrality (0.207) - this node is a cross-community bridge._
- **Why does `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` connect `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` to `3. Encomendas`, `6. Relatórios / Analytics (`wc-analytics/*`)`, `7. Webhooks`, `4. Clientes`, `5. Cupões / Descontos`, `9. Blueprint para réplica`?**
_High betweenness centrality (0.197) - this node is a cross-community bridge._
- **Why does `EMCP Tools — Mapeamento completo e blueprint de réplica` connect `EMCP Tools — Mapeamento completo e blueprint de réplica` to `5. Sequência de construção recomendada para uma réplica`?**
_High betweenness centrality (0.152) - this node is a cross-community bridge._
- **What connects `0. Panorama — porque este documento é diferente dos outros 11 e o que fundamenta cada peça`, `1. A "spec" de um widget — estrutura de dados proposta `[DESENHO PRÓPRIO]``, `2. Como um widget é normalmente registado — contrato real do Elementor `[REAL]`` to the rest of the system?**
_62 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `2. Produtos` be split into smaller, more focused modules?**
_Cohesion score 0.14285714285714285 - nodes in this community are weakly interconnected._
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/11-WOOCOMMERCE-BLUEPRINT.md":{"size":49610,"mtime_ns":1787119782238708136,"hash":"9fd04f9130e82a256c669972c07eed11c97a82dbb0a05703f99c467702c67601"},"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/INDEX.md":{"size":17826,"mtime_ns":1787119806433982191,"hash":"75376ed5bec049c735c11a41cf8c0a4dfe472c35624a0242eeac261222f52bcf"}}
{"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/11-WOOCOMMERCE-BLUEPRINT.md":{"size":49610,"mtime_ns":1787119782238708136,"hash":"9fd04f9130e82a256c669972c07eed11c97a82dbb0a05703f99c467702c67601"},"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/INDEX.md":{"size":18350,"mtime_ns":1787121733648981838,"hash":"922b90021fe3c471cd76a515e327e48ca18f738e3523ffbcd6af292724cdafbf"},"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/12-WIDGET-BUILDER-BLUEPRINT.md":{"size":45994,"mtime_ns":1787121705537649698,"hash":"03e68caf73760e3daf660ac7f435d75eb697c63d0e0e581182dc20db53127c36"}}
File diff suppressed because one or more lines are too long
+1023 -533
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -60,8 +60,13 @@
"semantic_hash": ""
},
"docs/INDEX.md": {
"mtime": 1787119806.4339821,
"ast_hash": "bc362e3c65cd504567232c69a800427c",
"mtime": 1787121733.6489818,
"ast_hash": "11020b07601ac32fba355c0fce9df991",
"semantic_hash": ""
},
"docs/12-WIDGET-BUILDER-BLUEPRINT.md": {
"mtime": 1787121705.5376496,
"ast_hash": "b6bc6deff940e3f37ed1c27d21711f0b",
"semantic_hash": ""
}
}