diff --git a/.claude/skills/swamp-model/SKILL.md b/.claude/skills/swamp-model/SKILL.md index faa99a20..9fa7a402 100644 --- a/.claude/skills/swamp-model/SKILL.md +++ b/.claude/skills/swamp-model/SKILL.md @@ -12,19 +12,19 @@ machine-readable output. Swamp uses a dual-layer architecture: -- **Data directory (`/data/`)** - Internal storage organized by entity type +- **Data directory (`/.data/`)** - Internal storage organized by entity type - **Logical views (`/models/`)** - Human-friendly symlinked directories The `/models/` directory provides convenient exploration of each model: ``` /models/{model-name}/ - input.yaml → ../data/inputs/{type}/{id}.yaml - resource.yaml → ../data/resources/{type}/{id}.yaml - data.yaml → ../data/data/{type}/{id}.yaml - outputs/ → ../data/outputs/{type}/{id}/ - logs/ → ../data/logs/{type}/{id}/ - files/ → ../data/files/{type}/{id}/ + input.yaml → ../.data/inputs/{type}/{id}.yaml + resource.yaml → ../.data/resources/{type}/{id}.yaml + data.yaml → ../.data/data/{type}/{id}.yaml + outputs/ → ../.data/outputs/{type}/{id}/ + logs/ → ../.data/logs/{type}/{id}/ + files/ → ../.data/files/{type}/{id}/ ``` This structure is maintained automatically. Use `swamp repo index` to rebuild if diff --git a/.claude/skills/swamp-workflow/SKILL.md b/.claude/skills/swamp-workflow/SKILL.md index 2055bb97..bda838b1 100644 --- a/.claude/skills/swamp-workflow/SKILL.md +++ b/.claude/skills/swamp-workflow/SKILL.md @@ -12,18 +12,18 @@ machine-readable output. Swamp uses a dual-layer architecture: -- **Data directory (`/data/`)** - Internal storage organized by entity type +- **Data directory (`/.data/`)** - Internal storage organized by entity type - **Logical views (`/workflows/`)** - Human-friendly symlinked directories The `/workflows/` directory provides convenient exploration of each workflow: ``` /workflows/{workflow-name}/ - workflow.yaml → ../data/workflows/{id}.yaml + workflow.yaml → ../.data/workflows/{id}.yaml runs/ latest → {most-recent-run}/ {timestamp}/ - run.yaml → ../data/workflow-runs/{id}/{run-id}.yaml + run.yaml → ../.data/workflow-runs/{id}/{run-id}.yaml ``` This structure is maintained automatically. Use `swamp repo index` to rebuild if diff --git a/.gitignore b/.gitignore index f90ed6ad..87e4c6dc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,13 +2,13 @@ dev-logs/* swamp # Generated/runtime data (ignored) -data/data/ -data/outputs/ -data/workflow-runs/ -data/inputs-evaluated/ -data/workflows-evaluated/ -data/logs/ -data/files/ +.data/data/ +.data/outputs/ +.data/workflow-runs/ +.data/inputs-evaluated/ +.data/workflows-evaluated/ +.data/logs/ +.data/files/ test-repo/ experiments/webapp/frontend/node_modules/ diff --git a/design/agent.md b/design/agent.md index 3e37e5c1..fd1504e6 100644 --- a/design/agent.md +++ b/design/agent.md @@ -22,7 +22,7 @@ are the recommended way to explore and understand the repository structure. ### Data Directory (Direct Access) -The `/data/` directory contains the internal storage format. Agents can access +The `/.data/` directory contains the internal storage format. Agents can access it directly when needed, but the layout reflects swamp's internal architecture rather than user-facing concerns. diff --git a/design/expressions.md b/design/expressions.md index 340c7aba..7dd8e667 100644 --- a/design/expressions.md +++ b/design/expressions.md @@ -99,11 +99,11 @@ registering custom types, functions, etc in their swamp repo. When loading the YAML, first parse the CEL expressions. Then take the data structures they emit and embed them in the data structure. Write those to a -directory in the repository called `/data/inputs-evaluated/` whose structure is -the same as `/data/inputs/`. This directory should be in a swamp repo's +directory in the repository called `/.data/inputs-evaluated/` whose structure is +the same as `/.data/inputs/`. This directory should be in a swamp repo's .gitignore file. -The same is true for `/data/workflows-evaluated/`, and it should also be in +The same is true for `/.data/workflows-evaluated/`, and it should also be in .gitignore. These evaluated directories are internal working directories in the data layer, diff --git a/design/models.md b/design/models.md index 2ee40c71..2f1b678f 100644 --- a/design/models.md +++ b/design/models.md @@ -40,10 +40,10 @@ A model can migrate its inputs and resources from one version to the next. ## Inputs -Inputs are specified as YAML files that live in the `/data/inputs/` directory of -a repository, underneath the normalized type as a directory. The file name is +Inputs are specified as YAML files that live in the `/.data/inputs/` directory +of a repository, underneath the normalized type as a directory. The file name is `${id}.yaml`. For example, -`data/inputs/aws/ec2/vpc/fc7fd41e-ae16-4b31-b57a-86de716e3ece.yaml`. +`.data/inputs/aws/ec2/vpc/fc7fd41e-ae16-4b31-b57a-86de716e3ece.yaml`. The valid shape of an input is specified with a Zod 4 schema. @@ -79,7 +79,7 @@ method. ### Logs A method may produce 0..\* log artifacts. They have a name, can have lines -streamed to them, and by default are stored in `/data/logs/` in the repository +streamed to them, and by default are stored in `/.data/logs/` in the repository underneath the normalized model type as a directory. Logs are named like `{model-id}-{method name}-{log name}-{timestamp}.log`. @@ -91,17 +91,17 @@ artifact named 'k8slog'. The stream of log output should have an event emitter attached to it, so we can stream logs in real time. -By default, the `/data/logs/` directory is not stored in git. +By default, the `/.data/logs/` directory is not stored in git. ### Files A method may store 0..\* file artifacts. They have a name, and can be written to -directly. By default they will be stored in the `/data/files/` directory of the +directly. By default they will be stored in the `/.data/files/` directory of the repository underneath the normalized model type as a directory plus the model ID and method name. For example -`data/files/aws/s3/bucket/{model-id}/{method-name}/{filename}`. +`.data/files/aws/s3/bucket/{model-id}/{method-name}/{filename}`. -By default, the `/data/files/` directory is not stored in git. +By default, the `/.data/files/` directory is not stored in git. ## Resource @@ -109,9 +109,9 @@ Resource artifacts are used to track data about an external resource that should be persisted over time (for example, the data about an AWS cloud resource). A method may produce 0..1 resource as specified as YAML files that live in the -`/data/resources/` directory of a repository, underneath the normalized type as +`/.data/resources/` directory of a repository, underneath the normalized type as a directory. The file name is `${id}.yaml`. For example, -`data/resources/aws/ec2/vpc/fc7fd41e-ae16-4b31-b57a-86de716e3ece.yaml`. +`.data/resources/aws/ec2/vpc/fc7fd41e-ae16-4b31-b57a-86de716e3ece.yaml`. The valid shape of a resource is specified with a Zod 4 schema. @@ -122,7 +122,7 @@ Resources are tracked in git. Data artifacts are pure data objects that are not persisted over time in git. A method may produce 0..1 data artifacts, stored as YAML files in the -`/data/data/` directory of a repository, underneath the normalized type as a +`/.data/data/` directory of a repository, underneath the normalized type as a directory. The file name is `${id}.yaml`. The valid shape of data is specified with a Zod 4 schema. @@ -151,11 +151,11 @@ Use **data artifacts** when: ## Output Each method invocation produces an output record, which gets tracked in the -`/data/outputs/` directory of a repository (which should not be tracked in git). -The output record should track the state of the method execution, and the list -of artifacts produced by the method. It should track state as the method +`/.data/outputs/` directory of a repository (which should not be tracked in +git). The output record should track the state of the method execution, and the +list of artifacts produced by the method. It should track state as the method executes. It should be structured as -`/data/outputs/{normalized-type}/{method}/{model-id}-{timestamp}.yaml`. +`/.data/outputs/{normalized-type}/{method}/{model-id}-{timestamp}.yaml`. ## Logical Views @@ -166,13 +166,13 @@ provides human/agent-friendly exploration of models by name. ``` /models/{model-name}/ - input.yaml → symlink to /data/inputs/{type}/{id}.yaml - resource.yaml → symlink to /data/resources/{type}/{id}.yaml - data.yaml → symlink to /data/data/{type}/{id}.yaml - logs/ → symlink to /data/logs/{type}/{id}/ - files/ → symlink to /data/files/{type}/{id}/ + input.yaml → symlink to /.data/inputs/{type}/{id}.yaml + resource.yaml → symlink to /.data/resources/{type}/{id}.yaml + data.yaml → symlink to /.data/data/{type}/{id}.yaml + logs/ → symlink to /.data/logs/{type}/{id}/ + files/ → symlink to /.data/files/{type}/{id}/ outputs/ - {method}/ → symlinks to /data/outputs/{type}/{method}/{id}-*.yaml + {method}/ → symlinks to /.data/outputs/{type}/{method}/{id}-*.yaml ``` This structure allows exploring all artifacts for a model in one place, using diff --git a/design/repo.md b/design/repo.md index 918268f9..8c1104bf 100644 --- a/design/repo.md +++ b/design/repo.md @@ -115,24 +115,24 @@ The RepoIndexService maintains two primary logical views: ``` /models/{model-name}/ - input.yaml → /data/inputs/{type}/{id}.yaml - resource.yaml → /data/resources/{type}/{id}.yaml - data.yaml → /data/data/{type}/{id}.yaml - logs/ → /data/logs/{type}/{id}/ - files/ → /data/files/{type}/{id}/ + input.yaml → /.data/inputs/{type}/{id}.yaml + resource.yaml → /.data/resources/{type}/{id}.yaml + data.yaml → /.data/data/{type}/{id}.yaml + logs/ → /.data/logs/{type}/{id}/ + files/ → /.data/files/{type}/{id}/ outputs/ - {method}/ → /data/outputs/{type}/{method}/{id}-{timestamp}.yaml + {method}/ → /.data/outputs/{type}/{method}/{id}-{timestamp}.yaml ``` **Workflow View (`/workflows/`):** ``` /workflows/{workflow-name}/ - workflow.yaml → /data/workflows/{id}.yaml + workflow.yaml → /.data/workflows/{id}.yaml runs/ latest/ -> (points to latest timestamp) {timestamp}/ - run.yaml → /data/workflow-runs/{workflow-id}/{run-id}.yaml + run.yaml → /.data/workflow-runs/{workflow-id}/{run-id}.yaml steps/ {step-name}/ output.yaml → symlink to step output diff --git a/design/workflow.md b/design/workflow.md index 43324b76..5307e93c 100644 --- a/design/workflow.md +++ b/design/workflow.md @@ -18,14 +18,14 @@ weighted topological sort, so thtat htye have maximum paralleism through the workflow. Like steps, jobs also have conditions that trigger them. Workflows are specified in YAML files, that are validated with Zod, in the -`/data/workflows/` directory of the repository, with their `{uuid}.yaml`. -Workflow run output is stored in `/data/workflow-runs/` at -`/data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml`. +`/.data/workflows/` directory of the repository, with their `{uuid}.yaml`. +Workflow run output is stored in `/.data/workflow-runs/` at +`/.data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml`. ## Workflow Definition -Workflows are specified in `/data/workflows/{uuid}.yaml`. They have a unique id, -a globally unique name, and a set of jobs. +Workflows are specified in `/.data/workflows/{uuid}.yaml`. They have a unique +id, a globally unique name, and a set of jobs. ## Jobs @@ -49,7 +49,7 @@ not vary between identical inputs. (If the inputs are identical, the run order should be deterministic.) The output of the run will be written to a workflow run log, kept in -`/data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml`. +`/.data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml`. ## Logical Views @@ -60,10 +60,10 @@ that provides human/agent-friendly exploration of workflows by name. ``` /workflows/{workflow-name}/ - workflow.yaml → symlink to /data/workflows/{uuid}.yaml + workflow.yaml → symlink to /.data/workflows/{uuid}.yaml runs/ {run-id}/ - run.yaml → symlink to /data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml + run.yaml → symlink to /.data/workflow-runs/{workflow-uuid}/{run-uuid}.yaml steps/ {step-name}/ output.yaml → symlink to step output diff --git a/integration/echo_model_test.ts b/integration/echo_model_test.ts index 3e967e0f..d4257edc 100644 --- a/integration/echo_model_test.ts +++ b/integration/echo_model_test.ts @@ -122,8 +122,8 @@ Deno.test("Echo model: directory structure is correct", async () => { await dataRepo.save(modelType, result.data!); // Verify directory structure - const inputDir = join(repoDir, "data", "inputs", "swamp/echo"); - const dataDir = join(repoDir, "data", "data", "swamp/echo"); + const inputDir = join(repoDir, ".data", "inputs", "swamp/echo"); + const dataDir = join(repoDir, ".data", "data", "swamp/echo"); assertEquals(existsSync(inputDir), true, "Input directory should exist"); assertEquals( diff --git a/integration/repo_index_test.ts b/integration/repo_index_test.ts index 004646c8..43156d40 100644 --- a/integration/repo_index_test.ts +++ b/integration/repo_index_test.ts @@ -31,14 +31,14 @@ async function withTempDir(fn: (dir: string) => Promise): Promise { async function setupRepoDir(dir: string): Promise { const subdirs = [ - "data/inputs", - "data/resources", - "data/data", - "data/outputs", - "data/workflows", - "data/workflow-runs", - "data/logs", - "data/files", + ".data/inputs", + ".data/resources", + ".data/data", + ".data/outputs", + ".data/workflows", + ".data/workflow-runs", + ".data/logs", + ".data/files", "models", "workflows", ]; @@ -318,7 +318,7 @@ Deno.test("Integration: repo index verify detects broken symlinks", async () => const modelDir = join(repoDir, "models", "broken-model"); await ensureDir(modelDir); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", join(modelDir, "input.yaml"), ); @@ -341,7 +341,7 @@ Deno.test("Integration: repo index prune removes broken symlinks", async () => { await ensureDir(modelDir); const brokenLink = join(modelDir, "input.yaml"); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", brokenLink, ); @@ -453,7 +453,7 @@ Deno.test("CLI: repo index --verify fails on broken symlinks", async () => { const modelDir = join(repoDir, "models", "broken-verify-test"); await ensureDir(modelDir); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", join(modelDir, "input.yaml"), ); @@ -481,7 +481,7 @@ Deno.test("CLI: repo index --prune removes broken symlinks", async () => { await ensureDir(modelDir); const brokenLink = join(modelDir, "input.yaml"); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", brokenLink, ); diff --git a/integration/workflow_test.ts b/integration/workflow_test.ts index 9a9a2fe3..4db86ebe 100644 --- a/integration/workflow_test.ts +++ b/integration/workflow_test.ts @@ -1121,7 +1121,7 @@ Deno.test("CLI: model delete cleans up empty type directories", async () => { assertEquals(createResult.code, 0, "Model create should succeed"); // Verify the directory structure was created - const inputsDir = `${repoDir}/data/inputs`; + const inputsDir = `${repoDir}/.data/inputs`; const echoDir = `${inputsDir}/swamp/echo`; const swampDir = `${inputsDir}/swamp`; diff --git a/src/domain/repo/repo_index_service.ts b/src/domain/repo/repo_index_service.ts index 76d7ec59..616ac69c 100644 --- a/src/domain/repo/repo_index_service.ts +++ b/src/domain/repo/repo_index_service.ts @@ -50,22 +50,22 @@ export interface RebuildResult { * * Model View (`/models/{model-name}/`): * ``` - * input.yaml → ../data/inputs/{type}/{id}.yaml - * resource.yaml → ../data/resources/{type}/{id}.yaml - * data.yaml → ../data/data/{type}/{id}.yaml - * logs/ → ../data/logs/{type}/{id}/ - * files/ → ../data/files/{type}/{id}/ + * input.yaml → ../.data/inputs/{type}/{id}.yaml + * resource.yaml → ../.data/resources/{type}/{id}.yaml + * data.yaml → ../.data/data/{type}/{id}.yaml + * logs/ → ../.data/logs/{type}/{id}/ + * files/ → ../.data/files/{type}/{id}/ * outputs/ - * {method}/ → ../data/outputs/{type}/{method}/ + * {method}/ → ../.data/outputs/{type}/{method}/ * ``` * * Workflow View (`/workflows/{workflow-name}/`): * ``` - * workflow.yaml → ../data/workflows/workflow-{id}.yaml + * workflow.yaml → ../.data/workflows/workflow-{id}.yaml * runs/ * latest/ → {latest-timestamp}/ * {timestamp}/ - * run.yaml → ../data/workflow-runs/{workflow-id}/workflow-run-{run-id}.yaml + * run.yaml → ../.data/workflow-runs/{workflow-id}/workflow-run-{run-id}.yaml * steps/ * {step-name}/ * output.yaml → symlink to step output @@ -161,7 +161,7 @@ export interface RepoIndexService { * Rebuilds all logical views from scratch. * * Deletes existing /models/ and /workflows/ directories - * and recreates them from the /data/ directory. + * and recreates them from the /.data/ directory. * * @returns Rebuild result with counts */ diff --git a/src/domain/repo/repo_service.ts b/src/domain/repo/repo_service.ts index b7df6a56..6624ebcd 100644 --- a/src/domain/repo/repo_service.ts +++ b/src/domain/repo/repo_service.ts @@ -205,7 +205,7 @@ Use \`swamp --help\` to see available commands. private async createDataDirectoryStructure( repoPath: RepoPath, ): Promise { - const dataDir = join(repoPath.value, "data"); + const dataDir = join(repoPath.value, ".data"); const subdirs = [ "inputs", "resources", diff --git a/src/domain/repo/repo_service_test.ts b/src/domain/repo/repo_service_test.ts index 8ad6489c..e69b28e1 100644 --- a/src/domain/repo/repo_service_test.ts +++ b/src/domain/repo/repo_service_test.ts @@ -92,16 +92,16 @@ Deno.test("RepoService.init creates data directory structure", async () => { // Check all data subdirectories exist const expectedDirs = [ - "data/inputs", - "data/resources", - "data/workflows", - "data/data", - "data/outputs", - "data/workflow-runs", - "data/inputs-evaluated", - "data/workflows-evaluated", - "data/logs", - "data/files", + ".data/inputs", + ".data/resources", + ".data/workflows", + ".data/data", + ".data/outputs", + ".data/workflow-runs", + ".data/inputs-evaluated", + ".data/workflows-evaluated", + ".data/logs", + ".data/files", ]; for (const dir of expectedDirs) { diff --git a/src/infrastructure/persistence/directory_cleanup_test.ts b/src/infrastructure/persistence/directory_cleanup_test.ts index 79a6b2dd..f36d339d 100644 --- a/src/infrastructure/persistence/directory_cleanup_test.ts +++ b/src/infrastructure/persistence/directory_cleanup_test.ts @@ -15,7 +15,7 @@ async function withTempDir(fn: (dir: string) => Promise): Promise { Deno.test("cleanupEmptyParentDirs removes empty parent directories", async () => { await withTempDir(async (tempDir) => { // Create nested directory structure: tempDir/data/inputs/aws/ec2/vpc - const inputsDir = join(tempDir, "data", "inputs"); + const inputsDir = join(tempDir, ".data", "inputs"); const vpcDir = join(inputsDir, "aws", "ec2", "vpc"); await ensureDir(vpcDir); @@ -42,7 +42,7 @@ Deno.test("cleanupEmptyParentDirs removes empty parent directories", async () => Deno.test("cleanupEmptyParentDirs stops at non-empty directory", async () => { await withTempDir(async (tempDir) => { // Create nested directory structure - const inputsDir = join(tempDir, "data", "inputs"); + const inputsDir = join(tempDir, ".data", "inputs"); const vpcDir = join(inputsDir, "aws", "ec2", "vpc"); const subnetDir = join(inputsDir, "aws", "ec2", "subnet"); await ensureDir(vpcDir); @@ -79,7 +79,7 @@ Deno.test("cleanupEmptyParentDirs stops at non-empty directory", async () => { Deno.test("cleanupEmptyParentDirs handles already deleted directories", async () => { await withTempDir(async (tempDir) => { - const inputsDir = join(tempDir, "data", "inputs"); + const inputsDir = join(tempDir, ".data", "inputs"); await ensureDir(inputsDir); // Try to clean up a path that doesn't exist @@ -96,7 +96,7 @@ Deno.test("cleanupEmptyParentDirs handles already deleted directories", async () Deno.test("cleanupEmptyParentDirs does not go above stop directory", async () => { await withTempDir(async (tempDir) => { // Create structure - const dataDir = join(tempDir, "data"); + const dataDir = join(tempDir, ".data"); const inputsDir = join(dataDir, "inputs"); const typeDir = join(inputsDir, "swamp", "echo"); await ensureDir(typeDir); diff --git a/src/infrastructure/persistence/fs_file_repository.ts b/src/infrastructure/persistence/fs_file_repository.ts index 7f80d24b..37857218 100644 --- a/src/infrastructure/persistence/fs_file_repository.ts +++ b/src/infrastructure/persistence/fs_file_repository.ts @@ -178,7 +178,7 @@ export class FileSystemFileRepository implements FileRepository { } private getTypeDir(type: ModelType): string { - return join(this.repoDir, "data", "files", type.toDirectoryPath()); + return join(this.repoDir, ".data", "files", type.toDirectoryPath()); } private getFileDir( diff --git a/src/infrastructure/persistence/fs_file_repository_test.ts b/src/infrastructure/persistence/fs_file_repository_test.ts index d76ce35a..d190fb6d 100644 --- a/src/infrastructure/persistence/fs_file_repository_test.ts +++ b/src/infrastructure/persistence/fs_file_repository_test.ts @@ -45,7 +45,7 @@ Deno.test("FileSystemFileRepository.save creates directory structure", async () const expectedDir = join( dir, - "data", + ".data", "files", "swamp/echo", testModelId, @@ -269,7 +269,7 @@ Deno.test("FileSystemFileRepository.getPath returns correct path", () => { const path = repo.getPath(type, testModelId, testMethodName, id); assertEquals( path, - `/repo/data/files/swamp/echo/${testModelId}/${testMethodName}/550e8400-e29b-41d4-a716-446655440001.yaml`, + `/repo/.data/files/swamp/echo/${testModelId}/${testMethodName}/550e8400-e29b-41d4-a716-446655440001.yaml`, ); }); @@ -284,7 +284,7 @@ Deno.test("FileSystemFileRepository.getContentPath returns correct path with act const path = repo.getContentPath(type, testModelId, testMethodName, file); assertEquals( path, - `/repo/data/files/swamp/echo/${testModelId}/${testMethodName}/my-downloaded-file.tar.gz`, + `/repo/.data/files/swamp/echo/${testModelId}/${testMethodName}/my-downloaded-file.tar.gz`, ); }); diff --git a/src/infrastructure/persistence/streaming_log_repository.ts b/src/infrastructure/persistence/streaming_log_repository.ts index c0c0283a..1263c936 100644 --- a/src/infrastructure/persistence/streaming_log_repository.ts +++ b/src/infrastructure/persistence/streaming_log_repository.ts @@ -233,6 +233,6 @@ export class StreamingLogRepository implements LogRepository { } private getTypeDir(type: ModelType): string { - return join(this.repoDir, "data", "logs", type.toDirectoryPath()); + return join(this.repoDir, ".data", "logs", type.toDirectoryPath()); } } diff --git a/src/infrastructure/persistence/streaming_log_repository_test.ts b/src/infrastructure/persistence/streaming_log_repository_test.ts index 12132218..70a4e4f5 100644 --- a/src/infrastructure/persistence/streaming_log_repository_test.ts +++ b/src/infrastructure/persistence/streaming_log_repository_test.ts @@ -25,7 +25,7 @@ Deno.test("StreamingLogRepository.save creates directory structure", async () => await repo.save(type, log); - const expectedDir = join(dir, "data", "logs", "swamp/echo"); + const expectedDir = join(dir, ".data", "logs", "swamp/echo"); const stat = await Deno.stat(expectedDir); assertEquals(stat.isDirectory, true); }); @@ -44,7 +44,7 @@ Deno.test("StreamingLogRepository.save creates metadata and entries files", asyn // Check metadata file const metadataPath = join( dir, - "data", + ".data", "logs", "swamp/echo", `${log.id}.yaml`, @@ -261,7 +261,7 @@ Deno.test("StreamingLogRepository.getPath returns correct path", () => { const path = repo.getPath(type, id); assertEquals( path, - "/repo/data/logs/swamp/echo/550e8400-e29b-41d4-a716-446655440001.log", + "/repo/.data/logs/swamp/echo/550e8400-e29b-41d4-a716-446655440001.log", ); }); diff --git a/src/infrastructure/persistence/yaml_data_repository.ts b/src/infrastructure/persistence/yaml_data_repository.ts index 39143321..7466847f 100644 --- a/src/infrastructure/persistence/yaml_data_repository.ts +++ b/src/infrastructure/persistence/yaml_data_repository.ts @@ -78,7 +78,7 @@ export class YamlDataRepository implements DataRepository { await Deno.remove(path); // Clean up empty parent directories - const dataDir = join(this.repoDir, "data", "data"); + const dataDir = join(this.repoDir, ".data", "data"); await cleanupEmptyParentDirs(path, dataDir); } catch (error) { if (!(error instanceof Deno.errors.NotFound)) { @@ -96,6 +96,6 @@ export class YamlDataRepository implements DataRepository { } private getTypeDir(type: ModelType): string { - return join(this.repoDir, "data", "data", type.toDirectoryPath()); + return join(this.repoDir, ".data", "data", type.toDirectoryPath()); } } diff --git a/src/infrastructure/persistence/yaml_data_repository_test.ts b/src/infrastructure/persistence/yaml_data_repository_test.ts index 33b3fa99..b6138efa 100644 --- a/src/infrastructure/persistence/yaml_data_repository_test.ts +++ b/src/infrastructure/persistence/yaml_data_repository_test.ts @@ -24,7 +24,7 @@ Deno.test("YamlDataRepository.save creates directory structure", async () => { await repo.save(type, data); - const expectedDir = join(dir, "data", "data", "swamp/echo"); + const expectedDir = join(dir, ".data", "data", "swamp/echo"); const stat = await Deno.stat(expectedDir); assertEquals(stat.isDirectory, true); }); @@ -143,7 +143,7 @@ Deno.test("YamlDataRepository.getPath returns correct path", () => { const path = repo.getPath(type, id); assertEquals( path, - "/repo/data/data/swamp/echo/550e8400-e29b-41d4-a716-446655440001.yaml", + "/repo/.data/data/swamp/echo/550e8400-e29b-41d4-a716-446655440001.yaml", ); }); diff --git a/src/infrastructure/persistence/yaml_evaluated_input_repository.ts b/src/infrastructure/persistence/yaml_evaluated_input_repository.ts index c7435ca2..5fc0741d 100644 --- a/src/infrastructure/persistence/yaml_evaluated_input_repository.ts +++ b/src/infrastructure/persistence/yaml_evaluated_input_repository.ts @@ -88,7 +88,7 @@ export class YamlEvaluatedInputRepository { await Deno.remove(path); // Clean up empty parent directories - const evaluatedDir = join(this.repoDir, "data", "inputs-evaluated"); + const evaluatedDir = join(this.repoDir, ".data", "inputs-evaluated"); await cleanupEmptyParentDirs(path, evaluatedDir); } catch (error) { if (!(error instanceof Deno.errors.NotFound)) { @@ -101,7 +101,7 @@ export class YamlEvaluatedInputRepository { * Clears all evaluated inputs. */ async clear(): Promise { - const dir = join(this.repoDir, "data", "inputs-evaluated"); + const dir = join(this.repoDir, ".data", "inputs-evaluated"); try { await Deno.remove(dir, { recursive: true }); } catch (error) { @@ -121,7 +121,7 @@ export class YamlEvaluatedInputRepository { private getTypeDir(type: ModelType): string { return join( this.repoDir, - "data", + ".data", "inputs-evaluated", type.toDirectoryPath(), ); diff --git a/src/infrastructure/persistence/yaml_evaluated_workflow_repository.ts b/src/infrastructure/persistence/yaml_evaluated_workflow_repository.ts index 4b9175dd..5c198dc1 100644 --- a/src/infrastructure/persistence/yaml_evaluated_workflow_repository.ts +++ b/src/infrastructure/persistence/yaml_evaluated_workflow_repository.ts @@ -98,6 +98,6 @@ export class YamlEvaluatedWorkflowRepository { } private getWorkflowsDir(): string { - return join(this.repoDir, "data", "workflows-evaluated"); + return join(this.repoDir, ".data", "workflows-evaluated"); } } diff --git a/src/infrastructure/persistence/yaml_input_repository.ts b/src/infrastructure/persistence/yaml_input_repository.ts index 6405e0a9..358ef203 100644 --- a/src/infrastructure/persistence/yaml_input_repository.ts +++ b/src/infrastructure/persistence/yaml_input_repository.ts @@ -77,7 +77,7 @@ export class YamlInputRepository implements InputRepository { async findByNameGlobal( name: string, ): Promise<{ input: ModelInput; type: ModelType } | null> { - const inputsDir = join(this.repoDir, "data", "inputs"); + const inputsDir = join(this.repoDir, ".data", "inputs"); return await this.searchInputByName(inputsDir, [], name); } @@ -130,7 +130,7 @@ export class YamlInputRepository implements InputRepository { * Finds all inputs across all model types in the repository. */ async findAllGlobal(): Promise<{ input: ModelInput; type: ModelType }[]> { - const inputsDir = join(this.repoDir, "data", "inputs"); + const inputsDir = join(this.repoDir, ".data", "inputs"); const results: { input: ModelInput; type: ModelType }[] = []; await this.collectAllInputs(inputsDir, [], results); return results; @@ -226,7 +226,7 @@ export class YamlInputRepository implements InputRepository { await Deno.remove(path); // Clean up empty parent directories - const inputsDir = join(this.repoDir, "data", "inputs"); + const inputsDir = join(this.repoDir, ".data", "inputs"); await cleanupEmptyParentDirs(path, inputsDir); // Emit event if we had a name @@ -250,6 +250,6 @@ export class YamlInputRepository implements InputRepository { } private getTypeDir(type: ModelType): string { - return join(this.repoDir, "data", "inputs", type.toDirectoryPath()); + return join(this.repoDir, ".data", "inputs", type.toDirectoryPath()); } } diff --git a/src/infrastructure/persistence/yaml_input_repository_test.ts b/src/infrastructure/persistence/yaml_input_repository_test.ts index 07897de8..e81a997b 100644 --- a/src/infrastructure/persistence/yaml_input_repository_test.ts +++ b/src/infrastructure/persistence/yaml_input_repository_test.ts @@ -24,7 +24,7 @@ Deno.test("YamlInputRepository.save creates directory structure", async () => { await repo.save(type, input); - const expectedDir = join(dir, "data", "inputs", "swamp/echo"); + const expectedDir = join(dir, ".data", "inputs", "swamp/echo"); const stat = await Deno.stat(expectedDir); assertEquals(stat.isDirectory, true); }); @@ -169,7 +169,7 @@ Deno.test("YamlInputRepository.getPath returns correct path", () => { const path = repo.getPath(type, id); assertEquals( path, - "/repo/data/inputs/swamp/echo/550e8400-e29b-41d4-a716-446655440000.yaml", + "/repo/.data/inputs/swamp/echo/550e8400-e29b-41d4-a716-446655440000.yaml", ); }); diff --git a/src/infrastructure/persistence/yaml_output_repository.ts b/src/infrastructure/persistence/yaml_output_repository.ts index 829af636..d03470b5 100644 --- a/src/infrastructure/persistence/yaml_output_repository.ts +++ b/src/infrastructure/persistence/yaml_output_repository.ts @@ -155,7 +155,7 @@ export class YamlOutputRepository implements OutputRepository { await Deno.remove(path); // Clean up empty parent directories - const outputsDir = join(this.repoDir, "data", "outputs"); + const outputsDir = join(this.repoDir, ".data", "outputs"); await cleanupEmptyParentDirs(path, outputsDir); return; } @@ -179,7 +179,7 @@ export class YamlOutputRepository implements OutputRepository { } private getOutputsDir(): string { - return join(this.repoDir, "data", "outputs"); + return join(this.repoDir, ".data", "outputs"); } private getTypeDir(type: ModelType): string { diff --git a/src/infrastructure/persistence/yaml_output_repository_test.ts b/src/infrastructure/persistence/yaml_output_repository_test.ts index aaed7e98..c3cbcba3 100644 --- a/src/infrastructure/persistence/yaml_output_repository_test.ts +++ b/src/infrastructure/persistence/yaml_output_repository_test.ts @@ -39,7 +39,7 @@ Deno.test("YamlOutputRepository.save creates directory structure", async () => { const expectedDir = join( dir, - "data", + ".data", "outputs", testType.normalized, "create", diff --git a/src/infrastructure/persistence/yaml_resource_repository.ts b/src/infrastructure/persistence/yaml_resource_repository.ts index a51fa64e..1ef3e160 100644 --- a/src/infrastructure/persistence/yaml_resource_repository.ts +++ b/src/infrastructure/persistence/yaml_resource_repository.ts @@ -78,7 +78,7 @@ export class YamlResourceRepository implements ResourceRepository { await Deno.remove(path); // Clean up empty parent directories - const resourcesDir = join(this.repoDir, "data", "resources"); + const resourcesDir = join(this.repoDir, ".data", "resources"); await cleanupEmptyParentDirs(path, resourcesDir); } catch (error) { if (!(error instanceof Deno.errors.NotFound)) { @@ -96,6 +96,6 @@ export class YamlResourceRepository implements ResourceRepository { } private getTypeDir(type: ModelType): string { - return join(this.repoDir, "data", "resources", type.toDirectoryPath()); + return join(this.repoDir, ".data", "resources", type.toDirectoryPath()); } } diff --git a/src/infrastructure/persistence/yaml_resource_repository_test.ts b/src/infrastructure/persistence/yaml_resource_repository_test.ts index 176fc29d..f08951ea 100644 --- a/src/infrastructure/persistence/yaml_resource_repository_test.ts +++ b/src/infrastructure/persistence/yaml_resource_repository_test.ts @@ -24,7 +24,7 @@ Deno.test("YamlResourceRepository.save creates directory structure", async () => await repo.save(type, resource); - const expectedDir = join(dir, "data", "resources", "swamp/echo"); + const expectedDir = join(dir, ".data", "resources", "swamp/echo"); const stat = await Deno.stat(expectedDir); assertEquals(stat.isDirectory, true); }); @@ -143,6 +143,6 @@ Deno.test("YamlResourceRepository.getPath returns correct path", () => { const path = repo.getPath(type, id); assertEquals( path, - "/repo/data/resources/swamp/echo/550e8400-e29b-41d4-a716-446655440001.yaml", + "/repo/.data/resources/swamp/echo/550e8400-e29b-41d4-a716-446655440001.yaml", ); }); diff --git a/src/infrastructure/persistence/yaml_workflow_repository.ts b/src/infrastructure/persistence/yaml_workflow_repository.ts index ba1bec41..bbd4f3d1 100644 --- a/src/infrastructure/persistence/yaml_workflow_repository.ts +++ b/src/infrastructure/persistence/yaml_workflow_repository.ts @@ -147,6 +147,6 @@ export class YamlWorkflowRepository implements WorkflowRepository { } private getWorkflowsDir(): string { - return join(this.repoDir, "data", "workflows"); + return join(this.repoDir, ".data", "workflows"); } } diff --git a/src/infrastructure/persistence/yaml_workflow_run_repository.ts b/src/infrastructure/persistence/yaml_workflow_run_repository.ts index d23fd477..34f6e3a1 100644 --- a/src/infrastructure/persistence/yaml_workflow_run_repository.ts +++ b/src/infrastructure/persistence/yaml_workflow_run_repository.ts @@ -105,7 +105,7 @@ export class YamlWorkflowRunRepository implements WorkflowRunRepository { { run: WorkflowRun; workflowId: WorkflowId }[] > { const results: { run: WorkflowRun; workflowId: WorkflowId }[] = []; - const workflowRunsDir = join(this.repoDir, "data", "workflow-runs"); + const workflowRunsDir = join(this.repoDir, ".data", "workflow-runs"); try { for await (const entry of Deno.readDir(workflowRunsDir)) { @@ -201,7 +201,7 @@ export class YamlWorkflowRunRepository implements WorkflowRunRepository { } private getRunsDir(workflowId: WorkflowId): string { - return join(this.repoDir, "data", "workflow-runs", workflowId); + return join(this.repoDir, ".data", "workflow-runs", workflowId); } async deleteAllByWorkflowId(workflowId: WorkflowId): Promise { diff --git a/src/infrastructure/repo/symlink_repo_index_service.ts b/src/infrastructure/repo/symlink_repo_index_service.ts index 630dadfc..c3856c17 100644 --- a/src/infrastructure/repo/symlink_repo_index_service.ts +++ b/src/infrastructure/repo/symlink_repo_index_service.ts @@ -278,7 +278,7 @@ export class SymlinkRepoIndexService implements RepoIndexService { // Symlink to input.yaml const inputTarget = join( - "data", + ".data", "inputs", modelType, `${modelInputId}.yaml`, @@ -290,7 +290,7 @@ export class SymlinkRepoIndexService implements RepoIndexService { // Symlink to resource.yaml if it exists const resourceTarget = join( - "data", + ".data", "resources", modelType, `${modelInputId}.yaml`, @@ -301,21 +301,21 @@ export class SymlinkRepoIndexService implements RepoIndexService { } // Symlink to data.yaml if it exists - const dataTarget = join("data", "data", modelType, `${modelInputId}.yaml`); + const dataTarget = join(".data", "data", modelType, `${modelInputId}.yaml`); const dataPath = join(this.repoDir, dataTarget); if (await this.exists(dataPath)) { await this.createSymlink(dataPath, join(modelDir, "data.yaml")); } // Symlink to logs directory if it exists - const logsTarget = join("data", "logs", modelType, modelInputId); + const logsTarget = join(".data", "logs", modelType, modelInputId); const logsPath = join(this.repoDir, logsTarget); if (await this.exists(logsPath)) { await this.createSymlink(logsPath, join(modelDir, "logs")); } // Symlink to files directory if it exists - const filesTarget = join("data", "files", modelType, modelInputId); + const filesTarget = join(".data", "files", modelType, modelInputId); const filesPath = join(this.repoDir, filesTarget); if (await this.exists(filesPath)) { await this.createSymlink(filesPath, join(modelDir, "files")); @@ -326,7 +326,7 @@ export class SymlinkRepoIndexService implements RepoIndexService { await ensureDir(outputsDir); // Scan for output method directories - const methodsDir = join(this.repoDir, "data", "outputs", modelType); + const methodsDir = join(this.repoDir, ".data", "outputs", modelType); if (await this.exists(methodsDir)) { try { for await (const entry of Deno.readDir(methodsDir)) { @@ -358,7 +358,7 @@ export class SymlinkRepoIndexService implements RepoIndexService { // Symlink to workflow.yaml const workflowTarget = join( this.repoDir, - "data", + ".data", "workflows", `workflow-${workflowId}.yaml`, ); @@ -404,7 +404,7 @@ export class SymlinkRepoIndexService implements RepoIndexService { // Symlink to run.yaml const runTarget = join( this.repoDir, - "data", + ".data", "workflow-runs", workflowId, `workflow-run-${runId}.yaml`, diff --git a/src/infrastructure/repo/symlink_repo_index_service_test.ts b/src/infrastructure/repo/symlink_repo_index_service_test.ts index 3c894cf6..e018ce34 100644 --- a/src/infrastructure/repo/symlink_repo_index_service_test.ts +++ b/src/infrastructure/repo/symlink_repo_index_service_test.ts @@ -29,14 +29,14 @@ async function withTempDir(fn: (dir: string) => Promise): Promise { async function setupRepoDir(dir: string): Promise { // Create standard data directory structure const subdirs = [ - "data/inputs", - "data/resources", - "data/data", - "data/outputs", - "data/workflows", - "data/workflow-runs", - "data/logs", - "data/files", + ".data/inputs", + ".data/resources", + ".data/data", + ".data/outputs", + ".data/workflows", + ".data/workflow-runs", + ".data/logs", + ".data/files", "models", "workflows", ]; @@ -220,7 +220,7 @@ Deno.test("SymlinkRepoIndexService.verify detects broken symlinks", async () => const modelDir = join(dir, "models", "broken-model"); await ensureDir(modelDir); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", join(modelDir, "input.yaml"), ); @@ -261,7 +261,7 @@ Deno.test("SymlinkRepoIndexService.prune removes broken symlinks", async () => { await ensureDir(modelDir); const brokenLink = join(modelDir, "input.yaml"); await Deno.symlink( - "../data/inputs/nonexistent/file.yaml", + "../.data/inputs/nonexistent/file.yaml", brokenLink, );