Skip to content
This repository was archived by the owner on Jun 9, 2026. It is now read-only.

Hb balances patch test process#445

Draft
atticusofsparta wants to merge 4 commits into
developfrom
hb-balances-patch-test-process
Draft

Hb balances patch test process#445
atticusofsparta wants to merge 4 commits into
developfrom
hb-balances-patch-test-process

Conversation

@atticusofsparta

@atticusofsparta atticusofsparta commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes

    • Improved primary-names synchronization: patch generation now normalizes removals and skips empty updates to avoid sending no-op patches.
  • New Features

    • Added a utility to build primary-names patch payloads that returns nil when nothing needs patching.
    • Added a message-sending helper that assembles messages, tags, and optional reply handling.
  • Tests

    • Added end-to-end test tooling and test data for balance-patch workflows to build and record test processes.

@coderabbitai

coderabbitai Bot commented Nov 13, 2025

Copy link
Copy Markdown

Walkthrough

Adds a new Lua patch builder createPrimaryNamesPatch that composes affectedPrimaryNamesAddresses from HyperbeamSync.primaryNames (names, owners, requests), enriches owner entries via primary_names.getPrimaryNameDataWithOwnerFromAddress, normalizes empty sub-tables, and returns the payload or nil. Also adds test tooling and fixtures (TypeScript scripts, Lua helper, and JSON test data) for balance/process creation.

Changes

Cohort / File(s) Summary
Primary Names Patch Logic
patches/2025-11-06-primary-names-hb-sync-fix.lua
New global function createPrimaryNamesPatch() that reads HyperbeamSync.primaryNames, converts removals into empty strings/tables, enriches owner entries, normalizes empty sub-tables, and returns affectedPrimaryNamesAddresses or nil.
Hyperbeam / Primary Names integration
src/hb.lua, src/primary_names.lua
src/hb.lua: added require(".src.primary_names"), updated explicit type annotation for affectedPrimaryNamesAddresses, and now populates owners by calling primaryNames.getPrimaryNameDataWithOwnerFromAddress(owner). src/primary_names.lua: minor formatting change inside getPrimaryNameDataWithOwnerFromAddress return (no API change).
Test tooling: balances -> Lua generator
test-process-balances-patch/create_balances.ts
New TypeScript script: reads patch-balances.json, writes balances.lua with Balances["<address>"] = "<balance>" entries.
Test process orchestration & fixtures
test-process-balances-patch/create_test_process.ts, test-process-balances-patch/test-process.json, test-process-balances-patch/test-wallet.json
New end-to-end test script that assembles and runs an ARIO test process (loads Lua, submits patch messages), plus test-process.json (process metadata/IDs/timestamp) and test-wallet.json (RSA JWK test key).
AO send helper (test)
test-process-balances-patch/ao_send.lua
New Lua helper ao.send(msg) that validates and constructs messages, manages Tags/Reference, optionally queues to ao.outbox.Messages, and provides reply handling via Handlers (onReply/receive).

Sequence Diagram(s)

sequenceDiagram
    participant HB as HyperbeamSync
    participant Patch as createPrimaryNamesPatch()
    participant PN as primary_names
    participant Out as affectedPrimaryNamesAddresses

    Note over HB,Patch: build patch from current primary-names state
    HB->>Patch: read names, owners, requests
    Patch->>Patch: iterate names -> removals -> set to ""
    Patch->>Patch: iterate requests -> removals -> set to {}
    Patch->>PN: getPrimaryNameDataWithOwnerFromAddress(owner)
    PN-->>Patch: PrimaryNameInfo or {}
    Patch->>Patch: populate owners with returned data
    Patch->>Patch: normalize empty sub-tables (set {} or nil)
    Patch->>Out: return payload or nil
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Review normalization/removal semantics in patches/2025-11-06-primary-names-hb-sync-fix.lua.
  • Verify src/hb.lua type annotation and owner enrichment call site.
  • Inspect ao.send interactions with Handlers and ao.outbox.Messages and file I/O in test scripts.

Possibly related PRs

Suggested reviewers

  • arielmelendez

Poem

🐇 I nibble names and stitch them tight,
Owners found by lantern-light,
Balances spun into a Lua thread,
Patch hops out and books are read,
Tests clap paws — the sync stays bright.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Hb balances patch test process' is vague and generic, using non-descriptive terms that don't clearly convey what the changeset does. Consider a more specific title that clearly describes the main change, such as 'Add test process for Hyperbeam balances patching' or 'Create Hyperbeam balances patch integration test'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hb-balances-patch-test-process

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

