Analytics foundation: track_event hook + core instrumentation (COPLAN-20)#116
Conversation
…-20)
Adds CoPlan.configuration.track_event — a no-op-by-default lambda that
the host wires to a destination (MySQL events table / Snowflake /
Datadog). Mirrors the existing notification_handler shape.
CoPlan::Analytics.track is a thin facade used at call sites:
- page_view — ApplicationController#track_page_view (2xx HTML
GETs only; skips Turbo Frame and agent traffic)
- plan_created — Plans::Create (fires after the DB transaction
commits, so rolled-back creates do not emit)
- plan_published — PlansController#update_status, only on the
brainstorm→considering transition
- comment_created — Comment after_create_commit
- thread_resolved — CommentThread#resolve!
Handler errors are swallowed and reported via error_reporter so a
broken analytics sink never breaks a user request.
The search_performed / search_result_clicked events from the ticket
are deferred — plans index has no text search yet, and emitting fake
events would just produce misleading data. They'll land with the
search feature.
Specs (19 new examples; full suite 888/888 green):
- spec/lib/coplan/analytics_spec.rb — facade contract
- spec/services/plans/create_spec.rb — plan_created
- spec/models/coplan/comment_analytics_spec.rb — comment_created
- spec/models/coplan/comment_thread_analytics_spec.rb — thread_resolved
- spec/requests/analytics_instrumentation_spec.rb — page_view + plan_published
- spec/support/analytics_helpers.rb — capture_analytics_events
Destination recommendation for the host wiring (separate PR in
coplan-square): a simple MySQL analytics_events table. Joins cleanly
to plans/users for every Cycle 2 KPI, ~1 day to wire, trivial to
dual-write to Snowflake later by extending the handler.
Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-17e7-738c-99b6-23e54fcd386f
Co-authored-by: Amp <amp@ampcode.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ebb19f798
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if new_status == "considering" && old_status != "considering" | ||
| CoPlan::Analytics.track( | ||
| "plan_published", |
There was a problem hiding this comment.
Add plan_published tracking to API status updates
When a plan is published through the documented API metadata endpoint rather than this web-only update_status action, no plan_published event is emitted; I checked engine/app/controllers/coplan/api/v1/plans_controller.rb and its status-change path only logs the event and triggers reviews at lines 95-101. Agents are explicitly instructed to update status via the API, so publishing a brainstorm plan that way will undercount the core publish KPI even though the status transition succeeds.
Useful? React with 👍 / 👎.
| comment_thread_id: comment_thread_id, | ||
| comment_id: id, | ||
| author_type: author_type, | ||
| is_first_in_thread: first_comment_in_thread?, |
There was a problem hiding this comment.
Stop using UUID order for first-comment analytics
For replies whose randomly generated UUID sorts before the existing thread comments, this new payload records is_first_in_thread: true even though the comment is not the first one; first_comment_in_thread? compares id < ?, but these IDs are UUID strings rather than insertion-ordered values. This makes comment_created analytics unreliable for replies and can skew any first-comment/thread-start counts derived from the event.
Useful? React with 👍 / 👎.
Two findings from codex on #116: 1. plan_published was only emitted from the web update_status action, missing every publish that flows through the documented API path at PATCH /api/v1/plans/:id. Since agent users are explicitly told to use the API, that was undercounting the core Cycle 2 publish KPI. Adds the same track call to the API controller, gated identically on the brainstorm→considering transition. Both call sites now tag the event with via: "web" or via: "api" so analytics can split or unify them as needed. 2. is_first_in_thread was derived from first_comment_in_thread?, which compares UUID strings with id < ? — UUIDs are not insertion-ordered, so a reply whose ID happens to sort below the existing comments would incorrectly record is_first=true. After-create-commit fires after the row is persisted, so a total count of 1 is the reliable signal. Inlined and commented. Pre-existing first_comment_in_thread? still has the same UUID bug for notify_plan_author — out of scope here; flagged separately to Hampton. Specs: +3 new examples (API plan_published, UUID-out-of-order reply). Full suite 890/890 green. Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-17e7-738c-99b6-23e54fcd386f Co-authored-by: Amp <amp@ampcode.com>
|
Addressed both Codex findings in 4941710:
Pre-existing Specs +3, full suite 890/890 green. |
Resolves COPLAN-20.
What
Lays down the engine-side event-tracking layer that the rest of Cycle 2's KPIs depend on.
New
CoPlan.configuration.track_eventhook (no-op default). Mirrors the existingnotification_handlershape so hosts have one consistent pattern to wire.New
CoPlan::Analytics.track(event, user:, **props)facade used at every call site.Instrumentation:
page_viewApplicationController#track_page_view— fires once per successful 2xx HTML GET, skipping Turbo Frame requests, agent/non-browser requests, and non-HTML responsesplan_createdPlans::Create— emitted after the DB transaction commitsplan_publishedPlansController#update_status— only on thebrainstorm → consideringtransitioncomment_createdCommentafter_create_committhread_resolvedCommentThread#resolve!Handler errors are swallowed and reported via
error_reporterso a broken analytics sink never breaks a user request.Scope decisions
search_performed/search_result_clickeddeferred. Plans index has no text search yet — only filter-by-status/tag/plan_type and no result-click handler. Emitting events with no real semantics would just produce misleading data. They'll land alongside the actual search feature.analytics_eventstable in the host (coplan-square). It joins cleanly tocoplan_plans/coplan_usersfor every Cycle 2 KPI, takes ~1 day to wire, and is trivial to dual-write to Snowflake later by extending the handler. This PR only ships the engine hook; the host-side wiring is a follow-up PR in coplan-square that I'll confirm with Hampton first.Testing
bundle exec rspec→ 888 examples, 0 failures.Host docs
Updated
docs/HOST_APP_GUIDE.mdConfiguration reference with the newtrack_eventparameter.