@ai-sdlc/orchestrator 0.6.0 → 0.10.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.
Files changed (209) hide show
  1. package/dist/__test-helpers/git-env.d.ts +40 -0
  2. package/dist/__test-helpers/git-env.js +73 -0
  3. package/dist/admission-composite.d.ts +89 -0
  4. package/dist/admission-composite.js +241 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +341 -0
  7. package/dist/admission-hc.d.ts +81 -0
  8. package/dist/admission-hc.js +94 -0
  9. package/dist/admission-score.d.ts +109 -5
  10. package/dist/admission-score.js +90 -11
  11. package/dist/analysis/analyzer.js +3 -2
  12. package/dist/analysis/convention-detector.d.ts +85 -2
  13. package/dist/analysis/convention-detector.js +375 -70
  14. package/dist/analysis/diff-analyzer.d.ts +33 -0
  15. package/dist/analysis/diff-analyzer.js +122 -0
  16. package/dist/analysis/hotspot-analyzer.js +3 -1
  17. package/dist/analysis/index.d.ts +2 -1
  18. package/dist/analysis/index.js +2 -1
  19. package/dist/artifacts/index.d.ts +65 -0
  20. package/dist/artifacts/index.js +142 -0
  21. package/dist/backlog-adapter.d.ts +121 -0
  22. package/dist/backlog-adapter.js +438 -0
  23. package/dist/calibration.d.ts +43 -0
  24. package/dist/calibration.js +76 -0
  25. package/dist/cli/commands/agents.d.ts +24 -0
  26. package/dist/cli/commands/agents.js +66 -1
  27. package/dist/cli/commands/git-remote.d.ts +74 -0
  28. package/dist/cli/commands/git-remote.js +170 -0
  29. package/dist/cli/commands/health.d.ts +4 -0
  30. package/dist/cli/commands/health.js +13 -1
  31. package/dist/cli/commands/init-features.d.ts +208 -0
  32. package/dist/cli/commands/init-features.js +473 -0
  33. package/dist/cli/commands/init-templates.d.ts +104 -0
  34. package/dist/cli/commands/init-templates.js +399 -0
  35. package/dist/cli/commands/init.d.ts +48 -0
  36. package/dist/cli/commands/init.js +322 -23
  37. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  38. package/dist/cli/commands/mcp-setup.js +78 -8
  39. package/dist/cli/formatters/table.js +15 -2
  40. package/dist/cli/index.d.ts +14 -1
  41. package/dist/cli/index.js +81 -20
  42. package/dist/cli/versions.d.ts +57 -0
  43. package/dist/cli/versions.js +128 -0
  44. package/dist/code-area-classifier.d.ts +21 -0
  45. package/dist/code-area-classifier.js +48 -0
  46. package/dist/config.d.ts +33 -1
  47. package/dist/config.js +78 -8
  48. package/dist/database/adapters/external.d.ts +24 -0
  49. package/dist/database/adapters/external.js +80 -0
  50. package/dist/database/adapters/neon.d.ts +41 -0
  51. package/dist/database/adapters/neon.js +98 -0
  52. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  53. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  54. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  55. package/dist/database/adapters/sqlite-copy.js +145 -0
  56. package/dist/database/connection-injection.d.ts +35 -0
  57. package/dist/database/connection-injection.js +93 -0
  58. package/dist/database/index.d.ts +12 -0
  59. package/dist/database/index.js +23 -0
  60. package/dist/database/registry.d.ts +13 -0
  61. package/dist/database/registry.js +27 -0
  62. package/dist/database/topology.d.ts +52 -0
  63. package/dist/database/topology.js +44 -0
  64. package/dist/database/types.d.ts +89 -0
  65. package/dist/database/types.js +26 -0
  66. package/dist/design-authority.d.ts +53 -0
  67. package/dist/design-authority.js +84 -0
  68. package/dist/design-lookahead.d.ts +64 -0
  69. package/dist/design-lookahead.js +86 -0
  70. package/dist/design-quality-trend.d.ts +87 -0
  71. package/dist/design-quality-trend.js +190 -0
  72. package/dist/design-system-context.d.ts +46 -0
  73. package/dist/design-system-context.js +80 -0
  74. package/dist/design-system-correction-loop.d.ts +64 -0
  75. package/dist/design-system-correction-loop.js +128 -0
  76. package/dist/design-system-metrics.d.ts +61 -0
  77. package/dist/design-system-metrics.js +104 -0
  78. package/dist/design-system-stewardship.d.ts +22 -0
  79. package/dist/design-system-stewardship.js +85 -0
  80. package/dist/design-system-validation.d.ts +37 -0
  81. package/dist/design-system-validation.js +88 -0
  82. package/dist/dispatch/index.d.ts +4 -0
  83. package/dist/dispatch/index.js +4 -0
  84. package/dist/dispatch/merge-gate.d.ts +46 -0
  85. package/dist/dispatch/merge-gate.js +90 -0
  86. package/dist/dispatch/requeue.d.ts +57 -0
  87. package/dist/dispatch/requeue.js +131 -0
  88. package/dist/dispatch/worker-pool.d.ts +62 -0
  89. package/dist/dispatch/worker-pool.js +60 -0
  90. package/dist/execute.d.ts +40 -0
  91. package/dist/execute.js +188 -15
  92. package/dist/fix-ci.js +8 -1
  93. package/dist/fix-review.js +8 -1
  94. package/dist/harness/adapters/claude-code.d.ts +29 -0
  95. package/dist/harness/adapters/claude-code.js +191 -0
  96. package/dist/harness/adapters/codex.d.ts +25 -0
  97. package/dist/harness/adapters/codex.js +61 -0
  98. package/dist/harness/independence.d.ts +51 -0
  99. package/dist/harness/independence.js +67 -0
  100. package/dist/harness/index.d.ts +14 -0
  101. package/dist/harness/index.js +20 -0
  102. package/dist/harness/registry.d.ts +17 -0
  103. package/dist/harness/registry.js +31 -0
  104. package/dist/harness/types.d.ts +123 -0
  105. package/dist/harness/types.js +8 -0
  106. package/dist/harness/version-probe.d.ts +14 -0
  107. package/dist/harness/version-probe.js +123 -0
  108. package/dist/index.d.ts +30 -4
  109. package/dist/index.js +35 -2
  110. package/dist/models/classifier.d.ts +78 -0
  111. package/dist/models/classifier.js +277 -0
  112. package/dist/models/index.d.ts +3 -0
  113. package/dist/models/index.js +3 -0
  114. package/dist/models/registry.d.ts +97 -0
  115. package/dist/models/registry.js +173 -0
  116. package/dist/pillar-breakdown.d.ts +85 -0
  117. package/dist/pillar-breakdown.js +162 -0
  118. package/dist/priority.d.ts +5 -0
  119. package/dist/priority.js +18 -5
  120. package/dist/review-meta.d.ts +65 -0
  121. package/dist/review-meta.js +149 -0
  122. package/dist/review.d.ts +4 -0
  123. package/dist/review.js +12 -2
  124. package/dist/runners/claude-code-sdk.d.ts +31 -0
  125. package/dist/runners/claude-code-sdk.js +238 -0
  126. package/dist/runners/claude-code.d.ts +3 -0
  127. package/dist/runners/claude-code.js +30 -66
  128. package/dist/runners/codex.js +4 -1
  129. package/dist/runners/copilot.js +4 -1
  130. package/dist/runners/cursor.js +4 -1
  131. package/dist/runners/git-utils.d.ts +81 -0
  132. package/dist/runners/git-utils.js +201 -0
  133. package/dist/runners/index.d.ts +3 -0
  134. package/dist/runners/index.js +3 -0
  135. package/dist/runners/review-agent.d.ts +20 -0
  136. package/dist/runners/review-agent.js +171 -41
  137. package/dist/runners/runner-registry.js +10 -0
  138. package/dist/runners/sdk-review-runner.d.ts +65 -0
  139. package/dist/runners/sdk-review-runner.js +185 -0
  140. package/dist/runners/security-triage.d.ts +20 -4
  141. package/dist/runners/security-triage.js +39 -15
  142. package/dist/runners/types.d.ts +6 -0
  143. package/dist/runtime/attestations.d.ts +766 -0
  144. package/dist/runtime/attestations.js +1195 -0
  145. package/dist/runtime/git-env.d.ts +53 -0
  146. package/dist/runtime/git-env.js +60 -0
  147. package/dist/runtime/index.d.ts +7 -0
  148. package/dist/runtime/index.js +7 -0
  149. package/dist/runtime/parallelism-flag.d.ts +28 -0
  150. package/dist/runtime/parallelism-flag.js +39 -0
  151. package/dist/runtime/port-allocator.d.ts +32 -0
  152. package/dist/runtime/port-allocator.js +96 -0
  153. package/dist/runtime/worktree-pool.d.ts +86 -0
  154. package/dist/runtime/worktree-pool.js +204 -0
  155. package/dist/runtime/worktree.d.ts +25 -0
  156. package/dist/runtime/worktree.js +111 -0
  157. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  158. package/dist/sa-scoring/auto-calibrate.js +107 -0
  159. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  160. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  161. package/dist/sa-scoring/composite.d.ts +107 -0
  162. package/dist/sa-scoring/composite.js +139 -0
  163. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  164. package/dist/sa-scoring/depparse-client.js +187 -0
  165. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  166. package/dist/sa-scoring/did-compiler.js +286 -0
  167. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  168. package/dist/sa-scoring/drift-monitor.js +186 -0
  169. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  170. package/dist/sa-scoring/exemplar-bank.js +154 -0
  171. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  172. package/dist/sa-scoring/feedback-store.js +156 -0
  173. package/dist/sa-scoring/index.d.ts +71 -0
  174. package/dist/sa-scoring/index.js +158 -0
  175. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  176. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  177. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  178. package/dist/sa-scoring/layer2-structural.js +151 -0
  179. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  180. package/dist/sa-scoring/layer3-llm.js +282 -0
  181. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  182. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  183. package/dist/scheduling/burn-down.d.ts +27 -0
  184. package/dist/scheduling/burn-down.js +43 -0
  185. package/dist/scheduling/calibration.d.ts +42 -0
  186. package/dist/scheduling/calibration.js +150 -0
  187. package/dist/scheduling/index.d.ts +8 -0
  188. package/dist/scheduling/index.js +8 -0
  189. package/dist/scheduling/ledger.d.ts +59 -0
  190. package/dist/scheduling/ledger.js +216 -0
  191. package/dist/scheduling/off-peak.d.ts +27 -0
  192. package/dist/scheduling/off-peak.js +112 -0
  193. package/dist/scheduling/schedule-decision.d.ts +39 -0
  194. package/dist/scheduling/schedule-decision.js +89 -0
  195. package/dist/scheduling/tier-analysis.d.ts +47 -0
  196. package/dist/scheduling/tier-analysis.js +81 -0
  197. package/dist/scheduling/types.d.ts +140 -0
  198. package/dist/scheduling/types.js +11 -0
  199. package/dist/shared.d.ts +13 -0
  200. package/dist/shared.js +32 -0
  201. package/dist/state/schema.d.ts +5 -1
  202. package/dist/state/schema.js +223 -1
  203. package/dist/state/store.d.ts +55 -1
  204. package/dist/state/store.js +342 -7
  205. package/dist/state/types.d.ts +139 -0
  206. package/dist/types.d.ts +1 -1
  207. package/dist/validate-agent-output.js +4 -1
  208. package/dist/watch.js +6 -0
  209. package/package.json +3 -2
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ExternalAdapter per RFC §15.2. Operator-declared shell hooks for proprietary or
3
+ * custom branching mechanisms. Pipeline-load REQUIRES `acknowledgeUntrusted: true`
4
+ * because shell hooks execute with full credential scope.
5
+ */
6
+ import { execFile } from 'node:child_process';
7
+ import { promisify } from 'node:util';
8
+ import { DatabaseBranchAdapterError, } from '../types.js';
9
+ const execFileAsync = promisify(execFile);
10
+ export class ExternalAdapter {
11
+ deps;
12
+ name = 'external';
13
+ capabilities = {
14
+ branchCreationLatencyP50Ms: 0, // declared by the hook script's behavior
15
+ maxBranches: 0, // declared by operator config
16
+ supportsMigrations: true,
17
+ supportsReadOnlyBranches: false,
18
+ supportsBranchFromBranch: true,
19
+ multiDatabase: true,
20
+ costModel: 'free',
21
+ };
22
+ constructor(deps = {}) {
23
+ this.deps = deps;
24
+ }
25
+ async validate(pool) {
26
+ const creds = pool.credentials ?? {};
27
+ if (creds.acknowledgeUntrusted !== true) {
28
+ return {
29
+ ok: false,
30
+ error: 'external adapter requires credentials.acknowledgeUntrusted: true. Operator-controlled shell hooks execute with full credential scope; this opt-in is intentional friction.',
31
+ };
32
+ }
33
+ if (typeof creds.allocateCommand !== 'string' || !creds.allocateCommand) {
34
+ return { ok: false, error: 'credentials.allocateCommand (string) is required' };
35
+ }
36
+ if (typeof creds.reclaimCommand !== 'string' || !creds.reclaimCommand) {
37
+ return { ok: false, error: 'credentials.reclaimCommand (string) is required' };
38
+ }
39
+ return { ok: true };
40
+ }
41
+ async allocate(pool, branchKey) {
42
+ const cmd = pool.credentials?.allocateCommand;
43
+ const exec = this.deps.exec ?? ((c, a) => execFileAsync(c, a));
44
+ let stdout;
45
+ try {
46
+ const result = await exec('sh', ['-c', `${cmd} ${branchKey}`]);
47
+ stdout = result.stdout;
48
+ }
49
+ catch (err) {
50
+ throw new DatabaseBranchAdapterError(`external allocate hook failed for branch ${branchKey}: ${err.message}`, this.name, branchKey, err);
51
+ }
52
+ const connectionString = stdout.trim();
53
+ if (!connectionString) {
54
+ throw new DatabaseBranchAdapterError(`external allocate hook produced empty stdout (expected connection string)`, this.name, branchKey);
55
+ }
56
+ return {
57
+ branchKey,
58
+ connectionString,
59
+ createdAt: new Date(),
60
+ upstream: pool.upstream.branchFrom ?? 'external',
61
+ metadata: { hook: 'external' },
62
+ };
63
+ }
64
+ async reclaim(handle) {
65
+ // The pool isn't passed here; the orchestrator MUST track (handle, pool) pairs and
66
+ // call this via the dispatcher. For test purposes, callers that use ExternalAdapter
67
+ // directly must provide their own dispatcher that supplies the reclaim command.
68
+ void handle;
69
+ }
70
+ async list(_pool) {
71
+ // External hooks don't expose a list mechanism; orchestrator tracks active branches
72
+ // in its own state.
73
+ return [];
74
+ }
75
+ async isAvailable(pool) {
76
+ const v = await this.validate(pool);
77
+ return v.ok;
78
+ }
79
+ }
80
+ //# sourceMappingURL=external.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * NeonAdapter per RFC §15.2. Wraps Neon REST API for branch lifecycle. Phase 6 ships
3
+ * the adapter shell + capability matrix + validation; the actual REST calls (allocate,
4
+ * reclaim, list) are stubbed until end-to-end integration testing against a real Neon
5
+ * sandbox project lands as a follow-up.
6
+ *
7
+ * Operators wire this up via:
8
+ * credentials:
9
+ * apiTokenEnv: NEON_API_TOKEN
10
+ * projectId: prj_abc123
11
+ */
12
+ import { type DatabaseBranchAdapter, type DatabaseBranchCapabilities, type DatabaseBranchHandle, type DatabaseAdapterName, type ResolvedDatabaseBranchPool, type ValidationResult } from '../types.js';
13
+ export interface NeonAdapterDeps {
14
+ env?: NodeJS.ProcessEnv;
15
+ /** Override for tests; replaces the actual fetch calls. */
16
+ api?: {
17
+ createBranch: (projectId: string, branchName: string, parentBranch: string) => Promise<{
18
+ branchId: string;
19
+ connectionString: string;
20
+ }>;
21
+ deleteBranch: (projectId: string, branchId: string) => Promise<void>;
22
+ listBranches: (projectId: string) => Promise<Array<{
23
+ branchId: string;
24
+ branchName: string;
25
+ parent: string;
26
+ createdAt: string;
27
+ }>>;
28
+ };
29
+ }
30
+ export declare class NeonAdapter implements DatabaseBranchAdapter {
31
+ private readonly deps;
32
+ readonly name: DatabaseAdapterName;
33
+ readonly capabilities: DatabaseBranchCapabilities;
34
+ constructor(deps?: NeonAdapterDeps);
35
+ validate(pool: ResolvedDatabaseBranchPool): Promise<ValidationResult>;
36
+ allocate(pool: ResolvedDatabaseBranchPool, branchKey: string): Promise<DatabaseBranchHandle>;
37
+ reclaim(handle: DatabaseBranchHandle): Promise<void>;
38
+ list(pool: ResolvedDatabaseBranchPool): Promise<DatabaseBranchHandle[]>;
39
+ isAvailable(pool: ResolvedDatabaseBranchPool): Promise<boolean>;
40
+ }
41
+ //# sourceMappingURL=neon.d.ts.map
@@ -0,0 +1,98 @@
1
+ /**
2
+ * NeonAdapter per RFC §15.2. Wraps Neon REST API for branch lifecycle. Phase 6 ships
3
+ * the adapter shell + capability matrix + validation; the actual REST calls (allocate,
4
+ * reclaim, list) are stubbed until end-to-end integration testing against a real Neon
5
+ * sandbox project lands as a follow-up.
6
+ *
7
+ * Operators wire this up via:
8
+ * credentials:
9
+ * apiTokenEnv: NEON_API_TOKEN
10
+ * projectId: prj_abc123
11
+ */
12
+ import { DatabaseBranchAdapterError, } from '../types.js';
13
+ export class NeonAdapter {
14
+ deps;
15
+ name = 'neon';
16
+ capabilities = {
17
+ branchCreationLatencyP50Ms: 1500,
18
+ maxBranches: 5000,
19
+ supportsMigrations: true,
20
+ supportsReadOnlyBranches: true,
21
+ supportsBranchFromBranch: true,
22
+ multiDatabase: false,
23
+ costModel: 'per-branch-storage',
24
+ };
25
+ constructor(deps = {}) {
26
+ this.deps = deps;
27
+ }
28
+ async validate(pool) {
29
+ const env = this.deps.env ?? process.env;
30
+ const creds = pool.credentials ?? {};
31
+ const apiTokenEnv = creds.apiTokenEnv;
32
+ const projectId = creds.projectId;
33
+ if (!apiTokenEnv) {
34
+ return { ok: false, error: 'credentials.apiTokenEnv is required for neon adapter' };
35
+ }
36
+ if (!env[apiTokenEnv]) {
37
+ return {
38
+ ok: false,
39
+ error: `${apiTokenEnv} is not set in env; cannot authenticate against Neon API`,
40
+ };
41
+ }
42
+ if (!projectId) {
43
+ return { ok: false, error: 'credentials.projectId is required for neon adapter' };
44
+ }
45
+ if (!pool.upstream.branchFrom) {
46
+ return {
47
+ ok: false,
48
+ error: 'upstream.branchFrom is required for neon (named branch to fork from)',
49
+ };
50
+ }
51
+ return { ok: true };
52
+ }
53
+ async allocate(pool, branchKey) {
54
+ if (!this.deps.api) {
55
+ throw new DatabaseBranchAdapterError('NeonAdapter.allocate is not wired against the live Neon REST API yet (Phase 6 follow-up). ' +
56
+ 'Tests should inject deps.api; production deployments require the integration test pass before use.', this.name, branchKey);
57
+ }
58
+ const projectId = pool.credentials?.projectId;
59
+ const parent = pool.upstream.branchFrom;
60
+ const result = await this.deps.api.createBranch(projectId, branchKey, parent);
61
+ return {
62
+ branchKey,
63
+ connectionString: result.connectionString,
64
+ createdAt: new Date(),
65
+ upstream: parent,
66
+ metadata: { branchId: result.branchId, projectId },
67
+ };
68
+ }
69
+ async reclaim(handle) {
70
+ if (!this.deps.api) {
71
+ throw new DatabaseBranchAdapterError('NeonAdapter.reclaim not wired (Phase 6 follow-up)', this.name, handle.branchKey);
72
+ }
73
+ const projectId = handle.metadata.projectId;
74
+ const branchId = handle.metadata.branchId;
75
+ if (!projectId || !branchId) {
76
+ throw new DatabaseBranchAdapterError(`handle missing projectId/branchId in metadata: ${JSON.stringify(handle.metadata)}`, this.name, handle.branchKey);
77
+ }
78
+ await this.deps.api.deleteBranch(projectId, branchId);
79
+ }
80
+ async list(pool) {
81
+ if (!this.deps.api)
82
+ return [];
83
+ const projectId = pool.credentials?.projectId;
84
+ const branches = await this.deps.api.listBranches(projectId);
85
+ return branches.map((b) => ({
86
+ branchKey: b.branchName,
87
+ connectionString: '<masked — re-fetch via API>',
88
+ createdAt: new Date(b.createdAt),
89
+ upstream: b.parent,
90
+ metadata: { projectId, branchId: b.branchId },
91
+ }));
92
+ }
93
+ async isAvailable(pool) {
94
+ const v = await this.validate(pool);
95
+ return v.ok;
96
+ }
97
+ }
98
+ //# sourceMappingURL=neon.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * PgSnapshotRestoreAdapter per RFC §15.2. For vanilla Postgres or AWS RDS — uses
3
+ * `pg_dump` + `pg_restore` for vanilla, or RDS snapshot/restore APIs for AWS.
4
+ *
5
+ * Phase 6 ships the adapter shell with capability matrix + validation. End-to-end
6
+ * integration test against local Postgres in CI is a follow-up.
7
+ */
8
+ import { type DatabaseBranchAdapter, type DatabaseBranchCapabilities, type DatabaseBranchHandle, type DatabaseAdapterName, type ResolvedDatabaseBranchPool, type ValidationResult } from '../types.js';
9
+ export interface PgSnapshotRestoreAdapterDeps {
10
+ env?: NodeJS.ProcessEnv;
11
+ /** Override for tests; mocks the dump+restore execution. */
12
+ exec?: (cmd: string, args: string[], env?: NodeJS.ProcessEnv) => Promise<{
13
+ stdout: string;
14
+ stderr: string;
15
+ }>;
16
+ }
17
+ export declare class PgSnapshotRestoreAdapter implements DatabaseBranchAdapter {
18
+ private readonly deps;
19
+ readonly name: DatabaseAdapterName;
20
+ readonly capabilities: DatabaseBranchCapabilities;
21
+ constructor(deps?: PgSnapshotRestoreAdapterDeps);
22
+ validate(pool: ResolvedDatabaseBranchPool): Promise<ValidationResult>;
23
+ allocate(pool: ResolvedDatabaseBranchPool, branchKey: string): Promise<DatabaseBranchHandle>;
24
+ reclaim(handle: DatabaseBranchHandle): Promise<void>;
25
+ list(_pool: ResolvedDatabaseBranchPool): Promise<DatabaseBranchHandle[]>;
26
+ isAvailable(pool: ResolvedDatabaseBranchPool): Promise<boolean>;
27
+ }
28
+ //# sourceMappingURL=pg-snapshot-restore.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * PgSnapshotRestoreAdapter per RFC §15.2. For vanilla Postgres or AWS RDS — uses
3
+ * `pg_dump` + `pg_restore` for vanilla, or RDS snapshot/restore APIs for AWS.
4
+ *
5
+ * Phase 6 ships the adapter shell with capability matrix + validation. End-to-end
6
+ * integration test against local Postgres in CI is a follow-up.
7
+ */
8
+ import { DatabaseBranchAdapterError, } from '../types.js';
9
+ export class PgSnapshotRestoreAdapter {
10
+ deps;
11
+ name = 'pg-snapshot-restore';
12
+ capabilities = {
13
+ branchCreationLatencyP50Ms: 30_000,
14
+ maxBranches: 100,
15
+ supportsMigrations: true,
16
+ supportsReadOnlyBranches: false,
17
+ supportsBranchFromBranch: false,
18
+ multiDatabase: false,
19
+ costModel: 'per-snapshot',
20
+ };
21
+ constructor(deps = {}) {
22
+ this.deps = deps;
23
+ }
24
+ async validate(pool) {
25
+ const env = this.deps.env ?? process.env;
26
+ const creds = pool.credentials ?? {};
27
+ const adminEnv = creds.adminConnectionStringEnv;
28
+ const storageVolume = creds.storageVolume;
29
+ if (!adminEnv) {
30
+ return {
31
+ ok: false,
32
+ error: 'credentials.adminConnectionStringEnv is required (privileged user with CREATEDB)',
33
+ };
34
+ }
35
+ if (!env[adminEnv]) {
36
+ return { ok: false, error: `${adminEnv} not set in env` };
37
+ }
38
+ if (!storageVolume) {
39
+ return {
40
+ ok: false,
41
+ error: 'credentials.storageVolume is required (where snapshots are restored)',
42
+ };
43
+ }
44
+ return { ok: true };
45
+ }
46
+ async allocate(pool, branchKey) {
47
+ if (!this.deps.exec) {
48
+ throw new DatabaseBranchAdapterError('PgSnapshotRestoreAdapter.allocate not wired (Phase 6 follow-up against fixture Postgres in CI)', this.name, branchKey);
49
+ }
50
+ // Placeholder — actual implementation: pg_dump from upstream, createdb branch_<key>, pg_restore into branch.
51
+ throw new DatabaseBranchAdapterError('PgSnapshotRestoreAdapter integration pending', this.name, branchKey);
52
+ }
53
+ async reclaim(handle) {
54
+ if (!this.deps.exec)
55
+ return;
56
+ // Placeholder — actual: dropdb branch_<key>
57
+ void handle;
58
+ }
59
+ async list(_pool) {
60
+ // Placeholder — actual: query pg_database for branch_-prefixed databases.
61
+ return [];
62
+ }
63
+ async isAvailable(pool) {
64
+ const v = await this.validate(pool);
65
+ return v.ok;
66
+ }
67
+ }
68
+ //# sourceMappingURL=pg-snapshot-restore.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * SqliteCopyAdapter per RFC §15.2. Copies the upstream `.sqlite` file to a per-branch
3
+ * file in the worktree. Hardlinks for read-only branches; full copy for write/migrate.
4
+ *
5
+ * Connection-string format (the orchestrator's existing convention): plain absolute
6
+ * file path, optionally prefixed with `file://`.
7
+ */
8
+ import { type DatabaseBranchAdapter, type DatabaseBranchCapabilities, type DatabaseBranchHandle, type DatabaseAdapterName, type ResolvedDatabaseBranchPool, type ValidationResult } from '../types.js';
9
+ export interface SqliteCopyAdapterDeps {
10
+ env?: NodeJS.ProcessEnv;
11
+ /** Override the per-branch storage root for tests. Defaults to `<cwd>/.ai-sdlc/db`. */
12
+ storageRoot?: string;
13
+ }
14
+ export declare class SqliteCopyAdapter implements DatabaseBranchAdapter {
15
+ private readonly deps;
16
+ readonly name: DatabaseAdapterName;
17
+ readonly capabilities: DatabaseBranchCapabilities;
18
+ constructor(deps?: SqliteCopyAdapterDeps);
19
+ private storageRootFor;
20
+ validate(pool: ResolvedDatabaseBranchPool): Promise<ValidationResult>;
21
+ allocate(pool: ResolvedDatabaseBranchPool, branchKey: string): Promise<DatabaseBranchHandle>;
22
+ /**
23
+ * Read-only branch via hardlink (saves disk space when many readers share the same
24
+ * upstream snapshot). Caller is responsible for never opening the resulting file
25
+ * for write — SQLite will happily corrupt the hardlinked upstream if treated as RW.
26
+ */
27
+ allocateReadOnly(pool: ResolvedDatabaseBranchPool, branchKey: string): Promise<DatabaseBranchHandle>;
28
+ reclaim(handle: DatabaseBranchHandle): Promise<void>;
29
+ list(pool: ResolvedDatabaseBranchPool): Promise<DatabaseBranchHandle[]>;
30
+ isAvailable(pool: ResolvedDatabaseBranchPool): Promise<boolean>;
31
+ }
32
+ //# sourceMappingURL=sqlite-copy.d.ts.map
@@ -0,0 +1,145 @@
1
+ /**
2
+ * SqliteCopyAdapter per RFC §15.2. Copies the upstream `.sqlite` file to a per-branch
3
+ * file in the worktree. Hardlinks for read-only branches; full copy for write/migrate.
4
+ *
5
+ * Connection-string format (the orchestrator's existing convention): plain absolute
6
+ * file path, optionally prefixed with `file://`.
7
+ */
8
+ import { copyFile, link, mkdir, readdir, rm, stat } from 'node:fs/promises';
9
+ import { dirname, join } from 'node:path';
10
+ import { DatabaseBranchAdapterError, } from '../types.js';
11
+ export class SqliteCopyAdapter {
12
+ deps;
13
+ name = 'sqlite-copy';
14
+ capabilities = {
15
+ branchCreationLatencyP50Ms: 50,
16
+ maxBranches: 10_000,
17
+ supportsMigrations: true,
18
+ supportsReadOnlyBranches: true,
19
+ supportsBranchFromBranch: true,
20
+ multiDatabase: false,
21
+ costModel: 'free',
22
+ };
23
+ constructor(deps = {}) {
24
+ this.deps = deps;
25
+ }
26
+ storageRootFor(_pool) {
27
+ return this.deps.storageRoot ?? join(process.cwd(), '.ai-sdlc', 'db');
28
+ }
29
+ async validate(pool) {
30
+ const env = this.deps.env ?? process.env;
31
+ const upstreamPath = env[pool.upstream.connectionStringEnv];
32
+ if (!upstreamPath) {
33
+ return {
34
+ ok: false,
35
+ error: `${pool.upstream.connectionStringEnv} is not set in env; cannot locate upstream SQLite file`,
36
+ };
37
+ }
38
+ try {
39
+ const s = await stat(stripFileScheme(upstreamPath));
40
+ if (!s.isFile()) {
41
+ return { ok: false, error: `upstream SQLite path is not a file: ${upstreamPath}` };
42
+ }
43
+ }
44
+ catch {
45
+ return { ok: false, error: `upstream SQLite file not found: ${upstreamPath}` };
46
+ }
47
+ return { ok: true };
48
+ }
49
+ async allocate(pool, branchKey) {
50
+ const env = this.deps.env ?? process.env;
51
+ const upstreamRaw = env[pool.upstream.connectionStringEnv];
52
+ if (!upstreamRaw) {
53
+ throw new DatabaseBranchAdapterError(`${pool.upstream.connectionStringEnv} not set`, this.name, branchKey);
54
+ }
55
+ const upstreamPath = stripFileScheme(upstreamRaw);
56
+ const branchPath = join(this.storageRootFor(pool), `${branchKey}.sqlite`);
57
+ await mkdir(dirname(branchPath), { recursive: true });
58
+ try {
59
+ await copyFile(upstreamPath, branchPath);
60
+ }
61
+ catch (err) {
62
+ throw new DatabaseBranchAdapterError(`failed to copy SQLite database for branch '${branchKey}': ${err.message}`, this.name, branchKey, err);
63
+ }
64
+ return {
65
+ branchKey,
66
+ connectionString: `file://${branchPath}`,
67
+ createdAt: new Date(),
68
+ upstream: upstreamPath,
69
+ metadata: { branchPath, mode: 'copy' },
70
+ };
71
+ }
72
+ /**
73
+ * Read-only branch via hardlink (saves disk space when many readers share the same
74
+ * upstream snapshot). Caller is responsible for never opening the resulting file
75
+ * for write — SQLite will happily corrupt the hardlinked upstream if treated as RW.
76
+ */
77
+ async allocateReadOnly(pool, branchKey) {
78
+ const env = this.deps.env ?? process.env;
79
+ const upstreamRaw = env[pool.upstream.connectionStringEnv];
80
+ if (!upstreamRaw) {
81
+ throw new DatabaseBranchAdapterError(`${pool.upstream.connectionStringEnv} not set`, this.name, branchKey);
82
+ }
83
+ const upstreamPath = stripFileScheme(upstreamRaw);
84
+ const branchPath = join(this.storageRootFor(pool), `${branchKey}.ro.sqlite`);
85
+ await mkdir(dirname(branchPath), { recursive: true });
86
+ try {
87
+ await link(upstreamPath, branchPath);
88
+ }
89
+ catch {
90
+ // Fall back to a copy if hardlink fails (e.g., across filesystems).
91
+ await copyFile(upstreamPath, branchPath);
92
+ }
93
+ return {
94
+ branchKey,
95
+ connectionString: `file://${branchPath}?mode=ro`,
96
+ createdAt: new Date(),
97
+ upstream: upstreamPath,
98
+ metadata: { branchPath, mode: 'hardlink-ro' },
99
+ };
100
+ }
101
+ async reclaim(handle) {
102
+ const path = handle.metadata.branchPath;
103
+ if (!path)
104
+ return;
105
+ await rm(path, { force: true });
106
+ }
107
+ async list(pool) {
108
+ const root = this.storageRootFor(pool);
109
+ let entries;
110
+ try {
111
+ entries = await readdir(root);
112
+ }
113
+ catch (err) {
114
+ if (err.code === 'ENOENT')
115
+ return [];
116
+ throw err;
117
+ }
118
+ const handles = [];
119
+ for (const entry of entries) {
120
+ if (!entry.endsWith('.sqlite'))
121
+ continue;
122
+ const branchPath = join(root, entry);
123
+ const s = await stat(branchPath);
124
+ const branchKey = entry.replace(/\.(?:ro\.)?sqlite$/, '');
125
+ handles.push({
126
+ branchKey,
127
+ connectionString: `file://${branchPath}`,
128
+ createdAt: new Date(s.birthtimeMs || s.mtimeMs),
129
+ upstream: 'unknown',
130
+ metadata: { branchPath, mode: entry.includes('.ro.') ? 'hardlink-ro' : 'copy' },
131
+ });
132
+ }
133
+ return handles;
134
+ }
135
+ async isAvailable(pool) {
136
+ const validation = await this.validate(pool);
137
+ return validation.ok;
138
+ }
139
+ }
140
+ function stripFileScheme(path) {
141
+ if (path.startsWith('file://'))
142
+ return path.slice('file://'.length);
143
+ return path;
144
+ }
145
+ //# sourceMappingURL=sqlite-copy.js.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Connection-string injection per RFC §15.6. The agent sees the per-worktree branch
3
+ * via the same env-var name (`DATABASE_URL` etc.) the application expects. Adapters
4
+ * provide the rewritten string; this module derives optional component env vars
5
+ * (PGHOST, PGDATABASE, etc.) from it.
6
+ */
7
+ import type { DatabaseBranchHandle, ResolvedDatabaseBranchPool } from './types.js';
8
+ export interface InjectedEnvOverrides {
9
+ [envName: string]: string;
10
+ }
11
+ /**
12
+ * Build the env-var overlay for a single (handle, pool) pair. The agent's process
13
+ * env is the orchestrator's env merged with this overlay.
14
+ *
15
+ * Component derivation supports postgres:// connection strings — for non-Postgres
16
+ * URLs (e.g., sqlite file paths), additionalEnvs is silently ignored.
17
+ */
18
+ export declare function buildInjectionOverrides(handle: DatabaseBranchHandle, pool: ResolvedDatabaseBranchPool): InjectedEnvOverrides;
19
+ interface ConnectionComponents {
20
+ host: string;
21
+ port: string;
22
+ database: string;
23
+ user: string;
24
+ password: string;
25
+ }
26
+ export declare function parsePostgresUrl(url: string): ConnectionComponents | null;
27
+ /**
28
+ * Mask a connection string for safe logging. Replaces the password segment with
29
+ * `****`. Per RFC §15.1 the connection string itself MUST NEVER appear in any
30
+ * observability output — this helper exists for adapter error messages where
31
+ * the URL form might leak.
32
+ */
33
+ export declare function maskConnectionString(url: string): string;
34
+ export {};
35
+ //# sourceMappingURL=connection-injection.d.ts.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Connection-string injection per RFC §15.6. The agent sees the per-worktree branch
3
+ * via the same env-var name (`DATABASE_URL` etc.) the application expects. Adapters
4
+ * provide the rewritten string; this module derives optional component env vars
5
+ * (PGHOST, PGDATABASE, etc.) from it.
6
+ */
7
+ /**
8
+ * Build the env-var overlay for a single (handle, pool) pair. The agent's process
9
+ * env is the orchestrator's env merged with this overlay.
10
+ *
11
+ * Component derivation supports postgres:// connection strings — for non-Postgres
12
+ * URLs (e.g., sqlite file paths), additionalEnvs is silently ignored.
13
+ */
14
+ export function buildInjectionOverrides(handle, pool) {
15
+ const overrides = {};
16
+ overrides[pool.injection.targetEnv] = handle.connectionString;
17
+ if (pool.injection.additionalEnvs && pool.injection.additionalEnvs.length > 0) {
18
+ const components = parsePostgresUrl(handle.connectionString);
19
+ if (components) {
20
+ for (const envName of pool.injection.additionalEnvs) {
21
+ const mapped = mapEnvNameToComponent(envName, components);
22
+ if (mapped !== null)
23
+ overrides[envName] = mapped;
24
+ }
25
+ }
26
+ }
27
+ return overrides;
28
+ }
29
+ export function parsePostgresUrl(url) {
30
+ // postgres://[user[:password]@]host[:port][/database][?...]
31
+ // Also handles postgresql:// scheme.
32
+ try {
33
+ const parsed = new URL(url);
34
+ if (!/^postgres(?:ql)?:$/.test(parsed.protocol))
35
+ return null;
36
+ return {
37
+ host: parsed.hostname,
38
+ port: parsed.port || '5432',
39
+ database: parsed.pathname.replace(/^\//, ''),
40
+ user: decodeURIComponent(parsed.username),
41
+ password: decodeURIComponent(parsed.password),
42
+ };
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ function mapEnvNameToComponent(name, c) {
49
+ const upper = name.toUpperCase();
50
+ switch (upper) {
51
+ case 'PGHOST':
52
+ case 'POSTGRES_HOST':
53
+ case 'DB_HOST':
54
+ return c.host;
55
+ case 'PGPORT':
56
+ case 'POSTGRES_PORT':
57
+ case 'DB_PORT':
58
+ return c.port;
59
+ case 'PGDATABASE':
60
+ case 'POSTGRES_DB':
61
+ case 'DB_NAME':
62
+ return c.database;
63
+ case 'PGUSER':
64
+ case 'POSTGRES_USER':
65
+ case 'DB_USER':
66
+ return c.user;
67
+ case 'PGPASSWORD':
68
+ case 'POSTGRES_PASSWORD':
69
+ case 'DB_PASSWORD':
70
+ return c.password;
71
+ default:
72
+ return null;
73
+ }
74
+ }
75
+ /**
76
+ * Mask a connection string for safe logging. Replaces the password segment with
77
+ * `****`. Per RFC §15.1 the connection string itself MUST NEVER appear in any
78
+ * observability output — this helper exists for adapter error messages where
79
+ * the URL form might leak.
80
+ */
81
+ export function maskConnectionString(url) {
82
+ try {
83
+ const parsed = new URL(url);
84
+ if (parsed.password) {
85
+ parsed.password = '****';
86
+ }
87
+ return parsed.toString();
88
+ }
89
+ catch {
90
+ return '<unparseable connection string>';
91
+ }
92
+ }
93
+ //# sourceMappingURL=connection-injection.js.map
@@ -0,0 +1,12 @@
1
+ export * from './types.js';
2
+ export { DatabaseAdapterRegistry, UnknownDatabaseAdapterError } from './registry.js';
3
+ export { buildInjectionOverrides, parsePostgresUrl, maskConnectionString, type InjectedEnvOverrides, } from './connection-injection.js';
4
+ export { enforceTopologyGuard, buildMigrationDivergedEvent, type KnownInFlightBranches, type MigrationDivergedEvent, } from './topology.js';
5
+ export { SqliteCopyAdapter, type SqliteCopyAdapterDeps } from './adapters/sqlite-copy.js';
6
+ export { NeonAdapter, type NeonAdapterDeps } from './adapters/neon.js';
7
+ export { PgSnapshotRestoreAdapter, type PgSnapshotRestoreAdapterDeps, } from './adapters/pg-snapshot-restore.js';
8
+ export { ExternalAdapter, type ExternalAdapterDeps } from './adapters/external.js';
9
+ import { DatabaseAdapterRegistry } from './registry.js';
10
+ /** Pre-populated registry with the four shipped v1 adapters. */
11
+ export declare function createDefaultDatabaseAdapterRegistry(): DatabaseAdapterRegistry;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,23 @@
1
+ export * from './types.js';
2
+ export { DatabaseAdapterRegistry, UnknownDatabaseAdapterError } from './registry.js';
3
+ export { buildInjectionOverrides, parsePostgresUrl, maskConnectionString, } from './connection-injection.js';
4
+ export { enforceTopologyGuard, buildMigrationDivergedEvent, } from './topology.js';
5
+ export { SqliteCopyAdapter } from './adapters/sqlite-copy.js';
6
+ export { NeonAdapter } from './adapters/neon.js';
7
+ export { PgSnapshotRestoreAdapter, } from './adapters/pg-snapshot-restore.js';
8
+ export { ExternalAdapter } from './adapters/external.js';
9
+ import { DatabaseAdapterRegistry } from './registry.js';
10
+ import { SqliteCopyAdapter } from './adapters/sqlite-copy.js';
11
+ import { NeonAdapter } from './adapters/neon.js';
12
+ import { PgSnapshotRestoreAdapter } from './adapters/pg-snapshot-restore.js';
13
+ import { ExternalAdapter } from './adapters/external.js';
14
+ /** Pre-populated registry with the four shipped v1 adapters. */
15
+ export function createDefaultDatabaseAdapterRegistry() {
16
+ const reg = new DatabaseAdapterRegistry();
17
+ reg.register(new SqliteCopyAdapter());
18
+ reg.register(new NeonAdapter());
19
+ reg.register(new PgSnapshotRestoreAdapter());
20
+ reg.register(new ExternalAdapter());
21
+ return reg;
22
+ }
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ import type { DatabaseAdapterName, DatabaseBranchAdapter } from './types.js';
2
+ export declare class UnknownDatabaseAdapterError extends Error {
3
+ readonly name: string;
4
+ constructor(name: string);
5
+ }
6
+ export declare class DatabaseAdapterRegistry {
7
+ private readonly adapters;
8
+ register(adapter: DatabaseBranchAdapter): void;
9
+ get(name: string): DatabaseBranchAdapter;
10
+ has(name: string): boolean;
11
+ list(): DatabaseAdapterName[];
12
+ }
13
+ //# sourceMappingURL=registry.d.ts.map