BEX-470 refactor(config): normalize app-config.json keys to snake_case with legacy migration - #104
Merged
piyushsarin-sib merged 6 commits intoSep 4, 2026
Conversation
…egacy migration Rename the camelCase keys in app-config.json to snake_case, matching the wire contract: appId → app_id, appName → app_name, logoUri → logo_uri, appType → app_type, auth.redirectUris → auth.redirect_uris. The ProjectConfig interface now IS the file shape, so every read site was found by the compiler. Backward compatibility: readProjectConfigAt folds the legacy camelCase keys (and the older auth.redirectUrls) through one alias table, drops them from the returned object, and warns once on stderr when a file carries both spellings with different values. Migration on write: create/upload/scaffold/start now emit snake_case only. upload and scaffold also rewrite an in-sync legacy file on their no-op paths via migrateProjectConfigKeys(), so one run migrates a project regardless of drift. Values are never changed. Also: template, scaffold docs, agent-context docs, README, smoke scripts (accept both spellings so published-build runs still pass), tests for camelCase-only / snake_case-only / mixed reads and the write-back migration, and a changeset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…igration note Print the snake_case migration notice whenever a legacy app-config.json is rewritten — after an upload push and after a consented scaffold refresh, not only on the no-op paths — and word it so users know to update their own scripts that read the file. Add an explicit migration rule to agent-context/SKILL.md and AGENTS.md so agents steer custom code to the new key names and never write the camelCase names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pin the ordering `assertAppTypeAgrees` documents: a config that is BOTH mislabelled and structurally broken must report the structural problem, so a partner is never sent off to fix a label only to hit the real refusal on the next upload. Asserted positively (the entry-named `.label:` error) plus an explicit check that the message does not mention `app_type`, rather than via `rejects.not.toThrow`, which can pass for the wrong reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`app upload` now reads `app_type` to check it agrees with the blocks, so three docs that said the CLI never reads it were wrong as written. - CLAUDE.md: read for validation, never for detection; the pre-flight ordering and why it is last; and the wire decision, which was nowhere stated — the field is file-only because `UploadAppPayload` and the create body are closed structs built key by key, so nothing strips it and bo-be needs no change. - The scaffolded AGENTS.md template said "the CLI never reads it". It now says what the field is for and that a hand-edited block needs the label updated. - agent-context/SKILL.md and AGENTS.md documented the key but not its meaning; both now carry the same paragraph, kept in sync per the repo rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…changeset CLAUDE.md asks for one changeset file per branch, appended to rather than multiplied. Merges the separate snake_case file into the existing one and adds the app_type agreement check. Bump level stays `minor` — both entries already warranted it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
piyushsarin-sib
changed the base branch from
main
to
features_set-dp-function
September 4, 2026 10:23
…e-keys Resolve conflicts from the base's appType → app_type revert (1d90504, dae10cf): keep this branch's ProjectConfig doc block, changeset text, AGENTS template wording and test fixtures, keep the base's three appType-migration tests, and drop the base's one-off appType fold in readProjectConfigAt — LEGACY_KEY_ALIASES already covers it with the other camelCase keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
satyamdev10
approved these changes
Sep 4, 2026
piyushsarin-sib
added a commit
that referenced
this pull request
Sep 4, 2026
* feat: add Brevo Function commands with smoke tests Add `brevo function list` and `brevo function get <id>` commands for managing Brevo Functions, plus the "Brevo Function" app type choice in `brevo app create`. - New service (src/services/function.ts) with list, draft list, and get - New command handlers (src/commands/function/list.ts, get.ts) - Register functionCommandGroup in definitions.ts and bin/index.ts - Add Function commands section to root help screen - Add brevo_function template flag for app-config.json rendering - Add Brevo Function app type to interactive create prompt (private only) - Unit tests for service, list command, and get command (23 tests) - Command registration tests in definitions.test.ts (4 tests) - Help formatting tests updated for functionCommandGroup - New smoke suite (scripts/smoke/function.ts) exercising list, list --draft, get, and get-not-found against a real account — opt-in via `yarn smoke --suite=function` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: gate Brevo Function behind __BREVO_PREVIEW__ and add fn alias - Add `brevo-function-type` to FEATURE_STAGE as a preview feature - Move function group definition to preview-definitions.ts so esbuild can eliminate it from published builds - Gate the Brevo Function choice in `app create` behind __BREVO_PREVIEW__ - Gate the Function commands section in help.ts behind __BREVO_PREVIEW__ - Add `aliases` support to SubcommandGroupDefinition so `brevo fn list` and `brevo fn get` work as shortcuts - Update tests for the conditional export and gated app-type prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add brevo function activate, deactivate and delete commands Add three management commands to the Brevo Function group: - `brevo function activate <id>` — PATCH with is_active: true - `brevo function deactivate <id>` — PATCH with is_active: false - `brevo function delete <id>` — DELETE with --force to skip confirmation All three are gated behind __BREVO_PREVIEW__ alongside the existing list and get commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add brevo fn init command with AI generation, template flow, and SSE error handling - Add `brevo fn init` command with two creation flows: AI generation and global templates - Implement SSE streaming for AI code generation and iteration with colored progress stages - Add preview execution (fetch contacts + execute + results table) after generation and iteration - Auto-derive `attribute_id` from function name in SCREAMING_SNAKE_CASE - Add graceful error handling for SSE stream termination and API failures - Add shared function app selector (`select-function.ts`) used by fn subcommands - Update all fn subcommands to use shared app selector - Set `source: 'cli'` on all dp-functions API calls - Wrap SSE reader.read() errors into ApiError for clean failure messages - Add comprehensive tests for init command and SSE stream module Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use draft_id instead of code for post-iterate execute preview The backend updates the draft in the database during iterate (PATCH /generate/stream) in all auth modes, so referencing the draft by ID is consistent with the initial preview and avoids sending the full code payload to the execute endpoint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: show friendly message when Brevo Functions is not enabled (403) The dp-functions backend returns 403 with code `feature_not_enabled` when the account lacks the dp-functions entitlement. Map this to a user-friendly message in both the REST client (ApiClient) and the SSE stream handler so all fn subcommands display: "Brevo Functions is not enabled for this account." Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: address SonarCloud issues — reduce complexity, fix nested ternaries - sse-stream.ts: extract performSSEFetch, handleSSEErrorResponse, readChunk, processSSELine, flushSSEState helpers to reduce cognitive complexity from 49 to ~11; replace nested ternary with extractErrorMessage function - init.ts: extract executePreview, saveGeneratedFunction, mergeGenerateResult, updateSpinnerFromEvent, accumulateResult helpers to reduce cognitive complexity of processGenerateStream (38→~8) and aiGenerationFlow (40→~11); combine consecutive push calls into single push with multiple args - list.ts: extract inner template literals to variables to eliminate nesting - templates/index.ts: replace nested ternary with if/else in resolveTemplateFlags - create.ts: replace nested ternary with if/else in buildCreatePayload; extract cacheCreatedAppCredentials to reduce createCommand complexity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: address remaining SonarCloud warnings - Parameterize similar tests in sse-stream.test.ts and definitions.test.ts (S5976) - Add formatCellValue helper for safe object stringification in init.ts (S6551) - Replace boolean statusBadge function with ACTIVE_BADGE/INACTIVE_BADGE constants in list.ts (S2301) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: fix last S6551 warning in formatCellValue (init.ts) Use explicit type checks (string, number, boolean) instead of String(value) on unknown, so SonarCloud can verify no object reaches the default toString path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: hide attribute_id from predefined template preview table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove Attribute ID line from template preview output The Attribute ID was shown both as a header line and could appear in the results table. Remove the explicit header line so the preview only shows the description and the data table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #69 review comments - Use shared functionService from container instead of local instance (init.ts) - Wire STAGE_LABELS to lang file keys instead of hardcoded strings - Rethrow ApiError/CliError in generate and iterate catch blocks - Guard iterate request and preview against missing draftId - Match duplicate-name detection on HTTP 409 status code, not message copy - Union columns across all rows in printResultsTable - Document AbortSignal.timeout as a deadline in sse-stream.ts - Evaluate statusBadge at call time to respect TTY/NO_COLOR (list.ts) - Change --id [id] to --id <id> in all fn subcommands - Remove orphaned lang keys (stage labels, success messages, file-written) - Hoist distribution_type out of template branches (app-config.json.tmpl) - Remove unused src/__tests__/tsconfig.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve SonarCloud issues — cognitive complexity and boolean param - init.ts: extract `runIterateRound` and `tryPreview` from `aiGenerationFlow` to reduce cognitive complexity from 21 to within the 15 threshold (typescript:S3776). - list.ts: replace `statusBadge(isActive)` boolean-param function with two named functions `activeBadge()` / `inactiveBadge()` (typescript:S2301). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add `brevo fn deploy` command for deploying draft functions Add a standalone deploy command that lets users deploy draft Brevo Functions independently of the `brevo fn init` AI generation flow. - New `brevo function deploy` command with `--id` and `--json` flags - Interactive draft picker when `--id` is omitted - Preview with sample contacts before deployment - Name prompt with duplicate-name (409) retry loop - Stop flow with "Unable to deploy function" when preview returns errors - Extract shared preview-table utilities from init.ts into preview-table.ts - 11 test cases covering success, JSON, picker, errors, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve SonarCloud issues in deploy.ts — cognitive complexity and boolean param - Extract `deployJsonMode`, `deployInteractive`, and `tryPreview` from the main handler to reduce cognitive complexity (typescript:S3776). - Replace `assertDraftSelectionAllowed(jsonMode?)` boolean-param function with `assertInteractiveTerminal()` that only checks TTY (typescript:S2301). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: replace backtracking regex with lastIndexOf in deriveNameFromDescription Sonar flagged `/\s+\S*$/` as super-linear due to backtracking. Use `lastIndexOf(' ', 50)` instead — same word-boundary cut, O(n). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: link deployed function to app after creation Add app-linking step to `function deploy` and `function init` flows. After a function is created, it is linked to the selected app via POST /v3/app-store/app-functions. Adds --app-id flag to deploy for non-interactive use, and an interactive app picker when omitted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR #85 review — shared module, JSON safety, tests - Extract selectFunctionApp/tryLinkFunctionToApp into select-app.ts, used by both deploy.ts and init.ts (review point 3) - Fix --json output corruption: link runs silent, spinner uses { silent: true }, no logInfo to stdout (point 1) - Add linked/app_id to JSON payload so callers can detect link status (point 2) - Move app picker after preview+confirm to avoid wasted round-trips on early failure (point 4) - Remove appId! non-null assertion — narrowed via control flow (point 5) - Log link error detail behind isDebug via logDebug (point 6) - Add 5 new tests: --app-id path, --json without --app-id, link failure in JSON mode, no-apps throw, link failure warning (point 7) - Fix template literal + concatenation mix in app name display (point 8) - Add changeset for new --app-id flag (point 8) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract shared deploy helpers to reduce code duplication Move isDuplicateNameError, executePreview, tryPreview, and the name-confirm-deploy loop into deploy-helpers.ts. Both deploy.ts and init.ts now delegate to these shared functions, parameterized by message constants. This addresses the SonarCloud duplication quality gate (11.2% → well below 3%). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove changeset file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve SonarCloud duplication and nested template literal - Generalize executePreview to accept templateArgs (draft_id or template_id) so init.ts templateFlow uses the shared function instead of an inline duplicate. - Fix nested template literal in select-app.ts (S4624). - Extract test setup into setupHappyPath() helper to reduce duplicated mock boilerplate across 16 tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: remove preview gate from brevo function commands (GA) Move the `brevo function` surface out of the `__BREVO_PREVIEW__` build gate so it ships in every published build. This follows the same pattern as the UI-apps GA transition (BEX-290). Changes: - Flip FEATURE_STAGE['brevo-function-type'] from 'preview' to 'ga' - Move function group definition from preview-definitions.ts to definitions.ts (always-defined, no longer conditional) - Add function handler imports directly in definitions.ts - Remove __BREVO_PREVIEW__ guard from bin/index.ts, help.ts, create.ts - Move all FUNCTION_* strings from preview-messages.ts to en.ts - Add GA markers (listFunctionCommand, initFunctionCommand, deployFunctionCommand) to scripts/build.mjs - Update tests to reflect the GA state Verified: yarn test (1513 pass), yarn build (public), PREVIEW=1 yarn build (preview), yarn lint, yarn format:check — all clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add function suite to smoke test live surfaces Include `function` in the smoke test's default and pinned suites so the GA'd Brevo Function commands are exercised on every publish and pre-merge lane — matching the CLAUDE.md requirement that a GA'd feature joins the smoke test in the same PR. - scripts/smoke-test.ts: add `function` to DEFAULT_SUITES - smoke.yml: update workflow_call/dispatch defaults and options - smoke-post-merge.yml: widen the pinned suite to `private,ui,function` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: expand function smoke suite with mutation steps, init flow, and dedicated account Wire BREVO_TEST_API_KEY_FUNCTION secret and split function suite into its own CI job so it runs against a dedicated dp-functions account. Add activate/deactivate cycle, not-found error probes, deploy+cleanup, and pty-driven function init (template path) with automatic cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve SonarCloud duplication in function smoke not-found probes Extract shared assertNotFound() helper from the four identical not-found error probe functions, bringing new duplicated lines density well under the 3% quality gate threshold. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve SonarCloud duplication in function action commands Extract shared function state-change pattern (ID resolution, spinner, 404 handling, JSON/card output) into buildFunctionActionCommand helper. Activate and deactivate become thin config wrappers; delete delegates execution while keeping its confirmation prompt. Shared logic is tested once in function-action.test.ts, individual tests verify wiring only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: reduce SonarCloud duplication across function commands and smoke tests Extract resolveFunctionId and withNotFoundHandling helpers for get.ts, deduplicate smoke test cleanup with deleteAndAssert and toggle helpers, and remove unused import flagged by code-quality bot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add app_type field to app-config.json template Add explicit `app_type` field ('oauth', 'ui', or 'function') to the scaffolded app-config.json so the app type is immediately visible without relying solely on the presence of discriminator blocks. Addresses PR #100 review comment by @piyush. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: address PR #100 review comments - Add `function` to app-type registry with positive `brevo_function` detection on both config and record paths - Add `function` row to capability matrix (no OAuth flow, no redirect URIs, no account-install) - Add `brevo_function` field to `OAuthApp` type and `AppRecordLike` - Add `brevo function deploy` to help section - Replace IIFE with `resolveFunctionId` in `executeFunctionAction` - Add pagination to `fetchFunctionList` for >50 functions - Wire `ensureFresh` into `SSEStreamDeps` for token refresh before SSE connections - Expose `runEnsureFresh()` on `ApiClient`, plumb through container - Fix comment in templates/index.ts (`brevo_function` is mutually exclusive with oauth, not orthogonal) - Add explanatory comments in init.ts, tsconfig.eslint.json, help.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add backward compatibility and smoke tests for app_type field - Config backward compat: legacy configs without app_type parse correctly (field is undefined, not an error) - Config round-trip: app_type survives writeProjectConfig, and legacy configs without it stay absent after write-back - Wire isolation: app_type never leaks into the upload payload (UploadAppPayload has no app_type field) - Template rendering: brevo_function branch renders app_type correctly, all three app types produce the right value - Capability matrix: function type has no OAuth/UI capabilities, only review-lifecycle on public distribution - Record resolution: brevo_function record resolves to function type; blockless records still resolve to UI (not function) - Recoverability: function apps are always recoverable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: add changeset for function app-type registry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: rename app_type to appType in config for camelCase consistency Aligns with the existing camelCase convention used by other config fields (appId, appName, logoUri). The field is local metadata only and never sent to the server, so this is a config-shape change with no wire impact. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update stale doc comments for appType and add app_type migration - Fix doc comment in config.ts that said "there is no separate appType key" - Fix comment in en.ts that said "there is no app-type field" - Fix AGENTS.md.tmpl that said "There is no appType key" - Add legacy app_type → appType migration in readProjectConfigAt - Add 3 migration tests (read migration, write-back cleanup, precedence) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: revert appType back to app_type in config Rename the informational app-type field from camelCase `appType` back to snake_case `app_type` to match the wire-mirrored key convention used by other platform-facing fields (`distribution_type`, `ui_app`, `brevo_function`). The migration logic now migrates the legacy `appType` key to `app_type` on read and drops it on write-back. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * BEX-470 refactor(config): normalize app-config.json keys to snake_case with legacy migration (#104) * refactor(config): normalize app-config.json keys to snake_case with legacy migration Rename the camelCase keys in app-config.json to snake_case, matching the wire contract: appId → app_id, appName → app_name, logoUri → logo_uri, appType → app_type, auth.redirectUris → auth.redirect_uris. The ProjectConfig interface now IS the file shape, so every read site was found by the compiler. Backward compatibility: readProjectConfigAt folds the legacy camelCase keys (and the older auth.redirectUrls) through one alias table, drops them from the returned object, and warns once on stderr when a file carries both spellings with different values. Migration on write: create/upload/scaffold/start now emit snake_case only. upload and scaffold also rewrite an in-sync legacy file on their no-op paths via migrateProjectConfigKeys(), so one run migrates a project regardless of drift. Values are never changed. Also: template, scaffold docs, agent-context docs, README, smoke scripts (accept both spellings so published-build runs still pass), tests for camelCase-only / snake_case-only / mixed reads and the write-back migration, and a changeset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(config): announce key migration on every rewrite and add agent migration note Print the snake_case migration notice whenever a legacy app-config.json is rewritten — after an upload push and after a consented scaffold refresh, not only on the no-op paths — and word it so users know to update their own scripts that read the file. Add an explicit migration rule to agent-context/SKILL.md and AGENTS.md so agents steer custom code to the new key names and never write the camelCase names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(upload): assert the block error pre-empts the app_type label check Pin the ordering `assertAppTypeAgrees` documents: a config that is BOTH mislabelled and structurally broken must report the structural problem, so a partner is never sent off to fix a label only to hit the real refusal on the next upload. Asserted positively (the entry-named `.label:` error) plus an explicit check that the message does not mention `app_type`, rather than via `rejects.not.toThrow`, which can pass for the wrong reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: describe app_type as a validated label, not a discriminator `app upload` now reads `app_type` to check it agrees with the blocks, so three docs that said the CLI never reads it were wrong as written. - CLAUDE.md: read for validation, never for detection; the pre-flight ordering and why it is last; and the wire decision, which was nowhere stated — the field is file-only because `UploadAppPayload` and the create body are closed structs built key by key, so nothing strips it and bo-be needs no change. - The scaffolded AGENTS.md template said "the CLI never reads it". It now says what the field is for and that a hand-edited block needs the label updated. - agent-context/SKILL.md and AGENTS.md documented the key but not its meaning; both now carry the same paragraph, kept in sync per the repo rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(changeset): fold the snake_case entry into the branch's single changeset CLAUDE.md asks for one changeset file per branch, appended to rather than multiplied. Merges the separate snake_case file into the existing one and adds the app_type agreement check. Bump level stays `minor` — both entries already warranted it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> * fix: address PR #100 review comments for brevo function - Add Brevo Functions documentation to SKILL.md and AGENTS.md (was missing entirely — agents had no knowledge of the function commands) - Fix `brevo app upload` for Function apps: skip redirect_uris check, send `brevo_function: {}` on the wire, omit `auth` block, handle write-back and diff rendering correctly for the third app type - Paginate `fetchDraftFunctionList` (was capped at 50, causing silent truncation and false not-found errors in `brevo fn deploy --id`) - Fix `fetchFunctionList` pagination loop: use `has_more` + empty-page guard instead of `total` comparison to prevent infinite loops - Refactor `executeFunctionAction` to delegate to `withNotFoundHandling` instead of re-implementing the 404 catch block (Sonar duplication) - Move hardcoded strings (`Description:`, `Name:`, `Untitled Function`) to `src/lang/en.ts` - Remove unused `functionService` mock from function-action.test.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * BEX-471 feat(json): emit snake_case twins for every camelCase --json key (transition step) (#106) feat(json): emit snake_case twins for every camelCase --json key (transition step) Machine-readable output had two conventions: pass-through commands (app list, app submit, function *) emit the wire's snake_case, while CLI-built objects (create, upload, credentials, install, delete, whoami, the error envelope) emit camelCase. app-config.json settled on snake_case in BEX-470, and scripts should be able to read one spelling everywhere. Renaming outright would break every `jq .appId` in a pipeline with nothing the CLI could migrate, so this is the deprecation step: jsonOutput() now adds a snake_case twin after each camelCase key at the top level of every document (per element for arrays) and inside the error envelope. redirectUri / redirectUris alias to the wire name redirect_uris. Nested objects are left untouched — they are wire records or user data. Nothing is removed; camelCase goes away in the next major. Agent docs and the changeset carry the deprecation note; the seven tests that pinned exact camelCase shapes now pin both spellings. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Piyush Sarin <piyush.sarin@brevo.com> Co-authored-by: Piyush Sarin <84779634+piyushsarin-sib@users.noreply.github.com>
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.



