fix: make runName optional#48
Conversation
fa25212 to
2b09b54
Compare
a74d355 to
84e6a60
Compare
84e6a60 to
e366e0e
Compare
📝 WalkthroughWalkthroughThe change introduces deterministic request IDs derived from run sources, input, options, and optional run names. Scheduler, context, tracker, actor, task, Apify, and run clients now propagate request IDs through extended run objects and batch records. Public start and call contracts move run names into options. Tests, mocks, E2E calls, hashing utilities, and actor-local dependency installation are updated accordingly. Poem
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/run-scheduler.ts (1)
90-104: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winKeep the request ID derived from the adapted request.
requestRunStart()/startRun()key the pool before adaptation, butprocessRunRequest()stores the run under the adapted request ID. When the adapter changes the hash (for example, unnamed runs oncefixedInputis merged), lookup and dedupe can diverge. Compute the key once from the adapted request, or carry the original key through tracking.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/run-scheduler.ts` around lines 90 - 104, The request pool key must match the ID used by processRunRequest after adaptation. Update requestRunStart and startRun to adapt the RunStartRequest before calling getRequestId and pool.findOrAddRequest, computing the adapted ID once and reusing it for lookup/deduplication while preserving the adapted request for processing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Around line 58-59: Update the package version in package.json from 0.9.0-rc.1
to the appropriate bumped release version for the request identity and public
client contract changes before publishing.
In `@src/__unit__/mocks.ts`:
- Around line 8-9: Update createActorRunMock to construct a complete valid
ExtendedActorRun with defaults for required fields such as requestId, id, and
status, then spread params over those defaults. Remove the direct cast of
arbitrary partial data so overrides remain type-safe and the mock always
satisfies the ExtendedActorRun contract.
In `@src/clients/actor-client.ts`:
- Around line 103-107: Strip the call-only waitSecs option before invoking start
in the ActorClient call flow, while continuing to pass it to waitForFinish.
Apply the same change to src/clients/actor-client.ts lines 103-107 and
src/clients/task-client.ts lines 90-93 so waitSecs does not affect request-id
hashing or Run creation; preserve all other options.
In `@src/clients/task-client.test.ts`:
- Around line 110-124: Update the relevant task-client tests, including the
“generates a request ID if no run name is provided” case and the additional
indicated assertions, to include the expected requestId in each returned-result
objectContaining check. Use the mock run’s request identifier so the tests
verify requestId propagation alongside id and status.
In `@src/clients/task-client.ts`:
- Line 48: Update the warning in the duplicate-enqueue branch of the task client
to describe duplicate requests, including unnamed identical requests, rather
than only duplicate run names. Reuse the Actor client wording for “identical Run
requests, or requests with the same name,” while preserving the existing
requestId-prefixed logger and readable formatting.
In `@src/entities/run-source.ts`:
- Around line 35-38: Add unit tests for RunSource.getRequestId covering the
runName override and verifying generated IDs change when source ID, input, or
options differ. Use the existing RunSource test setup and assert stable
request-ID behavior without altering the implementation.
In `@src/types.ts`:
- Around line 508-511: Update the JSDoc for exported RunRecord in src/types.ts
lines 508-511 to state that records are keyed by request ID rather than run
name. Also update the final comment in src/context/client-context.ts lines 52-62
to refer to a request ID; no code behavior changes are needed.
In `@src/utils/hash.ts`:
- Around line 3-7: Update stringifyObject to recursively canonicalize object
keys without using the top-level key list as JSON.stringify’s global whitelist,
preserving every nested field. In src/utils/hash.ts lines 3-7, change
stringifyObject accordingly; in src/utils/hash.test.ts lines 17-20, add a case
where objects differing only in a nested value produce different hashes.
- Around line 10-12: Update hashObject to serialize objects with a recursive,
stable stringify that preserves nested properties and deterministic key ordering
before passing the result to murmur3. Replace or fix stringifyObject so nested
structures cannot collapse into the same hash, while preserving the existing
hexadecimal hash output.
---
Outside diff comments:
In `@src/run-scheduler.ts`:
- Around line 90-104: The request pool key must match the ID used by
processRunRequest after adaptation. Update requestRunStart and startRun to adapt
the RunStartRequest before calling getRequestId and pool.findOrAddRequest,
computing the adapted ID once and reusing it for lookup/deduplication while
preserving the adapted request for processing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 617d743b-f737-4355-8527-a19d5330d283
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.json,!package-lock.jsonsrc/utils/__snapshots__/hash.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (25)
e2e-test.jspackage.jsonsrc/__e2e__/resurrection.tssrc/__e2e__/test-actor-runner.tssrc/__unit__/mocks.tssrc/clients/actor-client.test.tssrc/clients/actor-client.tssrc/clients/apify-client.test.tssrc/clients/apify-client.tssrc/clients/run-client.test.tssrc/clients/run-client.tssrc/clients/task-client.test.tssrc/clients/task-client.tssrc/context/client-context.test.tssrc/context/client-context.tssrc/entities/run-source.test.tssrc/entities/run-source.tssrc/errors.tssrc/run-scheduler.test.tssrc/run-scheduler.tssrc/run-tracker.test.tssrc/run-tracker.tssrc/types.tssrc/utils/hash.test.tssrc/utils/hash.ts
| "apify-client": "^2.22.3", | ||
| "murmurhash-js": "^1.0.0" |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Bump the package version for this API change.
The package remains 0.9.0-rc.1 while request identity and public client contracts change. Update the appropriate release version before publishing. As per coding guidelines, “Remember to bump the patch/minor/major version number.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` around lines 58 - 59, Update the package version in
package.json from 0.9.0-rc.1 to the appropriate bumped release version for the
request identity and public client contract changes before publishing.
Source: Coding guidelines
| export function createActorRunMock(params: Partial<ExtendedActorRun> = {}): ExtendedActorRun { | ||
| return { ...params } as ExtendedActorRun; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Keep the mock valid instead of casting arbitrary partial data.
This cast allows {} to masquerade as an ExtendedActorRun, hiding missing requestId, id, or status fields in tests. Restore valid defaults and only spread overrides onto them.
Proposed fix
export function createActorRunMock(params: Partial<ExtendedActorRun> = {}): ExtendedActorRun {
- return { ...params } as ExtendedActorRun;
+ return {
+ id: 'mock-run-id',
+ requestId: 'mock-request-id',
+ status: 'RUNNING',
+ startedAt: new Date('2024-01-01T00:00:00.000Z'),
+ ...params,
+ } as ExtendedActorRun;
}📝 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.
| export function createActorRunMock(params: Partial<ExtendedActorRun> = {}): ExtendedActorRun { | |
| return { ...params } as ExtendedActorRun; | |
| export function createActorRunMock(params: Partial<ExtendedActorRun> = {}): ExtendedActorRun { | |
| return { | |
| id: 'mock-run-id', | |
| requestId: 'mock-request-id', | |
| status: 'RUNNING', | |
| startedAt: new Date('2024-01-01T00:00:00.000Z'), | |
| ...params, | |
| } as ExtendedActorRun; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/__unit__/mocks.ts` around lines 8 - 9, Update createActorRunMock to
construct a complete valid ExtendedActorRun with defaults for required fields
such as requestId, id, and status, then spread params over those defaults.
Remove the direct cast of arbitrary partial data so overrides remain type-safe
and the mock always satisfies the ExtendedActorRun contract.
| override async call(input?: object, options?: ExtendedActorCallOptions): Promise<ExtendedActorRun> { | ||
| const startedRun = await this.start(input, options); | ||
| return this.apifyClient | ||
| .extendedRunClient(runName, startedRun.id) | ||
| .extendedRunClient(startedRun.requestId, startedRun.id) | ||
| .waitForFinish({ waitSecs: options?.waitSecs }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file list ==\n'
git ls-files 'src/clients/*' 'src/*types*' | sed -n '1,120p'
printf '\n== outlines ==\n'
ast-grep outline src/clients/actor-client.ts --view expanded || true
ast-grep outline src/clients/task-client.ts --view expanded || true
printf '\n== actor-client relevant slice ==\n'
sed -n '1,180p' src/clients/actor-client.ts
printf '\n== task-client relevant slice ==\n'
sed -n '1,170p' src/clients/task-client.ts
printf '\n== call option types / start option types ==\n'
rg -n "waitSecs|ExtendedActorCallOptions|ExtendedTaskCallOptions|start\\(" src -g '*.ts'Repository: apify-professional-services/apify-orchestrator
Length of output: 17704
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== types around call/start options ==\n'
sed -n '170,340p' src/types.ts
printf '\n== RunSource and request-id logic ==\n'
sed -n '1,240p' src/entities/run-source.ts
printf '\n== apify-client wrapper methods ==\n'
sed -n '1,240p' src/clients/apify-client.ts
printf '\n== any tests around waitSecs or duplicate runs ==\n'
rg -n "waitSecs|duplicate|requestId|runName" src/clients/*.test.ts src/entities/*.test.ts src -g '*.ts'Repository: apify-professional-services/apify-orchestrator
Length of output: 50402
🌐 Web query:
Apify Client JS ActorCallOptions waitSecs TaskCallOptions waitSecs start options requestId hashing
💡 Result:
In the Apify Client for JavaScript, waitSecs is a property available in ActorCallOptions and TaskCallOptions that defines the maximum duration in seconds to wait for an actor or task run to finish [1][2][3][4]. When using the call method (e.g., client.actor('actor-id').call), the client starts the run and automatically polls for its completion [4]. If the run finishes within the specified waitSecs, the call method resolves with the run object; if the time limit is exceeded, the wait concludes [3][4]. Regarding request ID hashing or related options, there is no direct reference in the standard ActorCallOptions or TaskCallOptions for "hashing" options in the Apify Client JS documentation [2][3][5]. The available options for starting and calling actors/tasks primarily focus on runtime configuration such as build tags, memory limits, timeouts, and webhooks [3][5]. If you are looking for mechanisms to ensure request idempotency or unique identification, Apify's API typically handles this through the platform's internal run management, and users typically manage uniqueness via input parameters rather than client-side hashing options [3][4]. Key differences between options: - waitSecs (used in call): Implemented via client-side polling; wait time is indefinite by default unless specified [3][4]. - waitForFinish (used in start): A parameter that executes on the API side directly, limited to 60 seconds [5]. Top results: [1] https://docs.apify.com/api/client/js/reference/interface/ActorCallOptions [3] https://github.com/apify/apify-client-js/blob/master/src/resource_clients/actor.ts [4] https://docs.apify.com/api/client/js/reference/class/ActorClient [5] https://docs.apify.com/api/client/js/reference/interface/ActorStartOptions.md
Citations:
- 1: https://docs.apify.com/api/client/js/reference/interface/ActorCallOptions
- 2: https://docs.apify.com/api/client/js/reference/interface/ActorCallOptions.md
- 3: https://github.com/apify/apify-client-js/blob/master/src/resource_clients/actor.ts
- 4: https://docs.apify.com/api/client/js/reference/class/ActorClient
- 5: https://docs.apify.com/api/client/js/reference/interface/ActorStartOptions.md
Strip waitSecs before starting the Run
waitSecs is call-only, but both src/clients/actor-client.ts and src/clients/task-client.ts pass it into start, so it changes the request-id hash. Identical calls with different wait times can create duplicate Runs.
📍 Affects 2 files
src/clients/actor-client.ts#L103-L107(this comment)src/clients/task-client.ts#L90-L93
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/clients/actor-client.ts` around lines 103 - 107, Strip the call-only
waitSecs option before invoking start in the ActorClient call flow, while
continuing to pass it to waitForFinish. Apply the same change to
src/clients/actor-client.ts lines 103-107 and src/clients/task-client.ts lines
90-93 so waitSecs does not affect request-id hashing or Run creation; preserve
all other options.
| expect(result).toStrictEqual(expect.objectContaining({ id: mockRun.id, status: mockRun.status })); | ||
| }); | ||
| }); | ||
|
|
||
| describe('call', () => { | ||
| it('throws if called without a run name', async () => { | ||
| await expect(taskClient.call({ key: 'value1' })).rejects.toThrow(); | ||
| it('generates a request ID if no run name is provided', async () => { | ||
| const result = await taskClient.start({ key: 'value1' }); | ||
|
|
||
| expect(apifyClient.findOrStartRun).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| source: runSource, | ||
| runName: undefined, | ||
| input: { key: 'value1' }, | ||
| options: undefined, | ||
| }), | ||
| ); | ||
| expect(result).toStrictEqual(expect.objectContaining({ id: mockRun.id, status: mockRun.status })); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Assert the returned requestId.
These assertions only check id and status, so request-identifier propagation can regress unnoticed—including in the test named “generates a request ID.” Include the expected requestId in each objectContaining.
As per coding guidelines, “Remember to add/fix unit tests.” <coding_guidelines>
Also applies to: 150-152, 299-299
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/clients/task-client.test.ts` around lines 110 - 124, Update the relevant
task-client tests, including the “generates a request ID if no run name is
provided” case and the additional indicated assertions, to include the expected
requestId in each returned-result objectContaining check. Use the mock run’s
request identifier so the tests verify requestId propagation alongside id and
status.
Source: Coding guidelines
| this.context.logger.prefixed(runRequest.runName).warning('Skipping enqueuing duplicate run name.'); | ||
| const requestId = this.runSource.getRequestId(runRequest.input, runRequest.options, runRequest.runName); | ||
| if (requestIds.has(requestId)) { | ||
| this.context.logger.prefixed(requestId).warning('Skipping enqueuing duplicate run name.'); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Describe duplicate requests, not duplicate run names.
Unnamed identical requests also enter this branch, so this warning is misleading. Use the Actor client’s “identical Run requests, or requests with the same name” wording.
As per coding guidelines, events should be logged in a format that is “easy to read and debug.” <coding_guidelines>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/clients/task-client.ts` at line 48, Update the warning in the
duplicate-enqueue branch of the task client to describe duplicate requests,
including unnamed identical requests, rather than only duplicate run names.
Reuse the Actor client wording for “identical Run requests, or requests with the
same name,” while preserving the existing requestId-prefixed logger and readable
formatting.
Source: Coding guidelines
| getRequestId(input?: unknown, options?: unknown, runName?: string): string { | ||
| if (runName) return runName; | ||
| return hashObject({ sourceType: this.type, sourceId: this.id, input, options }); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add direct coverage for request-ID generation.
This new identity path has no corresponding test coverage. Test that runName wins and that generated IDs vary with source ID, input, and options. As per coding guidelines, “Remember to add/fix unit tests”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/entities/run-source.ts` around lines 35 - 38, Add unit tests for
RunSource.getRequestId covering the runName override and verifying generated IDs
change when source ID, input, or options differ. Use the existing RunSource test
setup and assert stable request-ID behavior without altering the implementation.
Source: Coding guidelines
| /** | ||
| * A record of Runs, having their names as keys and their `ActorRun` objects as values. | ||
| * A record of Runs, having their names as keys and their `ExtendedActorRun` objects as values. | ||
| */ | ||
| export type RunRecord = Record<string, ActorRun>; | ||
| export type RunRecord = { [requestId: string]: ExtendedActorRun }; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update stale run-name documentation.
Run identity is now request-ID based, but these comments still describe name-based lookup/keys.
src/types.ts#L508-L511: state thatRunRecordis keyed by request ID.src/context/client-context.ts#L52-L62: change the final comment to refer to a request ID, not a name.
As per path instructions, “Where JSDoc exists on exported APIs, verify it is accurate.”
📍 Affects 2 files
src/types.ts#L508-L511(this comment)src/context/client-context.ts#L52-L62
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/types.ts` around lines 508 - 511, Update the JSDoc for exported RunRecord
in src/types.ts lines 508-511 to state that records are keyed by request ID
rather than run name. Also update the final comment in
src/context/client-context.ts lines 52-62 to refer to a request ID; no code
behavior changes are needed.
Source: Path instructions
| function stringifyObject(obj: unknown): string { | ||
| return JSON.stringify( | ||
| obj, | ||
| Object.keys(obj as object).sort((a, b) => a.localeCompare(b)), | ||
| ); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'FILES:\n'
git ls-files 'src/utils/hash.ts' 'src/utils/hash.test.ts'
printf '\nOUTLINE hash.ts:\n'
ast-grep outline src/utils/hash.ts --view expanded || true
printf '\nOUTLINE hash.test.ts:\n'
ast-grep outline src/utils/hash.test.ts --view expanded || true
printf '\nhash.ts contents:\n'
cat -n src/utils/hash.ts
printf '\nhash.test.ts contents:\n'
cat -n src/utils/hash.test.ts
printf '\nNode probe:\n'
node - <<'JS'
const obj = { a: { b: 1, c: 2 }, z: 3 };
const keys = Object.keys(obj).sort();
console.log('replacer keys:', keys);
console.log('stringified:', JSON.stringify(obj, keys));
console.log('full stringify:', JSON.stringify(obj));
JSRepository: apify-professional-services/apify-orchestrator
Length of output: 2249
Canonicalize nested objects without dropping fields. JSON.stringify(..., Object.keys(obj).sort()) acts as a global whitelist, so nested objects lose keys that aren’t present at the top level; e.g. { a: { b: 1 } } and { a: { b: 2 } } can hash to the same ID. Add a case that differs only in a nested value and ensure the hashes diverge.
📍 Affects 2 files
src/utils/hash.ts#L3-L7(this comment)src/utils/hash.test.ts#L17-L20
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/hash.ts` around lines 3 - 7, Update stringifyObject to recursively
canonicalize object keys without using the top-level key list as
JSON.stringify’s global whitelist, preserving every nested field. In
src/utils/hash.ts lines 3-7, change stringifyObject accordingly; in
src/utils/hash.test.ts lines 17-20, add a case where objects differing only in a
nested value produce different hashes.
| export function hashObject(obj: unknown): string { | ||
| const str = stringifyObject(obj); | ||
| return murmur3(str).toString(16); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
printf '\n== hash utility ==\n'
git ls-files 'src/utils/*' 'src/**/hash*' 'src/**/stringify*' | sed 's#^`#-` #'
printf '\n-- src/utils/hash.ts --\n'
cat -n src/utils/hash.ts
printf '\n== hash usages ==\n'
rg -n "hashObject\(|murmur3\(|stringifyObject\(" src test
printf '\n== related serializer ==\n'
for f in $(git ls-files 'src/**/stringify*' 'src/**/serialize*'); do
echo "\n-- $f --"
cat -n "$f"
doneRepository: apify-professional-services/apify-orchestrator
Length of output: 3097
🏁 Script executed:
set -euo pipefail
printf '\n== package metadata ==\n'
cat -n package.json | sed -n '1,220p'
printf '\n== dependency lock mentions ==\n'
rg -n '"murmurhash-js"|murmurhash-js' package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json 2>/dev/null || trueRepository: apify-professional-services/apify-orchestrator
Length of output: 3670
🏁 Script executed:
set -euo pipefail
printf '\n== src/entities/run-source.ts ==\n'
cat -n src/entities/run-source.ts
printf '\n== src/utils/hash.test.ts ==\n'
cat -n src/utils/hash.test.ts
printf '\n== quick serializer probe ==\n'
node --input-type=module <<'JS'
import { murmur3 } from 'murmurhash-js';
function stringifyObject(obj) {
return JSON.stringify(obj, Object.keys(obj).sort((a,b) => a.localeCompare(b)));
}
const samples = [
[{a:{x:1,y:2},b:3}, {a:{x:1,y:9},b:3}],
[{a:1,b:2}, {a:2,b:1}],
[{a:{nested:{x:1}}, b:2}, {a:{nested:{x:9}}, b:2}],
];
for (const [x,y] of samples) {
const sx = stringifyObject(x);
const sy = stringifyObject(y);
console.log('X', sx);
console.log('Y', sy);
console.log('equal?', sx === sy);
console.log('hashX', murmur3(sx).toString(16));
console.log('hashY', murmur3(sy).toString(16));
console.log('---');
}
JSRepository: apify-professional-services/apify-orchestrator
Length of output: 5027
🏁 Script executed:
set -euo pipefail
printf '\n== find any request-key logic ==\n'
rg -n "requestKey|hashObject\(" src testRepository: apify-professional-services/apify-orchestrator
Length of output: 4410
Use a deep stable serializer before hashing. src/utils/hash.ts drops nested properties because the replacer array only contains the top-level keys, so different inputs can collapse to the same request ID and get deduplicated as the same run. A recursive stable stringify is needed here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/hash.ts` around lines 10 - 12, Update hashObject to serialize
objects with a recursive, stable stringify that preserves nested properties and
deterministic key ordering before passing the result to murmur3. Replace or fix
stringifyObject so nested structures cannot collapse into the same hash, while
preserving the existing hexadecimal hash output.
startandcallsignature by movingrunNameinsideoptions, making it coherent with the Task client.requestId, which identifies a specific request, and corresponds to therunName, if provided.requestIdwhenrunNameis not provided, hashing the resource ID, input, and options.Addresses #35.
Updates Actor and Task client APIs so
runNameis optional and supplied through options, aligning theirstartandcallsignatures. Introduces deterministicrequestIdgeneration and propagates request IDs through scheduling, tracking, run clients, batching, and error handling to reliably deduplicate and identify runs. Adds stable hashing support and updates tests and end-to-end dependencies accordingly.