feat: preserve evidence in workspace artifacts

This commit is contained in:
2026-08-09 20:03:28 +02:00
parent 64b778ade9
commit 212c973e3b
6 changed files with 656 additions and 7 deletions
+67
View File
@@ -177,6 +177,72 @@ function localizedIntroduction(workspace: WorkspaceDescriptor): string {
: `Installation setup for ${workspace.workspace.name}. Configure only the bindings supported by this installation.`;
}
function evidenceDocumentation(
workspace: WorkspaceDescriptor,
variables: readonly InstallationVariable[],
): string[] {
if (!("evidence" in workspace) || workspace.evidence === undefined) return [];
const { source, policy } = workspace.evidence;
const common = [
"## Evidence source",
"",
`- Type: \`${source.type}\``,
];
let details: string[];
if (source.type === "filesystem") {
details = [
`- URI: \`${source.uri}\``,
`- Patterns: ${source.patterns.map((pattern) => `\`${pattern}\``).join(", ")}`,
`- Maximum source bytes: \`${source.max_bytes}\``,
"- Ownership: the descriptor and its Evidence tree are owned by the same Git revision.",
"- Materialization: P6 materializes that revision-pinned tree and verifies real containment, including symlink safety.",
"- Export boundary: the browser/API ZIP does not include Evidence file bytes.",
];
} else if (source.type === "http") {
details = [
"- URIs:",
...source.uris.map((uri) => ` - \`${uri}\``),
`- Authentication: \`${source.authentication}\`. ${source.authentication === "none"
? "No credential file is required."
: "Provide the signed URL file through the installation file variable listed below."}`,
`- Connect timeout (ms): \`${source.connect_timeout_ms}\``,
`- Read timeout (ms): \`${source.read_timeout_ms}\``,
`- Maximum source bytes: \`${source.max_bytes}\``,
`- Maximum redirects: \`${source.max_redirects}\``,
`- Private hosts allowed: \`${source.allow_private_hosts}\``,
`- Maximum cache bytes: \`${source.max_cache_bytes}\``,
];
} else {
details = [
`- URI: \`${source.uri}\``,
...(source.endpoint_url === undefined ? [] : [`- Endpoint URL: \`${source.endpoint_url}\``]),
...(source.region === undefined ? [] : [`- Region: \`${source.region}\``]),
`- Credentials: \`${source.credentials}\`. ${source.credentials === "ambient"
? "Use ambient credentials; no Evidence credential file is required."
: "Provide credentials through the installation file variables listed below."}`,
`- Trusted endpoint: \`${source.trusted_endpoint}\``,
`- Private endpoint allowed: \`${source.allow_private_endpoint}\``,
`- Insecure endpoint allowed: \`${source.allow_insecure_endpoint}\``,
`- Maximum source bytes: \`${source.max_bytes}\``,
`- Maximum objects: \`${source.max_objects}\``,
`- Maximum pages: \`${source.max_pages}\``,
`- Page size: \`${source.page_size}\``,
];
}
const evidenceVariables = variables.filter(({ role }) => role === "EVIDENCE");
return [
...common,
...details,
`- Maximum chunk characters: \`${policy.max_chunk_chars}\``,
`- Retained published generations: \`${policy.retain_published_generations}\``,
...(evidenceVariables.length === 0 ? [] : [
"- Required installation file variables:",
...evidenceVariables.map(({ name }) => ` - \`${name}\``),
]),
"",
];
}
export function renderWorkspaceDocs(workspace: WorkspaceDescriptor): { envExample: string; markdown: string } {
const descriptor = validateWorkspaceDescriptor(workspace);
const contract = buildInstallationContract(descriptor);
@@ -213,6 +279,7 @@ export function renderWorkspaceDocs(workspace: WorkspaceDescriptor): { envExampl
)),
"",
]),
...evidenceDocumentation(descriptor, contract.variables),
].join("\n");
return { envExample, markdown };
+2
View File
@@ -790,6 +790,8 @@ export class WorkspaceRegistry {
}
private expectedSnapshotFiles(state: ActiveState): string[] {
// P1 snapshots only descriptors and derived public docs. P6 owns revision-pinned
// workspace-content materialization and its recursive containment checks.
return state.revisions.flatMap((revision) => revision.state === "operational"
? [`${revision.id}.yaml`, `${revision.id}.env.example`, `${revision.id}.md`]
: [`${revision.id}.yaml`]);