codecov-commenter commented Nov 13, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.36%. Comparing base (36fd94d) to head (654655a).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #445   +/-   ##
========================================
  Coverage    91.36%   91.36%           
========================================
  Files           10       10           
  Lines         3230     3230           
========================================
  Hits          2951     2951           
  Misses         279      279           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@atticusofsparta atticusofsparta force-pushed the hb-balances-patch-test-process branch from 2d5e55d to b457847 Compare November 13, 2025 18:55

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test-process-balances-patch/create_test_process.ts (1)

5-28: Use fileURLToPath for cross-platform __dirname.
new URL(import.meta.url).pathname leaves a leading slash on Windows (/C:/...), so every subsequent path.join produces \C:\... and the script can’t find any of the bundled Lua files. Switch to fileURLToPath so the tooling works on Windows as well.

-import fs from 'fs';
-import path from 'path';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
 
-const __dirname = path.dirname(new URL(import.meta.url).pathname);
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36fd94d and b457847.

📒 Files selected for processing (7)
  • patches/2025-11-06-primary-names-hb-sync-fix.lua (1 hunks)
  • src/hb.lua (3 hunks)
  • src/primary_names.lua (0 hunks)
  • test-process-balances-patch/create_balances.ts (1 hunks)
  • test-process-balances-patch/create_test_process.ts (1 hunks)
  • test-process-balances-patch/test-process.json (1 hunks)
  • test-process-balances-patch/test-wallet.json (1 hunks)
💤 Files with no reviewable changes (1)
  • src/primary_names.lua
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.923Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.797Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.
📚 Learning: 2025-11-03T22:41:58.658Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.

Applied to files:

  • src/hb.lua
  • patches/2025-11-06-primary-names-hb-sync-fix.lua
  • test-process-balances-patch/create_balances.ts
📚 Learning: 2025-10-08T16:44:07.797Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.797Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.

Applied to files:

  • src/hb.lua
  • patches/2025-11-06-primary-names-hb-sync-fix.lua
  • test-process-balances-patch/create_balances.ts
📚 Learning: 2025-10-08T16:45:02.923Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.923Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.

Applied to files:

  • src/hb.lua
  • patches/2025-11-06-primary-names-hb-sync-fix.lua
  • test-process-balances-patch/create_balances.ts
🧬 Code graph analysis (3)
test-process-balances-patch/create_test_process.ts (3)
tools/bootstrapping/send-message.mjs (1)
  • jwk (6-6)
tests/code-patches/2025-10-07-hb-balances-patch/index.test.mjs (1)
  • authority (96-96)
tests/monitor/cranking-monitor.test.mjs (1)
  • ao (12-14)
src/hb.lua (1)
src/primary_names.lua (1)
  • primaryNames.getPrimaryNameDataWithOwnerFromAddress (310-322)
patches/2025-11-06-primary-names-hb-sync-fix.lua (1)
src/primary_names.lua (1)
  • primaryNames.getPrimaryNameDataWithOwnerFromAddress (310-322)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: sdk

Comment on lines +3 to +12
const balances = fs.readFileSync('patch-balances.json', 'utf8');
const balanceObj = JSON.parse(balances);

let balancesLua = '';

for (const [address, balance] of Object.entries(balanceObj)) {
balancesLua += `Balances["${address}"] = "${balance}";\n`;
}

fs.writeFileSync('balances.lua', balancesLua);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix path resolution for patch-balances.json.
Running this script from the repo root blows up with ENOENT because both reads and writes are relative to the current working directory, not the script’s directory. That means the new tooling fails in the default workflow (tsx test-process-balances-patch/create_balances.ts). Please anchor the paths to the file location so it is runnable from anywhere.

-import fs from 'fs';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
 
-const balances = fs.readFileSync('patch-balances.json', 'utf8');
-const balanceObj = JSON.parse(balances);
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const balances = fs.readFileSync(
+  path.join(__dirname, 'patch-balances.json'),
+  'utf8',
+);
+const balanceObj = JSON.parse(balances);
@@
-fs.writeFileSync('balances.lua', balancesLua);
+fs.writeFileSync(path.join(__dirname, 'balances.lua'), balancesLua);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const balances = fs.readFileSync('patch-balances.json', 'utf8');
const balanceObj = JSON.parse(balances);
let balancesLua = '';
for (const [address, balance] of Object.entries(balanceObj)) {
balancesLua += `Balances["${address}"] = "${balance}";\n`;
}
fs.writeFileSync('balances.lua', balancesLua);
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const balances = fs.readFileSync(
path.join(__dirname, 'patch-balances.json'),
'utf8',
);
const balanceObj = JSON.parse(balances);
let balancesLua = '';
for (const [address, balance] of Object.entries(balanceObj)) {
balancesLua += `Balances["${address}"] = "${balance}";\n`;
}
fs.writeFileSync(path.join(__dirname, 'balances.lua'), balancesLua);
🤖 Prompt for AI Agents
In test-process-balances-patch/create_balances.ts around lines 3 to 12, path
handling is currently relative to the CWD which causes ENOENT when the script is
run from a different directory; change the reads/writes to use the script file's
directory: obtain the script directory via fileURLToPath(import.meta.url) and
path.dirname (or __dirname if using CommonJS), then join that directory with
'patch-balances.json' for fs.readFileSync and with 'balances.lua' for
fs.writeFileSync so the script works when invoked from any working directory.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test-process-balances-patch/ao_send.lua (1)

132-132: Consider commenting out or removing the example usage.

This line appears to be a demonstration call that will execute whenever the module is loaded. In test tooling, it's generally better to comment out example calls or move them to separate test files to avoid unintended side effects.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b457847 and 84826ba.

📒 Files selected for processing (3)
  • test-process-balances-patch/ao_send.lua (1 hunks)
  • test-process-balances-patch/create_test_process.ts (1 hunks)
  • test-process-balances-patch/test-process.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test-process-balances-patch/create_test_process.ts
  • test-process-balances-patch/test-process.json
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.923Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.797Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.
📚 Learning: 2025-11-03T22:41:58.658Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.

Applied to files:

  • test-process-balances-patch/ao_send.lua
📚 Learning: 2025-10-08T16:45:02.923Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.923Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.

Applied to files:

  • test-process-balances-patch/ao_send.lua
📚 Learning: 2025-10-08T16:44:07.797Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.797Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.

Applied to files:

  • test-process-balances-patch/ao_send.lua
🧬 Code graph analysis (1)
test-process-balances-patch/ao_send.lua (1)
test-process-balances-patch/test-process.lua (2)
  • padZero32 (6324-6326)
  • isArray (6352-6365)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: sdk
🔇 Additional comments (2)
test-process-balances-patch/ao_send.lua (2)

1-46: Helper functions look good.

The utility functions padZero32, _includes, and isArray are all correctly implemented and serve their purposes well.


48-50: Reference initialization is correct.

Properly initializes the global reference counter if not already set.

Comment on lines +52 to +77
--- Sends a message.
-- @function send
-- @tparam {table} msg The message to send
ao.send = function(msg)
assert(type(msg) == "table", "msg should be a table")
ao.reference = ao.reference + 1
local referenceString = tostring(ao.reference)

local message = {
Target = msg.Target,
Data = msg.Data,
Anchor = padZero32(ao.reference),
Tags = {
{ name = "Data-Protocol", value = "ao" },
{ name = "Variant", value = "ao.TN.1" },
{ name = "Type", value = "Message" },
{ name = "Reference", value = referenceString },
},
}

-- if custom tags in root move them to tags
for k, v in pairs(msg) do
if not _includes({ "Target", "Data", "Anchor", "Tags", "From" })(k) then
table.insert(message.Tags, { name = k, value = v })
end
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate required msg.Target field.

The function uses msg.Target at line 61 but never validates its presence. If msg.Target is nil, the constructed message will have a nil Target field, which could break downstream logic that expects a valid target address.

Apply this diff to add Target validation:

 ao.send = function(msg)
 	assert(type(msg) == "table", "msg should be a table")
+	assert(msg.Target, "msg.Target is required")
 	ao.reference = ao.reference + 1

Clarify the exclusion of "From" field.

Line 74 excludes "From" from being migrated to Tags, but the "From" field is never actually set in the message object. This exclusion seems unnecessary unless "From" is intended to be set elsewhere.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--- Sends a message.
-- @function send
-- @tparam {table} msg The message to send
ao.send = function(msg)
assert(type(msg) == "table", "msg should be a table")
ao.reference = ao.reference + 1
local referenceString = tostring(ao.reference)
local message = {
Target = msg.Target,
Data = msg.Data,
Anchor = padZero32(ao.reference),
Tags = {
{ name = "Data-Protocol", value = "ao" },
{ name = "Variant", value = "ao.TN.1" },
{ name = "Type", value = "Message" },
{ name = "Reference", value = referenceString },
},
}
-- if custom tags in root move them to tags
for k, v in pairs(msg) do
if not _includes({ "Target", "Data", "Anchor", "Tags", "From" })(k) then
table.insert(message.Tags, { name = k, value = v })
end
end
--- Sends a message.
-- @function send
-- @tparam {table} msg The message to send
ao.send = function(msg)
assert(type(msg) == "table", "msg should be a table")
assert(msg.Target, "msg.Target is required")
ao.reference = ao.reference + 1
local referenceString = tostring(ao.reference)
local message = {
Target = msg.Target,
Data = msg.Data,
Anchor = padZero32(ao.reference),
Tags = {
{ name = "Data-Protocol", value = "ao" },
{ name = "Variant", value = "ao.TN.1" },
{ name = "Type", value = "Message" },
{ name = "Reference", value = referenceString },
},
}
-- if custom tags in root move them to tags
for k, v in pairs(msg) do
if not _includes({ "Target", "Data", "Anchor", "Tags", "From" })(k) then
table.insert(message.Tags, { name = k, value = v })
end
end
🤖 Prompt for AI Agents
In test-process-balances-patch/ao_send.lua around lines 52 to 77, add validation
to ensure msg.Target is present and of the expected type (e.g., non-nil string)
before constructing the message (use assert or return an error), and adjust the
tag-migration exclusion: either remove "From" from the exclusion list if it is
never set on msg, or ensure msg.From is set on the message object before
excluding it; implement the simpler fix by deleting "From" from the not-includes
list so custom "From" passed in msg gets migrated into Tags.

Comment on lines +97 to +104
-- clone message info and add to outbox
local extMessage = {}
for k, v in pairs(message) do
extMessage[k] = v
end

-- add message to outbox
table.insert(ao.outbox.Messages, extMessage)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Shallow copy may cause unintended side effects.

The message cloning uses a shallow copy, which means nested tables (like the Tags array) will be shared by reference between message and extMessage. If either copy is modified after insertion into the outbox, the other will be affected.

Consider whether a deep copy is needed, or at minimum document this limitation if the shallow copy is intentional.

🤖 Prompt for AI Agents
In test-process-balances-patch/ao_send.lua around lines 97 to 104, the code
performs a shallow copy of message into extMessage which will share nested
tables (e.g., Tags) by reference and can cause side effects; either implement a
deep copy of message before inserting into ao.outbox.Messages (recursively clone
tables and values) or explicitly document in a comment that the shallow copy is
intentional and that nested tables will be shared, and if shared mutation is
unsafe, replace the shallow copy with a recursive deep-copy helper and use it
here.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
test-process-balances-patch/create_test_process.ts (4)

33-168: Consider importing ao.send from ao_send.lua instead of embedding.

The ao.send implementation (lines 89-164) appears to duplicate code from test-process-balances-patch/ao_send.lua. Embedding this 76-line implementation increases maintenance burden.

Consider one of these approaches:

Option 1: Load ao_send.lua and embed it:

const aoSendLua = fs.readFileSync(
  path.join(__dirname, 'ao_send.lua'),
  'utf8',
);
const patchHbLua = `
  ${balancesLua}\n
  ${aoSendLua}\n
  ao.send({ device = "patch@1.0", balances = { device = "trie@1.0" } })
`;

Option 2: If the inline definition is intentional for testing specific behavior, add a comment explaining why the duplication is necessary.


166-166: Add comment explaining the purpose of this ao.send call.

The purpose of sending this device configuration message is not immediately clear. A brief comment would help future maintainers understand why this initialization is necessary.


185-218: Consider adding error handling or logging between message steps.

The script sends five sequential messages without error handling. If any step fails, the error context may be unclear. Consider adding try-catch blocks or at minimum logging between steps to aid debugging.

Example with logging:

console.log('Loading Ario Lua...');
const loadArioId = await ao.message({
  process: processId,
  data: arioLua,
  tags: [{ name: 'Action', value: 'Eval' }],
  signer,
});
console.log(`Loaded Ario: ${loadArioId}`);

console.log('Loading patch code...');
const loadCodeId = await ao.message({
  process: processId,
  data: patchHbLua,
  tags: [{ name: 'Action', value: 'Eval' }],
  signer,
});
console.log(`Loaded patch code: ${loadCodeId}`);

// ... etc

Or wrap the entire sequence in a try-catch block for better error reporting.


220-234: Consider tracking all message IDs in test-process.json.

The output JSON includes loadCodeId and patchBalancesId but omits loadArioId, initBalances2Id, and patchBalances2Id. This makes it harder to track the complete test sequence.

If these IDs might be useful for debugging or verification, include them:

 fs.writeFileSync(
   path.join(__dirname, 'test-process.json'),
   JSON.stringify(
     {
       processId,
       authority,
       scheduler,
+      loadArioId,
       loadCodeId,
       patchBalancesId,
+      initBalances2Id,
+      patchBalances2Id,
       timestamp: Date.now(),
     },
     null,
     2,
   ),
 );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84826ba and 654655a.

