Skip to content

Commit 2e3f5b1

Browse files
grypezclaude
andcommitted
feat(kernel-utils): add getDiscoverableSection, deprecate global section methods
Adds getDiscoverableSection and getDiscoverableGlobalSection to the Sheaf API so callers can attach a MethodSchema (for __getDescription__) to the caller-facing dispatch section rather than inside individual capability wrappers. Marks getGlobalSection and getDiscoverableGlobalSection as @deprecated — callers should supply an explicit InterfaceGuard via getSection/getDiscoverableSection instead of relying on the auto-computed union. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8c61672 commit 2e3f5b1

3 files changed

Lines changed: 129 additions & 16 deletions

File tree

packages/kernel-utils/src/sheaf/sheafify.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { makeExo, GET_INTERFACE_GUARD } from '@endo/exo';
22
import { M, getInterfaceGuardPayload } from '@endo/patterns';
33
import { describe, it, expect } from 'vitest';
44

5+
import { GET_DESCRIPTION } from '../discoverable.ts';
56
import { constant } from './metadata.ts';
67
import { sheafify } from './sheafify.ts';
78
import type {
@@ -614,6 +615,63 @@ describe('sheafify', () => {
614615
expect(methodGuards).toHaveProperty('getBalance');
615616
});
616617

618+
it('getDiscoverableGlobalSection exposes __getDescription__', async () => {
619+
const schema = {
620+
getBalance: {
621+
description: 'Get account balance.',
622+
args: { acct: { type: 'string' as const, description: 'Account id.' } },
623+
returns: { type: 'number' as const, description: 'Balance.' },
624+
},
625+
};
626+
const sections: PresheafSection<Record<string, never>>[] = [
627+
{
628+
exo: makeExo(
629+
'Wallet:0',
630+
M.interface('Wallet:0', {
631+
getBalance: M.call(M.string()).returns(M.number()),
632+
}),
633+
{ getBalance: (_acct: string) => 42 },
634+
) as unknown as Section,
635+
},
636+
];
637+
638+
const section = sheafify({
639+
name: 'Wallet',
640+
sections,
641+
}).getDiscoverableGlobalSection({
642+
async *lift(germs) {
643+
yield germs[0]!;
644+
},
645+
schema,
646+
});
647+
648+
expect(E(section)[GET_DESCRIPTION]()).toStrictEqual(schema);
649+
});
650+
651+
it('getSection does not expose __getDescription__', () => {
652+
const sections: PresheafSection<Record<string, never>>[] = [
653+
{
654+
exo: makeExo(
655+
'Wallet:0',
656+
M.interface('Wallet:0', {
657+
getBalance: M.call(M.string()).returns(M.number()),
658+
}),
659+
{ getBalance: (_acct: string) => 42 },
660+
) as unknown as Section,
661+
},
662+
];
663+
664+
const section = sheafify({ name: 'Wallet', sections }).getGlobalSection({
665+
async *lift(germs) {
666+
yield germs[0]!;
667+
},
668+
});
669+
670+
expect(
671+
(section as Record<string, unknown>)[GET_DESCRIPTION],
672+
).toBeUndefined();
673+
});
674+
617675
it('getExported excludes revoked sections', () => {
618676
const sections: PresheafSection<{ cost: number }>[] = [
619677
{

packages/kernel-utils/src/sheaf/sheafify.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
} from '@endo/patterns';
2121
import type { InterfaceGuard, MethodGuard } from '@endo/patterns';
2222

23+
import { makeDiscoverableExo } from '../discoverable.ts';
24+
import type { MethodSchema } from '../schema.ts';
2325
import { stringify } from '../stringify.ts';
2426
import { driveLift } from './drive.ts';
2527
import { collectSheafGuard } from './guard.ts';
@@ -210,12 +212,14 @@ export const sheafify = <
210212
);
211213
const grants: Grant[] = [];
212214

213-
const getSection = ({
215+
const buildSection = ({
214216
guard,
215217
lift,
218+
schema,
216219
}: {
217220
guard: InterfaceGuard;
218221
lift: Lift<MetaData>;
222+
schema?: Record<string, MethodSchema>;
219223
}): object => {
220224
const resolvedGuard = guard;
221225

@@ -286,11 +290,14 @@ export const sheafify = <
286290
handlers[method] = async (...args: unknown[]) => dispatch(method, args);
287291
}
288292

289-
const exo = makeExo(
290-
`${name}:section`,
291-
asyncGuard,
292-
handlers,
293-
) as unknown as Section;
293+
const exo = (schema === undefined
294+
? makeExo(`${name}:section`, asyncGuard, handlers)
295+
: makeDiscoverableExo(
296+
`${name}:section`,
297+
handlers,
298+
schema,
299+
asyncGuard,
300+
)) as unknown as Section;
294301

295302
grants.push({
296303
exo,
@@ -304,15 +311,40 @@ export const sheafify = <
304311
return exo;
305312
};
306313

307-
const getGlobalSection = ({ lift }: { lift: Lift<MetaData> }): object => {
308-
return getSection({
309-
guard: collectSheafGuard(
310-
name,
311-
frozenSections.map(({ exo }) => exo),
312-
),
313-
lift,
314-
});
315-
};
314+
const unionGuard = (): InterfaceGuard =>
315+
collectSheafGuard(
316+
name,
317+
frozenSections.map(({ exo }) => exo),
318+
);
319+
320+
const getSection = ({
321+
guard,
322+
lift,
323+
}: {
324+
guard: InterfaceGuard;
325+
lift: Lift<MetaData>;
326+
}): object => buildSection({ guard, lift });
327+
328+
const getDiscoverableSection = ({
329+
guard,
330+
lift,
331+
schema,
332+
}: {
333+
guard: InterfaceGuard;
334+
lift: Lift<MetaData>;
335+
schema: Record<string, MethodSchema>;
336+
}): object => buildSection({ guard, lift, schema });
337+
338+
const getGlobalSection = ({ lift }: { lift: Lift<MetaData> }): object =>
339+
buildSection({ guard: unionGuard(), lift });
340+
341+
const getDiscoverableGlobalSection = ({
342+
lift,
343+
schema,
344+
}: {
345+
lift: Lift<MetaData>;
346+
schema: Record<string, MethodSchema>;
347+
}): object => buildSection({ guard: unionGuard(), lift, schema });
316348

317349
const revokePoint = (method: string, ...args: unknown[]): void => {
318350
for (const grant of grants) {
@@ -342,7 +374,9 @@ export const sheafify = <
342374

343375
return {
344376
getSection,
377+
getDiscoverableSection,
345378
getGlobalSection,
379+
getDiscoverableGlobalSection,
346380
revokePoint,
347381
getExported,
348382
revokeAll,

packages/kernel-utils/src/sheaf/types.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import type { GET_INTERFACE_GUARD, Methods } from '@endo/exo';
1111
import type { InterfaceGuard } from '@endo/patterns';
1212

13+
import type { MethodSchema } from '../schema.ts';
14+
1315
/** A section: a capability covering a region of the interface topology. */
1416
export type Section<Core extends Methods = Methods> = Partial<Core> & {
1517
[K in typeof GET_INTERFACE_GUARD]?: (() => InterfaceGuard) | undefined;
@@ -98,8 +100,27 @@ export type Presheaf<MetaData extends Record<string, unknown>> =
98100
export type Sheaf<MetaData extends Record<string, unknown>> = {
99101
/** Produce a revocable dispatch exo over the given guard. */
100102
getSection: (opts: { guard: InterfaceGuard; lift: Lift<MetaData> }) => object;
101-
/** Produce a revocable dispatch exo over the full union guard of all presheaf sections. */
103+
/** Produce a revocable discoverable dispatch exo over the given guard. */
104+
getDiscoverableSection: (opts: {
105+
guard: InterfaceGuard;
106+
lift: Lift<MetaData>;
107+
schema: Record<string, MethodSchema>;
108+
}) => object;
109+
/**
110+
* Produce a revocable dispatch exo over the full union guard of all presheaf sections.
111+
*
112+
* @deprecated Provide an explicit guard via getSection instead.
113+
*/
102114
getGlobalSection: (opts: { lift: Lift<MetaData> }) => object;
115+
/**
116+
* Produce a revocable discoverable dispatch exo over the full union guard of all presheaf sections.
117+
*
118+
* @deprecated Provide an explicit guard via getDiscoverableSection instead.
119+
*/
120+
getDiscoverableGlobalSection: (opts: {
121+
lift: Lift<MetaData>;
122+
schema: Record<string, MethodSchema>;
123+
}) => object;
103124
/** Revoke every granted section whose guard covers the point (method, ...args). */
104125
revokePoint: (method: string, ...args: unknown[]) => void;
105126
/** Union guard of all active (non-revoked) granted sections, or undefined. */

0 commit comments

Comments
 (0)