Broadcast Alerts (cross-session notices with expiry + priority)
O sistema único de **alertas de broadcast** da Koder Stack: arquivos em `meta/context/notices/active/*.md` que o hook `notices-check.sh` (`UserPromptSubmit`) injeta no contexto de **toda sessão**, ANTES de qualquer ação, ordenados por prioridade, cada um com **expiração explícita** (auto-some) e/ou **disparo agendado** (`fires_on`, aparece só a partir de uma data futura — substitui `/schedule` para obrigações datadas). Unifica os antigos `notices` (locks/cortesias técnicas cross-session) e `reminders.md` (lembretes do owner com data) num só formato + um só hook. Substitui o TTL implícito por severity+idade (onde `high` = nunca expirava) por um `expires:` explícito.
Por quê
Toda sessão do Claude Code precisa ler, antes de qualquer coisa, os avisos importantes que outras sessões/o owner deixaram: locks de componente, cortesias, lembretes datados, obrigações futuras (ex.: "remover compat-shims em 2026-06-20"). O hook notices-check.sh já injeta notices/active/ no UserPromptSubmit de toda sessão — esta spec formaliza o schema (com expiração + prioridade + disparo agendado) e o comportamento do hook, e unifica o antigo reminders.md aqui.
R1 — Local + formato
- Um alerta = um arquivo
meta/context/notices/active/<slug>.mdcom frontmatter YAML+ corpo Markdown. Arquivado = movido para
meta/context/notices/archive/. - O hook lê só
active/.archive/é histórico (nunca injetado).
R2 — Schema (frontmatter)
| Campo | Obrig. | Default | Significado |
|---|---|---|---|
title |
sim | — | manchete curta (1 linha) |
created |
sim | — | data de autoria YYYY-MM-DD (alias retro: date) |
priority |
não | 5 |
0–9; maior = aparece primeiro |
expires |
não | — | YYYY-MM-DD; não exibe a partir desta data + auto-arquiva (TTL) |
fires_on |
não | — | YYYY-MM-DD; não exibe ANTES desta data (disparo agendado) |
status |
não | active |
active | done (done não exibe) |
kind |
não | alert |
lock | courtesy | reminder | alert | bug-dep |
audience |
não | all |
all (técnico, toda sessão) | owner (pessoal) |
recurring |
não | none |
none | daily (re-arma manual: ao disparar, bumpar fires_on +1 até done) |
severity |
não | normal |
retrocompat: se NÃO houver expires, o TTL cai no esquema antigo idade+severidade (high∞, normal7d, low=3d a partir de created) |
component |
não | — | componente alvo (locks) |
session_id |
não | — | (locks) UUID da sessão Claude Code que colocou o lock ($CLAUDE_CODE_SESSION_ID); permite a uma sessão bloqueada nomear quem detém o lock. Ausente em locks legados. |
applies_when |
não | — | (R3.10) expressão Kanon (domínio notices) sobre o foco da sessão; ausente ⇒ universal |
kind: bug-dep(dependência de bug,component-bug-discovery.kmd): emitido por uma sessão que, usando uma instância instalada de um componente Koder, achou um bug nele e depende da correção para prosseguir. O corpo declara o ticket do bug (<COMPONENT>-NNN) e o componente. Sinal-back: quem corrige o bug flipa este alerta parastatus: doneao fechar o ticket — é o sinal que re-aciona a sessão descobridora (que então atualiza a instância e retoma). Pareia compriorityalta (7–8) +component:+ oblocked-by:no ticket da descobridora.
expiresvsfires_on:expires= "vale ATÉ" (some depois).fires_on= "aparece A PARTIR DE" (some antes). Os dois podem coexistir: uma obrigação que só importa numa janela. Um lock típico usaexpires; uma obrigação datada (ex.: remover shims) usafires_on(+ opcionalexpiresalguns dias depois).
R3 — Comportamento do hook (notices-check.sh, UserPromptSubmit)
Para cada arquivo em active/, nesta ordem:
- R3.1
status: done→ não exibe (candidato a arquivar). - R3.2
fires_ondefinido ehoje < fires_on→ não exibe (ainda não disparou). - R3.3
expiresdefinido ehoje >= expires→ não exibe + auto-arquiva(
mvidempotente p/archive/, falha silenciosa em corrida entre sessões). - R3.4 sem
expires→ retrocompat: idade =hoje - created; TTL porseverity(
high999d,normal7d,low=3d); se idade > TTL → não exibe + auto-arquiva. - R3.5 senão → exibe, com countdown:
expires→⏳ vence em Nd/⚠️ VENCE HOJE; obrigaçãofires_onrecém-disparada/atrasada ourecurringvencido →🔔 [VENCIDO há Nd]/🔔 HOJE/🔔 em Nd. - R3.6 Ordenar os exibidos por
prioritydesc, desempate por urgência(menor dias-até-expirar / mais atrasado primeiro).
- R3.7 Emitir um bloco único
[alerts]noadditionalContext(JSON válido).Saída sempre JSON bem-formado; qualquer erro de parse de um arquivo → pular esse arquivo, nunca abortar o hook (não pode quebrar nenhuma sessão).
- R3.8 — disciplina de renderização (corpo sob demanda). O bloco é injetado
em todo turno (custo de token por-turno); por isso o corpo (primeiras 6 linhas) só é incluído para itens que exigem ação a cada turno:
kind: lock,kind: bug-dep, e obrigaçõesfires_onjá disparadas. Todo o resto (courtesy,alertinformativo,reminder) é exibido como uma linha (título + countdown); o corpo vive no arquivo, lido sob demanda (Read) quando o título for relevante à tarefa. Reduziu o bloco de 3.964→1.051 tok/turno (~73%) sem perda de acionabilidade (locksbug-depsobrigações mantêm corpo). A relevância por-sessão (mostrar um notice só a sessões que casam um predicado) é trabalho separado (applies_when:avaliado pelo motor Kanon — verrfcs/stack-RFC-015), não esta regra de apresentação. - R3.9 —
audience: ownerfora do bloco técnico. Noticesaudience: owner(pessoaljurídicoadmin do owner) não entram no bloco lido por sessões de código — são ruído para trabalho técnico. Continuam acessíveis via
/k-reminders(que lê os arquivos direto, não a saída do hook). Defaultaudience: all→ só pula quem é explicitamenteowner. - R3.10 — relevância por-sessão via Kanon
applies_when(motor, não bash). Umnotice pode declarar
applies_when:(expressão Kanon do domínio notices — verspecs/kanon/expressions.kmd §4, fatoscomponent/repodo foco da sessão). O hook resolve a relevância chamandokoder-spec-audit notices --component <foco>(o motor — nunca reavalia Kanon em bash, RFC-015 R4): exibe os universais (semapplies_when) + os que casam o foco. Opt-in (ausência = universal) e fail-open por contrato: motor ausenteerrotimeout, foco desconhecido, ou regra inválida ⇒ mostra (nunca esconde um lock). Notices não precisam declararkanon:(default = versão corrente do motor); umkanon:não suportado fail-opens. É o análogo por-turno doapplicable(seleção computada de specs).
R4 — Precedência de leitura (CLAUDE.md)
Toda sessão DEVE tratar o bloco [alerts] como leitura prioritária antes de agir: respeitar locks, honrar obrigações fires_on disparadas, considerar cortesias.
R5 — Migração
- Os itens de
meta/context/reminders.mdviram arquivos `notices/active