@dailephd/my-dev-kit-lab 0.4.2 → 0.4.3

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.
Files changed (92) hide show
  1. package/README.md +23 -113
  2. package/benchmarks/contracts/todo-behavior.md +1 -1
  3. package/benchmarks/projects/task-analytics-large-mixed/README.md +2 -0
  4. package/benchmarks/projects/task-workflow-medium-ts/README.md +3 -1
  5. package/dist/src/evaluation/stageContextDeterminism/calculateStageContextDeterminism.js +52 -0
  6. package/dist/src/evaluation/stageContextDeterminism/canonicalizeStageContextRun.js +53 -0
  7. package/dist/src/evaluation/stageContextDeterminism/index.js +2 -0
  8. package/dist/src/evaluation/stageContextDeterminism/types.js +1 -0
  9. package/dist/src/evaluation/stageContextExpectations/index.js +2 -0
  10. package/dist/src/evaluation/stageContextExpectations/readStageContextExpectationFixtureV1.js +50 -0
  11. package/dist/src/evaluation/stageContextExpectations/readTypes.js +1 -0
  12. package/dist/src/evaluation/stageContextExpectations/types.js +1 -0
  13. package/dist/src/evaluation/stageContextExpectations/validation.js +568 -0
  14. package/dist/src/evaluation/stageContextMetrics/calculateArtifactStateMetrics.js +130 -0
  15. package/dist/src/evaluation/stageContextMetrics/calculateContextSizeMetrics.js +85 -0
  16. package/dist/src/evaluation/stageContextMetrics/calculateExpectationMetrics.js +103 -0
  17. package/dist/src/evaluation/stageContextMetrics/calculateResponsibilityMappingMetrics.js +36 -0
  18. package/dist/src/evaluation/stageContextMetrics/collectContextCapsuleEvidence.js +312 -0
  19. package/dist/src/evaluation/stageContextMetrics/collectRetrievalAuditEvidence.js +35 -0
  20. package/dist/src/evaluation/stageContextMetrics/collectWorkflowInstructionEvidence.js +137 -0
  21. package/dist/src/evaluation/stageContextMetrics/evaluateV043StageContextExecution.js +247 -0
  22. package/dist/src/evaluation/stageContextMetrics/index.js +10 -0
  23. package/dist/src/evaluation/stageContextMetrics/matchStageContextExpectations.js +38 -0
  24. package/dist/src/evaluation/stageContextMetrics/targetKeys.js +35 -0
  25. package/dist/src/evaluation/stageContextMetrics/types.js +1 -0
  26. package/dist/src/evaluation/stageContextSelectors/contextArtifactConsistency.js +79 -0
  27. package/dist/src/evaluation/stageContextSelectors/contextCapsuleSelectors.js +126 -0
  28. package/dist/src/evaluation/stageContextSelectors/index.js +4 -0
  29. package/dist/src/evaluation/stageContextSelectors/retrievalAuditRecordSelectors.js +54 -0
  30. package/dist/src/evaluation/stageContextSelectors/workflowInstructionPacketSelectors.js +51 -0
  31. package/dist/src/evaluation/targetImmutability/captureTargetSnapshot.js +208 -0
  32. package/dist/src/evaluation/targetImmutability/compareTargetSnapshots.js +56 -0
  33. package/dist/src/evaluation/targetImmutability/index.js +3 -0
  34. package/dist/src/evaluation/targetImmutability/types.js +1 -0
  35. package/dist/src/evaluation/targetImmutability/validateTargetImmutabilityConfig.js +130 -0
  36. package/dist/src/evaluation/upstreamArtifacts/artifactReadTypes.js +1 -0
  37. package/dist/src/evaluation/upstreamArtifacts/index.js +3 -0
  38. package/dist/src/evaluation/upstreamArtifacts/jsonTypes.js +1 -0
  39. package/dist/src/evaluation/upstreamArtifacts/myDevKitClassificationTypesV1.js +1 -0
  40. package/dist/src/evaluation/upstreamArtifacts/myDevKitContextArtifactsV1.js +1 -0
  41. package/dist/src/evaluation/upstreamArtifacts/myDevKitSemanticTypesV1.js +1 -0
  42. package/dist/src/evaluation/upstreamArtifacts/orchestratorWorkflowInstructionPacketV1.js +1 -0
  43. package/dist/src/evaluation/upstreamArtifacts/readJsonArtifact.js +53 -0
  44. package/dist/src/evaluation/upstreamArtifacts/readMyDevKitContextCapsuleV1.js +9 -0
  45. package/dist/src/evaluation/upstreamArtifacts/readMyDevKitRetrievalAuditRecordV1.js +9 -0
  46. package/dist/src/evaluation/upstreamArtifacts/readOrchestratorWorkflowInstructionPacketV1.js +9 -0
  47. package/dist/src/evaluation/upstreamArtifacts/runtimeValidation.js +131 -0
  48. package/dist/src/evaluation/upstreamArtifacts/schemaVersion.js +32 -0
  49. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitContextCapsuleV1.js +1208 -0
  50. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitRetrievalAuditRecordV1.js +105 -0
  51. package/dist/src/evaluation/upstreamArtifacts/validateOrchestratorWorkflowInstructionPacketV1.js +235 -0
  52. package/dist/src/experiments/plugins/contextStrategyComparison/executeV043StageContextStrategy.js +46 -0
  53. package/dist/src/experiments/plugins/contextStrategyComparison/index.js +9 -0
  54. package/dist/src/experiments/plugins/contextStrategyComparison/loadV043StrategyArtifacts.js +286 -0
  55. package/dist/src/experiments/plugins/contextStrategyComparison/plugin.js +39 -1
  56. package/dist/src/experiments/plugins/contextStrategyComparison/readV043FullWorkflowLibraryFixture.js +162 -0
  57. package/dist/src/experiments/plugins/contextStrategyComparison/resolveV043StrategyInputs.js +43 -0
  58. package/dist/src/experiments/plugins/contextStrategyComparison/resultMapping.js +3 -0
  59. package/dist/src/experiments/plugins/contextStrategyComparison/runV043StageContextStrategyWithAssurance.js +190 -0
  60. package/dist/src/experiments/plugins/contextStrategyComparison/v043FullWorkflowLibraryFixture.js +1 -0
  61. package/dist/src/experiments/plugins/contextStrategyComparison/v043RunAssuranceTypes.js +1 -0
  62. package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyExecutionTypes.js +1 -0
  63. package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyIds.js +13 -0
  64. package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyInputContracts.js +1 -0
  65. package/dist/src/experiments/plugins/contextStrategyComparison/validateV043RunAssuranceConfig.js +59 -0
  66. package/dist/src/experiments/plugins/contextStrategyComparison/validateV043StrategyInput.js +252 -0
  67. package/dist/src/report/experiments/buildContextStrategyComparisonV043Report.js +398 -0
  68. package/dist/src/report/experiments/buildPluginExperimentReport.js +21 -6
  69. package/dist/src/report/experiments/contextStrategyComparisonV043ReportModel.js +1 -0
  70. package/dist/src/report/experiments/index.js +4 -0
  71. package/dist/src/report/experiments/renderContextStrategyComparisonV043Html.js +234 -0
  72. package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
  73. package/dist/src/report/experiments/renderPluginExperimentReportText.js +225 -0
  74. package/dist/src/report/experiments/writePluginExperimentReports.js +3 -0
  75. package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +215 -9
  76. package/docs/METRICS.md +56 -10
  77. package/package.json +1 -1
  78. package/tests/fixtures/full-workflow-library/complete-v1.0.0.json +24 -0
  79. package/tests/fixtures/full-workflow-library/future-minor-v1.1.0-additive.json +25 -0
  80. package/tests/fixtures/stage-context-expectations/complete-v1.0.0.json +230 -0
  81. package/tests/fixtures/stage-context-expectations/future-minor-v1.1.0-additive.json +232 -0
  82. package/tests/fixtures/stage-context-expectations/invalid/malformed-json.txt +1 -0
  83. package/tests/fixtures/stage-context-expectations/invalid/non-object-root.json +1 -0
  84. package/tests/fixtures/stage-context-expectations/minimal-v1.0.0.json +18 -0
  85. package/tests/fixtures/upstream-artifacts/invalid/malformed-json.txt +1 -0
  86. package/tests/fixtures/upstream-artifacts/invalid/non-object-root.json +1 -0
  87. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/complete-v1.0.0.json +504 -0
  88. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/future-minor-v1.1.0-additive.json +955 -0
  89. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/complete-v1.0.0.json +190 -0
  90. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/future-minor-v1.1.0-additive.json +327 -0
  91. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/complete-v1.0.0.json +160 -0
  92. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/future-minor-v1.1.0-additive.json +186 -0