📒 Files selected for processing (2)
  • test-process-balances-patch/create_test_process.ts (1 hunks)
  • test-process-balances-patch/test-process.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test-process-balances-patch/test-process.json
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.923Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.797Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.
📚 Learning: 2025-11-03T22:41:58.658Z
Learnt from: atticusofsparta
Repo: ar-io/ar-io-network-process PR: 442
File: src/hb.lua:7-27
Timestamp: 2025-11-03T22:41:58.658Z
Learning: In `src/hb.lua`, the `hb.createBalancesPatch()` function intentionally iterates both `Balances` and `HyperbeamSync.balances` in two separate loops to detect all changes (additions, modifications, and removals) for complete remote store synchronization. The `HyperbeamSync.balances` table holds a snapshot of balances before handlers run, so comparing in both directions ensures every altered address is included in the patch sent to Hyperbeam.

Applied to files:

  • test-process-balances-patch/create_test_process.ts
🧬 Code graph analysis (1)
test-process-balances-patch/create_test_process.ts (3)
tools/bootstrapping/send-message.mjs (1)
  • jwk (6-6)
tests/code-patches/2025-10-07-hb-balances-patch/index.test.mjs (1)
  • authority (96-96)
tests/monitor/cranking-monitor.test.mjs (1)
  • ao (12-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: sdk

import path from 'path';
import { connect, createDataItemSigner } from '@permaweb/aoconnect';

const __dirname = path.dirname(new URL(import.meta.url).pathname);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix Windows path compatibility.

The current __dirname construction fails on Windows because URL.pathname includes a leading slash (e.g., /C:/path). Use fileURLToPath from the url module for cross-platform compatibility.

Apply this diff:

 import fs from 'fs';
 import path from 'path';
+import { fileURLToPath } from 'url';
 import { connect, createDataItemSigner } from '@permaweb/aoconnect';

-const __dirname = path.dirname(new URL(import.meta.url).pathname);
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const __dirname = path.dirname(new URL(import.meta.url).pathname);
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { connect, createDataItemSigner } from '@permaweb/aoconnect';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
🤖 Prompt for AI Agents
In test-process-balances-patch/create_test_process.ts around line 5, the current
construction const __dirname = path.dirname(new URL(import.meta.url).pathname);
is not cross-platform and fails on Windows due to the leading slash in
URL.pathname; replace it by importing fileURLToPath from 'url' and computing
__dirname as path.dirname(fileURLToPath(import.meta.url)) so the path is
normalized correctly on all platforms.

Comment on lines +11 to +13
const jwk = JSON.parse(
fs.readFileSync(path.join(__dirname, 'test-wallet.json'), 'utf8'),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify test-wallet.json is not committed with real credentials.

Ensure that test-wallet.json either contains only test credentials or is included in .gitignore to prevent accidental exposure of real wallet keys.

Run this script to check if test-wallet.json is gitignored:


🏁 Script executed:

#!/bin/bash
# Check if test-wallet.json is in .gitignore

if grep -q "test-wallet.json" .gitignore 2>/dev/null; then
  echo "✓ test-wallet.json is gitignored"
else
  echo "⚠ test-wallet.json is NOT gitignored - verify it contains only test data"
fi

# Check if file exists and show first few lines (without exposing full key)
if [ -f "test-process-balances-patch/test-wallet.json" ]; then
  echo "File exists. First line preview:"
  head -n 1 test-process-balances-patch/test-wallet.json
fi

Length of output: 3258


Add test-wallet.json to .gitignore immediately—it contains private key material.

The file is currently not gitignored and contains an RSA private key in JWK format (including private components d, p, q, dp, dq, qi). Even if this is intended as test data, private key material must never be committed to the repository. Add test-wallet.json to .gitignore and ensure any committed reference to this file is removed from git history.

🤖 Prompt for AI Agents
In test-process-balances-patch/create_test_process.ts lines 11-13: this file
reads test-wallet.json which contains private key material—add test-wallet.json
to .gitignore immediately, remove any tracked instance from the index with git
rm --cached test-process-balances-patch/test-wallet.json and commit that
removal, then permanently purge the file from repository history using a
history-rewrite tool (git filter-repo or BFG) targeting that path and push a
forced update to remote; finally, rotate any keys contained in the file and
verify no other references remain.

@atticusofsparta atticusofsparta marked this pull request as draft November 20, 2025 18:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants