Remove dead code and stale docs in compiler/ext, common, depends and ml - #8699
cristianoc wants to merge 29 commits into
Conversation
31 constants in compiler/ext/literals.ml (js_array_ctor, prim, param, the fn_run/method_run family, node_modules, package_json, the .a/.cmo/.cma/ .cmx/.cmxa/.mll/.cmt/.cmti/.d/.gen.js/.gen.tsx suffixes, esmodule, commonjs, unused_attribute, sourcedirs_meta and others) had no reference: no file names them as Literals.x or L.x, and nothing opens Literals. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ext_array (reverse_in_place, filter, filter_map, filter_mapi, range, map2i, is_empty), Ext_list (map_split_opt, map2i, filter_map2, find_def, mem_string, drop), Ext_string (repeat, rindex_opt, lowercase_ascii, unsafe_sub), Ext_buffer.clear, Ext_char.valid_hex, Ext_fmt.invalid_argf, Ext_obj.bt, Ext_pp_scope.print and Hash_set_poly (clear, reset, iter, to_list) had no caller outside their own definitions; with them go the helper rindex_rec_opt and the Ext_char comment about a Char.escaped backport the module does not contain. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Misc.map_left_right, list_remove, log2, chop_extensions and Int_literal_converter.int32/int64 had no caller, qualified or through `open Misc`; typecore.ml uses only Int_literal_converter.int. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ident is the only Identifiable.Make instance, and nothing uses its Map.disjoint_union, union_right, union_left, union_merge, map_keys, of_set, transpose_keys_and_data(_set) or Tbl.memoize; nothing applies the Pair functor. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ordered_hash_map_local_ident, the only instance, is used through create, add, rank, find_value, iter, length and to_sorted_array (lambda_scc.ml and the ounit hashtbl suite). The clear, reset, mem, fold, elements and choose members of Ordered_hash_map_gen.S had no caller; with them go the gen-level clear, reset, fold, elements, the unused bucket_length and the initial_size field that only reset read. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No instance of Map_gen.S (Map_int, Map_string, Map_ident, and Handler_map in core) is used through remove or add_list, and nothing calls Map_gen.concat; Map_gen.merge and its min-binding helpers served only remove. No instance of Set_gen.S (Set_int, Set_string, Set_ident) is used through print, so the functor's element print requirement goes too, and nothing calls Set_gen.partition. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
config.mli described cmt_magic_number with the comment copied from
cmi_magic_number ("compiled interface files"); the value ("Caml1999T039")
heads the typed-tree files that Cmt_format.save_cmt writes (.cmt, .cmti).
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing calls Bs_loc.merge; its private helper is_ghost and the commented-out none/is_ghost declarations go with it. The frontend uses only the Bs_loc.t alias. bs_loc.mli stays: the common library enables warning 70 (missing interface). Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Js_config documented a browser flag it does not define and kept commented-out declarations of package-info accessors that no longer exist; Ext_log's example called an `err` function the module does not export (it exports dwarn); Ml_binary's header described Reason AST reading instead of the Parsetree/Parsetree0 conversion it provides. Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The write_ast doc described a { magic number; filename; ast } layout, a
"-" stdin filename and a `fan` tool, and pointed to Bsb_depfile_gen; the
function writes a length-prefixed dependency block, the source file name
and the marshalled AST, and rewatch's get_dep_modules decodes the block.
magic_sep_char was exported but used only inside binary_ast.ml.
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stypes and Annot recorded type annotations for an -annot dump that bsc never enables: Clflags.annotations was never set to true, Stypes.record returned immediately and Stypes.dump had no caller. The scope arguments threaded through Typecore and Typemod.type_structure fed only those records. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
tool_name, apply, run_main, register_function and register (with apply_lazy, extension_of_exn and Ppx_context.update_cookies, which only they used) had no caller: bsc runs ppx binaries as separate processes through cmd_ppx_apply, which uses only the add/drop_ppx_context helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Each removed value had no reference anywhere in the repository (checked with git grep -w over all sources and the modules that open them): Ast_helper with_default_loc and Const.nativeint, Env copy_local and diff, Lambda ref_field_info, Parmatch inactive, Consistbl filter, Tbl print, Pprintast string_of_expression, Predef type_option, Printlambda structured_constant, Printtyp print_items, Subst typexp, Ast_untagged_variants block_type_to_user_visible_string, Record_type_spread t_equals and Asttypes virtual_flag. The helpers only they used go with them. Error_message_utils type_clash_context_maybe_option was the only constructor of MaybeUnwrapOption, so that context and its never-printed hint are removed and the error catalog no longer lists maybe_unwrap_option.res as covering it. Ast_helper0 keeps its copies because it is frozen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Variant_runtime named Ast_untagged_variants as re-exporting and deriving the layout; the derivation is in Variant_layout and the open re-exports nothing. Cmt_format_common named a selected Cmt_format implementation; the selected module is Cmt_format_persistence, copied by compiler/ml/dune per profile. Clflags.binary_annotations was labelled -annot; it controls .cmt writing and is cleared by -bs-no-bin-annot. Outcometree described OCaml toplevel hooks; ReScript has no toplevel and prints the trees through Oprint's hooks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
compiler/common/bs_loc.ml defines only `type t = Location.t = {...}`, a
re-export of Location.t. Its sole users are the signature of
Ast_external_process.handle_attributes (.ml and .mli), which now name
Location.t directly. The module and its interface are deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
A typed scan of every .cmt in the build (value paths in Texp_ident, resolved through module aliases and includes) finds no use of these outside their own definitions: - Literals.debugger (core prints the keyword from Js_dump_lit.debugger) - Misc.for_all2, Misc.fst3, Misc.output_to_file_via_temporary - Set_gen.invariant (the sets use the functor's own invariant, which calls Set_gen.check and Set_gen.is_ordered) The commented-out `let compare = Set_gen.compare ...` in ext_set.ml names a function Set_gen does not define; it is deleted too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
A typed scan of every .cmt in the build (value paths in Texp_ident, resolved through module aliases and includes, plus a token check of the playground sources the default profile does not compile) finds no use of these outside their own definitions: - Ast_helper0.with_default_loc - Ast_mapper_from0.map_snd and Ast_mapper_to0.map_snd - Btype.set_name, the only producer of the Cname trail entry; the constructor and its undo case go with it (warning 37 reports it otherwise) - Consistbl.source - Depend.weaken_map - Env.summary (keep_only_summary reads the summary field directly) - Printtyp.type_sch - Tbl.mem, Tbl.remove (and merge, which only remove called), Tbl.map The commented-out dump_used_attributes in used_attributes.ml calls Hash_set_poly.iter, which no longer exists; it is deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Hash_set_poly repeated the remove, add and mem bodies of Hash_set.Make with Hashtbl.hash and ( = ) in place of the functor's hash and equal. Its only compiler user was ml/used_attributes.ml, which now instantiates Hash_set.Make with exactly those two functions for string Asttypes.loc, so key_index, bucket equality and resizing are unchanged. compiler/ext/hash_set_poly.ml and .mli are deleted. The three ounit cases for Hash_set_poly go with it; the Id_hash_set case beside them runs the same add, mem, remove and length sequence through Hash_set.Make and continues further. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Misc.split_last and Ext_list.split_at_last both return the list without its last element paired with that element. Misc's two callers, Misc.did_you_mean and Includemod.report_error, each call it only on a non-empty list (both match [] first), where the results are equal; the definitions differ only on [] (assert false vs Invalid_argument), which neither caller reaches. Ext_list.split_at_last is the ext list helper with ounit coverage, so both callers use it and Misc.split_last is removed. includemod.ml used Misc only for split_last, so its open Misc goes too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Misc.may and Misc.may_map are Stdlib.Option.iter and Stdlib.Option.map. Ext_option.iter and Ext_option.map restated the same two matches with the option first. They now call Stdlib.Option.iter and Stdlib.Option.map with the arguments swapped, so all four names share one implementation and keep their argument orders; the ~70 Misc.may/may_map call sites in ml and the Ext_option call sites in core are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
magic_of_kind and magic_of_ast0 both mapped an AST kind to Config.ast0_impl_magic_number or Config.ast0_intf_magic_number, once for the kind witness (Ml, Mli) and once for the AST value (Impl, Intf). magic_of_kind now holds the mapping and magic_of_ast0 maps Impl to Ml and Intf to Mli through it, so cmd_ppx_apply.ml writes and checks the same magic strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Ext_pervasives.reraise, Map_gen.fill_array_with_f and fill_array_aux, Warnings.is_error and nerrors, Set_gen.remove_min_elt, Misc.edit_distance, Ext_list.exclude, Ext_ident.is_uppercase_exotic, Ext_string.ends_with_index and concat5 are exported but used only inside their own module. Evidence: a compiler-libs reader over the .cmt files of `dune build @check` and of `dune build --profile browser @check` (Texp_ident paths resolved through module aliases and dune's library wrappers) finds no use of them in any other module, and a token check of compiler/jsoo, the platform/playground directories and tests/ounit_tests finds no qualified use. Each one stays used inside its module, so only the interface changes; the edit_distance documentation moves to its definition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
These values are exported by compiler/ml interfaces and used by no other module: Ast_helper.default_loc, Ast_mapper.map_opt, Ast_payload.unrecognized_config_record, Bigint_utils.is_neg, is_pos, remove_leading_sign and remove_leading_zeros, Cmt_format.read, read_magic_number and record_deprecated_used, Ctype.sort_row_fields, full_expand, rigidify, all_distinct_vars and is_contractive, Datarepr.constructor_existentials, Depend.make_leaf, make_node and add_signature_binding, Env.add_functor_arg, save_signature_with_imports, crc_units, add_import and report_error, External_arg_spec.empty_label, Includemod.print_coercion, Lambda.lambda_true and lambda_false, Location.error_reporter, Longident.unflatten, Matching.flatten_pattern, Mtype.no_code_needed and no_code_needed_sig, Oprint.parenthesized_ident, Pprintast.expression and core_type, Predef.type_list, path_iterable, path_async_iterable, path_extension_constructor and ident_division_by_zero, Printast.expression and structure, Printlambda.primitive, Printtyp.reset, tree_of_type_scheme, tree_of_module, tree_of_modtype_declaration, type_expansion, prepare_expansion and trace, Printtyped.implementation, String_literal.encode_js_template, Subst.module_declaration, Translmod.report_error, Typecore.check_partial, type_approx, option_some, option_none, extract_option_type, iter_pattern, generalizable and report_error, Typedecl.abstract_type_decl, and Typemod.type_module, check_nongen_schemes, simplify_signature and path_of_module. Evidence: a compiler-libs reader over the .cmt files of `dune build @check` and `dune build --profile browser @check` (Texp_ident paths resolved through module aliases and dune's library wrappers) finds no use of them in another module, and a token check of compiler/jsoo, the platform/playground directories and tests/ounit_tests finds no qualified use. Most stay used inside their module, so only the interface changes. Four were used nowhere and are deleted: Includemod.print_coercion (with its print_list helpers), Mtype.no_code_needed and no_code_needed_sig, and the Pprintast.expression and core_type wrappers. The `open Format` in env.mli and typecore.mli served only the removed report_error declarations. Documentation of the narrowed values with no comment at their definition moves to the definition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Ident.unique_name had one user outside ident.ml, the coercion printer in includemod.ml, which is gone. Evidence: a compiler-libs reader over the .cmt files of `dune build @check` and `dune build --profile browser @check` (Texp_ident paths resolved through module aliases and dune's library wrappers) finds no use in another module, and a token check of compiler/jsoo, the platform/playground directories and tests/ounit_tests finds none. Ident.output still uses it, so only the interface changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Four invalid_arg/fatal_error strings named functions that do not exist: - Ext_list: arr_list_combine_unsafe raised "Ext_list.combine" (no combine in ext_list.ml). The message names arr_list_combine_unsafe. - Ext_list: the function was spelled arr_list_filter_map_unasfe while its error said "Ext_list.arr_list_filter_map_unsafe". The function is renamed to arr_list_filter_map_unsafe (private to ext_list.ml, absent from ext_list.mli), so the message names it. - Matching: flatten_cases raised "Matching.flatten_case". - Parmatch: record_arg raised "Parmatch.as_record". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
- hash.ml, hash_set.ml and hash_set_poly.ml kept commented-out bindings to Hash_gen.stats, Hash_set_gen.copy and Hash_set_gen.stats, which are not defined; they are deleted. - ext_string.ml kept a commented check_any_suffix_case calling the undefined check_suffix_case, and a commented extract_until calling the undefined index_rec; extract_until's doc and commented val in the .mli go with it. - literals.ml tied the "create" literal to Caml_exceptions.create; it is used as Primitive_exceptions.create (js_exp_make.ml), which the runtime's Primitive_exceptions.resi exports. - ext_pp_scope.ml compared str_of_ident with Js_dump.ident; the printing function is ident in the same module. It also pointed at test/test_global_print.ml, which is not in the tree. - hash_set_ident_mask.mli documented mask_and_check_all_hit as check_mask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
- parmatch.ml and typecore.ml said record fields are sorted by Typecore.type_label_a_list; the sort by lbl_pos is in type_record_elem_list. - typecore.ml said partial_pred is passed to Partial.parmatch; it is passed to Parmatch.check_partial_gadt and Parmatch.check_unused. A "same as Ctype.poly" remark is deleted (Ctype has no poly), as is a commented unify_pat call building a row with the removed row_bound field. Two notes cited res_core.ml:3841 for normalize_for_of_pattern, which is no longer at that line; they now cite the file. - parmatch.ml kept the commented non-GADT exhaustiveness path (exhaust, try_many, combinations, do_check_partial_normal, do_check_fragile_normal, check_partial); the live path is the GADT one, and the commented code is deleted. Prose naming every_satisfiable / every_satisfied now names every_satisfiables. - matching.ml named may_constr_equal and compiled_flattened; the functions are Types.may_equal_constr and compile_flattened. - env.ml said save_signature_with_imports makes imported_unit() return the crc; the function is imports (). - location.ml pointed at a super_error_reporter above; the reporter is default_error_reporter below. - typedtree.mli misspelled Longident.t as Longindent.t. - lambda_traverse.ml named Pervasives.compare; the comparison used is Stdlib.compare. - lambda.mli described main_module_block_size and the flambda middle-end, which this compiler does not have; deleted. - Commented-out code over undefined names is deleted: debug prints over StringSet, PathSet and IdentSet (depend, mtype, translmod), cmt_format.mli's list of vals (is_magic_number, read_signature, ...), ctype's actual_mode guard and correct_abbrev val, external_arg_spec's empty_lit val, and printlambda's field_kind over Pgenval and boxed_integer_name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Printtyp.raw_type_expr printed a type's internal representation. It was installed into Btype.print_raw, and both were reached only from commented-out debug lines in typecore, typedecl, ctype, env and printtyp. The printer, its helpers (raw_list, safe_repr, list_of_memo, print_name), the hook and those commented-out calls are removed. Restore with `git revert` if the printer is still wanted for debugging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: afe955585f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| The first argument to [register] is a symbolic name to be used by | ||
| the ppx driver. *) | ||
|
|
||
| (** {1 Convenience functions to write mappers} *) |
There was a problem hiding this comment.
Update the PPX example after removing
register
When Ast_mapper is used as its own module documentation directs (lines 16–47), removing register and run_main makes the sample PPX fail to compile with Unbound value register; the overview also still claims this module handles serialization and command-line forwarding. Either preserve the documented standalone entry point for compatibility or replace the example and overview with the supported external-PPX workflow.
AGENTS.md reference: AGENTS.md:L91-L91
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Right: the overview and example still described the standalone register/run_main driver this PR removes. c8d2bec rewrites the ast_mapper.mli overview: the example now applies a mapper directly (test_mapper.structure test_mapper str), and the text describes how bsc runs external -ppx executables through Cmd_ppx_apply, exchanging the marshalled Parsetree0 in the Ml_binary format. It also drops the empty Cookies section. Documentation only, so no test; I checked that the example typechecks against the current Parsetree.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #8699 +/- ##
==========================================
+ Coverage 78.67% 79.92% +1.24%
==========================================
Files 476 470 -6
Lines 64402 63355 -1047
==========================================
- Hits 50670 50637 -33
+ Misses 13732 12718 -1014
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/darwin-x64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-8699 |
The module overview and example referred to the removed standalone register/run_main entry point. The overview now describes mapper and default_mapper, gives an example that applies a mapper directly, and describes how bsc exchanges the marshalled Parsetree0 with external -ppx executables through Cmd_ppx_apply and Ml_binary. The empty Cookies section is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
|
Discussed the removal of the "unused raw type printer" with Codex (see below, shortened a bit). I think we should keep it. Yes—there is value in keeping it for type-checker debugging. The remaining printers serve a different purpose: they show readable types, while ... The raw printer exposes information that ordinary type printing hides:
For example, two variables can both render as It does have limitations: it follows The other facilities are:
... The My recommendation is to drop the raw-printer removal commit. “No active callers” is expected for a printer enabled through temporary instrumentation. Keeping this relatively small tool preserves a capability the readable printers do not provide. If it is removed, the justification should be that maintainers no longer want to maintain internal graph debugging—not that other type printers already cover it. |
As I said to keep it we need to add a command line option to turn it on if it does not exist already |
Part of #8712.
"Remove the unused raw type printer" deletes a debugging aid kept on purpose:
Printtyp.raw_type_expr, its helpers and theBtype.print_rawhook. Nothing uses them; only commented-out debug lines did. It is a separate commit, so it can be dropped if the printer is still wanted.Summary
open Misc; typecore.ml uses only Int_literal_converter.int.errfunction the module does not export (it exports dwarn); Ml_binary's header described Reason AST reading instead of the Parsetree/Parsetree0 conversion it provides.fantool, and pointed to Bsb_depfile_gen; the function writes a length-prefixed dependency block, the source file name and the marshalled AST, and rewatch's get_dep_modules decodes the block. magic_sep_char was exported but used only inside binary_ast.ml.type t = Location.t = {...}, a re-export of Location.t. Its sole users are the signature of Ast_external_process.handle_attributes (.ml and .mli), which now name Location.t directly. The module and its interface are deleted.dune build @checkand ofdune build --profile browser @check(Texp_ident paths resolved through module aliases and dune's library wrappers) finds no use of them in any other module, and a token check of compiler/jsoo, the platform/playground directories and tests/ounit_tests finds no qualified use. Each one stays used inside its module, so only the interface changes; the edit_distance documentation moves to its definition.dune build @checkanddune build --profile browser @check(Texp_ident paths resolved through module aliases and dune's library wrappers) finds no use in another module, and a token check of compiler/jsoo, the platform/playground directories and tests/ounit_tests finds none. Ident.output still uses it, so only the interface changes.git revertif the printer is still wanted for debugging.Test plan
dune builddune build @fmtdune build --profile browser @checkmakemake liblib/outputsmake testmake test-analysismake test-toolsmake test-gentypemake test-syntaxmake checkformatnpm run check🤖 Generated with Claude Code