Skip to content

Commit abeec4d

Browse files
committed
feat(presenter): add presenter workflow and deploy_presenter hand-off
# Conflicts: # apps/hub/src/inference/model-limits.ts # apps/hub/src/workflows/packaged.test.ts
1 parent 14863c2 commit abeec4d

39 files changed

Lines changed: 2201 additions & 24 deletions

‎apps/hub/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@corbits-builder/coder": "workspace:*",
1919
"@corbits-builder/coordinator": "workspace:*",
2020
"@corbits-builder/designer": "workspace:*",
21+
"@corbits-builder/presenter": "workspace:*",
2122
"@corbits-builder/reviewer": "workspace:*",
2223
"@corbits-builder/schemas": "workspace:*",
2324
"@corbits-builder/writer": "workspace:*",

‎apps/hub/src/artifacts/approval-package.ts‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ export type StoredPackageSource = {
4545
kind: string;
4646
version: number;
4747
sha256: string;
48+
approved?: boolean;
49+
};
50+
51+
export type SealedPackageAssignment = {
52+
audience: string;
53+
decisionNeeded?: string;
54+
sources: readonly {
55+
artifactId: string;
56+
kind: string;
57+
version: number;
58+
sha256: string;
59+
}[];
4860
};
4961

5062
export type StoredSourceReader = (
@@ -118,11 +130,61 @@ async function verifySourcePin(
118130
message: `${source.artifactId}@${String(source.version)}`,
119131
},
120132
]);
133+
if (stored.approved === false)
134+
throw new ProjectError(422, "unapproved_package_source", [
135+
{
136+
path: "manifest.sources",
137+
message: `${source.kind}@${String(source.version)}`,
138+
},
139+
]);
140+
}
141+
142+
function verifySealedAssignment(
143+
bundle: ApprovalPackageBundle,
144+
sealed: SealedPackageAssignment,
145+
): void {
146+
if (bundle.manifest.audience.trim() !== sealed.audience.trim()) {
147+
throw new ProjectError(422, "package_assignment_mismatch", [
148+
{
149+
path: "manifest.audience",
150+
message: `expected ${sealed.audience}`,
151+
},
152+
]);
153+
}
154+
if (
155+
sealed.decisionNeeded !== undefined &&
156+
bundle.manifest.decisionNeeded.trim() !== sealed.decisionNeeded.trim()
157+
) {
158+
throw new ProjectError(422, "package_assignment_mismatch", [
159+
{
160+
path: "manifest.decisionNeeded",
161+
message: `expected ${sealed.decisionNeeded}`,
162+
},
163+
]);
164+
}
165+
const actualKeys = bundle.manifest.sources.map(
166+
(s) => `${s.artifactId}:${s.kind}:${String(s.version)}:${s.sha256}`,
167+
);
168+
const expectedKeys = sealed.sources.map(
169+
(s) => `${s.artifactId}:${s.kind}:${String(s.version)}:${s.sha256}`,
170+
);
171+
if (
172+
actualKeys.length !== expectedKeys.length ||
173+
actualKeys.some((key, idx) => key !== expectedKeys[idx])
174+
) {
175+
throw new ProjectError(422, "package_assignment_mismatch", [
176+
{
177+
path: "manifest.sources",
178+
message: "sources do not match the approved hand-off selection",
179+
},
180+
]);
181+
}
121182
}
122183

123184
export async function validateApprovalPackage(
124185
input: string | ApprovalPackageBundle,
125186
readSource: StoredSourceReader,
187+
sealedAssignment?: SealedPackageAssignment,
126188
): Promise<ApprovalPackageBundle> {
127189
const bundle =
128190
typeof input === "string" ? parseApprovalPackageBundle(input) : input;
@@ -133,6 +195,9 @@ export async function validateApprovalPackage(
133195
]);
134196
}
135197
scanApprovalPackageForSecrets(bundle);
198+
if (sealedAssignment !== undefined) {
199+
verifySealedAssignment(bundle, sealedAssignment);
200+
}
136201
for (const source of bundle.manifest.sources)
137202
await verifySourcePin(source, readSource);
138203
return bundle;
@@ -141,17 +206,27 @@ export async function validateApprovalPackage(
141206
export function readProjectPackageSource(
142207
db: ArtifactDb,
143208
tenantId: string,
209+
isApproved?: (
210+
artifactId: string,
211+
kind: string,
212+
version: number,
213+
) => Promise<boolean>,
144214
): StoredSourceReader {
145215
return async (artifactId, version) => {
146216
if (!UUID_RE.test(artifactId)) return undefined;
147217
const row = await getArtifact(db, artifactId);
148218
if (!row || row.tenantId !== tenantId) return undefined;
149219
const stored = await getArtifactVersion(db, artifactId, version);
150220
if (!stored?.contentSha256) return undefined;
221+
const approved =
222+
isApproved === undefined
223+
? true
224+
: await isApproved(artifactId, row.kind, stored.version);
151225
return {
152226
kind: row.kind,
153227
version: stored.version,
154228
sha256: stored.contentSha256,
229+
approved,
155230
};
156231
};
157232
}

0 commit comments

Comments
 (0)