> ## Documentation Index
> Fetch the complete documentation index at: https://exosphere-auto-translate-docs-20260624-1149.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Arquitetura

> Como o handler de hooks, o carregamento de configuração e a avaliação de políticas funcionam internamente

Este documento explica como o failproofai funciona internamente: como o sistema de hooks intercepta chamadas de ferramentas do agente, como a configuração é carregada e mesclada, como as políticas são avaliadas e como o dashboard monitora a atividade do agente.

***

## Visão geral

O failproofai possui dois subsistemas independentes:

1. **Hook handler** - Um subprocesso CLI rápido que o Claude Code invoca a cada chamada de ferramenta do agente. Avalia as políticas e retorna uma decisão.
2. **Agent Monitor (Dashboard)** - Uma aplicação web Next.js para monitorar sessões de agentes e gerenciar políticas.

Ambos os subsistemas compartilham arquivos de configuração em `~/.failproofai/` e no diretório `.failproofai/` do projeto, mas são executados como processos separados e se comunicam apenas pelo sistema de arquivos.

***

## Hook handler

### Integração com o Claude Code

Quando você executa `failproofai policies --install`, ele grava entradas como esta no `~/.claude/settings.json`:

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
```

O Claude Code então invoca `failproofai --hook PreToolUse` como subprocesso antes de cada chamada de ferramenta, passando um payload JSON via stdin.

### Formato do payload

```json theme={null}
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
```

Para eventos `PostToolUse`, o payload também contém `tool_result` com a saída da ferramenta.

O handler impõe um limite de 1 MB para o stdin. Payloads que excedam esse limite são descartados e todas as políticas implicitamente permitem a operação.

### Formato da resposta

**Negar (PreToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
```

