Skip to content

feat(devtools): frigg validate, and make the app-definition schema match the runtime (ADR-051) - #666

Merged
seanspeaks merged 7 commits into
nextfrom
feat/frigg-validate
Oct 24, 2026
Merged

seanspeaks merged 7 commits into
nextfrom
feat/frigg-validate

Conversation

@seanspeaks

@seanspeaks seanspeaks commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds frigg validate and makes app-definition.schema.json describe what Frigg actually reads. The decision is recorded in ADR-051.

  • Integration classes validate. integrations.items was type: object, but apps list integration classes (functions), so every app with an integration failed validateAppDefinition. Nobody noticed because only frigg init ran the schema, as a warning, against a hard-coded stand-in object. validateAppDefinition now checks a serialised view where each class in integrations, adminScripts and reports becomes { Definition }. frigg validate checks each class structurally, using the same rules as Module.validateDefinition and the integration builder.

  • The schema matches the builders. The enums now hold the values the builders accept. The schema now includes the keys the builders and core read but used to reject (over 50 of them). Keys nothing reads are marked deprecated and lose their defaults. The drift table below lists every change.

  • One step applies the defaults. applyAppDefinitionDefaults() lives in @friggframework/schemas and loads only the JSON, not Ajv. It is called by core's loadAppDefinition, by devtools' createFriggInfrastructure and by frigg validate, and nothing else applies schema defaults. Rule: a schema default must equal what the code does when the key is absent; where it didn't, the default was removed. The one intended behaviour change is that the user defaults now apply, which fixes login (see "Behaviour changes").

  • frigg validate [--stage <s>] [--json]

    • Loads index.js the same way build does.
    • Validates it against the schema, then runs checks the schema can't express:
      • integration classes and their API module definitions
      • an enabled database
      • management values against the other settings
      • encryption method against the required env and the stage
      • a user config that can log in
      • 'ssm' env entries without ssm.enable
      • keys that have no effect
    • Prints each problem with a JSON pointer and a fix, and exits 1 on errors.
  • Wiring

    • frigg build and frigg deploy run the same validation first and stop on errors. --skip-validate bypasses it and prints a highlighted VALIDATION SKIPPED line.
    • frigg start only warns.
    • frigg init validates the generated index.js through the same code path (as warnings).
  • Contract test (packages/devtools/infrastructure/app-definition-schema-contract.test.js): composes the infrastructure for 12 definitions with discovery mocked and records every key the builders read through a proxy. It asserts that:

    • every key read is in the schema;
    • every schema key is read by a builder, or listed with the code that reads it, or marked deprecated;
    • no builder reads a deprecated key;
    • applying the schema defaults changes nothing in the composed output;
    • the schema accepts every definition in the matrix.

    Run against the old schema, it fails on more than 50 keys.

  • Docs: ADR-051, the CLI README, the schemas README, CLAUDE.md "Frigg CLI Features", the frigg skill's CLI list, and the PostgreSQL docs. The PostgreSQL docs used management: 'create-new' and keys the Aurora builder never reads (scaling, databaseName, deletionProtection, ...).

Behaviour changes (all in user, through loadAppDefinition)

Definition Before After
no user block userConfig = null, so authenticated routes and /user/login throw a TypeError individual users, appUserId login, Frigg token + shared-secret auth
user: { usePassword: true } /user/login throws "must require either individualUserRequired or organizationUserRequired" username/password login works
user: { password: true } (deprecated spelling) ignored, login throws treated as usePassword: true
user: { organizationUserRequired: true } with individualUserRequired unset login looks up the org user only individualUserRequired defaults to true, which the x-frigg header path and the User model already assumed. frigg validate warns and suggests individualUserRequired: false

The schema default of user.usePassword changes from true to false, and authModes.sharedSecret changes from false to true. Both now match what core already did when the key was absent, so apps that identify users by appUserId, or that use the shared secret, keep working.

frigg build and frigg deploy can now stop on definitions that used to deploy, when the definition has an unknown key (e.g. a typo such as encrpytion, which used to deploy without encryption) or a value the builders would reject anyway.

Drift table: schema vs runtime

