Skip to content

Commit 7d9e766

Browse files
kubohiroyacodex
andauthored
feat(ide-gsm-client): add FDM space delete dry-run helper (#1750)
Co-authored-by: codex <codex@example.com>
1 parent a312682 commit 7d9e766

6 files changed

Lines changed: 59 additions & 0 deletions

File tree

docs/fdm-node-spec.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ Mutations:
346346
- `cancelTask(taskId)`: cancellation request for a server task.
347347
- Existing `sim`, `calib`, and `check` command mutations through the owning `idegsm-project` command service, not a second FDM-specific command implementation.
348348

349+
Client-side server-space deletion must use the explicit FDM space lifecycle API. `fdmSpaceDeleteDryRun` is the safe inspection helper and always sends `apply: false`; apply/archive/delete UI remains gated on server authorization and reference-aware semantics. `fdmDirectoryRemove` removes an entry inside a space and must not be used as server-side FDM space delete/archive.
350+
349351
Subscriptions:
350352

351353
- `subscribeFdmCellLog`: bounded live log snapshots for the selected cell.

docs/fdm-seven-layer-contract-audit.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,7 @@ Phase B1 now keeps each layer represented by an executable fixture or an explici
9494
| L6 space | space catalog metadata with explicit unavailable or unscoped capability provenance | covered, pending #1735/#1736 action/view expansion |
9595

9696
Later issues should extend these fixtures instead of creating divergent local terminology. #1732 adds read-only intake for baseline/fork/lineage capability summaries without per-space inference, #1735 owns lifecycle dry-run/safety wiring, and #1736 owns fork/cross-space read-only dashboard views.
97+
98+
## L6 lifecycle safety
99+
100+
`fdmSpaceDeleteDryRun` is the safe client helper for server-side FDM space delete/archive inspection. It always calls `fdmSpaceDelete` with `apply: false` and is tested separately from `fdmDirectoryRemove`. Destructive apply behavior and UI actions remain gated until the server contract provides reference-aware denial, authorization evidence, and explicit apply semantics.

packages/ide-gsm-client/__tests__/IdeGsmClient.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,42 @@ describe('directory read contracts', () => {
617617
});
618618
});
619619

620+
it('runs FDM space delete dry-run through the space lifecycle mutation only', async () => {
621+
const { GraphQLClient } = await import('graphql-request');
622+
const spy = vi.spyOn(GraphQLClient.prototype, 'request').mockResolvedValueOnce({
623+
fdmSpaceDelete: {
624+
apply: false,
625+
archived: true,
626+
byteCount: 1024,
627+
confirmed: false,
628+
deleted: false,
629+
fileCount: 3,
630+
physicalDelete: false,
631+
spaceId: 'working',
632+
topLevelEntries: ['runs', 'snapshots'],
633+
spaces: null,
634+
},
635+
});
636+
const client = new IdeGsmClient('https://endpoint.example', 'jwt-secret');
637+
638+
await expect(
639+
client.fdmSpaceDeleteDryRun({ spaceId: 'working', deleteFiles: true })
640+
).resolves.toMatchObject({
641+
apply: false,
642+
deleted: false,
643+
physicalDelete: false,
644+
spaceId: 'working',
645+
});
646+
647+
expect(String(spy.mock.calls[0]?.[0])).toContain('fdmSpaceDelete');
648+
expect(String(spy.mock.calls[0]?.[0])).not.toContain('fdmDirectoryRemove');
649+
expect(spy.mock.calls[0]?.[1]).toEqual({
650+
spaceId: 'working',
651+
apply: false,
652+
deleteFiles: true,
653+
});
654+
});
655+
620656
it.each([
621657
[
622658
'project path',

packages/ide-gsm-client/src/IdeGsmClient.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
IdeGsmFdmSpace,
1313
IdeGsmFdmSpaceCreateInput,
1414
IdeGsmFdmSpaceDefaults,
15+
IdeGsmFdmSpaceDeleteDryRunInput,
1516
IdeGsmFdmSpaceDeleteInput,
1617
IdeGsmFdmSpaceDeleteReport,
1718
IdeGsmFdmSpacesReport,
@@ -1676,6 +1677,16 @@ export class IdeGsmClient {
16761677
);
16771678
}
16781679

1680+
async fdmSpaceDeleteDryRun(
1681+
input: IdeGsmFdmSpaceDeleteDryRunInput
1682+
): Promise<IdeGsmFdmSpaceDeleteReport> {
1683+
return this.fdmSpaceDelete({
1684+
spaceId: input.spaceId,
1685+
apply: false,
1686+
deleteFiles: input.deleteFiles,
1687+
});
1688+
}
1689+
16791690
async fdmDashboardStatus(input: FdmDashboardStatusInput): Promise<FdmDashboardStatusPayload> {
16801691
return this.requestReport(
16811692
'fdmDashboardStatus',

packages/ide-gsm-client/src/ideGsmDirectoryTypes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ export interface IdeGsmFdmSpaceDeleteInput {
107107
confirmation?: string;
108108
}
109109

110+
export interface IdeGsmFdmSpaceDeleteDryRunInput {
111+
spaceId: string;
112+
deleteFiles?: boolean;
113+
}
114+
110115
export interface IdeGsmFdmSpaceDeleteReport {
111116
apply: boolean;
112117
archived: boolean;

packages/ide-gsm-client/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type {
1010
IdeGsmFdmSpace,
1111
IdeGsmFdmSpaceCreateInput,
1212
IdeGsmFdmSpaceDefaults,
13+
IdeGsmFdmSpaceDeleteDryRunInput,
1314
IdeGsmFdmSpaceDeleteInput,
1415
IdeGsmFdmSpaceDeleteReport,
1516
IdeGsmFdmSpacesReport,

0 commit comments

Comments
 (0)