From 4404f95f11d731908059060d41804b1cbdc0eafb Mon Sep 17 00:00:00 2001 From: Yogesh Rao Date: Sun, 19 Apr 2026 21:22:56 +0530 Subject: [PATCH 1/2] feat: improve skill scores for coinset-cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hey @cameroncooper 👋 I ran your skills through `tessl skill review` at work and found some targeted improvements. Here's the full before/after: | Skill | Before | After | Change | |-------|--------|-------|--------| | coinset-cli | 5% | 90% | +85% | The big jump is because the SKILL.md was missing YAML frontmatter entirely, which caused the reviewer to skip all LLM evaluation. Once that was fixed, I also made a couple of structural improvements.
Changes made - **Added YAML frontmatter** with a kebab-case `name` field and a detailed `description` including domain-specific trigger terms and a "Use when..." clause - **Moved reference tables to separate files** — the conditions opcode reference and known puzzle mod hashes now live in `references/conditions.md` and `references/puzzle-hashes.md`, linked from the main skill. This improves progressive disclosure and reduces token cost for the agent - **Added validation checkpoints to workflows** — key workflows like "get puzzle and solution" and "trace coin lineage" now include verification steps and error recovery guidance
Honest disclosure — I work at @tesslio where we build tooling around skills like these. Not a pitch — just saw room for improvement and wanted to contribute. Want to self-improve your skills? Just point your agent (Claude Code, Codex, etc.) at [this Tessl guide](https://docs.tessl.io/evaluate/optimize-a-skill-using-best-practices) and ask it to optimize your skill. Ping me — [@yogesh-tessl](https://github.com/yogesh-tessl) — if you hit any snags. Thanks in advance 🙏 --- SKILL.md | 154 +++--------------------------------- references/conditions.md | 72 +++++++++++++++++ references/puzzle-hashes.md | 70 ++++++++++++++++ 3 files changed, 153 insertions(+), 143 deletions(-) create mode 100644 references/conditions.md create mode 100644 references/puzzle-hashes.md diff --git a/SKILL.md b/SKILL.md index 2968bb4..e8fee21 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,3 +1,8 @@ +--- +name: coinset-cli +description: "Query, inspect, and investigate the Chia blockchain using the coinset CLI. Wraps the Chia Full Node RPC (mainnet, testnet, or local node) for coin record lookups, spend bundle inspection, CLVM compilation/decompilation/execution, address-to-puzzle-hash conversion, mempool monitoring, and real-time blockchain event streaming. Use when querying blockchain state or blocks, tracing coin lineage and spend history, analyzing spend bundles with condition parsing, working with CLVM programs including curry and uncurry operations, computing puzzle hashes and coin IDs, estimating fees, submitting transactions, or monitoring Chia network activity in real time." +--- + # coinset CLI Use this tool to query, inspect, and investigate the Chia blockchain. `coinset` wraps the Chia Full Node RPC (hosted at api.coinset.org or any full node) and includes built-in spend inspection and local CLVM utilities. @@ -259,6 +264,8 @@ Check `spent` (boolean) and `spent_block_index` in the result. coinset get_puzzle_and_solution 0xCOIN_ID --inspect ``` +Verify `success` is `true`. If the coin is unspent, this errors — check `spent` via `get_coin_record_by_name` first. If `--inspect` fails, retry without it. + ### Inspect all spends in a block ```bash @@ -281,6 +288,8 @@ coinset get_coin_records_by_parent_ids 0xPARENT_COIN_ID --include-spent-coins coinset get_puzzle_and_solution 0xPARENT_COIN_ID --inspect ``` +If `coin_records` is empty, verify the parent coin exists with `get_coin_record_by_name` first — it may be unspent or the ID may be incorrect. + ### Decompile a puzzle reveal ```bash @@ -513,81 +522,7 @@ Every CLVM operation has a cost. Key numbers: ## Conditions reference -When a puzzle executes, it returns a list of conditions. Each condition is a list starting with an opcode number. - -### Coin creation - -| Opcode | Name | Format | Description | -|---|---|---|---| -| 51 | `CREATE_COIN` | `(51 puzzle_hash amount (...memos)?)` | Create a new coin. Optional memos list; first 32-byte memo is the hint. Cost: 1,800,000. | -| 52 | `RESERVE_FEE` | `(52 amount)` | Assert minimum fee in this transaction. | - -### Signatures - -All AGG_SIG conditions cost 1,200,000 each. - -| Opcode | Name | Description | -|---|---|---| -| 49 | `AGG_SIG_UNSAFE` | Verify signature on raw message. No domain separation -- can be replayed. | -| 50 | `AGG_SIG_ME` | Verify signature on message + coin_id + genesis_id. Recommended for most uses. | -| 43-48 | `AGG_SIG_PARENT` / `AGG_SIG_PUZZLE` / `AGG_SIG_AMOUNT` / `AGG_SIG_PUZZLE_AMOUNT` / `AGG_SIG_PARENT_AMOUNT` / `AGG_SIG_PARENT_PUZZLE` | CHIP-11 domain-separated signature variants binding to specific coin attributes. | - -### Announcements (legacy) - -| Opcode | Name | Description | -|---|---|---| -| 60 | `CREATE_COIN_ANNOUNCEMENT` | Create announcement bound to this coin's ID. | -| 61 | `ASSERT_COIN_ANNOUNCEMENT` | Assert `sha256(coin_id + message)` was announced. | -| 62 | `CREATE_PUZZLE_ANNOUNCEMENT` | Create announcement bound to this coin's puzzle hash. | -| 63 | `ASSERT_PUZZLE_ANNOUNCEMENT` | Assert `sha256(puzzle_hash + message)` was announced. | - -### Messages (modern replacement for announcements) - -| Opcode | Name | Description | -|---|---|---| -| 66 | `SEND_MESSAGE` | `(66 mode message ...)` -- Send message with sender/receiver commitment via mode bitmask. | -| 67 | `RECEIVE_MESSAGE` | `(67 mode message ...)` -- Assert receipt of matching message. | - -The `mode` byte is 6 bits: 3 bits for sender commitment, 3 bits for receiver commitment. Each 3-bit group encodes which coin attributes (parent, puzzle, amount) to commit to. For example, `mode=0b111110` means sender commits to all three (coin ID) and receiver commits to parent+puzzle. - -### Self-assertions - -| Opcode | Name | Description | -|---|---|---| -| 70 | `ASSERT_MY_COIN_ID` | Assert this coin's ID matches. | -| 71 | `ASSERT_MY_PARENT_ID` | Assert this coin's parent ID matches. | -| 72 | `ASSERT_MY_PUZZLEHASH` | Assert this coin's puzzle hash matches. | -| 73 | `ASSERT_MY_AMOUNT` | Assert this coin's amount matches. | -| 74 | `ASSERT_MY_BIRTH_SECONDS` | Assert coin creation timestamp. | -| 75 | `ASSERT_MY_BIRTH_HEIGHT` | Assert coin creation height. | -| 76 | `ASSERT_EPHEMERAL` | Assert this coin was created in the current block. | - -### Timelocks - -| Opcode | Name | Description | -|---|---|---| -| 80 | `ASSERT_SECONDS_RELATIVE` | Min seconds since coin creation. | -| 81 | `ASSERT_SECONDS_ABSOLUTE` | Min absolute timestamp. | -| 82 | `ASSERT_HEIGHT_RELATIVE` | Min blocks since coin creation. | -| 83 | `ASSERT_HEIGHT_ABSOLUTE` | Min absolute block height. | -| 84 | `ASSERT_BEFORE_SECONDS_RELATIVE` | Max seconds since coin creation. | -| 85 | `ASSERT_BEFORE_SECONDS_ABSOLUTE` | Max absolute timestamp. | -| 86 | `ASSERT_BEFORE_HEIGHT_RELATIVE` | Max blocks since coin creation. | -| 87 | `ASSERT_BEFORE_HEIGHT_ABSOLUTE` | Max absolute block height. | - -### Concurrency - -| Opcode | Name | Description | -|---|---|---| -| 64 | `ASSERT_CONCURRENT_SPEND` | Assert another specific coin is spent in same block. | -| 65 | `ASSERT_CONCURRENT_PUZZLE` | Assert a coin with specific puzzle hash is spent in same block. | - -### Other - -| Opcode | Name | Description | -|---|---|---| -| 1 | `REMARK` | No-op; always valid. | -| 90 | `SOFTFORK` | Soft-fork guard for future conditions. Cost is specified in ten-thousands. | +See [references/conditions.md](references/conditions.md) for the full opcode table (CREATE_COIN, AGG_SIG_*, announcements, messages, self-assertions, timelocks, concurrency). ## Puzzle composition @@ -648,74 +583,7 @@ The settlement puzzle is versatile: it can be used as an inner puzzle inside a C ## Known puzzle mod hashes -Reference table of well-known puzzle template hashes. Use these with `coinset clvm curry` and `coinset clvm uncurry` to identify and compose puzzles. Full serialized mod bytes are available at `https://raw.githubusercontent.com/Chia-Network/chia_puzzles/main/src/programs.rs`. - -### Core puzzles - -**Standard transaction** (`P2_DELEGATED_PUZZLE_OR_HIDDEN_PUZZLE`) -`0xe9aaa49f45bad5c889b86ee3341550c155cfdd10c3a6757de618d20612fffd52` -Args: `SYNTHETIC_PUBLIC_KEY` (48-byte atom) - -**CAT v2** (`CAT_PUZZLE`) -`0x37bef360ee858133b69d595a906dc45d01af50379dad515eb9518abb7c1d2a7a` -Args: `MOD_HASH` (atom — the CAT mod hash itself), `TAIL_PROGRAM_HASH` (atom — the asset ID), `INNER_PUZZLE` (tree) - -**Singleton v1.1** (`SINGLETON_TOP_LAYER_V1_1`) -`0x7faa3253bfddd1e0decb0906b2dc6247bbc4cf608f58345d173adb63e8b47c9f` -Args: `SINGLETON_STRUCT` (tree — `(MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))`), `INNER_PUZZLE` (tree) - -**Singleton launcher** (`SINGLETON_LAUNCHER`) -`0xeff07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9` -No curried args. The launcher coin ID becomes the `launcher_id`. - -**Settlement payments** (`SETTLEMENT_PAYMENT`) -`0xcfbfdeed5c4ca2de3d0bf520b9cb4bb7743a359bd2e6a188d19ce7dffc21d3e7` -No curried args. Used as inner puzzle in offers. - -**DID inner puzzle** (`DID_INNERPUZ`) -`0x33143d2bef64f14036742673afd158126b94284b4530a28c354fac202b0c910e` -Args: `INNER_PUZZLE` (tree), `RECOVERY_DID_LIST_HASH` (atom), `NUM_VERIFICATIONS_REQUIRED` (atom), `SINGLETON_STRUCT` (tree), `METADATA` (tree) - -**NFT state layer** (`NFT_STATE_LAYER`) -`0xa04d9f57764f54a43e4030befb4d80026e870519aaa66334aef8304f5d0393c2` -Args: `MOD_HASH` (atom), `METADATA` (tree), `METADATA_UPDATER_PUZZLE_HASH` (atom), `INNER_PUZZLE` (tree) - -**NFT ownership layer** (`NFT_OWNERSHIP_LAYER`) -`0xc5abea79afaa001b5427dfa0c8cf42ca6f38f5841b78f9b3c252733eb2de2726` -Args: `MOD_HASH` (atom), `CURRENT_OWNER` (atom), `TRANSFER_PROGRAM` (tree), `INNER_PUZZLE` (tree) - -**Royalty transfer program** (`NFT_OWNERSHIP_TRANSFER_PROGRAM_ONE_WAY_CLAIM_WITH_ROYALTIES`) -`0x025dee0fb1e9fa110302a7e9bfb6e381ca09618e2778b0184fa5c6b275cfce1f` -Args: `SINGLETON_STRUCT` (tree), `ROYALTY_ADDRESS` (atom), `TRADE_PRICE_PERCENTAGE` (atom) - -**NFT metadata updater** (`NFT_METADATA_UPDATER_DEFAULT`) -`0xfe8a4b4e27a2e29a4d3fc7ce9d527adbcaccbab6ada3903ccf3ba9a769d2d78b` -No curried args. - -### Commonly encountered puzzles - -**Pay-to-singleton** (`P2_SINGLETON`) -`0x40f828d8dd55603f4ff9fbf6b73271e904e69406982f4fbefae2c8dcceaf9834` -Used as pool farming reward target. - -**Notification** (`NOTIFICATION`) -`0xb8b9d8ffca6d5cba5422ead7f477ecfc8f6aaaa1c024b8c3aeb1956b24a0ab1e` - -**Pool member inner puzzle** (`POOL_MEMBER_INNERPUZ`) -`0xa8490702e333ddd831a3ac9c22d0fa26d2bfeaf2d33608deb22f0e0123eb0494` - -**Pool waiting room** (`POOL_WAITINGROOM_INNERPUZ`) -`0xa317541a765bf8375e1c6e7c13503d0d2cbf56cacad5182befe947e78e2c0307` - -### Common TAIL programs - -**Everything with signature** (`EVERYTHING_WITH_SIGNATURE`) -`0xf26fe751e5a9b13e87e4c19e4c383e713cec8c854cf8e591053e179e60e3b856` -Args: `PUBLIC_KEY` (48-byte atom). Most common TAIL for standard CAT issuance. - -**Genesis by coin ID** (`GENESIS_BY_COIN_ID`) -`0x493afb89eed93ab86741b2aa61b8f5de495d33ff9b781dfc8919e602b2571571` -Args: `GENESIS_COIN_ID` (atom). Single-issuance tokens. +See [references/puzzle-hashes.md](references/puzzle-hashes.md) for the full table of well-known puzzle template hashes (standard transaction, CAT v2, singleton, settlement, DID, NFT layers, TAILs) with curried args. ## Agent operating rules diff --git a/references/conditions.md b/references/conditions.md new file mode 100644 index 0000000..822ad3c --- /dev/null +++ b/references/conditions.md @@ -0,0 +1,72 @@ +# Conditions reference + +When a puzzle executes, it returns a list of conditions. Each condition is a list starting with an opcode number. + +## Coin creation + +| Opcode | Name | Format | Description | +|---|---|---|---| +| 51 | `CREATE_COIN` | `(51 puzzle_hash amount (...memos)?)` | Create a new coin. Optional memos list; first 32-byte memo is the hint. Cost: 1,800,000. | +| 52 | `RESERVE_FEE` | `(52 amount)` | Assert minimum fee in this transaction. | + +## Signatures + +All AGG_SIG conditions cost 1,200,000 each. + +| Opcode | Name | Description | +|---|---|---| +| 49 | `AGG_SIG_UNSAFE` | Verify signature on raw message. No domain separation -- can be replayed. | +| 50 | `AGG_SIG_ME` | Verify signature on message + coin_id + genesis_id. Recommended for most uses. | +| 43-48 | `AGG_SIG_PARENT` / `AGG_SIG_PUZZLE` / `AGG_SIG_AMOUNT` / `AGG_SIG_PUZZLE_AMOUNT` / `AGG_SIG_PARENT_AMOUNT` / `AGG_SIG_PARENT_PUZZLE` | CHIP-11 domain-separated signature variants binding to specific coin attributes. | + +## Announcements (legacy) + +| Opcode | Name | Description | +|---|---|---| +| 60 | `CREATE_COIN_ANNOUNCEMENT` | Create announcement bound to this coin's ID. | +| 61 | `ASSERT_COIN_ANNOUNCEMENT` | Assert `sha256(coin_id + message)` was announced. | +| 62 | `CREATE_PUZZLE_ANNOUNCEMENT` | Create announcement bound to this coin's puzzle hash. | +| 63 | `ASSERT_PUZZLE_ANNOUNCEMENT` | Assert `sha256(puzzle_hash + message)` was announced. | + +## Messages (modern replacement for announcements) + +| Opcode | Name | Description | +|---|---|---| +| 66 | `SEND_MESSAGE` | `(66 mode message ...)` -- Send message with sender/receiver commitment via mode bitmask. | +| 67 | `RECEIVE_MESSAGE` | `(67 mode message ...)` -- Assert receipt of matching message. | + +The `mode` byte is 6 bits: 3 bits for sender commitment, 3 bits for receiver commitment. Each 3-bit group encodes which coin attributes (parent, puzzle, amount) to commit to. For example, `mode=0b111110` means sender commits to all three (coin ID) and receiver commits to parent+puzzle. + +## Self-assertions + +| Opcode | Name | Description | +|---|---|---| +| 70 | `ASSERT_MY_COIN_ID` | Assert this coin's ID matches. | +| 71 | `ASSERT_MY_PARENT_ID` | Assert this coin's parent ID matches. | +| 72 | `ASSERT_MY_PUZZLEHASH` | Assert this coin's puzzle hash matches. | +| 73 | `ASSERT_MY_AMOUNT` | Assert this coin's amount matches. | +| 74 | `ASSERT_MY_BIRTH_SECONDS` | Assert coin creation timestamp. | +| 75 | `ASSERT_MY_BIRTH_HEIGHT` | Assert coin creation height. | +| 76 | `ASSERT_EPHEMERAL` | Assert this coin was created in the current block. | + +## Timelocks + +| Opcode | Name | Description | +|---|---|---| +| 80 | `ASSERT_SECONDS_RELATIVE` | Min seconds since coin creation. | +| 81 | `ASSERT_SECONDS_ABSOLUTE` | Min absolute timestamp. | +| 82 | `ASSERT_HEIGHT_RELATIVE` | Min blocks since coin creation. | +| 83 | `ASSERT_HEIGHT_ABSOLUTE` | Min absolute block height. | +| 84 | `ASSERT_BEFORE_SECONDS_RELATIVE` | Max seconds since coin creation. | +| 85 | `ASSERT_BEFORE_SECONDS_ABSOLUTE` | Max absolute timestamp. | +| 86 | `ASSERT_BEFORE_HEIGHT_RELATIVE` | Max blocks since coin creation. | +| 87 | `ASSERT_BEFORE_HEIGHT_ABSOLUTE` | Max absolute block height. | + +## Concurrency + +| Opcode | Name | Description | +|---|---|---| +| 64 | `ASSERT_CONCURRENT_SPEND` | Assert another specific coin is spent in same block. | +| 65 | `ASSERT_CONCURRENT_PUZZLE` | Assert a coin with specific puzzle hash is spent in same block. | +| 1 | `REMARK` | No-op; always valid. | +| 90 | `SOFTFORK` | Soft-fork guard for future conditions. Cost is specified in ten-thousands. | diff --git a/references/puzzle-hashes.md b/references/puzzle-hashes.md new file mode 100644 index 0000000..238698e --- /dev/null +++ b/references/puzzle-hashes.md @@ -0,0 +1,70 @@ +# Known puzzle mod hashes + +Reference table of well-known puzzle template hashes. Use these with `coinset clvm curry` and `coinset clvm uncurry` to identify and compose puzzles. Full serialized mod bytes are available at `https://raw.githubusercontent.com/Chia-Network/chia_puzzles/main/src/programs.rs`. + +## Core puzzles + +**Standard transaction** (`P2_DELEGATED_PUZZLE_OR_HIDDEN_PUZZLE`) +`0xe9aaa49f45bad5c889b86ee3341550c155cfdd10c3a6757de618d20612fffd52` +Args: `SYNTHETIC_PUBLIC_KEY` (48-byte atom) + +**CAT v2** (`CAT_PUZZLE`) +`0x37bef360ee858133b69d595a906dc45d01af50379dad515eb9518abb7c1d2a7a` +Args: `MOD_HASH` (atom — the CAT mod hash itself), `TAIL_PROGRAM_HASH` (atom — the asset ID), `INNER_PUZZLE` (tree) + +**Singleton v1.1** (`SINGLETON_TOP_LAYER_V1_1`) +`0x7faa3253bfddd1e0decb0906b2dc6247bbc4cf608f58345d173adb63e8b47c9f` +Args: `SINGLETON_STRUCT` (tree — `(MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))`), `INNER_PUZZLE` (tree) + +**Singleton launcher** (`SINGLETON_LAUNCHER`) +`0xeff07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9` +No curried args. The launcher coin ID becomes the `launcher_id`. + +**Settlement payments** (`SETTLEMENT_PAYMENT`) +`0xcfbfdeed5c4ca2de3d0bf520b9cb4bb7743a359bd2e6a188d19ce7dffc21d3e7` +No curried args. Used as inner puzzle in offers. + +**DID inner puzzle** (`DID_INNERPUZ`) +`0x33143d2bef64f14036742673afd158126b94284b4530a28c354fac202b0c910e` +Args: `INNER_PUZZLE` (tree), `RECOVERY_DID_LIST_HASH` (atom), `NUM_VERIFICATIONS_REQUIRED` (atom), `SINGLETON_STRUCT` (tree), `METADATA` (tree) + +**NFT state layer** (`NFT_STATE_LAYER`) +`0xa04d9f57764f54a43e4030befb4d80026e870519aaa66334aef8304f5d0393c2` +Args: `MOD_HASH` (atom), `METADATA` (tree), `METADATA_UPDATER_PUZZLE_HASH` (atom), `INNER_PUZZLE` (tree) + +**NFT ownership layer** (`NFT_OWNERSHIP_LAYER`) +`0xc5abea79afaa001b5427dfa0c8cf42ca6f38f5841b78f9b3c252733eb2de2726` +Args: `MOD_HASH` (atom), `CURRENT_OWNER` (atom), `TRANSFER_PROGRAM` (tree), `INNER_PUZZLE` (tree) + +**Royalty transfer program** (`NFT_OWNERSHIP_TRANSFER_PROGRAM_ONE_WAY_CLAIM_WITH_ROYALTIES`) +`0x025dee0fb1e9fa110302a7e9bfb6e381ca09618e2778b0184fa5c6b275cfce1f` +Args: `SINGLETON_STRUCT` (tree), `ROYALTY_ADDRESS` (atom), `TRADE_PRICE_PERCENTAGE` (atom) + +**NFT metadata updater** (`NFT_METADATA_UPDATER_DEFAULT`) +`0xfe8a4b4e27a2e29a4d3fc7ce9d527adbcaccbab6ada3903ccf3ba9a769d2d78b` +No curried args. + +## Commonly encountered puzzles + +**Pay-to-singleton** (`P2_SINGLETON`) +`0x40f828d8dd55603f4ff9fbf6b73271e904e69406982f4fbefae2c8dcceaf9834` +Used as pool farming reward target. + +**Notification** (`NOTIFICATION`) +`0xb8b9d8ffca6d5cba5422ead7f477ecfc8f6aaaa1c024b8c3aeb1956b24a0ab1e` + +**Pool member inner puzzle** (`POOL_MEMBER_INNERPUZ`) +`0xa8490702e333ddd831a3ac9c22d0fa26d2bfeaf2d33608deb22f0e0123eb0494` + +**Pool waiting room** (`POOL_WAITINGROOM_INNERPUZ`) +`0xa317541a765bf8375e1c6e7c13503d0d2cbf56cacad5182befe947e78e2c0307` + +## Common TAIL programs + +**Everything with signature** (`EVERYTHING_WITH_SIGNATURE`) +`0xf26fe751e5a9b13e87e4c19e4c383e713cec8c854cf8e591053e179e60e3b856` +Args: `PUBLIC_KEY` (48-byte atom). Most common TAIL for standard CAT issuance. + +**Genesis by coin ID** (`GENESIS_BY_COIN_ID`) +`0x493afb89eed93ab86741b2aa61b8f5de495d33ff9b781dfc8919e602b2571571` +Args: `GENESIS_COIN_ID` (atom). Single-issuance tokens. From e22a983cbe966c9b8746ec31ea298c2f61bd2109 Mon Sep 17 00:00:00 2001 From: Yogesh Rao Date: Mon, 20 Apr 2026 19:25:34 +0530 Subject: [PATCH 2/2] ci: add skill-review GitHub Action for automated skill review on PRs --- .github/workflows/skill-review.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/skill-review.yml diff --git a/.github/workflows/skill-review.yml b/.github/workflows/skill-review.yml new file mode 100644 index 0000000..f0ab779 --- /dev/null +++ b/.github/workflows/skill-review.yml @@ -0,0 +1,13 @@ +name: Skill Review +on: + pull_request: + paths: ['**/SKILL.md'] +jobs: + review: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + steps: + - uses: actions/checkout@v4 + - uses: tesslio/skill-review@22e928dd837202b2b1d1397e0114c92e0fae5ead # main