**Negar (PostToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
```

**Instruir (qualquer evento exceto Stop):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
```

**Instruir no evento Stop:**

* Código de saída: `2`
* Motivo gravado em stderr (não stdout)

**Permitir:**

* Código de saída: `0`
* stdout vazio

**Permitir com mensagem:**

`allow(message)` permite que uma política envie contexto informacional de volta ao Claude mesmo quando a operação é permitida. O hook handler grava o seguinte JSON no **stdout** (não em um arquivo de configuração — esta é a resposta do handler ao Claude Code, assim como as respostas de deny e instruct acima):

```json theme={null}
// Written to stdout by the hook handler process
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
```

* Código de saída: `0` (a operação é permitida)
* Quando múltiplas políticas retornam `allow` com uma mensagem, suas mensagens são concatenadas com quebras de linha em uma única string `additionalContext`
* Se nenhuma política fornecer uma mensagem, o stdout fica vazio (como antes)

### Pipeline de processamento

`src/hooks/handler.ts` implementa o pipeline completo:

```text theme={null}
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
```

Todo o processo é executado em menos de 100ms para payloads típicos, sem chamadas a LLMs.

***

## Carregamento de configuração

`src/hooks/hooks-config.ts` implementa o carregamento de configuração em três escopos.

```text theme={null}
[1] {cwd}/.failproofai/policies-config.json        ← projeto   (maior prioridade)
[2] {cwd}/.failproofai/policies-config.local.json  ← local
[3] ~/.failproofai/policies-config.json             ← global    (menor prioridade)
```

Lógica de mesclagem:

* `enabledPolicies` - união deduplicada dos três arquivos
* `policyParams` - por chave de política, o primeiro arquivo que a define prevalece inteiramente
* `customPoliciesPath` - o primeiro arquivo que o define prevalece
* `llm` - o primeiro arquivo que o define prevalece

O dashboard web usa `readHooksConfig()` (somente global) para leitura e escrita, pois não é invocado com um cwd de projeto.

***

## Avaliação de políticas

`src/hooks/policy-evaluator.ts` executa as políticas em ordem.

Para cada política:

1. Busca o schema de `params` da política (se houver).
2. Lê `policyParams[policy.name]` da configuração mesclada.
3. Mescla os valores fornecidos pelo usuário sobre os padrões do schema para produzir `ctx.params`.
4. Chama `policy.fn(ctx)` com o contexto resolvido.
5. Se o resultado for `deny`, para imediatamente e retorna essa decisão.
6. Se o resultado for `instruct`, acumula a mensagem e continua.
7. Se o resultado for `allow`, continua para a próxima política.

Após a execução de todas as políticas:

* Se algum `deny` foi retornado, emite a resposta de negação.
* Se algum retorno `instruct` foi coletado, emite uma única resposta de instrução com todas as mensagens concatenadas.
* Caso contrário, emite uma resposta de permissão (stdout vazio, exit 0).

***

## Políticas integradas

`src/hooks/builtin-policies.ts` define todas as 39 políticas integradas como objetos `BuiltinPolicyDefinition`:

```typescript theme={null}
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
```

Políticas que aceitam `params` declaram um `PolicyParamsSchema` com tipos e valores padrão para cada parâmetro. O avaliador de políticas injeta os valores resolvidos em `ctx.params` antes de chamar `fn`. As funções de política leem `ctx.params` sem verificações de nulo, pois os padrões são sempre aplicados primeiro.

A correspondência de padrões dentro das políticas usa tokens de comando analisados (argv), não correspondência de string bruta. Isso evita contornar via injeção de operadores shell (por exemplo, um padrão para `sudo systemctl status *` não pode ser contornado adicionando `; rm -rf /` ao comando).

***

## Políticas personalizadas

`src/hooks/custom-hooks-registry.ts` implementa um registro baseado em `globalThis`:

```typescript theme={null}
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
```

`src/hooks/custom-hooks-loader.ts` carrega o arquivo de políticas do usuário:

1. Lê `customPoliciesPath` da configuração; ignora se ausente.
2. Resolve para caminho absoluto; verifica se o arquivo existe.
3. Reescreve todas as importações `from "failproofai"` para o caminho real de dist, de modo que `customPolicies` resolva para o mesmo registro `globalThis`.
4. Reescreve recursivamente as importações locais transitivas para garantir compatibilidade com ESM.
5. Grava arquivos `.mjs` temporários e importa o arquivo de entrada com `import()`.
6. Chama `getCustomHooks()` para recuperar os hooks registrados.
7. Remove todos os arquivos temporários em um bloco `finally`.

Em caso de qualquer erro (arquivo não encontrado, erro de sintaxe, falha de importação), o erro é registrado em `~/.failproofai/hook.log` e o carregador retorna um array vazio. As políticas integradas não são afetadas.

As políticas personalizadas são avaliadas após todas as políticas integradas. Um `deny` de uma política personalizada ainda interrompe o processamento das demais políticas personalizadas (mas todos os integrados já foram executados nesse ponto).

***

## Log de atividade

Após cada evento de hook, o handler adiciona uma linha JSONL em `~/.failproofai/hook-activity.jsonl`:

```json theme={null}
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
```

Uma linha por política que tomou uma decisão diferente de allow. Decisões de allow não são registradas (para manter o arquivo pequeno).

***

## Arquitetura do dashboard

O dashboard é uma aplicação **Next.js 16** que usa o App Router com React Server Components e Server Actions.

```text theme={null}
app/
  layout.tsx                  ← Layout raiz (tema, telemetria, navegação)
  projects/page.tsx           ← Server component: lista todos os projetos Claude
  project/[name]/page.tsx     ← Server component: lista sessões de um projeto
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: renderiza o visualizador de sessão
  policies/page.tsx           ← Client component: gerenciamento de políticas + log de atividade
  actions/
    get-hooks-config.ts       ← Lê configuração + lista de políticas
    update-hooks-config.ts    ← Ativa/desativa política
    update-policy-params.ts   ← Atualiza parâmetros de política
    get-hook-activity.ts      ← Pagina/pesquisa o log de atividade
    install-hooks-web.ts      ← Instala/remove hooks pelo navegador
  api/
    download/[project]/[session]/route.ts   ← Exportação de sessão por CLI (JSONL ou JSON)
```

**Fluxo de dados:**

* Os componentes de página chamam `lib/projects.ts` e `lib/log-entries.ts` para ler dados de projetos/sessões diretamente do sistema de arquivos (sem camada de API para leituras).
* A página de Políticas usa Server Actions para todas as mutações (ativar/desativar, atualização de parâmetros, instalar/remover).
* O visualizador de sessão analisa o formato de transcrição JSONL do Claude e renderiza uma linha do tempo de mensagens e chamadas de ferramentas.

**Principais decisões de design:**

* Sem banco de dados — todo o estado persistente está em arquivos simples (`~/.failproofai/`, `~/.claude/projects/`).
* Server Actions para mutações — nenhuma API REST necessária para operações CRUD.
* React Server Components para páginas de leitura — carregamento inicial mais rápido, sem bundle de cliente para busca de dados.
* Client components apenas onde há necessidade de interatividade (toggles de políticas, pesquisa de atividade, visualizador de log).

***

## Estrutura de arquivos

```text theme={null}
failproofai/
├── bin/
│   └── failproofai.mjs           # Roteador CLI (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Pipeline de eventos de hook
│   ├── builtin-policies.ts       # 39 definições de políticas
│   ├── policy-evaluator.ts       # Motor de execução de políticas
│   ├── policy-registry.ts        # Registro e busca de políticas
│   ├── policy-types.ts           # Interfaces TypeScript
│   ├── hooks-config.ts           # Carregamento de configuração em múltiplos escopos
│   ├── custom-hooks-registry.ts  # Registro de hooks baseado em globalThis
│   ├── custom-hooks-loader.ts    # Carregador ESM para hooks JS do usuário
│   ├── manager.ts                # Operações de instalar / remover / listar
│   ├── install-prompt.ts         # Prompt interativo de seleção de políticas
│   ├── hook-logger.ts            # Logging para hook.log
│   ├── hook-activity-store.ts    # Persistência de atividade em hook-activity.jsonl
│   └── llm-client.ts             # Cliente de API LLM (para políticas com IA)
├── app/                          # Dashboard Next.js (páginas + server actions)
├── lib/                          # Utilitários compartilhados
│   ├── projects.ts               # Enumera projetos Claude do sistema de arquivos
│   ├── log-entries.ts            # Analisa o formato de transcrição JSONL do Claude
│   ├── paths.ts                  # Resolve caminhos do sistema
│   └── ...
├── components/                   # Componentes React de UI compartilhados
├── contexts/                     # Provedores de contexto React (tema, auto-refresh, telemetria)
├── examples/                     # Arquivos de hooks personalizados de exemplo
└── __tests__/                    # Testes unitários e E2E
```
