--- name: flowfile-failure-archaeology description: Chronicles 25+ major incidents in Flowfile's git history (worker/kernel deadlocks and races, reverted or self-reverted branches, Electron/Tauri packaging breakage, codegen correctness bugs, CI flakiness, security-fix lineage, in-flight save/open and flow-naming churn) with symptom, root cause, evidence commit/PR, and current status, plus a live/stalled-branch inventory and a verified-and-rejected list of already-settled debates — use BEFORE re-fighting a settled battle, before proposing a fix that touches worker transport, kernel lifecycle, sort/join/group_by codegen, flow save/open/naming, Tauri packaging or CSP, CI test ordering/coverage, or version/release tooling, and before trusting an old branch, a `git tag`, or a CLAUDE.md line as current truth without checking here first. --- # Flowfile Failure Archaeology **Purpose:** this is the "have we been here before" file. Scan the quick index below *before* writing a fix for something that feels familiar — a race condition, a silent revert, a packaging break, a codegen bug. Several incidents below have a matching "verified and rejected" fix that someone already tried and walked back; re-proposing it wastes a review cycle. All facts here were re-verified against the working tree / git history on 2026-07-03 at commit `f6963c77` (repo version 0.12.7). SHAs are copy-pasteable into `git show ` or `git log -1 `. ## When NOT to use this skill - Need the *current* non-historical contract for a subsystem → `flowfile-architecture-contract`. - Actively debugging a live failure ("has this happened before" is a means, not the goal) → `flowfile-debugging-playbook`. - Need build/CI/testing mechanics, not *why* they're shaped this way → `flowfile-build-and-env` / `flowfile-testing-and-validation`. - Need env var / feature-flag semantics, not the incident behind them → `flowfile-config-and-flags`. - Adding a new node type, want current conventions not history → `flowfile-node-development`. - Want the open TODO/debt backlog rather than closed incidents → not this file. ## Quick index (symptom keyword → incident #) `1` worker hangs forever / no timeout / `p.join()` / result queue · `2` Kafka/Redpanda node or scheduler sync · `3` app version wrong/blank on Windows, `importlib.metadata` · `4` SQL viz 504, `VizSessionRegistry`, `response_q` · `5` `"File must end with PAR1"`, kernel parquet I/O · `6` kernel won't start / double-starts on fast click · `7` Tauri sidecar spawn/shutdown ordering · `8` Windows/macOS E2E always green · `9` macOS codesign / Python.framework notarization · `10` DMG missing files, huge asar, electron-builder bump · `11` anything still says "Electron" · `12` icons/fonts missing only in the desktop app, not web · `13` output path defaults, "." resolves next to the binary · `14` sort/ node output order wrong only in generated code · `15` flow tab title / display name vs filename · `16` save-as / open overwriting an existing flow · `17` upgrading `fastapi` · `18` adding/renumbering an Alembic migration · `19` CI job slow/flaky, or want pytest-xdist · `20` file-explorer path traversal / sandbox escape · `21` worker transport (HTTP polling vs WebSocket) · `22` GA4 / any connection failing only when flow is opened · `23` Airbyte, in-house fuzzy match, `website/` · `24` draggable panels, z-index, overlay resize · `25` run history / flow-version linkage · `26` polars version bump, silent CI hang/timeout, `SQLContext` + `scan_delta` deadlock · a `git tag` or "since v0.9.4" claim → **Tag/release gotchas** below · an old/unmerged branch that "already does this" → **Branch inventory** below · lowering the worker poll, the catalog sleep, coverage `parallel=true`, re-trying the FastAPI bump → **Verified-and-rejected** below. --- ## Incidents **1. Worker result-queue deadlock (#564) — the flagship.** Symptom: worker tasks hang forever, no timeout (wide-table `calculate_schema`, deep query plans trigger it). Root cause: both transports (`spawner.handle_task`, `streaming.ws_submit`) joined the subprocess **before** reading its result queue — a child `put()`ing a payload bigger than the OS pipe buffer (~64KB) blocks in its feeder thread until the parent reads, so `p.join()` first deadlocks parent and child. Fix: `flowfile_worker/flowfile_worker/spawner.py:20` `def drain_result_queue(q, p, timeout=...)` polls and bails the moment the child exits without a result; `streaming.py:316` calls it via `asyncio.to_thread`; `apply_model_task` now `put()`s before signalling progress 100 so `progress == 100` always implies the result is already queued. Evidence: `3857aced` "fix(worker): drain result queue before joining child to avoid deadlock (#564)", 2026-07-02; regression test at `flowfile_worker/tests/test_result_queue_deadlock.py` pushes a >64KB payload through both paths — self-guarded since **pytest-timeout isn't installed** here. **Status: merged. Rule: never `p.join()` before draining `q`; any new task func must `put()` before signalling `progress==100`.** **2. Kafka pushed straight to main, reverted, re-landed as PRs.** Kafka connection management lands via PR `572012e1` (#380, 2026-04-02). Next day two commits go **direct to main, no PR**: `fa86d940` "Adding kafka to flowgraph" (also accidentally committed `diff.txt`, 4144 lines, and `current-review.md`, 119 lines, straight to main) and `b7e0be80` "Adding kafka sync solutions to the scheduler". Both reverted same day (`84392a6c`, `3ab65033`). Work re-landed properly as `434980b3` (#400) and `7be19166` (#404). **Status: shipped** (own CI workflow, `kafka` pytest marker). Watch for stray plan/diff files landing on main — recurred (`775fda49`, `f670c126`). **3. Windows `app_version` hotfix chain → root-cause fix.** Three escalating hotfixes to `init_db.py:update_db_info` in ~36h: `ff2644d8` (2026-06-23, `PackageNotFoundError` fallback → hardcoded `"0.12.3"`) → `ebeca9a4` (same day, add bare `except Exception` too) → `95f60158` (2026-06-24, `importlib.metadata.version()` was returning **`None`**, no exception, in the PyInstaller Windows binary — added explicit `is None` guard). Root-cause fix same day: `b21f518c` "Centralize version management across all manifests (#547)" — `shared/_version.py` (`__version__`, `get_version()`) is now the single source of truth, plus `tools/bump_version.py` / `tools/check_version_sync.py` (CI drift gate). **Rule: never read app version via `importlib.metadata` in frozen builds — use `shared/_version.py::get_version()`.** **4. VizSessionRegistry response-queue race → 504s under SQL viz.** `969dd74d` (2026-04-26): two parents sharing a `session_key` both blocked on the same `response_q` and stole each other's responses; the correct response for parent B got discarded by parent A's "stale, draining" branch, so B timed out at 115s and 504'd. Fix: per-`SessionHandle` `parent_lock: threading.Lock` around put-and-wait in `execute()`; drain loop replaced by a single `get()` + hard raise on request-id mismatch. Ten minutes later `b9dfed1e` fixed a companion field-name drift: worker responses use `column_schema`, `catalog/service.py` read `data["schema"]` and silently fell back to a local read via broad `except Exception`. **Rule: shared mp queues need a per-handle lock (pattern in `flowfile_worker/flowfile_worker/viz_sessions.py`); grep both sides of worker↔core JSON field names when renaming.** **5. Parquet corruption race between host and kernel container (#302).** Symptom: `"File must end with PAR1"` reading kernel input/output. Root cause: `write_parquet()` can leave data in OS buffers; sharing files between host and Docker container over a mounted volume lets the reader see an incomplete file. Fix: explicit `fsync` after writing parquet in both `flowfile_core/flowfile_core/flowfile/flow_graph/` and `kernel_runtime/kernel_runtime/flowfile_client.py`. Evidence: `84b8e416`, 2026-02-03. **Rule: any new host↔kernel file handoff must `fsync` before signalling readiness — Docker volume mounts don't guarantee write visibility ordering.** **6. Kernel double-start race.** Start button stayed clickable while a kernel was `starting`; a double-click fired two starts that collided on the container name. Fixed both layers: UI disables the button mid-flight; `flowfile_core/kernel/manager.py::start_kernel` treats an already-STARTING kernel as a no-op (`f43874bb`, 2026-05-26). **Rule: guard the state machine server-side; never trust the client to disable the button correctly.** **7. Tauri sidecar lifecycle races.** `161682ba` (2026-05-29, 57 insertions, terse message) hardens spawn/shutdown ordering in `src-tauri/src/sidecar/{mod.rs,shutdown.rs}` and `state.rs` right after the Electron→Tauri migration (#11). Pairs with the shutdown ladder (HTTP `/shutdown` → SIGTERM → SIGKILL) and the macOS quit-path fix (Cmd+Q fires `RunEvent::Exit` not `ExitRequested`; sidecars leaked until `lib.rs` matched both plus a parent-death watcher). **Status: still the most fragile part of the desktop shell** — quit-path desktop E2E is a TODO per the header of `flowfile_frontend/tests/web-flow.spec.ts`. **8. Windows Electron tests were green while testing nothing (#184).** Four compounding anti-patterns made Windows E2E always pass: safety timeout used OR instead of AND across two startup signals; `test.skip()` instead of throwing on app-launch failure; `.catch(() => false)` swallowing; an explicit "mark test as passed anyway" branch on unexpected window close. Fix: all four inverted. `09ffc9df`, 2026-01-10; follow-up `67e64190` (#187). **Status:** the suite this hardened was later deleted in the Tauri migration (#11) — fix outlived its own tests. **Rule: in Playwright helpers, failures must throw; never `test.skip()` a broken launch; never force-pass.** **9. macOS codesign failure on Python.framework (#266).** Electron v36 + electron-builder v26 broke codesign on the PyInstaller-bundled `Python.framework` ("bundle format is ambiguous") because `hardenedRuntime` forces `--timestamp --options runtime`. Fix: `signIgnore` for `Python.framework`; `CSC_IDENTITY_AUTO_DISCOVERY=false` in the CI test workflow. Evidence: `97808864`, 2026-01-27. **10. electron-builder v26 DMG breakage + 1GB asar (#269, #271).** electron-builder `^26.5.0` → `^25.1.8` (`b424fdbf`, 2026-01-28): v26.5.0 shipped DMGs missing the Electron Framework binary; also dropped `node_modules` from the `files` array (~1GB of dev deps was landing in the asar) and compression "maximum" → "normal" (maximum made asar reads slow). Companion `99c92db1` (#271): `vite.config.js` → `.mjs` (ESM-only `@vitejs/plugin-react` v5 caused `ERR_REQUIRE_ESM`); pypi-release workflow moved to Node 20. **Status:** whole Electron toolchain later deleted (#11); lesson generalizes — bundler major bumps repeatedly broke packaging in non-obvious ways. **11. Electron → Tauri migration (#462).** `3777c661`, 2026-05-30: 100 files, +10,653/−6,621. Deleted the Electron main process (`flowfile_frontend/src/main/*.ts`) **and its whole E2E suite** (`tests/app.spec.ts`, `tests/complex-flow.spec.ts`, `tests/helpers/electronTestHelper.ts`). Added `src-tauri/`, `tools/rename_sidecar.py`, `tools/sign_macos_sidecars.sh`. **Status: desktop-shell E2E was never re-landed** — root CLAUDE.md still calls Tauri-shell E2E via `tauri-driver` a follow-up, so #8/#9's hardening was discarded with the platform. Some docs pages still say "Electron" — treat any such mention as possibly stale. Aftermath: `8aa1e5c6` (ship `.deb` only), `a812a326` (release matrix), `161682ba` (#7), `a1e397d1` (#494, overlay), `da2a5f2c` (#529, see 13). **12. AG Grid icons vanished in Tauri — the CSP trap (#514, #515).** Two same-day PRs (2026-06-13) share the literal subject "Fix missing icons in aggrid table viewer" but are unrelated. `3ffa00d0` (#514) is the real fix: `src-tauri/tauri.conf.json`'s CSP lacked `font-src 'self' data:`, silently blocking AG Grid's icon font **only in the desktop webview** (fine in `npm run dev:web`) — current CSP includes it, verify with `grep -n csp flowfile_frontend/src-tauri/tauri.conf.json`. `4913e27a` (#515) reused the title but is actually perf: `include_output: bool = True` added to `FlowNode.get_node_data` / `GET /node` so the settings panel opens instantly instead of computing output-schema prediction. **Rule: a desktop-only rendering bug → check `tauri.conf.json` CSP first; don't trust PR titles here — read the diff.** **13. Writing output next to the app binary killed the worker (#529).** `da2a5f2c`, 2026-06-19, issue #526: a `.` default output path resolved to the app install location; writing there crashed the worker with an opaque error. Fix: default output location is now the user's home directory. Windows/desktop path handling is a repeat offender — see also `082a0136` (#496). **14. Sort codegen silently flipped Descending → ascending (#544).** The generator computed `descending = (how == "desc")`, but visual sort nodes store `how` as `"Descending"`/`"Ascending"` — generated Polars code sorted **ascending** for a Descending sort, inverting downstream logic **with no error raised** (the executed graph was correct; only exported code was wrong). Fix: centralized parsing in `flowfile_core/flowfile_core/schemas/transform_schema.py:952 def is_descending(how)` (+ `SortByInput.descending` property at line 970), accepting both spellings, used by both codegen (sort + window `order_by`) and the execution engine, which previously each carried their own copy. Evidence: `eac45a01`, 2026-06-22. **Status: fixed** — part of a larger codegen-bug family with the same root pattern (`71851cd5` #223, `dd521299` #224 join codegen, `65153e0b` #512 alias bug, `d723e0de` #549, `6cd67c4a` #553 group_by, `a96ab2bf` #503). **Rule: the visual graph and generated code are two separate executors of the same settings — every codegen change needs an equivalence test against real execution; enums with two spellings need one shared parser, not per-consumer copies.** **15. Flow-naming saga — attempted fix, self-revert, current live branch.** 2026-04-22, branch `claude/fix-flow-tab-display-kM4qD`: `f060a2e8` "fix: use catalog registration name for flow tab titles", **self-reverted the same session** (`e339ffcb`), never merged. As of 2026-07-03, branch `improvement/improve-naming-unnamed-flows` (**unmerged, 1 ahead**: `fa23a297`) would change `handler.py`: `create_flow_name()` returns a friendly `"Unnamed flow YYYY-MM-DD HH:MM:SS"` (main today still returns the old `"%Y%m%d_%H_%M_%S_flow.yaml"`, verified at `handler.py:25-27`); a new `create_unnamed_flow_filename(flow_id)` produces `Unnamed_flow__.yaml` for the on-disk path ("registration dedupes on path, not name"). On main today, unnamed flows already live under `storage.unnamed_flows_directory` — display name and path are already loosely coupled; the branch tightens it further. **Status: landed** — `create_flow_name()` returns `"Untitled flow "` and `create_unnamed_flow_filename(flow_id)` gives the unique on-disk path; the branch is gone. **16. Save/open overwrite saga (still in flight).** `1133cc77` (#522, 2026-06-16): Save-As handling, `SaveDialog`, `CatalogFlowPicker`, +508/−37. `8681308e` "Fix open flow overwrite" on branch `fix/overwrite-open-flow` (2026-07-02, 1 ahead locally): guards in `handler.py` + new `test_flow_dirty_state.py`. **Origin's copy of that branch has since walked part of it back**: `b85abc66` "Revert changes to only targeting the catalog" (deleted 81 lines of the dirty-state test, stripped handler/routes changes) and `dc9ff9bb` "Revert changes in routes.py", both 2026-07-03 — an ambitious dirty-state-tracking approach scoped back to a catalog-only fix. **Status:** the branch is gone; read `handler.py`'s current save/open guards before writing new logic there. **17. FastAPI upgrade reverted in 2026-05, retried 2026-10.** `eff7287b` "Reverting upgrade Fastapi" (2026-05-11) exists only on branch `feature/LLM-security-patches`; `fastapi = "~0.115.2"` stayed from the initial commit until the retry. The upgrade was tried during the LLM-safeguards work (which landed separately as `f55aa92c` #457) and abandoned before merge; **the revert message does not record why**. **Retried 2026-10-01** (`~0.142.2`, for the Starlette Dependabot alerts) with the most likely cause fixed: FastAPI 0.132 made `strict_content_type=True` the default, so a JSON body without a `Content-Type` header gets a 422 — and ~10 core→worker calls in `subprocess_operations.py` posted `data=model.model_dump_json()` with no header. They now pass `_JSON_HEADERS`. **Rule: any new JSON call to a Flowfile FastAPI app must send `Content-Type: application/json`** (or use `json=`); don't "fix" it by setting `strict_content_type=False`. **18. Database migrations: born from a production bug.** Pre-history: `e2977f5c` (#422, ~2026-04-08) — a run-type mismatch between local and docker DBs needed a downgrade mechanism, migration `006_normalize_run_type.py`, and two tries to align local/worker DBs. Alembic itself arrived in `0ded1ebf` (#403, 2026-04-08): `alembic.ini`, `env.py`, `001_initial_schema.py`, `database/migration.py` — its own PR body notes "Fix package building with alembic" (bundling it into PyInstaller needed a follow-up fix too). Count/head: `ls flowfile_core/flowfile_core/alembic/versions/`. Import-time side effect (`database/init_db.py:24-27`): importing `flowfile_core` runs `run_startup_migration()` unless `FLOWFILE_SKIP_STARTUP_MIGRATION` is set — always set that before importing `flowfile_core` outside the running app. **19. CI timing & flakiness arc.** `f8dbb470` "reverting the test order for now" (2025-04-12) — earliest CI-ordering churn, rolled back. Flaky-test firefighting: `66b14004`/`e3a2bc80` (2025-04-13, select.vue timing), `49e15434` (2025-09-05, deleted 3 lines from `test_endpoints.py` rather than fix root cause). `90b8dbb2` (#247) — first parallelization. The big one: `093ead72` "ci: split coverage into its own job with sysmon tracer, drop redundant frontend builds, cancel superseded (#539)", 2026-06-20 — coverage now runs in its own job using Python 3.12's `sys.monitoring` tracer (`COVERAGE_CORE=sysmon`, `test.yaml` ~lines 217-264), redundant `build:web` calls dropped from backend jobs, a `concurrency:` block (line 21) cancels superseded runs. The coverage job was ~56min before this change; **pytest-xdist was explicitly deferred as high-risk** — tests share one SQLite DB. See Verified-and-rejected below before re-proposing xdist. **20. Security-fix lineage.** `3c7c42fa` (#136, 2026-01-06): path-traversal fix in file endpoints — `SecureFileExplorer`, `sandbox_root`, filename sanitization, 403 outside user data dir. `64bca383` (#280, 2026-01-31): that sandbox then **broke desktop UX** — backend silently returned home-dir contents when navigating above home while the frontend's path state desynced, breaking all subsequent navigation; fix: desktop/Electron mode drops the sandbox entirely, `SecureFileExplorer` raises `PermissionError` instead of silently falling back. `b4e723f2` (#355, 2026-03-16): hardened `flow_data_engine/polars_code_parser.py` — a sandbox-escape surface, since user-supplied Polars code runs through it. `f55aa92c` (#457, 2026-05-11): LLM safeguards + litellm bump; follow-ups on the since-deleted `feature/LLM-security-patches` branch were never merged. **Rule: security fix → silent-fallback regression → explicit-error redesign already repeated once (#136→#280); prefer raising over silently falling back to a safer-looking default.** **21. Worker↔core transport evolution (context for #1).** `7f9d4768` (#233): HTTP polling with double-base64 → raw-bytes bodies + metadata headers, ~33% bandwidth cut. → `44543f89` (#267): WebSocket streaming for `ExternalDfFetcher`/`ExternalSampler`, killing poll latency. → `e8b91fa8` (#268): parallel execution via stage-based topological sort (`ThreadPoolExecutor` within stages). The #1 deadlock is the latest chapter of this same seam — read all four commits before touching worker transport. **22. GA4 / deferred connection resolution (#490).** Symptom: a flow whose secrets/connections had gone invalid could not even be **opened**; GA4 also failed specifically when run from a scheduled workflow. Fix (`09f9a2a0` #490 + follow-ups): secrets validate at **run** time, not open time. Follow-ups fixed three latent races: undo-restore from an empty graph re-stamped connection-backed nodes with `user_id=None` (fixed by FlowGraph remembering the session owner); memoized credential/Kafka getters ran on a background schema-callback thread concurrently with execution (locks added); stale echoed Kafka fields served stale columns after programmatic updates. **Rule: never resolve secrets at flow open, only at run; memoized getters touched by the schema callback must be thread-safe.** **23. Removed-feature record (deleted = don't reintroduce).** Airbyte connector removed wholesale in `aac7177e` (#93, 2025-07-29), replaced by native cloud-storage read. In-house fuzzy matching (`flowfile_worker/polars_fuzzy_match/*`) extracted to the external `pl-fuzzy-frame-match` package in `c1d1d1b2` (#108, 2025-08-22). Landing website `website/` added `118132ba` (#253) then removed `0086b216` (#260). Electron main process + its E2E suite removed in #462 (see 11). DraggablePanel + 3 panel Pinia stores in WASM removed by `1c549242` (#561); replaced by DraggableItem (see 24). `AppHeader.vue` deleted #524; `aiCompletions.ts` deleted #540 (LLM transparency rework). **24. The draggable-panel/overlay saga (longest-running UI pain).** Almost a dozen PRs across six months: `c224c866` #141 (resize alignment) → `2d31ff35` #198 (z-index bring-to-front) → `47d3f587` #216 (remember position/size) → `5bfaee7b` #221 (canvas/panel resize) → `33bc7ef7` #305 (layout not resetting on viewport change) → `e3703152` #314 (**unbounded z-index growth**) → `42094e9d` #439 → `a1e397d1` #494 (Tauri overlay) → `e4b72ff4` #557 (improve dynamic screens) → `ed6a12c6` #559 (hotfix sizing) → `1c549242` #561 (2026-07-02) ports the rebuilt **DraggableItem** system (per-axis resize scale/fill/fixed, tab-strip headers) into `flowfile_wasm`, deleting `DraggablePanel` and its three Pinia stores. Branch `claude/draggable-item-sizing-kpu3cn` (2026-07-01, 1 ahead) continues sizing work on top. **Rule: build new overlay work on `DraggableItem` + `drawerRegistry`, never resurrect `DraggablePanel` patterns; z-index growth and viewport-resize regressions are the two failure modes that recurred repeatedly.** **25. Run-history fix cluster.** `62774106` (#505, 2026-06-11) → `636975c5` (#507, 2026-06-11) → `b776bf2c` (#501, 2026-06-09) — three PRs in one week on the same subsystem, plus unmerged branches `fix/to-flow-version-from-run-history` **and a typo twin** `fix/to-flow-version-from-run-histroy` (both 2026-06-11, 1 ahead each) and `fix/flowfile-lite-run-history-on-error`. **Rule: run-history/flow-version linkage is fragile — read all three merged commits and check the two near-duplicate branches before modifying this area.** **26. Polars 1.43.0 SQLContext-over-delta deadlock (the silent CI hang).** Symptom: after bumping polars 1.39.3→1.43.0 (branch `maintance/polars-version-bump`, PR #617, 2026-07-26), every CI job hung with **zero output** until the job timeout — kernel job stalled after `test_artifact_lineage_kernel_integration` PASSED (20 min kill), all five backend matrix jobs (incl. Windows/macOS, no Docker) stalled entering `tests/docs_examples/` (40 min kill). Root cause: on polars 1.43.0 + deltalake 1.6.2, `pl.SQLContext.execute("SELECT ...")` over a registered `pl.scan_delta(...)` frame **deadlocks in Rust while holding the GIL** — 5-line repro, main thread, no threads/network needed; plain `scan_delta().collect()`/`collect_schema()` are fine. Every catalog SQL reader / virtual view (schema prediction via `_add_catalog_sql_reader`'s `SQLContext`, view materialization) freezes the whole process — new threads can't even bootstrap (`Thread.start()` never returns). 1.40.1 / 1.41.2 / 1.42.1 all pass; no upstream issue existed as of 2026-07-26. Silence explained: the hang sites emit nothing before the wedge (fixture `write_delta` line / `runpy` of a docs example) and pytest-timeout (1800s) exceeds the CI job timeouts, so no traceback ever printed. Fix: ceiling `<1.43` (later relaxed to `!=1.43.0, !=1.43.1, <1.44` once 1.43.2 fixed it); same change also caught the kernel image pins in `kernel/manager.py` still at 0.5.1 after the 0.5.2 bump (masked because CI never reached `test_kernel_image_pin_sync.py`). **Rule: before raising the polars ceiling, run the SQLContext-over-scan_delta repro (root CLAUDE.md "Things to Avoid"); and treat a totally-silent CI timeout as "a test wedged before its first log line" — check what the *next* collected test would be, not the last one printed.** --- ## Tag and release gotchas **A tag literally named `main` exists.** `git show-ref main` prints three refs: `refs/heads/main` and `refs/remotes/origin/main` at current HEAD, plus `refs/tags/main` at `f6cb5f27` — a January-2026 commit ("Fix Windows Electron build by installing pyinstaller (#185)"). Any command resolving the bare name `main` (e.g. `git log main..branch`) warns `refname 'main' is ambiguous` and may silently resolve to the **tag** instead of the branch, giving a wildly wrong range. Always write `refs/heads/main` or `origin/main` explicitly. **Release tags do NOT stop at v0.9.4 — that's a sort-order illusion.** A naive `git tag | tail` or `git ls-remote --tags origin | tail` appears to end at `v0.9.4` (2026-05-07). This is plain-string alphabetical sort: `git tag` without `-V` sorts `"v0.10.0"` before `"v0.9.4"` character-by-character (`'1' < '9'`), so every `v0.10.x`–`v0.12.x` tag sorts *earlier* and gets truncated off a naive `tail`. Tags track the in-repo version closely. Correct check: `git tag | sort -V | tail -5` (or `git ls-remote --tags origin | awk -F/ '{print $3}' | sort -V | tail -5` for the remote). **Rule: if an unsorted `tail` on tags implies "releases stopped," re-check with `sort -V` before writing that into any doc or decision** — this exact mistake is easy to make. --- ## Live / stalled branch inventory Squash-merging means ~50 local `feature/*`/`fix/*` branches remain "ahead" of main even after landing — ahead-count alone does **not** mean stalled work. Confirmed-live as of 2026-07-03: | Branch | Ahead | Status | |---|---|---| | `claude/core-abstraction-flowgraph-001hrn` | 7, active 2026-07-02 | Unmerged: `WorkerTransport`, `ExecutionBackend` seam, `NodeSpec` registry, declarative node builders. If merged, reshapes how nodes/execution are added — **pending direction**, not current truth. | | `fix/ga-4-when-run-from-workflow` | 3 | Leftover — content landed via the #490 squash merge; ignore. | **Rule: unmerged `claude/*` branches are an idea graveyard, not authority.** Dozens exist from one-shot Claude sessions and several were deliberately abandoned (e.g. `claude/fix-flow-tab-display-kM4qD`'s same-session self-revert in 15, `claude/plan-future-features-*`, `claude/research-revenue-opportunities-*`). A branch that "already does X" is not evidence X is the right design — check *why* it wasn't merged before reusing it. --- ## Verified and rejected — do not re-investigate these - **Lowering the worker's 0.5s poll interval.** The hot path is the WebSocket push transport (`streaming.py`, incident 21), not HTTP polling — polling is a fallback, not the latency-critical path. - **Removing the catalog's ~1.05s sleep in tests** (`flowfile_core/tests/test_catalog_dashboards.py:194`, `time.sleep(1.05)`). Load-bearing: SQLite's timestamp columns here have **1-second granularity**, so two writes in the same second are indistinguishable without it. The ~2s cancel-flow sleeps elsewhere wait out a real polling/settle interval for the same reason. - **Turning on coverage's `parallel=true` without pytest-xdist and `coverage combine`.** Tried, actively harmful: `parallel=true` writes per-process `.coverage..` fragments expecting a combine step; without both you get an **empty `coverage.xml`** — worse than not setting the flag. xdist itself was deliberately deferred (shared SQLite test DB needs per-worker isolation first) as part of incident 19. - **Downgrading FastAPI back to `~0.115.2`.** It was raised to `~0.142.2` on 2026-10-01 to clear Starlette CVEs (incident 17); a 422 on a JSON call means a missing `Content-Type` header, not a reason to revert. --- ## Provenance and maintenance Re-run these before trusting a fact above that looks stale (verified 2026-07-03 at `f6963c77`, v0.12.7): ```bash git rev-list --count HEAD # commit count git show-ref main # tag-named-main trap git tag | sort -V | tail -5 # correct latest-tag check (not `tail` alone) git ls-remote --tags origin | awk -F/ '{print $3}' | sort -V | tail -5 git log -1 --format='%ad %s' --date=short # spot-check any cited commit grep -n "def drain_result_queue" flowfile_worker/flowfile_worker/spawner.py # incident 1 grep -n "def is_descending" flowfile_core/flowfile_core/schemas/transform_schema.py # 14 cat shared/_version.py # incident 3 ls flowfile_core/flowfile_core/alembic/versions/ | grep -c '^[0-9]' # incident 18 grep -n "time.sleep(1.05)" flowfile_core/tests/test_catalog_dashboards.py # verified-rejected grep -n '^fastapi' pyproject.toml # incident 17 ``` When you land a fix for something chronicled here, or a branch above finally merges or gets abandoned, update its **Status** in place rather than adding a new incident — one entry per root cause keeps this a lookup table, not a duplicate-riddled log.