Metadata-Version: 2.5
Name: toposync-ext-ai
Version: 0.9.0
Summary: Toposync first-party extension: AI-assisted image operators and provider routing.
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: litellm<2,>=1.74
Requires-Dist: pillow<13,>=10
Requires-Dist: toposync-core>=0.3.6
Description-Content-Type: text/markdown

# Toposync AI Extension

First-party extension for AI-assisted pipeline operators.

This first phase focuses on image workflows:

- `ai.smart_crop`: locates an object/region from a natural-language description and crops the frame.
- `ai.condition_filter`: evaluates a natural-language visual condition and emits packets only when it matches.

The extension is designed around local-first usage. Ollama is the default provider target, with
`qwen3.8:27b-mlx` as the initial high-quality local vision recommendation. Cloud providers can be
added through provider profiles and explicit fallback chains.

## Pipeline activation checks

Both image operators check the task description, enabled profiles/providers and existing
image permissions before activation. The same checks protect saved and manually enabled
pipelines. A disabled draft can still be saved when these prerequisites are missing.

For Ollama, validation queries the model inventory from the selected processing server,
using the proposed provider settings without saving them on the worker. Each analysis
shares one inventory per provider and a five-second inventory budget across its AI steps.
An unreachable provider or absent model blocks activation unless another eligible profile
remains available. Checks never run inference, install models or change image permissions.
For other providers, operational availability remains unverified.

A listed model does not prove that it loads, understands images or produces accurate
results for the task. Representative evidence is still required. Processing workers load
AI services and provider routes; the recipe assistant and notification history remain on
the application host with their required application services.

## Experimental JEV-compatible text review

The internal service `ai.jev.evaluate_event_text` optionally classifies a description
as an observation, attributed speech, or unclear text. Its output always requires
human review: it does not establish identity, intent, danger, or truth and must not
authorize actions. No pipeline, chat provider, or frontend enables this service
implicitly. It is disabled by default. The local connection can be explicitly
enabled in **Settings → AI → JEV**; environment configuration remains available
when no connection has been saved.

The JEV section offers a manual **Verify connection** action using a fixed,
short synthetic text. It does not send camera images, event descriptions or
transcripts. Opening or saving settings does not run inference or install a
model. Save through the usual settings action, then reopen to confirm the saved
connection. Changing its address, model or enabled state clears the previous
verification result. A successful check only verifies the connection and response
contract: semantic quality remains unqualified and automatic consumers stay off.

The optional `jev` settings block contains only `enabled`, `url` and `model`.
Saved settings take precedence over the process environment, including when
disabled. Credentials stay in the server environment. Advanced connection fields
default to `http://127.0.0.1:11435` and `laya:multilingual`.

`TOPOSYNC_JEV_BACKEND=typesafe` (the default) uses TypeSafe's `jev-1.13.0`.
It requires `TYPESAFE_API_KEY` and the caller's `remote_authorized=True` for the
specific text. There is no automatic cloud fallback.

