Skip to content

Replace instruction arguments with a single data type node - #142

Merged
lorisleiva merged 3 commits into
mainfrom
instruction-data-type-node
Sep 2, 2026
Merged

Replace instruction arguments with a single data type node#142
lorisleiva merged 3 commits into
mainfrom
instruction-data-type-node

Conversation

@lorisleiva

@lorisleiva lorisleiva commented Sep 2, 2026

Copy link
Copy Markdown
Member

This PR implements #131, the last of the data-unification wave: instructions describe their serialised data with one optional type node, exactly like accounts (#137) and events.

data?: TypeNode replaces arguments/extraArguments; instructionArgumentNode is deleted (it was structFieldTypeNode plus argument-only machinery). Absent data = no bytes. Contextual defaults use inject/provide: field defaults carry injectedValueNodes, the instruction's provides supplies contextual values. Two rules are now normative on injectedValueNode/providedNode/provides: lexical scoping (nearest enclosing provides wins, shadowing allowed — relevant for subInstructions) and the self-contained rule (every key resolves in scope or has a fallback), which makes "can this instruction be built from its IDL alone?" statically checkable.

Resolvers are gone, plugins take over with graceful degradation: refine a declared shape (remaining-accounts input + codama.jsResolver plugin) or mark an honest absence (account with no defaultValue + plugin) — the standard never carries a fake value. instructionRemainingAccountsNode now declares a named client input via identifier (same model as instructionAccountNode); the byte-delta and condition unions lose their resolver members; resolverDependency and instructionRemainingAccountsValue unions are deleted.

instructionArgumentLinkNode removed: link nodes point at nodes on the tree; values inside a type node are reached by path expressions (#141) — a different paradigm. An "instruction + path" definition-link can return additively if a need appears.

Follow-up from review: argumentValueNodedataValueNode and accountFieldValueNodeaccountDataValueNode — with arguments gone as a concept, both names describe what the nodes do (a value at a path within anchored data), mirroring the data attributes and interpolation roots. And structFieldTypeNode.defaultValueStrategy adopts the soft rule the deleted instructionArgumentNode carried: only relevant when defaultValue is set (a strategy without a default is meaningless); when absent, optional is assumed — so the lens needs no strategy synthesis.

Counts: 90 nodes (−3), 31 unions (−2). Examples rewritten throughout (instruction walkthroughs, all four contextual-value showcases now model inject/provide, remaining-accounts plugin form, display/discriminator hosts).

Upgrade lens notes (for upgradeV1ToV2): arguments → struct fields one-to-one (plain defaults carry over with their strategy as-is; contextual defaults become provide/inject pairs with the field identifier as key); empty arguments → omit data; extraArguments/resolvers → plugins under a codama-owned namespace; remainingAccounts.value: argumentValueNode(x)identifier: x; resolver-valued byte deltas with no expressible value → drop the node, attach the plugin to the instruction; kind renames argumentValueNodedataValueNode (its namepath) and accountFieldValueNodeaccountDataValueNode (its fieldpath).

Closes #131 (manual close — default branch is 1.x). Part of #102.

Instructions describe their serialised data with one optional type node, exactly like accounts and events: data replaces arguments and extraArguments, instructionArgumentNode is deleted, and contextual defaults flow through the inject/provide pattern with lexical scoping and the self-contained rule made normative. Resolvers are removed in favour of namespaced plugins with graceful degradation — refine a declared shape or mark an honest absence, never a fake value. instructionRemainingAccountsNode declares a named client input via identifier, the byte-delta and condition unions lose their resolver members, and instructionArgumentLinkNode is removed since values inside a type node are reached by path expressions, not links.
@lorisleiva

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex 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.

This PR completes the data-unification wave (#131, part of #102): instructionNode.arguments/extraArguments are replaced by a single optional data: TypeNode, instructionArgumentNode and its satellite machinery (instructionArgumentLinkNode, resolverValueNode, resolverDependency, instructionRemainingAccountsValue) are deleted, contextual defaults move to the inject/provide pattern with newly-normative lexical-scoping and self-contained rules, and instructionRemainingAccountsNode becomes a named client input with plugin-based resolution. Node/union counts, tests, examples, and generated artifacts (spec.json, docs/) all line up — schema.json is untouched, which is expected since it's still the placeholder.

The design is coherent and the changeset does a good job spelling out the upgrade lens. major bump matches the CONTRIBUTING semver policy. One consistency issue worth resolving before (or right after) merge, plus a couple of non-blocking notes:

1. defaultValueStrategy semantics got stricter in the migration (inline comment). The deleted instructionArgumentNode documented defaultValueStrategy as "when absent, optional is assumed", but structFieldTypeNode says it's "Required when defaultValue is set" — and most of the converted examples in this PR set a defaultValue without a strategy. Either the examples or the structFieldTypeNode doc needs to give; details inline.

2. argumentValueNode naming (non-blocking question). With arguments gone as a concept, argumentValueNode now "refers to a value within the data of the surrounding instruction" via a path — the name is the last remnant of the old model. Since this wave is already maximally breaking, was a rename (e.g. dataValueNode) considered? Fine to keep for familiarity/ecosystem continuity, just flagging that the window to rename cheaply is now.

3. Changeset remaining-accounts snippet (cosmetic). The second diff block in the changeset starts from the new positional-identifier signature (instructionRemainingAccountsNode('authorities', {) and only adds plugins — a reader migrating from v1 sees a "before" state that never existed. Since the identifier change is described in its own bullet this is arguably fine as a staged illustration, but worth a second look. Also trivial: that snippet uses dependsOn: ['data.multisig'] while the docs example uses data.hasMultisig — different examples, just noting in case they were meant to match.

For subsequent reviewers:

  • The normative additions (lexical scoping on providedNode/injectedValueNode, the self-contained rule) are stated consistently in three places (InjectedValueNode.ts, ProvidedNode.ts, InstructionNode.ts provides docs) — worth a read to confirm the wording can't be interpreted divergently. The providedNode "transparent pipe / family check at the injection point" model composes cleanly with the new rules.
  • Verify CI's artifact-lockstep check passed — docs/ and spec.json are machine output here, so any review nit on examples should be fixed in src/**/*.examples.ts and regenerated.
  • The new tests in tests/spec.test.ts pin the removals and the new shapes well (including union member lists and attribute ordering on instructionRemainingAccountsNode).

Comment thread src/spec/nodes/InstructionNode.examples.ts
argumentValueNode becomes dataValueNode and accountFieldValueNode becomes accountDataValueNode — with arguments gone as a concept, both names describe what the nodes do: refer to a value at a path within anchored data, mirroring the data attributes and interpolation roots. The structFieldTypeNode defaultValueStrategy docs adopt the soft rule the deleted instructionArgumentNode carried (only relevant when defaultValue is set; absent means optional), resolving the inconsistency the migration had silently promoted. The changeset's plugin illustration becomes a plain v2 snippet and gains the rename bullet.

@trevor-cortex trevor-cortex 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.

Re-review after the follow-up commits. All three points from my previous review are resolved:

  1. defaultValueStrategy semanticsstructFieldTypeNode now adopts the soft rule the deleted instructionArgumentNode carried ("Only relevant when defaultValue is set … When absent, optional is assumed"), consistently across source, spec.json, and the generated doc. The converted examples that set a contextual defaultValue without a strategy are now valid as written, and the lens needs no strategy synthesis. ✅
  2. NamingargumentValueNodedataValueNode and (bonus) accountFieldValueNodeaccountDataValueNode. Both names now describe the path-into-anchored-data semantics, and the rename is propagated everywhere I checked: all five affected unions, examples, docs, tests, and the changeset's upgrade notes. ✅
  3. Changeset snippet — the remaining-accounts block is now a plain // v2: illustration rather than a diff from a state that never existed, and dependsOn: ['data.hasMultisig'] now matches the docs example. ✅

Two trivial leftovers, neither blocking:

  • Grammar typo in instructionByteDeltaValueUnion docs: "an dataValueNode" → "a dataValueNode" (inline comment). Fix in InstructionNodeUnions.ts and regenerate — it currently flows into spec.json and docs/InstructionByteDeltaValue.md.
  • In src/spec/nodes/contextualValueNodes/AccountDataValueNode.examples.ts (~line 5, unchanged so not anchorable), the first example title still reads "Create an account field value node from an account name and a field path" — stale after the accountDataValueNode rename; also surfaces as the heading in the generated docs/contextualValueNodes/AccountDataValueNode.md.

For subsequent reviewers: the new tests/spec.test.ts blocks pin the removals, the union member lists, and the instructionRemainingAccountsNode identifier-first shape precisely — good regression coverage for the whole wave. Nothing else changed my prior assessment; the normative scoping wording remains consistent across InjectedValueNode, ProvidedNode, and InstructionNode.provides.

Comment thread src/spec/nodes/InstructionNodeUnions.ts Outdated
The instructionByteDeltaValue union docs read 'a dataValueNode' rather than the article the mechanical rename left behind, and the accountDataValueNode example title drops the stale 'account field value node' wording. Both flow into spec.json and the generated docs.
@lorisleiva
lorisleiva merged commit baf8510 into main Sep 2, 2026
3 checks passed
@lorisleiva
lorisleiva deleted the instruction-data-type-node branch September 2, 2026 09:12
@lorisleiva lorisleiva linked an issue Sep 2, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v2] Replace instruction arguments with a single data type node

2 participants