mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
parent
ff069b8dca
commit
071c544960
4 changed files with 37 additions and 7 deletions
|
|
@ -1,32 +1,36 @@
|
|||
{
|
||||
"timestamp": "2026-03-19T15:20:29.910009Z",
|
||||
"current_node": "preflight_compile",
|
||||
"timestamp": "2026-03-19T15:20:46.286203Z",
|
||||
"current_node": "preflight_lint",
|
||||
"completed_nodes": [
|
||||
"start",
|
||||
"toolchain",
|
||||
"preflight_compile"
|
||||
"preflight_compile",
|
||||
"preflight_lint"
|
||||
],
|
||||
"node_retries": {
|
||||
"start": 1,
|
||||
"toolchain": 1,
|
||||
"preflight_lint": 1,
|
||||
"preflight_compile": 1
|
||||
},
|
||||
"context_values": {
|
||||
"thread.toolchain.current_node": "preflight_compile",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.thread_id": "toolchain",
|
||||
"internal.thread_id": "preflight_compile",
|
||||
"internal.retry_count.preflight_compile": 1,
|
||||
"command.output": "",
|
||||
"internal.retry_count.preflight_lint": 1,
|
||||
"graph.goal": "# Plan: JSONL analytics event file format\n\n## Context\n\nCurrently each CLI invocation writes a single `Track` event as a standalone JSON file (`~/.fabro/tmp/fabro-event-{uuid}.json`) and spawns a detached subprocess to send it. We want to switch to JSONL format (one JSON event per line) so a single file can contain multiple events. Filenames keep a UUID for uniqueness. This enables callers to batch multiple events into one file/subprocess.\n\nThe panic sender (`__send_panic`) is unaffected — it stays single-JSON-per-file.\n\n## Changes\n\n### 1. `lib/crates/fabro-util/src/telemetry/sender.rs` — rewrite\n\n**Writer — rename `send()` to `emit()`, accept multiple events:**\n- `pub fn emit(tracks: &[Track])` (was `pub fn send(track: Track)`)\n- Early return if `SEGMENT_WRITE_KEY` is `None` or `tracks` is empty\n- Generate a UUID for the filename: `fabro-events-{uuid}.jsonl`\n- Serialize each `Track` as a compact JSON line (`serde_json::to_string`), join with `\\n`\n- Pass the bytes to `spawn_fabro_subcommand(\"__send_analytics\", &filename, &json)` as before\n\nNo file locking needed — each invocation writes its own uniquely-named file.\n\n**Reader — rename `send_to_segment()` to `upload()`:**\n- Read file contents as string\n- Parse each non-empty line as `serde_json::Value`, inject `\"type\": \"track\"`, collect into batch array\n- Skip malformed lines with `tracing::warn!`\n- If no valid events, return `Ok(())`\n- POST to `https://api.segment.io/v1/batch` with payload `{\"batch\": [...]}`\n- Keep Basic auth the same\n\nExtract a pure `fn build_segment_batch(content: &str) -> Option<Value>` for testability.\n\n**Constants:**\n- Change `SEGMENT_API_URL` from `.../v1/track` to `.../v1/batch`\n\n### 2. `lib/crates/fabro-cli/src/main.rs`\n\n**`send_telemetry_event()` (~line 428):** Change call from `sender::send(track)` to `sender::emit(&[track])`.\n\n**`SendAnalytics` handler (~line 910):** Change call from `sender::send_to_segment(&path)` to `sender::upload(&path)`.\n\n### 3. `lib/crates/fabro-util/src/telemetry/spawn.rs` — no changes\n\n`spawn_fabro_subcommand` is generic (takes raw bytes). It continues to work for both JSONL analytics files and single-JSON panic files.\n\n### 4. No changes to these files\n- `event.rs` — `Track` struct unchanged\n- `panic.rs` — stays single-JSON-per-file\n- `mod.rs`, `anonymous_id.rs`, `context.rs`, `git.rs`, `sanitize.rs` — unchanged\n\n## Implementation order (red/green TDD)\n\nWrite each test first (red), then implement just enough to make it pass (green).\n\n### Step 1: `build_segment_batch` — pure function, no I/O\n\n1. **Red:** Write test `build_segment_batch_empty_content` — empty string returns `None`\n2. **Green:** Add `fn build_segment_batch(content: &str) -> Option<Value>` stub returning `None`\n3. **Red:** Write test `build_segment_batch_single_event` — one JSONL line produces `{\"batch\": [{\"type\": \"track\", ...}]}`\n4. **Green:** Implement line parsing, `\"type\": \"track\"` injection, batch wrapping\n5. **Red:** Write test `build_segment_batch_multiple_events` — two lines produce batch of 2\n6. **Green:** Should already pass\n7. **Red:** Write test `build_segment_batch_skips_malformed_lines` — one good + one bad line produces batch of 1\n8. **Green:** Add `continue` on parse error\n\n### Step 2: `emit()` — writer side\n\n9. **Red:** Update existing `send_noops_without_write_key` to use `emit(&[track])` signature\n10. **Green:** Rename `send` to `emit`, change signature to `&[Track]`, serialize as JSONL (one JSON line per track, joined with `\\n`), generate `fabro-events-{uuid}.jsonl` filename\n\n### Step 3: `upload()` — reader side\n\n11. **Red:** Write test `upload_noops_without_write_key` — same pattern as existing `send_panic_noops_without_dsn`\n12. **Green:** Rename `send_to_segment` to `upload`, change internals to read file as string, call `build_segment_batch`, POST to `/v1/batch`\n\n### Step 4: Wire up call sites in `main.rs`\n\n13. Update `send_telemetry_event()` to call `sender::emit(&[track])`\n14. Update `SendAnalytics` handler to call `sender::upload(&path)`\n\n### Step 5: Final checks\n\n```bash\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\ncargo test -p fabro-util\ncargo test --workspace\n```\n",
|
||||
"current.preamble": "Goal: # Plan: JSONL analytics event file format\n\n## Context\n\nCurrently each CLI invocation writes a single `Track` event as a standalone JSON file (`~/.fabro/tmp/fabro-event-{uuid}.json`) and spawns a detached subprocess to send it. We want to switch to JSONL format (one JSON event per line) so a single file can contain multiple events. Filenames keep a UUID for uniqueness. This enables callers to batch multiple events into one file/subprocess.\n\nThe panic sender (`__send_panic`) is unaffected — it stays single-JSON-per-file.\n\n## Changes\n\n### 1. `lib/crates/fabro-util/src/telemetry/sender.rs` — rewrite\n\n**Writer — rename `send()` to `emit()`, accept multiple events:**\n- `pub fn emit(tracks: &[Track])` (was `pub fn send(track: Track)`)\n- Early return if `SEGMENT_WRITE_KEY` is `None` or `tracks` is empty\n- Generate a UUID for the filename: `fabro-events-{uuid}.jsonl`\n- Serialize each `Track` as a compact JSON line (`serde_json::to_string`), join with `\\n`\n- Pass the bytes to `spawn_fabro_subcommand(\"__send_analytics\", &filename, &json)` as before\n\nNo file locking needed — each invocation writes its own uniquely-named file.\n\n**Reader — rename `send_to_segment()` to `upload()`:**\n- Read file contents as string\n- Parse each non-empty line as `serde_json::Value`, inject `\"type\": \"track\"`, collect into batch array\n- Skip malformed lines with `tracing::warn!`\n- If no valid events, return `Ok(())`\n- POST to `https://api.segment.io/v1/batch` with payload `{\"batch\": [...]}`\n- Keep Basic auth the same\n\nExtract a pure `fn build_segment_batch(content: &str) -> Option<Value>` for testability.\n\n**Constants:**\n- Change `SEGMENT_API_URL` from `.../v1/track` to `.../v1/batch`\n\n### 2. `lib/crates/fabro-cli/src/main.rs`\n\n**`send_telemetry_event()` (~line 428):** Change call from `sender::send(track)` to `sender::emit(&[track])`.\n\n**`SendAnalytics` handler (~line 910):** Change call from `sender::send_to_segment(&path)` to `sender::upload(&path)`.\n\n### 3. `lib/crates/fabro-util/src/telemetry/spawn.rs` — no changes\n\n`spawn_fabro_subcommand` is generic (takes raw bytes). It continues to work for both JSONL analytics files and single-JSON panic files.\n\n### 4. No changes to these files\n- `event.rs` — `Track` struct unchanged\n- `panic.rs` — stays single-JSON-per-file\n- `mod.rs`, `anonymous_id.rs`, `context.rs`, `git.rs`, `sanitize.rs` — unchanged\n\n## Implementation order (red/green TDD)\n\nWrite each test first (red), then implement just enough to make it pass (green).\n\n### Step 1: `build_segment_batch` — pure function, no I/O\n\n1. **Red:** Write test `build_segment_batch_empty_content` — empty string returns `None`\n2. **Green:** Add `fn build_segment_batch(content: &str) -> Option<Value>` stub returning `None`\n3. **Red:** Write test `build_segment_batch_single_event` — one JSONL line produces `{\"batch\": [{\"type\": \"track\", ...}]}`\n4. **Green:** Implement line parsing, `\"type\": \"track\"` injection, batch wrapping\n5. **Red:** Write test `build_segment_batch_multiple_events` — two lines produce batch of 2\n6. **Green:** Should already pass\n7. **Red:** Write test `build_segment_batch_skips_malformed_lines` — one good + one bad line produces batch of 1\n8. **Green:** Add `continue` on parse error\n\n### Step 2: `emit()` — writer side\n\n9. **Red:** Update existing `send_noops_without_write_key` to use `emit(&[track])` signature\n10. **Green:** Rename `send` to `emit`, change signature to `&[Track]`, serialize as JSONL (one JSON line per track, joined with `\\n`), generate `fabro-events-{uuid}.jsonl` filename\n\n### Step 3: `upload()` — reader side\n\n11. **Red:** Write test `upload_noops_without_write_key` — same pattern as existing `send_panic_noops_without_dsn`\n12. **Green:** Rename `send_to_segment` to `upload`, change internals to read file as string, call `build_segment_batch`, POST to `/v1/batch`\n\n### Step 4: Wire up call sites in `main.rs`\n\n13. Update `send_telemetry_event()` to call `sender::emit(&[track])`\n14. Update `SendAnalytics` handler to call `sender::upload(&path)`\n\n### Step 5: Final checks\n\n```bash\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\ncargo test -p fabro-util\ncargo test --workspace\n```\n\n\n## Completed stages\n- **toolchain**: success\n - 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`\n - Stdout:\n ```\n cargo 1.94.0 (85eff7c80 2026-01-15)\n ```\n - Stderr: (empty)\n",
|
||||
"current.preamble": "Goal: # Plan: JSONL analytics event file format\n\n## Context\n\nCurrently each CLI invocation writes a single `Track` event as a standalone JSON file (`~/.fabro/tmp/fabro-event-{uuid}.json`) and spawns a detached subprocess to send it. We want to switch to JSONL format (one JSON event per line) so a single file can contain multiple events. Filenames keep a UUID for uniqueness. This enables callers to batch multiple events into one file/subprocess.\n\nThe panic sender (`__send_panic`) is unaffected — it stays single-JSON-per-file.\n\n## Changes\n\n### 1. `lib/crates/fabro-util/src/telemetry/sender.rs` — rewrite\n\n**Writer — rename `send()` to `emit()`, accept multiple events:**\n- `pub fn emit(tracks: &[Track])` (was `pub fn send(track: Track)`)\n- Early return if `SEGMENT_WRITE_KEY` is `None` or `tracks` is empty\n- Generate a UUID for the filename: `fabro-events-{uuid}.jsonl`\n- Serialize each `Track` as a compact JSON line (`serde_json::to_string`), join with `\\n`\n- Pass the bytes to `spawn_fabro_subcommand(\"__send_analytics\", &filename, &json)` as before\n\nNo file locking needed — each invocation writes its own uniquely-named file.\n\n**Reader — rename `send_to_segment()` to `upload()`:**\n- Read file contents as string\n- Parse each non-empty line as `serde_json::Value`, inject `\"type\": \"track\"`, collect into batch array\n- Skip malformed lines with `tracing::warn!`\n- If no valid events, return `Ok(())`\n- POST to `https://api.segment.io/v1/batch` with payload `{\"batch\": [...]}`\n- Keep Basic auth the same\n\nExtract a pure `fn build_segment_batch(content: &str) -> Option<Value>` for testability.\n\n**Constants:**\n- Change `SEGMENT_API_URL` from `.../v1/track` to `.../v1/batch`\n\n### 2. `lib/crates/fabro-cli/src/main.rs`\n\n**`send_telemetry_event()` (~line 428):** Change call from `sender::send(track)` to `sender::emit(&[track])`.\n\n**`SendAnalytics` handler (~line 910):** Change call from `sender::send_to_segment(&path)` to `sender::upload(&path)`.\n\n### 3. `lib/crates/fabro-util/src/telemetry/spawn.rs` — no changes\n\n`spawn_fabro_subcommand` is generic (takes raw bytes). It continues to work for both JSONL analytics files and single-JSON panic files.\n\n### 4. No changes to these files\n- `event.rs` — `Track` struct unchanged\n- `panic.rs` — stays single-JSON-per-file\n- `mod.rs`, `anonymous_id.rs`, `context.rs`, `git.rs`, `sanitize.rs` — unchanged\n\n## Implementation order (red/green TDD)\n\nWrite each test first (red), then implement just enough to make it pass (green).\n\n### Step 1: `build_segment_batch` — pure function, no I/O\n\n1. **Red:** Write test `build_segment_batch_empty_content` — empty string returns `None`\n2. **Green:** Add `fn build_segment_batch(content: &str) -> Option<Value>` stub returning `None`\n3. **Red:** Write test `build_segment_batch_single_event` — one JSONL line produces `{\"batch\": [{\"type\": \"track\", ...}]}`\n4. **Green:** Implement line parsing, `\"type\": \"track\"` injection, batch wrapping\n5. **Red:** Write test `build_segment_batch_multiple_events` — two lines produce batch of 2\n6. **Green:** Should already pass\n7. **Red:** Write test `build_segment_batch_skips_malformed_lines` — one good + one bad line produces batch of 1\n8. **Green:** Add `continue` on parse error\n\n### Step 2: `emit()` — writer side\n\n9. **Red:** Update existing `send_noops_without_write_key` to use `emit(&[track])` signature\n10. **Green:** Rename `send` to `emit`, change signature to `&[Track]`, serialize as JSONL (one JSON line per track, joined with `\\n`), generate `fabro-events-{uuid}.jsonl` filename\n\n### Step 3: `upload()` — reader side\n\n11. **Red:** Write test `upload_noops_without_write_key` — same pattern as existing `send_panic_noops_without_dsn`\n12. **Green:** Rename `send_to_segment` to `upload`, change internals to read file as string, call `build_segment_batch`, POST to `/v1/batch`\n\n### Step 4: Wire up call sites in `main.rs`\n\n13. Update `send_telemetry_event()` to call `sender::emit(&[track])`\n14. Update `SendAnalytics` handler to call `sender::upload(&path)`\n\n### Step 5: Final checks\n\n```bash\ncargo fmt --check --all\ncargo clippy --workspace -- -D warnings\ncargo test -p fabro-util\ncargo test --workspace\n```\n\n\n## Completed stages\n- **toolchain**: success\n - 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`\n - Stdout:\n ```\n cargo 1.94.0 (85eff7c80 2026-01-15)\n ```\n - Stderr: (empty)\n- **preflight_compile**: success\n - Script: `cargo check -q --workspace 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n",
|
||||
"graph.model_stylesheet": "\n * { backend: api; model: claude-opus-4-6;}\n ",
|
||||
"failure_signature": "",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.toolchain": 1,
|
||||
"command.stderr": "",
|
||||
"thread.preflight_compile.current_node": "preflight_lint",
|
||||
"internal.run_id": "01KM3AVN0Z99W7AX4PCVEB8C9J",
|
||||
"graph.rankdir": "LR",
|
||||
"current_node": "preflight_compile",
|
||||
"current_node": "preflight_lint",
|
||||
"internal.retry_count.start": 1,
|
||||
"outcome": "success",
|
||||
"failure_class": "",
|
||||
|
|
@ -34,6 +38,15 @@
|
|||
},
|
||||
"logs": [],
|
||||
"node_outcomes": {
|
||||
"preflight_lint": {
|
||||
"status": "success",
|
||||
"context_updates": {
|
||||
"command.stderr": "",
|
||||
"command.output": ""
|
||||
},
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1",
|
||||
"duration_ms": 12861
|
||||
},
|
||||
"start": {
|
||||
"status": "success",
|
||||
"duration_ms": 0
|
||||
|
|
@ -57,9 +70,10 @@
|
|||
"duration_ms": 71459
|
||||
}
|
||||
},
|
||||
"next_node_id": "preflight_lint",
|
||||
"next_node_id": "implement",
|
||||
"node_visits": {
|
||||
"start": 1,
|
||||
"preflight_lint": 1,
|
||||
"toolchain": 1,
|
||||
"preflight_compile": 1
|
||||
}
|
||||
|
|
|
|||
5
nodes/preflight_lint/script_invocation.json
Normal file
5
nodes/preflight_lint/script_invocation.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"command": "cargo clippy -q --workspace -- -D warnings 2>&1",
|
||||
"language": "shell",
|
||||
"timeout_ms": null
|
||||
}
|
||||
5
nodes/preflight_lint/script_timing.json
Normal file
5
nodes/preflight_lint/script_timing.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"duration_ms": 12860,
|
||||
"exit_code": 0,
|
||||
"timed_out": false
|
||||
}
|
||||
6
nodes/preflight_lint/status.json
Normal file
6
nodes/preflight_lint/status.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"status": "success",
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1",
|
||||
"failure_reason": null,
|
||||
"timestamp": "2026-03-19T15:20:46.285960+00:00"
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue