diff --git a/run.json b/run.json index f55b1cd58..4a409be8a 100644 --- a/run.json +++ b/run.json @@ -496,14 +496,112 @@ } }, "web_url": "http://127.0.0.1:32276/runs/01KSTE7AK905MJ5YJR2Z2X5MJS", - "start": null, - "status": { - "kind": "starting" + "start": { + "start_time": "2026-05-29T17:59:12.457254Z", + "run_branch": "fabro/run/01KSTE7AK905MJ5YJR2Z2X5MJS", + "base_sha": "0e224aa70598433ce2dccb0a669b8b4e497978dc" }, - "status_updated_at": "2026-05-29T17:58:57.951997Z", - "last_event_at": "2026-05-29T17:59:12.172718Z", + "status": { + "kind": "running" + }, + "status_updated_at": "2026-05-29T17:59:12.457328Z", + "last_event_at": "2026-05-29T17:59:14.730623Z", "pending_control": null, - "checkpoints": [], + "checkpoints": [ + { + "seq": 20, + "checkpoint": { + "timestamp": "2026-05-29T17:59:14.730451Z", + "current_node": "start", + "completed_nodes": [ + "start" + ], + "node_retries": {}, + "context_values": { + "graph.rankdir": "LR", + "internal.run_id": "01KSTE7AK905MJ5YJR2Z2X5MJS", + "current_node": "start", + "internal.work_dir": "/home/daytona/workspace/fabro", + "internal.node_visit_count": 1, + "internal.thread_id": null, + "outcome": "succeeded", + "failure_class": "", + "graph.model_stylesheet": "\n * { model: claude-opus-4-7; }\n ", + "internal.fidelity": "compact", + "failure_signature": "", + "graph.goal": "---\ntitle: \"feat: Add Environment REST CRUD API\"\ntype: feat\nstatus: active\ndate: 2026-05-28\n---\n\n# feat: Add Environment REST CRUD API\n\n## Summary\n\nAdd server-owned Environment CRUD under `/api/v1/environments`, modeled after\nAutomations and backed by the existing `EnvironmentStore`. The API manages only\nthe server-side environment catalog in `environments/*.toml`; client-side\nenvironment definitions in `workflow.toml`, `.fabro/project.toml`, or run inputs\ncontinue to work and are not managed by this API.\n\n## API Contract\n\n- Add OpenAPI paths:\n - `GET /api/v1/environments`\n - `POST /api/v1/environments`\n - `GET /api/v1/environments/{id}`\n - `PUT /api/v1/environments/{id}`\n - `DELETE /api/v1/environments/{id}`\n- Mirror Automations semantics:\n - List returns `{ data: Environment[], meta: { total } }`, sorted by id.\n - Create body includes `id`; replace body omits `id`; path id is authoritative.\n - `GET` and `PUT` return `ETag: \"\"`.\n - `PUT` and `DELETE` require `If-Match`.\n - Use existing Automation-style statuses: `400`, `404`, `409`, `422`, `428`, `500`.\n - Stale revisions return `409` to match Automations.\n- Add API-specific Environment request/response schemas so REST `image.dockerfile`\n accepts only inline content or `null`.\n - Existing workflow/settings schemas keep supporting Dockerfile `path`.\n - REST requests with Dockerfile `path` return `422` and must not read\n server-local files.\n- Do not add `PATCH` in v1.\n\n## Implementation Changes\n\n- OpenAPI and generated clients:\n - Update `docs/public/api-reference/fabro-api.yaml` with an `Environments` tag,\n an `EnvironmentId` parameter, CRUD paths, list envelope, and inline-only API\n image schema.\n - Regenerate Rust API types and the TypeScript Axios client.\n - Keep the existing `EnvironmentSettings` schema intact for workflow settings.\n- Server:\n - Add `lib/crates/fabro-server/src/server/handler/environments.rs`, following\n `automations.rs` for routes, auth, ETag parsing, and error mapping.\n - Merge the routes into real API routes; do not add demo routes unless an\n existing convention requires it.\n - Convert API request DTOs into `EnvironmentDraft` / `EnvironmentSettings` only\n after rejecting Dockerfile path sources.\n - Map `EnvironmentStoreError` similarly to Automations: duplicate, protected,\n and stale as `409`; missing as `404`; validation as `422`; internal\n storage/parse/io as curated `500`.\n - After successful create, replace, or delete, refresh cached manifest run\n settings from the current `EnvironmentStore` catalog so `/system/info` and\n default run settings reflect the updated catalog.\n- Domain and API types:\n - Use a meaningful API DTO boundary rather than treating REST and TOML as\n identical Dockerfile-source surfaces.\n - Reuse `fabro-environment::Environment` for persisted domain behavior where\n the wire shape matches; keep API-only request schemas distinct where\n inline-only Dockerfile behavior differs.\n\n## Implementation Units\n\n- [ ] **Unit 1: Define the OpenAPI contract**\n - Add the environment CRUD paths, schemas, and path parameter.\n - Ensure the spec distinguishes REST-safe inline Dockerfile sources from the\n existing workflow/settings Dockerfile source schema.\n - Verification: OpenAPI route conformance can see the new paths and generated\n clients expose an `EnvironmentsApi`.\n\n- [ ] **Unit 2: Add server environment handlers**\n - Implement a new handler module mirroring the Automation CRUD handler shape.\n - Enforce authentication, id parsing, ETag/If-Match behavior, and error mapping.\n - Reject REST Dockerfile path sources before calling `EnvironmentStore`.\n - Verification: server API tests prove CRUD behavior and failure responses.\n\n- [ ] **Unit 3: Refresh derived server state after mutations**\n - Ensure successful environment create, replace, and delete refresh any cached\n manifest run settings derived from `EnvironmentStore::catalog_layer()`.\n - Preserve existing client-side environment precedence and behavior.\n - Verification: a test proves newly created server environments affect the\n resolved server default run environment where applicable.\n\n- [ ] **Unit 4: Regenerate clients and add contract tests**\n - Regenerate `fabro-api` and `lib/packages/fabro-api-client`.\n - Add Rust server integration tests and keep OpenAPI conformance passing.\n - Verification: generated Rust and TypeScript surfaces compile and expose the\n new environment operations.\n\n## Test Plan\n\n- Add server API tests in\n `lib/crates/fabro-server/tests/it/api/environments.rs` and register the module.\n- Cover:\n - List returns seeded environments and correct total.\n - Create persists `environments/{id}.toml`, returns `201`, and is visible via\n list/get.\n - Get returns current `ETag` matching `revision`.\n - Replace with valid `If-Match` updates the file, returns a new revision, and\n updates the `ETag`.\n - Replace/delete without `If-Match` return `428`.\n - Stale replace/delete return `409`.\n - Duplicate create returns `409`.\n - Invalid id/header returns `400`.\n - Domain validation failures return `422`.\n - Dockerfile `path` over REST returns `422` and does not persist or expose file\n contents.\n - Delete removes a non-default environment; deleting `default` returns a\n protected conflict.\n - Unauthenticated environment routes return `401`.\n - Creating an environment referenced by server default run settings refreshes\n cached manifest run settings.\n\n## Assumptions\n\n- This API manages server-owned environments only; client-defined catalogs remain\n file/request scoped.\n- Built-in seed behavior follows the current store: seeded environments are\n listed, create conflicts with existing ids, and `default` is protected from\n delete.\n- Create responses match Automations and do not need an `ETag`; clients can use\n the returned `revision` or call `GET`.\n- Inline-only Dockerfile policy applies only to REST CRUD, not local TOML\n configuration.\n\n## Sources\n\n- `docs/public/api-reference/fabro-api.yaml`\n- `lib/crates/fabro-server/src/server/handler/automations.rs`\n- `lib/crates/fabro-environment/src/store.rs`\n- `lib/crates/fabro-environment/src/model.rs`\n- `docs/public/execution/environments.mdx`\n", + "internal.retry_count.start": 0 + }, + "node_outcomes": { + "start": { + "status": "succeeded", + "usage": null + } + }, + "next_node_id": "toolchain", + "node_visits": { + "start": 1 + } + }, + "diff": {} + }, + { + "seq": 0, + "checkpoint": { + "timestamp": "2026-05-29T17:59:16.148086Z", + "current_node": "toolchain", + "completed_nodes": [ + "start", + "toolchain" + ], + "node_retries": {}, + "context_values": { + "internal.work_dir": "/home/daytona/workspace/fabro", + "internal.retry_count.start": 0, + "internal.thread_id": "start", + "current_node": "toolchain", + "thread.start.current_node": "toolchain", + "command.output": "blob://sha256/fc14b2ba2d770e5cd3169df7a29525c962adfc4cfa3097b9098c63ebd61a748c", + "graph.goal": "---\ntitle: \"feat: Add Environment REST CRUD API\"\ntype: feat\nstatus: active\ndate: 2026-05-28\n---\n\n# feat: Add Environment REST CRUD API\n\n## Summary\n\nAdd server-owned Environment CRUD under `/api/v1/environments`, modeled after\nAutomations and backed by the existing `EnvironmentStore`. The API manages only\nthe server-side environment catalog in `environments/*.toml`; client-side\nenvironment definitions in `workflow.toml`, `.fabro/project.toml`, or run inputs\ncontinue to work and are not managed by this API.\n\n## API Contract\n\n- Add OpenAPI paths:\n - `GET /api/v1/environments`\n - `POST /api/v1/environments`\n - `GET /api/v1/environments/{id}`\n - `PUT /api/v1/environments/{id}`\n - `DELETE /api/v1/environments/{id}`\n- Mirror Automations semantics:\n - List returns `{ data: Environment[], meta: { total } }`, sorted by id.\n - Create body includes `id`; replace body omits `id`; path id is authoritative.\n - `GET` and `PUT` return `ETag: \"\"`.\n - `PUT` and `DELETE` require `If-Match`.\n - Use existing Automation-style statuses: `400`, `404`, `409`, `422`, `428`, `500`.\n - Stale revisions return `409` to match Automations.\n- Add API-specific Environment request/response schemas so REST `image.dockerfile`\n accepts only inline content or `null`.\n - Existing workflow/settings schemas keep supporting Dockerfile `path`.\n - REST requests with Dockerfile `path` return `422` and must not read\n server-local files.\n- Do not add `PATCH` in v1.\n\n## Implementation Changes\n\n- OpenAPI and generated clients:\n - Update `docs/public/api-reference/fabro-api.yaml` with an `Environments` tag,\n an `EnvironmentId` parameter, CRUD paths, list envelope, and inline-only API\n image schema.\n - Regenerate Rust API types and the TypeScript Axios client.\n - Keep the existing `EnvironmentSettings` schema intact for workflow settings.\n- Server:\n - Add `lib/crates/fabro-server/src/server/handler/environments.rs`, following\n `automations.rs` for routes, auth, ETag parsing, and error mapping.\n - Merge the routes into real API routes; do not add demo routes unless an\n existing convention requires it.\n - Convert API request DTOs into `EnvironmentDraft` / `EnvironmentSettings` only\n after rejecting Dockerfile path sources.\n - Map `EnvironmentStoreError` similarly to Automations: duplicate, protected,\n and stale as `409`; missing as `404`; validation as `422`; internal\n storage/parse/io as curated `500`.\n - After successful create, replace, or delete, refresh cached manifest run\n settings from the current `EnvironmentStore` catalog so `/system/info` and\n default run settings reflect the updated catalog.\n- Domain and API types:\n - Use a meaningful API DTO boundary rather than treating REST and TOML as\n identical Dockerfile-source surfaces.\n - Reuse `fabro-environment::Environment` for persisted domain behavior where\n the wire shape matches; keep API-only request schemas distinct where\n inline-only Dockerfile behavior differs.\n\n## Implementation Units\n\n- [ ] **Unit 1: Define the OpenAPI contract**\n - Add the environment CRUD paths, schemas, and path parameter.\n - Ensure the spec distinguishes REST-safe inline Dockerfile sources from the\n existing workflow/settings Dockerfile source schema.\n - Verification: OpenAPI route conformance can see the new paths and generated\n clients expose an `EnvironmentsApi`.\n\n- [ ] **Unit 2: Add server environment handlers**\n - Implement a new handler module mirroring the Automation CRUD handler shape.\n - Enforce authentication, id parsing, ETag/If-Match behavior, and error mapping.\n - Reject REST Dockerfile path sources before calling `EnvironmentStore`.\n - Verification: server API tests prove CRUD behavior and failure responses.\n\n- [ ] **Unit 3: Refresh derived server state after mutations**\n - Ensure successful environment create, replace, and delete refresh any cached\n manifest run settings derived from `EnvironmentStore::catalog_layer()`.\n - Preserve existing client-side environment precedence and behavior.\n - Verification: a test proves newly created server environments affect the\n resolved server default run environment where applicable.\n\n- [ ] **Unit 4: Regenerate clients and add contract tests**\n - Regenerate `fabro-api` and `lib/packages/fabro-api-client`.\n - Add Rust server integration tests and keep OpenAPI conformance passing.\n - Verification: generated Rust and TypeScript surfaces compile and expose the\n new environment operations.\n\n## Test Plan\n\n- Add server API tests in\n `lib/crates/fabro-server/tests/it/api/environments.rs` and register the module.\n- Cover:\n - List returns seeded environments and correct total.\n - Create persists `environments/{id}.toml`, returns `201`, and is visible via\n list/get.\n - Get returns current `ETag` matching `revision`.\n - Replace with valid `If-Match` updates the file, returns a new revision, and\n updates the `ETag`.\n - Replace/delete without `If-Match` return `428`.\n - Stale replace/delete return `409`.\n - Duplicate create returns `409`.\n - Invalid id/header returns `400`.\n - Domain validation failures return `422`.\n - Dockerfile `path` over REST returns `422` and does not persist or expose file\n contents.\n - Delete removes a non-default environment; deleting `default` returns a\n protected conflict.\n - Unauthenticated environment routes return `401`.\n - Creating an environment referenced by server default run settings refreshes\n cached manifest run settings.\n\n## Assumptions\n\n- This API manages server-owned environments only; client-defined catalogs remain\n file/request scoped.\n- Built-in seed behavior follows the current store: seeded environments are\n listed, create conflicts with existing ids, and `default` is protected from\n delete.\n- Create responses match Automations and do not need an `ETag`; clients can use\n the returned `revision` or call `GET`.\n- Inline-only Dockerfile policy applies only to REST CRUD, not local TOML\n configuration.\n\n## Sources\n\n- `docs/public/api-reference/fabro-api.yaml`\n- `lib/crates/fabro-server/src/server/handler/automations.rs`\n- `lib/crates/fabro-environment/src/store.rs`\n- `lib/crates/fabro-environment/src/model.rs`\n- `docs/public/execution/environments.mdx`\n", + "failure_class": "", + "failure_signature": "", + "graph.rankdir": "LR", + "graph.model_stylesheet": "\n * { model: claude-opus-4-7; }\n ", + "outcome": "succeeded", + "internal.node_visit_count": 1, + "internal.retry_count.toolchain": 0, + "internal.run_id": "01KSTE7AK905MJ5YJR2Z2X5MJS", + "internal.fidelity": "compact" + }, + "node_outcomes": { + "start": { + "status": "succeeded", + "usage": null + }, + "toolchain": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/fc14b2ba2d770e5cd3169df7a29525c962adfc4cfa3097b9098c63ebd61a748c" + }, + "notes": "Script completed: command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 1407, + "active_time_ms": 1407 + } + } + }, + "next_node_id": "preflight_compile", + "node_visits": { + "toolchain": 1, + "start": 1 + } + }, + "diff": {} + } + ], "conclusion": null, "sandbox": { "kind": "ready", @@ -530,5 +628,67 @@ "superseded_by": null, "retried_from": "01KSTDT820G3PQC64V37321J2N", "pending_interviews": {}, - "stages": {} + "stages": { + "toolchain@1": { + "first_event_seq": 21, + "prompt": null, + "response": null, + "completion": null, + "provider_used": null, + "diff": null, + "script_invocation": { + "script": "command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "command": "exec 2>&1\ncommand -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "language": "shell" + }, + "script_timing": null, + "parallel_results": null, + "output": null, + "started_at": "2026-05-29T17:59:14.730582Z", + "handler": "command", + "usage": { + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "reasoning_tokens": 0, + "cache_read_tokens": 0, + "cache_write_tokens": 0 + }, + "state": "running" + }, + "start@1": { + "first_event_seq": 17, + "prompt": null, + "response": null, + "completion": { + "outcome": "succeeded", + "notes": null, + "failure_reason": null, + "timestamp": "2026-05-29T17:59:14.730252Z" + }, + "provider_used": null, + "diff": null, + "script_invocation": null, + "script_timing": null, + "parallel_results": null, + "output": null, + "started_at": "2026-05-29T17:59:14.730071Z", + "handler": "start", + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 0, + "active_time_ms": 0 + }, + "usage": { + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "reasoning_tokens": 0, + "cache_read_tokens": 0, + "cache_write_tokens": 0 + }, + "state": "succeeded" + } + } } \ No newline at end of file diff --git a/stages/001-start@1/status.json b/stages/001-start@1/status.json new file mode 100644 index 000000000..c268fda9e --- /dev/null +++ b/stages/001-start@1/status.json @@ -0,0 +1,6 @@ +{ + "outcome": "succeeded", + "notes": null, + "failure_reason": null, + "timestamp": "2026-05-29T17:59:14.730252Z" +} \ No newline at end of file diff --git a/stages/002-toolchain@1/script_invocation.json b/stages/002-toolchain@1/script_invocation.json new file mode 100644 index 000000000..92c244949 --- /dev/null +++ b/stages/002-toolchain@1/script_invocation.json @@ -0,0 +1,5 @@ +{ + "script": "command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "command": "exec 2>&1\ncommand -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "language": "shell" +} \ No newline at end of file