@semacode/cli 3.11.1 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -567,18 +567,19 @@ module app.example {
567
567
 
568
568
  ## Canonical \`use\` and \`impl\` Origins
569
569
 
570
- Use these origins before inventing a new one:
570
+ Use these origins before inventing a new one (aliases after \`ou\`):
571
571
 
572
572
  - \`ts\` ou \`typescript\`
573
573
  - \`js\` ou \`javascript\`
574
574
  - \`py\` ou \`python\`
575
575
  - \`dart\`
576
576
  - \`lua\`
577
- - \`cs\` ou \`dotnet\`
577
+ - \`cs\`, \`csharp\` ou \`dotnet\`
578
578
  - \`java\`
579
- - \`go\`
580
- - \`rust\`
581
- - \`cpp\`
579
+ - \`go\` ou \`golang\`
580
+ - \`rust\` ou \`rs\`
581
+ - \`cpp\`, \`cxx\`, \`cc\` ou \`c++\`
582
+ - \`php\`
582
583
 
583
584
  Examples:
584
585
 
@@ -590,11 +591,19 @@ impl {
590
591
  }
591
592
  \`\`\`
592
593
 
594
+ \`\`\`sema
595
+ use php app.legacy.pedidos
596
+
597
+ impl {
598
+ php: pedidos.criarPedido
599
+ }
600
+ \`\`\`
601
+
593
602
  \`sema compilar --alvo javascript\` defines a generation target. \`impl { js: ... }\` defines the live-code origin linked to the contract. They are different layers and both are valid.
594
603
 
595
604
  ## Layered \`impl\` Roles
596
605
 
597
- Layered code (route + service + persistence in the same language) can declare one \`impl\` per role. Append a supported role to the origin: \`rota\`, \`servico\`, \`persistencia\` or \`repositorio\`.
606
+ Layered code (route + service + persistence in the same language) can declare one \`impl\` per role. Append a supported role to any origin: \`rota\`, \`servico\`, \`persistencia\` or \`repositorio\`.
598
607
 
599
608
  \`\`\`sema
600
609
  impl {
@@ -604,6 +613,16 @@ impl {
604
613
  }
605
614
  \`\`\`
606
615
 
616
+ The role suffix combines with every origin, not only TypeScript:
617
+
618
+ \`\`\`sema
619
+ impl {
620
+ php_rota: pedidos.rota.criarPedido
621
+ php_servico: pedidos.servico.criarPedido
622
+ php_persistencia: pedidos.repositorio.criar
623
+ }
624
+ \`\`\`
625
+
607
626
  Each origin-plus-role pair must appear at most once; the bare origin (for example \`ts\`) stays valid for single-layer tasks, and drift resolves every role independently.
608
627
 
609
628
  ## Small Canonical Lists
@@ -733,7 +752,7 @@ Then read every required doc returned by \`docs-impacto\`.
733
752
 
734
753
  ## Contract and Discovery
735
754
 
736
- - \`sema iniciar --template <template> [--force]\`: creates a new Sema project and preserves existing files by default; \`--force\` is the only explicit overwrite path.
755
+ - \`sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|node-firebase-worker|aspnet-api|springboot-api|go-http-api|rust-axum-api|cpp-service-bridge> [--force]\`: creates a new Sema project and preserves existing files by default; \`--force\` is the only explicit overwrite path.
737
756
  - \`sema validar <arquivo-ou-pasta> --json\`: validates \`.sema\` contracts.
738
757
  - \`sema diagnosticos <arquivo.sema> --json\`: details errors and warnings.
739
758
  - \`sema formatar <arquivo-ou-pasta>\`: formats contracts.
@@ -755,7 +774,7 @@ Then read every required doc returned by \`docs-impacto\`.
755
774
 
756
775
  Honest closure: unwrap drift JSON and treat its command payload as the source of truth. \`payload.sucesso:false\`, non-empty \`payload.vinculos_quebrados\`, non-empty \`payload.rotas_divergentes\`, or non-empty \`payload.impls_quebrados\` mean the change is not complete yet. Do not report "clean drift" without a green payload.
757
776
 
758
- Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in \`escopo_aplicado\`. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only \`--escopo projeto\` may walk every configured code root. Logical roots such as \`src\` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` reuse the same directed boundary without reopening arbitrary external paths.
777
+ Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in \`escopo_aplicado\`. A missing declared file or a missing code-referenced dependency is a blocker reported as \`vinculo_quebrado\`; a missing inferred candidate derived from an \`impl\` symbol is informational under \`arquivosAusentesInferidos\` and never blocks an implementation that resolved. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only \`--escopo projeto\` may walk every configured code root. Logical roots such as \`src\` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` reuse the same directed boundary without reopening arbitrary external paths.
759
778
 
760
779
  Drift analysis and cache modes are explicit:
761
780
 
@@ -769,9 +788,9 @@ A cache hit is acceleration, not final evidence; closure still requires fresh dr
769
788
 
770
789
  ## Sema Code
771
790
 
772
- - \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio>\`: generates starter/support artifacts from the contract.
791
+ - \`sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]\`: generates starter/support artifacts from the contract.
773
792
  - \`sema testar <arquivo.sema> --alvo <alvo> --saida <diretorio-temporario>\`: generates and runs local tests when the target supports it.
774
- - \`sema importar <fonte> <diretorio> --saida <diretorio> --json\`: imports a legacy project into initial contracts.
793
+ - \`sema importar <nestjs|express|fastify|koa|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|sveltekit-consumer|nuxt-consumer|firebase|typescript|python|dart|dotnet|java|go|rust|cpp|php> <diretorio> --saida <diretorio> --json\`: imports a legacy project into initial contracts; only these sources have a real importer.
775
794
  - \`sema renomear-semantico <arquivo-ou-pasta> --de <nome> --para <nome> --json\`: helps rename symbols semantically.
776
795
 
777
796
  Rule for \`--saida\`: the folder passed to \`sema compilar --saida\` is generated output. It is not the final delivery by itself. The final delivery is the target files/links declared by the contract. If the contract asks for \`index.html\`, \`css/styles.css\`, and \`js/app.js\`, creating only \`saida/expense_control.ts\` does not complete the task.
@@ -782,7 +801,10 @@ Ready UI rule: if the task generates an app, site, dashboard, form, or static HT
782
801
 
783
802
  ## Canonical Syntax Lists
784
803
 
785
- - Origins for \`use\` and \`impl\`: \`ts/typescript\`, \`js/javascript\`, \`py/python\`, \`dart\`, \`lua\`, \`cs/dotnet\`, \`java\`, \`go\`, \`rust\`, \`cpp\`.
804
+ - Origins for \`use\` and \`impl\` (11, aliases after \`/\`): \`ts/typescript\`, \`js/javascript\`, \`py/python\`, \`dart\`, \`lua\`, \`cs/csharp/dotnet\`, \`java\`, \`go/golang\`, \`rust/rs\`, \`cpp/cxx/cc/c++\`, \`php\`.
805
+ - Layered \`impl\` roles: append \`_rota\`, \`_servico\`, \`_persistencia\` or \`_repositorio\` to any origin (\`ts_rota\`, \`php_servico\`, \`cs_persistencia\`, \`py_repositorio\`).
806
+ - Generation targets (10): \`typescript\`, \`javascript/js\`, \`python\`, \`php\`, \`dart\`, \`lua\`, \`html\`, \`css\`, \`dotnet/cs/csharp\`, \`cpp/c++/cxx/cc\`.
807
+ - \`sema iniciar --template\` (14): \`base\`, \`nestjs\`, \`fastapi\`, \`nextjs-api\`, \`nextjs-consumer\`, \`react-vite-consumer\`, \`angular-consumer\`, \`flutter-consumer\`, \`node-firebase-worker\`, \`aspnet-api\`, \`springboot-api\`, \`go-http-api\`, \`rust-axum-api\`, \`cpp-service-bridge\`.
786
808
  - Frequent \`effects\` categories: \`persistencia\`, \`consulta\`, \`evento\`, \`auditoria\`, \`db.write\`, \`queue.publish\`, \`fs.write\`, \`network.egress\`, \`secret.read\`, \`shell.exec\`.
787
809
  - Accepted \`audit.motivo\` values: \`obrigatorio\`, \`opcional\`, \`dispensado\`.
788
810
 
@@ -23,8 +23,8 @@ const TEMPLATES_INIT = new Set([
23
23
  ]);
24
24
  const FONTES_IMPORTACAO = new Set([
25
25
  "nestjs", "express", "fastify", "koa", "fastapi", "flask", "nextjs", "nextjs-consumer", "react-vite-consumer",
26
- "angular-consumer", "flutter-consumer", "sveltekit-consumer", "nuxt-consumer", "firebase", "typescript", "javascript",
27
- "python", "dart", "lua", "dotnet", "java", "go", "rust", "cpp", "php",
26
+ "angular-consumer", "flutter-consumer", "sveltekit-consumer", "nuxt-consumer", "firebase", "typescript",
27
+ "python", "dart", "dotnet", "java", "go", "rust", "cpp", "php",
28
28
  ]);
29
29
  const ALVOS_GERACAO = new Set([
30
30
  "typescript", "python", "dart", "lua", "javascript", "js", "html", "css", "php",
@@ -25,7 +25,7 @@ export function criarAjudaRaiz(versao) {
25
25
  "",
26
26
  renderizarSecaoAscii("Fluxos rápidos", [
27
27
  "[1] Projeto novo / produção inicial",
28
- "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer> [--force] [--com-exemplos]",
28
+ "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|node-firebase-worker|aspnet-api|springboot-api|go-http-api|rust-axum-api|cpp-service-bridge> [--force] [--com-exemplos]",
29
29
  "Arquivos existentes são preservados; use --force somente para sobrescrita explícita. O starter de exemplos vem por padrão; --com-exemplos instala o pacote completo.",
30
30
  "sema validar contratos/<modulo>.sema --json",
31
31
  "sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio>",
@@ -177,6 +177,7 @@ export interface ConfiguracaoEscopoDriftAplicada {
177
177
  arquivosDeclarados?: string[];
178
178
  arquivosInferidos?: string[];
179
179
  arquivosAusentes?: string[];
180
+ arquivosAusentesInferidos?: string[];
180
181
  bloqueios?: string[];
181
182
  catalogo?: MetricasCatalogoDrift;
182
183
  cache?: EstadoCacheDriftAplicado;
@@ -148,6 +148,7 @@ export async function analisarDriftLegado(contexto, opcoes) {
148
148
  configuracaoEscopo.arquivosDeclarados = planoEscopo.arquivosDeclarados.map(relativoEscopo);
149
149
  configuracaoEscopo.arquivosInferidos = planoEscopo.arquivosInferidos.map(relativoEscopo);
150
150
  configuracaoEscopo.arquivosAusentes = planoEscopo.arquivosAusentes.map(relativoEscopo);
151
+ configuracaoEscopo.arquivosAusentesInferidos = planoEscopo.arquivosAusentesInferidos.map(relativoEscopo);
151
152
  const bloqueiosAnalise = new Set(planoEscopo.bloqueios);
152
153
  configuracaoEscopo.bloqueios = [...bloqueiosAnalise];
153
154
  configuracaoEscopo.catalogo = metricasCatalogo;
@@ -495,6 +495,7 @@ function representarPlanoCache(contexto, configuracao, plano) {
495
495
  arquivosDeclarados: ordenar(plano.arquivosDeclarados),
496
496
  arquivosInferidos: ordenar(plano.arquivosInferidos),
497
497
  arquivosAusentes: ordenar(plano.arquivosAusentes),
498
+ arquivosAusentesInferidos: ordenar(plano.arquivosAusentesInferidos),
498
499
  diretorios: ordenar(plano.diretorios),
499
500
  diretoriosCodigo: ordenar(contexto.diretoriosCodigo),
500
501
  ignorarWorktrees: configuracao.ignorarWorktrees,
@@ -11,6 +11,7 @@ export interface PlanoEscopoDrift {
11
11
  arquivosDeclarados: string[];
12
12
  arquivosInferidos: string[];
13
13
  arquivosAusentes: string[];
14
+ arquivosAusentesInferidos: string[];
14
15
  diretorios: string[];
15
16
  modulos: string[];
16
17
  cobertura: CoberturaEscopoDrift;
@@ -365,6 +365,7 @@ export async function planejarEscopoDrift(contexto, configuracao) {
365
365
  arquivosDeclarados: [],
366
366
  arquivosInferidos: [],
367
367
  arquivosAusentes: [],
368
+ arquivosAusentesInferidos: [],
368
369
  diretorios,
369
370
  modulos: contexto.modulosSelecionados
370
371
  .map((modulo) => modulo.resultado.ir?.nome)
@@ -382,6 +383,7 @@ export async function planejarEscopoDrift(contexto, configuracao) {
382
383
  .filter((ir) => Boolean(ir));
383
384
  const declarados = new Map();
384
385
  const ausentes = new Map();
386
+ const ausentesInferidos = new Map();
385
387
  const inferidos = new Map();
386
388
  const bloqueiosPlano = new Set();
387
389
  const raizReal = await realpath(contexto.baseProjeto);
@@ -440,7 +442,9 @@ export async function planejarEscopoDrift(contexto, configuracao) {
440
442
  }
441
443
  const primeiroCandidato = gruposCandidatos[0]?.[0];
442
444
  if (encontrados.length === 0 && primeiroCandidato) {
443
- ausentes.set(chaveCaminho(primeiroCandidato), primeiroCandidato);
445
+ // Candidato derivado do simbolo do impl, nao uma promessa do contrato:
446
+ // ausencia aqui e informacao de ancoragem, nunca vinculo quebrado.
447
+ ausentesInferidos.set(chaveCaminho(primeiroCandidato), primeiroCandidato);
444
448
  }
445
449
  }
446
450
  const raizesAncoradas = escolherDiretoriosLogicos(contexto, [...declarados.values(), ...inferidos.values()]);
@@ -470,6 +474,7 @@ export async function planejarEscopoDrift(contexto, configuracao) {
470
474
  }
471
475
  const arquivos = [...arquivosMesclados.values()].sort((a, b) => a.localeCompare(b, "pt-BR"));
472
476
  const arquivosAusentes = [...ausentes.values()].sort((a, b) => a.localeCompare(b, "pt-BR"));
477
+ const arquivosAusentesInferidos = [...ausentesInferidos.values()].sort((a, b) => a.localeCompare(b, "pt-BR"));
473
478
  const exigeAncoraCodigo = irs.some(moduloExigeAncoraCodigo);
474
479
  if (arquivos.length === 0
475
480
  && exigeAncoraCodigo
@@ -484,6 +489,7 @@ export async function planejarEscopoDrift(contexto, configuracao) {
484
489
  arquivosDeclarados: [...declarados.values()].sort((a, b) => a.localeCompare(b, "pt-BR")),
485
490
  arquivosInferidos: [...inferidos.values()].sort((a, b) => a.localeCompare(b, "pt-BR")),
486
491
  arquivosAusentes,
492
+ arquivosAusentesInferidos,
487
493
  diretorios: escolherDiretoriosLogicos(contexto, arquivos),
488
494
  modulos: irs.map((ir) => ir.nome),
489
495
  cobertura: arquivosAusentes.length === 0 && bloqueios.length === 0 ? "completa" : "parcial",
package/docs/commands.md CHANGED
@@ -31,7 +31,7 @@ Then read every required doc returned by `docs-impacto`.
31
31
 
32
32
  ## Contract and Discovery
33
33
 
34
- - `sema iniciar --template <template> [--force]`: creates a new Sema project and preserves existing files by default; `--force` is the only explicit overwrite path.
34
+ - `sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|node-firebase-worker|aspnet-api|springboot-api|go-http-api|rust-axum-api|cpp-service-bridge> [--force]`: creates a new Sema project and preserves existing files by default; `--force` is the only explicit overwrite path.
35
35
  - `sema validar <arquivo-ou-pasta> --json`: validates `.sema` contracts.
36
36
  - `sema diagnosticos <arquivo.sema> --json`: details errors and warnings.
37
37
  - `sema formatar <arquivo-ou-pasta>`: formats contracts.
@@ -53,7 +53,7 @@ Then read every required doc returned by `docs-impacto`.
53
53
 
54
54
  Honest closure: unwrap drift JSON and treat its command payload as the source of truth. `payload.sucesso:false`, non-empty `payload.vinculos_quebrados`, non-empty `payload.rotas_divergentes`, or non-empty `payload.impls_quebrados` mean the change is not complete yet. Do not report "clean drift" without a green payload.
55
55
 
56
- Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in `escopo_aplicado`. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only `--escopo projeto` may walk every configured code root. Logical roots such as `src` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and `inspecionar`, `impacto`, and `renomear-semantico` reuse the same directed boundary without reopening arbitrary external paths.
56
+ Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in `escopo_aplicado`. A missing declared file or a missing code-referenced dependency is a blocker reported as `vinculo_quebrado`; a missing inferred candidate derived from an `impl` symbol is informational under `arquivosAusentesInferidos` and never blocks an implementation that resolved. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only `--escopo projeto` may walk every configured code root. Logical roots such as `src` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and `inspecionar`, `impacto`, and `renomear-semantico` reuse the same directed boundary without reopening arbitrary external paths.
57
57
 
58
58
  Drift analysis and cache modes are explicit:
59
59
 
@@ -67,9 +67,9 @@ A cache hit is acceleration, not final evidence; closure still requires fresh dr
67
67
 
68
68
  ## Sema Code
69
69
 
70
- - `sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio>`: generates starter/support artifacts from the contract.
70
+ - `sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]`: generates starter/support artifacts from the contract.
71
71
  - `sema testar <arquivo.sema> --alvo <alvo> --saida <diretorio-temporario>`: generates and runs local tests when the target supports it.
72
- - `sema importar <fonte> <diretorio> --saida <diretorio> --json`: imports a legacy project into initial contracts.
72
+ - `sema importar <nestjs|express|fastify|koa|fastapi|flask|nextjs|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|sveltekit-consumer|nuxt-consumer|firebase|typescript|python|dart|dotnet|java|go|rust|cpp|php> <diretorio> --saida <diretorio> --json`: imports a legacy project into initial contracts; only these sources have a real importer.
73
73
  - `sema renomear-semantico <arquivo-ou-pasta> --de <nome> --para <nome> --json`: helps rename symbols semantically.
74
74
 
75
75
  Rule for `--saida`: the folder passed to `sema compilar --saida` is generated output. It is not the final delivery by itself. The final delivery is the target files/links declared by the contract. If the contract asks for `index.html`, `css/styles.css`, and `js/app.js`, creating only `saida/expense_control.ts` does not complete the task.
@@ -80,7 +80,10 @@ Ready UI rule: if the task generates an app, site, dashboard, form, or static HT
80
80
 
81
81
  ## Canonical Syntax Lists
82
82
 
83
- - Origins for `use` and `impl`: `ts/typescript`, `js/javascript`, `py/python`, `dart`, `lua`, `cs/dotnet`, `java`, `go`, `rust`, `cpp`.
83
+ - Origins for `use` and `impl` (11, aliases after `/`): `ts/typescript`, `js/javascript`, `py/python`, `dart`, `lua`, `cs/csharp/dotnet`, `java`, `go/golang`, `rust/rs`, `cpp/cxx/cc/c++`, `php`.
84
+ - Layered `impl` roles: append `_rota`, `_servico`, `_persistencia` or `_repositorio` to any origin (`ts_rota`, `php_servico`, `cs_persistencia`, `py_repositorio`).
85
+ - Generation targets (10): `typescript`, `javascript/js`, `python`, `php`, `dart`, `lua`, `html`, `css`, `dotnet/cs/csharp`, `cpp/c++/cxx/cc`.
86
+ - `sema iniciar --template` (14): `base`, `nestjs`, `fastapi`, `nextjs-api`, `nextjs-consumer`, `react-vite-consumer`, `angular-consumer`, `flutter-consumer`, `node-firebase-worker`, `aspnet-api`, `springboot-api`, `go-http-api`, `rust-axum-api`, `cpp-service-bridge`.
84
87
  - Frequent `effects` categories: `persistencia`, `consulta`, `evento`, `auditoria`, `db.write`, `queue.publish`, `fs.write`, `network.egress`, `secret.read`, `shell.exec`.
85
88
  - Accepted `audit.motivo` values: `obrigatorio`, `opcional`, `dispensado`.
86
89
 
package/docs/syntax.md CHANGED
@@ -62,18 +62,19 @@ module app.example {
62
62
 
63
63
  ## Canonical `use` and `impl` Origins
64
64
 
65
- Use these origins before inventing a new one:
65
+ Use these origins before inventing a new one (aliases after `ou`):
66
66
 
67
67
  - `ts` ou `typescript`
68
68
  - `js` ou `javascript`
69
69
  - `py` ou `python`
70
70
  - `dart`
71
71
  - `lua`
72
- - `cs` ou `dotnet`
72
+ - `cs`, `csharp` ou `dotnet`
73
73
  - `java`
74
- - `go`
75
- - `rust`
76
- - `cpp`
74
+ - `go` ou `golang`
75
+ - `rust` ou `rs`
76
+ - `cpp`, `cxx`, `cc` ou `c++`
77
+ - `php`
77
78
 
78
79
  Examples:
79
80
 
@@ -85,11 +86,19 @@ impl {
85
86
  }
86
87
  ```
87
88
 
89
+ ```sema
90
+ use php app.legacy.pedidos
91
+
92
+ impl {
93
+ php: pedidos.criarPedido
94
+ }
95
+ ```
96
+
88
97
  `sema compilar --alvo javascript` defines a generation target. `impl { js: ... }` defines the live-code origin linked to the contract. They are different layers and both are valid.
89
98
 
90
99
  ## Layered `impl` Roles
91
100
 
92
- Layered code (route + service + persistence in the same language) can declare one `impl` per role. Append a supported role to the origin: `rota`, `servico`, `persistencia` or `repositorio`.
101
+ Layered code (route + service + persistence in the same language) can declare one `impl` per role. Append a supported role to any origin: `rota`, `servico`, `persistencia` or `repositorio`.
93
102
 
94
103
  ```sema
95
104
  impl {
@@ -99,6 +108,16 @@ impl {
99
108
  }
100
109
  ```
101
110
 
111
+ The role suffix combines with every origin, not only TypeScript:
112
+
113
+ ```sema
114
+ impl {
115
+ php_rota: pedidos.rota.criarPedido
116
+ php_servico: pedidos.servico.criarPedido
117
+ php_persistencia: pedidos.repositorio.criar
118
+ }
119
+ ```
120
+
102
121
  Each origin-plus-role pair must appear at most once; the bare origin (for example `ts`) stays valid for single-layer tasks, and drift resolves every role independently.
103
122
 
104
123
  ## Small Canonical Lists
@@ -0,0 +1,170 @@
1
+ module exemplos.php.crud {
2
+ entity Pedido {
3
+ fields {
4
+ id: Id
5
+ cliente: Texto
6
+ total: Numero
7
+ status: Texto
8
+ }
9
+ }
10
+
11
+ task criar_pedido {
12
+ input {
13
+ cliente: Texto required
14
+ total: Numero required
15
+ }
16
+ output {
17
+ pedido: Pedido
18
+ }
19
+ rules {
20
+ cliente deve_ser preenchido
21
+ total deve_ser positivo
22
+ }
23
+ effects {
24
+ persistencia Pedido
25
+ auditoria pedido_criado
26
+ }
27
+ guarantees {
28
+ pedido existe
29
+ }
30
+ impl {
31
+ php_rota: pedidos.rota.criarPedido
32
+ php_servico: pedidos.servico.criarPedido
33
+ php_persistencia: pedidos.repositorio.criar
34
+ }
35
+ vinculos {
36
+ arquivo: "src/Pedidos/Controller/PedidoController.php"
37
+ arquivo: "src/Pedidos/Service/PedidoService.php"
38
+ arquivo: "src/Pedidos/Repository/PedidoRepository.php"
39
+ simbolo: pedidos.rota.criarPedido
40
+ }
41
+ tests {
42
+ caso "cria pedido" {
43
+ given {
44
+ cliente: "Cliente Exemplo"
45
+ total: 150.0
46
+ }
47
+
48
+ expect {
49
+ sucesso: verdadeiro
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ task listar_pedidos {
56
+ input {
57
+ status: Texto
58
+ }
59
+ output {
60
+ pedidos: Lista
61
+ }
62
+ effects {
63
+ consulta Pedido
64
+ }
65
+ guarantees {
66
+ pedidos existe
67
+ }
68
+ impl {
69
+ php_rota: pedidos.rota.listarPedidos
70
+ php_servico: pedidos.servico.listarPedidos
71
+ }
72
+ vinculos {
73
+ arquivo: "src/Pedidos/Controller/PedidoController.php"
74
+ arquivo: "src/Pedidos/Service/PedidoService.php"
75
+ simbolo: pedidos.rota.listarPedidos
76
+ }
77
+ tests {
78
+ caso "lista pedidos por status" {
79
+ given {
80
+ status: "pago"
81
+ }
82
+
83
+ expect {
84
+ sucesso: verdadeiro
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ task cancelar_pedido {
91
+ input {
92
+ id: Id required
93
+ }
94
+ output {
95
+ cancelado: Booleano
96
+ }
97
+ rules {
98
+ id deve_ser valido
99
+ }
100
+ effects {
101
+ persistencia Pedido
102
+ auditoria pedido_cancelado
103
+ }
104
+ guarantees {
105
+ cancelado == verdadeiro
106
+ }
107
+ impl {
108
+ php_rota: pedidos.rota.cancelarPedido
109
+ php_servico: pedidos.servico.cancelarPedido
110
+ php_persistencia: pedidos.repositorio.atualizarStatus
111
+ }
112
+ vinculos {
113
+ arquivo: "src/Pedidos/Controller/PedidoController.php"
114
+ arquivo: "src/Pedidos/Service/PedidoService.php"
115
+ arquivo: "src/Pedidos/Repository/PedidoRepository.php"
116
+ simbolo: pedidos.rota.cancelarPedido
117
+ }
118
+ tests {
119
+ caso "cancela pedido por id" {
120
+ given {
121
+ id: "ped_1"
122
+ }
123
+
124
+ expect {
125
+ sucesso: verdadeiro
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ route pedidos_criar {
132
+ metodo: POST
133
+ caminho: /pedidos
134
+ task: criar_pedido
135
+ finalidade: cadastro_pedido
136
+ input {
137
+ cliente: Texto
138
+ total: Numero
139
+ }
140
+ output {
141
+ pedido: Pedido
142
+ }
143
+ }
144
+
145
+ route pedidos_listar {
146
+ metodo: GET
147
+ caminho: /pedidos
148
+ task: listar_pedidos
149
+ finalidade: consulta_pedidos
150
+ input {
151
+ status: Texto
152
+ }
153
+ output {
154
+ pedidos: Lista
155
+ }
156
+ }
157
+
158
+ route pedidos_cancelar {
159
+ metodo: PATCH
160
+ caminho: "/pedidos/{id}/cancelar"
161
+ task: cancelar_pedido
162
+ finalidade: cancelamento_pedido
163
+ input {
164
+ id: Id
165
+ }
166
+ output {
167
+ cancelado: Booleano
168
+ }
169
+ }
170
+ }
@@ -279,7 +279,7 @@ export function validarImplementacoesTask(task, diagnosticos) {
279
279
  for (const campo of task.impl.campos) {
280
280
  const nome = normalizarNomeCampoImplementacao(campo.nome);
281
281
  if (!nome) {
282
- diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use ts, js, py, dart, lua, cs, java, go, rust, cpp ou php, opcionalmente com papel: ts_rota, ts_servico, ts_persistencia ou ts_repositorio."));
282
+ diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use ts, js, py, dart, lua, cs, java, go, rust, cpp ou php, opcionalmente com papel de qualquer origem: <origem>_rota, <origem>_servico, <origem>_persistencia ou <origem>_repositorio (ex.: ts_rota, php_servico, cs_persistencia)."));
283
283
  continue;
284
284
  }
285
285
  const rotulo = nome.papel ? `${nome.origem}_${nome.papel}` : nome.origem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semacode/cli",
3
- "version": "3.11.1",
3
+ "version": "3.12.1",
4
4
  "description": "Local-first semantic governance CLI for AI agents, proven with Claude, Codex, zCode (GLM) and Kimi: contracts, drift, impact, docs gates, and code generation.",
5
5
  "type": "module",
6
6
  "icon": "logo.png",