@boxxy/dispatch 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/test-utils/build-mock-bind-change.d.ts +7 -0
- package/dist/test-utils/build-mock-claim-change.d.ts +2 -0
- package/dist/test-utils/build-mock-file-item-change.d.ts +2 -0
- package/dist/test-utils/build-mock-lever-change.d.ts +2 -0
- package/dist/test-utils/build-mock-link-change.d.ts +7 -0
- package/dist/test-utils/build-mock-pause-change.d.ts +2 -0
- package/dist/test-utils/build-mock-pull-request-change.d.ts +7 -0
- package/dist/test-utils/build-mock-settle-change.d.ts +7 -0
- package/dist/test-utils/build-mock-start-change.d.ts +2 -0
- package/dist/test-utils/claim-store-attempt-reader-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-claim-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-contract.d.ts +2 -0
- package/dist/test-utils/claim-store-exclusive-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-guard-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-item-reader-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-pool-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-rollback-cases.d.ts +2 -0
- package/dist/test-utils/create-memory-claim-store.d.ts +2 -0
- package/dist/test-utils/types.d.ts +9 -0
- package/package.json +8 -2
- package/src/test-utils/build-mock-bind-change.ts +36 -0
- package/src/test-utils/build-mock-claim-change.ts +42 -0
- package/src/test-utils/build-mock-file-item-change.ts +18 -0
- package/src/test-utils/build-mock-lever-change.ts +18 -0
- package/src/test-utils/build-mock-link-change.ts +40 -0
- package/src/test-utils/build-mock-pause-change.ts +27 -0
- package/src/test-utils/build-mock-pull-request-change.ts +53 -0
- package/src/test-utils/build-mock-settle-change.ts +58 -0
- package/src/test-utils/build-mock-start-change.ts +31 -0
- package/src/test-utils/claim-store-attempt-reader-cases.ts +327 -0
- package/src/test-utils/claim-store-claim-cases.ts +231 -0
- package/src/test-utils/claim-store-contract.ts +18 -0
- package/src/test-utils/claim-store-exclusive-cases.ts +197 -0
- package/src/test-utils/claim-store-guard-cases.ts +373 -0
- package/src/test-utils/claim-store-item-reader-cases.ts +257 -0
- package/src/test-utils/claim-store-pool-cases.ts +178 -0
- package/src/test-utils/claim-store-rollback-cases.ts +175 -0
- package/src/test-utils/create-memory-claim-store.ts +574 -0
- package/src/test-utils/types.ts +11 -0
package/README.md
CHANGED
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
|
|
10
10
|
boxxy's dispatch module, which claims the next task for an agent and settles each attempt.
|
|
11
11
|
|
|
12
|
-
This version
|
|
12
|
+
This version exports only its package name, as `packageName`, from its main entry. Its
|
|
13
|
+
`./test-utils/*` entry holds `createMemoryClaimStore`, the memory fake of the `ClaimStore` port, and
|
|
14
|
+
`claimStoreContract`, the cases that every `ClaimStore` passes
|
|
15
|
+
([ports and adapters](../../docs/design/boxxy/slice-1.md#ports-and-adapters)). The
|
|
13
16
|
[boxxy README](https://github.com/zgeoff/boxxy#workspace) lists the workspace's packages.
|
|
14
17
|
|
|
15
18
|
## Install
|
|
16
19
|
|
|
20
|
+
Install the package from [npm](https://www.npmjs.com/package/@boxxy/dispatch) with Bun:
|
|
21
|
+
|
|
17
22
|
```bash
|
|
18
23
|
bun add @boxxy/dispatch
|
|
19
24
|
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AgentSessionId, AttemptRow, StoreChange } from '@boxxy/model';
|
|
2
|
+
interface BindInput {
|
|
3
|
+
readonly attempt: AttemptRow;
|
|
4
|
+
readonly agentSession: AgentSessionId;
|
|
5
|
+
}
|
|
6
|
+
export declare function buildMockBindChange(input: BindInput): StoreChange;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AttemptRow, PullRequestRow, StoreChange } from '@boxxy/model';
|
|
2
|
+
interface PullRequestInput {
|
|
3
|
+
readonly attempt: AttemptRow;
|
|
4
|
+
readonly state: PullRequestRow['state'];
|
|
5
|
+
}
|
|
6
|
+
export declare function buildMockPullRequestChange(input: PullRequestInput): StoreChange;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ClaimStore } from '@boxxy/model';
|
|
2
|
+
export interface ClaimStoreOutcome {
|
|
3
|
+
readonly received: unknown;
|
|
4
|
+
readonly expected: unknown;
|
|
5
|
+
}
|
|
6
|
+
export type ClaimStoreCase = readonly [
|
|
7
|
+
title: string,
|
|
8
|
+
check: (store: ClaimStore) => Promise<ClaimStoreOutcome>
|
|
9
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boxxy/dispatch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "boxxy's dispatch: claims the next task for an agent",
|
|
5
5
|
"homepage": "https://github.com/zgeoff/boxxy/tree/main/modules/dispatch#readme",
|
|
6
6
|
"bugs": "https://github.com/zgeoff/boxxy/issues",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
".": {
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"default": "./src/index.ts"
|
|
23
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"./test-utils/*": "./src/test-utils/*.ts"
|
|
24
25
|
},
|
|
25
26
|
"publishConfig": {
|
|
26
27
|
"access": "public"
|
|
@@ -29,6 +30,11 @@
|
|
|
29
30
|
"build": "rm -rf dist && tsrs -p tsconfig.build.json",
|
|
30
31
|
"typecheck": "tsrs --noEmit"
|
|
31
32
|
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@boxxy/model": "0.1.2",
|
|
35
|
+
"tiny-invariant": "1.3.3",
|
|
36
|
+
"ts-pattern": "5.9.2"
|
|
37
|
+
},
|
|
32
38
|
"engines": {
|
|
33
39
|
"bun": ">=1.3",
|
|
34
40
|
"node": ">=24"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { AgentSessionId, AttemptRow, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
|
|
5
|
+
interface BindInput {
|
|
6
|
+
readonly attempt: AttemptRow;
|
|
7
|
+
readonly agentSession: AgentSessionId;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function buildMockBindChange(input: BindInput): StoreChange {
|
|
11
|
+
const attempt = input.attempt;
|
|
12
|
+
const agentSession = input.agentSession;
|
|
13
|
+
const atcSession = brandSchemas.atcSessionId.parse('atc-session-1');
|
|
14
|
+
const seenAt = brandSchemas.instant.parse(2500);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
at: seenAt,
|
|
18
|
+
under: { item: attempt.item, phase: attempt.phase, gen: attempt.leaseGen },
|
|
19
|
+
writes: [
|
|
20
|
+
{
|
|
21
|
+
op: 'insert',
|
|
22
|
+
table: 'attempt_sessions',
|
|
23
|
+
row: { attempt: attempt.id, agentSession, atcSession, seenAt },
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
facts: [
|
|
27
|
+
{
|
|
28
|
+
id: buildMockFactId(),
|
|
29
|
+
type: 'boxxy.attempt.session_bound',
|
|
30
|
+
entity: `boxxy:attempt:${attempt.id}`,
|
|
31
|
+
actor: `boxxy:runner:${attempt.runner}`,
|
|
32
|
+
payload: { atcSession, agentSession, seenAt },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AttemptRow, StoreChange } from '@boxxy/model';
|
|
2
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
3
|
+
|
|
4
|
+
export function buildMockClaimChange(attempt: AttemptRow): StoreChange {
|
|
5
|
+
return {
|
|
6
|
+
at: attempt.claimedAt,
|
|
7
|
+
writes: [
|
|
8
|
+
{ op: 'take-slot', pool: attempt.pool, holder: attempt.id },
|
|
9
|
+
{
|
|
10
|
+
op: 'insert',
|
|
11
|
+
table: 'leases',
|
|
12
|
+
row: {
|
|
13
|
+
item: attempt.item,
|
|
14
|
+
phase: attempt.phase,
|
|
15
|
+
attempt: attempt.id,
|
|
16
|
+
gen: attempt.leaseGen,
|
|
17
|
+
takenAt: attempt.claimedAt,
|
|
18
|
+
releasedAt: null,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{ op: 'insert', table: 'attempts', row: { ...attempt, slot: 0, slotGen: 0 } },
|
|
22
|
+
],
|
|
23
|
+
facts: [
|
|
24
|
+
{
|
|
25
|
+
id: buildMockFactId(),
|
|
26
|
+
type: 'boxxy.attempt.claimed',
|
|
27
|
+
entity: `boxxy:attempt:${attempt.id}`,
|
|
28
|
+
actor: `boxxy:runner:${attempt.runner}`,
|
|
29
|
+
payload: {
|
|
30
|
+
item: attempt.item,
|
|
31
|
+
phase: attempt.phase,
|
|
32
|
+
number: attempt.number,
|
|
33
|
+
pool: attempt.pool,
|
|
34
|
+
slot: 0,
|
|
35
|
+
gen: 0,
|
|
36
|
+
recipeHash: attempt.recipeHash,
|
|
37
|
+
rulesVersion: attempt.rulesVersion,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ItemRow, StoreChange } from '@boxxy/model';
|
|
2
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
3
|
+
|
|
4
|
+
export function buildMockFileItemChange(item: ItemRow): StoreChange {
|
|
5
|
+
return {
|
|
6
|
+
at: item.filedAt,
|
|
7
|
+
writes: [{ op: 'insert', table: 'items', row: item }],
|
|
8
|
+
facts: [
|
|
9
|
+
{
|
|
10
|
+
id: buildMockFactId(),
|
|
11
|
+
type: 'boxxy.item.filed',
|
|
12
|
+
entity: `boxxy:item:${item.id}`,
|
|
13
|
+
actor: 'boxxy:owner:local',
|
|
14
|
+
payload: item,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LeverRow, StoreChange } from '@boxxy/model';
|
|
2
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
3
|
+
|
|
4
|
+
export function buildMockLeverChange(lever: LeverRow): StoreChange {
|
|
5
|
+
return {
|
|
6
|
+
at: lever.at,
|
|
7
|
+
writes: [{ op: 'insert', table: 'levers', row: lever }],
|
|
8
|
+
facts: [
|
|
9
|
+
{
|
|
10
|
+
id: buildMockFactId(),
|
|
11
|
+
type: 'boxxy.lever.recorded',
|
|
12
|
+
entity: `boxxy:item:${lever.item}`,
|
|
13
|
+
actor: lever.actor,
|
|
14
|
+
payload: lever,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { ItemId, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
|
|
5
|
+
interface LinkInput {
|
|
6
|
+
readonly source: ItemId;
|
|
7
|
+
readonly target: ItemId;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function buildMockLinkChange(input: LinkInput): StoreChange {
|
|
11
|
+
const type = brandSchemas.linkTypeName.parse('blocks');
|
|
12
|
+
const addedAt = brandSchemas.instant.parse(3500);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
at: addedAt,
|
|
16
|
+
writes: [
|
|
17
|
+
{
|
|
18
|
+
op: 'insert',
|
|
19
|
+
table: 'links',
|
|
20
|
+
row: {
|
|
21
|
+
source: input.source,
|
|
22
|
+
target: input.target,
|
|
23
|
+
type,
|
|
24
|
+
addedBy: 'boxxy:owner:local',
|
|
25
|
+
addedAt,
|
|
26
|
+
removedAt: null,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
facts: [
|
|
31
|
+
{
|
|
32
|
+
id: buildMockFactId(),
|
|
33
|
+
type: 'boxxy.link.added',
|
|
34
|
+
entity: `boxxy:link:${input.source}:${type}:${input.target}`,
|
|
35
|
+
actor: 'boxxy:owner:local',
|
|
36
|
+
payload: { source: input.source, target: input.target, type },
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { RunnerId, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
|
|
5
|
+
export function buildMockPauseChange(runner: RunnerId): StoreChange {
|
|
6
|
+
return {
|
|
7
|
+
at: brandSchemas.instant.parse(1500),
|
|
8
|
+
writes: [
|
|
9
|
+
{
|
|
10
|
+
op: 'update',
|
|
11
|
+
table: 'runners',
|
|
12
|
+
key: { id: runner },
|
|
13
|
+
expect: { paused: false },
|
|
14
|
+
set: { paused: true },
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
facts: [
|
|
18
|
+
{
|
|
19
|
+
id: buildMockFactId(),
|
|
20
|
+
type: 'boxxy.runner.paused',
|
|
21
|
+
entity: `boxxy:runner:${runner}`,
|
|
22
|
+
actor: 'boxxy:owner:local',
|
|
23
|
+
payload: { runner },
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { AttemptRow, PullRequestRow, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
import { buildMockPullRequestRow } from '@boxxy/model/test-utils/build-mock-pull-request-row';
|
|
5
|
+
|
|
6
|
+
interface PullRequestInput {
|
|
7
|
+
readonly attempt: AttemptRow;
|
|
8
|
+
readonly state: PullRequestRow['state'];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildMockPullRequestChange(input: PullRequestInput): StoreChange {
|
|
12
|
+
const attempt = input.attempt;
|
|
13
|
+
const linkedAt = brandSchemas.instant.parse(4000);
|
|
14
|
+
const factId = buildMockFactId();
|
|
15
|
+
const url = 'https://github.com/acme/lamps/pull/7';
|
|
16
|
+
|
|
17
|
+
const pullRequest = buildMockPullRequestRow({
|
|
18
|
+
repo: brandSchemas.repoName.parse('acme/lamps'),
|
|
19
|
+
number: 7,
|
|
20
|
+
attempt: attempt.id,
|
|
21
|
+
state: input.state,
|
|
22
|
+
checkedAt: linkedAt,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
at: linkedAt,
|
|
27
|
+
writes: [
|
|
28
|
+
{ op: 'insert', table: 'pull_requests', row: pullRequest },
|
|
29
|
+
{
|
|
30
|
+
op: 'insert',
|
|
31
|
+
table: 'evidence',
|
|
32
|
+
row: {
|
|
33
|
+
factId,
|
|
34
|
+
attempt: attempt.id,
|
|
35
|
+
kind: 'pr',
|
|
36
|
+
ref: url,
|
|
37
|
+
observed: null,
|
|
38
|
+
source: 'hook',
|
|
39
|
+
at: linkedAt,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
facts: [
|
|
44
|
+
{
|
|
45
|
+
id: factId,
|
|
46
|
+
type: 'boxxy.evidence.pr_linked',
|
|
47
|
+
entity: `boxxy:attempt:${attempt.id}`,
|
|
48
|
+
actor: `boxxy:hook:${attempt.id}`,
|
|
49
|
+
payload: { repo: pullRequest.repo, number: 7, url },
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { AttemptRow, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
|
|
5
|
+
interface SettleInput {
|
|
6
|
+
// the attempt as claimed, with the slot and slot gen its take got
|
|
7
|
+
readonly attempt: AttemptRow;
|
|
8
|
+
readonly state: 'succeeded' | 'failed';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildMockSettleChange(input: SettleInput): StoreChange {
|
|
12
|
+
const attempt = input.attempt;
|
|
13
|
+
const endedAt = brandSchemas.instant.parse(3000);
|
|
14
|
+
const failure = input.state === 'failed' ? 'closeout-missing' : null;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
at: endedAt,
|
|
18
|
+
under: {
|
|
19
|
+
item: attempt.item,
|
|
20
|
+
phase: attempt.phase,
|
|
21
|
+
gen: attempt.leaseGen,
|
|
22
|
+
slot: { pool: attempt.pool, slot: attempt.slot, gen: attempt.slotGen },
|
|
23
|
+
},
|
|
24
|
+
writes: [
|
|
25
|
+
{
|
|
26
|
+
op: 'update',
|
|
27
|
+
table: 'attempts',
|
|
28
|
+
key: { id: attempt.id },
|
|
29
|
+
expect: { state: attempt.state },
|
|
30
|
+
set: { state: input.state, failure, endedAt },
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
op: 'update',
|
|
34
|
+
table: 'leases',
|
|
35
|
+
key: { item: attempt.item, phase: attempt.phase, gen: attempt.leaseGen },
|
|
36
|
+
expect: { releasedAt: null },
|
|
37
|
+
set: { releasedAt: endedAt },
|
|
38
|
+
},
|
|
39
|
+
{ op: 'release-slot', pool: attempt.pool, slot: attempt.slot, holder: attempt.id },
|
|
40
|
+
],
|
|
41
|
+
facts: [
|
|
42
|
+
{
|
|
43
|
+
id: buildMockFactId(),
|
|
44
|
+
type: 'boxxy.attempt.settled',
|
|
45
|
+
entity: `boxxy:attempt:${attempt.id}`,
|
|
46
|
+
actor: `boxxy:runner:${attempt.runner}`,
|
|
47
|
+
payload: {
|
|
48
|
+
state: input.state,
|
|
49
|
+
failure,
|
|
50
|
+
endedAt,
|
|
51
|
+
criteria: [],
|
|
52
|
+
matched: [],
|
|
53
|
+
frictionDropped: 0,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { AttemptRow, StoreChange } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
|
|
5
|
+
export function buildMockStartChange(attempt: AttemptRow): StoreChange {
|
|
6
|
+
const atcSession = brandSchemas.atcSessionId.parse('atc-session-1');
|
|
7
|
+
const startedAt = brandSchemas.instant.parse(2500);
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
at: startedAt,
|
|
11
|
+
under: { item: attempt.item, phase: attempt.phase, gen: attempt.leaseGen },
|
|
12
|
+
writes: [
|
|
13
|
+
{
|
|
14
|
+
op: 'update',
|
|
15
|
+
table: 'attempts',
|
|
16
|
+
key: { id: attempt.id },
|
|
17
|
+
expect: { state: 'starting' },
|
|
18
|
+
set: { state: 'running', atcSession, startedAt },
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
facts: [
|
|
22
|
+
{
|
|
23
|
+
id: buildMockFactId(),
|
|
24
|
+
type: 'boxxy.attempt.started',
|
|
25
|
+
entity: `boxxy:attempt:${attempt.id}`,
|
|
26
|
+
actor: `boxxy:runner:${attempt.runner}`,
|
|
27
|
+
payload: { atcSession },
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|