opencode-plugin-flow 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +21 -8
- package/dist/application/flow-service.d.ts +197 -101
- package/dist/application/ports/evidence-artifact-store.d.ts +28 -0
- package/dist/application/ports/session-repository.d.ts +8 -1
- package/dist/application/ports/source-identity.d.ts +61 -0
- package/dist/application/replay/canonical-json.d.ts +7 -0
- package/dist/application/replay/contract.d.ts +2007 -0
- package/dist/application/replay/engine.d.ts +101 -0
- package/dist/application/replay/index.d.ts +7 -0
- package/dist/application/replay/privacy.d.ts +14 -0
- package/dist/application/schema.d.ts +891 -5
- package/dist/cli.js +13 -2
- package/dist/cli.js.map +3 -3
- package/dist/domain/session.d.ts +174 -0
- package/dist/domain/transitions.d.ts +138 -55
- package/dist/domain/validation-command.d.ts +8 -0
- package/dist/index.js +3923 -1422
- package/dist/index.js.map +19 -13
- package/dist/infrastructure/fs/evidence-artifact-store.d.ts +5 -0
- package/dist/infrastructure/fs/source-identity.d.ts +26 -0
- package/dist/infrastructure/fs/workspace-flow-service.d.ts +1 -1
- package/dist/infrastructure/fs/workspace.d.ts +2 -0
- package/dist/platform/opencode/tools.d.ts +1 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "node:fs/promises";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { isAbsolute, join, normalize, sep } from "node:path";
|
|
15
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
15
16
|
|
|
16
17
|
// src/guidance/ids.ts
|
|
17
18
|
var FLOW_GUIDANCE_TOPICS = [
|
|
@@ -29,6 +30,8 @@ var FLOW_GUIDANCE_TOPICS = [
|
|
|
29
30
|
var LEGACY_MARKER = ".flow-skill-version";
|
|
30
31
|
var NO_FOLLOW = constants.O_NOFOLLOW ?? 0;
|
|
31
32
|
var SUPPORTED_LEGACY_MAJOR = "4";
|
|
33
|
+
var POST_MOVE_VERIFICATION_ATTEMPTS = 4;
|
|
34
|
+
var POST_MOVE_VERIFICATION_RETRY_MS = 25;
|
|
32
35
|
var SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
33
36
|
function configuredHome() {
|
|
34
37
|
return process.env.HOME?.trim() || process.env.USERPROFILE?.trim() || homedir();
|
|
@@ -226,6 +229,14 @@ async function ensureRealArchiveRoot(path) {
|
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
}
|
|
232
|
+
async function verifyMovedLegacyFolder(name, archivePath) {
|
|
233
|
+
let verified = await inspectLegacyFolder(name, archivePath);
|
|
234
|
+
for (let attempt = 1;attempt < POST_MOVE_VERIFICATION_ATTEMPTS && verified.status !== "eligible"; attempt += 1) {
|
|
235
|
+
await sleep(POST_MOVE_VERIFICATION_RETRY_MS * attempt);
|
|
236
|
+
verified = await inspectLegacyFolder(name, archivePath);
|
|
237
|
+
}
|
|
238
|
+
return verified;
|
|
239
|
+
}
|
|
229
240
|
async function cleanupLegacySkills(options) {
|
|
230
241
|
const home = options?.home ?? configuredHome();
|
|
231
242
|
const root = resolveLegacySkillsRoot(home);
|
|
@@ -259,7 +270,7 @@ async function cleanupLegacySkills(options) {
|
|
|
259
270
|
continue;
|
|
260
271
|
}
|
|
261
272
|
await options?.afterQuarantine?.({ name, path, archivePath });
|
|
262
|
-
const verified = await
|
|
273
|
+
const verified = await verifyMovedLegacyFolder(name, archivePath);
|
|
263
274
|
if (verified.status !== "eligible") {
|
|
264
275
|
results.push({
|
|
265
276
|
name,
|
|
@@ -374,4 +385,4 @@ main(process.argv).catch((error) => {
|
|
|
374
385
|
process.exitCode = 1;
|
|
375
386
|
});
|
|
376
387
|
|
|
377
|
-
//# debugId=
|
|
388
|
+
//# debugId=4B60DFA5FC84470464756E2164756E21
|
package/dist/cli.js.map
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/distribution/legacy-cleanup.ts", "../src/guidance/ids.ts", "../src/version.ts", "../src/cli.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { createHash } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport {\n\ttype FileHandle,\n\tlstat,\n\tmkdir,\n\topen,\n\treaddir,\n\trename,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, normalize, sep } from \"node:path\";\nimport { FLOW_GUIDANCE_TOPICS } from \"../guidance/ids.js\";\n\nconst LEGACY_MARKER = \".flow-skill-version\";\nconst NO_FOLLOW = constants.O_NOFOLLOW ?? 0;\nconst SUPPORTED_LEGACY_MAJOR = \"4\";\nconst SEMVER_PATTERN =\n\t/^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\ntype ParsedMarker = {\n\tversion: string;\n\tfiles: Map<string, string>;\n};\n\nexport type LegacySkillCleanupResult = {\n\tname: string;\n\tpath: string;\n\tstatus: \"absent\" | \"eligible\" | \"archived\" | \"refused\" | \"quarantined\";\n\treason?: string;\n\tarchivePath?: string;\n};\n\nexport type LegacyCleanupReport = {\n\tmode: \"dry-run\" | \"apply\";\n\troot: string;\n\tarchiveRoot: string;\n\tresults: LegacySkillCleanupResult[];\n};\n\nfunction configuredHome(): string {\n\treturn (\n\t\tprocess.env.HOME?.trim() || process.env.USERPROFILE?.trim() || homedir()\n\t);\n}\n\nexport function resolveLegacySkillsRoot(home = configuredHome()): string {\n\treturn join(home, \".config\", \"opencode\", \"skills\");\n}\n\nexport function resolveLegacyArchiveRoot(home = configuredHome()): string {\n\treturn join(home, \".config\", \"opencode\", \"flow-legacy-skills\");\n}\n\nfunction sha256(content: string): string {\n\treturn createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction safeLegacyPath(folder: string, relativePath: string): string {\n\tif (\n\t\t!relativePath ||\n\t\tisAbsolute(relativePath) ||\n\t\trelativePath.includes(\"\\\\\") ||\n\t\trelativePath\n\t\t\t.split(\"/\")\n\t\t\t.some((part) => !part || part === \".\" || part === \"..\")\n\t) {\n\t\tthrow new Error(`unsafe marker path '${relativePath}'`);\n\t}\n\tconst resolved = normalize(join(folder, ...relativePath.split(\"/\")));\n\tif (!resolved.startsWith(`${folder}${sep}`)) {\n\t\tthrow new Error(`unsafe marker path '${relativePath}'`);\n\t}\n\treturn resolved;\n}\n\nasync function optionalStat(path: string) {\n\ttry {\n\t\treturn await lstat(path, { bigint: false });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n\t\tthrow error;\n\t}\n}\n\nasync function readRegularFileWithoutFollowing(path: string): Promise<string> {\n\tlet handle: FileHandle | undefined;\n\ttry {\n\t\tconst pathMetadata = await lstat(path);\n\t\tif (pathMetadata.isSymbolicLink()) {\n\t\t\tthrow new Error(`symbolic link refused: ${path}`);\n\t\t}\n\t\tif (!pathMetadata.isFile()) throw new Error(`not a regular file: ${path}`);\n\t\thandle = await open(path, constants.O_RDONLY | NO_FOLLOW);\n\t\tconst metadata = await handle.stat();\n\t\tif (!metadata.isFile()) throw new Error(`not a regular file: ${path}`);\n\t\tif (\n\t\t\tmetadata.dev !== pathMetadata.dev ||\n\t\t\tmetadata.ino !== pathMetadata.ino\n\t\t) {\n\t\t\tthrow new Error(`file changed while cleanup was running: ${path}`);\n\t\t}\n\t\treturn await handle.readFile({ encoding: \"utf8\" });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ELOOP\") {\n\t\t\tthrow new Error(`symbolic link refused: ${path}`);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tawait handle?.close();\n\t}\n}\n\nfunction parseMarker(content: string): ParsedMarker {\n\tlet version: string | undefined;\n\tconst files = new Map<string, string>();\n\tfor (const line of content.split(/\\r?\\n/)) {\n\t\tif (!line) continue;\n\t\tconst versionMatch = /^version=(.+)$/.exec(line);\n\t\tif (versionMatch?.[1]) {\n\t\t\tif (version) throw new Error(\"marker contains duplicate versions\");\n\t\t\tversion = versionMatch[1];\n\t\t\tcontinue;\n\t\t}\n\t\tconst fileMatch =\n\t\t\t/^file=(.+) sha256=([a-f0-9]{64})$/.exec(line) ??\n\t\t\t/^file=(.+)=sha256:([a-f0-9]{64})$/.exec(line);\n\t\tif (fileMatch?.[1] && fileMatch[2]) {\n\t\t\tif (files.has(fileMatch[1])) {\n\t\t\t\tthrow new Error(`marker contains duplicate file '${fileMatch[1]}'`);\n\t\t\t}\n\t\t\tfiles.set(fileMatch[1], fileMatch[2]);\n\t\t\tcontinue;\n\t\t}\n\t\tconst topLevelHash = /^hash=sha256:([a-f0-9]{64})$/.exec(line);\n\t\tif (topLevelHash?.[1] && !files.has(\"SKILL.md\")) {\n\t\t\tfiles.set(\"SKILL.md\", topLevelHash[1]);\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`marker contains an invalid line: '${line}'`);\n\t}\n\tif (!version) throw new Error(\"marker has no version\");\n\tif (!files.has(\"SKILL.md\")) throw new Error(\"marker does not own SKILL.md\");\n\treturn { version, files };\n}\n\nfunction assertSupportedLegacyVersion(version: string): void {\n\tconst match = SEMVER_PATTERN.exec(version);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`marker version '${version}' is not a valid semantic version`,\n\t\t);\n\t}\n\tif (match[1] !== SUPPORTED_LEGACY_MAJOR) {\n\t\tthrow new Error(\n\t\t\t`marker version '${version}' is outside the supported legacy range >=4.0.0 <5.0.0`,\n\t\t);\n\t}\n}\n\nfunction expectedDirectoryEntries(\n\tmarker: ParsedMarker,\n): Map<string, Set<string>> {\n\tconst entries = new Map<string, Set<string>>([\n\t\t[\"\", new Set([LEGACY_MARKER])],\n\t]);\n\tfor (const relativePath of marker.files.keys()) {\n\t\tconst parts = relativePath.split(\"/\");\n\t\tlet parent = \"\";\n\t\tfor (let index = 0; index < parts.length; index += 1) {\n\t\t\tconst part = parts[index];\n\t\t\tif (!part) throw new Error(`unsafe marker path '${relativePath}'`);\n\t\t\tconst children = entries.get(parent) ?? new Set<string>();\n\t\t\tchildren.add(part);\n\t\t\tentries.set(parent, children);\n\t\t\tif (index < parts.length - 1) {\n\t\t\t\tparent = parent ? `${parent}/${part}` : part;\n\t\t\t\tif (!entries.has(parent)) entries.set(parent, new Set());\n\t\t\t}\n\t\t}\n\t}\n\treturn entries;\n}\n\nasync function inspectLegacyFolder(\n\tname: string,\n\tfolder: string,\n): Promise<LegacySkillCleanupResult> {\n\tconst metadata = await optionalStat(folder);\n\tif (!metadata) return { name, path: folder, status: \"absent\" };\n\tif (!metadata.isDirectory()) {\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"refused\",\n\t\t\treason: \"path is not a real directory\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst markerPath = join(folder, LEGACY_MARKER);\n\t\tconst marker = parseMarker(\n\t\t\tawait readRegularFileWithoutFollowing(markerPath),\n\t\t);\n\t\tassertSupportedLegacyVersion(marker.version);\n\t\tconst directories = expectedDirectoryEntries(marker);\n\t\tfor (const [relativeDirectory, expectedEntries] of directories) {\n\t\t\tconst directory = relativeDirectory\n\t\t\t\t? safeLegacyPath(folder, relativeDirectory)\n\t\t\t\t: folder;\n\t\t\tconst directoryMetadata = await optionalStat(directory);\n\t\t\tif (!directoryMetadata?.isDirectory()) {\n\t\t\t\tthrow new Error(`expected real directory: ${relativeDirectory || \".\"}`);\n\t\t\t}\n\t\t\tconst actualEntries = await readdir(directory);\n\t\t\tconst unexpected = actualEntries.filter(\n\t\t\t\t(entry) => !expectedEntries.has(entry),\n\t\t\t);\n\t\t\tconst missing = [...expectedEntries].filter(\n\t\t\t\t(entry) => !actualEntries.includes(entry),\n\t\t\t);\n\t\t\tif (unexpected.length > 0 || missing.length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t[\n\t\t\t\t\t\tunexpected.length > 0\n\t\t\t\t\t\t\t? `unexpected entries: ${unexpected.join(\", \")}`\n\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\tmissing.length > 0 ? `missing entries: ${missing.join(\", \")}` : \"\",\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\"; \"),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tfor (const [relativePath, expectedHash] of marker.files) {\n\t\t\tconst path = safeLegacyPath(folder, relativePath);\n\t\t\tconst content = await readRegularFileWithoutFollowing(path);\n\t\t\tif (sha256(content) !== expectedHash) {\n\t\t\t\tthrow new Error(`edited file refused: ${relativePath}`);\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"eligible\",\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"refused\",\n\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nasync function ensureRealArchiveRoot(path: string): Promise<void> {\n\tconst existing = await optionalStat(path);\n\tif (existing) {\n\t\tif (!existing.isDirectory()) {\n\t\t\tthrow new Error(`Legacy archive path is not a real directory: ${path}`);\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tawait mkdir(path, { mode: 0o700 });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\tconst raced = await optionalStat(path);\n\t\tif (!raced?.isDirectory()) {\n\t\t\tthrow new Error(`Legacy archive path is not a real directory: ${path}`);\n\t\t}\n\t}\n}\n\nexport async function cleanupLegacySkills(options?: {\n\thome?: string;\n\tapply?: boolean;\n\t/** @internal Deterministic race-testing seam; package consumers should omit it. */\n\tafterQuarantine?: (context: {\n\t\tname: string;\n\t\tpath: string;\n\t\tarchivePath: string;\n\t}) => Promise<void>;\n}): Promise<LegacyCleanupReport> {\n\tconst home = options?.home ?? configuredHome();\n\tconst root = resolveLegacySkillsRoot(home);\n\tconst archiveRoot = resolveLegacyArchiveRoot(home);\n\tconst apply = options?.apply === true;\n\tconst results: LegacySkillCleanupResult[] = [];\n\n\tlet archiveReady = false;\n\tfor (const name of FLOW_GUIDANCE_TOPICS) {\n\t\tconst path = join(root, name);\n\t\tconst inspected = await inspectLegacyFolder(name, path);\n\t\tif (!apply || inspected.status !== \"eligible\") {\n\t\t\tresults.push(inspected);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!archiveReady) {\n\t\t\tawait ensureRealArchiveRoot(archiveRoot);\n\t\t\tarchiveReady = true;\n\t\t}\n\t\tconst archivePath = join(\n\t\t\tarchiveRoot,\n\t\t\t`${name}-${new Date().toISOString().replaceAll(\":\", \"-\")}-${crypto.randomUUID()}`,\n\t\t);\n\t\ttry {\n\t\t\tawait rename(path, archivePath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\tresults.push({\n\t\t\t\tname,\n\t\t\t\tpath,\n\t\t\t\tstatus: \"refused\",\n\t\t\t\treason: \"folder changed while cleanup was running\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tawait options?.afterQuarantine?.({ name, path, archivePath });\n\t\tconst verified = await inspectLegacyFolder(name, archivePath);\n\t\tif (verified.status !== \"eligible\") {\n\t\t\tresults.push({\n\t\t\t\tname,\n\t\t\t\tpath,\n\t\t\t\tstatus: \"quarantined\",\n\t\t\t\treason:\n\t\t\t\t\t\"folder changed while cleanup was running; preserved for manual recovery\",\n\t\t\t\tarchivePath,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tresults.push({\n\t\t\tname,\n\t\t\tpath,\n\t\t\tstatus: \"archived\",\n\t\t\tarchivePath,\n\t\t});\n\t}\n\n\treturn {\n\t\tmode: apply ? \"apply\" : \"dry-run\",\n\t\troot,\n\t\tarchiveRoot,\n\t\tresults,\n\t};\n}\n",
|
|
5
|
+
"import { createHash } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport {\n\ttype FileHandle,\n\tlstat,\n\tmkdir,\n\topen,\n\treaddir,\n\trename,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, normalize, sep } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { FLOW_GUIDANCE_TOPICS } from \"../guidance/ids.js\";\n\nconst LEGACY_MARKER = \".flow-skill-version\";\nconst NO_FOLLOW = constants.O_NOFOLLOW ?? 0;\nconst SUPPORTED_LEGACY_MAJOR = \"4\";\nconst POST_MOVE_VERIFICATION_ATTEMPTS = 4;\nconst POST_MOVE_VERIFICATION_RETRY_MS = 25;\nconst SEMVER_PATTERN =\n\t/^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\ntype ParsedMarker = {\n\tversion: string;\n\tfiles: Map<string, string>;\n};\n\nexport type LegacySkillCleanupResult = {\n\tname: string;\n\tpath: string;\n\tstatus: \"absent\" | \"eligible\" | \"archived\" | \"refused\" | \"quarantined\";\n\treason?: string;\n\tarchivePath?: string;\n};\n\nexport type LegacyCleanupReport = {\n\tmode: \"dry-run\" | \"apply\";\n\troot: string;\n\tarchiveRoot: string;\n\tresults: LegacySkillCleanupResult[];\n};\n\nfunction configuredHome(): string {\n\treturn (\n\t\tprocess.env.HOME?.trim() || process.env.USERPROFILE?.trim() || homedir()\n\t);\n}\n\nexport function resolveLegacySkillsRoot(home = configuredHome()): string {\n\treturn join(home, \".config\", \"opencode\", \"skills\");\n}\n\nexport function resolveLegacyArchiveRoot(home = configuredHome()): string {\n\treturn join(home, \".config\", \"opencode\", \"flow-legacy-skills\");\n}\n\nfunction sha256(content: string): string {\n\treturn createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction safeLegacyPath(folder: string, relativePath: string): string {\n\tif (\n\t\t!relativePath ||\n\t\tisAbsolute(relativePath) ||\n\t\trelativePath.includes(\"\\\\\") ||\n\t\trelativePath\n\t\t\t.split(\"/\")\n\t\t\t.some((part) => !part || part === \".\" || part === \"..\")\n\t) {\n\t\tthrow new Error(`unsafe marker path '${relativePath}'`);\n\t}\n\tconst resolved = normalize(join(folder, ...relativePath.split(\"/\")));\n\tif (!resolved.startsWith(`${folder}${sep}`)) {\n\t\tthrow new Error(`unsafe marker path '${relativePath}'`);\n\t}\n\treturn resolved;\n}\n\nasync function optionalStat(path: string) {\n\ttry {\n\t\treturn await lstat(path, { bigint: false });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n\t\tthrow error;\n\t}\n}\n\nasync function readRegularFileWithoutFollowing(path: string): Promise<string> {\n\tlet handle: FileHandle | undefined;\n\ttry {\n\t\tconst pathMetadata = await lstat(path);\n\t\tif (pathMetadata.isSymbolicLink()) {\n\t\t\tthrow new Error(`symbolic link refused: ${path}`);\n\t\t}\n\t\tif (!pathMetadata.isFile()) throw new Error(`not a regular file: ${path}`);\n\t\thandle = await open(path, constants.O_RDONLY | NO_FOLLOW);\n\t\tconst metadata = await handle.stat();\n\t\tif (!metadata.isFile()) throw new Error(`not a regular file: ${path}`);\n\t\tif (\n\t\t\tmetadata.dev !== pathMetadata.dev ||\n\t\t\tmetadata.ino !== pathMetadata.ino\n\t\t) {\n\t\t\tthrow new Error(`file changed while cleanup was running: ${path}`);\n\t\t}\n\t\treturn await handle.readFile({ encoding: \"utf8\" });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ELOOP\") {\n\t\t\tthrow new Error(`symbolic link refused: ${path}`);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tawait handle?.close();\n\t}\n}\n\nfunction parseMarker(content: string): ParsedMarker {\n\tlet version: string | undefined;\n\tconst files = new Map<string, string>();\n\tfor (const line of content.split(/\\r?\\n/)) {\n\t\tif (!line) continue;\n\t\tconst versionMatch = /^version=(.+)$/.exec(line);\n\t\tif (versionMatch?.[1]) {\n\t\t\tif (version) throw new Error(\"marker contains duplicate versions\");\n\t\t\tversion = versionMatch[1];\n\t\t\tcontinue;\n\t\t}\n\t\tconst fileMatch =\n\t\t\t/^file=(.+) sha256=([a-f0-9]{64})$/.exec(line) ??\n\t\t\t/^file=(.+)=sha256:([a-f0-9]{64})$/.exec(line);\n\t\tif (fileMatch?.[1] && fileMatch[2]) {\n\t\t\tif (files.has(fileMatch[1])) {\n\t\t\t\tthrow new Error(`marker contains duplicate file '${fileMatch[1]}'`);\n\t\t\t}\n\t\t\tfiles.set(fileMatch[1], fileMatch[2]);\n\t\t\tcontinue;\n\t\t}\n\t\tconst topLevelHash = /^hash=sha256:([a-f0-9]{64})$/.exec(line);\n\t\tif (topLevelHash?.[1] && !files.has(\"SKILL.md\")) {\n\t\t\tfiles.set(\"SKILL.md\", topLevelHash[1]);\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`marker contains an invalid line: '${line}'`);\n\t}\n\tif (!version) throw new Error(\"marker has no version\");\n\tif (!files.has(\"SKILL.md\")) throw new Error(\"marker does not own SKILL.md\");\n\treturn { version, files };\n}\n\nfunction assertSupportedLegacyVersion(version: string): void {\n\tconst match = SEMVER_PATTERN.exec(version);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`marker version '${version}' is not a valid semantic version`,\n\t\t);\n\t}\n\tif (match[1] !== SUPPORTED_LEGACY_MAJOR) {\n\t\tthrow new Error(\n\t\t\t`marker version '${version}' is outside the supported legacy range >=4.0.0 <5.0.0`,\n\t\t);\n\t}\n}\n\nfunction expectedDirectoryEntries(\n\tmarker: ParsedMarker,\n): Map<string, Set<string>> {\n\tconst entries = new Map<string, Set<string>>([\n\t\t[\"\", new Set([LEGACY_MARKER])],\n\t]);\n\tfor (const relativePath of marker.files.keys()) {\n\t\tconst parts = relativePath.split(\"/\");\n\t\tlet parent = \"\";\n\t\tfor (let index = 0; index < parts.length; index += 1) {\n\t\t\tconst part = parts[index];\n\t\t\tif (!part) throw new Error(`unsafe marker path '${relativePath}'`);\n\t\t\tconst children = entries.get(parent) ?? new Set<string>();\n\t\t\tchildren.add(part);\n\t\t\tentries.set(parent, children);\n\t\t\tif (index < parts.length - 1) {\n\t\t\t\tparent = parent ? `${parent}/${part}` : part;\n\t\t\t\tif (!entries.has(parent)) entries.set(parent, new Set());\n\t\t\t}\n\t\t}\n\t}\n\treturn entries;\n}\n\nasync function inspectLegacyFolder(\n\tname: string,\n\tfolder: string,\n): Promise<LegacySkillCleanupResult> {\n\tconst metadata = await optionalStat(folder);\n\tif (!metadata) return { name, path: folder, status: \"absent\" };\n\tif (!metadata.isDirectory()) {\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"refused\",\n\t\t\treason: \"path is not a real directory\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst markerPath = join(folder, LEGACY_MARKER);\n\t\tconst marker = parseMarker(\n\t\t\tawait readRegularFileWithoutFollowing(markerPath),\n\t\t);\n\t\tassertSupportedLegacyVersion(marker.version);\n\t\tconst directories = expectedDirectoryEntries(marker);\n\t\tfor (const [relativeDirectory, expectedEntries] of directories) {\n\t\t\tconst directory = relativeDirectory\n\t\t\t\t? safeLegacyPath(folder, relativeDirectory)\n\t\t\t\t: folder;\n\t\t\tconst directoryMetadata = await optionalStat(directory);\n\t\t\tif (!directoryMetadata?.isDirectory()) {\n\t\t\t\tthrow new Error(`expected real directory: ${relativeDirectory || \".\"}`);\n\t\t\t}\n\t\t\tconst actualEntries = await readdir(directory);\n\t\t\tconst unexpected = actualEntries.filter(\n\t\t\t\t(entry) => !expectedEntries.has(entry),\n\t\t\t);\n\t\t\tconst missing = [...expectedEntries].filter(\n\t\t\t\t(entry) => !actualEntries.includes(entry),\n\t\t\t);\n\t\t\tif (unexpected.length > 0 || missing.length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t[\n\t\t\t\t\t\tunexpected.length > 0\n\t\t\t\t\t\t\t? `unexpected entries: ${unexpected.join(\", \")}`\n\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\tmissing.length > 0 ? `missing entries: ${missing.join(\", \")}` : \"\",\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\"; \"),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tfor (const [relativePath, expectedHash] of marker.files) {\n\t\t\tconst path = safeLegacyPath(folder, relativePath);\n\t\t\tconst content = await readRegularFileWithoutFollowing(path);\n\t\t\tif (sha256(content) !== expectedHash) {\n\t\t\t\tthrow new Error(`edited file refused: ${relativePath}`);\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"eligible\",\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: folder,\n\t\t\tstatus: \"refused\",\n\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nasync function ensureRealArchiveRoot(path: string): Promise<void> {\n\tconst existing = await optionalStat(path);\n\tif (existing) {\n\t\tif (!existing.isDirectory()) {\n\t\t\tthrow new Error(`Legacy archive path is not a real directory: ${path}`);\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tawait mkdir(path, { mode: 0o700 });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\tconst raced = await optionalStat(path);\n\t\tif (!raced?.isDirectory()) {\n\t\t\tthrow new Error(`Legacy archive path is not a real directory: ${path}`);\n\t\t}\n\t}\n}\n\nasync function verifyMovedLegacyFolder(\n\tname: string,\n\tarchivePath: string,\n): Promise<LegacySkillCleanupResult> {\n\tlet verified = await inspectLegacyFolder(name, archivePath);\n\tfor (\n\t\tlet attempt = 1;\n\t\tattempt < POST_MOVE_VERIFICATION_ATTEMPTS && verified.status !== \"eligible\";\n\t\tattempt += 1\n\t) {\n\t\t// Windows may briefly report a just-renamed directory as missing or\n\t\t// inaccessible while a competing inspection releases its file handles.\n\t\t// Retrying never weakens cleanup safety: only a complete byte-pristine\n\t\t// inspection can return eligible, while persistent edits still quarantine.\n\t\tawait sleep(POST_MOVE_VERIFICATION_RETRY_MS * attempt);\n\t\tverified = await inspectLegacyFolder(name, archivePath);\n\t}\n\treturn verified;\n}\n\nexport async function cleanupLegacySkills(options?: {\n\thome?: string;\n\tapply?: boolean;\n\t/** @internal Deterministic race-testing seam; package consumers should omit it. */\n\tafterQuarantine?: (context: {\n\t\tname: string;\n\t\tpath: string;\n\t\tarchivePath: string;\n\t}) => Promise<void>;\n}): Promise<LegacyCleanupReport> {\n\tconst home = options?.home ?? configuredHome();\n\tconst root = resolveLegacySkillsRoot(home);\n\tconst archiveRoot = resolveLegacyArchiveRoot(home);\n\tconst apply = options?.apply === true;\n\tconst results: LegacySkillCleanupResult[] = [];\n\n\tlet archiveReady = false;\n\tfor (const name of FLOW_GUIDANCE_TOPICS) {\n\t\tconst path = join(root, name);\n\t\tconst inspected = await inspectLegacyFolder(name, path);\n\t\tif (!apply || inspected.status !== \"eligible\") {\n\t\t\tresults.push(inspected);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!archiveReady) {\n\t\t\tawait ensureRealArchiveRoot(archiveRoot);\n\t\t\tarchiveReady = true;\n\t\t}\n\t\tconst archivePath = join(\n\t\t\tarchiveRoot,\n\t\t\t`${name}-${new Date().toISOString().replaceAll(\":\", \"-\")}-${crypto.randomUUID()}`,\n\t\t);\n\t\ttry {\n\t\t\tawait rename(path, archivePath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\tresults.push({\n\t\t\t\tname,\n\t\t\t\tpath,\n\t\t\t\tstatus: \"refused\",\n\t\t\t\treason: \"folder changed while cleanup was running\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tawait options?.afterQuarantine?.({ name, path, archivePath });\n\t\tconst verified = await verifyMovedLegacyFolder(name, archivePath);\n\t\tif (verified.status !== \"eligible\") {\n\t\t\tresults.push({\n\t\t\t\tname,\n\t\t\t\tpath,\n\t\t\t\tstatus: \"quarantined\",\n\t\t\t\treason:\n\t\t\t\t\t\"folder changed while cleanup was running; preserved for manual recovery\",\n\t\t\t\tarchivePath,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tresults.push({\n\t\t\tname,\n\t\t\tpath,\n\t\t\tstatus: \"archived\",\n\t\t\tarchivePath,\n\t\t});\n\t}\n\n\treturn {\n\t\tmode: apply ? \"apply\" : \"dry-run\",\n\t\troot,\n\t\tarchiveRoot,\n\t\tresults,\n\t};\n}\n",
|
|
6
6
|
"export const FLOW_GUIDANCE_TOPICS = [\n\t\"flow\",\n\t\"flow-plan\",\n\t\"flow-run\",\n\t\"flow-test\",\n\t\"flow-review\",\n\t\"flow-deslop\",\n\t\"flow-ui-quality\",\n\t\"flow-commit\",\n] as const;\n\nexport type FlowGuidanceTopic = (typeof FLOW_GUIDANCE_TOPICS)[number];\n\nexport const FLOW_GUIDANCE_IDS = [\n\t\"flow\",\n\t\"flow/references/recovery-playbook.md\",\n\t\"flow/references/parallel-orchestration.md\",\n\t\"flow/references/parallel-decision.md\",\n\t\"flow/references/parallel-manifest.md\",\n\t\"flow/references/parallel-execution.md\",\n\t\"flow/references/parallel-synthesis.md\",\n\t\"flow/references/parallel-pass-example.md\",\n\t\"flow/references/handoff-format.md\",\n\t\"flow-plan\",\n\t\"flow-plan/references/planning-examples.md\",\n\t\"flow-plan/references/plan-quality-checklist.md\",\n\t\"flow-plan/references/parallel-discovery.md\",\n\t\"flow-run\",\n\t\"flow-run/references/validation-rubric.md\",\n\t\"flow-run/references/audit-rubric.md\",\n\t\"flow-test\",\n\t\"flow-review\",\n\t\"flow-review/references/hidden-reviewer-contract.md\",\n\t\"flow-review/references/review-rubric.md\",\n\t\"flow-deslop\",\n\t\"flow-deslop/references/smell-rubric.md\",\n\t\"flow-deslop/references/refactor-workflow.md\",\n\t\"flow-ui-quality\",\n\t\"flow-ui-quality/references/ui-rubric.md\",\n\t\"flow-ui-quality/references/visual-verification.md\",\n\t\"flow-commit\",\n] as const;\n\nexport type FlowGuidanceId = (typeof FLOW_GUIDANCE_IDS)[number];\n",
|
|
7
7
|
"import { createRequire } from \"node:module\";\n\nexport function resolveFlowPluginVersion(): string {\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\tconst manifest = require(\"../package.json\") as { version?: string };\n\t\tif (manifest.version) return manifest.version;\n\t} catch {\n\t\t// Fall through to the development sentinel.\n\t}\n\treturn \"0.0.0\";\n}\n",
|
|
8
8
|
"import {\n\tcleanupLegacySkills,\n\ttype LegacyCleanupReport,\n} from \"./distribution/legacy-cleanup.js\";\nimport { resolveFlowPluginVersion } from \"./version.js\";\n\nfunction usage(): string {\n\treturn [\n\t\t\"usage: opencode-plugin-flow legacy-cleanup <--dry-run|--apply> [--json]\",\n\t\t\"\",\n\t\t\"commands:\",\n\t\t\" legacy-cleanup Inspect or archive marker-proven legacy global Flow skills\",\n\t\t\"\",\n\t\t\"options:\",\n\t\t\" --dry-run Report eligible folders without changing the filesystem\",\n\t\t\" --apply Move eligible folders to a recoverable archive outside skill discovery\",\n\t\t\" Cleanup never deletes legacy folders\",\n\t\t\" --json Write the report as JSON\",\n\t\t\" --help Show this help\",\n\t\t\" --version Print the plugin version\",\n\t].join(\"\\n\");\n}\n\nfunction writeReport(report: LegacyCleanupReport, json: boolean): void {\n\tif (json) {\n\t\tprocess.stdout.write(`${JSON.stringify(report, null, 2)}\\n`);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Flow legacy skill cleanup (${report.mode})\\n`);\n\tprocess.stdout.write(`- legacy root: ${report.root}\\n`);\n\tprocess.stdout.write(`- archive root: ${report.archiveRoot}\\n`);\n\tfor (const result of report.results) {\n\t\tprocess.stdout.write(`- ${result.name}: ${result.status}\\n`);\n\t\tif (result.reason) process.stdout.write(` reason: ${result.reason}\\n`);\n\t\tif (result.archivePath) {\n\t\t\tconst label = result.status === \"archived\" ? \"archived\" : \"preserved\";\n\t\t\tprocess.stdout.write(` ${label} at: ${result.archivePath}\\n`);\n\t\t}\n\t}\n}\n\nasync function main(argv: string[]): Promise<void> {\n\tconst [command, ...flags] = argv.slice(2);\n\tif (command === \"--help\" || command === \"-h\") {\n\t\tprocess.stdout.write(`${usage()}\\n`);\n\t\treturn;\n\t}\n\tif (command === \"--version\" || command === \"-v\") {\n\t\tprocess.stdout.write(`${resolveFlowPluginVersion()}\\n`);\n\t\treturn;\n\t}\n\tconst knownFlags = new Set([\"--dry-run\", \"--apply\", \"--json\"]);\n\tconst validFlags = flags.every((flag) => knownFlags.has(flag));\n\tconst dryRun = flags.includes(\"--dry-run\");\n\tconst apply = flags.includes(\"--apply\");\n\tif (command !== \"legacy-cleanup\" || !validFlags || dryRun === apply) {\n\t\tprocess.stderr.write(`${usage()}\\n`);\n\t\tprocess.exitCode = 2;\n\t\treturn;\n\t}\n\tconst report = await cleanupLegacySkills({ apply });\n\twriteReport(report, flags.includes(\"--json\"));\n\tif (\n\t\tapply &&\n\t\treport.results.some((result) =>\n\t\t\t[\"refused\", \"quarantined\"].includes(result.status),\n\t\t)\n\t) {\n\t\tprocess.exitCode = 1;\n\t}\n}\n\nmain(process.argv).catch((error) => {\n\tprocess.stderr.write(\n\t\t`${error instanceof Error ? error.message : String(error)}\\n`,\n\t);\n\tprocess.exitCode = 1;\n});\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": ";;;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;AACA;;;
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "4B60DFA5FC84470464756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
package/dist/domain/session.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
import type { ValidationCommandClass } from "./validation-command.js";
|
|
1
2
|
export type FeatureStatus = "pending" | "in_progress" | "completed" | "blocked";
|
|
2
3
|
export type SessionStatus = "planning" | "ready" | "running" | "blocked" | "completed";
|
|
3
4
|
export type FeatureReviewDepth = "quick" | "standard" | "detailed";
|
|
4
5
|
export type FinalReviewPolicy = "broad" | "detailed";
|
|
5
6
|
export type ValidationScope = "targeted" | "broad";
|
|
7
|
+
export type SnapshotId = string;
|
|
8
|
+
export type EvidenceId = string;
|
|
9
|
+
export type EvidenceArtifactRef = {
|
|
10
|
+
kind: "restricted_evidence_v1";
|
|
11
|
+
digest: `sha256:${string}`;
|
|
12
|
+
byteLength: number;
|
|
13
|
+
};
|
|
14
|
+
export type CausalGuard = {
|
|
15
|
+
operationId: string;
|
|
16
|
+
expectedRevision: number;
|
|
17
|
+
expectedSnapshotId: SnapshotId;
|
|
18
|
+
};
|
|
6
19
|
declare const featureIdBrand: unique symbol;
|
|
7
20
|
declare const sessionIdBrand: unique symbol;
|
|
8
21
|
export type FeatureId = string & {
|
|
@@ -48,6 +61,49 @@ export type ReviewFinding = {
|
|
|
48
61
|
summary: string;
|
|
49
62
|
severity: "blocking" | "advisory";
|
|
50
63
|
};
|
|
64
|
+
export type ReviewFindingTaxonomy = "implementation_defect" | "regression_coverage_gap" | "evidence_gap" | "advisory";
|
|
65
|
+
export type ReviewExecutionFindingInput = {
|
|
66
|
+
taxonomy: ReviewFindingTaxonomy;
|
|
67
|
+
subject: string;
|
|
68
|
+
requirementOrRisk: string;
|
|
69
|
+
evidenceLocator: string;
|
|
70
|
+
summary: string;
|
|
71
|
+
severity: "blocking" | "advisory";
|
|
72
|
+
};
|
|
73
|
+
export type ReviewExecutionFinding = ReviewExecutionFindingInput & {
|
|
74
|
+
fingerprint: string;
|
|
75
|
+
};
|
|
76
|
+
export type ReviewExecutionInput = {
|
|
77
|
+
attemptId: string;
|
|
78
|
+
logicalPassId: string;
|
|
79
|
+
featureId: FeatureId;
|
|
80
|
+
reviewKind: "feature" | "final";
|
|
81
|
+
reviewSnapshotId: string;
|
|
82
|
+
verdict: "passed" | "failed";
|
|
83
|
+
findings: ReviewExecutionFindingInput[];
|
|
84
|
+
startedAt: string;
|
|
85
|
+
completedAt: string;
|
|
86
|
+
terminalDisposition: "submitted" | "observed_unsubmitted";
|
|
87
|
+
};
|
|
88
|
+
export type ReviewExecution = Omit<ReviewExecutionInput, "findings"> & {
|
|
89
|
+
findings: ReviewExecutionFinding[];
|
|
90
|
+
};
|
|
91
|
+
export type ReviewLifecycleTelemetry = {
|
|
92
|
+
featureAttemptCount: number;
|
|
93
|
+
finalAttemptCount: number;
|
|
94
|
+
passedVerdictCount: number;
|
|
95
|
+
failedVerdictCount: number;
|
|
96
|
+
retryConsumedCount: number;
|
|
97
|
+
};
|
|
98
|
+
export type ObservedReviewWorkerLedger = {
|
|
99
|
+
source: "unavailable";
|
|
100
|
+
reconciliationStatus: "unreconciled";
|
|
101
|
+
observedExecutionCount: null;
|
|
102
|
+
} | {
|
|
103
|
+
source: "host_observed";
|
|
104
|
+
reconciliationStatus: "reconciled";
|
|
105
|
+
observedExecutionCount: number;
|
|
106
|
+
};
|
|
51
107
|
export type Review = {
|
|
52
108
|
status: "passed" | "failed";
|
|
53
109
|
summary: string;
|
|
@@ -61,6 +117,89 @@ export type ValidationRun = {
|
|
|
61
117
|
status: "passed" | "failed";
|
|
62
118
|
summary: string;
|
|
63
119
|
};
|
|
120
|
+
export type ValidationEvidence = {
|
|
121
|
+
kind: "validation";
|
|
122
|
+
evidenceId: EvidenceId;
|
|
123
|
+
snapshotId: SnapshotId;
|
|
124
|
+
sourceDigest: string;
|
|
125
|
+
commandClass: ValidationCommandClass;
|
|
126
|
+
startedAt: string;
|
|
127
|
+
completedAt: string;
|
|
128
|
+
exitCode: number;
|
|
129
|
+
outputDigest: string;
|
|
130
|
+
artifactRef?: EvidenceArtifactRef | undefined;
|
|
131
|
+
environmentKeys: string[];
|
|
132
|
+
};
|
|
133
|
+
export type ReviewEvidence = {
|
|
134
|
+
kind: "review";
|
|
135
|
+
evidenceId: EvidenceId;
|
|
136
|
+
snapshotId: SnapshotId;
|
|
137
|
+
sourceDigest: string;
|
|
138
|
+
attemptId: string;
|
|
139
|
+
packetDigest: string;
|
|
140
|
+
startedAt: string;
|
|
141
|
+
completedAt: string;
|
|
142
|
+
};
|
|
143
|
+
export type EvidenceRecord = ValidationEvidence | ReviewEvidence;
|
|
144
|
+
export type CausalMutationRecord = {
|
|
145
|
+
operationId: string;
|
|
146
|
+
operationKind: "plan_save" | "plan_approve" | "run_start" | "review_record" | "evidence_record" | "feature_complete" | "feature_reset" | "session_close";
|
|
147
|
+
requestDigest: string;
|
|
148
|
+
priorMutationDigest: string | null;
|
|
149
|
+
mutationDigest: string;
|
|
150
|
+
priorRevision: number;
|
|
151
|
+
revision: number;
|
|
152
|
+
priorSnapshotId: SnapshotId;
|
|
153
|
+
currentSnapshotId: SnapshotId;
|
|
154
|
+
changedEntity: {
|
|
155
|
+
kind: "session" | "plan" | "feature" | "review" | "evidence" | "closure";
|
|
156
|
+
id: string;
|
|
157
|
+
};
|
|
158
|
+
changedFields: string[];
|
|
159
|
+
blockerDelta: {
|
|
160
|
+
added: string[];
|
|
161
|
+
removed: string[];
|
|
162
|
+
};
|
|
163
|
+
evidenceRefs: EvidenceId[];
|
|
164
|
+
recordedAt: string;
|
|
165
|
+
};
|
|
166
|
+
export type CausalState = {
|
|
167
|
+
revision: number;
|
|
168
|
+
genesisSnapshotId: SnapshotId;
|
|
169
|
+
snapshotId: SnapshotId;
|
|
170
|
+
mutations: CausalMutationRecord[];
|
|
171
|
+
evidence: EvidenceRecord[];
|
|
172
|
+
};
|
|
173
|
+
type ReviewerProjectionRequestBase = {
|
|
174
|
+
featureId: FeatureId;
|
|
175
|
+
packetHash: string;
|
|
176
|
+
evidenceRefs: string[];
|
|
177
|
+
expectedRevision?: number | undefined;
|
|
178
|
+
expectedSnapshotId?: SnapshotId | undefined;
|
|
179
|
+
};
|
|
180
|
+
export type ReviewerProjectionRequest = (ReviewerProjectionRequestBase & {
|
|
181
|
+
reviewKind: "feature";
|
|
182
|
+
}) | (ReviewerProjectionRequestBase & {
|
|
183
|
+
reviewKind: "final";
|
|
184
|
+
});
|
|
185
|
+
type ReviewerProjectionBase = {
|
|
186
|
+
view: "reviewer";
|
|
187
|
+
featureId: FeatureId;
|
|
188
|
+
assignedScope: string[];
|
|
189
|
+
packetHash: string;
|
|
190
|
+
evidenceRefs: string[];
|
|
191
|
+
expectedRevision: number;
|
|
192
|
+
expectedSnapshotId: SnapshotId;
|
|
193
|
+
};
|
|
194
|
+
export type ReviewerProjection = (ReviewerProjectionBase & {
|
|
195
|
+
reviewKind: "feature";
|
|
196
|
+
requiredDepth: FeatureReviewDepth;
|
|
197
|
+
}) | (ReviewerProjectionBase & {
|
|
198
|
+
reviewKind: "final";
|
|
199
|
+
requiredDepth: FinalReviewPolicy;
|
|
200
|
+
requirements: string[];
|
|
201
|
+
decisions: string[];
|
|
202
|
+
});
|
|
64
203
|
export type Artifact = {
|
|
65
204
|
path: string;
|
|
66
205
|
};
|
|
@@ -82,6 +221,30 @@ export type Plan = {
|
|
|
82
221
|
finalReviewPolicy: FinalReviewPolicy;
|
|
83
222
|
features: Feature[];
|
|
84
223
|
};
|
|
224
|
+
export type ExecutionProjection = {
|
|
225
|
+
view: "execution";
|
|
226
|
+
goal: string;
|
|
227
|
+
plan: {
|
|
228
|
+
summary: string;
|
|
229
|
+
overview: string;
|
|
230
|
+
requirements: string[];
|
|
231
|
+
decisions: string[];
|
|
232
|
+
finalReviewPolicy: FinalReviewPolicy;
|
|
233
|
+
};
|
|
234
|
+
feature: {
|
|
235
|
+
id: FeatureId;
|
|
236
|
+
title: string;
|
|
237
|
+
summary: string;
|
|
238
|
+
targets: string[];
|
|
239
|
+
validation: string[];
|
|
240
|
+
dependsOn: FeatureId[];
|
|
241
|
+
reviewDepth: FeatureReviewDepth;
|
|
242
|
+
};
|
|
243
|
+
isFinalFeature: boolean;
|
|
244
|
+
requiredValidationScope: ValidationScope;
|
|
245
|
+
expectedRevision: number;
|
|
246
|
+
expectedSnapshotId: SnapshotId;
|
|
247
|
+
};
|
|
85
248
|
export type PlanInput = {
|
|
86
249
|
summary: string;
|
|
87
250
|
overview: string;
|
|
@@ -111,6 +274,11 @@ export type NeedsInputOutcome = {
|
|
|
111
274
|
};
|
|
112
275
|
export type WorkerOutcome = CompletedWorkerOutcome | NeedsInputOutcome;
|
|
113
276
|
type WorkerResultBase = {
|
|
277
|
+
operationId?: string | undefined;
|
|
278
|
+
expectedRevision?: number | undefined;
|
|
279
|
+
expectedSnapshotId?: SnapshotId | undefined;
|
|
280
|
+
/** Trusted application-boundary digest of the normalized public request. */
|
|
281
|
+
requestDigest?: string | undefined;
|
|
114
282
|
featureId: FeatureId;
|
|
115
283
|
summary: string;
|
|
116
284
|
artifactsChanged: Artifact[];
|
|
@@ -119,6 +287,8 @@ type WorkerResultBase = {
|
|
|
119
287
|
featureReviewDepth?: FeatureReviewDepth | undefined;
|
|
120
288
|
featureReview?: Review | undefined;
|
|
121
289
|
finalReview?: FinalReview | undefined;
|
|
290
|
+
reviewExecutions?: ReviewExecutionInput[] | undefined;
|
|
291
|
+
evidence?: EvidenceRecord[] | undefined;
|
|
122
292
|
orchestrationPasses: OrchestrationPassRecord[];
|
|
123
293
|
};
|
|
124
294
|
export type WorkerResult = (WorkerResultBase & {
|
|
@@ -149,6 +319,9 @@ export type BudgetTelemetry = {
|
|
|
149
319
|
reviewCount: number;
|
|
150
320
|
failedReviewCount: number;
|
|
151
321
|
failedReviewAttemptsByFeature: Record<string, number>;
|
|
322
|
+
reviewExecutions: ReviewExecution[];
|
|
323
|
+
reviewLifecycle: ReviewLifecycleTelemetry;
|
|
324
|
+
observedReviewWorkers: ObservedReviewWorkerLedger;
|
|
152
325
|
orchestration: OrchestrationTelemetry;
|
|
153
326
|
};
|
|
154
327
|
export type Session = {
|
|
@@ -161,6 +334,7 @@ export type Session = {
|
|
|
161
334
|
activeFeatureId: FeatureId | null;
|
|
162
335
|
history: ExecutionHistoryEntry[];
|
|
163
336
|
budget: BudgetTelemetry;
|
|
337
|
+
causal: CausalState;
|
|
164
338
|
closure: {
|
|
165
339
|
kind: "completed" | "deferred" | "abandoned";
|
|
166
340
|
summary: string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CausalGuard, CausalMutationRecord, EvidenceRecord, ExecutionProjection, Feature, FeatureId, FeatureReviewDepth, PlanInput, ReviewExecution, ReviewExecutionFindingInput, ReviewExecutionInput, ReviewerProjection, ReviewerProjectionRequest, Session, SessionId, SnapshotId, WorkerResult } from "./session.js";
|
|
2
|
+
export declare const MAX_EXECUTION_PROJECTION_BYTES: number;
|
|
2
3
|
export type TransitionEnvironment = {
|
|
3
4
|
now(): string;
|
|
4
5
|
newSessionId(): SessionId;
|
|
6
|
+
newOperationId?(revision: number): string;
|
|
5
7
|
};
|
|
6
8
|
export type TransitionResult<T> = {
|
|
7
9
|
ok: true;
|
|
@@ -12,6 +14,25 @@ export type TransitionResult<T> = {
|
|
|
12
14
|
recovery?: string;
|
|
13
15
|
session?: Session;
|
|
14
16
|
};
|
|
17
|
+
export declare function canonicalOperationRequestDigest(kind: CausalMutationRecord["operationKind"], input: unknown): string;
|
|
18
|
+
export declare function canonicalSessionSnapshotId(session: Session): SnapshotId;
|
|
19
|
+
export declare function canonicalEvidenceId(evidence: EvidenceRecord): string;
|
|
20
|
+
export declare function validateCausalChain(session: Session): string | null;
|
|
21
|
+
export declare function stableReviewFindingFingerprint(finding: ReviewExecutionFindingInput): string;
|
|
22
|
+
export type LogicalReviewPassProjection = {
|
|
23
|
+
logicalPassId: string;
|
|
24
|
+
featureId: FeatureId;
|
|
25
|
+
reviewKind: ReviewExecution["reviewKind"];
|
|
26
|
+
reviewSnapshotId: string;
|
|
27
|
+
latestAttemptId: string;
|
|
28
|
+
verdict: ReviewExecution["verdict"];
|
|
29
|
+
attemptCount: number;
|
|
30
|
+
};
|
|
31
|
+
export declare function projectLogicalReviewPasses(executions: readonly ReviewExecution[]): LogicalReviewPassProjection[];
|
|
32
|
+
export declare function recordReviewExecutions(session: Session, executions: readonly ReviewExecutionInput[], environment: TransitionEnvironment, operationId?: string, guard?: Pick<CausalGuard, "expectedRevision" | "expectedSnapshotId">): TransitionResult<Session>;
|
|
33
|
+
export declare function mergeReviewExecutionsForCompletion(session: Session, executions: readonly ReviewExecutionInput[]): TransitionResult<Session>;
|
|
34
|
+
export declare function mergeEvidenceForCompletion(session: Session, evidenceRecords: readonly EvidenceRecord[]): TransitionResult<Session>;
|
|
35
|
+
export declare function recordEvidence(session: Session, evidenceRecords: readonly EvidenceRecord[], environment: TransitionEnvironment, operationId?: string): TransitionResult<Session>;
|
|
15
36
|
export declare function createSession(goal: string, environment: TransitionEnvironment): Session;
|
|
16
37
|
export declare function applyPlan(session: Session, planInput: PlanInput, environment: TransitionEnvironment): TransitionResult<Session>;
|
|
17
38
|
export declare function approvePlan(session: Session, environment: TransitionEnvironment): TransitionResult<Session>;
|
|
@@ -20,61 +41,123 @@ export declare function startRun(session: Session, environment: TransitionEnviro
|
|
|
20
41
|
feature: Feature;
|
|
21
42
|
}>;
|
|
22
43
|
export declare function completeFeature(session: Session, worker: WorkerResult, environment: TransitionEnvironment): TransitionResult<Session>;
|
|
23
|
-
export declare function resetFeature(session: Session, featureId: FeatureId, environment: TransitionEnvironment): TransitionResult<Session>;
|
|
24
|
-
export declare function closeSession(session: Session, kind: "completed" | "deferred" | "abandoned", environment: TransitionEnvironment, summary?: string): TransitionResult<Session>;
|
|
25
|
-
export declare function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
export declare function resetFeature(session: Session, featureId: FeatureId, environment: TransitionEnvironment, guard?: CausalGuard): TransitionResult<Session>;
|
|
45
|
+
export declare function closeSession(session: Session, kind: "completed" | "deferred" | "abandoned", environment: TransitionEnvironment, summary?: string, guard?: CausalGuard): TransitionResult<Session>;
|
|
46
|
+
export declare function serializedUtf8JsonBytes(value: unknown): number;
|
|
47
|
+
export declare function compactSessionProjection(session: Session): {
|
|
48
|
+
view: "compact";
|
|
49
|
+
sessionId: SessionId;
|
|
50
|
+
status: import("./session.js").SessionStatus;
|
|
51
|
+
approval: "approved" | "pending";
|
|
52
|
+
revision: number;
|
|
53
|
+
snapshotId: string;
|
|
54
|
+
feature: {
|
|
55
|
+
id: FeatureId;
|
|
56
|
+
status: import("./session.js").FeatureStatus;
|
|
57
|
+
} | null;
|
|
58
|
+
progress: {
|
|
59
|
+
completed: number;
|
|
60
|
+
total: number;
|
|
61
|
+
remaining: number;
|
|
62
|
+
};
|
|
63
|
+
blockers: {
|
|
64
|
+
featureIds: string[];
|
|
65
|
+
summary: string | null;
|
|
66
|
+
};
|
|
67
|
+
closure: {
|
|
68
|
+
kind: "abandoned" | "completed" | "deferred";
|
|
69
|
+
} | null;
|
|
36
70
|
nextAction: string;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
failedReviewCount: number;
|
|
57
|
-
failedReviewAttemptsByFeature: Record<string, number>;
|
|
58
|
-
orchestration: import("./session.js").OrchestrationTelemetry;
|
|
59
|
-
};
|
|
60
|
-
closure: {
|
|
61
|
-
kind: "completed" | "deferred" | "abandoned";
|
|
62
|
-
summary: string;
|
|
63
|
-
recordedAt: string;
|
|
64
|
-
} | null;
|
|
65
|
-
lastError: {
|
|
66
|
-
tool: string;
|
|
67
|
-
summary: string;
|
|
68
|
-
recovery?: string | undefined;
|
|
69
|
-
recordedAt: string;
|
|
70
|
-
} | null;
|
|
71
|
-
latestHistoryEntry: ExecutionHistoryEntry | null;
|
|
72
|
-
historyCount: number;
|
|
73
|
-
timestamps: {
|
|
74
|
-
createdAt: string;
|
|
75
|
-
updatedAt: string;
|
|
76
|
-
completedAt: string | null;
|
|
77
|
-
};
|
|
71
|
+
};
|
|
72
|
+
export declare function executionSessionProjection(session: Session): TransitionResult<ExecutionProjection>;
|
|
73
|
+
export declare function detailSessionProjection(session: Session): {
|
|
74
|
+
view: "detail";
|
|
75
|
+
compact: {
|
|
76
|
+
view: "compact";
|
|
77
|
+
sessionId: SessionId;
|
|
78
|
+
status: import("./session.js").SessionStatus;
|
|
79
|
+
approval: "approved" | "pending";
|
|
80
|
+
revision: number;
|
|
81
|
+
snapshotId: string;
|
|
82
|
+
feature: {
|
|
83
|
+
id: FeatureId;
|
|
84
|
+
status: import("./session.js").FeatureStatus;
|
|
85
|
+
} | null;
|
|
86
|
+
progress: {
|
|
87
|
+
completed: number;
|
|
88
|
+
total: number;
|
|
89
|
+
remaining: number;
|
|
78
90
|
};
|
|
91
|
+
blockers: {
|
|
92
|
+
featureIds: string[];
|
|
93
|
+
summary: string | null;
|
|
94
|
+
};
|
|
95
|
+
closure: {
|
|
96
|
+
kind: "abandoned" | "completed" | "deferred";
|
|
97
|
+
} | null;
|
|
98
|
+
nextAction: string;
|
|
99
|
+
};
|
|
100
|
+
plan: {
|
|
101
|
+
summary: string;
|
|
102
|
+
overview: string;
|
|
103
|
+
requirements: string[];
|
|
104
|
+
decisions: string[];
|
|
105
|
+
finalReviewPolicy: import("./session.js").FinalReviewPolicy;
|
|
106
|
+
features: {
|
|
107
|
+
id: FeatureId;
|
|
108
|
+
title: string;
|
|
109
|
+
summary: string;
|
|
110
|
+
status: import("./session.js").FeatureStatus;
|
|
111
|
+
reviewDepth: FeatureReviewDepth;
|
|
112
|
+
targets: string[];
|
|
113
|
+
dependsOn: FeatureId[];
|
|
114
|
+
}[];
|
|
115
|
+
} | null;
|
|
116
|
+
history: {
|
|
117
|
+
featureId: FeatureId;
|
|
118
|
+
status: "blocked" | "completed" | "needs_input";
|
|
119
|
+
summary: string;
|
|
120
|
+
recordedAt: string;
|
|
121
|
+
validation: {
|
|
122
|
+
status: "failed" | "passed";
|
|
123
|
+
summary: string;
|
|
124
|
+
}[];
|
|
125
|
+
}[];
|
|
126
|
+
causal: {
|
|
127
|
+
revision: number;
|
|
128
|
+
snapshotId: string;
|
|
129
|
+
mutations: CausalMutationRecord[];
|
|
130
|
+
evidence: EvidenceRecord[];
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
export declare function reviewerSessionProjection(session: Session, request: ReviewerProjectionRequest): TransitionResult<ReviewerProjection>;
|
|
134
|
+
export declare function mutationReceiptProjection(session: Session, warnings?: readonly string[], operationId?: string): {
|
|
135
|
+
view: "mutation_receipt";
|
|
136
|
+
status: import("./session.js").SessionStatus;
|
|
137
|
+
operationId: string | null;
|
|
138
|
+
revision: number;
|
|
139
|
+
snapshotId: string;
|
|
140
|
+
changedEntity: {
|
|
141
|
+
kind: "session" | "plan" | "feature" | "review" | "evidence" | "closure";
|
|
142
|
+
id: string;
|
|
143
|
+
} | null;
|
|
144
|
+
changedFields: string[];
|
|
145
|
+
blockerDelta: {
|
|
146
|
+
added: string[];
|
|
147
|
+
removed: string[];
|
|
79
148
|
};
|
|
149
|
+
evidenceRefs: string[];
|
|
150
|
+
warnings: string[];
|
|
151
|
+
nextAction: string;
|
|
80
152
|
};
|
|
153
|
+
export declare function causalDeltaProjection(session: Session, sinceRevision: number): TransitionResult<{
|
|
154
|
+
view: "delta";
|
|
155
|
+
changed: boolean;
|
|
156
|
+
fromRevision: number;
|
|
157
|
+
throughRevision: number;
|
|
158
|
+
currentRevision: number;
|
|
159
|
+
snapshotId: SnapshotId;
|
|
160
|
+
mutations: CausalMutationRecord[];
|
|
161
|
+
hasMore: boolean;
|
|
162
|
+
nextSinceRevision: number | null;
|
|
163
|
+
}>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ValidationCommandClass = "test" | "typecheck" | "lint" | "build" | "format" | "smoke" | "other";
|
|
2
|
+
/**
|
|
3
|
+
* Classify a validation command for evidence matching.
|
|
4
|
+
*
|
|
5
|
+
* Precedence is intentional and preserves the classifier contract previously
|
|
6
|
+
* exposed at the application boundary.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validationCommandClass(command: string): ValidationCommandClass;
|