philosophy shifting for Workspaces and Projects
This commit is contained in:
parent
0df6661fc5
commit
0482dfbace
81
docs/executive/workspace-philosophy-change.md
Normal file
81
docs/executive/workspace-philosophy-change.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Gadget Code Workspaces
|
||||||
|
|
||||||
|
I want to talk about Project Management in the application, and a bit of a change in philosophy. This can be viewed as maturity for the system, a refinement that has evolved out of use, and the careful consideration of that use.
|
||||||
|
|
||||||
|
This session won't involve the socket protocol, or AI API calls. This session will focus on @docs/workspace-management.md and @docs/gadget-workspace.md, and the @gadget-code/src/models/project.ts managed by them.
|
||||||
|
|
||||||
|
We are going to revisit @gadget-drone/docs/gadget-drone.md, and specifically the startup procedure.
|
||||||
|
|
||||||
|
Now that you have a fairly complete picture of how things are, I want you to please reasona about the following:
|
||||||
|
|
||||||
|
1. A drone 'instance' is now used for locking a Workspace. The workspace itself is now assigned an ID (see below), which it can now do using nanoid as used elsewhere throughout the project.
|
||||||
|
2. When a drone is starting in a workspace directory (one with a .gadget directory populated with the expected contents, intact), it will register as the drone that is currently servicing that workspace. The drone will "lock" the workspace to itself. And this is to prevent other drones from starting in the same directory, and colliding.
|
||||||
|
|
||||||
|
That is what makes it obvious that a drone's specific runtime instance is never what matters. The drone 'instance' is not what matters. That only determines if the Workspace is current Available, or not.
|
||||||
|
|
||||||
|
## Workspace Philosophy Change
|
||||||
|
|
||||||
|
The new [Workspace](../../gadget-code/src/models/workspace.ts) mode and [IWorkSpace](../../packages/api/src/interfaces/workspace.ts) interface have been created to provide the backing for a workspace instance on a host.
|
||||||
|
|
||||||
|
A drone runs in a directory on a host. We need to make sure only one drone is ever running in the same directory on the same host at the same time.
|
||||||
|
|
||||||
|
## Workspace Files
|
||||||
|
|
||||||
|
In the `.gadget` directory there is a `workspace.json` file. We will now be driving drone startup logic using this file. The drone will do more to maintain this file.
|
||||||
|
|
||||||
|
For one, the drone must now maintain the workspace file as it deploys projects to the workspace, removes them from the workspace, and works within them.
|
||||||
|
|
||||||
|
The drone will also, at startup, query `gadget-code:backend` for each project it has installed, looking for change that might have happened within each `Project` in the time since the drone was shut down. This lets the drone sense changes such as to the slug - which will then need to become a directory name change.
|
||||||
|
|
||||||
|
Here is a current workspace.json file contents:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"workspaceId": "0266f92d-feb8-4875-8d3d-701636d937ab",
|
||||||
|
"createdAt": "2026-04-30T23:05:23.789Z",
|
||||||
|
"hostname": "mysterymachine",
|
||||||
|
"workspaceDir": "/home/rob/workspaces/core-001",
|
||||||
|
"chatSession": null,
|
||||||
|
"lockedProject": {
|
||||||
|
"lockedAt": "2026-05-10T15:07:10.884Z",
|
||||||
|
"_id": "ZX3ig5Y2cPIJFY1mzrF11",
|
||||||
|
"slug": "test-project"
|
||||||
|
},
|
||||||
|
"projects": [
|
||||||
|
{
|
||||||
|
"_id": "ZX3ig5Y2cPIJFY1mzrF11",
|
||||||
|
"slug": "test-project",
|
||||||
|
"clonedAt": null,
|
||||||
|
"lastSyncAt": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"registration": {
|
||||||
|
"_id": "uvlB26jV7vlki1jlULFUS",
|
||||||
|
"status": "available",
|
||||||
|
"registeredAt": "2026-05-10T14:32:30.809Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`workspace.json` is always read at startup. It is always written as change happens, and at shutdown.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
1. Create gadget-code/src/services/workspace.ts as the CRUD-style service you'll find for Project in that same directory
|
||||||
|
|
||||||
|
### WorkspaceService
|
||||||
|
|
||||||
|
Implements the CRUD services needed to support workspaces on hosts, and the drones running within them.
|
||||||
|
|
||||||
|
### Workspace API Controller
|
||||||
|
|
||||||
|
Route: /api/v1/workspace
|
||||||
|
|
||||||
|
Endpoints:
|
||||||
|
|
||||||
|
- POST /
|
||||||
|
- GET /
|
||||||
|
- GET /:workspaceId
|
||||||
|
- PUT /:workspaceId
|
||||||
|
- PUT /:workspaceId
|
||||||
|
- DELETE /:workspaceId
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
The Chat View area of Chat Session view was growing in width (unbounded), and has been fixed. From the agent:
|
||||||
|
|
||||||
|
> The root cause was a flexbox sizing rule: in ChatSessionView.tsx:768 the parent row flex-1 flex bg-bg-primary overflow-hidden relative has two children — the content area and the sidebar. The content area (line 777) was flex-1 flex flex-col relative, which as a flex child defaults to min-width: auto. This means its intrinsic content width (driven by the wide <pre>) is used as a minimum, forcing the flex item to expand the whole row beyond the viewport.
|
||||||
|
>
|
||||||
|
> Adding min-w-0 overrides min-width: auto to min-width: 0, letting the flex item shrink below its content's natural width. Now the overflow-x-auto on the markdown wrapper actually has somewhere to scroll into.
|
||||||
24
gadget-code/src/models/workspace.ts
Normal file
24
gadget-code/src/models/workspace.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// src/models/workspace.ts
|
||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import { model, Schema } from "mongoose";
|
||||||
|
import { IWorkspace, WorkspaceStatus } from "@gadget/api";
|
||||||
|
|
||||||
|
export const WorkspaceSchema = new Schema<IWorkspace>({
|
||||||
|
_id: { type: String, required: true },
|
||||||
|
createdAt: { type: Date, required: true, default: Date.now, index: -1 },
|
||||||
|
user: { type: String, required: true, index: 1, ref: "User" },
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
enum: WorkspaceStatus,
|
||||||
|
default: WorkspaceStatus.Inactive,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hostname: { type: String, required: true },
|
||||||
|
rootDir: { type: String, required: true },
|
||||||
|
lockedDrone: { type: String, ref: "DroneRegistration" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Workspace = model<IWorkspace>("Workspace", WorkspaceSchema);
|
||||||
|
export default Workspace;
|
||||||
@ -23,6 +23,7 @@ export * from "./interfaces/drone-registration.ts";
|
|||||||
export * from "./interfaces/ide-session.ts";
|
export * from "./interfaces/ide-session.ts";
|
||||||
export * from "./interfaces/project.ts";
|
export * from "./interfaces/project.ts";
|
||||||
export * from "./interfaces/user.ts";
|
export * from "./interfaces/user.ts";
|
||||||
|
export * from "./interfaces/workspace.ts";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Socket.IO Interfaces
|
* Socket.IO Interfaces
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// src/models/chat-session.ts
|
// src/interfaces/project.ts
|
||||||
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
// Licensed under the Apache License, Version 2.0
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
|||||||
26
packages/api/src/interfaces/workspace.ts
Normal file
26
packages/api/src/interfaces/workspace.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// src/interfaces/workspace.ts
|
||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import type { IUser } from "./user.js";
|
||||||
|
import { GadgetId } from "../lib/gadget-id.ts";
|
||||||
|
import { HydratedDocument } from "mongoose";
|
||||||
|
import { IDroneRegistration } from "./drone-registration.ts";
|
||||||
|
|
||||||
|
export enum WorkspaceStatus {
|
||||||
|
Active = "online",
|
||||||
|
Inactive = "offline",
|
||||||
|
Archived = "archived",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWorkspace {
|
||||||
|
_id: GadgetId;
|
||||||
|
createdAt: Date;
|
||||||
|
user: IUser | GadgetId;
|
||||||
|
status: WorkspaceStatus;
|
||||||
|
hostname: string;
|
||||||
|
rootDir: string;
|
||||||
|
lockedDrone?: GadgetId | IDroneRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WorkspaceDocument = HydratedDocument<IWorkspace>;
|
||||||
Loading…
Reference in New Issue
Block a user