@@ -0,0 +1,126 @@
1
+ export function selectContextCapsuleSchemaVersion(artifact) {
2
+ return artifact.schemaVersion;
3
+ }
4
+ export function selectContextCapsuleGeneratedAt(artifact) {
5
+ return artifact.generatedAt;
6
+ }
7
+ export function selectContextCapsuleTool(artifact) {
8
+ return artifact.tool;
9
+ }
10
+ export function selectContextCapsuleRequest(artifact) {
11
+ return artifact.request;
12
+ }
13
+ export function selectContextCapsuleIndex(artifact) {
14
+ return artifact.index;
15
+ }
16
+ export function selectContextCapsuleLimits(artifact) {
17
+ return artifact.limits;
18
+ }
19
+ export function selectContextCapsuleRequiredContext(artifact) {
20
+ return artifact.requiredContext;
21
+ }
22
+ export function selectContextCapsuleOptionalSupportContext(artifact) {
23
+ return artifact.optionalSupportContext;
24
+ }
25
+ export function selectContextCapsuleDroppedContext(artifact) {
26
+ return artifact.droppedContext;
27
+ }
28
+ export function selectContextCapsuleWarnings(artifact) {
29
+ return artifact.warnings;
30
+ }
31
+ export function selectContextCapsuleContextAdequacy(artifact) {
32
+ return artifact.contextAdequacy;
33
+ }
34
+ export function selectContextCapsuleQueryPlan(artifact) {
35
+ return artifact.queryPlan;
36
+ }
37
+ export function selectContextCapsuleCandidateFiles(artifact) {
38
+ return artifact.candidateFiles;
39
+ }
40
+ export function selectContextCapsuleCandidateNodes(artifact) {
41
+ return artifact.candidateNodes;
42
+ }
43
+ export function selectContextCapsuleFocus(artifact) {
44
+ return artifact.focus;
45
+ }
46
+ export function selectContextCapsuleSelectedGraph(artifact) {
47
+ return artifact.selectedGraph;
48
+ }
49
+ export function selectContextCapsuleRetention(artifact) {
50
+ return artifact.retention;
51
+ }
52
+ export function selectContextCapsuleSelectedSource(artifact) {
53
+ return artifact.selectedSource;
54
+ }
55
+ export function selectContextCapsuleSelectedSourceBundles(artifact) {
56
+ return artifact.selectedSourceBundles;
57
+ }
58
+ export function selectContextCapsuleSemanticSummary(artifact) {
59
+ return artifact.semanticSummary;
60
+ }
61
+ export function selectContextCapsuleClassificationSummary(artifact) {
62
+ return artifact.classificationSummary;
63
+ }
64
+ export function selectContextCapsuleArtifactReferenceSummary(artifact) {
65
+ return artifact.artifactReferenceSummary;
66
+ }
67
+ export function selectContextCapsulePruning(artifact) {
68
+ return artifact.pruning;
69
+ }
70
+ export function selectContextCapsuleConflicts(artifact) {
71
+ return artifact.conflicts;
72
+ }
73
+ export function selectContextCapsuleModeEffects(artifact) {
74
+ return artifact.modeEffects;
75
+ }
76
+ export function selectContextCapsuleSourceControl(artifact) {
77
+ return artifact.sourceControl;
78
+ }
79
+ export function selectContextCapsuleDeferredRequestFields(artifact) {
80
+ return artifact.deferredRequestFields;
81
+ }
82
+ export function selectContextCapsuleRoleContext(artifact) {
83
+ return artifact.roleContext;
84
+ }
85
+ export function selectContextCapsuleEvidenceGroups(artifact) {
86
+ return artifact.evidenceGroups;
87
+ }
88
+ export function selectContextCapsuleSelectedOwners(artifact) {
89
+ return artifact.selectedOwners;
90
+ }
91
+ export function selectContextCapsuleSelectedContracts(artifact) {
92
+ return artifact.selectedContracts;
93
+ }
94
+ export function selectContextCapsuleSelectedTests(artifact) {
95
+ return artifact.selectedTests;
96
+ }
97
+ export function selectContextCapsuleTestInfrastructure(artifact) {
98
+ return artifact.testInfrastructure;
99
+ }
100
+ export function selectContextCapsuleUnresolvedItems(artifact) {
101
+ return artifact.unresolvedItems;
102
+ }
103
+ export function selectContextCapsuleGroupTruncation(artifact) {
104
+ return artifact.groupTruncation;
105
+ }
106
+ export function selectContextCapsuleResponsibilityMappings(artifact) {
107
+ return artifact.responsibilityMappings;
108
+ }
109
+ export function selectContextCapsuleRoleAdequacy(artifact) {
110
+ return artifact.roleAdequacy;
111
+ }
112
+ export function selectContextCapsuleFreshness(artifact) {
113
+ return artifact.freshness;
114
+ }
115
+ export function selectContextCapsuleBudget(artifact) {
116
+ return artifact.budget;
117
+ }
118
+ export function selectContextCapsuleTruncation(artifact) {
119
+ return artifact.truncation;
120
+ }
121
+ export function selectContextCapsuleFullFileFallback(artifact) {
122
+ return artifact.fullFileFallback;
123
+ }
124
+ export function selectContextCapsuleProvenance(artifact) {
125
+ return artifact.provenance;
126
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./contextCapsuleSelectors.js";
2
+ export * from "./retrievalAuditRecordSelectors.js";
3
+ export * from "./workflowInstructionPacketSelectors.js";
4
+ export * from "./contextArtifactConsistency.js";
@@ -0,0 +1,54 @@
1
+ export function selectRetrievalAuditRecordSchemaVersion(artifact) {
2
+ return artifact.schemaVersion;
3
+ }
4
+ export function selectRetrievalAuditRecordGeneratedAt(artifact) {
5
+ return artifact.generatedAt;
6
+ }
7
+ export function selectRetrievalAuditRecordTool(artifact) {
8
+ return artifact.tool;
9
+ }
10
+ export function selectRetrievalAuditRecordRequest(artifact) {
11
+ return artifact.request;
12
+ }
13
+ export function selectRetrievalAuditRecordIndex(artifact) {
14
+ return artifact.index;
15
+ }
16
+ export function selectRetrievalAuditRecordSteps(artifact) {
17
+ return artifact.steps;
18
+ }
19
+ export function selectRetrievalAuditRecordFallbacks(artifact) {
20
+ return artifact.fallbacks;
21
+ }
22
+ export function selectRetrievalAuditRecordFullFileReadRecommendations(artifact) {
23
+ return artifact.fullFileReadRecommendations;
24
+ }
25
+ export function selectRetrievalAuditRecordWarnings(artifact) {
26
+ return artifact.warnings;
27
+ }
28
+ export function selectRetrievalAuditRecordContextAdequacy(artifact) {
29
+ return artifact.contextAdequacy;
30
+ }
31
+ export function selectRetrievalAuditRecordRoleContext(artifact) {
32
+ return artifact.roleContext;
33
+ }
34
+ export function selectRetrievalAuditRecordResponsibilityMappings(artifact) {
35
+ return artifact.responsibilityMappings;
36
+ }
37
+ export function selectRetrievalAuditRecordRoleAdequacy(artifact) {
38
+ return artifact.roleAdequacy;
39
+ }
40
+ export function selectRetrievalAuditRecordFreshness(artifact) {
41
+ return artifact.freshness;
42
+ }
43
+ export function selectRetrievalAuditRecordBudget(artifact) {
44
+ return artifact.budget;
45
+ }
46
+ export function selectRetrievalAuditRecordTruncation(artifact) {
47
+ return artifact.truncation;
48
+ }
49
+ export function selectRetrievalAuditRecordFullFileFallback(artifact) {
50
+ return artifact.fullFileFallback;
51
+ }
52
+ export function selectRetrievalAuditRecordProvenance(artifact) {
53
+ return artifact.provenance;
54
+ }
@@ -0,0 +1,51 @@
1
+ export function selectWorkflowInstructionPacketSchemaVersion(artifact) {
2
+ return artifact.schemaVersion;
3
+ }
4
+ export function selectWorkflowInstructionPacketCatalogSchemaVersion(artifact) {
5
+ return artifact.catalogSchemaVersion;
6
+ }
7
+ export function selectWorkflowInstructionPacketCatalogVersion(artifact) {
8
+ return artifact.catalogVersion;
9
+ }
10
+ export function selectWorkflowInstructionPacketWorkflowId(artifact) {
11
+ return artifact.workflowId;
12
+ }
13
+ export function selectWorkflowInstructionPacketStageId(artifact) {
14
+ return artifact.stageId;
15
+ }
16
+ export function selectWorkflowInstructionPacketPrimaryEntry(artifact) {
17
+ return artifact.primaryEntry;
18
+ }
19
+ export function selectWorkflowInstructionPacketResolvedCommands(artifact) {
20
+ return artifact.resolvedCommands;
21
+ }
22
+ export function selectWorkflowInstructionPacketResolvedRules(artifact) {
23
+ return artifact.resolvedRules;
24
+ }
25
+ export function selectWorkflowInstructionPacketReportContract(artifact) {
26
+ return artifact.reportContract;
27
+ }
28
+ export function selectWorkflowInstructionPacketValidationRequirements(artifact) {
29
+ return artifact.validationRequirements;
30
+ }
31
+ export function selectWorkflowInstructionPacketStopConditions(artifact) {
32
+ return artifact.stopConditions;
33
+ }
34
+ export function selectWorkflowInstructionPacketResolutionProvenance(artifact) {
35
+ return artifact.resolutionProvenance;
36
+ }
37
+ export function selectWorkflowInstructionPacketBudget(artifact) {
38
+ return artifact.budget;
39
+ }
40
+ export function selectWorkflowInstructionPacketTruncation(artifact) {
41
+ return artifact.truncation;
42
+ }
43
+ export function selectWorkflowInstructionPacketAdequacy(artifact) {
44
+ return artifact.adequacy;
45
+ }
46
+ export function selectWorkflowInstructionPacketUnresolvedReferences(artifact) {
47
+ return artifact.unresolvedReferences;
48
+ }
49
+ export function selectWorkflowInstructionPacketWarnings(artifact) {
50
+ return artifact.warnings;
51
+ }
@@ -0,0 +1,208 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { lstat, readFile, readlink } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ const execFileAsync = promisify(execFile);
7
+ function sha256Hex(data) {
8
+ return createHash("sha256").update(data).digest("hex");
9
+ }
10
+ function stripTrailingLineTerminator(text) {
11
+ return text.replace(/\r?\n$/, "");
12
+ }
13
+ function splitLines(text) {
14
+ const stripped = stripTrailingLineTerminator(text);
15
+ return stripped.length > 0 ? stripped.split(/\r?\n/) : [];
16
+ }
17
+ function isInsideRoot(resolvedRoot, candidate) {
18
+ const relative = path.relative(resolvedRoot, candidate);
19
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
20
+ }
21
+ async function snapshotPathEntry(resolvedPath) {
22
+ let stats;
23
+ try {
24
+ stats = await lstat(resolvedPath);
25
+ }
26
+ catch (error) {
27
+ const code = error.code;
28
+ if (code === "ENOENT") {
29
+ return { state: "missing", sha256: null, symbolicLinkTarget: null };
30
+ }
31
+ throw error;
32
+ }
33
+ if (stats.isSymbolicLink()) {
34
+ const target = await readlink(resolvedPath);
35
+ return { state: "symbolic-link", sha256: null, symbolicLinkTarget: target };
36
+ }
37
+ if (stats.isDirectory()) {
38
+ return { state: "directory", sha256: null, symbolicLinkTarget: null };
39
+ }
40
+ if (stats.isFile()) {
41
+ const contents = await readFile(resolvedPath);
42
+ return { state: "file", sha256: sha256Hex(contents), symbolicLinkTarget: null };
43
+ }
44
+ return { state: "other", sha256: null, symbolicLinkTarget: null };
45
+ }
46
+ async function runGit(resolvedRoot, args) {
47
+ try {
48
+ const { stdout } = await execFileAsync("git", ["-C", resolvedRoot, ...args], {
49
+ encoding: "utf8",
50
+ maxBuffer: 1024 * 1024 * 64
51
+ });
52
+ return { ok: true, stdout };
53
+ }
54
+ catch (error) {
55
+ const err = error;
56
+ if (err.code === "ENOENT") {
57
+ return { ok: false, unavailable: true, message: "git executable was not found." };
58
+ }
59
+ return { ok: false, unavailable: false, message: error.message };
60
+ }
61
+ }
62
+ async function runRequiredGit(resolvedRoot, args, targetRootPath) {
63
+ const result = await runGit(resolvedRoot, args);
64
+ if (result.ok)
65
+ return result;
66
+ const code = result.unavailable ? "GIT_COMMAND_UNAVAILABLE" : "GIT_COMMAND_FAILED";
67
+ const message = result.unavailable
68
+ ? "The git executable is not available."
69
+ : `git ${args.join(" ")} failed: ${result.message}`;
70
+ return { ok: false, code, targetRootPath, message };
71
+ }
72
+ export async function captureTargetSnapshot(config) {
73
+ const resolvedTargetRootPath = path.resolve(config.targetRootPath);
74
+ let rootStats;
75
+ try {
76
+ rootStats = await lstat(resolvedTargetRootPath);
77
+ }
78
+ catch (error) {
79
+ const code = error.code;
80
+ if (code === "ENOENT") {
81
+ return {
82
+ ok: false,
83
+ code: "TARGET_ROOT_NOT_FOUND",
84
+ targetRootPath: config.targetRootPath,
85
+ message: `Target root was not found at "${resolvedTargetRootPath}".`
86
+ };
87
+ }
88
+ return {
89
+ ok: false,
90
+ code: "TARGET_ROOT_UNREADABLE",
91
+ targetRootPath: config.targetRootPath,
92
+ message: `Target root at "${resolvedTargetRootPath}" could not be read: ${error.message}`
93
+ };
94
+ }
95
+ if (!rootStats.isDirectory()) {
96
+ return {
97
+ ok: false,
98
+ code: "TARGET_ROOT_NOT_DIRECTORY",
99
+ targetRootPath: config.targetRootPath,
100
+ message: `Target root at "${resolvedTargetRootPath}" is not a directory.`
101
+ };
102
+ }
103
+ const configuredFiles = [];
104
+ for (const relativePath of config.relativeFilePaths) {
105
+ const resolvedPath = path.resolve(resolvedTargetRootPath, relativePath);
106
+ if (!isInsideRoot(resolvedTargetRootPath, resolvedPath)) {
107
+ return {
108
+ ok: false,
109
+ code: "CONFIGURED_FILE_READ_FAILED",
110
+ targetRootPath: config.targetRootPath,
111
+ message: `Configured file "${relativePath}" resolves outside the target root.`,
112
+ fieldPath: relativePath
113
+ };
114
+ }
115
+ try {
116
+ const entry = await snapshotPathEntry(resolvedPath);
117
+ configuredFiles.push({
118
+ relativePath,
119
+ resolvedPath,
120
+ state: entry.state,
121
+ sha256: entry.sha256,
122
+ symbolicLinkTarget: entry.symbolicLinkTarget
123
+ });
124
+ }
125
+ catch (error) {
126
+ return {
127
+ ok: false,
128
+ code: "CONFIGURED_FILE_READ_FAILED",
129
+ targetRootPath: config.targetRootPath,
130
+ message: `Configured file "${relativePath}" could not be read: ${error.message}`,
131
+ fieldPath: relativePath
132
+ };
133
+ }
134
+ }
135
+ const isRepoOutcome = await runGit(resolvedTargetRootPath, ["rev-parse", "--is-inside-work-tree"]);
136
+ if (!isRepoOutcome.ok) {
137
+ if (isRepoOutcome.unavailable) {
138
+ return {
139
+ ok: false,
140
+ code: "GIT_COMMAND_UNAVAILABLE",
141
+ targetRootPath: config.targetRootPath,
142
+ message: "The git executable is not available."
143
+ };
144
+ }
145
+ const git = {
146
+ availability: "not-repository",
147
+ branch: null,
148
+ head: null,
149
+ statusEntries: [],
150
+ worktreeDiffSha256: null,
151
+ stagedDiffSha256: null,
152
+ untrackedFiles: []
153
+ };
154
+ return {
155
+ ok: true,
156
+ snapshot: { targetRootPath: config.targetRootPath, resolvedTargetRootPath, configuredFiles, git }
157
+ };
158
+ }
159
+ const branchOutcome = await runRequiredGit(resolvedTargetRootPath, ["branch", "--show-current"], config.targetRootPath);
160
+ if (!branchOutcome.ok)
161
+ return branchOutcome;
162
+ const branch = stripTrailingLineTerminator(branchOutcome.stdout);
163
+ const headOutcome = await runRequiredGit(resolvedTargetRootPath, ["rev-parse", "HEAD"], config.targetRootPath);
164
+ if (!headOutcome.ok)
165
+ return headOutcome;
166
+ const head = stripTrailingLineTerminator(headOutcome.stdout);
167
+ const statusOutcome = await runRequiredGit(resolvedTargetRootPath, ["status", "--porcelain=v1", "--untracked-files=all"], config.targetRootPath);
168
+ if (!statusOutcome.ok)
169
+ return statusOutcome;
170
+ const statusEntries = splitLines(statusOutcome.stdout);
171
+ const worktreeDiffOutcome = await runRequiredGit(resolvedTargetRootPath, ["diff", "--binary", "--no-ext-diff"], config.targetRootPath);
172
+ if (!worktreeDiffOutcome.ok)
173
+ return worktreeDiffOutcome;
174
+ const worktreeDiffSha256 = sha256Hex(Buffer.from(worktreeDiffOutcome.stdout, "utf8"));
175
+ const stagedDiffOutcome = await runRequiredGit(resolvedTargetRootPath, ["diff", "--cached", "--binary", "--no-ext-diff"], config.targetRootPath);
176
+ if (!stagedDiffOutcome.ok)
177
+ return stagedDiffOutcome;
178
+ const stagedDiffSha256 = sha256Hex(Buffer.from(stagedDiffOutcome.stdout, "utf8"));
179
+ const untrackedOutcome = await runRequiredGit(resolvedTargetRootPath, ["ls-files", "--others", "--exclude-standard"], config.targetRootPath);
180
+ if (!untrackedOutcome.ok)
181
+ return untrackedOutcome;
182
+ const untrackedPaths = splitLines(untrackedOutcome.stdout);
183
+ const untrackedFiles = [];
184
+ for (const untrackedRelativePath of untrackedPaths) {
185
+ const resolvedPath = path.resolve(resolvedTargetRootPath, untrackedRelativePath);
186
+ const entry = await snapshotPathEntry(resolvedPath);
187
+ const state = entry.state === "directory" ? "other" : entry.state;
188
+ untrackedFiles.push({
189
+ path: untrackedRelativePath,
190
+ state,
191
+ sha256: entry.sha256,
192
+ symbolicLinkTarget: entry.symbolicLinkTarget
193
+ });
194
+ }
195
+ const git = {
196
+ availability: "available",
197
+ branch,
198
+ head,
199
+ statusEntries,
200
+ worktreeDiffSha256,
201
+ stagedDiffSha256,
202
+ untrackedFiles
203
+ };
204
+ return {
205
+ ok: true,
206
+ snapshot: { targetRootPath: config.targetRootPath, resolvedTargetRootPath, configuredFiles, git }
207
+ };
208
+ }
@@ -0,0 +1,56 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ function mutation(id, kind, fieldPath, before, after) {
3
+ return { id, kind, fieldPath, before, after };
4
+ }
5
+ export function compareTargetSnapshots(before, after) {
6
+ const mutations = [];
7
+ if (before.git.availability !== after.git.availability) {
8
+ mutations.push(mutation("git.availability", "git-availability", "git.availability", before.git.availability, after.git.availability));
9
+ }
10
+ if (before.git.branch !== after.git.branch) {
11
+ mutations.push(mutation("git.branch", "git-branch", "git.branch", before.git.branch, after.git.branch));
12
+ }
13
+ if (before.git.head !== after.git.head) {
14
+ mutations.push(mutation("git.head", "git-head", "git.head", before.git.head, after.git.head));
15
+ }
16
+ if (!isDeepStrictEqual(before.git.statusEntries, after.git.statusEntries)) {
17
+ mutations.push(mutation("git.status", "git-status", "git.statusEntries", before.git.statusEntries, after.git.statusEntries));
18
+ }
19
+ if (before.git.worktreeDiffSha256 !== after.git.worktreeDiffSha256) {
20
+ mutations.push(mutation("git.worktree-diff", "git-worktree-diff", "git.worktreeDiffSha256", before.git.worktreeDiffSha256, after.git.worktreeDiffSha256));
21
+ }
22
+ if (before.git.stagedDiffSha256 !== after.git.stagedDiffSha256) {
23
+ mutations.push(mutation("git.staged-diff", "git-staged-diff", "git.stagedDiffSha256", before.git.stagedDiffSha256, after.git.stagedDiffSha256));
24
+ }
25
+ const afterUntrackedByPath = new Map(after.git.untrackedFiles.map((entry) => [entry.path, entry]));
26
+ const beforeUntrackedPaths = new Set(before.git.untrackedFiles.map((entry) => entry.path));
27
+ for (const beforeEntry of before.git.untrackedFiles) {
28
+ const afterEntry = afterUntrackedByPath.get(beforeEntry.path);
29
+ if (afterEntry === undefined) {
30
+ mutations.push(mutation(`git.untracked:${beforeEntry.path}`, "git-untracked-file", `git.untrackedFiles.${beforeEntry.path}`, beforeEntry, null));
31
+ }
32
+ else if (!isDeepStrictEqual(beforeEntry, afterEntry)) {
33
+ mutations.push(mutation(`git.untracked:${beforeEntry.path}`, "git-untracked-file", `git.untrackedFiles.${beforeEntry.path}`, beforeEntry, afterEntry));
34
+ }
35
+ }
36
+ for (const afterEntry of after.git.untrackedFiles) {
37
+ if (!beforeUntrackedPaths.has(afterEntry.path)) {
38
+ mutations.push(mutation(`git.untracked:${afterEntry.path}`, "git-untracked-file", `git.untrackedFiles.${afterEntry.path}`, null, afterEntry));
39
+ }
40
+ }
41
+ const afterConfiguredByPath = new Map(after.configuredFiles.map((entry) => [entry.relativePath, entry]));
42
+ for (const beforeEntry of before.configuredFiles) {
43
+ const afterEntry = afterConfiguredByPath.get(beforeEntry.relativePath);
44
+ if (afterEntry === undefined || !isDeepStrictEqual(beforeEntry, afterEntry)) {
45
+ mutations.push(mutation(`file:${beforeEntry.relativePath}`, "configured-file", `configuredFiles.${beforeEntry.relativePath}`, beforeEntry, (afterEntry ?? null)));
46
+ }
47
+ }
48
+ return {
49
+ status: mutations.length > 0 ? "mutated" : "unchanged",
50
+ targetRootPath: before.targetRootPath,
51
+ resolvedTargetRootPath: before.resolvedTargetRootPath,
52
+ preExistingGitStatusEntryCount: before.git.statusEntries.length,
53
+ newMutationCount: mutations.length,
54
+ mutations
55
+ };
56
+ }
@@ -0,0 +1,3 @@
1
+ export { validateTargetImmutabilityConfig } from "./validateTargetImmutabilityConfig.js";
2
+ export { captureTargetSnapshot } from "./captureTargetSnapshot.js";
3
+ export { compareTargetSnapshots } from "./compareTargetSnapshots.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,130 @@
1
+ const ALLOWED_KEYS = ["targetRootPath", "relativeFilePaths"];
2
+ const MAX_RELATIVE_FILES = 100;
3
+ function isPlainObject(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+ function isAbsolutePathLike(value) {
7
+ if (value.startsWith("/") || value.startsWith("\\"))
8
+ return true;
9
+ if (/^[A-Za-z]:[\\/]/.test(value))
10
+ return true;
11
+ return false;
12
+ }
13
+ function hasTraversalSegment(value) {
14
+ return value.split(/[\\/]/).includes("..");
15
+ }
16
+ export function validateTargetImmutabilityConfig(value) {
17
+ if (!isPlainObject(value)) {
18
+ return {
19
+ ok: false,
20
+ issues: [{ code: "NON_OBJECT_CONFIG", fieldPath: "", message: "Target immutability config must be an object." }]
21
+ };
22
+ }
23
+ const root = value;
24
+ const issues = [];
25
+ for (const key of Object.keys(root)) {
26
+ if (!ALLOWED_KEYS.includes(key)) {
27
+ issues.push({ code: "UNKNOWN_CONFIG_FIELD", fieldPath: key, message: `"${key}" is not a recognized field.` });
28
+ }
29
+ }
30
+ if (root.targetRootPath === undefined) {
31
+ issues.push({ code: "MISSING_REQUIRED_FIELD", fieldPath: "targetRootPath", message: "targetRootPath is required." });
32
+ }
33
+ if (root.relativeFilePaths === undefined) {
34
+ issues.push({ code: "MISSING_REQUIRED_FIELD", fieldPath: "relativeFilePaths", message: "relativeFilePaths is required." });
35
+ }
36
+ const targetRootPathIsString = root.targetRootPath !== undefined && typeof root.targetRootPath === "string";
37
+ if (root.targetRootPath !== undefined && !targetRootPathIsString) {
38
+ issues.push({ code: "INVALID_FIELD_TYPE", fieldPath: "targetRootPath", message: "targetRootPath must be a string." });
39
+ }
40
+ const relativeFilePathsIsArray = root.relativeFilePaths !== undefined && Array.isArray(root.relativeFilePaths);
41
+ if (root.relativeFilePaths !== undefined && !relativeFilePathsIsArray) {
42
+ issues.push({ code: "INVALID_FIELD_TYPE", fieldPath: "relativeFilePaths", message: "relativeFilePaths must be an array." });
43
+ }
44
+ if (targetRootPathIsString && root.targetRootPath.trim().length === 0) {
45
+ issues.push({ code: "EMPTY_TARGET_ROOT_PATH", fieldPath: "targetRootPath", message: "targetRootPath must be a nonempty string." });
46
+ }
47
+ let pathsArray = [];
48
+ if (relativeFilePathsIsArray) {
49
+ pathsArray = root.relativeFilePaths;
50
+ if (pathsArray.length === 0) {
51
+ issues.push({
52
+ code: "EMPTY_RELATIVE_FILE_SET",
53
+ fieldPath: "relativeFilePaths",
54
+ message: "relativeFilePaths must contain at least one entry."
55
+ });
56
+ }
57
+ else if (pathsArray.length > MAX_RELATIVE_FILES) {
58
+ issues.push({
59
+ code: "TOO_MANY_RELATIVE_FILES",
60
+ fieldPath: "relativeFilePaths",
61
+ message: `relativeFilePaths must contain at most ${MAX_RELATIVE_FILES} entries.`
62
+ });
63
+ }
64
+ }
65
+ const validStringPaths = [];
66
+ if (relativeFilePathsIsArray) {
67
+ pathsArray.forEach((entry, index) => {
68
+ if (typeof entry !== "string") {
69
+ issues.push({
70
+ code: "INVALID_FIELD_TYPE",
71
+ fieldPath: `relativeFilePaths[${index}]`,
72
+ message: `relativeFilePaths[${index}] must be a string.`
73
+ });
74
+ validStringPaths.push(null);
75
+ }
76
+ else if (entry.trim().length === 0) {
77
+ issues.push({
78
+ code: "EMPTY_RELATIVE_FILE_PATH",
79
+ fieldPath: `relativeFilePaths[${index}]`,
80
+ message: `relativeFilePaths[${index}] must be a nonempty string.`
81
+ });
82
+ validStringPaths.push(null);
83
+ }
84
+ else {
85
+ validStringPaths.push(entry);
86
+ }
87
+ });
88
+ }
89
+ if (relativeFilePathsIsArray) {
90
+ validStringPaths.forEach((entry, index) => {
91
+ if (entry !== null && isAbsolutePathLike(entry)) {
92
+ issues.push({
93
+ code: "ABSOLUTE_RELATIVE_FILE_PATH",
94
+ fieldPath: `relativeFilePaths[${index}]`,
95
+ message: `relativeFilePaths[${index}] must be a relative path.`
96
+ });
97
+ }
98
+ });
99
+ }
100
+ if (relativeFilePathsIsArray) {
101
+ validStringPaths.forEach((entry, index) => {
102
+ if (entry !== null && hasTraversalSegment(entry)) {
103
+ issues.push({
104
+ code: "RELATIVE_FILE_PATH_TRAVERSAL",
105
+ fieldPath: `relativeFilePaths[${index}]`,
106
+ message: `relativeFilePaths[${index}] must not contain a ".." path segment.`
107
+ });
108
+ }
109
+ });
110
+ }
111
+ if (relativeFilePathsIsArray) {
112
+ const seen = new Set();
113
+ validStringPaths.forEach((entry, index) => {
114
+ if (entry === null)
115
+ return;
116
+ if (seen.has(entry)) {
117
+ issues.push({
118
+ code: "DUPLICATE_RELATIVE_FILE_PATH",
119
+ fieldPath: `relativeFilePaths[${index}]`,
120
+ message: `relativeFilePaths[${index}] duplicates an earlier entry.`
121
+ });
122
+ }
123
+ seen.add(entry);
124
+ });
125
+ }
126
+ if (issues.length > 0) {
127
+ return { ok: false, issues };
128
+ }
129
+ return { ok: true, config: value };
130
+ }
@@ -0,0 +1,3 @@
1
+ export { readMyDevKitContextCapsuleV1 } from "./readMyDevKitContextCapsuleV1.js";
2
+ export { readMyDevKitRetrievalAuditRecordV1 } from "./readMyDevKitRetrievalAuditRecordV1.js";
3
+ export { readOrchestratorWorkflowInstructionPacketV1 } from "./readOrchestratorWorkflowInstructionPacketV1.js";
@@ -0,0 +1 @@
1
+ export {};