Summary
Normalizes every key in
app-config.jsonto snake_case, matching the wire contract. The file was meant to be snake_case throughout, but several keys were written in camelCase.appIdapp_idappNameapp_namelogoUrilogo_uriappTypeapp_typeauth.redirectUrisauth.redirect_urisversion,distribution_type,auth.scopes,ui_appandbrevo_functionare unchanged. Nothing sent to or received from the Brevo API changes — this is only the local file contract.Jira: BEX-470. Base branch:
features_set-dp-function(PR #100) — this PR carries only the snake_case change on top of it.Backward compatibility guarantee
auth.redirectUrls). The folding happens in one place,readProjectConfigAtinsrc/lib/config.ts, via two alias tables (LEGACY_KEY_ALIASES,LEGACY_AUTH_KEY_ALIASES) — no per-command checks.--jsonstays a single parseable document). Identical values are folded silently. The notice fires once per process.ProjectConfigis the file shape. The interface fields were renamed rather than translated at a boundary, so the compiler found every read site andwriteProjectConfigemits snake_case by construction.Migration on write
brevo app create,brevo app upload,brevo app scaffoldandbrevo app startnow write snake_case keys only; the camelCase copies are dropped.brevo app upload(the "already up to date" path) andbrevo app scaffold(the no-drift path) previously wrote nothing, so an in-sync legacy file would never have been migrated. Both now callmigrateProjectConfigKeys()there — a pure key rewrite, values untouched — and print a one-line note in human mode. One run of either command is enough to migrate a project.Nothing breaks functionally
app upload's local-vs-server diff compares normalized values, so a rename alone is not a change.app install's drift check,app start's port/redirect resolution,app status/delete/withdraw/init's linked-app lookup all read through the same normalizer and were covered by the compiler-driven rename.--jsonoutput: the only place that echoed config key names wasbrevo app scaffold'sdiffs[].field, which now reportsapp_name/redirect_uris/logo_uri. Every other command's--jsonkeys (appId,appName,logoUri, …) are unchanged — those are output contracts, not config keys. Documented in the changeset.upload's raw-file guard accepts eitherapp_idorappId.Users with custom code, and agents
uploadno-op path, after anuploadpush, on eitherscaffoldpath — the CLI prints one line naming the new keys and saying: If your own scripts read this file, update them to the new key names.agent-context/SKILL.md(Hard rules) andagent-context/AGENTS.md(Conventions) carry an explicit migration rule: keys are snake_case, the CLI still reads camelCase but rewrites on every write, tell the user to switch custom code, never write camelCase into a file, and--jsonoutput keys are a separate unchanged contract.Tests
config.test.ts: newlegacy camelCase keyssuite — camelCase-only, snake_case-only, mixed (snake wins + single stderr warning), write-back migration for OAuth / UI / Function apps,hasLegacyProjectConfigKeysfor every legacy key,migrateProjectConfigKeysidempotency and no-op cases. Theapp_typesuite was reworked and a legacy-shaped fixture is kept deliberately.upload.test.ts/scaffold.test.ts: migration on the no-op paths, silence when nothing migrated,--jsonstays one document, legacyappIdaccepted by the raw guard.--jsonassertions left as they were.Docs
agent-context/SKILL.md,agent-context/AGENTS.md,README.md,CLAUDE.md, the scaffold templates (app-config.json.tmpl,README.md.tmpl,CLAUDE.md.tmpl,AGENTS.md.tmpl), and the smoke scripts (which now accept both spellings soagainst=publishedruns keep passing).Changeset
Folded into the branch's single pending changeset,
.changeset/function-app-type-registry.md(minor), per the one-changeset-per-branch rule; itsappTypemention now readsapp_type.Checks
yarn lint✅yarn test:ci✅ (70 suites, 1587 tests)yarn build✅yarn format:check✅🤖 Generated with Claude Code