Skip to content

RPCN - Support for optional fields#2560

Merged
SpicyPete merged 21 commits into
masterfrom
rpcn/optional-field-support
Jul 21, 2026
Merged

RPCN - Support for optional fields#2560
SpicyPete merged 21 commits into
masterfrom
rpcn/optional-field-support

Conversation

@SpicyPete

@SpicyPete SpicyPete commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

RPCN: correct optional-field support, lint-anchored editing, and topic selection in the visual editor

Background

cloudv2 #27724 (merged 2026-07-14) bumped benthos/v4 4.53 → 4.73 in redpanda-connect-api, which re-serializes the Connect config schema before serving it. Benthos only began emitting per-field is_optional / is_advanced / is_secret / is_deprecated at v4.59, so until that bump the console never received them and the frontend compensated with a defensive heuristic ladder (checkRequired) that guessed required-ness from proto flags and name patterns.

The two RPCs still differ in what they carry (verified against cloudv2 source and the embedded RPCN 4.100 schema):

Signal ListComponents (proto FieldSpec) GetPipelineServiceConfigSchema (raw JSON)
optional / advanced / deprecated
is_secret ❌ no field in the proto at all
defaults ⚠️ string defaults only — int/bool/collection defaults arrive as '' ✅ exact required arrays computed by the backend with full default knowledge

So required/secret truth must come from the raw JSON path; the proto alone cannot distinguish "required" from "default lost in serialization".

This branch adopts those restored signals end-to-end, then fixes the editing friction they exposed: lint errors referencing invisible line numbers, edits that only saved on node-deselect, and topic fields that were plain text boxes.

What's changed

Schema-signal enrichment

  • New enrichComponentsWithConfigSchema (utils/schema-enrichment.ts) stamps requiredBySchema and secret onto proto-derived specs by walking the raw config-schema JSON (object/array/map wrappers unwrapped so JSON-Schema nodes line up with proto children).
  • The enrichment gate keys on the presence of required arrays (long-standing in the JSON) rather than is_optional (4.59+ only), so mid-generation dataplanes that serve required arrays without per-field flags still get correct required/optional grouping.
  • Curated LINT_REQUIRED_FIELDS overrides for requirements the schema can't express (input:redpanda topics/consumer_group are .Optional() in the engine but enforced conditionally by lint) — the form shows them as required instead of contradicting the linter.
  • Degrades gracefully: a schema that is missing, unparsable, or pre-enrichment leaves specs unstamped.
  • New useEnrichedComponents hook consolidates the recipe for all three YAML-generating consumers (pipeline editor, onboarding wizard, template form panel) and keeps components empty until the schema query settles, so YAML is never generated from un-enriched specs. Display-only consumers (gallery grid, tiles, command palette) intentionally stay on plain parseSchema.

Required-field detection

  • checkRequired is now: deprecated guard → trust requiredBySchema stamp → documented degraded proto ladder (for pre-4.59 dataplanes only). Dead heuristic branches deleted.
  • generateDefaultValue no longer zero-fills fields whose default was dropped by the proto — they're omitted so engine defaults apply (previously e.g. auto_replay_nacks defaulting to true was rendered as false).
  • Field-less components (drop, etc.) generate {} instead of nothing, so they render as canvas nodes and produce valid YAML.

Lint errors anchored to form fields

  • New lint-field-mapping.ts maps a node's lint hints onto the inspector's form fields via three tiers: explicit field x tokens in the message, the hint's line number resolved through the parsed document's node ranges (deepest field wins, walking up to the nearest rendered ancestor), and loose name mentions (consumer group finds consumer_group).
  • Errors render under the field they're about — including group-level errors like expected object value, got !!null (batching) inside their section — and are hidden while the field is dirty so stale complaints don't fight live edits.
  • Collapsed groups (including Advanced) force-open when new errors anchor inside them; a manual collapse sticks until the error set actually changes.
  • Hints that anchor to nothing the form renders stay in the banner, labeled with the field path instead of a line number the Visual view can't show.
  • Empty configs skip the server lint round-trip entirely (it could only say "config_yaml: value is required").

