@warren-dean/code-kit 0.4.1 → 0.6.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +35 -28
- package/bin/code-kit.mjs +114 -14
- package/hooks/guard-bash.mjs +84 -29
- package/hooks/lane-audit.mjs +17 -6
- package/hooks/lib/config.mjs +33 -5
- package/hooks/lib/diff.mjs +5 -1
- package/hooks/lib/harness.mjs +131 -0
- package/hooks/lib/next.mjs +13 -2
- package/hooks/lib/plan.mjs +18 -7
- package/hooks/lib/registry.mjs +37 -3
- package/hooks/lib/reviews.mjs +39 -0
- package/hooks/lib/rules.mjs +66 -4
- package/hooks/lib/shell.mjs +94 -0
- package/hooks/lib/trace.mjs +4 -2
- package/hooks/lib/verify.mjs +74 -3
- package/hooks/mod/hold.mjs +25 -0
- package/hooks/mod/register.mjs +129 -4
- package/hooks/mod/register.test.ts +305 -4
- package/hooks/mod/view.mjs +355 -11
- package/package.json +1 -1
- package/skills/init/SKILL.md +10 -2
- package/skills/review/SKILL.md +9 -1
- package/templates/agent.md +1 -1
|
@@ -6,6 +6,7 @@ import { expect, mock, test } from 'claude-code/testing';
|
|
|
6
6
|
const PANE = 'code-kit-lanes';
|
|
7
7
|
const APPROVE = 'code-kit-approve';
|
|
8
8
|
const RESULT = 'code-kit-result';
|
|
9
|
+
const SETTINGS = 'code-kit-settings';
|
|
9
10
|
|
|
10
11
|
/** A project as the stubs report it; tests change it to move the world on. */
|
|
11
12
|
function project() {
|
|
@@ -68,7 +69,8 @@ function project() {
|
|
|
68
69
|
taken: [] as string[], // command names another command already holds
|
|
69
70
|
stops: [] as any[],
|
|
70
71
|
refs: ' aaa refs/heads/main\n bbb refs/heads/web/st-4\n',
|
|
71
|
-
agents: [] as { type: string; status: string }[],
|
|
72
|
+
agents: [] as { type: string; status: string; id?: string; description?: string }[],
|
|
73
|
+
limits: [] as { kind: string; percentUsed: number }[],
|
|
72
74
|
approveExit: 0,
|
|
73
75
|
mergeExit: 0,
|
|
74
76
|
answer: 'Merge',
|
|
@@ -78,6 +80,18 @@ function project() {
|
|
|
78
80
|
prompts: [] as string[],
|
|
79
81
|
acts: [] as string[][],
|
|
80
82
|
runs: 0,
|
|
83
|
+
settings: [
|
|
84
|
+
{
|
|
85
|
+
key: 'autonomy',
|
|
86
|
+
value: 'autonomous',
|
|
87
|
+
default: 'autonomous',
|
|
88
|
+
about: 'How the lead loop acts',
|
|
89
|
+
},
|
|
90
|
+
{ key: 'hold.minutes', value: 2, default: 2, about: 'How long a call is held' },
|
|
91
|
+
{ key: 'agents.reviewer.on', value: false, default: false, about: 'A background reviewer' },
|
|
92
|
+
{ key: 'agents.reviewer.model', value: null, default: null, about: "The reviewer's model" },
|
|
93
|
+
] as any[],
|
|
94
|
+
setExit: 0,
|
|
81
95
|
};
|
|
82
96
|
}
|
|
83
97
|
type World = ReturnType<typeof project>;
|
|
@@ -119,6 +133,29 @@ function stub(on: any, w: World) {
|
|
|
119
133
|
? ran(1, '', 'Nothing was merged. code-kit verify found 1 problem(s) on web/st-4')
|
|
120
134
|
: ran(0, 'Merged web/st-4 into main.');
|
|
121
135
|
}
|
|
136
|
+
if (sub === 'settings' && argv[3] === 'set') {
|
|
137
|
+
w.acts.push([...argv.slice(2)]);
|
|
138
|
+
if (w.setExit)
|
|
139
|
+
return ran(
|
|
140
|
+
1,
|
|
141
|
+
'',
|
|
142
|
+
'"harness.hold.minutes" must be a number of minutes from 0 (off) to 30\nNothing was changed.',
|
|
143
|
+
);
|
|
144
|
+
const row = w.settings.find((s) => s.key === argv[4]);
|
|
145
|
+
const raw = argv[5];
|
|
146
|
+
row.value =
|
|
147
|
+
raw === 'null'
|
|
148
|
+
? null
|
|
149
|
+
: raw === 'true'
|
|
150
|
+
? true
|
|
151
|
+
: raw === 'false'
|
|
152
|
+
? false
|
|
153
|
+
: /^\d+$/.test(raw)
|
|
154
|
+
? Number(raw)
|
|
155
|
+
: raw;
|
|
156
|
+
return ran(0, `Set harness.${argv[4]} to ${raw}.`);
|
|
157
|
+
}
|
|
158
|
+
if (sub === 'settings') return ran(0, JSON.stringify(w.settings));
|
|
122
159
|
return ran(1, '', `unexpected ${argv.join(' ')}`);
|
|
123
160
|
});
|
|
124
161
|
on('session.start', () => ({ cwd: '/work' }));
|
|
@@ -135,6 +172,19 @@ function stub(on: any, w: World) {
|
|
|
135
172
|
w.refusal ? { deny: w.refusal } : { result: { stdout: '', stderr: '', interrupted: false } },
|
|
136
173
|
);
|
|
137
174
|
on('agent.list', () => ({ value: w.agents }));
|
|
175
|
+
on('session.usage', () => ({ value: { startedAt: 0, context: {}, rateLimits: w.limits } }));
|
|
176
|
+
on('session.measure', ($: any, e: any) => ({ changed: e.changed }));
|
|
177
|
+
// A model request, answered with the usage the test gave it.
|
|
178
|
+
on('turn.step', async function* ($: any, e: any) {
|
|
179
|
+
return {
|
|
180
|
+
turnId: e.turnId,
|
|
181
|
+
index: e.index,
|
|
182
|
+
answer: '',
|
|
183
|
+
toolUses: [],
|
|
184
|
+
stopReason: 'end_turn',
|
|
185
|
+
usage: e.messageCount === 0 ? null : { model: e.model, ...stepUsage(e.messageCount) },
|
|
186
|
+
};
|
|
187
|
+
} as any);
|
|
138
188
|
on('fs.stat', () => ({ value: { kind: 'file', size: 1, mtimeMs: 1, isLink: false } }));
|
|
139
189
|
on('fs.list', () => ({ value: [] }));
|
|
140
190
|
on('session.cwd', () => ({ value: '/work' }));
|
|
@@ -163,6 +213,30 @@ function stub(on: any, w: World) {
|
|
|
163
213
|
});
|
|
164
214
|
}
|
|
165
215
|
|
|
216
|
+
/** A request of n thousand tokens, split as a model request's usage is. */
|
|
217
|
+
function stepUsage(thousands: number) {
|
|
218
|
+
const n = thousands * 1000;
|
|
219
|
+
return {
|
|
220
|
+
input_tokens: n * 0.3,
|
|
221
|
+
cache_creation_input_tokens: n * 0.1,
|
|
222
|
+
output_tokens: n * 0.1,
|
|
223
|
+
cache_read_input_tokens: n * 0.5,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** One model request by an agent (or the lead) of so many thousand tokens, run to its end. */
|
|
228
|
+
async function request($: any, agentId: string | undefined, thousands: number) {
|
|
229
|
+
const s = $.turn.step({
|
|
230
|
+
turnId: `t-${agentId ?? 'lead'}`,
|
|
231
|
+
index: 0,
|
|
232
|
+
model: 'claude-sonnet-5-5',
|
|
233
|
+
messageCount: thousands, // the stub beneath answers with this many thousand tokens
|
|
234
|
+
...(agentId ? { agentId } : {}),
|
|
235
|
+
});
|
|
236
|
+
for await (const _ of s);
|
|
237
|
+
return s.result;
|
|
238
|
+
}
|
|
239
|
+
|
|
166
240
|
/** A session started on the project, with the mock clock. */
|
|
167
241
|
async function start($: any, on: any, w: World) {
|
|
168
242
|
stub(on, w);
|
|
@@ -336,7 +410,9 @@ test('ACT-1 and BAND-3 approving with the prefilled reason grants it for the lan
|
|
|
336
410
|
const confirm = await pane($, APPROVE);
|
|
337
411
|
const reason = 'Approve dayjs for the web lane: npm install dayjs';
|
|
338
412
|
expect((await confirm.find({ key: 'approve-reason' }))?.props.value).toBe(reason);
|
|
339
|
-
expect(
|
|
413
|
+
expect(
|
|
414
|
+
await confirm.find({ type: 'Text', text: /until the install is committed \(at most 7 days\)/ }),
|
|
415
|
+
).toBeDefined();
|
|
340
416
|
await press($, 'approve-confirm', APPROVE);
|
|
341
417
|
expect(w.acts).toEqual([
|
|
342
418
|
['approve', 'dep-dayjs', '--lane', 'web', '--reason', reason, '--via', 'pane'],
|
|
@@ -558,7 +634,7 @@ test('CARD-1 a refused write is drawn as a card, its text a press away', async (
|
|
|
558
634
|
test('CARD-1 a refusal an approval would allow has Approve…, which opens the confirmation', async ($, on) => {
|
|
559
635
|
const w = project();
|
|
560
636
|
w.refusal =
|
|
561
|
-
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/web/dep-dayjs\n(a person runs it; it allows installing it for the web lane,
|
|
637
|
+
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/web/dep-dayjs\n(a person runs it; it allows installing it for the web lane, until the install is committed, at most 7 days)\nCommand: npm install dayjs';
|
|
562
638
|
await start($, on, w);
|
|
563
639
|
await $.tool.call({ tool: 'Bash', tool_use_id: 'tu-2', command: 'npm install dayjs' } as any);
|
|
564
640
|
const ui = await toolResult($, 'tu-2', 'Bash');
|
|
@@ -622,7 +698,7 @@ test('CARD-4 outside a code-kit project, /approvals and /verify-branch say so',
|
|
|
622
698
|
});
|
|
623
699
|
|
|
624
700
|
const install =
|
|
625
|
-
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/dep-dayjs\n(a person runs it; it allows installing it for any agent,
|
|
701
|
+
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/dep-dayjs\n(a person runs it; it allows installing it for any agent, until the install is committed, at most 7 days)\nCommand: npm install dayjs';
|
|
626
702
|
|
|
627
703
|
test('CARD-1 a group holding a refusal unfolds, so its row can be the card', async ($, on) => {
|
|
628
704
|
const w = project();
|
|
@@ -712,3 +788,228 @@ test(
|
|
|
712
788
|
await ui.unmount();
|
|
713
789
|
},
|
|
714
790
|
);
|
|
791
|
+
|
|
792
|
+
// --- the harness settings -------------------------------------------------------------------------
|
|
793
|
+
|
|
794
|
+
const harness = ($: any) => $.command.run({ command: 'harness', args: '' });
|
|
795
|
+
|
|
796
|
+
test('SET-2 /harness opens the settings, each with its value, and /harness again closes it', async ($, on) => {
|
|
797
|
+
const w = project();
|
|
798
|
+
await start($, on, w);
|
|
799
|
+
await harness($);
|
|
800
|
+
expect(w.opened).toContain(SETTINGS);
|
|
801
|
+
const ui = await pane($, SETTINGS);
|
|
802
|
+
expect(await ui.find({ type: 'Text', text: 'hold.minutes' })).toBeDefined();
|
|
803
|
+
expect((await ui.find({ key: 'set-hold.minutes' }))?.props.value).toBe('2');
|
|
804
|
+
expect((await ui.find({ key: 'set-autonomy' }))?.props.value).toBe('autonomous');
|
|
805
|
+
await ui.unmount();
|
|
806
|
+
await harness($);
|
|
807
|
+
expect(w.closed).toContain(SETTINGS);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
test('SET-2 a change asks for a reason, then is written by the CLI as the pane', async ($, on) => {
|
|
811
|
+
const w = project();
|
|
812
|
+
await start($, on, w);
|
|
813
|
+
await harness($);
|
|
814
|
+
const ui = await pane($, SETTINGS);
|
|
815
|
+
await $.ui.select({
|
|
816
|
+
plugin: 'code-kit',
|
|
817
|
+
key: 'set-autonomy',
|
|
818
|
+
value: 'propose',
|
|
819
|
+
requestId: SETTINGS,
|
|
820
|
+
});
|
|
821
|
+
expect(w.acts).toEqual([]);
|
|
822
|
+
expect(await ui.find({ type: 'Text', text: /Set autonomy to propose\?/ })).toBeDefined();
|
|
823
|
+
await $.ui.input({ plugin: 'code-kit', key: 'settings-reason', text: ' ' });
|
|
824
|
+
expect(w.acts).toEqual([]);
|
|
825
|
+
expect(await ui.find({ type: 'Text', text: /Give a reason/ })).toBeDefined();
|
|
826
|
+
await $.ui.input({ plugin: 'code-kit', key: 'settings-reason', text: 'watch it for a week' });
|
|
827
|
+
expect(w.acts).toEqual([
|
|
828
|
+
['settings', 'set', 'autonomy', 'propose', '--reason', 'watch it for a week', '--via', 'pane'],
|
|
829
|
+
]);
|
|
830
|
+
expect(await ui.find({ key: 'settings-confirm' })).toBeUndefined();
|
|
831
|
+
expect((await ui.find({ key: 'set-autonomy' }))?.props.value).toBe('propose');
|
|
832
|
+
await ui.unmount();
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test('SET-2 a number is typed; one the CLI refuses shows why and changes nothing', async ($, on) => {
|
|
836
|
+
const w = project();
|
|
837
|
+
w.setExit = 1;
|
|
838
|
+
await start($, on, w);
|
|
839
|
+
await harness($);
|
|
840
|
+
const ui = await pane($, SETTINGS);
|
|
841
|
+
await $.ui.input({ plugin: 'code-kit', key: 'set-hold.minutes', text: '45' });
|
|
842
|
+
await $.ui.input({ plugin: 'code-kit', key: 'settings-reason', text: 'longer' });
|
|
843
|
+
expect(await ui.find({ type: 'Text', text: /from 0 \(off\) to 30/ })).toBeDefined();
|
|
844
|
+
expect(w.settings[1].value).toBe(2);
|
|
845
|
+
await press($, 'settings-cancel', SETTINGS);
|
|
846
|
+
expect(await ui.find({ key: 'settings-confirm' })).toBeUndefined();
|
|
847
|
+
await ui.unmount();
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
test("SET-2 the reviewer's model can go back to the session's", async ($, on) => {
|
|
851
|
+
const w = project();
|
|
852
|
+
w.settings[3].value = 'haiku';
|
|
853
|
+
await start($, on, w);
|
|
854
|
+
await harness($);
|
|
855
|
+
const ui = await pane($, SETTINGS);
|
|
856
|
+
expect(await ui.find({ type: 'Text', text: /default the session's/ })).toBeDefined();
|
|
857
|
+
await $.ui.select({
|
|
858
|
+
plugin: 'code-kit',
|
|
859
|
+
key: 'set-agents.reviewer.model',
|
|
860
|
+
value: '',
|
|
861
|
+
requestId: SETTINGS,
|
|
862
|
+
});
|
|
863
|
+
await press($, 'settings-ok', SETTINGS);
|
|
864
|
+
expect(w.acts).toEqual([]);
|
|
865
|
+
await $.ui.input({ plugin: 'code-kit', key: 'settings-reason', text: 'same as the lead' });
|
|
866
|
+
expect(w.acts[0].slice(0, 4)).toEqual(['settings', 'set', 'agents.reviewer.model', 'null']);
|
|
867
|
+
await ui.unmount();
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
test('CARD-4 outside a code-kit project, /harness says so and opens nothing', async ($, on) => {
|
|
871
|
+
const w = project();
|
|
872
|
+
w.check.exists = false;
|
|
873
|
+
w.check.valid = false;
|
|
874
|
+
await start($, on, w);
|
|
875
|
+
const said = await harness($);
|
|
876
|
+
expect(String(said?.text)).toMatch(/code-kit/);
|
|
877
|
+
expect(w.opened).not.toContain(SETTINGS);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// --- usage per lane ---------------------------------------------------------------------------------
|
|
881
|
+
|
|
882
|
+
const working = (id: string, type: string, description: string) => ({
|
|
883
|
+
id,
|
|
884
|
+
type,
|
|
885
|
+
description,
|
|
886
|
+
status: 'running',
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test("USE-1 a lane agent's requests show on its lane's row and its story", async ($, on) => {
|
|
890
|
+
const w = project();
|
|
891
|
+
w.agents = [working('a1', 'web-engineer', 'Build ST-4: the booking form')];
|
|
892
|
+
const clock = await start($, on, w);
|
|
893
|
+
await lanes($);
|
|
894
|
+
await request($, 'a1', 120);
|
|
895
|
+
await request($, 'a1', 80);
|
|
896
|
+
await clock.advance(2000);
|
|
897
|
+
const ui = await pane($, PANE);
|
|
898
|
+
expect((await ui.find({ key: 'lane-web' }))?.text).toContain('200k');
|
|
899
|
+
expect((await ui.find({ key: 'usage-ST-4' }))?.text).toContain('200k');
|
|
900
|
+
expect((await ui.find({ key: 'lane-api' }))?.text).not.toMatch(/\d+k/);
|
|
901
|
+
await ui.unmount();
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
test("USE-1 the lead's requests are the lead's; an agent's brief names its story", async ($, on) => {
|
|
905
|
+
const w = project();
|
|
906
|
+
w.agents = [working('a2', 'core-engineer', 'ST-9 pricing rules')];
|
|
907
|
+
await start($, on, w);
|
|
908
|
+
await lanes($);
|
|
909
|
+
await request($, undefined, 30);
|
|
910
|
+
await request($, 'a2', 50);
|
|
911
|
+
const ui = await pane($, PANE);
|
|
912
|
+
expect((await ui.find({ key: 'lane-lead' }))?.text).toContain('30k');
|
|
913
|
+
expect((await ui.find({ key: 'lane-core' }))?.text).toContain('50k');
|
|
914
|
+
expect((await ui.find({ key: 'usage-ST-9' }))?.text).toContain('50k');
|
|
915
|
+
await ui.unmount();
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
test("USE-2 the background agents' share and the plan's 5-hour use", async ($, on) => {
|
|
919
|
+
const w = project();
|
|
920
|
+
w.limits = [{ kind: 'five_hour', percentUsed: 62 }];
|
|
921
|
+
w.agents = [
|
|
922
|
+
working('a1', 'web-engineer', 'ST-4 booking form'),
|
|
923
|
+
working('r1', 'code-reviewer', 'Review web/st-4'),
|
|
924
|
+
];
|
|
925
|
+
await start($, on, w);
|
|
926
|
+
await lanes($);
|
|
927
|
+
await request($, 'a1', 150);
|
|
928
|
+
await request($, 'r1', 50);
|
|
929
|
+
const ui = await pane($, PANE);
|
|
930
|
+
expect(await ui.find({ type: 'Text', text: 'Background agents 50k (25%)' })).toBeDefined();
|
|
931
|
+
expect(await ui.find({ type: 'Text', text: '62% of the 5-hour window' })).toBeDefined();
|
|
932
|
+
expect(await ui.find({ type: 'Text', text: 'This session 200k tokens' })).toBeDefined();
|
|
933
|
+
await ui.unmount();
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
test('USE-3 a story past three times the median of three finished ones is flagged in the band', async ($, on) => {
|
|
937
|
+
const w = project();
|
|
938
|
+
w.stories = [
|
|
939
|
+
...['ST-1', 'ST-2', 'ST-3'].map((id) => ({
|
|
940
|
+
id,
|
|
941
|
+
title: id,
|
|
942
|
+
lane: 'api',
|
|
943
|
+
state: 'done',
|
|
944
|
+
branch: `api/${id.toLowerCase()}`,
|
|
945
|
+
waitingOn: [],
|
|
946
|
+
})),
|
|
947
|
+
...w.stories,
|
|
948
|
+
];
|
|
949
|
+
w.agents = ['ST-1', 'ST-2', 'ST-3', 'ST-4'].map((id, i) =>
|
|
950
|
+
working(`a${i}`, i < 3 ? 'api-engineer' : 'web-engineer', `${id} work`),
|
|
951
|
+
);
|
|
952
|
+
await start($, on, w);
|
|
953
|
+
await request($, 'a0', 190);
|
|
954
|
+
await request($, 'a1', 200);
|
|
955
|
+
await request($, 'a2', 210);
|
|
956
|
+
await request($, 'a3', 500);
|
|
957
|
+
let ui = await bandUi($);
|
|
958
|
+
expect(await ui.find({ type: 'Text', text: /has used/ })).toBeUndefined();
|
|
959
|
+
await ui.unmount();
|
|
960
|
+
await request($, 'a3', 200);
|
|
961
|
+
ui = await bandUi($);
|
|
962
|
+
expect(await ui.find({ type: 'Text', text: 'ST-4 has used 3.5× the usual' })).toBeDefined();
|
|
963
|
+
await ui.unmount();
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
test('USE-3 with fewer than three finished, nothing is flagged', async ($, on) => {
|
|
967
|
+
const w = project();
|
|
968
|
+
w.stories = [
|
|
969
|
+
{ id: 'ST-1', title: 'a', lane: 'api', state: 'done', branch: 'api/st-1', waitingOn: [] },
|
|
970
|
+
...w.stories,
|
|
971
|
+
];
|
|
972
|
+
w.agents = [working('a0', 'api-engineer', 'ST-1'), working('a1', 'web-engineer', 'ST-4')];
|
|
973
|
+
await start($, on, w);
|
|
974
|
+
await request($, 'a0', 10);
|
|
975
|
+
await request($, 'a1', 900);
|
|
976
|
+
const ui = await bandUi($);
|
|
977
|
+
expect(await ui.find({ type: 'Text', text: /has used/ })).toBeUndefined();
|
|
978
|
+
await ui.unmount();
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test('USE-4 past the pause point the band says background agents are paused, and below it not', async ($, on) => {
|
|
982
|
+
const w = project();
|
|
983
|
+
await start($, on, w);
|
|
984
|
+
const measure = (percentUsed: number) =>
|
|
985
|
+
$.session.measure({
|
|
986
|
+
context: {} as any,
|
|
987
|
+
rateLimits: [{ kind: 'five_hour', percentUsed }],
|
|
988
|
+
changed: ['rateLimits'],
|
|
989
|
+
});
|
|
990
|
+
await measure(82);
|
|
991
|
+
let ui = await bandUi($);
|
|
992
|
+
expect(
|
|
993
|
+
await ui.find({ type: 'Text', text: 'background agents paused: plan at 82%' }),
|
|
994
|
+
).toBeDefined();
|
|
995
|
+
await ui.unmount();
|
|
996
|
+
await measure(79);
|
|
997
|
+
ui = await bandUi($);
|
|
998
|
+
expect(await ui.find({ type: 'Text', text: /paused/ })).toBeUndefined();
|
|
999
|
+
await ui.unmount();
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
test('USE-4 the pause point is the harness setting', async ($, on) => {
|
|
1003
|
+
const w = project();
|
|
1004
|
+
(w.check as any).harness = { background: { pauseAtPercent: 90 } };
|
|
1005
|
+
w.limits = [{ kind: 'five_hour', percentUsed: 85 }];
|
|
1006
|
+
await start($, on, w);
|
|
1007
|
+
let ui = await bandUi($);
|
|
1008
|
+
expect(await ui.find({ type: 'Text', text: /paused/ })).toBeUndefined();
|
|
1009
|
+
await ui.unmount();
|
|
1010
|
+
w.limits = [{ kind: 'five_hour', percentUsed: 91 }];
|
|
1011
|
+
await $.session.measure({ context: {} as any, rateLimits: w.limits, changed: ['rateLimits'] });
|
|
1012
|
+
ui = await bandUi($);
|
|
1013
|
+
expect(await ui.find({ type: 'Text', text: /plan at 91%/ })).toBeDefined();
|
|
1014
|
+
await ui.unmount();
|
|
1015
|
+
});
|