The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
2.4 KiB
Compromises
Deliberate scope cuts on in-flight work, with the follow-up they imply.
Account tag workflow trigger: no loop detection or throttling yet
Workflow-driven tag adds intentionally emit $account_tag_added so workflows can chain (e.g. MRR-threshold workflow tags the account → tag-triggered workflow emails the CSM). The flip side: two workflows whose actions add/remove each other's trigger tags (tags_mode: set/remove deletes rows, so re-adding fires again) can loop indefinitely, rate-limited only by workflow execution latency. Nothing detects or breaks such cycles today — the runtime's step cap is per-invocation and its dedup is per-event-uuid, and each loop iteration is a fresh event.
Events already carry the attribution a guard needs (actor_type: "workflow" + workflow_id, forwarded via the X-Insights-Script-Flow-Id header from the CDP worker). Follow-up options, in rough order of value:
- Static pre-save warning:
InsightsFlow.triggerandInsightsFlow.actionsare JSON, so a per-team graph (edge A→B when A'stemplate-insights-update-accountaction writes tags intersecting B's trigger tags) can be cycle-checked on save/activate. Templated tag inputs ({event.properties...}) force conservative "any tag" edges, so this should warn, not block. - Runtime backstop: a chain-depth property incremented when a workflow-attributed event triggers another workflow (cap at N hops), or a throttle keyed on
(account, tag, workflow).
Account tag events: emission blocks the request thread post-commit
$account_tag_added is emitted synchronously inside transaction.on_commit (_schedule_account_tags_added in facade/api.py): one batched capture_batch_internal HTTP call per tag write, after the group-type lookup (Redis-cached). Happy path is single-digit ms; the degraded case (capture-rs slow or down) blocks the request for up to the 2s timeout times retries. A Celery offload was tried and reverted (b6c62469ee9) as too much overhead for this volume — tag adds are low-frequency, and this matches how conversation ticket events already emit.
Revisit if account tagging becomes bulk/high-frequency (e.g. CRM syncs tagging thousands of accounts). Cheapest fixes then: cap the worst case (timeout=1, max_attempts=1 — drops instead of retries during a blip), or prebuild the payload in-request and POST from a small module-level thread pool so no ORM touches the worker thread.