Field-level commits

  • Edits now flush to the YAML on field blur, ⌘⏎, or an explicit Apply affordance in a pending-edits footer — not just on node-deselect — so the canvas, structure tree, and lint react while you're still in the field.
  • Commits keep the form mounted (focus and scroll survive); external YAML writes re-sync a clean form in place and rebuild pending drafts from the fresh value so nothing clobbers concurrent writes (e.g. a topic created mid-edit).
  • Malformed numeric edits and invalid raw-YAML sections are excluded from commits and visibly stay dirty instead of silently reverting.
  • Undo/redo remounts the form so stale edit state can't resurrect undone values.

Topic selection on topic fields

  • topic/topics fields on Redpanda-family components (kafka, kafka_franz, redpanda* — same gate as the template flow) get cluster-aware pickers: a creatable combobox on scalar fields (existing topics, free text for interpolations/topic:partition) and an append-only picker under the topics textarea that preserves free-text lines.
  • A "Create new topic" affordance opens the Add-topic dialog; the created topic is patched into the component's YAML — appending to an existing topics list in place (dedup, comments and non-string entries preserved).
  • Foreign connectors' topic fields (mqtt, gcp_pubsub, pulsar, …) stay plain inputs — their topics are not cluster topics.

Editor polish

  • Minimap no longer clips flow edges after adding nodes (world bounds union the viewport).
  • Unsaved-state indicators (canvas chip, node dots, structure tree, header, inspector footer) switched from warning-orange to the informative tone so they read as state, not a second tier of error.
  • Overlong default-value hints truncate with a title tooltip; "Optional" grouping is dropped when a component has no required fields (everything rendered plainly).
  • Create-topic dialog steps in the template gallery and editor sized to their content (no dead space / floating buttons); add-topic step describes exactly what the current mode allows.

Template fixes (drift caught by this work)

Four templates referenced components/fields the engine never shipped or has since renamed — invisible because the test snapshot was frozen at RPCN 4.65 with the components allowlisted as known-missing:

  • dynamodb-cdc: aws_dynamodb_streamaws_dynamodb_cdc (tables list, start_from/snapshot_mode)
  • oracle-cdc: oracle_cdcoracledb_cdc (connection_string, include)
  • sqlserver-cdc: sql_server_cdcmicrosoft_sql_server_cdc (connection_string, include)
  • iceberg: catalog_uri/warehousecatalog.url / optional catalog.warehouse — this one was user-visible broken: the component exists, so the template showed in the gallery and its YAML failed dataplane lint on every deploy
  • Refreshing assets/rp-connect-schema-full.json 4.65 → RPCN 4.100 (the cloud schema cloudv2 actually serves) also caught redpanda_migrator having dropped its batching field; removed from the mirror template.
  • KNOWN_MISSING_COMPONENTS is now empty — every template is fully validated by the regression tests (schemaField paths resolve, stitched YAML contains only known fields, required fields always emitted).

Docs links module

  • All docs.redpanda.com URLs across the app now live in src/utils/docs-links.ts (~25 call sites migrated). Every entry is canonical against the restructured docs site (direct 200s, no redirects).

Behavior changes to be aware of

  • The redpanda input's seed_brokers is no longer marked required (genuinely .Optional() in the engine); topics/consumer_group remain required via the curated lint override. The wizard still auto-populates them.
  • Optional fields whose defaults don't survive the proto are omitted from starter YAML instead of being zero-filled.
  • Hardcoded template slot required values were audited against the served schema: templates stricter than the engine (e.g. S3 region/credentials) are deliberate curation; the one looser case (postgres-cdc slot_name) is safe because defaultWhenBlank always supplies a value.
  • Creating a topic from the editor now appends to an existing topics list instead of replacing it.

