Add the workspace coding agent, launch gate, and composable locks

- run a singleton Codex App Server adapter in each dev workspace
- gate Web Studio with a reloadable fragment-token to HttpOnly-session exchange
- add ChatGPT device login and streamed chat to Web Studio
- isolate authoring access in a declarative NixOS service account
- split Web Studio canvas, object, panel, and model concerns
- check generated Quixos protocol clients for drift
- resolve same-repository quixos.lock fragments deterministically
- preserve the collaborative canvas gesture fixes
This commit is contained in:
2026-09-05 12:33:52 -07:00
parent c4d42a0ac5
commit 0c46850973
53 changed files with 5056 additions and 2514 deletions
+121 -5
View File
@@ -4,7 +4,15 @@ import {
capabilityId,
type WorkspaceRevision,
} from "../../src/capability-model/index.js";
import { compileCapabilitySource } from "../../src/capability-language/index.js";
import {
compileCapabilityResourceSource,
compileCapabilitySource,
type CapabilityImportEnvironment,
} from "../../src/capability-language/index.js";
import type {
InterfaceRevision,
PackageRevision,
} from "../../src/capability-model/index.js";
export const fixtureId = {
workspace: capabilityId.workspace("workspace:todo"),
@@ -62,11 +70,119 @@ export const capabilityFixtureSource = readFileSync(
"utf8",
);
export const makeValidCapabilityWorkspace = (): WorkspaceRevision => {
const result = compileCapabilitySource(
capabilityFixtureSource,
capabilityFixturePath,
const resourceSource = (name: string) => readFileSync(
resolve(process.cwd(), "test/fixtures", name),
"utf8",
);
export const capabilityResourceSources = {
named: resourceSource("named.interface.qx"),
owned: resourceSource("owned.interface.qx"),
summary: resourceSource("summary.interface.qx"),
todo: resourceSource("todo.package.qx"),
} as const;
const source = (repository: string, commit: string) => ({ repository, commit });
const interfaceRevision = (
resource: { kind: "interface"; revision: InterfaceRevision } |
{ kind: "package"; revision: PackageRevision },
) => {
if (resource.kind !== "interface") throw new Error("Expected interface fixture");
return resource.revision;
};
const packageRevision = (
resource: { kind: "interface"; revision: InterfaceRevision } |
{ kind: "package"; revision: PackageRevision },
) => {
if (resource.kind !== "package") throw new Error("Expected package fixture");
return resource.revision;
};
export const compileCapabilityFixture = (overrides: Partial<{
workspace: string;
named: string;
owned: string;
summary: string;
todo: string;
}> = {}) => {
const named = compileCapabilityResourceSource(
overrides.named ?? capabilityResourceSources.named,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-named.git",
"2".repeat(40),
),
fileName: "named.interface.qx",
},
);
if (!named.ok) return named;
const namedRevision = interfaceRevision(named.resource);
const namedEnvironment: CapabilityImportEnvironment = {
interfaces: new Map([["Named", namedRevision]]),
interfaceClosure: [namedRevision],
};
const owned = compileCapabilityResourceSource(
overrides.owned ?? capabilityResourceSources.owned,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-owned.git",
"3".repeat(40),
),
fileName: "owned.interface.qx",
environment: namedEnvironment,
},
);
if (!owned.ok) return owned;
const ownedRevision = interfaceRevision(owned.resource);
const summary = compileCapabilityResourceSource(
overrides.summary ?? capabilityResourceSources.summary,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-summary.git",
"4".repeat(40),
),
fileName: "summary.interface.qx",
},
);
if (!summary.ok) return summary;
const summaryRevision = interfaceRevision(summary.resource);
const todo = compileCapabilityResourceSource(
overrides.todo ?? capabilityResourceSources.todo,
{
source: source(
"https://repos.quixos.org/quixos-todo/package-todo-runtime.git",
"5".repeat(40),
),
fileName: "todo.package.qx",
environment: namedEnvironment,
},
);
if (!todo.ok) return todo;
const todoRevision = packageRevision(todo.resource);
return compileCapabilitySource(
overrides.workspace ?? capabilityFixtureSource,
capabilityFixturePath,
{
interfaces: new Map([
["Named", namedRevision],
["Owned", ownedRevision],
["Summary", summaryRevision],
]),
packages: new Map([["TodoRuntime", todoRevision]]),
interfaceClosure: [
namedRevision,
ownedRevision,
summaryRevision,
],
packageClosure: [todoRevision],
},
);
};
export const makeValidCapabilityWorkspace = (): WorkspaceRevision => {
const result = compileCapabilityFixture();
if (!result.ok) {
throw new Error(
result.diagnostics
+10
View File
@@ -0,0 +1,10 @@
import interface Named;
external atom Project id "atom:project";
external atom ProjectComponent id "atom:project-component";
package ComponentRuntime id "package:component-runtime" revision "package:component-runtime@1" {
constructor createComponent id "export:component-runtime:create-component" constructs ProjectComponent : atom-ref<Project>;
operation propsGet id "export:component-runtime:props-get" : unit -> string mode call receiver atom ProjectComponent requires {
interface project id "port:component:project" : Named;
};
}
+7
View File
@@ -0,0 +1,7 @@
import interface ReactComponent;
interface HasReactComponent id "interface:org.quixos.web-studio.has-react-component" revision "interface:org.quixos.web-studio.has-react-component@1" {
relation component id "member:org.quixos.web-studio.has-react-component:component" : optional-one interface ReactComponent {
resolve id "operation:org.quixos.web-studio.has-react-component:component:resolve";
}
}
+7
View File
@@ -0,0 +1,7 @@
interface Named id "interface:named" revision "interface:named@1" {
value name id "member:named:name" : string {
get id "operation:named:name:get";
set id "operation:named:name:set";
watch start id "operation:named:name:watch-start" stop id "operation:named:name:watch-stop";
}
}
+10
View File
@@ -0,0 +1,10 @@
import interface Named;
interface Owned id "interface:owned" revision "interface:owned@1" {
relation owner id "member:owned:owner" : exactly-one interface Named {
resolve id "operation:owned:owner:resolve";
connect id "operation:owned:owner:connect";
disconnect id "operation:owned:owner:disconnect";
watch start id "operation:owned:owner:watch-start" stop id "operation:owned:owner:watch-stop";
}
}
+5
View File
@@ -0,0 +1,5 @@
interface ReactComponent id "interface:org.quixos.web-studio.react-component" revision "interface:org.quixos.web-studio.react-component@1" {
value props id "member:org.quixos.web-studio.react-component:props" : string {
get id "operation:org.quixos.web-studio.react-component:props:get";
}
}
+5
View File
@@ -0,0 +1,5 @@
interface Summary id "interface:summary" revision "interface:summary@1" {
value summary id "member:summary:summary" : string {
get id "operation:summary:summary:get";
}
}
+4 -44
View File
@@ -2,50 +2,10 @@ workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "111111111
atom Project id "atom:project" doc "A project containing work.";
atom Person id "atom:person";
interface Named id "interface:named" revision "interface:named@1" source {
repository "https://repos.quixos.org/quixos-todo/interface-named.git";
commit "2222222222222222222222222222222222222222";
} {
value name id "member:named:name" : string {
get id "operation:named:name:get";
set id "operation:named:name:set";
watch start id "operation:named:name:watch-start" stop id "operation:named:name:watch-stop";
}
}
interface Owned id "interface:owned" revision "interface:owned@1" source {
repository "https://repos.quixos.org/quixos-todo/interface-owned.git";
commit "3333333333333333333333333333333333333333";
} {
relation owner id "member:owned:owner" : exactly-one interface Named {
resolve id "operation:owned:owner:resolve";
connect id "operation:owned:owner:connect";
disconnect id "operation:owned:owner:disconnect";
watch start id "operation:owned:owner:watch-start" stop id "operation:owned:owner:watch-stop";
}
}
interface Summary id "interface:summary" revision "interface:summary@1" source {
repository "https://repos.quixos.org/quixos-todo/interface-summary.git";
commit "4444444444444444444444444444444444444444";
} {
value summary id "member:summary:summary" : string {
get id "operation:summary:summary:get";
}
}
package TodoRuntime id "package:todo-runtime" revision "package:todo-runtime@1" source {
repository "https://repos.quixos.org/quixos-todo/package-todo-runtime.git";
commit "5555555555555555555555555555555555555555";
} {
operation summaryGet id "export:todo-runtime:summary-get" : unit -> string
mode call receiver interfaces [Named] requires {
state title id "port:summary:title" : string [read];
interface named id "port:summary:named" : Named;
constructor person id "port:summary:person" : Person;
};
constructor createPerson id "export:todo-runtime:create-person" constructs Person : unit;
}
import interface Named;
import interface Owned;
import interface Summary;
import package TodoRuntime;
shared state ProjectTitle id "slot:project:title" on Project : string
policy optimistic-register default "Untitled project";
+12
View File
@@ -0,0 +1,12 @@
import interface Named;
external atom Person id "atom:person";
package TodoRuntime id "package:todo-runtime" revision "package:todo-runtime@1" {
operation summaryGet id "export:todo-runtime:summary-get" : unit -> string
mode call receiver interfaces [Named] requires {
state title id "port:summary:title" : string [read];
interface named id "port:summary:named" : Named;
constructor person id "port:summary:person" : Person;
};
constructor createPerson id "export:todo-runtime:create-person" constructs Person : unit;
}
+12 -30
View File
@@ -2,34 +2,10 @@ workspace WebStudioFixture id "workspace:web-studio-fixture" revision "workspace
atom Project id "atom:project";
atom ProjectComponent id "atom:project-component";
interface ReactComponent id "interface:org.quixos.web-studio.react-component" revision "interface:org.quixos.web-studio.react-component@1" source {
repository "https://repos.quixos.org/org-quixos-web-studio/interface-react-component.git";
commit "2222222222222222222222222222222222222222";
} {
value props id "member:org.quixos.web-studio.react-component:props" : string {
get id "operation:org.quixos.web-studio.react-component:props:get";
}
}
interface HasReactComponent id "interface:org.quixos.web-studio.has-react-component" revision "interface:org.quixos.web-studio.has-react-component@1" source {
repository "https://repos.quixos.org/org-quixos-web-studio/interface-has-react-component.git";
commit "3333333333333333333333333333333333333333";
} {
relation component id "member:org.quixos.web-studio.has-react-component:component" : optional-one interface ReactComponent {
resolve id "operation:org.quixos.web-studio.has-react-component:component:resolve";
}
}
package ComponentRuntime id "package:component-runtime" revision "package:component-runtime@1" source {
repository "https://repos.quixos.org/quixos-test/package-component-runtime.git";
commit "4444444444444444444444444444444444444444";
} {
constructor createComponent id "export:component-runtime:create-component" constructs ProjectComponent : atom-ref<Project>;
operation propsGet id "export:component-runtime:props-get" : unit -> string mode call receiver atom ProjectComponent requires {
edge subject id "port:component:subject" : exactly-one atom Project [resolve];
state projectName id "port:component:project-name" : string [read];
};
}
import interface Named;
import interface ReactComponent;
import interface HasReactComponent;
import package ComponentRuntime;
shared state ProjectName id "slot:project:name" on Project : string
policy optimistic-register default "Untitled project";
@@ -38,14 +14,20 @@ workspace WebStudioFixture id "workspace:web-studio-fixture" revision "workspace
atom Project projection component id "projection:project:component" optional-one;
}
conform Project as Named {
bind name.get to state ProjectName.read;
bind name.set to state ProjectName.write;
bind name.watch-start to state ProjectName.watch-start;
bind name.watch-stop to state ProjectName.watch-stop;
}
conform Project as HasReactComponent {
bind component.resolve to edge ProjectComponentEdge.component.resolve;
materialize component if absent using constructor ProjectComponent via edge ProjectComponentEdge.subject;
}
conform ProjectComponent as ReactComponent {
bind props.get to package ComponentRuntime.propsGet with {
subject to edge ProjectComponentEdge.subject;
projectName to state ProjectName via edge ProjectComponentEdge.subject;
project to interface Named via edge ProjectComponentEdge.subject;
};
}