feat(commands): comandos dinâmicos dirigidos por manifest com executor REST genérico (LIN-6332)#10
Open
djalmaaraujo wants to merge 11 commits into
Open
feat(commands): comandos dinâmicos dirigidos por manifest com executor REST genérico (LIN-6332)#10djalmaaraujo wants to merge 11 commits into
djalmaaraujo wants to merge 11 commits into
Conversation
…or REST genérico (LIN-6332)
…ão via -update (LIN-6332)
…o repo Rails (LIN-6332)
… :x não declarado (LIN-6332)
… a cópia local (LIN-6332)
…valores voláteis (LIN-6332)
djalmaaraujo
marked this pull request as ready for review
July 17, 2026 23:33
There was a problem hiding this comment.
2 issues found across 21 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/commands/dynamic_exec.go">
<violation number="1" location="internal/commands/dynamic_exec.go:141">
P2: Object and array-of-object flags accept any valid JSON scalar/container, then send a payload that violates the manifest's declared type. Validate the decoded top-level shape (and array elements) before issuing the request so invalid flag values fail locally like native typed flags.</violation>
</file>
<file name="internal/manifest/manifest.go">
<violation number="1" location="internal/manifest/manifest.go:150">
P2: A manifest parameter without `name` passes validation but becomes an empty-name Cobra flag, making required parameters impossible to provide and sending no value. Reject empty parameter names while loading the manifest.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if err := json.Unmarshal([]byte(raw), &v); err != nil { | ||
| return nil, fmt.Errorf("--%s must be valid JSON (%s): %w", name, kind, err) | ||
| } | ||
| return v, nil |
There was a problem hiding this comment.
P2: Object and array-of-object flags accept any valid JSON scalar/container, then send a payload that violates the manifest's declared type. Validate the decoded top-level shape (and array elements) before issuing the request so invalid flag values fail locally like native typed flags.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/commands/dynamic_exec.go, line 141:
<comment>Object and array-of-object flags accept any valid JSON scalar/container, then send a payload that violates the manifest's declared type. Validate the decoded top-level shape (and array elements) before issuing the request so invalid flag values fail locally like native typed flags.</comment>
<file context>
@@ -0,0 +1,195 @@
+ if err := json.Unmarshal([]byte(raw), &v); err != nil {
+ return nil, fmt.Errorf("--%s must be valid JSON (%s): %w", name, kind, err)
+ }
+ return v, nil
+}
+
</file context>
| var reservedParamNames = map[string]bool{"format": true, "help": true, "h": true} | ||
|
|
||
| func (p *Param) validate() error { | ||
| if reservedParamNames[p.Name] { |
There was a problem hiding this comment.
P2: A manifest parameter without name passes validation but becomes an empty-name Cobra flag, making required parameters impossible to provide and sending no value. Reject empty parameter names while loading the manifest.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/manifest/manifest.go, line 150:
<comment>A manifest parameter without `name` passes validation but becomes an empty-name Cobra flag, making required parameters impossible to provide and sending no value. Reject empty parameter names while loading the manifest.</comment>
<file context>
@@ -0,0 +1,173 @@
+var reservedParamNames = map[string]bool{"format": true, "help": true, "h": true}
+
+func (p *Param) validate() error {
+ if reservedParamNames[p.Name] {
+ return fmt.Errorf("reserved name (would shadow the built-in --%s flag)", p.Name)
+ }
</file context>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fecha a trilha CLI da base de comandos dinâmicos da LIN-6332.
O que muda
A CLI passa a montar comandos Cobra em runtime a partir de um
cli-manifest.jsongerado pelo backend Rails (linkana#14102 + linkana#14103) e vendorado aqui viago:embed. Um executor REST genérico faz o request; comandos manuais têm precedência sobre dinâmicos em colisão de nome.internal/manifest/: types do schema v1,Load()com validação (command/method/path obrigatórios, tipos fechados, path_params consistentes com o path, nomes reservados rejeitados) e a cópia vendorada do contrato (4 endpoints:identity show,settings email-message list|show|update).internal/client: método genéricoDo(ctx, method, path, query, payload)roteado pelodo()existente — gate read/write (ErrReadOnly), Bearer, sufixo.jsone 401 herdados sem mudança no core.internal/commands/dynamic.go+dynamic_exec.go: builder de grupos aninhados a partir decommand[], flags tipadas (string/integer/boolean nativos; date/datetime/decimal como string; array de scalar repete a flag; object/array-de-object como flag JSON), path params como args posicionais, help LLM-first (Endpoint/Auth/Arguments/Parameters/Response). Roteamento query/body honra o campoindo param. Erros com a mesma UX dos comandos manuais (hint delk auth loginno 401, ciente de impersonação).lk versionmostramanifest: <generated_at> (<source>).SURFACE.txt: golden da árvore completa de comandos — qualquer drift/takeover na superfície aparece no diff.make update-manifest: baixa a cópia commitada do repo Rails com validação de shape viajq; falha limpa em 404 sem tocar a cópia local.Por quê
Hoje cada endpoint exige struct + método no client + comando Cobra à mão. Com o manifest, expor um endpoint novo vira mudança só no Rails + refresh do manifest aqui. O consumidor primário é agente LLM, então o help estruturado é documentação de primeira classe.
Como testar
Parte 1 — sem backend (5 min)
Parte 2 — e2e contra o backend local (o teste de verdade)
Pré-requisito: o repo
linkanana branchdjalma1/lin-6332-email-messages(PR #14103), com Postgres de pé e banco dev semeado (bin/rails db:prepare db:seed).DB_HOST=localhost bin/rails server -p 3000(no repo linkana).bin/rails runner 'u = User.find_by!(email: "admin@demo.local"); t = ApiTokens::Build.call(key_name: "review", buyer_id: u.buyer_id, user_id: u.id).tap(&:save!); puts t.one_time_display_api_key'observer@demo.locale rode olist—{"error":"..."}no stderr, exit 1../lk mode reade, no linkana,bin/rails runner 'ApiToken.where(key_name: "review").each(&:revoke!)'.Esse roteiro completo (14 passos, incluindo CSRF via curl) já foi executado contra o backend real — 14/14 pass; ver relatório na LIN-6332.
Notas de review
lk versiondenuncia commanifest: null).--format stylednos comandos dinâmicos cai no fallback JSON (RawMessage não implementa Styler) — by design nesta fase.endpoints(via jq), não byte-diff — a formatação de arrays difere entre o Ruby e a cópia vendorada.make update-manifestsó funciona depois que o #14102/#14103 mergearem no main do linkana (o arquivo ainda não existe lá; até lá o target falha limpo).