`TOPOSYNC_JEV_BACKEND=ollaya` selects an **experimental local alternative**.
[Ollaya](https://github.com/ollaya-dev/ollaya) is a separate runtime from Ollama:
it implements the compatible `/v1/systemone` API with open models, **not proprietary
Jev weights**. Install an official release, verify its published checksum, and pull
a model explicitly; inference does not install missing models. For example:

```sh
OLLAYA_HOST=127.0.0.1:11435 OLLAYA_DEVICE=cpu OLLAYA_KEEP_ALIVE=30s \
  OLLAYA_MAX_LOADED_MODELS=1 OLLAYA_MAX_QUEUE=2 ollaya serve
# In another terminal, while the daemon is running:
ollaya pull laya:multilingual
```

Alternatively, leave the saved connection absent and set these variables in the
Toposync backend process:

```sh
export TOPOSYNC_JEV_ENABLED=1
export TOPOSYNC_JEV_BACKEND=ollaya
export TOPOSYNC_OLLAYA_URL=http://127.0.0.1:11435
export TOPOSYNC_OLLAYA_MODEL=laya:multilingual
```

The URL accepts only literal `127.0.0.1` or `[::1]`, with no credentials, path,
query, or fragment. Redirects and environment proxies are disabled. If the daemon
uses `OLLAYA_API_KEY`, provide that same variable to Toposync; the TypeSafe key is
never forwarded to Ollaya. Use an explicit `name:tag`, not a router alias. The
returned model must match. Model names do not certify model quality.

Requests have a ten-second total budget, no retry, a 16 KiB input limit and a
64 KiB response limit. A cold model may require explicit preloading through
Ollaya's native `/api/decide` API before evaluation. Missing models, invalid
distributions, timeouts and unavailable daemons return `status: unavailable`
with `review_required: true`. Stop the daemon when the experiment is over;
`keep_alive` bounds residency, not the daemon lifetime.

The read-only `GET /api/ai/jev/connection` endpoint describes the effective
connection and its origin without contacting a model. The manual
`POST /api/ai/jev/connection/test` accepts the local connection fields, checks
the existing AI usage/concurrency limits and applies a ten-second budget including
queue wait. It never saves settings or returns a semantic classification. Its
timestamped response keeps `semantic_qualified: false` and `review_required: true`.

The result identifies `backend`, `experimental` and
`probabilities_calibrated_for_task: false`. Model confidence is not measured
accuracy. Local processing and a valid API response do not qualify semantic
decisions, prompt-injection resistance, audio classification, or camera control.
Qualify representative independent examples before integrating a consumer.

A bounded local check on Ollaya 0.7.5 used eight fixed Portuguese/English texts:
`laya:multilingual` matched 4/8 expected classes and `decider:0.8b` matched 6/8.
Both failed the all-cases acceptance gate; neither is qualified for automatic
semantic decisions. These small authored examples test the integration, not
general accuracy. Choice, Score and Noul responded through the runtime, but this
Toposync service consumes only Choice. Score and Noul are not exposed features.

A separate five-class transcript-context experiment on the same Ollaya version
used `laya:multilingual` with short Portuguese descriptions. It matched 10/15
development labels; reversing option order changed all three probed answers.
The reserved set was not evaluated after those failures, and no transcript
consumer was connected. The original eight-case results remain unchanged.
The installed Laya token budgets fit both questions without truncation;
truncation does not explain these failures. The installed `decider:0.8b`
manifest declares English, so it is not a qualified substitute for Portuguese
transcript review. These results do not justify adjusting a confidence threshold
or treating a successful connection as semantic acceptance.

## Visual occurrences

`ai.condition_filter` supports `emit_mode: "occurrence"`. Each new captured frame is evaluated;
filter decision caching and permissive failure policies do not apply in this mode. A match lasting
`minimum_duration_seconds` opens an event, later matches update it, and a negative, missing image,
unverifiable capture freshness or source close ends it. Silence expires after
`evidence_timeout_seconds`. Duration ends at the last confirmed observation; an uncertain ending
does not certify that the physical activity stopped. Capture publication age does not certify
camera buffering or remote clock synchronization. Evaluation is serialized to preserve event order.

Occurrence mode requires the existing `capture_evidence` contract and monotonic media timestamps.
Repeated captures and out-of-order observations do not extend duration. The event carries a stable
subject and correlation identifier, the last evidence packet identifier, image, model result and
`condition_occurrence` timing/status. A subsequent visit receives a different identifier.
Capture instance/generation changes close the old occurrence and allow a restarted media clock.
Old generations and publications older than the accepted capture are ignored. Publication ordering
therefore requires a coherent source clock; clock regression does not certify continuity.
State is bounded by `maximum_occurrence_streams` (128 by default); least recently used streams
are closed with `stream_capacity` before eviction. Replay protection is scoped to retained runtime
state, while notification occurrence identifiers remain deterministic for the same capture epoch.

Camera preset requests accept `cat_litter_use` and `cat_drinking`. These recipes combine a fresh
semantic crop, explicit visual condition, occurrence lifecycle, stored image and occurrence-scoped
notification deduplication. The litter recipe reports observable box activity, not elimination;
the water recipe requires visible drinking action, not proximity. Neither estimates health,
identity or consumed volume. They require a configured vision-capable AI profile and must be
validated on representative positive, difficult negative and occluded recordings before relying
on their recognition. API persistence and synthetic replay tests do not establish model accuracy.

## Visual evidence and crops

Visual history indexes the notification's published primary image (`imageUrl`).
A crop selected by the existing image-storage and notification operators uses
the same index as a full frame. Each event has one cached vector per model space;
changing the image bytes invalidates that vector. Deleting the evidence, deleting
the notification, or making it silent removes it from visual retrieval.

Additional stored image layers and private identity-gallery crops are not
automatically indexed. Publishing a crop does not establish identity, and visual
similarity remains separate from confirmed person or pet associations. Text and
metadata search remain available when the local visual model is unavailable.

## Exact history metadata filters

The notification and history search endpoints accept `data_filter` as a JSON query
parameter, for example `{"camera_id":"patio","area_id":"water"}`. The internal
`notifications.list`, `ai.search_notifications` and `ai.search_visual_notifications`
services accept the same dictionary directly. All fields must match: comparisons
are exact, case-sensitive strings within `payload.data`, before pagination and the
semantic corpus limit. Nested fields use simple dotted paths. Missing, numeric,
array or object values do not match text. At most eight fields, sixteen path
segments, 256 characters per path and 512 per value are accepted; HTTP input is
limited to 8192 characters. Invalid filters return 422.

The same filter applies to date-only queries, consistency reads and literal
fallback. Filtering an identity field does not establish or strengthen identity;
callers must distinguish confirmed evidence from suggestions. Existing global
priority counts remain global. The history filter offers camera and area selectors under **Camera and area**,
with names from persisted evidence and exact identifiers in requests. Selection
persists locally; missing historical names and deleted selections remain explicit.
`GET /api/notifications/data-options?field=camera_id&label_field=camera_name`
returns distinct text values from all visible history, using the latest record's
label for each value. It uses the existing notification read permission and
cancellable request worker. It does not enumerate silent records or imply that a
historical camera is currently available. The same generic endpoint supports area
metadata. Live updates that leave the selected location are removed from results;
new semantic matches still require refreshing the search.

## History time windows

`GET /api/notifications`, the `notifications.list` service and
`GET /api/ai/history/search` accept optional `created_after` (inclusive) and
`created_before` (exclusive) as finite, nonnegative Unix seconds. Both bounds
filter notification creation time before pagination or the semantic corpus limit.
Updating an existing notification does not move it into a newer creation window.
The semantic route also applies the window during its consistency read and literal
fallback; its result reports `time_basis: "notification_created_at"` and the bounds.

Callers must convert civil dates using an explicit time zone and each local
midnight separately, since a daylight-saving day may differ from 24 hours. These
parameters do not interpret natural-language dates or certify the physical capture
timestamp. Configure the installation time zone under **Settings → Base**; it is
stored as `settings.core.timezone` and remains unset until selected explicitly.
`PATCH /api/settings/timezone` saves only that validated IANA zone under the existing
settings write permission, preserving other settings. Send `{"timezone": null}` to
clear it.

With no explicit numeric bounds, both history endpoints recognize a finite date
suffix: `hoje`/`ontem`, `today`/`yesterday`, optionally followed by morning (06–12),
afternoon (12–18), evening/night (18–24), or Portuguese `de madrugada` (00–06).
For example, `gato no bebedouro ontem à tarde` selects yesterday's 12–18 interval
in the installation zone, then searches the remaining terms. The interface shows
the exact half-open interval and its notification-creation basis. A query containing
only the date filters history without invoking an embedding model. Missing zone
returns a configuration request and no results; unsupported expressions remain
literal text. This is not unrestricted natural-language date understanding.
Refresh the search to include new events or recalculate a relative day; live
updates preserve the current server-selected results, including pagination.
The semantic limit remains
250 recent notifications within the selected interval, with resumable indexing
in batches of 32; the result reports partial coverage while indexing remains.
`search.returned_count` is the number of returned records. `results_truncated`
means more matching literal records or indexed semantic candidates existed than
fit in the requested result limit. For semantic search, `corpus_truncated`
separately reports older notifications outside the 250-record corpus; `pending`
counts only unindexed records inside that corpus. These flags describe the reads
used by that response, not an atomic snapshot or physical recording coverage.
A summary must retain these limits and must never equate a full index with a
complete record of activity.

## Assistente de receitas e alteração de pipelines

**Criar receita** interpreta o pedido usando o catálogo atual de câmeras/fontes de vídeo
habilitadas, áreas mapeadas e receitas existentes. O modelo seleciona recursos; o construtor
da extensão de câmeras produz e compila o grafo. Ambiguidades retornam uma pergunta.
A prévia não grava; salvar revalida os recursos e cria desativado. Desfazer remove somente
a receita ainda idêntica à criada, com comparação protegida pelo bloqueio do armazenamento.
Não executa nem ativa dispositivos. Receitas visuais de gatos usam os perfis de análise
configurados, que devem ser revisados antes da ativação.

O painel **Assistente de pipelines** prepara uma proposta para um pipeline existente,
desativado, em modo JSON ou interativo. A proposta usa um perfil Ollama habilitado
em endereço local, passa pelo compilador real e mostra as alterações antes de salvar.
A prévia requer leitura e compilação; aplicar e desfazer requerem permissão de escrita.
Salvar não ativa o pipeline. Alterações concorrentes impedem aplicar ou desfazer.

As propostas ficam na memória por uma hora. Reiniciar o servidor invalida a prévia e
o desfazer. Referências novas de câmera, fonte, modelo ou outro recurso devem ser
configuradas pelo editor; este primeiro fluxo reutiliza as referências e os tipos de
operadores existentes. Isso limita o contexto enviado ao modelo; adicionar tipos de
etapas diferentes ainda exige o editor.
O modelo não executa Python ou comandos. Falha do provedor ou da validação mantém o
pipeline original. O resumo e os detalhes técnicos precisam de revisão humana.

A criação está limitada às receitas do catálogo, com o modelo de detecção padrão disponível
no servidor local. Seleção livre de operadores/modelos/servidores exige o editor. Falta de
modelo pronto impede a proposta. Os ensaios não certificam qualidade geral do modelo nem
respostas narrativas sobre o histórico. Propostas textuais Ollama usam `think: false` para
evitar geração interna desnecessária; o prazo total continua limitado a 120 segundos.


## Respostas extrativas com fontes

`POST /api/ai/history/answer` recebe `question` e `profile_id` opcional. Usa a
busca existente, preserva seus limites e pede ao perfil Ollama local que selecione
campos completos de título/descrição. Referência inexistente, texto modificado,
recorte parcial de frase e campos adicionais são rejeitados. O servidor relê as
fontes após a inferência; alterações ou exclusões invalidam a resposta. Conteúdo
dos registros não chama serviços de configuração. `GET /api/ai/history/profiles`
reutiliza descoberta dos modelos instalados sob `core:notifications:read`, sem
exigir permissão de consultar pipelines.

A API também aceita `data_filter` com os mesmos filtros exatos do histórico,
`mode=text|visual`, `identity_id` explícito e `association=human|recognized`.
A seleção humana é padrão; sugestões nunca entram. A visão fornece a seleção
privada autorizada pela mesma função usada na recuperação de notificações.
Sem essa extensão, uma consulta com identidade falha com 503, sem ampliar a
busca. Uma identidade sem eventos associados não chama embeddings nem geração.

Antes e depois da geração, o servidor verifica acesso e associações atuais.
Mudanças de vínculo retornam 409; acesso revogado retorna 403. Fontes que saíram
dos filtros, foram apagadas ou se tornaram silenciosas não sustentam citações.
Falha do modelo conserva somente fontes atuais dentro da seleção. Respostas
usam `Cache-Control: private, no-store`. Biometria, galeria e payload bruto nunca
entram no modelo. A recuperação visual pode ordenar imagens, mas a resposta
continua extrativa de títulos/descrições, sem nova interpretação visual.
Na interface, “Restringir busca e escolher modo” abre seletores de pessoa/pet,
câmera/área e recuperação textual/visual. Opções vêm da galeria autorizada e dos
metadados do histórico; falha de carregamento não apaga seleção ativa. Trocar
perfil de identidade restaura confirmação humana. Mudanças de pergunta/filtro,
curadoria e retorno de foco invalidam resultados; respostas antigas são
ignoradas após cancelamento do consumo HTTP. Isso não certifica interrupção do
processamento no servidor. Erros de acesso, associação alterada e seleção
indisponível são distintos. A ação principal continua “Consultar acontecimentos”.

A interface apresenta os campos como citações, liga cada um ao registro original
e conserva horário de criação e imagem disponível. Durações só aparecem quando
já descritas na fonte; não são inferidas entre notificações. Campos maiores que
2500 caracteres ficam fora da seleção do modelo, mas o registro original continua
consultável. São até dez registros recuperados, sem certificação de cobertura,
identidade, relevância ou veracidade do conteúdo original. Não há análise visual
na resposta textual. Ausência, perfil indisponível ou seleção inválida mantêm um
estado explícito e acesso às fontes. Uma pergunta sobre a tarde sem hoje/ontem
pede definição de dia. A compreensão temporal permanece no vocabulário finito.

Essa síntese extrativa não equivale a uma narrativa livre validada sobre a rotina:
precisão de seleção, casos completos de gatos/visitantes e cobertura representativa
precisam de avaliação independente com o modelo real.


### Limite de memória da inferência textual

O assistente textual exige Ollama0.34.4 ou posterior, com suporte a `truncate`
e `shift` no [contrato da versão](https://github.com/ollama/ollama/blob/v0.34.4/api/types.go).
Solicita contexto32768, saída máxima8192tokens, `truncate=false`, `shift=false`
e retenção de30segundos. Entradas que não couberem falham; não são cortadas para
produzir uma resposta plausível. Apenas `done=true` com `done_reason=stop` é aceito,
inclusive quando uma saída cortada ainda forma JSON válido. O limite prévio de
64000caracteres do roteador continua sendo um teto de entrada, não uma promessa de
que qualquer texto desse tamanho cabe no modelo. Modelo/perfil indisponível ou
requisição excessiva preserva a alternativa manual. Chamadas visuais mantêm seu
contrato próprio. Esses limites reduzem reserva de contexto, mas não limitam o
tamanho dos pesos de um modelo escolhido pelo usuário.

### Anotação visual sem filtrar a ocorrência

`ai.condition_filter` aceita `emit_mode: annotate` quando uma condição visual deve
complementar um evento existente sem apagar resultados negativos. O lifecycle,
identificador, artefatos e demais dados do evento são preservados. O resultado fica
em `payload.ai.condition_filter`: `matched`, `not_matched`, `unknown` ou `closed`.
Consuma `status` junto de `matches` e `confidence`; uma resposta positiva abaixo do
limiar continua `unknown`.

Esse modo exige evidência de captura com publicação recente (limite configurado por
`evidence_timeout_seconds`). Não reaproveita decisões de outro quadro. Repetição,
intervalo entre avaliações, imagem ausente, falha de inferência, falta de capacidade
ou saturação conservam o evento com estado desconhecido. CLOSE não faz inferência.
O limite de streams existente também limita esse estado; entradas sem atividade
expiram para liberar capacidade. A chamada é limitada a 30 segundos. Isso certifica
somente o contrato de software: resultado do modelo requer validação visual própria,
e publicação recente não certifica sincronização ou ausência de buffering na câmera.

`annotation_labels` associa textos definidos pela receita aos quatro estados. Eles
são expostos como `payload.ai.condition_filter.description`, sem copiar conclusões
livres do modelo para a notificação. Não use o rótulo positivo quando `status` for
`unknown`. A receita visitante utiliza recorte estrito do sujeito e preserva o
visitante quando esse recorte não existe; imagem de outra detecção não o substitui.

Quando a anotação positiva usa um artefato já armazenado, `observation` registra a
referência da imagem, publicação, confiança e modelo. O texto identifica uma imagem
passada desta ocorrência; `current_state_verified=false` impede interpretá-la como
estado atual. Grave o recorte antes da inferência com `drop_data_after_store=false`
para conservar os pixels necessários à avaliação. Sem referência armazenada, não é
emitida essa evidência histórica.

`core.notify.historical_evidence_paths` pode reter o último valor não nulo de cada
caminho selecionado, em `data.historical_evidence`, separadamente dos dados atuais.
A receita de visitante seleciona `ai.condition_filter.observation`. A retenção usa
os limites da projeção existente: até 16 caminhos e 128 KiB, sem pixels; dados
inválidos não substituem a evidência anterior. Atualizações limitadas por intervalo
ainda alimentam o histórico. CLOSE normal ou sintetizado no desligamento conserva
a evidência; outra ocorrência começa sem herdá-la. É a última evidência por caminho,
não um arquivo completo de todas as avaliações ou de todas as imagens.

### Imagens e orçamento de inferência Ollama

Arrays de frames seguem a convenção BGR/BGRA do pipeline; a codificação converte
para RGB/RGBA. Imagens PIL e arquivos já codificados mantêm suas cores originais.
Ollama usa contexto32768, saída limitada a8192tokens e retenção30s tanto para texto
como para visão. Respostas incompletas e truncamento de contexto são recusados;
essa garantia exige Ollama0.34.4 ou posterior. Os limites não constituem garantia
de latência ou precisão; valide o modelo e as cenas do ambiente de implantação.

### Visitante e campainha no assistente

A receita exige câmera, área mapeada e associação explícita ao nome de um sinal
booleano descoberto nas configurações da câmera. O assistente lê somente a
 descoberta recente mantida em memória pelo gerenciador ONVIF; não inicia
 descoberta, captura ou assinatura de eventos. A consulta do catálogo exige
 também leitura de configurações, como a consulta de sinais da câmera.

Sinal ausente ou expirado exige descoberta explícita na câmera. Um único sinal
não implica campainha, e movimento não comprova acionamento. A prévia apresenta
o nome escolhido; salvar revalida a descoberta sem contato com o dispositivo e
mantém o fluxo desativado. A correlação temporal não identifica quem acionou e o
indício visual de pacote não identifica profissão ou entrega. Cache desaparece
no reinício e expira conforme o prazo do gerenciador. Suporte físico e precisão
exigem evidência independente; testes controlados não os certificam.


Visual condition responses include `observable`: true means the provider reports enough visible evidence to evaluate the condition; false means it cannot evaluate it (for example, an occluded subject or missing reference). False forces `matches=false` and confidence zero, and annotation emits `unknown` even with a zero confidence threshold. A visible negative remains `not_matched`. Missing `observable` is stored as null, never converted into a claim of visibility; existing decisions still follow their confidence contract. Both provider prompts request this distinction. This is a model-reported assessment, not proof of visibility or orientation accuracy. Subject/reference binding and empirical qualification remain necessary for orientation use cases.


For annotations about a particular tracked subject, `bind_subject_context: true` requires the selected artifact to carry `subject_context` produced by the object cropper. It checks subject identity, source bounding box, capture evidence and a finite normalized target box in the actual selected image. Missing or inconsistent context emits `unknown` without calling the model. The model receives the target box and an instruction not to substitute another subject; the annotation retains the binding for review. In this mode the provider must explicitly report `observable=true`; an omitted visibility assessment is unknown. The option is limited to annotation mode and does not alter upstream lifecycle. It does not establish a house reference or qualify model accuracy by itself.

## Revisão de fala registrada

Com a extensão AI habilitada, o detalhe de uma notificação com transcrição oferece
**Revisar conteúdo da fala**. A ação usa o perfil local padrão já configurado,
sem fallback para cloud. Exibe interpretação contextual e segmentos exatos com
posição no trecho; não identifica a pessoa, intenção, perigo ou segurança e não
aciona dispositivos. A transcrição automática também precisa ser conferida.

`POST /api/ai/history/{notification_id}/review-transcription` exige leitura de
notificações. Aceita `{}` ou `profile_id` e lê exclusivamente a transcrição
armazenada. Texto parcial, segmentos inconsistentes ou contexto insuficiente
não recebem uma conclusão afirmativa. Saída inválida ou provedor indisponível
resulta em indisponibilidade explícita. Após inferência, autorização e registro
são verificados novamente; alteração ou exclusão descarta a resposta. Citações
são extraídas no servidor pelos índices validados, nunca copiadas do modelo.

Antes da classificação, o modelo extrai trechos literais de ameaça afirmada,
negação explícita e atribuição, além da suficiência do contexto. Cada trecho deve
existir exatamente no segmento citado. O servidor aplica a precedência de contexto
insuficiente, atribuição, ameaça afirmada, negação e ausência de ameaça identificada.
Uma negação não cancela uma ação afirmada separadamente, nem depende de existir
ameaça afirmada. O modelo não escolhe diretamente
o rótulo final. Correspondência textual não comprova que o trecho sustenta a
interpretação: a avaliação semântica e a revisão humana continuam necessárias.

O contrato foi avaliado localmente com Qwen3.5 9B em uma transcrição humana inglesa
que antes recebia atribuição indevida, onze regressões textuais e oito textos
reservados. Os vinte rótulos e os trechos emitidos passaram nesse recorte. Textos
autorais não qualificam transcrição real em português, falas espontâneas, ameaças
reais ou desempenho geral. A narrativa anterior com oráculo insuficiente continua
inconclusiva.

Um piloto posterior com três áudios humanos portugueses do FLEURS não qualificou
o fluxo completo: o transcritor tiny passou no critério de erro de palavras de
um áudio, mas falhou nos outros dois. No primeiro, o Qwen3.5 9B confundiu negação
de participação em um incidente com negação de ameaça e omitiu a atribuição
explícita da fala. A revisão contextual em português permanece reprovada nesse
caso; validação de substrings e testes de código não corrigem essa falha semântica.

O modelo já instalado sob o nome local `qwen3.8:27b-mlx` identificou corretamente
a atribuição nesse caso e passou treze casos de desenvolvimento. Sua metadata
declara arquitetura `qwen3_5`; o nome local não comprova uma versão oficial.
Na reserva, sete de oito rótulos passaram. O oitavo revelou a precedência incorreta
de negação sobre outra ação afirmada, apesar de o modelo extrair os dois trechos
corretamente. O reparo dessa regra é determinístico e pode ser verificado com a
resposta guardada. A rodada original permanece reprovada; não houve troca do perfil
padrão. Atribuições com escopos mistos e qualidade da transcrição continuam sendo
limites distintos dessa verificação.

Resultados são temporários e descartados ao mudar a evidência ou voltar à janela.
A revisão diferencia negação, citação/ficção, fala potencialmente ameaçadora,
ausência de palavras ameaçadoras no trecho e contexto insuficiente. Essas classes
não certificam confiabilidade da transcrição nem são um detector de perigo.
Não há deduplicação acústica, localização ou automação de resposta neste recurso.