Key Runtime Schema before Schema now
integrations[] classes (integration builder, core loader) type: object, rejects classes { Definition } view, plus structural checks in frigg validate
managementMode managed, existing, else discover; default discover managed|discover|custom, default managed +existing; custom (= discover) warns; no default
vpcIsolation || 'shared' in translation, undefined = isolated elsewhere default isolated no default; warns under managed
vpc.management discover|create-new|use-existing discover|create-new|existing builder values
vpc.subnets.management acts on create, use-existing discover|create-new|existing (all no-ops) adds the acted-on values; no-ops warn
vpc.natGateway.management acts on createAndManage discover|create-new|existing (no-ops) adds createAndManage; no-ops warn
vpc.selfHeal only translated when set (config.selfHeal !== false) default false no default
vpc.shareAcrossStages/ownership/external/config VPC builder and resolver missing added
database.postgres.management discover|managed|use-existing discover|create-new|existing (2 of 3 fail the build) builder values
database.postgres.enable Aurora === true; migrations/Prisma layer !== false default false no default
database.postgres.maxCapacity default 4 default 1 default 4
postgres.engineVersion/ownership/external/clusterId/instanceId, database.dynamodb.enable Aurora builder/resolver, discovery, VPC resolver missing added
postgres.selfHeal, database.uri, mongoDB.uri not read described deprecated
encryption.fieldLevelEncryptionMethod only 'kms' acts; omitted = no key (core reads env vars, not this key) default aes no default; omission documented
encryption.createResourceIfNoneFound undefined ≠ false (env-var fallback) default false no default
encryption.ownership/kmsKeyAlias/keyAlias/disable KMS builder/resolver, discovery, encryption registry missing added
user.* defaults never applied declared applied by the loader; usePassword → false, sharedSecret → true (code behaviour)
user.password not read "deprecated alias", default true alias applied by the loader
user.strictUserValidation x-frigg headers use case missing added
auth.*, security.*, webhooks.*, custom.*, secretsManager, label, user.fields/model, websockets.routeSelectionExpression not read described, with defaults deprecated, no defaults; frigg validate warns
logging.level / logging.format Lambda log config only when set / always JSON default info / json no defaults
usePrismaLambdaLayer, lambda.keepNestedNodeModules, scheduler.enable, aws.discovery.*, migration.ownership.* builders missing added
telemetry.*, admin.*, adminScripts, reports, deployment.*, stage, region core, admin-scripts, CLI, KMS alias missing added (region deprecated: the region comes from AWS_REGION)
API module rateLimit (see #656) not read (requester has fixed back-off) described in api-module-definition frigg validate warns when set

Overlap with #639 and #663, and merge order

Test plan

  • packages/schemas: 158 passed. New app-definition-defaults.test.js (view, defaults walker, user defaults, alias, idempotence, no mutation) and app-definition-runtime-keys.test.js (builder enums, runtime keys, defaults equal the runtime's, deprecated keys).

  • packages/core: new loader tests (user defaults applied, missing user → defaults, app values kept). Full core suite has the same 64 failures as origin/next, all environment-related (MongoMemoryServer, Boom message text, deprecation output), and none new.

  • packages/devtools/frigg-cli: 45 new validate-command tests (integration checks, semantic checks, command and --json, load errors, backend discovery, preflight for build/deploy/start/skip, init) and 4 wiring tests. Same 4 failing suites as origin/next (@inquirer/prompts/tmp load error, dependencies.test.js).

  • packages/devtools/infrastructure: the contract test (7 tests) and the createFriggInfrastructure defaults test pass. The full devtools run has the same 32 failing suites as origin/next. template-generation.test.js fails identically (7/34) on both when run alone.

  • packages/admin-scripts: 278 passed (unchanged).

  • Manual: a scaffold-style backend (the fix: lazy-load scheduler AWS SDK in core and repair frigg init scaffolding #639 template plus an OAuth2 integration class with an API module definition) passes frigg validate --stage prod (exit 0, --json valid). With the template's management: 'external' on next, it reports /database/postgres/management (expected until fix: lazy-load scheduler AWS SDK in core and repair frigg init scaffolding #639).

  • Manual: a deliberately broken backend makes frigg validate exit 1 and lists 9 errors and 2 warnings, each with a pointer and a fix:

    • typo key encrpytion → "Did you mean "encryption"?"
    • postgres.management: 'create-new'
    • vpc.management: 'existing'
    • invalid integration name
    • module missing testAuthRequest
    • duplicate integration name
    • aes without AES_KEY_ID/AES_KEY
    • a user config that cannot log in
    • custom has no effect (warning)

    On the same backend, frigg build and frigg deploy stop before osls, and --skip-validate proceeds with the banner.

  • npm ci lock: package-lock.json gains only core's new @friggframework/schemas dependency.

Left for follow-ups

  • managementMode: 'managed' logs that it ignores the legacy VPC keys but the VPC builder still applies them (vpc-builder.js translateLegacyConfig). frigg validate warns; the builder fix is separate.
  • deploy-command requires ../../infrastructure/env-validator, which does not exist (the file is domains/shared/validation/env-validator.js), so it always falls back to basic env checks.
  • Core skips field-level encryption for STAGE dev/test/local, even on a deployed dev stage. frigg validate --stage dev warns; feat(core)!: fail closed on field-level encryption in deployed stages #663 is the place to change it.
  • docs/reference/aurora-public-access.md documents allowedIpAddresses and scaling, which nothing implements. This PR adds a status note; the feature or the page needs a decision.

🤖 Generated with Claude Code

📦 Published PR as canary version: 2.0.0--canary.666.abcf913.0

✨ Test out this PR locally via:

npm install @friggframework/admin-scripts@2.0.0--canary.666.abcf913.0
npm install @friggframework/core@2.0.0--canary.666.abcf913.0
npm install @friggframework/devtools@2.0.0--canary.666.abcf913.0
npm install @friggframework/eslint-config@2.0.0--canary.666.abcf913.0
npm install @friggframework/prettier-config@2.0.0--canary.666.abcf913.0
npm install @friggframework/schemas@2.0.0--canary.666.abcf913.0
npm install @friggframework/serverless-plugin@2.0.0--canary.666.abcf913.0
npm install @friggframework/test@2.0.0--canary.666.abcf913.0
npm install @friggframework/ui@2.0.0--canary.666.abcf913.0
# or 
yarn add @friggframework/admin-scripts@2.0.0--canary.666.abcf913.0
yarn add @friggframework/core@2.0.0--canary.666.abcf913.0
yarn add @friggframework/devtools@2.0.0--canary.666.abcf913.0
yarn add @friggframework/eslint-config@2.0.0--canary.666.abcf913.0
yarn add @friggframework/prettier-config@2.0.0--canary.666.abcf913.0
yarn add @friggframework/schemas@2.0.0--canary.666.abcf913.0
yarn add @friggframework/serverless-plugin@2.0.0--canary.666.abcf913.0
yarn add @friggframework/test@2.0.0--canary.666.abcf913.0
yarn add @friggframework/ui@2.0.0--canary.666.abcf913.0

Note

Medium Risk
Changes default user/auth behaviour at runtime and can block builds/deploys on configs that previously slipped through; escape hatch is --skip-validate.

Overview
Introduces frigg validate (schema + semantic checks on index.js, --stage, --json) and wires it into the CLI: frigg build and frigg deploy fail on errors (--skip-validate bypasses with a loud warning); frigg start and frigg init only warn.

Aligns app-definition.schema.json with what builders and core actually read (ADR-051): integration classes validate via a serialised { Definition } view plus structural checks; enums and keys match the infrastructure builders; unused keys are deprecated. applyAppDefinitionDefaults() in @friggframework/schemas is the single place schema defaults are applied—used by loadAppDefinition, createFriggInfrastructure, and validation—fixing user config when the block is missing or incomplete (login/auth behaviour changes documented in the ADR).

Adds a schema–builder contract test, core/schemas/devtools tests, and doc updates (CLI README, PostgreSQL/Aurora guides, hosted-schema notes in ADR-051).

Reviewed by Cursor Bugbot for commit abcf913. Bugbot is set up for automated code reviews on this repo. Configure here.

Version

Published prerelease version: v2.0.0-next.124

Changelog

🚀 Enhancement

  • @friggframework/core, @friggframework/devtools, @friggframework/schemas
    • feat(devtools): frigg validate, and make the app-definition schema match the runtime (ADR-051) #666 (@seanspeaks)

🐛 Bug Fix

  • @friggframework/admin-scripts, @friggframework/core, @friggframework/devtools, @friggframework/eslint-config, @friggframework/prettier-config, @friggframework/schemas, @friggframework/serverless-plugin, @friggframework/test, @friggframework/ui
    • fix(devtools): route /api/entity and /api/entities to the integration router #667 (@seanspeaks)

Authors: 1

@seanspeaks seanspeaks added release Create a release when this pr is merged prerelease This change is available in a prerelease. labels Oct 2, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, comment @claude review on this pull request, or push a new commit, to trigger a review.

@netlify

netlify Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for friggframework-org canceled.

Name Link
🔨 Latest commit abcf913
🔍 Latest deploy log https://app.netlify.com/projects/friggframework-org/deploys/6adc37de370553326609401f

@seanspeaks

Copy link
Copy Markdown
Contributor Author

ADR reconciliation needed after Stack A/B — drafted with Claude Code at Sean's request

Severity: must-reconcile. ADR-051 §1: "every key a builder, core or the CLI reads is in the schema". Stack A adds many keys, and it edits the #671 copy of app-definition.schema.json, which predates this PR's rewrite (and lacks admin/adminScripts/reports from next). Whichever merges second has to carry the rest.

App-definition keys Stack A reads (all need schema entries and frigg validate checks)

Integration Definition (structural checks, §3)

Module definitions: proxy.baseUrls (#690) in api-module-definition.schema.json.

Environment (ADR-027): Stack B reads FRIGG_PUBLIC_URL, INTEGRATION_DEFINITIONS_{POSTGRES,MONGO}_URL and INTEGRATION_RUNTIME_QUEUE_URL. Say which builder provisions them, or have frigg validate warn when the definitions runtime is registered without them.

Merge order: this PR and #671 should converge on one schema first. Stack A's schema hunks then rebase onto it.

cursor Bot pushed a commit that referenced this pull request Oct 20, 2026
Add frigg validate --module (also frigg validate module) to load Definition
from ./index.js, run validateApiModuleDefinition and checkModuleDefinition,
and exit non-zero with JSON field paths. Supports --json and -v.

Stacked on #701 (api-module schema fields) and includes #666 (ADR-051
frigg validate app mode) so the CLI canary has both.

Co-authored-by: Sean Matthews <sean.matthews@lefthook.co>
cursor Bot pushed a commit that referenced this pull request Oct 20, 2026
Add frigg validate --module (also frigg validate module) to load Definition
from ./index.js, run validateApiModuleDefinition and checkModuleDefinition,
and exit non-zero with JSON field paths. Supports --json and -v.

Stacked on #701 (api-module schema fields) and includes #666 (ADR-051
frigg validate app mode) so the CLI canary has both.

Co-authored-by: Sean Matthews <sean.matthews@lefthook.co>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 923c975. Configure here.

@cursor

cursor Bot commented Oct 24, 2026

Copy link
Copy Markdown

Frigg 2.0.1 review (Group 2) — COMMENT only

Checklist

  • Schema conflicts resolved ('none', postgres.management: 'external', stacked keys): fail
  • ADR-051 “hosted schemas” amendment on this branch: pass (amendment text is sound; must land with / before #705’s sync)

Verdict (would): changes needed

Findings

  1. blocking — packages/schemas/schemas/app-definition.schema.json:277

    • database.postgres.management enum is ["discover","managed","use-existing"] and omits 'external'.
    • Scenario: After #639 (already on next), frigg init scaffolds management: 'external'. frigg validate / frigg build / frigg deploy reject /database/postgres/management even though semantic checks already special-case 'external' (checks/config.js ~L118, ~L212). Scaffolded apps cannot pass validation.
  2. blocking — packages/schemas/schemas/app-definition.schema.json:404

    • fieldLevelEncryptionMethod enum is still ["kms","aes"] — no 'none'.
    • Scenario: App opts out with encryption: { fieldLevelEncryptionMethod: 'none' } (required by #663 fail-closed). Schema validation errors on the enum before semantic checks run → cannot deploy an explicit plaintext stage. Keep 'none', do not restore default: "aes".
  3. should-fix — schema still missing keys stacked PRs add (present on those branches’ schemas, absent here):

    • entities (#679 / ADR-059)
    • managementApi.proxy.* (#671 / ADR-052–053)
    • provider: "netlify" (#686)
    • extensions (#686 / ADR-017 note)
    • user.jwtConfig is already present (pass).
    • Scenario: Merge #666 then #679/#671/#686 (or the reverse) → additionalProperties/unknown-key errors or lost keys when rebasing app-definition.schema.json. Fold the enums/objects in (or rebase onto those stacks) before calling the schema “what Frigg reads”.
  4. should-fix — packages/devtools/frigg-cli/validate-command/checks/config.js:310 (no-field-encryption)

    • Still a warning when method is omitted on a non-bypass stage.
    • Scenario: After #663, a deployed stage with no method and no 'none' refuses to start. frigg validate --stage prod should be an error unless method is 'none', matching the runtime (as this PR’s own overlap section says).
  5. should-fix / merge order with #705

    • Hosted-schemas amendment correctly requires immutable …/v<major>.<minor>.json and offline validation.
    • Scenario: If #705 merges first, it freezes today’s pre-validate app-definition as v1.0. This PR then changes the same file without a version bump → either a conflicting rewrite of an already-published v1.0 or a schema that no longer matches the hosted bytes. Land #666 (with the enum fixes) before or with #705, then sync:hosted.

ADR-051 body + contract test approach look good once the enum gaps above are closed.

"enum": ["kms", "aes"],
"default": "aes"
"description": "Field-level encryption key. 'kms': Frigg creates or discovers a KMS key and sets KMS_KEY_ARN. 'aes': the app supplies AES_KEY_ID and AES_KEY (list both in `environment`). Omitted: no key is provisioned and deployed stages store sensitive fields unencrypted (core logs a warning). Stages dev, test and local never encrypt.",
"enum": ["kms", "aes"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: enum is ["kms","aes"] — missing 'none' from #663.

Scenario: encryption: { fieldLevelEncryptionMethod: 'none' } (the documented fail-closed opt-out) fails schema validation, so frigg build/deploy stop before they can set FRIGG_ENCRYPTION_DISABLED. Add 'none'; do not reintroduce default: "aes".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 89f6545. The enum is now kms | aes | none, with no default; a schema test asserts the default stays absent. 'none' is treated as an explicit opt-out: frigg validate emits neither the no-field-encryption warning nor the encryption-bypassed warning for it. The contract matrix also has an external db, encryption none case.

🤖 Generated with Claude Code

"enum": ["discover", "create-new", "existing"],
"default": "discover"
"description": "How Frigg gets the Aurora cluster (ignored when managementMode is 'managed' or 'existing'). 'discover' (also when omitted): find one, else create one in the stack. 'managed': create it in the stack. 'use-existing': connect to `endpoint` without creating anything.",
"enum": ["discover", "managed", "use-existing"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: enum omits 'external', but #639 is already on next and the scaffold uses database.postgres.management: 'external'. Semantic checks already treat 'external' as “Frigg provisions no DB”.

Scenario: frigg init → frigg validate / frigg build fail on /database/postgres/management with an enum error even though the Aurora builder accepts 'external'. Add 'external' to this enum.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 89f6545. database.postgres.management now accepts discover | managed | use-existing | external (the Aurora builder's values plus external, no default), and the description says what external does. Covered by a validate test that loads the real frigg init template (templates/backend/index.js) and passes --stage prod with no errors, and by a new frigg init scaffold (external db, no vpc) case in the schema contract matrix.

🤖 Generated with Claude Code

seanspeaks and others added 6 commits October 24, 2026 00:36
…e reads

- integrations (and adminScripts, reports) are validated as a serialised
  view in which each class is { Definition }: apps list classes, which
  `type: object` rejected, so every app with an integration failed.
- enums are the values the builders accept: postgres.management
  discover|managed|use-existing, vpc.management discover|create-new|
  use-existing, managementMode adds 'existing'; subnet/NAT management add
  the values the VPC builder acts on.
- adds the keys builders and core read but the schema rejected
  (ownership/external blocks, usePrismaLambdaLayer, scheduler, aws.discovery,
  telemetry, admin, adminScripts, reports, deployment, encryption.disable,
  kmsKeyAlias, keyAlias, lambda.keepNestedNodeModules, ...).
- keys nothing reads are marked deprecated and lose their defaults.
- every remaining default equals what the code does when the key is
  absent; defaults the code does not share are removed
  (fieldLevelEncryptionMethod, managementMode, vpcIsolation, ...).
- applyAppDefinitionDefaults(): the one schema-driven defaults step, loaded
  without Ajv, for core's loader and devtools (ADR-051).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
loadAppDefinition passed `user` through unchanged, so the documented
defaults never applied: a user block without individualUserRequired made
POST /user/login throw, and an app without one got userConfig = null and a
TypeError on every authenticated request. The loader now applies the
schema defaults (applyAppDefinitionDefaults from @friggframework/schemas)
before reading the definition, and maps the deprecated user.password to
usePassword (ADR-051).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and start

`frigg validate [--stage] [--json]` loads index.js the way build does,
validates it against the app-definition schema and runs the checks the
schema cannot express (integration classes and their module definitions,
an enabled database, management settings, encryption, a user config that
can log in, deprecated keys), with JSON pointers and fix hints; it exits 1
on errors.

`frigg build` and `frigg deploy` run the same validation first and stop on
errors (--skip-validate bypasses it, loudly); `frigg start` only warns;
`frigg init` validates the generated index.js instead of a stand-in object.

createFriggInfrastructure applies the schema defaults through the same
step as core. A contract test composes the infrastructure for a matrix of
definitions, records every key the builders read and checks it against the
schema in both directions, and checks that the schema defaults change
nothing in the composed output (ADR-051).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the decision (the schema describes what the runtime reads, its
defaults are applied by one loader step, frigg validate gates build and
deploy, a contract test stops the drift), adds `frigg validate` to the CLI
README, CLAUDE.md and the frigg skill, and corrects the PostgreSQL docs,
which used management values and keys the Aurora builder does not read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One $id pattern for every Frigg JSON Schema,
https://friggframework.org/schemas/<name>/v<major>.<minor>.json, served
from that URL by the website; immutable versions once a stable release
ships them; validation that resolves ids from the installed package and
never the network; $schema usage in module manifests; and the move of the
ten @friggframework/schemas ids to v1.0 with the old ids kept as aliases
for one release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013b2UUFaQ5Lk6TFPqzRr1j7
- app-definition schema: database.postgres.management accepts 'external'
  (the frigg init scaffold, #639), and fieldLevelEncryptionMethod accepts
  'none' (the explicit opt-out, #663) with no default.
- adopterJwt: a jwtConfig without a `secret` key is still an error, but an
  empty value (`secret: process.env.JWT_SECRET` with the variable unset in
  the shell; build and deploy validate before .env is loaded) is now a
  warning that the secret must be set at runtime. The schema no longer
  marks `secret` required, since it cannot tell the two apart.
- unreachable-database: under managementMode 'managed' or 'existing' the
  Aurora builder ignores postgres.management 'use-existing', so the check
  now warns in that case too.
- resolveBackendDir stops at the nearest backend/package.json like core's
  findNearestBackendPackageJson, and reports a missing index.js there
  instead of validating a farther app.
- 'none' is not warned about as plaintext or as a bypassed stage.
- Contract matrix: the frigg init scaffold and an external/none case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013b2UUFaQ5Lk6TFPqzRr1j7
app-definition.schema.json defined adminScripts, reports and admin twice:
once from next (#675) and once from the ADR-051 rewrite. JSON.parse keeps
the last, so the ADR-051 definitions were already the ones in force; the
earlier block is removed and the parsed schema is unchanged.

A new test parses every schema file with a duplicate-key check, so a key
written twice fails CI instead of silently replacing the first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013b2UUFaQ5Lk6TFPqzRr1j7
@seanspeaks

Copy link
Copy Markdown
Contributor Author

Reply to the Frigg 2.0.1 review (Group 2) and the bugbot review. The branch is rebased onto next (#639, #710, #667) and is now at abcf913c. Frigg CI is green: https://github.com/friggframework/frigg/actions/runs/40063591700

Rebase conflicts

Findings

  1. 'external' (blocking): fixed.
  2. 'none', with no default (blocking): fixed.
  3. Stack A/B keys (entities, managementApi.proxy.*, provider: 'netlify', extensions): not added here, on purpose. Each lands with its own stack PR, together with the code that reads it. Adding them now would describe keys that next does not read, and that would break this PR's own contract test (ADR-051 §1, schema ⇔ runtime in both directions).
  4. no-field-encryption warning → error: left as a warning. Fail-closed encryption belongs to feat(core)!: fail closed on field-level encryption in deployed stages #663, which is not merged. On next today, a deployed stage with no method still starts, so an error here would not match the runtime. It becomes a one-line change in checkEncryption when feat(core)!: fail closed on field-level encryption in deployed stages #663 lands. 'none' is already exempt from the warning.
  5. Order with feat(schemas): versioned schema ids, hosted on friggframework.org #705: unchanged. This PR keeps the ADR-051 hosted-schemas amendment, leaves the $ids alone, and should merge before feat(schemas): versioned schema ids, hosted on friggframework.org #705, which will rebase onto it.

Bugbot: the JWT secret, Aurora reachability and nearer-backend findings are fixed, with tests (see the inline replies).

Tests on Node 24.10: schemas 183/183, devtools frigg-cli plus the schema contract test 409 passed / 7 skipped, and npm run test:all passes.

🤖 Generated with Claude Code

seanspeaks added a commit that referenced this pull request Oct 24, 2026
Add frigg validate --module (also frigg validate module) to load Definition
from ./index.js, run validateApiModuleDefinition and checkModuleDefinition,
and exit non-zero with JSON field paths. Supports --json and -v.

Stacked on #701 (api-module schema fields) and includes #666 (ADR-051
frigg validate app mode) so the CLI canary has both.

Co-authored-by: Sean Matthews <sean.matthews@lefthook.co>
@sonarqubecloud

Copy link
Copy Markdown

@seanspeaks

Copy link
Copy Markdown
Contributor Author

Review follow-ups, rebased onto next (after #639, #710 and #667). CI is green at abcf913c.

Fixed in 89f65453:

  • Blocking: database.postgres.management now accepts 'external' (the scaffold value from fix: lazy-load scheduler AWS SDK in core and repair frigg init scaffolding #639), and fieldLevelEncryptionMethod accepts 'none', with no default.
  • JWT secret check: a missing secret key is still an error. An empty value, i.e. process.env.JWT_SECRET unset in the shell because build and deploy validate before .env loads, is now a warning that the secret must be set at runtime.
  • Unreachable database: the check now warns under managementMode managed/existing too, where the Aurora builder ignores use-existing.
  • resolveBackendDir stops at the nearest backend/package.json, matching core, and reports a missing index.js there instead of validating a farther app.

abcf913c: drops the duplicate adminScripts/reports/admin keys, the same fix as #702.

Deferred, deliberately:

🤖 Generated with Claude Code

@seanspeaks
seanspeaks merged commit a25325a into next Oct 24, 2026
18 checks passed
seanspeaks added a commit that referenced this pull request Oct 24, 2026
Add frigg validate --module (also frigg validate module) to load Definition
from ./index.js, run validateApiModuleDefinition and checkModuleDefinition,
and exit non-zero with JSON field paths. Supports --json and -v.

Stacked on #701 (api-module schema fields) and includes #666 (ADR-051
frigg validate app mode) so the CLI canary has both.

Co-authored-by: Sean Matthews <sean.matthews@lefthook.co>
@seanspeaks

Copy link
Copy Markdown
Contributor Author

🚀 PR was released in v2.0.0-next.124 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease This change is available in a prerelease. release Create a release when this pr is merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant