Skip to content

feat(CLI): Typed rendering for call output#2179

Draft
marijamijailovic wants to merge 4 commits into
0xMiden:mainfrom
walnuthq:pr/call-typed-rendering
Draft

feat(CLI): Typed rendering for call output#2179
marijamijailovic wants to merge 4 commits into
0xMiden:mainfrom
walnuthq:pr/call-typed-rendering

Conversation

@marijamijailovic

@marijamijailovic marijamijailovic commented May 13, 2026

Copy link
Copy Markdown
Contributor

This PR extends the call command: it now looks for type info in the package's debug sections, and if it's there it prints a typed signature and decodes the result (for now account-id, bool, word, structs) instead of showing raw felts. If there's no type info it just prints the raw stack like before, so nothing changes for existing packages.

It reads two sections from the package - DEBUG_FUNCTIONS (the procedure name, its params and signature) and DEBUG_TYPES (the type definitions). With those, the args you pass on the CLI get encoded according to the parameter types (e.g. an account-id hex token expands to two felts, a word to four), and the returned felts get decoded back into the same types for printing.

Example, you'll get something like:

Signature: take-account-id(id: account-id) -> account-id
Result: account-id(0xa591009a3022e800788f9ed177dcdb)

Closes #2098

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

Hi @igamigo! Quick question about the changelog for this PR: there is already an entry saying “Added miden-cli call command for invoking account procedures directly from the CLI”. Should we reuse that existing entry for this PR as well, or to add a new changelog entry that explicitly mentions typed rendering for the call output?

@igamigo igamigo changed the title Typed rendering for call output feat(CLI): Typed rendering for call output May 15, 2026
@igamigo igamigo requested review from Keinberger and igamigo May 15, 2026 14:42
@igamigo

igamigo commented May 15, 2026

Copy link
Copy Markdown
Collaborator

Hi @igamigo! Quick question about the changelog for this PR: there is already an entry saying “Added miden-cli call command for invoking account procedures directly from the CLI”. Should we reuse that existing entry for this PR as well, or to add a new changelog entry that explicitly mentions typed rendering for the call output?

I think we can reuse it and add this PR link to the same entry since it's very related, but not a strong opinion so if you prefer otherwise that's fine too.

@igamigo

igamigo commented May 15, 2026

Copy link
Copy Markdown
Collaborator

Can you expand the PR description explaining the design a bit and how it relates to the issue? And/or maybe add some examples of how this renders. I know there is some context there already but the actual implementation and the scope has not been fully discussed AFAICT

@igamigo igamigo requested a review from JereSalo May 19, 2026 19:07
@marijamijailovic

Copy link
Copy Markdown
Contributor Author

Hi @igamigo thanks for your comment! I updated the PR description.

There's one thing I just realized, and because of it I'm turning this into a draft: I accidentally tested it against a compiler branch that isn't merged into next yet, this branch is the one that stores the high-level types inside the debug sections. My bad, I didn't check that my cargo miden build was pointing at my local compiler on that branch instead of the official one from crates. So on packages built with the official compiler the debug sections are there, but it does not saves the functions signature with high level type, and the typed path won't trigger - it'll just fall back to raw.

@marijamijailovic marijamijailovic marked this pull request as draft May 25, 2026 12:10

@Keinberger Keinberger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve direction, but let's hold non-draft on the items below. Thanks @marijamijailovic!

Phase 1 from #2098 is covered.

Before flipping to non-draft though:

  1. Test coverage. See inline on package_types/mod.rsbool, u32, u64 are priority types from #2098 but have no unit test.
  2. Compiler dependency. See inline on tests/cli.rs — the typed path only triggers because the test stubs the debug sections in by hand.
  3. CHANGELOG. The check is still red and I can't drop an inline since CHANGELOG.md isn't in the diff. Per your thread with @igamigo, extending the existing #1943 entry sounds right, something like:

Added miden-cli call command for invoking account procedures directly from the CLI (#1943), with typed argument encoding and typed result rendering when the package carries debug type info (#2179).

Follow-ups to track separately, not here:

  • Phase 2 SchemaType rendering.
  • Module naming: package_types reads ambiguous next to the manifest's own FunctionType. Happy to be overruled.

proc.encode_args(&["1".to_string(), "2".to_string()]),
Err(PackageTypesError::TooManyArgs)
));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we add unit tests for bool, u32, and u64 here before non-draft? They're three of the five priority types I called out in #2098 but they're only covered by decode.rs:110-125, not by CI. A mod tests case per type (encode token, decode felts, format_signature) in the same shape as felt_roundtrip would lock the priority-types contract down. I don't think we need a CLI-level test for each, the unit tests should be enough.

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.

Thanks for comments! I will add the unit tests for bool, u32,u64.

/// `call` command exercises its typed encode/decode path. Only this proc is annotated; `add`
/// and `set_value` resolve to `None` in `TypedProcInfo` and stay on the raw path.
fn build_take_account_id_debug_sections() -> (
miden_mast_package::debug_info::DebugFunctionsSection,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This helper is load-bearing for the typed test, and it exists because the official compiler does not emit DebugTypeInfo::Function entries yet (per your draft-state note above). That means the only end-to-end coverage of the typed path runs against hand-built debug sections.

Could you link the corresponding compiler PR or open a tracking issue so we have an explicit gate for "pioneers running cargo miden build against the published toolchain will see typed output"? I want to be sure we don't ship this and then quietly have everyone fall back to raw rendering until the compiler catches up.

Comment on lines +61 to +67
pub(super) fn wit_short_name(name: &str) -> &str {
name.rsplit('/').next().filter(|s| !s.is_empty()).unwrap_or("")
}

pub(super) fn is_account_id_type(name: &str) -> bool {
wit_short_name(name) == "account-id"
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This ties AccountId detection to WIT-style /-separated names. It works for both the Walnut fork's miden:base/core-types@1.0.0/account-id and a bare account-id, but it assumes any future compiler picks one of those shapes. Worth a comment here pointing at the assumption, or a follow-up so we have one place to update once the upstream compiler picks its final naming for these structs.

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.

Added a comment for this

Comment thread crates/rust-client/src/lib.rs Outdated
pub mod keystore;
pub mod note;
pub mod note_transport;
pub mod package_types;

@Keinberger Keinberger May 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not a blocker: package_types reads ambiguous next to the manifest's own FunctionType. Would package_debug_info or package_typed_view describe what the module actually wraps better? Happy to be overruled.

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.

Agree! I will rename to package_debug_info

@marijamijailovic marijamijailovic force-pushed the pr/call-typed-rendering branch from a74dc47 to 927cc8f Compare May 31, 2026 19:00
@marijamijailovic

Copy link
Copy Markdown
Contributor Author

I’ve pushed a follow-up that addresses the review comments on the typed ‎call data:

  • Added unit tests for ‎bool, ‎u32, and ‎u64, following the same pattern as ‎felt_roundtrip.
  • Renamed the module from ‎package_types to ‎package_debug_info (and ‎PackageTypesError to ‎PackageDebugInfoError).
  • Centralized type detection by moving the WIT names into ‎ACCOUNT_ID_TYPE / ‎WORD_TYPE constants with a short comment about the naming assumption, so there is a single place to update once the upstream compiler settles on names.
  • Updated the CHANGELOG by extending the existing ‎miden-cli call.
  • Documented in ‎tests/cli.rs that the published compiler does not yet emit typed ‎DebugTypeInfo::Function entries (those are added in compiler#1151, so packages built with the current toolchain still fall back to the raw path.
  • Switched digest resolution to go through the manifest (export name → digest) instead of ‎package.mast, since ‎mast is marked as unstable and will move from ‎Library to ‎MastForest.
  • Fixed anonymous-struct handling to use the ‎<anon> sentinel, so they render by field shape (e.g. ‎{x: felt, y: felt}), consistent with what the compiler currently emits and what is described in this comment.
  • Rust contract functions are usually written in ‎snake_case, but the Miden toolchain exports them as kebab-case (‎take_account_id → ‎take-account-id). To make this friendlier for users, ‎call now treats ‎_ and ‎- as equivalent when resolving procedure names, so both forms work. This also fixed integration tests whose MASM fixture uses bare snake names by normalizing both sides.
  • make clippy currently fails on ‎fs_keystore.rs:232 (‎get_signature) with ‎clippy::unused_async, which seems unrelated to the ‎call changes. I’m happy to fix that in this PR as well if you’d like CI to be green here.

cc @Keinberger @igamigo

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

cc @BrianSeong99 I am tagging you here so you are aware of this one

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

Hi @igamigo! Would it make sense to start the review while keeping this PR in draft until the compiler changes land, and then make this one “ready” once the compiler is ready as well?

@igamigo igamigo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall I'm not entirely sure the new package_debug_info should be part of miden_client. It seems like it encompasses a bunch of package/compiler-related helpers and is not really used in the miden-client crate, only the CLI. In this sense, I wonder if it should be placed either in the CLI directly or as a separate compiler crate, as it's very much independent of the codebase here.

Comment on lines +33 to +45

pub struct TypedProcInfo {
types: DebugTypesSection,
name: String,
return_type_idx: Option<DebugTypeIdx>,
params: Vec<TypedParam>,
}

#[derive(Clone)]
struct TypedParam {
name: String,
type_idx: DebugTypeIdx,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Let's add doc comments to these structs

Comment thread crates/rust-client/src/lib.rs Outdated
pub mod keystore;
pub mod note;
pub mod note_transport;
pub mod package_debug_info;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this be named just package?

Comment on lines +25 to +27
pub use self::encode::parse_felt_token;
use self::encode::{arg_token_count, encode_tokens};
pub use self::errors::PackageDebugInfoError;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Let's move the pub use to be below the mod definitions (ie, below line 4 and 5). Let's also move all the imports to be above the mod definitions


impl TypedProcInfo {
/// `None` if the package has no debug info or no entry for `procedure_name`.
pub fn resolve(package: &Package, procedure_name: &str) -> Option<Self> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit (feel free to disregard): Should this be named from_package?

Some(Self { types, name, return_type_idx, params })
}

/// `get-count() -> felt`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we expand on these doc comments?

Comment on lines +59 to +71
pub fn format_signature(&self) -> String {
let params = self
.params
.iter()
.map(|p| format!("{}: {}", p.name, format_type(&self.types, p.type_idx, 0)))
.collect::<Vec<_>>()
.join(", ");
let ret = self
.return_type_idx
.map(|i| format!(" -> {}", format_type(&self.types, i, 0)))
.unwrap_or_default();
format!("{}({}){}", self.name, params, ret)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Would it be better to make this into a Display impl?

/// Encodes `tokens` as a flat felt vector matching the procedure's parameter types. `word`
/// and `account-id` each parse from a single hex token; other structs expect one token per
/// leaf field.
pub fn encode_args(&self, tokens: &[String]) -> Result<Vec<Felt>, PackageDebugInfoError> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we need this function here? It seems like this is an application responsibility. For example, perhaps a CLI would be interested in taking strings and parsing them, but I don't think we need to make it a concern of TypedProcInfo. Perhaps it can take a list of felts and validate them against the proc info


/// Number of felts the procedure pushes on the stack for its return value. `Some(0)` if
/// there is no return type; `None` if the return type has no statically-known felt size.
pub fn return_value_felt_count(&self) -> Option<usize> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Would output_felt_count be a fitting name for this?

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

Overall I'm not entirely sure the new package_debug_info should be part of miden_client. It seems like it encompasses a bunch of package/compiler-related helpers and is not really used in the miden-client crate, only the CLI. In this sense, I wonder if it should be placed either in the CLI directly or as a separate compiler crate, as it's very much independent of the codebase here.

Thanks, that’s a good point. I agree this probably live inmiden_client long‑term.

I'm thinking about moving the whole typed encoder/decoder into miden-vm/mast-package, under debug_info::typed. The main reason is that the the typed encoder/decoder into ‎mast-package, under ‎debug_info::typed. The main reason is that the encoder/decoder only needs the debug type table (‎DebugTypesSection / ‎DebugTypeInfo), and those types already live in ‎mast-package/src/debug_info/types.rs, so it feels more natural for the typed view to sit next to the data it consumes.

This way the split would be: the compiler writes the debug sections, ‎mast-package owns the debug types and the typed view over them, and client just consume it.

Let me know what do you think? And we can tag in people from the miden-vm / compiler side who can share their opinion.

@igamigo

igamigo commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Hey @marijamijailovic / @Keinberger, just a heads up, if we want this on the 0.15.x we'll need to rebase this to main.

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

This PR is blocked for now.

miden-client exec and call do not work with the latest compiler on the next branch. We must wait for the fix first.
See: 0xMiden/compiler#1192

After the fix is introduce, I will:

  1. Run full testing locally.
  2. Mark this PR as ready for review.

So this PR stays in draft until then.

  Drop the local package_debug_info module and consume the typed
  encoder/decoder from miden-mast-package's debug_info::typed instead.
@marijamijailovic marijamijailovic force-pushed the pr/call-typed-rendering branch from 5c1cb85 to 69c7a49 Compare June 20, 2026 10:57
@marijamijailovic marijamijailovic changed the base branch from next to main June 20, 2026 12:40
@marijamijailovic

Copy link
Copy Markdown
Contributor Author

Hey @igamigo, rebased on main, miden-vm PR is open — 0xMiden/miden-vm#3276

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

This PR is blocked for now.

miden-client exec and call do not work with the latest compiler on the next branch. We must wait for the fix first.
See: 0xMiden/compiler#1192

After the fix is introduce, I will:

  1. Run full testing locally.
  2. Mark this PR as ready for review.

So this PR stays in draft until then.

The above issue is resolved.

@igamigo

igamigo commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

With the issue resolved are we now ready to move this PR out of draft and have it reviewed? Or were we waiting on the VM patch release?

@marijamijailovic

Copy link
Copy Markdown
Contributor Author

With the issue resolved are we now ready to move this PR out of draft and have it reviewed? Or were we waiting on the VM patch release?

Thanks for bumping this up! Now that the ‎miden-vm PR is open, my slight preference would be to land the ‎miden-vm patch first so that we can point ‎Cargo.toml at a clean released dependency, and only then move this PR out of draft and mark it ready for review. If you’d prefer to start the review here in parallel with the VM patch, I’m happy to resolve all comments in parallel.

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.

miden-client call: Typed output rendering

3 participants