feat(sdk): cliente tipado gerado do spec servido cobre as 213 operações (core#125) - #139
Merged
Conversation
…es (core#125) `cs.api` é um cliente REST tipado por caminho e método, gerado do documento OpenAPI servido em https://api.codespar.dev/openapi.json (173 caminhos, 213 operações). Nenhum método escrito à mão: o snapshot do documento é commitado com sha256, data e origem; os tipos saem do openapi-typescript; a tabela de operações (método, caminho, content types) sai do mesmo snapshot e é amarrada ao tipo `paths` por `satisfies`. Portões: - vitest: sha do snapshot confere; src/generated/ é igual ao que o snapshot gera; a tabela tem as 213 operações do snapshot; cada uma é despachada pelo cliente com método, URL e content types corretos; controle positivo (remover uma operação do snapshot dá diff). - `npm run sdk:spec:check`: reprova snapshot editado à mão, gerado desatualizado, e snapshot divergente do documento servido (rc 1; rc 2 quando não consegue buscar). `npm run sdk:spec:refresh` rebaixa e regenera. Roda no workflow hosted-runtime-smoke. Versão: @codespar/sdk 0.12.0 (minor, superfície nova). O bump é a decisão de release que o waiver core#131 esperava; waiver removido. cli 0.6.1 (range ^0.12.0) e adapters patch (peer range || ^0.12.0), mesmo padrão de 0c0aacd, para o workspace não aninhar o sdk. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FV48HsCVN4Q53Yt3GwrDGG
…re#135) Os quatro jobs validate-example pinados em ghcr.io/codespar/codespar:main estão vermelhos no main desde 03/09 (core#135), com a mesma assinatura no run do main em 15dec02 e no run deste PR: o runtime roda como `node` (uid 1000), o checkout no runner pertence ao uid 1001 com modo 755, e o `mkdir /example/.codespar` morre em EACCES antes do /health responder. CODESPAR_STATE_DIR não redireciona (core#135, item 3). Conserto: o validate.sh cria `$SKELETON_DIR/.codespar` com modo 0777 antes do `docker run`, nos sete scripts que fazem o mesmo bind mount (os três em :latest rodam como root e não precisam hoje; ganham a mesma linha pelo motivo do #137). O diretório já é gitignored. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FV48HsCVN4Q53Yt3GwrDGG
O teste "src/generated/ equals what the snapshot generates" regenera 1,1 MB de tipos em processo: ~250 ms num laptop, mas passou dos 5 s padrão do vitest no runner do CI com o turbo rodando a suíte de todos os pacotes ao mesmo tempo (verde em 553e30f, vermelho em 79247dc com o código idêntico). Timeout explícito de 60 s; o teste não mudou. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FV48HsCVN4Q53Yt3GwrDGG
…#135) Com o estado gravável (79247dc) o runtime :main sobe (`runtime up after 5s` no CI), e o demo cai na camada seguinte: `session create failed: 401 api_token_invalid`. O runtime atual exige bearer em toda rota e, sem ENGINE_API_TOKEN, cunha um token no primeiro boot e grava em .codespar/api-token; o teste manda o placeholder "demo"/"local" que o :latest (maio) ignorava. Conserto, o mesmo que o scripts/validate-bridge.sh do próprio runtime faz: o validate.sh define DEMO_API_TOKEN (CODESPAR_API_KEY ou o default do teste), passa como ENGINE_API_TOKEN ao container (nada é cunhado nem gravado) e como CODESPAR_API_KEY ao vitest do modo docker. Sete scripts, mesmo bloco. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FV48HsCVN4Q53Yt3GwrDGG
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.
O que muda
Rate card v1 (
business-models/codespar-pricing-proposal-2026-08-07.mdno shared drive, adotado em 09/09/2026) vira a única verdade de preço no web: docs,/pricing,/producte o dashboard dizem a mesma frase, os mesmos quatro números e a quinta linha (Account governance)./dashboard/billing. A grade hobby/starter/growth por cota de tool calls, que levava a um Stripe Checkout REAL, sai. Entra o cartão Pay fee lido deGET /v1/fees/movimentar(codespar-enterprise#906), com três estados honestos: carregando, "Not computed yet" (rota 404/null) e valor com badge "Not yet charged". Abaixo, os quatro números do rate card lidos desrc/lib/rate-card.ts. A barra de cota de tool calls ("Upgrade to continue") também sai: tools são R$0 sem limite.createCheckoutfoi removido desrc/lib/actions/billing.ts(export"use server"é endpoint mesmo sem botão); fica sócreatePortal, atrás debilling.has_subscription.concepts/billing.mdx,faq.mdxehow-it-works.mdxdeixam de vender "US$0,10 por transação liquidada + 0,5% FX, sem assinatura" e passam a dizer: Build, measure and think: free. Move money under mandate: 10 bps. Get paid: 1%. Never more than R$2.00 per transaction. Rails always pass through at the partner's price. Free tier de R$1.000 liquidados/mês por lane por org, estorno do fee em refund de 7 dias, sem mínimo/platform fee/rev-share, BRL.concepts/refunds.mdx,cookbooks/marketplace-payout.mdxe oproduct.pricingSubtitle(três locales) também citavam US$0,10 e foram alinhados./pricing(en, pt-BR, es-419). Já falava rate card, mas imprimia preço fechado na lane OPERAR (R$0,10/desfecho, R$0,02 mensagem, 500 grátis) que o documento traz entre colchetes, e dizia que Pay "cobra hoje" quando o documento o coloca pré-GA. Agora OPERAR diz "Included while in preview" sem número; o rodapé diz que só Gate e Embed têm preço vigente e o resto é compromisso travado 24 meses, R$0 até o GA; "10bps"/"R$2" viram "10 bps"/"R$2.00" como nos docs; os três cenários mensais viram os quatro exemplos óbvios abaixo.scripts/v5_model.py(account_fee = 4.90) e positioning paper ED5. Entra em concepts/billing, faq, how-it-works, na/pricing(lane própria + linha do recibo, três locales), no JSON-LD da home e no bloco do rate card do dashboard, com "not yet charged" enquanto o backend não mede contas. Nunca com o nome antigo; o spec reprovaRelationshipcomo nome de linha./pricingos cartões viram tabela de sete linhas lida das messages.tests/unit/rate-card-consistency.spec.tslê os 3 mdx e as messages da/pricing(3 locales) e reprova se um dos seis valores divergir (10 bps, piso R$0,05, 1%, teto R$2,00, R$4,90/conta, free tier R$1.000), se um dos sete exemplos não bater commoveFeeMinor/getPaidFeeMinor/governanceFeeMinor(recalculado, não comparado a texto; getPaid é exato, meio centavo é meio centavo), se "0.10 per settled"/"per settled transaction"/"subscription"/"cross-border surcharge"/"Relationship" voltar, ou se qualquer valor em BRL colar numa unidade de desfecho (OPERAR).Rebase sobre
mainde hoje (4085f6fc, já com o #832): limpo, zero conflito nas duas passagens (fc4b8c02 e 4085f6fc).mainnão tocousrc/app/dashboard/billing/page.tsxdesde a base do PR; tocou os três mdx, que este PR reescreve por inteiro. Preservado: tokens v3, modal Enterprise, três estados do cartão de fee.Evidência
git rebase origin/maingrep -rn 'createCheckout|PLAN_DISPLAY|handleUpgrade' src testscreatePortalsó embilling.tse na página, atrás dehas_subscriptionnpx playwright test --config=playwright.unit.config.ts tests/unit/rate-card-consistency.spec.tsR$2.00→R$3.00em billing.mdxex1FeeR$0.10→R$0.20 em en.jsonR$2.00literal no TSX da páginaR$4.90→R$5.90no faq.mdxgovernTitle= "Relationship" no en.jsonex7FeeR$58,80→R$58,00 no es-419git checkoutnpm run checknpm run build/tmp/codespar-heavy-lock.sh, tree rebasednpm run test:unitnpm run docs:api:checkcontent/docs/api/reference/intocado)npm run check:meta-tool-docscontent/docs/concepts/meta-tools/**intocado)npm run test:scriptsnext start+ GET/pricing200,/docs/concepts/billing200,/pt/pricing200; seis valores, sete exemplos, "Account governance", "Included while in preview" e/v1/fees/movimentarno HTML; nenhuma frase do modelo antigo, nem/v1/billing/movimentar, nemRelationship; servidor morto ao fimgh pr checks 786(poll 60s)gh pr checks 786(poll 60s, limite 40 min)CI: os quatro validate-example vermelhos no main (core#135)
Estavam vermelhos no main desde 03/09 (último verde: run 30132831940 em 08d2cc1, 2026-07-24) e caíram igual no primeiro run deste PR. Não é segredo do repo: o job
cinão usasecrets.nenhum e o 401 vem do runtime local emlocalhost:3000. Duas camadas, cada uma com o log antes e depois:validate.shcria.codesparcom modo 0777 antes dodocker run(7 scripts; gitignored)runtime did not become healthy in 30s+[server] EACCES: permission denied, mkdir '/example/.codespar'(runtime roda como uid 1000, checkout do runner é uid 1001/755)validate.sh: runtime up after 5s(run 34426921902)validate.shpassa-e ENGINE_API_TOKEN="$DEMO_API_TOKEN"ao container eCODESPAR_API_KEYao vitest, mesmo padrão dovalidate-bridge.shdo runtimesession create failed: 401 {"code":"api_token_invalid", ... "If you set ENGINE_API_TOKEN, send that value instead."}(o:mainexige bearer e cunha token; o:latestde maio ignorava o placeholder):latestseguem verdesE um flake meu: 0569cf1 dá 60 s ao teste que regenera 1,1 MB de tipos em processo (250 ms local,
Test timed out in 5000msno runner com o turbo rodando todos os pacotes; verde em 553e30f, vermelho em 79247dc com código idêntico).Cético
v5_model.pycalculaaccount_fee_eff = min(4.90, 2% do fluxo mensal da conta)com o comentário "R$4,90 é preço de tabela, não preço cobrável em toda banda"; é premissa de modelo, não termo publicado, então as páginas dizem R$4,90 por conta ativa e nada sobre o teto de 2%. Se esse teto for termo comercial, precisa entrar no documento canônico antes de ir pra página./pricingcontinua imprimindo os degraus enterprise e o FX publicado, que estão no documento mas não na frase-mãe.createCheckoutfoi apagado e não existePOST /v1/billing/checkoutchamado emsrc/. O backend ainda pode expor a rota; isso é ent#906/lanepreco-enterprise.createPortalcontinua e é intencional (org com acordo pago vivo).NEXT_PUBLIC_STRIPE_*/sk_test_.NEXT_PUBLIC_STRIPE_PKaparece emsrc/components/mcp/stripe-demo.tsx(chave publicável do demo Stripe do MCP, pré-existente, não é billing).sk_test_só como placeholder de docs (install/page.tsx,server-details.json) e sentinela emtests/unit/*.setup.ts. Nenhum segredo no diff. Memória do projeto: houvesk_test_em produção no self-serve; não é este repo, mas fica registrado.rgba(245,158,11,0.1)porV3.surface+V3.line. A/pricingjá carregava 23 hex literais (accents de produto espelhando/product) antes deste PR; não mexi.lock cmd > log 2>&1; echo rc=$?. O único pipe (playwright | grep) é só para resumir a saída; o resultado veio do texto "12 passed"/"1 failed".next startsem Clerk responde 500. A primeira sondagem deu rc=23 porque o middleware exigeNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY; repetida com as mesmas sentinelaspk_test_/sk_test_unit_sentinel…quetests/unit/middleware/setup.tsusa (nunca uma chave real). O 200 nas páginas públicas não depende de Clerk, só de o middleware carregar.gh pr checks --watchcomtimeout. A primeira tentativa morreu com rc=127 (timeoutnão existe no macOS) e o log mostrava tudo pendente; substituído por loop de 60 s com prazo. Está na linha CI acima.dev:up(portão pré-push do CLAUDE.md). Não rodei: semcodespar-enterpriseneste clone. O smoke de dashboard roda no CI contra o preview Vercel; a linha CI cobre.git checkoutem arquivos ainda não commitados e reverteu billing.mdx, how-it-works.mdx e en.json; refiz e commitei antes de repetir os controles. O diff final foi verificado depois disso (git diff origin/main..HEAD).Fica de fora
GET /v1/fees/movimentarainda não existe em produção; o cartão mostra "Not computed yet". Shape camelCase (consumerCount,movedMinor,feeMinor,rateTier) conferido no diff do #906. A rota saiu de/v1/billing/…porque a matriz v2.1.1 reserva/billingao BFF do dashboard e o smoke do dev-up do enterprise reprova a substring; string trocada embilling.ts,billing.mdxetests/dashboard/billing.spec.ts(grep repo inteiro: zero referência velha).fees:read. Não muda nada para o usuário do dashboard: o web chama o backend comx-codespar-service-key(service auth,src/lib/backend.ts), não com chavecsk_escopada. O escopo só importa a quem chamar/v1/fees/movimentarcom API key própria, e isso não é a superfície documentada.GET /v1/feescomcomputed/chargingpor lane. O ent#906 passou a expor tambémGET /v1/feesdizendo, por lane (MOVIMENTAR, FATURAR, GOVERNAR, OPERAR), o que está computado e o que está sendo cobrado. Este PR não lê isso: o cartão de fee e as marcas "not yet charged" das páginas são texto fixo. Follow-up: o dashboard e as páginas passam a derivar a marca desses dois campos em vez de texto; a ligação mora em ent#1189 (lanes sem cobrança).GET /v1/billingcontinua sendo lido só parahas_subscription; o doc de billing não documenta mais o shape antigo (unit_price_usd: 0.10) porque ele era ficção./mcp"PAID · Provider Pricing" (Fiscal: R$0.10-0.50 per NFe,Messaging: R$0.01-0.10 per message) descreve custo de provider externo, não linha CodeSpar; deixei, mas é candidato a revisão junto com OPERAR.content/docs/meta.jsonnão precisou de mudança.🤖 Generated with Claude Code
https://claude.ai/code/session_01FV48HsCVN4Q53Yt3GwrDGG