SpicyPete and others added 4 commits July 15, 2026 07:35
The v4.53→v4.73 benthos bump in cloudv2 (PR #27724) re-serialized the
Connect config schema, restoring per-field is_optional/is_advanced/
is_secret/is_deprecated. Wire those signals through:

- enrichComponentsWithConfigSchema stamps requiredBySchema/secret onto
  proto specs from the raw GetPipelineServiceConfigSchema JSON (the
  ListComponents proto still drops non-string defaults and has no
  secret field at all).
- checkRequired prefers the stamp; the proto-flag ladder remains as a
  documented degraded fallback for pre-4.59 dataplanes.
- generateDefaultValue stops zero-filling fields whose default was lost
  in proto serialization, so engine defaults apply.
- Deprecated fields are excluded from forms and generated configs;
  secret masking is the schema flag unioned with the name heuristic.
- useEnrichedComponents consolidates the enrichment recipe for the
  pipeline editor, onboarding wizard, and template form panel, gating
  spec consumption until the config schema settles.
- Ground-truth fixtures captured from the served schema (RPCN 4.100.0)
  back integration tests for kafka/generate/http_client/aws_s3/mapping/
  chunker required-vs-optional behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four templates drifted from the components Connect actually ships, and
the drift was invisible because the schema snapshot the regression
tests validate against was frozen at RPCN 4.65 with the affected
components allowlisted as known-missing:

- dynamodb-cdc: aws_dynamodb_stream never shipped; rebuilt on
  aws_dynamodb_cdc (tables list, start_from/snapshot_mode knobs).
- oracle-cdc: oracle_cdc → oracledb_cdc (connection_string/include).
- sqlserver-cdc: sql_server_cdc → microsoft_sql_server_cdc
  (connection_string/include).
- iceberg: catalog_uri/warehouse moved to catalog.url and the
  now-optional catalog.warehouse. This one was user-visible broken —
  the component exists, so the template showed in the gallery and its
  YAML failed dataplane lint on deploy.

Refresh the snapshot to the RPCN 4.100 cloud schema cloudv2 actually
serves, which also surfaced that redpanda_migrator dropped its
batching field (removed from the mirror template). Empty the
KNOWN_MISSING_COMPONENTS allowlist: every template is now fully
validated against the snapshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SpicyPete
SpicyPete requested review from a team, eblairmckee and malinskibeniamin July 15, 2026 17:31
@SpicyPete SpicyPete self-assigned this Jul 15, 2026
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Clean — no registry drift, off-token colours, or ad-hoc classes

App: frontend · Scope: diff vs origin/master · Files: 77

Count
⚠️ Outdated registry components 0
🛠 Locally-modified components 0
❓ Unknown to registry 0
🎨 Off-token palette colours 0
🔢 Ad-hoc utility classes 0

Generated by lookout audit-changes.

…n fix

The isSchemaLoading gate in useEnrichedComponents held specs empty until
the multi-MB GetPipelineServiceConfigSchema settled, while pickers and
forms rendered from the instantly-available proto componentList. Every
confirmed regression traced to that window: silent no-op connector
inserts, the serverless create timeout firing then a late resolution
clobbering user-typed YAML, wizard YAML regeneration failing on
deep-linked steps, and view mode rendering schema-less (forever, on a
hung request). Specs are now available as soon as ListComponents
resolves and re-emit with enrichment stamps when the schema settles —
the interim proto-flag fallback is the same degraded mode used for
pre-4.59 dataplanes. The template form's one-shot defaults reset still
waits for full fidelity via isLoading, and enrichment results are
memoized module-wide instead of recomputed per mount.

Separately, harden the serverless create flow against the pre-existing
race: after the init timeout has surfaced the editor, a late resolution
no longer replaces the buffer.

Fix a deterministic generation bug: components whose root config is an
array of objects (switch, group_by) can never be "required", so the
required-only collection seeding emitted undefined → `switch:` YAML
null, which fails lint. Component roots now always produce a
type-correct value.

Cleanups from the same review: fold the deprecated-field exclusion into
isFormField, deduplicate the SASL array generation into
generateObjectArrayValue, and share a findConnectComponent lookup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SpicyPete
SpicyPete marked this pull request as draft July 15, 2026 20:31
@SpicyPete
SpicyPete marked this pull request as ready for review July 16, 2026 17:23
@SpicyPete

Copy link
Copy Markdown
Contributor Author

@claude review

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @SpicyPete's task in 3m 24s —— View job


Code Review — RPCN optional field support

  • Read core logic (schema-enrichment.ts, schema.ts, use-enriched-components.ts)
  • Review visual editor form changes
  • Review template fixes & deploy logic
  • Review tests
  • Post consolidated review

Reviewed the full diff against origin/master (skimming the 42k-line rp-connect-schema-full.json regen, reading the logic files closely). This is a careful, well-documented change — the layering (proto specs → JSON-schema enrichment → checkRequired) is clean, degradation paths are explicit, and the test coverage (schema-enrichment.test.ts, template-form-schema.test.tsx, ground-truth fixtures) is genuinely strong: it pins the exact proto-loss cases the design hinges on (checkpoint_limit int default, auto_replay_nacks bool, empty-string consumer_group, is_secret on root_cas). Nice work.

Findings below are all minor / non-blocking.

1. Template-form default reset now depends on the config-schema query settling

template-form-panel.tsx:174-184 — the one-shot form.reset(defaultValues) is now gated on isComponentsLoading (which is isComponentListLoading || isSchemaLoading). Previously it fired as soon as componentListResponse arrived. useEnrichedComponents already returns a non-empty components array the moment ListComponents resolves (enrichment degrades gracefully when configSchema is undefined), so if the config-schema query stalls indefinitely without erroring (React Query keeps isLoading: true), the schema-driven default reset never fires. On error isLoading flips to false, so only an indefinite hang is affected, and the form remains usable with its first-render defaults — impact is low. Worth a comment noting the intentional trade-off (waiting for schema-exact required-ness at the cost of resilience to a hung schema query), or a fallback that resets on componentList alone after a timeout.

2. List-entry scalars are force-set to Scalar.PLAIN

template-deploy.ts:116-118 — expanded list entries get scalar.type = Scalar.PLAIN. For the current call sites (table identifiers, validated by entryPattern to ^[a-zA-Z0-9_$]+$) this is safe, but a plain scalar bypasses the quoting the YAML serializer would otherwise apply, so a future list slot without a strict pattern could emit an entry like 123 or true that the engine parses as int/bool instead of string. Consider dropping the explicit PLAIN and letting the serializer decide, or documenting that list slots must be pattern-constrained to bare strings.

3. Module-level enrichment cache is never invalidated

use-enriched-components.ts:24-37lastEnrichment is a module singleton holding references to the (multi-MB) component list, config-schema string, and cloned spec tree for the lifetime of the session. It's a single slot, so a new componentList/configSchema identity replaces it, and the memo rationale is sound — just flagging that these large objects stay resident even after all consumers unmount. Fine to leave; noting for awareness.

4. PR-description wording is stale vs. the code (doc only)

The description says useEnrichedComponents "keeps components empty until the schema query settles, so YAML is never generated from un-enriched specs." The actual code (and the hook's own doc comment) does the opposite — components is populated as soon as ListComponents resolves and is re-emitted enriched once the schema settles, with checkRequired falling back to proto flags in between. The code behavior is the better one; just update the PR body so reviewers aren't misled about the invariant.

Nits

  • template-schema.ts:51checkRequired(field as unknown as RawFieldSpec): the double cast is only needed because resolveFieldByPath is typed to return the proto FieldSpec while the runtime value is the stamped RawFieldSpec. Consider having resolveFieldByPath return RawFieldSpec for enriched trees to drop the as unknown.
  • template-deploy.ts:84-92 — the Pair visitor calls substituteScalar purely to detect a null (drop) result, then the Scalar visitor recomputes it to apply the substitution. Harmless on small template docs, just slightly redundant.

Not verified

I did not run bun run type:check && bun run lint && bun run test in this review (would require a full dependency install). Please make sure those pass — in particular the removed useListComponentsQuery/parseSchema imports in onboarding-wizard.tsx and pipeline/index.tsx don't leave an unused useMemo or import behind for Biome.
· branch rpcn/optional-field-support

@SpicyPete

Copy link
Copy Markdown
Contributor Author

@claude review

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Claude finished @SpicyPete's task in 3m 32s —— View job


Code Review — RPCN optional field support (re-review)

  • Read core logic (schema-enrichment.ts, schema.ts, use-enriched-components.ts)
  • Review visual editor form + lint mapping (node-config-form.tsx, node-inspector.tsx, lint-field-mapping.ts)
  • Review template fixes & deploy logic (template-deploy.ts, template-form-panel.tsx, yaml.ts)
  • Check whether prior review feedback was addressed
  • Post consolidated review

Re-reviewed against the current branch tip, closely reading the logic files (schema enrichment/checkRequired, the field-commit form, lint anchoring, YAML mutation helpers). The design remains clean and the layering (proto → JSON-schema enrichment → checkRequired with a documented degraded ladder) holds up well. Every finding from the 2026-07-17 review has been addressed:

  • Add readiness & liveness check #1 (schema-query trade-off)template-form-panel.tsx:176-179 now documents the "wait for schema-exact required-ness at the cost of resilience to a hung query" trade-off explicitly. ✅
  • Add prometheus metrics #2 (Scalar.PLAIN on list entries)template-deploy.ts:116-118 dropped the forced PLAIN; it now uses new Scalar(entry) with a comment noting the serializer quotes non-string-parsing entries. ✅
  • Nit (double cast in template-schema.ts:51) — now a plain checkRequired(field); resolveFieldByPath returns RawFieldSpec. ✅
  • "Not verified" importsonboarding-wizard.tsx / pipeline/index.tsx are clean; useMemo is still legitimately used elsewhere, no dangling useListComponentsQuery/parseSchema. ✅

Findings below are all minor / non-blocking — nothing new that rises above the last round.

1. Container onBlur commits fire when focus moves into portaled popovers

node-config-form.tsx:1152-1154 — the passive onBlur={commitNow} on the form container flushes the draft on any bubbled focusout. Registry Select/Combobox render their listbox in a portal outside this <div>, so opening one moves focus out and triggers an early commitNow mid-interaction. It's not a correctness bug (the commit is guarded by isDirty and re-syncs the form in place), but it can produce an extra YAML round-trip before the user has actually picked a value, and then another commit once the selection lands. Worth confirming the intended UX; if undesired, gating the commit on !e.currentTarget.contains(e.relatedTarget) would ignore focus moving to your own portaled content. Low impact.

2. checkRequired called without ancestorOptional in display paths

node-config-form.tsxFieldLabel (535), ScalarControl (711), fieldGroupKey (943) call checkRequired(spec) with no ancestorOptional, whereas generateDefaultValue threads it through (schema.ts:592). For enriched specs this is moot (requiredBySchema short-circuits first), but on a degraded (pre-4.59) dataplane a non-scalar field under an optional ancestor can render with a * / land in the "required" bucket even though generation intentionally omitted it — a cosmetic form/YAML mismatch in that fallback mode only. Given enrichment is now the norm, this is a nit; a short comment noting the display path deliberately ignores ancestor optionality would prevent a future "why does this disagree?".

3. Module-level enrichment cache (carried over, still fine)

use-enriched-components.ts:24-37lastEnrichment remains a session-lifetime singleton holding the multi-MB component list + cloned spec tree. Single-slot, replaced on new query identities, so no leak growth — re-noting only for awareness, as last time.

Not verified

I could not run bun run type:check && bun run lint && bun run test — this is a fresh checkout with no node_modules and I don't have install access in this job. The prior review round didn't run them either. Please confirm CI is green (in particular the .test.tsx integration suites touched here) before merge, since the field-commit form and lint-anchoring logic lean heavily on their new tests.

Overall: solid, careful work with genuinely strong test coverage (ground-truth fixtures pinning the proto-loss cases, lint-field-mapping.test.tsx, visual-editor-panel.test.tsx). No blockers from me.
· branch rpcn/optional-field-support

Comment on lines +297 to +301
{
new: 'Enter a name for the new topic.',
existing: 'Choose an existing topic to read or write data from.',
both: 'Choose an existing topic to read or write data from, or create a new topic.',
}[selectionMode]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: why not hoist? kind of weird to see an object in the render body like this

Comment on lines -64 to +63
const { data: componentListResponse, isLoading: isComponentListLoading } = useListComponentsQuery();
const components = useMemo(
() => (componentListResponse?.components ? parseSchema(componentListResponse.components) : []),
[componentListResponse]
);
const { components, componentList, isLoading: isComponentListLoading } = useEnrichedComponents();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice

Comment on lines +17 to +21
/**
* Anchors a node's lint hints to the inspector form fields they belong to, so an error renders
* under the field (or inside the group section) it's about instead of as "(line 12)" in a banner
* the user has to decode. See `resolveHint` for the anchoring tiers.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

orphaned?

* the user has to decode. See `resolveHint` for the anchoring tiers.
*/

/** Field/group key (`path.join('/')`) → the lint messages anchored to it. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i'm gonna guess something weird happened wrt comments in this file?


function coerceArrayItems(spec: RawFieldSpec, text: string): unknown[] {
const lines = text
const arrayFieldLines = (text: string): string[] =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: for the functions in this file I wonder if they already exist in a util file. some look a little familiar

Comment on lines +583 to +595
// The "Create new topic" affordance next to topic pickers; opens the Add-topic dialog. The created
// topic is written into the YAML, which re-syncs into the (clean) form.
const CreateTopicButton = () => {
const { onCreateTopic } = useContext(ResourceFieldContext);
if (!onCreateTopic) {
return null;
}
return (
<Button className="self-start" onClick={onCreateTopic} size="sm" type="button" variant="outline">
<Plus className="size-3.5" /> Create new topic
</Button>
);
};

@eblairmckee eblairmckee Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[code smell]: any particular reason why you're using context to pass ar[ound event handlers like this?

Comment on lines +658 to +661
// A select can't be cleared by deleting text the way inputs can, so without an explicit unset
// item a value (often seeded by the insert template) is stuck in the YAML forever. Maps to ''
// which the save path treats as "remove the key". Sentinel because '' can't be an item value.
const UNSET_OPTION = '__unset__';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[nit]: a lot of these comments discuss business logic but seem to live in the wrong places... like a sentinel like this is self-explanatory based on the constant name... hwo it's used should not be relevant here, rather when rendering it if there are nuances to comment about that would imrpove future contributors' devex.. then comment verbosely

Comment on lines +1256 to +1260
{invalid ? (
<div className="text-body-sm text-destructive">
Invalid YAML — these settings won't be saved until fixed.
</div>
) : null}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice

'sql_server_cdc',
'iceberg',
]);
export const KNOWN_MISSING_COMPONENTS: ReadonlySet<string> = new Set([]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[minor]: remove if orphaned? or no use in future?

Comment on lines +7 to +10
// Stamped by enrichComponentsWithConfigSchema from the raw config schema JSON, which carries
// signals the proto FieldSpec drops (is_secret entirely; required-ness for fields whose
// non-string default is lost in serialization). Both stay undefined when the dataplane's
// schema predates flag serialization (benthos < 4.59) — consumers then fall back to proto flags.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

wrong line?

Comment on lines +115 to +120
// Stamps from the raw schema; returns the input untouched when the schema is missing, unparsable,
// or carries no signal. `required` arrays are the load-bearing signal and benthos has emitted them
// (computed knowing every default) far longer than the per-field flags (`is_optional`/`is_secret`,
// 4.59+) — so a mid-generation document still fixes required-ness even though it can't flag
// secrets (the name heuristic still covers those). A document with no `required` keys at all
// predates the arrays; stamping `requiredBySchema: false` everywhere from it would be a lie.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[nit]: would be nice to stick to jsdoc comments

@eblairmckee eblairmckee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm. left some minor nits that can be addressed in a follow

@SpicyPete
SpicyPete enabled auto-merge (squash) July 21, 2026 22:36
@SpicyPete
SpicyPete merged commit c431654 into master Jul 21, 2026
17 checks passed
@SpicyPete
SpicyPete deleted the rpcn/optional-field-support branch July 21, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants