globals and synthetic inits

This commit is contained in:
bQUARKz 2026-03-22 12:15:49 +00:00
parent f49dc64fe0
commit ecea7e96c0
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
10 changed files with 2205 additions and 69 deletions

View File

@ -4,6 +4,26 @@
Open
## Purpose
Define how PBS should introduce runtime globals and lifecycle-driven executable bootstrap without changing the runtime contract in this phase.
This document is the umbrella agenda for topic `19`.
It exists to:
- keep the full problem framed in one place;
- define the dependency order between discussions;
- prevent later discussions from re-opening earlier boundaries casually;
- and provide the parent reference for any follow-up agendas created under this topic.
This agenda must converge on:
- the source-level model for mutable module storage;
- the lifecycle model for module init, optional program init, and frame execution;
- the ownership of `FRAME_RET` once the published entrypoint stops being the user's `frame()` directly;
- the migration path from explicit `FrontendSpec` entrypoint configuration to source-derived PBS entrypoint discovery.
## Domain Owner
`docs/compiler/pbs`
@ -66,10 +86,10 @@ O cenário motivador é permitir globals de módulo no PBS sem exigir, nesta fas
Também queremos aproveitar essa discussão para mover a detecção de entrypoint para a source language do PBS, usando atributos explícitos:
```pbs
[INIT]
[Init]
fn init() -> void {}
[FRAME]
[Frame]
fn frame() -> void {}
```
@ -82,9 +102,9 @@ Essa direção permitiria ao frontend PBS:
Esta agenda também cobre a extensão desse modelo para discutir:
1. se `[INIT]` deve existir apenas como hook final de programa ou também como surface por módulo;
2. qual é exatamente o comportamento operacional de `[FRAME]` como root lógico de frame do usuário;
3. como `[INIT]` por módulo, `[INIT]` final de programa, wrapper sintético e `FRAME_RET` se compõem sem ambiguidade.
1. se `[Init]` deve existir apenas como hook final de programa ou também como surface por módulo;
2. qual é exatamente o comportamento operacional de `[Frame]` como root lógico de frame do usuário;
3. como `[Init]` por módulo, `[Init]` final de programa, wrapper sintético e `FRAME_RET` se compõem sem ambiguidade.
Isso empurra a solução para o compiler:
@ -95,6 +115,16 @@ Isso empurra a solução para o compiler:
5. detecção de `init`/`frame` a partir de atributos de source;
6. remoção progressiva das configs explícitas atuais em `FrontendSpec`.
## Inputs
Relevant inputs already present in the repository:
- `docs/vm-arch/ISA_CORE.md` already exposes `GET_GLOBAL` and `SET_GLOBAL`;
- `docs/compiler/pbs/specs/13. Lowering IRBackend Specification.md` currently requires a canonical executable entrypoint to be declared through `FrontendSpec`;
- `docs/compiler/pbs/specs/7. Cartridge Manifest and Runtime Capabilities Specification.md` currently expects PBS v1 manifest `entrypoint` to align with frontend-declared `frame`.
The discussion here must preserve compatibility with those contracts unless it explicitly proposes the propagation work needed to evolve them.
## Core Questions
1. PBS deve introduzir `declare global` como declaração top-level distinta de `declare const`?
@ -102,10 +132,10 @@ Isso empurra a solução para o compiler:
3. O compiler deve sintetizar um module init por módulo que contenha `declare global`?
4. A inicialização deve rodar eager no boot lógico do programa ou lazy no primeiro frame?
5. Deve existir um `init()` custom do usuário que roda depois de todos os module inits e antes do primeiro `frame()`?
6. O PBS deve introduzir atributos explícitos `[INIT]` e `[FRAME]` para identificar as funções corretas do usuário?
6. O PBS deve introduzir atributos explícitos `[Init]` e `[Frame]` para identificar as funções corretas do usuário?
7. A descoberta de entrypoint no frontend PBS deve deixar de depender de configuração explícita em `FrontendSpec`?
8. `[INIT]` deve poder existir também em escopo/módulo de forma declarativa para orientar module init explícito, ou o compiler deve manter module init sempre totalmente sintético?
9. Qual é o contrato preciso de comportamento de `[FRAME]` no PBS: callable obrigatório, assinatura fixa, frequência esperada e relação com o frame lógico publicado?
8. `[Init]` deve poder existir também em escopo/módulo de forma declarativa para orientar module init explícito, ou o compiler deve manter module init sempre totalmente sintético?
9. Qual é o contrato preciso de comportamento de `[Frame]` no PBS: callable obrigatório, assinatura fixa, frequência esperada e relação com o frame lógico publicado?
10. O published frame entrypoint deve passar a ser um wrapper sintético, por exemplo `__pbs_frame()`, em vez do `frame()` do usuário?
11. `FRAME_RET` deve continuar significando "fim do frame lógico" e apenas mudar de owner, saindo da função do usuário para o wrapper sintético?
12. Como o compiler deve tratar ordem topológica, dependências e ciclos entre globals de módulos distintos?
@ -123,11 +153,31 @@ Os seguintes pontos já parecem fixos ou fortemente estabelecidos e não devem s
- o frontend PBS hoje ainda carrega configuração explícita de entrypoint fora da source language;
- nesta fase queremos evitar mudanças novas no runtime e concentrar a evolução no compiler.
## Decisions To Produce
Como umbrella agenda, esta agenda não deve tentar fechar toda a solução normativa de uma vez.
Ela deve sair com:
1. a decomposição oficial do tema em discussões derivadas;
2. a ordem canônica dessas discussões;
3. os inputs e outputs esperados de cada boundary;
4. a lista de artefatos que não podem ser editados antes do boundary anterior fechar.
Depois que as discussões derivadas fecharem, o conjunto deve produzir direção suficiente para virar uma `decision` cobrindo pelo menos:
1. a surface syntax e o modelo semântico de `declare global`;
2. o modelo de bootstrap entre module init sintético, `[Init]` de programa e `[Frame]`;
3. o owner real do frame lógico publicado e o ponto correto de emissão de `FRAME_RET`;
4. a política de descoberta de entrypoint a partir da source language do PBS;
5. a política de ordenação, ciclos e falhas para globals e module init;
6. a política de imports/exports para globals entre módulos.
## Options
### Option A
Adicionar `declare global`, introduzir `[INIT]` e `[FRAME]` como superfícies explícitas para lifecycle/entrypoint, discutir `[INIT]` final vs `[INIT]` por módulo, gerar module init sintético por módulo, gerar wrapper sintético de frame published entrypoint e mover o `FRAME_RET` para esse wrapper.
Adicionar `declare global`, introduzir `[Init]` e `[Frame]` como superfícies explícitas para lifecycle/entrypoint, discutir `[Init]` final vs `[Init]` por módulo, gerar module init sintético por módulo, gerar wrapper sintético de frame published entrypoint e mover o `FRAME_RET` para esse wrapper.
### Option B
@ -155,7 +205,7 @@ Adicionar `declare global`, mas exigir lazy materialization no primeiro acesso e
- remove um acoplamento indesejado entre frontend PBS e configuração manual de entrypoint em `FrontendSpec`.
- Contras:
- exige nova modelagem no compiler para globals, init synthesis e published frame wrapper;
- exige regras novas de atributo e validação de unicidade para `[INIT]` e `[FRAME]`;
- exige regras novas de atributo e validação de unicidade para `[Init]` e `[Frame]`;
- obriga decisão clara sobre ordem, ciclos e falhas;
- muda a relação atual entre `frame()` do usuário e entrypoint publicado.
@ -201,86 +251,439 @@ Direção recomendada para discussão:
2. permitir inicializadores executáveis lowerables, incluindo `new` e calls compatíveis com o modelo de lowering executável;
3. sintetizar um module init por módulo owner de globals;
4. ordenar os module inits de forma determinística e topológica;
5. introduzir `[INIT]` e `[FRAME]` como superfícies explícitas de detecção do init/frame do usuário;
5. introduzir `[Init]` e `[Frame]` como superfícies explícitas de detecção do init/frame do usuário;
6. remover da evolução do PBS a dependência de configuração explícita de entrypoint em `FrontendSpec`, migrando a seleção para a análise da source language;
7. discutir explicitamente se `[INIT]` por módulo será uma surface do usuário ou se módulo continuará com init apenas sintético;
7. discutir explicitamente se `[Init]` por módulo será uma surface do usuário ou se módulo continuará com init apenas sintético;
8. permitir um `init()` custom opcional do usuário, executado depois de todos os module inits;
9. publicar um wrapper sintético de frame, por exemplo `__pbs_frame()`, como verdadeiro frame root;
10. tratar `frame()` do usuário como callable normal invocado por esse wrapper;
11. manter `FRAME_RET` como marcador do fim do frame lógico, mas emitido no wrapper sintético publicado em vez de no `frame()` do usuário;
12. manter toda a primeira fase restrita ao compiler e ao backend pipeline, sem novos requisitos para o runtime além dos contratos já existentes.
Essa recomendação ainda deixa alguns pontos para fechamento na `decision`, mas já estabelece um shape arquitetural coerente:
- storage global pertence ao módulo owner;
- bootstrap observável pertence ao compiler, não ao runtime;
- `frame()` do usuário deixa de ser o entrypoint publicado e passa a ser o root lógico invocado pelo wrapper;
- `FRAME_RET` continua significando fim do frame lógico, apenas com novo owner sintético;
- a seleção de entrypoint deixa de ser configuração paralela e vira semântica explícita da linguagem.
## Open Questions
1. O nome surface deve ser exatamente `declare global`, ou outra forma top-level?
2. Globals devem exigir initializer obrigatório em v1, ou existirão shells reservados parecidos com builtin const?
3. `[INIT]` e `[FRAME]` devem ser atributos reservados do PBS ou outra forma de marker surface?
4. `[INIT]` deve exigir assinatura fixa `fn init() -> void`?
5. `[FRAME]` deve exigir assinatura fixa `fn frame() -> void`?
6. Deve existir no máximo um `[INIT]` e um `[FRAME]` por programa, por módulo owner, ou por outro escopo?
7. `[INIT]` por módulo deve ser permitido como surface explícita do usuário ou ser non-goal da primeira fase?
8. Se `[INIT]` por módulo existir, ele roda antes ou no lugar do module init sintético de globals?
9. A ausência de `[INIT]` deve ser válida, mas a ausência de `[FRAME]` deve ser erro hard?
10. O `init()` do usuário pertence apenas ao módulo owner do published frame entrypoint ou pode vir de qualquer módulo visível?
11. O compiler deve sintetizar `boot_done` como global oculto de programa, local estático do wrapper, ou outro mecanismo interno?
12. Em caso de trap/falha durante module init ou `init()`, o próximo frame deve tentar novamente ou o programa deve entrar em estado terminal?
13. Módulos podem ler globals de outros módulos durante init, desde que a ordem topológica já os tenha materializado?
14. Como diagnosticar ciclos entre globals intra-módulo e inter-módulo de forma didática?
15. Qual é a semântica exata de `[FRAME]` para o compiler e para a documentação da linguagem: raiz de tick, raiz de frame lógico, ou apenas callable nomeado para publicação?
16. O barrel/export de `global` deve seguir a mesma superfície de `const`, ou precisamos de distinção explícita na documentação/export metadata?
17. O `IRBackend` deve ganhar instruções explícitas `GET_GLOBAL`/`SET_GLOBAL`, ou globals devem ser lowered por outra representação intermediária antes de virar bytecode?
18. Em que etapa da migração a configuração explícita atual em `FrontendSpec` deixa de existir por completo?
3. `[Init]` e `[Frame]` devem ser atributos reservados do PBS ou outra forma de marker surface?
4. `[Init]` deve exigir assinatura fixa `fn init() -> void`?
5. `[Frame]` deve exigir assinatura fixa `fn frame() -> void`?
6. Deve existir no máximo um `[Init]` e um `[Frame]` por programa, por módulo owner, ou por outro escopo?
7. Module init deve permanecer sempre sintético em v1, ou o usuário pode anotar hooks por módulo em alguma forma futura?
8. A política de falha de init deve ser fail-fast definitivo no boot, ou existe algum caso legítimo para retry controlado?
9. Reexport de globals entre módulos deve preservar identidade de storage do owner original ou materializar aliases/import bindings sem storage próprio?
10. O manifest `entrypoint` deve continuar expondo `frame` por compatibilidade nominal ou passar a refletir o símbolo sintético publicado?
## Expected Decisions to Produce
## Main Difficulties
1. A surface language e a gramática de `declare global`.
2. A surface language e a política de detecção de entrypoint via `[INIT]` e `[FRAME]`.
3. O papel de `[INIT]` por módulo versus module init totalmente sintético.
4. O contrato semântico de globals de módulo em PBS.
5. O modelo de inicialização sintética por módulo.
6. O contrato de `init()` custom do usuário.
7. A semântica comportamental de `[FRAME]` como root lógico do usuário.
8. A política de ordering e ciclo para init/global dependencies.
9. O novo owner do `FRAME_RET` no pipeline de frame.
10. O plano de remoção das configs explícitas atuais em `FrontendSpec`.
11. O recorte exato desta fase como mudança somente de compiler.
Os principais pontos de dificuldade desta agenda não são sintáticos; eles estão na composição entre contratos já existentes.
### 1. `declare global` colide com o recorte atual de `declare const`
Hoje a semântica estática do PBS fixa que:
- `declare const` entra no value namespace;
- `declare const` não materializa storage mutável runtime;
- inicializadores de `declare const` pertencem a um subset estritamente compile-time;
- dependências entre `declare const` são acíclicas e resolvidas por análise de dependência, não por ordem textual.
Introduzir `declare global` exige decidir o que é reaproveitado desse modelo e o que deixa de valer:
- namespace e visibilidade podem reaproveitar parte do modelo atual;
- constant evaluation não pode ser reutilizada como está;
- o modelo de dependência deixa de ser apenas compile-time e passa a produzir efeitos runtime.
### 2. A surface de atributos hoje tem um conjunto reservado fechado
O PBS já trata atributos como metadata compile-time com lowering explícito apenas quando outra spec define esse efeito.
Isso é favorável para `[Init]` e `[Frame]`, mas cria trabalho em três frentes:
- reservar novos atributos no conjunto normativo;
- definir targets válidos, unicidade e diagnóstico;
- definir o lowering sem deixar atributos "soltos" como metadata sem efeito operacional claro.
### 3. O entrypoint publicado hoje ainda está acoplado ao `FrontendSpec`
Existe uma obrigação atual em `13. Lowering IRBackend Specification.md` para o frontend declarar um `EntrypointRef` canônico.
A agenda 19 quer mover a descoberta para a source language, mas isso abre uma transição delicada:
- PBS precisa derivar esse `EntrypointRef` da source language;
- o contrato de `IRBackend` não deve perder determinismo;
- a migração não pode deixar coexistirem por muito tempo duas autoridades independentes para entrypoint.
### 4. O manifest ainda assume `frame` como callable publicado
`7. Cartridge Manifest and Runtime Capabilities Specification.md` hoje sugere alinhamento nominal entre manifest `entrypoint` e callable `frame`.
Se o compiler publicar `__pbs_frame()`:
- ou o manifest passa a refletir o símbolo sintético real;
- ou o manifest preserva um nome lógico enquanto o símbolo real do artefato muda;
- ou a pipeline passa a distinguir entrypoint lógico e entrypoint físico.
Sem fechar isso, a solução de wrapper fica tecnicamente incompleta.
### 5. `FRAME_RET` hoje coincide com o fim do callable do usuário
O ponto difícil não é só "mover um opcode". É redefinir qual callable delimita semanticamente o frame lógico:
- `frame()` do usuário continua sendo apenas código de frame;
- o wrapper sintético vira o owner do frame published entrypoint;
- `FRAME_RET` continua com o mesmo significado semântico, mas sua posição material muda.
Isso afeta lowering, testes de conformance e a forma de explicar o modelo da linguagem.
### 6. Globals intermodulares introduzem um problema novo de identidade e ordem
Hoje `declare const` exportado via `mod.barrel` é simples porque não há storage mutável compartilhado.
Com `declare global`, a agenda precisa fechar:
- quem é o owner real do storage;
- o que um import recebe: binding para o owner ou cópia/snapshot;
- como ciclos entre módulos são detectados;
- se init pode ler globals de módulos predecessores;
- qual é a ordem determinística entre módulos quando há efeitos de inicialização.
### 7. A política de falha deixa de ser apenas rejeição de build
Hoje boa parte do pipeline falha antes da emissão.
Com module init e `init()` do usuário, surge um problema runtime-observable:
- o que acontece quando o boot parcial falha;
- se o programa entra em estado terminal;
- se existe retry;
- como isso se relaciona com o fato de `FRAME_SYNC` permanecer o safepoint normativo.
## Work Boundaries
Para manter a agenda executável e evitar mistura de artefatos, o trabalho pode ser separado nos seguintes boundaries.
### Boundary A. Surface Language and AST
Owner principal: syntax + AST.
Inclui:
- gramática de `declare global`;
- superfície de atributos `[Init]` e `[Frame]`;
- targets permitidos desses atributos;
- novos nós ou flags obrigatórios no AST para globals e lifecycle markers.
Pergunta de fechamento:
- o parser apenas aceita as superfícies ou já impõe parte das restrições estruturais de target/shape?
### Boundary B. Static Semantics and Linking
Owner principal: static semantics + linking.
Inclui:
- namespace e visibilidade de `declare global`;
- regras de initializer obrigatório ou opcional;
- compatibilidade de tipo do initializer;
- unicidade e assinatura válida para `[Init]` e `[Frame]`;
- regras de import/export/barrel para globals;
- detecção de ciclos de dependência entre globals;
- política de identidade do storage através de imports e reexports.
Pergunta de fechamento:
- globals entram no mesmo value namespace de `let`/`declare const` ou exigem distinção semântica adicional apesar do namespace compartilhado?
### Boundary C. Dynamic Semantics and Lifecycle Model
Owner principal: dynamic semantics.
Inclui:
- ordem observável entre module init, `[Init]` de programa e `[Frame]`;
- definição de boot one-shot;
- política de falha de init;
- contrato semântico de `[Frame]` como raiz lógica do tick/frame do usuário;
- relação entre frame lógico do usuário e entrypoint efetivamente publicado.
Pergunta de fechamento:
- o programa tem dois conceitos distintos, `logical frame root` e `published runtime entrypoint`, ou a spec tenta esconder essa distinção do usuário?
### Boundary D. IR and Backend Lowering
Owner principal: lowering executável.
Inclui:
- representação de globals no `IRBackend` ou boundary imediatamente anterior;
- synthesis de module init;
- synthesis de wrapper published entrypoint;
- materialização do guard one-shot de boot;
- reposicionamento do `FRAME_RET`;
- preservação do `EntrypointRef` canônico para o restante do backend.
Pergunta de fechamento:
- globals entram como primitivo explícito no IR ou aparecem apenas em lowering posterior antes de IRVM/bytecode?
### Boundary E. Artifact and Manifest Publication
Owner principal: artifact contracts.
Inclui:
- nome do entrypoint exposto no manifest;
- relação entre callable do usuário, símbolo sintético e entrypoint published;
- compatibilidade com o contrato atual do cartridge manifest;
- possíveis ajustes na leitura de tooling sobre qual callable é o entrypoint "real".
Pergunta de fechamento:
- o manifest publica identidade lógica da linguagem ou identidade física do artefato executável?
### Boundary F. Diagnostics and Conformance
Owner principal: diagnostics + fixture model.
Inclui:
- diagnósticos para target inválido de `[Init]` e `[Frame]`;
- duplicidade ou ausência de markers obrigatórios;
- initializer inválido de `declare global`;
- ciclos intra/inter-módulo;
- imports/reexports ilegais de globals;
- fixture coverage para init ordering, wrapper published entrypoint e `FRAME_RET`.
Pergunta de fechamento:
- quais erros pertencem ao frontend/linking e quais só podem ser cobertos por fixtures de toolchain completo?
## Discussion Order
As discussões derivadas desta umbrella agenda devem seguir a ordem abaixo.
O objetivo da ordem é simples:
- fechar primeiro autoridade semântica e surface;
- depois fechar comportamento observável;
- só então descer para lowering, artefato publicado e conformance.
### Stage 1. Globals Surface and Static Identity
Boundary owner:
- `Boundary A. Surface Language and AST`
- `Boundary B. Static Semantics and Linking`
Esta discussão deve fechar primeiro porque todas as outras dependem da definição do que é um global no PBS.
Ela precisa decidir:
- a forma exata de `declare global`;
- initializer obrigatório ou opcional;
- namespace, visibilidade e barrel/export/import;
- identidade do storage owner;
- política de reexport;
- ciclos e dependências entre globals em nível de linking/static semantics.
Sem esse fechamento:
- lifecycle ainda não sabe o que precisa inicializar;
- lowering ainda não sabe que entidade precisa materializar;
- manifest ainda não sabe se está publicando símbolos que dependem de storage global importado.
### Stage 2. Lifecycle Markers and Program Bootstrap Semantics
Boundary owner:
- `Boundary C. Dynamic Semantics and Lifecycle Model`
Esta discussão vem depois de Stage 1 porque init e frame só fazem sentido quando o modelo de globals já estiver fixado.
Ela precisa decidir:
- se `[Init]` e `[Frame]` entram como superfícies oficiais;
- unicidade e assinatura desses markers;
- ordem entre module init, `[Init]` de programa e `[Frame]`;
- política de boot one-shot;
- política de falha;
- papel semântico exato de `frame()` do usuário.
Sem esse fechamento:
- o wrapper sintético não tem contrato observável estável;
- `FRAME_RET` não tem owner definido;
- qualquer modelagem de IR corre risco de materializar o comportamento errado.
### Stage 3. Published Entrypoint, Wrapper Ownership, and `FRAME_RET`
Boundary owner:
- `Boundary C. Dynamic Semantics and Lifecycle Model`
- `Boundary D. IR and Backend Lowering`
- `Boundary E. Artifact and Manifest Publication`
Esta discussão depende de Stage 2 porque ela não decide mais "o que é lifecycle"; ela decide como lifecycle é publicado.
Ela precisa decidir:
- se o published entrypoint é um wrapper sintético;
- qual é a relação entre `frame()` do usuário e esse wrapper;
- onde `FRAME_RET` passa a ser emitido;
- se o manifest expõe entrypoint lógico, entrypoint físico ou ambos;
- como o `EntrypointRef` do frontend continua determinístico durante a transição.
Sem esse fechamento:
- não existe contrato estável para lowering final;
- specs de manifest e `IRBackend` ficam em tensão;
- o tooling pode continuar com duas autoridades concorrentes para entrypoint.
### Stage 4. IR Representation and Lowering Mechanics
Boundary owner:
- `Boundary D. IR and Backend Lowering`
Esta discussão vem depois de Stage 3 porque a forma do IR depende do que exatamente precisa ser publicado e executado.
Ela precisa decidir:
- se globals entram como primitivo explícito do IR ou apenas em lowering posterior;
- como module init é representado;
- como o guard de boot é materializado;
- como o wrapper published entrypoint é emitido;
- como `FRAME_RET` é reposicionado mantendo o mesmo significado.
Sem esse fechamento:
- código pode ser implementado cedo demais sobre contratos ainda móveis;
- a propagação para specs gerais de lowering fica prematura;
- testes de backend podem congelar um shape errado.
### Stage 5. Diagnostics, Gates, and Conformance Coverage
Boundary owner:
- `Boundary F. Diagnostics and Conformance`
Esta discussão deve vir por último porque ela consolida o que as anteriores já fixaram.
Ela precisa decidir:
- catálogo mínimo de diagnósticos novos;
- divisão entre erro de syntax/AST, linking/static semantics, lowering e toolchain;
- fixtures obrigatórios para globals, lifecycle, wrapper e entrypoint publication;
- critérios de aceite para a migração de `FrontendSpec` entrypoint config para source-derived entrypoint discovery.
Fazer isso antes cria dois riscos:
- congelar diagnósticos para regras ainda instáveis;
- ou escrever fixtures de conformance que depois precisam ser reabertas por mudança arquitetural.
## Derived Agenda Outline
Se quisermos quebrar a umbrella agenda em agendas filhas, a ordem recomendada é:
1. `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
2. `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`
3. `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda`
4. `19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda`
5. `19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda`
Cada agenda filha deve declarar explicitamente:
- que deriva da umbrella agenda `19`;
- quais stages anteriores já são input fixo;
- e quais outputs ela precisa entregar para a agenda seguinte.
## Boundary Locking Rules
Para evitar que discussões posteriores prejudiquem as anteriores, esta umbrella agenda fixa as seguintes regras de sequenciamento:
1. `19.2` não redefine surface syntax ou identidade de globals já fechadas em `19.1`.
2. `19.3` não redefine o significado semântico de `[Init]`, `[Frame]` ou boot ordering já fechados em `19.2`.
3. `19.4` não reabre decisões de linguagem ou de lifecycle; ela apenas escolhe a representação e o mecanismo de lowering compatíveis com elas.
4. `19.5` não reabre arquitetura; ela consolida diagnostics, manifest propagation e coverage.
5. Qualquer ponto que force reabertura de stage anterior deve voltar explicitamente para a agenda filha owner correta, em vez de ser resolvido informalmente na etapa posterior.
## Likely Propagation Targets
Mesmo sem virar `decision` ainda, esta agenda já mostra impacto provável nos seguintes artefatos.
### PBS Specs
- `3. Core Syntax Specification.md`
- `4. Static Semantics Specification.md`
- `9. Dynamic Semantics Specification.md`
- `11. AST Specification.md`
- `12. Diagnostics Specification.md`
- `13. Lowering IRBackend Specification.md`
- `7. Cartridge Manifest and Runtime Capabilities Specification.md`
### Cross-Domain / VM Architecture
- `docs/vm-arch/ISA_CORE.md` provavelmente não precisa mudar em capability, mas precisa ser referenciado explicitamente como base de `GET_GLOBAL`/`SET_GLOBAL`;
- specs gerais de lowering fora de `docs/compiler/pbs` podem precisar propagação posterior se `IRBackend` ganhar representação explícita de globals ou novo contrato para entrypoint published.
### Compiler / Implementation Areas
- parser e AST model;
- linking/resolution;
- static semantics;
- IR model do frontend PBS;
- executable lowering para `IRBackend` e `IRVM`;
- montagem do manifest/cartridge metadata;
- suites de fixture de frontend, lowering e toolchain.
## Boundary Risks
Se os boundaries acima não forem respeitados, os riscos mais prováveis são:
1. criar `declare global` como açúcar superficial sem lifecycle coerente;
2. introduzir `[Init]` e `[Frame]` sem autoridade clara sobre `FrontendSpec`;
3. mover `FRAME_RET` sem redefinir com precisão o owner do frame lógico;
4. resolver globals intra-módulo mas deixar imports/reexports semanticamente ambíguos;
5. fechar a solução do compiler sem fechar o contrato do manifest;
6. misturar nesta agenda decisões de produto/runtime que deveriam continuar fora de escopo.
## Expected Spec Material
- atualização da Core Syntax specification para `declare global`;
- atualização da Core Syntax specification para `[INIT]` e `[FRAME]`;
- atualização da Static Semantics specification para typing, visibility e rules de init/global;
- atualização da Dynamic Semantics specification para lifecycle de globals e boot one-shot;
- atualização da Lowering spec para globals, module init synthesis, entrypoint detection e published frame wrapper;
- eventual atualização de AST/IR contracts se essas superfícies forem documentadas.
Se esta agenda virar `decision`, a propagação esperada deve atingir pelo menos:
- spec de declarações top-level do PBS para introduzir `declare global`;
- spec de lifecycle/entrypoint attributes para `[Init]` e `[Frame]`;
- spec de lowering executável/`IRBackend` para module init synthesis, wrapper published entrypoint e ownership de `FRAME_RET`;
- spec de cartridge manifest para esclarecer a relação entre entrypoint lógico do usuário e entrypoint publicado no artefato;
- diagnósticos de validação para unicidade, assinatura inválida, ciclos de init e uso incorreto de globals.
## Non-Goals
- redefinir a VM para adicionar uma nova capacidade de globals;
- introduzir um novo hook explícito de runtime nesta fase;
- redesenhar todo o lifecycle de cartridge/firmware fora do que o compiler já publica como entrypoint;
- resolver lazy module loading ou hot reload;
- manter indefinidamente configuração duplicada de entrypoint em `FrontendSpec` e na source language;
- projetar persistência/snapshot/save-state de globals como parte desta discussão inicial.
Esta agenda não deve:
## Next Step Suggested
1. redesenhar o runtime ou introduzir novas instruções de VM;
2. escrever a spec normativa final de globals e lifecycle;
3. redefinir o modelo geral de módulos do compiler fora do necessário para globals/init;
4. resolver todos os possíveis hooks futuros de programa além de `[Init]` e `[Frame]`;
5. discutir otimizações de performance de init além do necessário para garantir semântica determinística.
Converter esta agenda em uma `decision` do domínio `compiler/pbs` fechando:
## Next Step
1. a forma de `declare global`;
2. a superfície `[INIT]`/`[FRAME]` e a nova detecção de entrypoints;
3. o modelo de module init sintético;
4. o papel do `init()` do usuário;
5. o published frame wrapper;
6. o reposicionamento do `FRAME_RET`;
7. a remoção da configuração explícita atual em `FrontendSpec`.
Usar esta agenda como parent reference e abrir as agendas filhas na ordem abaixo:
Depois disso, abrir um `pull-request/plan` que separe explicitamente:
1. `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
2. `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`
3. `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda`
4. `19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda`
5. `19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda`
1. parser/AST;
2. semântica de atributos `[INIT]`/`[FRAME]` e resolução de entrypoint;
3. semântica e barrel/export/import;
4. IR/backend model para globals;
5. synthesis de init e frame wrapper;
6. remoção das configs explícitas antigas no frontend registry/specs;
7. testes de regressão do pipeline compilador -> IR -> bytecode.
Somente depois do fechamento dessas agendas derivadas esta linha deve virar uma `decision` consolidada em `docs/compiler/pbs/decisions`.

View File

@ -0,0 +1,406 @@
# PBS Globals Surface, Identity, and Module Boundaries Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
This is the first derived discussion under umbrella topic `19`.
## Purpose
Define the source-level and module-level model for PBS runtime globals before lifecycle, entrypoint publication, or lowering details are discussed.
This agenda exists to close the semantic base for:
- the `declare global` surface;
- storage ownership;
- import/export/alias behavior;
- and dependency/cycle rules at module boundary level.
Later agendas under topic `19` must treat the outputs of this agenda as fixed input.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS do compiler porque define surface language, linking semantics e boundaries de módulo para uma nova categoria de declaração top-level.
## Context
Hoje o PBS já possui `declare const`, mas esse surface é estritamente compile-time:
- `declare const` entra no value namespace;
- `declare const` continua reservado a valores imutáveis que não representam storage mutável de módulo;
- `declare const` não materializa storage mutável runtime;
- o initializer de `declare const` pertence a um subset constante;
- dependências de `declare const` são resolvidas por análise de dependência compile-time.
Ao mesmo tempo:
- a VM já expõe `GET_GLOBAL` e `SET_GLOBAL`;
- a linguagem ainda não expõe globals mutáveis de módulo;
- `mod.barrel` hoje já governa export/import de `const`, `fn`, tipos e outras famílias de declaração;
- não existe ainda contrato explícito para storage compartilhado intermodular em source PBS.
Antes de discutir init, frame wrapper ou `FRAME_RET`, precisamos fechar o que um `global` é no modelo da linguagem.
## Inputs Already Fixed Elsewhere
Os seguintes inputs já parecem fixos e não devem ser reabertos aqui:
- `declare const` não é storage mutável/runtime-initialized;
- `declare const` continua semanticamente separado de `declare global`;
- `declare const` continua voltado a valores imutáveis e não deve ser reutilizado como surface de global;
- modules e globals devem compartilhar o mesmo tipo estrutural de análise de dependência;
- essa análise comum deve vir de um refactor do algoritmo atual de modules para um `DependencyGraphAnaliser` em `util.structures` na infra;
- top-level executable statements continuam proibidos;
- a VM já suporta globals por slot;
- esta fase ainda é compiler-driven e não deve exigir capability nova do runtime;
- barrel continua sendo a fonte de visibilidade entre módulos.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. a surface exata de `declare global`;
2. a obrigatoriedade de initializer em todos os globals de v1;
3. o namespace ocupado por globals;
4. a política de visibility/barrel para globals;
5. a identidade do storage owner;
6. a semântica de import, export e alias;
7. a política de dependência e ciclos entre globals.
## Core Questions
1. PBS deve introduzir exatamente `declare global name: T = expr;` como nova forma top-level?
2. `declare global` deve exigir type annotation explícita em todos os casos?
3. Quais formas de initializer executável são admissíveis para `declare global` em v1?
4. `declare global` entra no mesmo value namespace de `let` e `declare const`?
5. O barrel deve ganhar uma entrada `global` distinta?
6. Um `global` importado referencia sempre o storage do módulo owner original?
7. Como imports de globals usam alias quando necessário sem introduzir shadowing?
8. O que é proibido em imports de globals para evitar ambiguidade?
9. A dependência entre globals deve ser modelada por grafo determinístico semelhante ao de `declare const`, mas agora com efeito runtime?
10. Como ciclos entre globals intra-módulo e inter-módulo devem ser detectados e reportados?
11. Como o grafo de globals mapeia para o `DependencyGraphAnaliser` comum sem perder owner canônico nem attribution?
## Options
### Option A
Adicionar `declare global` como nova declaração top-level explícita, com type annotation obrigatória, initializer obrigatório em v1, entry própria `global` em barrel, sem reexport e identidade de storage sempre pertencente ao módulo owner original.
### Option B
Adicionar `declare global`, mas permitir globals sem initializer em v1 e tratar inicialização default como parte do modelo base.
## Tradeoffs
### Option A
- Prós:
- semântica explícita e fácil de ensinar;
- evita ambiguidade entre constant value e mutable storage;
- força o barrel e os imports a refletirem corretamente a nova categoria declarativa;
- reduz risco de globals parcialmente formados sem política clara de init.
- Contras:
- aumenta a superfície da linguagem;
- exige atualização de parser, AST, linking e barrel matching.
### Option B
- Prós:
- maior flexibilidade futura;
- permite reservar globals antes de decidir a inicialização concreta.
- Contras:
- empurra complexidade para lifecycle e diagnostics;
- enfraquece a previsibilidade de v1;
- cria edge cases sobre leitura antes de materialização.
## Recommendation
Seguir com a **Option A**.
Direção recomendada:
1. `declare global` deve ser uma declaração top-level distinta de `declare const`;
2. type annotation explícita deve ser obrigatória;
3. initializer deve ser obrigatório em v1;
4. `declare const` não deve ser reutilizado para globals e continua reservado a valores imutáveis;
5. globals devem entrar no value namespace, mas com categoria declarativa distinta;
6. barrel deve refletir essa distinção explicitamente com `mod global` e `pub global`;
7. PBS não deve introduzir reexport de globals nesta linha;
8. imports de globals devem preservar a identidade do storage owner original e usar alias quando necessário;
9. shadowing entre imports de `fn`, `service`, `global` e `const` deve ser compile-time error;
10. dependências entre globals devem ser determinísticas e diagnosticáveis.
Também fica registrado nesta agenda um direction técnico já decidido:
1. modules e globals usarão o mesmo tipo estrutural de análise de dependência;
2. isso deve nascer de um refactor do algoritmo atual de modules;
3. o artefato comum deve viver na infra como `DependencyGraphAnaliser` em `util.structures`;
4. cada domínio continua responsável por construir seus próprios nós canônicos, suas arestas e seu mapeamento de diagnóstico;
5. o que é compartilhado é o kernel estrutural de ordenação topológica e detecção de ciclos, não a semântica específica de cada grafo.
## Global Dependencies
O ponto mais sensível desta agenda é a dependência entre globals.
Com `declare const`, dependência significa apenas ordem de avaliação compile-time.
Com `declare global`, dependência passa a significar ordem de materialização runtime, identidade de storage e risco real de efeito parcial.
Exemplos do tipo de dependência que passam a existir:
```pbs
declare global a: int = 1;
declare global b: int = a + 1;
```
Aqui `b` depende de `a` porque o initializer de `b` lê o valor de `a`.
```pbs
declare global origin: Vec2 = new Vec2(0, 0);
declare global camera: Camera = new Camera(origin);
```
Aqui `camera` depende de `origin` porque o initializer usa o valor já materializado de outro global.
Quando há múltiplos módulos, o problema cresce:
```pbs
// module A
declare global seed: int = 7;
// module B
import { seed } from @project:A;
declare global rng: Rng = new Rng(seed);
```
Agora a ordem de materialização não é apenas intra-módulo. Ela atravessa módulos e precisa continuar determinística.
### Dependency Kinds
Esta agenda precisa distinguir pelo menos três formas de dependência:
1. dependência local entre globals do mesmo módulo;
2. dependência intermodular por import de global do módulo owner original;
3. dependência indireta por alias de import, onde o nome local muda, mas o owner do storage permanece igual.
### Main Problems
Os principais problemas de dependência de globals são:
1. ordem de inicialização deixa de poder depender de ordem textual do arquivo;
2. imports com alias podem esconder o owner real do storage;
3. leitura de global ainda não materializado precisa ser proibida por modelo, não tratada como comportamento oportunista;
4. ciclos deixam de ser apenas "ciclos de expressão" e passam a ser ciclos de materialização runtime;
5. o grafo de dependências pode cruzar múltiplos módulos e aliases, o que piora atribuição de diagnóstico.
### Risks
Se a política de dependência de globals ficar frouxa, os riscos são altos:
1. inicialização parcialmente observável:
um global pode ler outro antes de ele existir semanticamente;
2. ordem acidental por arquivo:
a implementação pode acabar usando ordem textual ou ordem de carregamento do projeto, o que quebra determinismo;
3. aliasing ambíguo:
aliases de import podem parecer símbolo novo quando na verdade são só outro nome para o mesmo owner;
4. ciclos opacos:
`A -> B -> C -> A` pode ficar escondido atrás de import e alias, dificultando explicação ao usuário;
5. bootstrap contaminado:
se essa agenda deixar dependência frouxa, a 19.2 herda um lifecycle impossível de explicar;
6. lowering prematuro:
se o compiler materializar a ordem no backend sem política semântica firme, a arquitetura congela cedo demais.
### Direction for Discussion
Minha direção recomendada para dependências de globals nesta agenda é:
1. toda leitura de outro global no initializer cria uma edge explícita no grafo de dependência;
2. a resolução do grafo deve ser determinística e independente de source-file order;
3. imports e aliases devem preservar o owner original do storage no grafo;
4. ciclos entre globals devem ser erro estático, não comportamento runtime com retry ou valor parcial;
5. a agenda deve deixar claro que esta política fecha apenas identidade e dependência;
boot ordering completo continua pertencendo à `19.2`.
### Structural Reuse Decision
Esta agenda também registra uma decisão arquitetural de implementação que afeta a evolução futura:
1. o algoritmo atual usado para dependência de modules deve ser refatorado em um componente estrutural reutilizável;
2. esse componente comum deve se chamar `DependencyGraphAnaliser`;
3. ele deve viver em `util.structures` na infra;
4. modules e globals devem consumir esse mesmo componente estrutural;
5. a semântica que descobre edges continua separada por domínio.
Isso significa:
- o import graph de modules e o init graph de globals não serão o mesmo grafo semântico;
- mas ambos deverão usar o mesmo kernel estrutural para:
- topological ordering,
- cycle detection,
- e suporte a diagnóstico determinístico.
## Current Direction
Os pontos abaixo já podem ser tratados como direção fechada desta agenda, salvo objeção nova forte:
1. a surface correta é `declare global BLA: int = 1;`;
2. type annotation é obrigatória, como em `declare const`;
3. initializer é sempre obrigatório;
4. globals entram no value namespace como categoria declarativa distinta;
5. `mod.barrel` deve expor globals com entradas `mod global Name;` e `pub global Name;`;
6. imports de globals usam o mesmo esquema de alias quando necessário;
7. PBS não introduz reexport de globals nesta linha;
8. shadowing entre imports de `fn`, `service`, `global` e `const` é compile-time error e deve ser resolvido com alias;
9. detecção de dependência, ordenação e ciclo deve nascer do `DependencyGraphAnaliser` comum, com contexto semântico próprio de globals.
Também fica explicitado o modelo de namespace:
1. globals entram no value namespace;
2. `declare const`, `declare global` e o singleton canônico de `service` convivem no espaço nominal de valores;
3. `fn`, `service`, `global` e `const` não podem introduzir o mesmo nome visível no mesmo scope de resolução;
4. essa colisão é erro de compile time, mesmo quando a ambiguidade nasce de import;
5. `locals > struct/class members > globals`, inclusive quando o global veio por import.
## Remaining Open Points
Com a direção já consolidada nesta agenda, não restam pontos substantivos em aberto dentro do boundary de `19.1`.
Os temas adicionais de lifecycle seguem encaminhados para a `19.2`.
## Initializer Boundary
Esta agenda também precisa deixar explícita a fronteira entre:
1. init da declaração `global`;
2. init por módulo;
3. init de projeto.
Direção recomendada:
- o initializer de `declare global` deve permanecer restrito e simples;
- ele existe para materializar o valor inicial da storage de módulo;
- ele não deve carregar lógica procedural maior nem depender de top-level `fn`;
- lógica adicional de setup pertence ao init por módulo;
- setup de programa pertence ao init de projeto.
Com isso, a linha proposta fica:
1. init da global:
permite primitivas, operações simples com valor, member access com valor nessa etapa, `new`, e leitura de outras globals compatíveis com o dep graph;
2. init do módulo:
permite lógica adicional de conveniência e mutações nas globals do próprio módulo após a materialização inicial;
3. init do projeto:
permite setup do projeto como um todo, depois do fechamento do estado de módulo necessário.
### Consequence for `fn` in Global Initializers
Seguindo essa separação, esta agenda recomenda que:
1. calls para top-level `fn` não sejam permitidas em initializer de `global` em v1;
2. a necessidade de lógica mais rica seja absorvida pelo init de módulo discutido na `19.2`;
3. isso mantenha o dep graph de globals focado em materialização declarativa, não em execução procedural arbitrária.
Também fica recomendado que, em v1:
1. `some(...)` e `none` não façam parte do initializer declarativo de `global`;
2. `if` e `switch` não sejam permitidos diretamente na declaração de `global`.
## Diagnostics Recommendation
Esta agenda fecha o seguinte catálogo pequeno, forte e didático de diagnósticos obrigatórios.
### 1. Invalid Global Initializer
Quando o initializer usar uma forma não admissível em v1, o compiler deve emitir um erro apontando a subexpressão inválida.
Mensagem semântica recomendada:
- `global initializer uses unsupported form`
Exemplos de motivo associado:
- top-level `fn` call not allowed in global initializer;
- `some(...)` not allowed in global initializer;
- `if` not allowed in global initializer.
### 2. Global Dependency Cycle
Quando houver ciclo, o compiler deve diagnosticar o global owner local e, quando possível, renderizar o caminho canônico do ciclo.
Mensagem semântica recomendada:
- `global dependency cycle detected`
Detalhe recomendado:
- incluir o símbolo local que participa do ciclo;
- incluir o caminho canônico quando disponível, por exemplo:
`@project:A.seed -> @project:B.rng -> @project:C.config -> @project:A.seed`
### 3. Shadowing Requires Alias
Quando um import introduzir nome que colide com `fn`, `service`, `global` ou `const` já visível, o compiler deve rejeitar e exigir alias explícito.
Mensagem semântica recomendada:
- `imported symbol shadows existing visible symbol; alias required`
### 4. Global Import Category Mismatch
Se o barrel e a importação não preservarem corretamente a categoria declarativa `global`, o compiler deve rejeitar explicitamente em vez de tentar degradar para `const` ou valor genérico.
Mensagem semântica recomendada:
- `global import must resolve through a global barrel entry`
Essa escolha existe para preservar:
- clareza semântica;
- diagnósticos mais simples;
- e uma separação limpa entre storage initialization e lifecycle hooks.
## Forwarded To 19.2
Os seguintes temas surgiram desta agenda, mas pertencem ao boundary de lifecycle e devem ser tratados na `19.2`:
1. init por módulo como fase procedural separada do initializer declarativo de `global`;
2. init de projeto como fase distinta de coordenação final;
3. permissão de loops em init de módulo e init de projeto;
4. proibição de host calls por padrão durante init;
5. permissão apenas para host calls marcadas com atributo reservado `[InitAllowed]`;
6. validação compile-time dessa admissibilidade;
7. separação entre restrição normativa de host interaction e recomendação de design para manter bootstrap leve.
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `3. Core Syntax Specification.md`
- `4. Static Semantics Specification.md`
- `11. AST Specification.md`
- `12. Diagnostics Specification.md`
## Non-Goals
Esta agenda não deve:
1. definir boot ordering;
2. definir `[Init]` ou `[Frame]`;
3. definir wrapper published entrypoint;
4. definir posicionamento de `FRAME_RET`;
5. escolher a representação de IR/bytecode para globals.
## Next Step
Depois de fechar esta agenda, abrir ou aprofundar:
- `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`

View File

@ -0,0 +1,361 @@
# PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
Expected fixed input from previous stage:
- `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
## Purpose
Define the observable lifecycle model of PBS once runtime globals exist.
This agenda must close:
- whether `[Init]` and `[Frame]` become official source markers;
- the relationship between module init and program init;
- boot one-shot behavior;
- failure policy;
- and the semantic role of the user's frame root.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS porque define lifecycle observável da linguagem, não apenas mecanismo de backend.
## Context
Depois que `declare global` existir como storage runtime de módulo, o compiler precisará definir:
- quando os globals são materializados;
- em que ordem isso acontece;
- se existe um hook explícito de init de programa;
- se existe um init procedural por módulo separado do initializer declarativo de `global`;
- como a linguagem identifica o frame root do usuário;
- e o que acontece quando o boot falha.
Esta agenda ainda não decide a forma exata do wrapper published entrypoint nem o shape do IR. Ela define primeiro o comportamento observável que essas camadas posteriores terão de respeitar.
## Inputs Already Fixed Elsewhere
Os seguintes inputs devem ser tratados como fechados ou fortemente estáveis aqui:
- o modelo base de globals vindo de `19.1`;
- o initializer declarativo de `global` permanece restrito a primitivas, `new` e leitura de outras globals compatíveis com o dep graph;
- top-level `fn` não são permitidas em initializer de `global` em v1;
- `FRAME_SYNC` permanece como safepoint normativo;
- PBS v1 continua determinístico e single-threaded;
- esta fase continua evitando mudanças novas no runtime.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. se `[Init]` e `[Frame]` entram na surface oficial;
2. targets válidos, unicidade e assinaturas exigidas;
3. ordem entre module init, init de programa e frame;
4. política de boot one-shot;
5. política de falha;
6. papel semântico de `frame()` do usuário;
7. política de loops e host calls admissíveis durante init.
## Core Questions
1. PBS deve introduzir `[Init]` e `[Frame]` como atributos reservados?
2. `[Init]` deve ser permitido tanto por módulo quanto no init final de projeto?
3. `[Frame]` deve existir exatamente uma vez por programa?
4. `[Init]` deve existir no máximo uma vez por programa?
5. `[Init]` e `[Frame]` devem exigir assinatura fixa `fn ...() -> void`?
6. Todos os module inits devem rodar antes do `[Init]` do usuário?
7. A ausência de `[Init]` deve ser válida?
8. A ausência de `[Frame]` deve ser erro hard para programa executável?
9. Em caso de falha durante module init ou `[Init]`, o programa deve falhar definitivamente ou pode haver retry?
10. Qual é a definição semântica correta de `frame()` do usuário: root lógico do tick, callable publicado, ou apenas callable invocado por infraestrutura sintética futura?
11. Loops devem ser permitidos em init de módulo e init de projeto?
12. Host calls devem ser proibidas por padrão durante init?
13. Apenas host calls marcadas com `[InitAllowed]` devem ser admissíveis durante init?
14. Como a admissibilidade de `[InitAllowed]` é validada em compile time?
## Options
### Option A
Introduzir `[Init]` e `[Frame]` como markers reservados explícitos, com assinaturas fixas, `[Init]` opcional por módulo e por projeto, `[Frame]` obrigatório e único por projeto executável, permitindo init procedural por módulo e mantendo política restritiva de host interaction durante init.
### Option B
Introduzir apenas `[Frame]` e manter init inteiramente sintético, sem hook de programa do usuário nesta fase.
### Option C
Manter lifecycle implícito, sem markers source-level, e continuar derivando entrypoint/configuração fora da linguagem.
## Tradeoffs
### Option A
- Prós:
- deixa lifecycle explícito na linguagem;
- reduz dependência de configuração paralela;
- dá base clara para wrapper, manifest e lowering posteriores.
- Contras:
- amplia a surface de atributos reservados;
- exige validação de target, unicidade e assinatura.
### Option B
- Prós:
- escopo menor;
- adia parte da discussão de init.
- Contras:
- resolve só metade do lifecycle;
- pode forçar redesign quando o hook de init do usuário voltar.
### Option C
- Prós:
- menor custo imediato.
- Contras:
- mantém a autoridade do lifecycle fora da linguagem;
- enfraquece a coerência da série 19.
## Recommendation
Seguir com a **Option A**.
Direção recomendada:
1. `[Init]` e `[Frame]` devem ser markers reservados explícitos;
2. `declare global` sempre lowera para o init sintético do módulo owner;
3. `[Init]` de módulo deve ser opcional e executar depois de todos os `declare global` do módulo;
4. o init de projeto deve seguir o mesmo modelo, mas associado ao projeto que contém o `[Frame]`;
5. `[Init]` de projeto deve ser opcional;
6. `[Frame]` deve ser obrigatório para programa executável e único por projeto;
7. `[Init]` e `[Frame]` devem exigir assinatura fixa `fn name() -> void`;
8. boot deve ser one-shot;
9. falha de boot deve seguir direção fail-fast, salvo argumento forte em contrário;
10. loops devem ser permitidos em init de módulo e init de projeto;
11. host calls devem ser proibidas por padrão durante init;
12. apenas host calls com atributo reservado `[InitAllowed]` devem ser admissíveis durante init;
13. `[InitAllowed]` é atributo reservado de SDK, não userland.
## Additional Lifecycle Concerns
Esta agenda também precisa fechar a separação entre três camadas de bootstrap:
1. initializer declarativo de `global`;
2. init por módulo;
3. init de projeto.
Direção herdada da `19.1`:
- `global` materializa storage inicial;
- init de módulo executa lógica procedural de conveniência sobre globals do próprio módulo;
- init de projeto executa coordenação final do programa.
Com esse modelo:
- loops são legítimos em init de módulo e init de projeto, especialmente para carga e preparação de arrays e estruturas derivadas;
- a restrição normativa mais forte deve cair sobre host interaction;
- "bootstrap leve" continua sendo recomendação de design, não um limite impossível de verificar semanticamente.
## Current Direction
Os pontos abaixo já podem ser tratados como direção fechada desta agenda, salvo objeção nova forte:
1. `[Init]` e `[Frame]` entram como atributos reservados explícitos;
2. `declare global X: T = expr;` lowera sempre para o init sintético do módulo owner;
3. cada arquivo pode declarar opcionalmente um `[Init]` procedural próprio;
4. o `[Init]` de cada arquivo roda sempre depois das globals daquele arquivo e participa do init sintético do módulo;
5. existe também um init de projeto opcional, que é o `[Init]` associado ao projeto que contém o `[Frame]`;
6. `[Frame]` é obrigatório e único por projeto executável;
7. `[Init]` e `[Frame]` seguem a forma recomendada:
```pbs
[Init]
fn init() -> void { ... }
```
```pbs
[Frame]
fn frame() -> void { ... }
```
8. a assinatura exigida é sempre `fn name() -> void`;
9. loops são permitidos nos inits;
10. host calls são proibidas por padrão durante init;
11. apenas host calls com `[InitAllowed]` podem ser usadas durante init;
12. `[InitAllowed]` pertence às superfícies reservadas de SDK, não a userland;
13. falha de boot é fail-fast com mensagem coerente.
Também fica fechada a seguinte regra operacional:
14. o `[Init]` que estiver no mesmo arquivo que o `[Frame]` é o init de projeto;
15. `[Init]` em outros arquivos continuam sendo init de módulo;
16. `[Frame]` e o init de projeto devem ser co-localizados no mesmo arquivo por decisão deliberada de source organization.
17. cada arquivo pode declarar no máximo um `[Init]`;
18. cada módulo possui exatamente um init sintético de módulo, dentro do qual os inits por arquivo são ordenados e compostos;
19. a distinção entre init de módulo e init de projeto é apenas de ordenação e papel semântico, não de surface diferente;
20. como consequência, existe no máximo um `[Init]` de projeto por projeto executável.
21. arquivos sem globals podem declarar um `[Init]`, desde que continuem respeitando a regra de no máximo um `[Init]` por arquivo;
22. o `project init` segue exatamente a mesma assinatura `fn init() -> void`.
## Lifecycle Order Direction
Esta agenda também já aponta para a seguinte ordem lógica de bootstrap:
1. materialização das globals de cada arquivo do módulo conforme a ordem determinada pelo dependency graph;
2. execução do `[Init]` daquele arquivo, quando existir;
3. composição desses passos em um único init sintético do módulo;
4. depois do fechamento dos módulos necessários, execução do `[Init]` de projeto, quando existir;
5. entrada no primeiro `frame()`.
Lendo essa ordem de forma operacional:
1. cada arquivo inicializa suas próprias globals;
2. cada arquivo pode então executar seu `[Init]` opcional;
3. tudo isso se aninha dentro de um único init sintético por módulo;
4. o `[Init]` co-localizado com `[Frame]` é elevado a init de projeto por ordem e papel semântico.
Também fica fechada a seguinte direção adicional:
5. a ordem entre arquivos do mesmo módulo deve seguir a ordem derivada do `DependencyGraphAnaliser` das globals;
6. quando dois arquivos não tiverem dependência entre si, o compiler deve usar uma ordem determinística estável.
## Remaining Open Points
Com a direção já consolidada nesta agenda, não restam pontos substantivos em aberto dentro do boundary de `19.2`.
Os temas seguintes agora pertencem à `19.3`.
## InitAllowed Direction
Esta agenda fecha também o seguinte shape normativo inicial para `[InitAllowed]`:
1. `[InitAllowed]` é um atributo reservado sem argumentos em v1;
2. ele é válido apenas em host methods de SDK;
3. ele é inválido em userland;
4. ele é inválido em `fn`, `global`, `service` e demais superfícies que não sejam host methods de SDK;
5. seu papel é exclusivamente validar, em compile time, quais host calls podem aparecer durante init;
6. ele não faz distinção entre init de módulo e init de projeto;
7. a diferença entre esses inits é de ordenação e papel semântico, não de permissão separada de host call.
Exemplo recomendado:
```pbs
declare host Runtime {
[Host(module = "runtime", name = "random_u32", version = 1)]
[Capability(name = "runtime")]
[InitAllowed]
fn random_u32() -> int;
}
```
## Diagnostics Recommendation
Esta agenda fecha o seguinte catálogo mínimo de diagnósticos de lifecycle e bootstrap.
### 1. Project Init Must Be Colocated With Frame
Mensagem semântica recomendada:
- `project init must be colocated with frame`
### 2. Multiple Project Init Functions Detected
Mensagem semântica recomendada:
- `multiple project init functions detected`
### 3. Multiple Frame Functions Detected
Mensagem semântica recomendada:
- `multiple frame functions detected`
### 4. Missing Frame Function For Executable Project
Mensagem semântica recomendada:
- `missing frame function for executable project`
### 5. Invalid Init Signature
Mensagem semântica recomendada:
- `init function must have signature fn name() -> void`
### 6. Invalid Frame Signature
Mensagem semântica recomendada:
- `frame function must have signature fn name() -> void`
### 7. Host Call Not Allowed During Init
Mensagem semântica recomendada:
- `host call not allowed during init`
### 8. Invalid InitAllowed Target
Mensagem semântica recomendada:
- `InitAllowed is valid only on SDK host methods`
### 9. Boot Failed During Module Init
Mensagem semântica recomendada:
- `boot failed during module init`
### 10. Boot Failed During Project Init
Mensagem semântica recomendada:
- `boot failed during project init`
### 11. Multiple Module Init Functions Detected
Mensagem semântica recomendada:
- `multiple module init functions detected`
### 12. Init Attribute Target Invalid
Mensagem semântica recomendada:
- `Init attribute target invalid`
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `3. Core Syntax Specification.md`
- `4. Static Semantics Specification.md`
- `9. Dynamic Semantics Specification.md`
- `12. Diagnostics Specification.md`
## Non-Goals
Esta agenda não deve:
1. decidir o nome físico do wrapper published entrypoint;
2. decidir o nome físico do entrypoint no manifest;
3. definir shape de IR;
4. definir como `FRAME_RET` será reposicionado materialmente.
## Next Step
Depois de fechar esta agenda, abrir ou aprofundar:
- `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda`

View File

@ -0,0 +1,203 @@
# Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
Expected fixed inputs from previous stages:
- `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
- `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`
## Purpose
Define how PBS lifecycle is published into the executable artifact once globals and lifecycle semantics are already fixed.
This agenda must close:
- whether the published entrypoint becomes a synthetic wrapper;
- the semantic and publication relationship between user `frame()` and the published entrypoint;
- the new owner of `FRAME_RET`;
- and the contract boundary between source-derived entrypoint discovery, `FrontendSpec`, and cartridge manifest publication.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS porque decide a fronteira entre semântica da linguagem, entrypoint publicado e artefato executável.
## Context
Se `frame()` do usuário deixar de ser o callable publicado diretamente, o compiler precisará publicar outro root executável.
Isso abre quatro perguntas acopladas:
- quem é o entrypoint físico do artefato;
- qual callable continua sendo o root lógico do código do usuário;
- onde `FRAME_RET` passa a ser emitido;
- e como manifest e `FrontendSpec` representam isso sem ambiguidade.
Esta agenda existe para fechar esse contrato antes de discutir detalhe de representação de IR.
## Inputs Already Fixed Elsewhere
Os seguintes inputs devem ser tratados como fixos aqui:
- o modelo de globals vindo de `19.1`;
- o modelo de lifecycle e markers vindo de `19.2`;
- `FRAME_RET` continua significando fim do frame lógico;
- o runtime continua esperando um entrypoint published por frame.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. se o published entrypoint é um wrapper sintético;
2. qual é a relação entre `frame()` do usuário e esse wrapper;
3. onde `FRAME_RET` passa a viver;
4. como a autoridade de entrypoint sai de `FrontendSpec` e passa ao compiler PBS;
5. como o protocolo de runtime/manifest representa esse entrypoint.
## Core Questions
1. O compiler deve publicar um wrapper sintético como entrypoint real?
2. `frame()` do usuário deixa de ser o callable publicado e passa a ser apenas root lógico interno?
3. `FRAME_RET` deve ser emitido no wrapper, não mais no callable do usuário?
4. O compiler PBS deve se tornar a única autoridade para definir o entrypoint publicado?
5. O wrapper sintético deve ocupar protocolarmente o entrypoint físico `0`?
6. O campo `entrypoint` deve deixar de existir no `manifest.json` como estado alvo do protocolo?
7. Existe algum caso em que o wrapper não seja necessário depois de `19.2`?
## Options
### Option A
Publicar um wrapper sintético como entrypoint físico, tratar `frame()` do usuário como root lógico interno, mover `FRAME_RET` para o wrapper, tornar o compiler PBS a autoridade exclusiva de entrypoint e fixar protocolarmente o entrypoint físico em `0`.
### Option B
Manter `frame()` do usuário como entrypoint publicado e tentar acoplar boot/lifecycle diretamente nele.
### Option C
Publicar wrapper sintético, mas manter `entrypoint` no manifest como autoridade nominal paralela.
## Tradeoffs
### Option A
- Prós:
- separa lifecycle publicado do callable do usuário;
- acomoda boot one-shot de forma limpa;
- preserva o significado de `FRAME_RET` com owner mais correto.
- alinha melhor o protocolo físico do artefato com a política já existente de `func_id = 0` como entrypoint;
- remove autoridade duplicada entre compiler, manifest e `FrontendSpec`.
- Contras:
- exige trabalho claro de migração em manifest, runtime e `FrontendSpec`;
- torna a relação lógico/físico explicitamente dupla.
### Option B
- Prós:
- preserva a superfície atual de publicação.
- Contras:
- mistura responsabilidades demais no callable do usuário;
- enfraquece a clareza do lifecycle.
### Option C
- Prós:
- parece reduzir custo de transição imediata.
- Contras:
- mantém duas autoridades para entrypoint;
- cria dívida documental e de runtime desnecessária;
- enfraquece a ideia de entrypoint como protocolo fixo do artefato.
## Recommendation
Seguir com a **Option A**.
## Current Direction
Os pontos abaixo já podem ser tratados como direção fechada desta agenda, salvo objeção nova forte:
1. o compiler deve publicar um wrapper sintético como entrypoint físico real;
2. esse wrapper deve conter:
- boot one-shot,
- os inits necessários apenas uma vez,
- a chamada ao `frame()` do usuário,
- e o `FRAME_RET` final;
3. `frame()` anotado com `[Frame]` permanece como root lógico do usuário;
4. o wrapper sintético é o root físico publicado;
5. `FRAME_RET` sai do final do `frame()` do usuário e passa a existir no wrapper;
6. `FrontendSpec` perde autoridade para referenciar quem é o entrypoint;
7. a autoridade de entrypoint fica outorgada exclusivamente ao compiler PBS;
8. o wrapper sintético deve ser compilado no entrypoint físico `0`;
9. o estado alvo do protocolo deve remover `entrypoint` do `manifest.json`;
10. o runtime/loader deve tratar o entrypoint como protocolo fixo do artefato, não como metadado nominal configurável no manifest.
## Manifest and Runtime Direction
Esta agenda também já aponta para a seguinte direção de contrato:
1. o campo `entrypoint` deve deixar de existir no `manifest.json` como estado final do protocolo;
2. o runtime deve assumir protocolarmente o entrypoint físico `0`;
3. o compiler garante que esse índice `0` pertence ao wrapper sintético publicado;
4. export nominal do callable deixa de ser a autoridade de boot;
5. qualquer compatibilidade temporária com manifest nominal deve ser tratada apenas como transição, não como contrato final desejado.
## Exports Boundary
Esta agenda também fecha a seguinte separação:
1. exports nominais de funções podem continuar existindo no artefato;
2. esses exports deixam de ser usados como autoridade de loader/boot;
3. boot passa a depender exclusivamente do entrypoint físico `0` publicado pelo compiler;
4. exports nominais permanecem apenas como superfície útil para tooling, debug, introspection e casos correlatos.
## Remaining Open Points
Com a direção acima, os pontos que ainda pedem fechamento real nesta agenda ficam reduzidos a:
1. a forma de propagação normativa dessa mudança para specs gerais de bytecode, lowering e cartridge contract.
## Runtime Propagation
Esta agenda também deve referenciar explicitamente a discussão correspondente no domínio runtime:
- `../runtime/docs/runtime/agendas/025-cartridge-manifest-entrypoint-removal-and-runtime-protocol.md`
Direção fechada desta agenda:
1. não há linha de compatibilidade desejada como estado-alvo;
2. a remoção de `entrypoint` do runtime deve ser executada em prioridade alinhada ao compiler;
3. a agenda de runtime é o owner da discussão operacional de loader/VM/manifest no outro domínio.
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `9. Dynamic Semantics Specification.md`
- `13. Lowering IRBackend Specification.md`
- `7. Cartridge Manifest and Runtime Capabilities Specification.md`
- `12. Diagnostics Specification.md`
## Non-Goals
Esta agenda não deve:
1. escolher o encoding exato do wrapper no IR;
2. definir estrutura detalhada de module init lowering;
3. escrever fixtures completos de conformance.
## Next Step
Depois de fechar esta agenda, abrir ou aprofundar:
- `19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda`

View File

@ -0,0 +1,147 @@
# Globals and Lifecycle Lowering to IRBackend/IRVM Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
Expected fixed inputs from previous stages:
- `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
- `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`
- `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda`
## Purpose
Define how the already-decided globals and lifecycle model are represented and lowered through the executable compiler pipeline.
This agenda must close:
- the representation boundary for globals;
- module init synthesis;
- boot guard materialization;
- wrapper lowering;
- and the executable path that preserves the chosen `FRAME_RET` ownership.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS, mas encosta diretamente nas specs gerais de lowering e nas interfaces entre frontend e backend.
## Context
Depois que surface, lifecycle e publication contract estiverem fechados, ainda restará decidir:
- em que boundary globals passam a existir como entidade explícita;
- se `IRBackend` carrega globals diretamente ou só uma forma anterior;
- como module init sintético é representado;
- como o wrapper published entrypoint é emitido;
- como o guard one-shot de boot é materializado.
Esta agenda é mecânica/arquitetural. Ela não deve reabrir decisões de linguagem.
## Inputs Already Fixed Elsewhere
Os seguintes inputs devem ser tratados como fixos aqui:
- surface e identidade de globals vindas de `19.1`;
- lifecycle semântico vindo de `19.2`;
- owner do published entrypoint e de `FRAME_RET` vindo de `19.3`;
- `GET_GLOBAL` e `SET_GLOBAL` já existem na VM.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. se globals entram explicitamente no `IRBackend` ou apenas em lowering posterior;
2. como module init é sintetizado;
3. como o guard one-shot de boot é materializado;
4. como o wrapper é lowered;
5. como `FRAME_RET` é preservado no ponto correto;
6. quais contracts gerais de lowering precisam propagação.
## Core Questions
1. `IRBackend` deve ganhar representação explícita para globals?
2. Se não, qual é o boundary intermediário responsável por materializá-los antes de `IRVM`?
3. Module init sintético é emitido como callable próprio por módulo?
4. O boot guard é global oculto, local estático de wrapper, ou outra forma interna?
5. O wrapper published entrypoint é um callable sintético explícito no graph do frontend?
6. Como manter attribution e diagnostics úteis para código sintético?
7. Que mudanças mínimas precisam acontecer em specs gerais fora de `docs/compiler/pbs`?
## Options
### Option A
Dar representação explícita a globals e callables sintéticos já no boundary de `IRBackend`, preservando attribution e identidade suficientes para lowering posterior.
### Option B
Manter `IRBackend` sem globals explícitos e adiar essa materialização para um lowering mais baixo.
### Option C
Tratar globals como açúcar compilado diretamente para slots/ops sem entidade intermediária estável.
## Tradeoffs
### Option A
- Prós:
- boundary mais explícito e testável;
- menor inferência textual em lowers posteriores;
- facilita explicar wrapper e init synthesis.
- Contras:
- aumenta o contrato do `IRBackend`;
- pode exigir propagação além do domínio PBS.
### Option B
- Prós:
- preserva o boundary atual por mais tempo.
- Contras:
- empurra complexidade para fases posteriores;
- dificulta conformance do shape intermediário.
### Option C
- Prós:
- menor aparente custo de modelagem.
- Contras:
- fraco para teste, attribution e manutenção;
- mistura lowering demais cedo.
## Recommendation
Seguir com a **Option A**, salvo impedimento forte de boundary cross-domain.
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `13. Lowering IRBackend Specification.md`
- specs gerais de lowering/backend fora do domínio PBS, se necessário
- `12. Diagnostics Specification.md`
## Non-Goals
Esta agenda não deve:
1. redefinir surface syntax;
2. redefinir lifecycle;
3. redefinir manifest publication;
4. fechar catálogo final de fixtures.
## Next Step
Depois de fechar esta agenda, abrir ou aprofundar:
- `19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda`

View File

@ -0,0 +1,133 @@
# Diagnostics, Manifest Propagation, and Conformance Coverage Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
Expected fixed inputs from previous stages:
- `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda`
- `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`
- `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda`
- `19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda`
## Purpose
Consolidate the propagation layer of topic `19` after the architectural decisions are already fixed.
This agenda must close:
- the diagnostics surface;
- manifest-facing propagation details;
- fixture and conformance coverage;
- and the acceptance gates for declaring topic `19` ready to become a decision plus implementation plan.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS, mas toca tanto specs normativas quanto critérios de teste e artefato publicado.
## Context
Depois que surface, lifecycle, published entrypoint e lowering estiverem fechados, ainda faltará consolidar:
- quais erros novos são obrigatórios;
- em que fase cada erro aparece;
- como o manifest final reflete a arquitetura escolhida;
- e que coverage mínima de fixtures e gates garante que a série 19 ficou estável.
Esta agenda é de consolidação. Ela não deve rediscutir a arquitetura central.
## Inputs Already Fixed Elsewhere
Os seguintes inputs devem ser tratados como fixos aqui:
- shape de globals vindo de `19.1`;
- lifecycle vindo de `19.2`;
- publication contract vindo de `19.3`;
- lowering mechanics vindo de `19.4`.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. catálogo mínimo de diagnósticos;
2. fase responsável por cada diagnóstico;
3. propagação final para manifest/specs;
4. fixture matrix mínima;
5. critérios de aceite para converter a série 19 em `decision` e depois `pull-request/plan`.
## Core Questions
1. Quais diagnósticos são obrigatórios para `declare global`, imports/reexports e ciclos?
2. Quais diagnósticos são obrigatórios para `[Init]` e `[Frame]`?
3. Quais erros pertencem a parser/AST, static semantics/linking, lowering, ou toolchain full pipeline?
4. Como o manifest final deve refletir entrypoint lógico e/ou físico escolhido em `19.3`?
5. Que fixtures mínimas precisam existir para globals, module init, boot failure, wrapper e `FRAME_RET`?
6. Em que ponto a configuração explícita antiga de entrypoint em `FrontendSpec` pode ser considerada removida?
7. Quais gates de conformance precisam ser atualizados para cobrir a série 19?
## Options
### Option A
Consolidar diagnostics e coverage somente depois que os quatro stages anteriores estiverem fechados, com fixture matrix explícita por fase.
### Option B
Começar fixtures e diagnostics cedo, mesmo com arquitetura ainda mudando.
## Tradeoffs
### Option A
- Prós:
- reduz retrabalho;
- evita congelar erro e coverage cedo demais;
- combina melhor com a disciplina de umbrella agenda.
- Contras:
- posterga parte da validação final.
### Option B
- Prós:
- feedback mais cedo.
- Contras:
- alto risco de churn e reabertura de artefatos;
- conflita com os locking rules da umbrella 19.
## Recommendation
Seguir com a **Option A**.
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `7. Cartridge Manifest and Runtime Capabilities Specification.md`
- `12. Diagnostics Specification.md`
- `13. Lowering IRBackend Specification.md`
- docs de conformance/fixtures relevantes
## Non-Goals
Esta agenda não deve:
1. redefinir semântica de globals;
2. redefinir lifecycle;
3. redefinir wrapper ou ownership de `FRAME_RET`;
4. redefinir shape de IR.
## Next Step
Depois do fechamento desta agenda, a linha `19` fica pronta para:
- virar uma `decision` consolidada em `docs/compiler/pbs/decisions`;
- e então ser decomposta em `pull-request/plan` de execução.

View File

@ -13,6 +13,11 @@ Closed agendas are moved to `docs/pbs/agendas/archive`.
5. `18.4. Asset References in Game Code - Names vs Compile-Time Lowering Agenda.md`
6. `18.5. Ignored Call Results in Executable Lowering Agenda.md`
7. `19. Globals, Synthetic Module Init, and FRAME_RET Agenda.md`
8. `19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda.md`
9. `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.md`
10. `19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md`
11. `19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md`
12. `19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda.md`
## Purpose

View File

@ -0,0 +1,154 @@
# Globals Surface, Identity, and Module Boundaries Decision
Status: Accepted
Date: 2026-03-22
Related Agenda: `docs/compiler/pbs/agendas/19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda.md`
## Context
PBS v1 needed a precise language and linking policy for runtime globals before lifecycle, published entrypoint, and lowering work under topic `19` could proceed safely.
The open problem was not merely syntax.
It also required closing:
- how globals differ from `declare const`,
- how globals participate in value visibility and import flow,
- how module ownership of storage is preserved,
- how dependency order between globals is modeled,
- and which diagnostics are mandatory when that model is violated.
Important fixed inputs already existed:
- `declare const` is compile-time only and does not denote mutable module storage,
- top-level executable statements remain forbidden,
- the VM already exposes global-slot access,
- and `mod.barrel` remains the visibility authority for module exports.
## Decision
PBS adopts the following policy for runtime globals in v1:
1. PBS introduces a distinct top-level declaration form:
- `declare global Name: T = expr;`
2. `declare global` is not a variant of `declare const`.
3. `declare const` remains reserved to immutable values and must not be reused as runtime module storage.
4. `declare global` requires:
- an explicit type annotation,
- and an explicit initializer in all ordinary v1 cases.
5. Globals participate in the value namespace, but remain a distinct declaration category.
6. `mod.barrel` must expose globals through explicit `global` entries:
- `mod global Name;`
- `pub global Name;`
7. PBS does not introduce global re-export in this line of work.
8. Import of a global preserves the storage identity of the original owner module.
9. Imports may use aliasing when needed, but aliasing changes only the local visible name, never the canonical storage owner.
10. Shadowing between visible `fn`, `service`, `global`, and `const` names is a compile-time error and must be resolved with aliasing.
11. Lookup precedence remains:
- locals,
- then struct/class members,
- then globals,
including globals introduced by import.
12. Global dependency order is defined by a deterministic dependency graph:
- every read of another global in a global initializer creates a dependency edge,
- imports and aliases preserve canonical owner identity in that graph,
- source-file order must not affect dependency resolution.
13. Cycles between globals are compile-time errors.
14. Modules and globals must share the same structural dependency-analysis kernel through a refactor of the existing module dependency algorithm into:
- `DependencyGraphAnaliser`
- located in infra `util.structures`.
15. What is shared is the structural graph analysis kernel:
- topological ordering,
- cycle detection,
- deterministic traversal support.
16. What is not shared is domain semantics:
- each domain remains responsible for constructing canonical nodes, edges, and diagnostics.
## Global Initializer Policy
PBS v1 adopts the following initializer policy for `declare global`:
1. The initializer exists only to materialize the initial module storage value.
2. Admissible forms in v1 include:
- primitive literals and simple value operations,
- value-bearing member access at this stage,
- `new Struct(...)`,
- and reads of other globals compatible with the dependency graph.
3. Top-level `fn` calls are not permitted in a global initializer in v1.
4. `some(...)` and `none` are not permitted in a global initializer in v1.
5. `if` and `switch` are not permitted directly in the declaration initializer in v1.
6. Richer procedural setup belongs to later lifecycle stages rather than the declaration initializer itself.
## Rationale
This decision intentionally keeps global declarations narrow and explicit.
That choice:
- prevents semantic collapse between immutable constants and mutable runtime storage,
- keeps import and visibility rules legible,
- makes dependency analysis deterministic and explainable,
- avoids hiding lifecycle logic inside declaration expressions,
- and prepares a clean handoff to later topic `19` work on module init, project init, and published entrypoint behavior.
The decision also rejects implicit complexity:
- no silent reuse of `const`,
- no global re-export,
- no ambiguous cross-category name merging,
- and no procedural `fn`-driven initialization inside `declare global`.
## Mandatory Diagnostics
PBS must provide, at minimum, the following diagnostics for this policy:
1. `global initializer uses unsupported form`
- emitted at the invalid subexpression inside a global initializer.
2. `global dependency cycle detected`
- emitted on the local participating global,
- and should include the canonical cycle path when available.
3. `imported symbol shadows existing visible symbol; alias required`
- emitted when an imported `fn`, `service`, `global`, or `const` collides with an already-visible symbol of those categories.
4. `global import must resolve through a global barrel entry`
- emitted when import resolution would otherwise degrade a global into another category.
## Invariants
1. `declare const` and `declare global` remain semantically distinct.
2. Runtime storage ownership remains attached to the canonical owner module.
3. Global dependency order is semantic, not textual.
4. Alias spelling must not change canonical global identity.
5. The dependency-analysis kernel may be shared structurally, but semantic graph construction remains domain-owned.
## Explicit Non-Decisions
1. This decision does not define module init or project init behavior.
2. This decision does not define `[INIT]`, `[FRAME]`, or lifecycle markers.
3. This decision does not define published entrypoint or `FRAME_RET` ownership.
4. This decision does not define the final IR representation of globals.
5. This decision does not define host-call admissibility during lifecycle hooks.
## Spec Impact
This decision should feed at least:
1. `docs/compiler/pbs/specs/3. Core Syntax Specification.md`
2. `docs/compiler/pbs/specs/4. Static Semantics Specification.md`
3. `docs/compiler/pbs/specs/11. AST Specification.md`
4. `docs/compiler/pbs/specs/12. Diagnostics Specification.md`
It also constrains future topic `19` work in:
1. `docs/compiler/pbs/agendas/19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.md`
2. `docs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md`
3. `docs/compiler/pbs/agendas/19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md`
## Validation Notes
At minimum, validation for this decision should include:
1. accepted fixtures for `declare global` with primitive, member-value, `new`, and dependent-global initializers;
2. rejection fixtures for forbidden initializer forms such as top-level `fn`, `some(...)`, `if`, and `switch`;
3. import fixtures proving alias-based disambiguation;
4. negative fixtures for cross-category collisions;
5. dependency fixtures proving deterministic ordering independent of source-file order;
6. cycle fixtures proving deterministic detection and diagnostics for intra-module and inter-module cycles.

View File

@ -0,0 +1,194 @@
# Lifecycle Markers, Program Init, and Frame Root Semantics Decision
Status: Accepted
Date: 2026-03-22
Related Agenda: `docs/compiler/pbs/agendas/19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.md`
## Context
After closing the globals surface and module-boundary model, PBS v1 needed a precise lifecycle policy for:
- module bootstrap,
- project bootstrap,
- frame-root identification,
- host-call admissibility during init,
- and failure behavior at boot.
The key requirement was to keep initialization expressive enough for real setup work, while preserving deterministic semantics and preventing the declaration initializer of `global` from becoming a general procedural execution surface.
Important fixed inputs already existed:
- `declare global` lowers into module-owned runtime storage,
- global initializers remain declarative and restricted,
- top-level `fn` calls are not allowed inside `declare global` initializers,
- `FRAME_SYNC` remains the only normative safepoint,
- and PBS v1 remains deterministic and single-threaded.
## Decision
PBS adopts the following lifecycle-marker and bootstrap policy in v1:
1. PBS introduces the reserved userland attributes:
- `[Init]`
- `[Frame]`
2. `[Frame]` is mandatory for an executable project.
3. `[Frame]` is unique per executable project.
4. `[Init]` is optional.
5. `[Init]` may appear in ordinary userland source both for module-local bootstrap and for project bootstrap.
6. Both `[Init]` and `[Frame]` require the same signature shape:
- `fn name() -> void`
7. Recommended canonical source forms are:
```pbs
[Init]
fn init() -> void { ... }
```
```pbs
[Frame]
fn frame() -> void { ... }
```
## Module Init and Project Init Policy
PBS adopts the following bootstrap layering:
1. Every module has exactly one synthetic module init.
2. `declare global` always lowers into that synthetic module init.
3. Each file may declare at most one `[Init]`.
4. A file may declare `[Init]` even when it declares no globals.
5. For one file:
- that file's globals are materialized first,
- then that file's `[Init]`, if present, executes.
6. File-level init fragments are then composed into the one synthetic module init.
7. The `[Init]` colocated in the same file as `[Frame]` is the project init.
8. `[Init]` in other files remains module init.
9. The distinction between module init and project init is one of ordering and semantic role, not one of different source syntax.
10. There is therefore at most one project init per executable project.
11. Project init uses the same signature shape:
- `fn init() -> void`
## Bootstrap Order
PBS adopts the following logical bootstrap order:
1. materialize globals for each file according to dependency order,
2. execute that file's `[Init]`, if present,
3. compose those steps into one synthetic module init,
4. after required module work is complete, execute project init, if present,
5. then enter the first `frame()`.
Within one module:
1. file ordering must follow the order derived from the globals `DependencyGraphAnaliser`,
2. and when two files have no dependency edge between them, the compiler must still use a deterministic stable order.
## Host Interaction During Init
PBS adopts the following init-time host-call policy:
1. Loops are permitted in module init and project init.
2. Host calls are forbidden by default during init.
3. The only host calls admissible during init are host methods marked with `[InitAllowed]`.
4. `[InitAllowed]` is a reserved SDK attribute, not a userland attribute.
5. `[InitAllowed]` has no arguments in v1.
6. `[InitAllowed]` is valid only on SDK host methods.
7. `[InitAllowed]` is invalid on userland `fn`, `global`, `service`, or any non-host-method surface.
8. `[InitAllowed]` does not distinguish module init from project init.
9. The difference between those phases is ordering and semantic role, not a separate host-permission class.
10. Admissibility of `[InitAllowed]` must be checked at compile time.
Illustrative shape:
```pbs
declare host Runtime {
[Host(module = "runtime", name = "random_u32", version = 1)]
[Capability(name = "runtime")]
[InitAllowed]
fn random_u32() -> int;
}
```
## Failure Policy
PBS adopts a fail-fast boot policy:
1. Failure during module init aborts boot.
2. Failure during project init aborts boot.
3. No automatic retry occurs on the next frame.
4. Implementations must surface a coherent failure message identifying the failing boot phase.
## Rationale
This decision keeps the declaration initializer of `global` simple, while still allowing practical bootstrap work in later lifecycle stages.
That balance matters because:
- arrays and derived data often require loops during setup,
- module bootstrap must remain expressive enough for real projects,
- host interaction during boot must still be tightly controlled,
- and the runtime model must remain deterministic and explainable.
Colocation of project init with frame was chosen deliberately as a source-organization rule.
It avoids introducing a second project-level marker while keeping project bootstrap discoverable in source.
## Mandatory Diagnostics
PBS must provide, at minimum, the following diagnostics for this policy:
1. `project init must be colocated with frame`
2. `multiple project init functions detected`
3. `multiple frame functions detected`
4. `missing frame function for executable project`
5. `init function must have signature fn name() -> void`
6. `frame function must have signature fn name() -> void`
7. `host call not allowed during init`
8. `InitAllowed is valid only on SDK host methods`
9. `boot failed during module init`
10. `boot failed during project init`
11. `multiple module init functions detected`
12. `Init attribute target invalid`
## Invariants
1. `[Frame]` remains unique per executable project.
2. Project init is identified by colocation with `[Frame]`.
3. Each file contributes at most one init fragment.
4. Each module contributes exactly one synthetic module init.
5. Host-call admissibility during init is explicit and compile-time validated.
6. Boot ordering remains deterministic.
## Explicit Non-Decisions
1. This decision does not define the published synthetic wrapper entrypoint.
2. This decision does not define `FRAME_RET` ownership.
3. This decision does not define the final IR encoding of bootstrap.
4. This decision does not define cartridge manifest publication details.
## Spec Impact
This decision should feed at least:
1. `docs/compiler/pbs/specs/3. Core Syntax Specification.md`
2. `docs/compiler/pbs/specs/4. Static Semantics Specification.md`
3. `docs/compiler/pbs/specs/9. Dynamic Semantics Specification.md`
4. `docs/compiler/pbs/specs/12. Diagnostics Specification.md`
It also constrains future topic `19` work in:
1. `docs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md`
2. `docs/compiler/pbs/agendas/19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md`
3. `docs/compiler/pbs/agendas/19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda.md`
## Validation Notes
At minimum, validation for this decision should include:
1. accepted fixtures for `[Init]` and `[Frame]` with correct signatures;
2. accepted fixtures for file-level `[Init]` without globals;
3. accepted fixtures for loops inside module init and project init;
4. rejection fixtures for invalid marker targets and invalid signatures;
5. rejection fixtures for illegal host calls during init;
6. accepted fixtures for SDK host calls marked `[InitAllowed]`;
7. failure fixtures that distinguish module-init boot failure from project-init boot failure;
8. ordering fixtures proving deterministic file ordering within one module.

View File

@ -0,0 +1,130 @@
# Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Decision
Status: Accepted
Date: 2026-03-22
Related Agenda: `docs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md`
## Context
After closing globals and lifecycle semantics, PBS v1 still needed a precise publication contract for executable boot.
The remaining problem was no longer how initialization behaves in userland.
It was how that behavior is published into the artifact and consumed by backend stages and runtime.
The open points were:
- whether the published entrypoint is still the user `frame()` callable,
- whether a synthetic wrapper becomes the true physical entrypoint,
- where `FRAME_RET` must live once lifecycle bootstrap exists,
- whether `FrontendSpec` continues to hold authority over entrypoint identity,
- and whether `manifest.json` remains part of entrypoint authority.
Important fixed inputs already existed:
- `[Frame]` identifies the userland logical frame root,
- module init and project init are already defined in lifecycle terms,
- boot is one-shot and fail-fast,
- and general backend specs already converge on entrypoint function index `0`.
## Decision
PBS adopts the following publication and ownership policy for executable boot in v1:
1. The compiler publishes a synthetic wrapper as the physical executable entrypoint.
2. The userland callable marked with `[Frame]` remains the logical frame root of user code.
3. The synthetic wrapper is therefore the physical root, while `[Frame]` remains the logical root.
4. The wrapper must contain:
- one-shot boot orchestration,
- the required init execution exactly once,
- invocation of the userland `[Frame]` callable,
- and the final `FRAME_RET`.
5. `FRAME_RET` no longer belongs at the end of the userland `frame()` body.
6. `FRAME_RET` belongs to the published wrapper.
7. The published wrapper must occupy physical entrypoint index `0`.
## Entrypoint Authority
PBS adopts the following authority model:
1. `FrontendSpec` no longer holds authority to decide which callable is the published entrypoint for PBS.
2. Entrypoint authority is owned exclusively by the PBS compiler.
3. Source-derived lifecycle semantics determine the logical frame root.
4. The compiler then publishes the physical wrapper that realizes that lifecycle contract.
5. Backend handoff must therefore preserve the compiler-selected published wrapper as the effective entrypoint identity.
## Manifest and Runtime Protocol
PBS adopts the following target protocol direction:
1. `entrypoint` must stop being part of the final `manifest.json` contract.
2. Runtime boot must not depend on nominal entrypoint resolution from the manifest.
3. Runtime boot must treat physical entrypoint index `0` as protocol.
4. The compiler guarantees that the published wrapper occupies that index.
5. Any temporary compatibility line with manifest-declared nominal entrypoint is transitional only and is not the target contract.
## Exports Boundary
PBS adopts the following boundary for nominal exports:
1. Nominal function exports may continue to exist in the emitted artifact.
2. Those exports are no longer authoritative for loader or boot.
3. Boot depends exclusively on the published physical entrypoint at index `0`.
4. Nominal exports remain useful only for tooling, debug, introspection, and related non-boot concerns.
## Rationale
This decision keeps logical user semantics and physical boot publication clearly separated.
That matters because:
- the user `frame()` callable should remain the semantic root of frame logic,
- the one-shot bootstrap sequence must still execute before normal frame work,
- `FRAME_RET` should mark the true end of the logical published frame,
- and the compiler must be the single authority that turns source lifecycle into executable publication.
The decision also deliberately removes duplicated authority:
- not `FrontendSpec`,
- not manifest nominal metadata,
- and not PBX nominal exports
are allowed to compete with the compiler-selected physical wrapper as boot authority.
## Invariants
1. `[Frame]` remains the userland logical frame root.
2. The published wrapper remains the physical executable root.
3. `FRAME_RET` belongs to the published wrapper.
4. Physical entrypoint index `0` belongs to that wrapper.
5. Nominal exports are not boot authority.
6. Entrypoint authority for PBS belongs to the compiler, not to `FrontendSpec` or manifest metadata.
## Explicit Non-Decisions
1. This decision does not define the final IR encoding of the wrapper body.
2. This decision does not define the detailed lowering mechanics of globals and lifecycle into IR.
3. This decision does not define the runtime-side migration implementation plan in detail.
4. This decision does not define the final diagnostics catalog for backend/runtime structural failures.
## Spec Impact
This decision should feed at least:
1. `docs/compiler/pbs/specs/9. Dynamic Semantics Specification.md`
2. `docs/compiler/pbs/specs/13. Lowering IRBackend Specification.md`
3. `docs/compiler/pbs/specs/7. Cartridge Manifest and Runtime Capabilities Specification.md`
4. `docs/compiler/general/specs/15. Bytecode and PBX Mapping Specification.md`
5. `docs/compiler/general/specs/20. IRBackend to IRVM Lowering Specification.md`
It also requires explicit cross-domain propagation to:
1. `../runtime/docs/runtime/agendas/025-cartridge-manifest-entrypoint-removal-and-runtime-protocol.md`
## Validation Notes
At minimum, validation for this decision should include:
1. emitted artifact evidence that the published wrapper occupies function index `0`;
2. conformance evidence that the userland `[Frame]` callable is invoked by that wrapper rather than being published directly;
3. evidence that `FRAME_RET` is emitted in the wrapper path;
4. evidence that nominal exports, when present, do not participate in boot authority;
5. integration evidence that runtime boot aligns with the fixed physical entrypoint protocol.