fix(fleet): additive hook-event merge and gated-composition acceptance reads
Integration adjudication (fred, W-F1): the general arrays-replace merge rule conflicts with the gap-7 base/overlay split — base and lease overlay share the PreToolUse and Stop events, so replace semantics would silently drop the base QA hooks from every gated seat. Ruling: hook event arrays directly under the top-level hooks key concatenate (base first); all other arrays keep replace semantics; null tombstones still delete an event. - mutator-gate acceptance now asserts lease wiring against the COMPOSED gated settings (base + lease-overlay via the launcher's own merge), matching the post-split contract. - fleet subcommand canary gains the intended new 'agent' surface from T3. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dtdjx4Gxude9fwyLezCrhh
This commit is contained in:
co-authored by
Claude Fable 5
parent
0fdcfa0ff4
commit
92e790ae9d
@@ -94,6 +94,36 @@ describe('three-layer settings merge', () => {
|
||||
).toEqual({ nested: { keep: true } });
|
||||
});
|
||||
|
||||
it('concatenates hook event arrays so an overlay adds gating without erasing base hooks', () => {
|
||||
const qaStop = { hooks: [{ type: 'command', command: 'qa-stop.sh' }] };
|
||||
const leaseStop = { hooks: [{ type: 'command', command: 'receipt-observer.py' }] };
|
||||
const qaPre = { matcher: 'Write', hooks: [{ type: 'command', command: 'qa-pre.sh' }] };
|
||||
expect(
|
||||
deepMergeSettings(
|
||||
{ hooks: { Stop: [qaStop], PreToolUse: [qaPre] } },
|
||||
{ hooks: { Stop: [leaseStop] } },
|
||||
),
|
||||
).toEqual({ hooks: { Stop: [qaStop, leaseStop], PreToolUse: [qaPre] } });
|
||||
});
|
||||
|
||||
it('still deletes a whole hook event via the null tombstone', () => {
|
||||
expect(
|
||||
deepMergeSettings(
|
||||
{ hooks: { Stop: [{ hooks: [{ type: 'command', command: 'qa-stop.sh' }] }] } },
|
||||
{ hooks: { Stop: null } },
|
||||
),
|
||||
).toEqual({ hooks: {} });
|
||||
});
|
||||
|
||||
it('keeps replace semantics for arrays outside the top-level hooks object', () => {
|
||||
expect(
|
||||
deepMergeSettings(
|
||||
{ plugins: ['base'], nested: { hooks: { Stop: ['base'] } } },
|
||||
{ plugins: ['user'], nested: { hooks: { Stop: ['user'] } } },
|
||||
),
|
||||
).toEqual({ plugins: ['user'], nested: { hooks: { Stop: ['user'] } } });
|
||||
});
|
||||
|
||||
it('deep-merges all three layers in precedence order', () => {
|
||||
expect(
|
||||
deepMergeSettings(
|
||||
|
||||
Reference in New Issue
Block a user