Skip to content

fix(ci): make the release guard reject only genuinely untagged pull requests - #105

Merged
carhartlewis merged 11 commits into
mainfrom
lewis/analytics-tracking
Aug 11, 2026
Merged

fix(ci): make the release guard reject only genuinely untagged pull requests#105
carhartlewis merged 11 commits into
mainfrom
lewis/analytics-tracking

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Opened automatically when lewis/analytics-tracking was pushed.

The title is written from the diff and rewritten as you push, because this is squashed onto main and the title becomes the commit subject and the changelog line. Retitle it yourself and it is yours — the automation stops touching it.


Summary by cubic

Adds first‑party website tracking with form capture and source attribution, plus a settings UI and CRM views to see visitor activity. Includes CI and test stability fixes, and hardens tracking with safer config caching, counter release, and better duplicate‑submission handling.

  • Bug Fixes

    • Config caching: only writes to cache when the current hash matches to prevent stale reads; clarified cache invalidation behavior.
    • Counters: handle zero/negative increments and add a release method to decrement counters; improved hourly contact cap so duplicate submissions don’t consume CONTACTS_PER_HOUR.
    • Docs: clarified referrer matching rules (including country‑code requirement) and concurrency around contact attachment; added tests for counter and filing behavior.
  • Migration

    • Run database migrations.
    • Set CRON_SECRET and (recommended) REDIS_URL for shared counters and compiled config.
    • Enable tracking in Settings → Tracking & Analytics, add allowed domains, and copy the snippet:
      • <script src="/t/crm.js" data-site="cmp_XXXXXXXX" defer></script>
    • Deploy so /t/crm.js and /t/[site].js are publicly reachable.

Written for commit 0c28e0a. Summary will update on new commits.

Review in cubic

… retention services

- Added TrackingModule to encapsulate tracking functionality.
- Implemented TrackingIngestService for handling incoming tracking events.
- Created TrackingFilingService to manage form submissions and contact filing.
- Introduced TrackingRollupService for daily aggregation of tracking data.
- Developed TrackingConfigService for managing tracking configurations.
- Added TrackingCounterService to handle rate limiting for submissions.
- Implemented TrackingController and TrackingRetentionController for API endpoints.
- Created necessary contracts and router for tracking operations.
- Updated .env.example and AGENTS.md to reflect new tracking features.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crm-agent Ready Ready Preview Aug 11, 2026 2:18am
crm-api Ready Ready Preview Aug 11, 2026 2:18am
crm-app Ready Ready Preview Aug 11, 2026 2:18am

Request Review

…ts for better modularity

- Removed TrackingSections component and integrated its functionality directly into the Tracking page.
- Added individual components: TrackingScript, VerifyInstallation, TrafficSources, TrackingRules, AllowedDomains, and TrackingCookies.
- Updated data fetching logic to handle settings readiness before rendering components.
@github-actions github-actions Bot changed the title feat(tracking): add website tracking with form capture and attribution feat(tracking): add website tracking with form capture and retention Aug 11, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/api/src/tracking/tracking.controller.ts">

<violation number="1" location="apps/api/src/tracking/tracking.controller.ts:153">
P2: Retention stops after 500,000 rows per nightly run, so a backlog or sustained allowed traffic leaves events older than 90 days indefinitely. Continue deletion asynchronously/add runs until caught up, or explicitly surface and schedule continuation for an incomplete sweep.</violation>
</file>

<file name="AGENTS.md">

<violation number="1" location="AGENTS.md:15">
P3: The new rows points contributors to docs/tracking.md, but that file does not exist in the repo (verified across the whole tree); every other doc referenced in this table exists. Since AGENTS.md's first rule is 'Read the doc for the area you are touching before you touch it', this row sends developers to a missing file and the tracking doc is never actually enforced. Add docs/tracking.md in this PR or drop the row until it exists.</violation>
</file>

<file name="apps/api/src/tracking/tracking-rollup.service.ts">

<violation number="1" location="apps/api/src/tracking/tracking-rollup.service.ts:22">
P1: Daily rows undercount the retention-boundary day because consecutive runs split one calendar day and retain only the larger partial aggregate. Roll and delete whole completed days (or otherwise merge non-overlapping partial slices) so both portions are preserved.</violation>
</file>

<file name="apps/app/app/(app)/[slug]/settings/tracking/traffic-sources.tsx">

<violation number="1" location="apps/app/app/(app)/[slug]/settings/tracking/traffic-sources.tsx:30">
P1: Non-managers visiting a ready tracking settings page receive a forbidden `sources` prefetch and the route render fails; render/prefetch TrafficSources only when `tracking.data.canManage` is true (or make the query available to authorized page viewers).</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread apps/app/app/t/[site]/route.ts Outdated
Comment thread apps/api/src/tracking/tracking.service.ts Outdated
Comment thread apps/api/src/tracking/tracking.service.ts Outdated
Comment thread apps/api/src/tracking/tracking-ingest.service.ts Outdated
Comment thread apps/api/src/tracking/tracking-ingest.service.ts Outdated
Comment thread apps/app/components/crm/record-sheet/company-sheet.tsx Outdated
Comment thread apps/app/app/(app)/[slug]/settings/tracking/verify-installation.tsx Outdated
Comment thread apps/app/app/t/crm.js/route.ts Outdated
Comment thread apps/app/components/crm/website-activity.tsx Outdated
Comment thread packages/db/src/tracking.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/app/app/(app)/[slug]/settings/tracking/page.tsx Outdated
…ate UI components

- Introduced a new `normalizePath` function to standardize path inputs in the TrackingIngestService.
- Updated path handling in event processing to utilize `normalizePath`.
- Enhanced the TrafficSources component description for clarity on visitor attribution.
- Refactored the WebsiteActivity component to improve layout and detail presentation, including the addition of new properties for better tracking insights.
- Simplified the CompanyOverview component by removing unnecessary props and streamlining its structure.
@github-actions github-actions Bot changed the title feat(tracking): add website tracking with form capture and retention feat(tracking): implement tracking module with ingestion, filing, and retention services Aug 11, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/db/src/tracking.ts">

<violation number="1" location="packages/db/src/tracking.ts:161">
P3: The `?? "/"` fallback after `raw.split(/[?#]/)[0]` is dead code: `String.prototype.split` always returns an array with at least one element (the original string when there is no match), so `[0]` is always a defined string and the fallback is unreachable. This is harmless but misleading — a reader may assume the fallback guards an empty result. Simplify to `const path = raw.split(/[?#]/)[0];` to keep the intent clear. (The rest of the normalization — query/fragment stripping, leading-slash check, and trailing-slash collapse — correctly implements the one-page-is-one-row goal.)</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread packages/db/mksession.ts Outdated
Comment thread packages/db/mksession.ts Outdated
const raw = input?.trim();
if (!raw) return "/";

const path = raw.split(/[?#]/)[0] ?? "/";

@cubic-dev-ai cubic-dev-ai Bot Aug 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The ?? "/" fallback after raw.split(/[?#]/)[0] is dead code: String.prototype.split always returns an array with at least one element (the original string when there is no match), so [0] is always a defined string and the fallback is unreachable. This is harmless but misleading — a reader may assume the fallback guards an empty result. Simplify to const path = raw.split(/[?#]/)[0]; to keep the intent clear. (The rest of the normalization — query/fragment stripping, leading-slash check, and trailing-slash collapse — correctly implements the one-page-is-one-row goal.)

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/src/tracking.ts, line 161:

<comment>The `?? "/"` fallback after `raw.split(/[?#]/)[0]` is dead code: `String.prototype.split` always returns an array with at least one element (the original string when there is no match), so `[0]` is always a defined string and the fallback is unreachable. This is harmless but misleading — a reader may assume the fallback guards an empty result. Simplify to `const path = raw.split(/[?#]/)[0];` to keep the intent clear. (The rest of the normalization — query/fragment stripping, leading-slash check, and trailing-slash collapse — correctly implements the one-page-is-one-row goal.)</comment>

<file context>
@@ -154,6 +154,18 @@ export function normalizeHost(input: string | null | undefined): string | null {
+	const raw = input?.trim();
+	if (!raw) return "/";
+
+	const path = raw.split(/[?#]/)[0] ?? "/";
+	if (!path.startsWith("/")) return "/";
+
</file context>
Fix with cubic

Comment thread apps/app/components/crm/website-activity.tsx Outdated
… session management code

- Removed the `shouldDehydrateQuery` option from the query client configuration for cleaner setup.
- Deleted the `mksession.ts` file, which contained session management logic that is no longer needed.
@github-actions github-actions Bot changed the title feat(tracking): implement tracking module with ingestion, filing, and retention services fix(app): add website tracking with form-to-contact filing Aug 11, 2026
- Updated RollupService to track contacts created and cap reasons for submissions.
- Refactored TrackingConfigService to manage cache generation for configuration.
- Enhanced TrackingCounterService to allow variable increment amounts for event limits.
- Improved TrackingFilingService to handle race conditions when creating contacts.
- Updated TrackingIngestService to streamline event processing and rate limiting.
- Added integration tests for tracking ingestion and filing functionalities.
- Enhanced documentation for tracking features and their configurations.
@github-actions github-actions Bot changed the title fix(app): add website tracking with form-to-contact filing feat(tracking): add website tracking with form capture and attribution Aug 11, 2026
- Updated TrackingConfigService to manage cache generation and prevent stale reads.
- Improved TrackingFilingService to handle race conditions when filing submissions.
- Enhanced TrackingIngestService to filter events based on type and limit host length.
- Refactored TrackingService to optimize database queries for tracked visitors.
- Introduced TrackingSections component to modularize tracking settings UI.
- Updated documentation to clarify referrer matching rules and cache invalidation behavior.
- Added integration tests for filing and ingesting submissions to ensure correct behavior.
- Eliminated unnecessary check for filedAt in the batch delivery test to streamline the integration test logic.
- Ensured that the test still verifies the correct behavior of submissions being filed only once.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 26 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/api/src/tracking/tracking-config.service.ts">

<violation number="1" location="apps/api/src/tracking/tracking-config.service.ts:27">
P1: Tracking changes can be undone in the shared cache when API replicas race: this process-local generation does not protect Redis writes from another replica. Use a shared/atomic config version (or distributed lock/CAS) before accepting a cache write, so paused, rotated, or domain-restricted config cannot be replaced with stale data for the TTL.</violation>
</file>

<file name="packages/db/src/attribution.ts">

<violation number="1" location="packages/db/src/attribution.ts:222">
P2: Referrers such as `google.com.example` are classified as trusted Google organic traffic because any `com.<label>` tail is accepted as a suffix. Use a Public Suffix List-based registrable-domain check (or a verified suffix mapping) so lookalike domains remain referrals.</violation>
</file>

<file name="apps/api/src/tracking/tracking-ingest.service.ts">

<violation number="1" location="apps/api/src/tracking/tracking-ingest.service.ts:212">
P2: Concurrent duplicate deliveries can consume multiple hourly-contact cap slots and race contact creation because both see the unfiled row and call `file()` before either `attach()` claims it. Claim the submission (or add an in-progress state) atomically before filing, while retaining a recoverable retry path for interrupted filings.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

export class TrackingConfigService {
private readonly logger = new Logger(TrackingConfigService.name);

private generation = 0;

@cubic-dev-ai cubic-dev-ai Bot Aug 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Tracking changes can be undone in the shared cache when API replicas race: this process-local generation does not protect Redis writes from another replica. Use a shared/atomic config version (or distributed lock/CAS) before accepting a cache write, so paused, rotated, or domain-restricted config cannot be replaced with stale data for the TTL.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/tracking/tracking-config.service.ts, line 27:

<comment>Tracking changes can be undone in the shared cache when API replicas race: this process-local generation does not protect Redis writes from another replica. Use a shared/atomic config version (or distributed lock/CAS) before accepting a cache write, so paused, rotated, or domain-restricted config cannot be replaced with stale data for the TTL.</comment>

<file context>
@@ -24,6 +24,8 @@ export interface CompiledConfig {
 export class TrackingConfigService {
 	private readonly logger = new Logger(TrackingConfigService.name);
 
+	private generation = 0;
+
 	constructor(
</file context>
Fix with cubic

Comment thread apps/app/app/(app)/[slug]/settings/tracking/tracking-sections.tsx Outdated
Comment thread apps/api/src/tracking/tracking-filing.service.ts
Comment thread packages/db/src/attribution.ts Outdated
if (tail.length === 1) return true;
if (tail.length !== 2) return false;

return SECOND_LEVEL.has(tail[0] ?? "");

@cubic-dev-ai cubic-dev-ai Bot Aug 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Referrers such as google.com.example are classified as trusted Google organic traffic because any com.<label> tail is accepted as a suffix. Use a Public Suffix List-based registrable-domain check (or a verified suffix mapping) so lookalike domains remain referrals.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/src/attribution.ts, line 222:

<comment>Referrers such as `google.com.example` are classified as trusted Google organic traffic because any `com.<label>` tail is accepted as a suffix. Use a Public Suffix List-based registrable-domain check (or a verified suffix mapping) so lookalike domains remain referrals.</comment>

<file context>
@@ -177,13 +190,38 @@ function mediumFrom(value: string | null | undefined): Medium {
+	if (tail.length === 1) return true;
+	if (tail.length !== 2) return false;
+
+	return SECOND_LEVEL.has(tail[0] ?? "");
+}
+
</file context>
Fix with cubic

Comment thread apps/api/src/tracking/tracking-counter.service.ts Outdated
});

if (!submission) return;
if (created.count === 0 && !unfiled(submission)) return;

@cubic-dev-ai cubic-dev-ai Bot Aug 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Concurrent duplicate deliveries can consume multiple hourly-contact cap slots and race contact creation because both see the unfiled row and call file() before either attach() claims it. Claim the submission (or add an in-progress state) atomically before filing, while retaining a recoverable retry path for interrupted filings.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/tracking/tracking-ingest.service.ts, line 212:

<comment>Concurrent duplicate deliveries can consume multiple hourly-contact cap slots and race contact creation because both see the unfiled row and call `file()` before either `attach()` claims it. Claim the submission (or add an in-progress state) atomically before filing, while retaining a recoverable retry path for interrupted filings.</comment>

<file context>
@@ -164,20 +197,19 @@ export class TrackingIngestService {
 		});
 
 		if (!submission) return;
+		if (created.count === 0 && !unfiled(submission)) return;
 
 		const outcome = await this.filing.file({
</file context>
Fix with cubic

Comment thread apps/api/test/tracking-ingest.integration.spec.ts
Comment thread apps/api/test/tracking-ingest.integration.spec.ts Outdated
The release guard read `gh pr list --label`, which goes through GitHub's
eventually-consistent search index. release-please swaps `autorelease: pending`
for `autorelease: tagged` about a second before the guard runs, so every release
run flagged the pull request it had just tagged: v1.4.0, v1.5.0 and v1.5.1 were
all released correctly and all three runs went red. Confirm each candidate
against the REST API, which is strongly consistent, and only fail on one that is
still genuinely pending.

`turbo run test` ran the packages in parallel against a single database, so
`apps/api`, `apps/agent`, `packages/auth` and `packages/telemetry` interleaved
their fixtures and their writes to the workspace organization row, the reporting
currency and the exchange-rate table. Three runs in four failed, on a different
test each time. Serialise the task: six consecutive runs clean, ten seconds
slower.
- Updated TrackingConfigService to ensure cache is only set when the current hash matches.
- Refactored TrackingCounterService to handle zero or negative amounts gracefully and added a release method for decrementing counter values.
- Improved TrackingFilingService to manage contact limits more effectively and removed redundant withinCap check.
- Enhanced integration tests to verify correct behavior of counters and filing logic under various conditions.
…ndling

- Updated documentation to specify that the last DNS label must be a two-letter country code for accurate referrer classification.
- Enhanced explanation of the `SECOND_LEVEL` heuristic to prevent false positives in referral tracking.
- Improved handling of contact creation to ensure duplicate submissions do not affect the hourly quota.
- Clarified the behavior of the `attach` method to prevent concurrent delivery issues.
…entation

- Enhanced documentation to explain the behavior of `CONTACTS_PER_HOUR` and the handling of duplicate submissions.
- Clarified the `attach` method's row claiming process to prevent issues with concurrent deliveries.
- Improved explanation of cache invalidation mechanisms, emphasizing the importance of per-process generation and shared database hash for consistency.
@github-actions github-actions Bot changed the title feat(tracking): add website tracking with form capture and attribution fix(ci): make the release guard reject only genuinely untagged pull requests Aug 11, 2026
@carhartlewis
carhartlewis merged commit 815a832 into main Aug 11, 2026
9 checks passed
@carhartlewis
carhartlewis deleted the lewis/analytics-tracking branch August 11, 2026 02:22
This was referenced Aug 11, 2026
carhartlewis added a commit that referenced this pull request Aug 11, 2026
The titler regenerated the title on every push once it had written one, so a
pull request's subject was whatever its *last* commits looked like. #105 carried
the whole website-tracking feature and was retitled `fix(ci)` by its final push,
squashed onto main under that subject, and released as a patch whose notes
mention none of it.

A generated title is now left alone unless it stops being a conventional commit
or stops covering the branch, and no title — generated or typed — may release
less than the commits behind it: `floor_of` takes the strongest bump on the
branch and `generate` raises its proposal to meet it. A branch holding a `feat`
cannot ship as a `fix`, and one holding a breaking change cannot ship without
the `!`. Over-releasing is the safe direction; losing a feature out of the
changelog is not.
strats360 pushed a commit to strats360/crm that referenced this pull request Aug 24, 2026
* Implement currency conversion features and enhance deal handling

- Introduced a new CurrencyModule to manage currency conversion and rates.
- Added ConversionService for handling currency conversions and fetching rates.
- Updated DealsService to support base amounts and currency conversion logic.
- Enhanced Deal and Dashboard functionalities to include reporting currency and unconverted deals.
- Implemented new currency-related contracts and routes for setting reporting currency and manual rates.
- Added integration tests to ensure correct handling of currency conversions and deal totals.

* Refactor currency rates service to use open.er-api.com

- Updated the currency rates service to fetch exchange rates from open.er-api.com, replacing the previous provider frankfurter.dev.
- Enhanced error handling to check for unsupported base currencies in the response.
- Implemented retry logic for fetching rates with a maximum of two attempts and a reduced timeout.
- Cleaned up stale exchange rates for unsupported currencies during the refresh process.
- Updated documentation to reflect the new exchange rate provider and its implications.

* Enhance currency handling and conversion logic

- Introduced baseCurrency to the Deal model to track the currency of baseAmount.
- Updated ConversionService to streamline currency conversion processes and improve deal field handling.
- Enhanced CurrencyService to enforce permissions for managing currency settings based on user roles.
- Refactored DealsService to incorporate base currency logic in deal aggregations and reporting.
- Improved DashboardService to accurately reflect open deal values based on the current reporting currency.
- Updated integration tests to validate new currency handling features and ensure correct behavior across services.

* Enhance currency conversion logic and improve deal handling

- Updated `pendingWhere` method in `ConversionService` to explicitly match null `baseCurrency`, ensuring no deals are excluded from totals.
- Added integration test to verify that deals with missing currency are correctly handled and updated.
- Modified seeding logic to ensure `baseCurrency` is set alongside `baseAmount` for newly created deals, preventing issues with unconverted figures.
- Updated documentation to clarify changes in currency handling and the implications for deal visibility.

* Refine currency conversion logic and enhance deal handling

- Updated `ConversionService` to conditionally clear rates only when `onlyMissing` is false, improving efficiency in handling missing currencies.
- Enhanced integration tests to verify correct behavior when dealing with unconverted figures and missing currency rates.
- Introduced a new utility function in the deal sheet component to manage currency options, ensuring proper display of unsupported currencies.

* Revise agent and API documentation for clarity and structure

- Updated AGENTS.md to emphasize the importance of reviewing relevant documentation before starting work, including a new index table for quick reference.
- Refined API rules in api.md to clarify logging practices and the separation of intelligence from the API.
- Consolidated environment setup instructions into a new setup.md file for better organization and ease of access.
- Enhanced currency handling in DashboardService and related tests to ensure accurate reporting and conversion logic.
- Improved integration tests to validate new currency handling features and ensure correct behavior across services.

* Enhance documentation and introduce new currency handling guidelines

- Updated AGENTS.md to include new references for the Agent panel and local setup instructions.
- Added a new docs/agent-panel.md file detailing the Agent panel's functionality and usage.
- Introduced docs/currency.md to clarify currency handling rules and reporting practices.
- Revised environment setup instructions in docs/environment.md for better clarity and organization.

* Add anonymous usage telemetry documentation and enhance currency handling in DealSheet

* Implement anonymous usage telemetry and enhance related documentation

- Added telemetry functionality to track anonymous usage data, including installation metrics and tool usage.
- Introduced new environment variables for telemetry configuration in `.env.example`.
- Updated `AGENTS.md` to reference the new telemetry documentation.
- Created a `TelemetryModule` with services and controllers for managing telemetry data.
- Added a settings page for telemetry configuration in the application.
- Enhanced error handling and logging for telemetry events across various services.
- Removed outdated ADR on telemetry usage from the repository.

* Remove telemetry-related components and references from the application

- Deleted the TelemetryRouter and its associated service, removing the telemetry status query.
- Updated the settings sidebar to eliminate the Telemetry option.
- Removed the TelemetrySettingsPage and its related components, including the TelemetryStatus display.
- Cleaned up unused imports and references to telemetry throughout the codebase.

* Enhance telemetry functionality and improve budget management

- Added an 'exhausted' state to the focus management to track when the research budget is depleted.
- Updated the spend function to prevent multiple budget exhaustion events from being recorded.
- Refactored the rollup service to handle telemetry rollup claims and restore counters more effectively.
- Improved error handling in telemetry events to ensure proper reporting and recovery from failures.
- Enhanced documentation to clarify the behavior of telemetry when disabled and the implications for data integrity.

* Add telemetry support and enhance landing page analytics

- Introduced `@crm/telemetry` package to manage telemetry configurations and constants.
- Integrated `posthog-js` for analytics on the landing page, ensuring it only runs on allowed domains.
- Updated the `LandingAnalytics` component to initialize analytics tracking based on hostname.
- Enhanced the `audit` hook to exclude specific event types from archiving.
- Improved agent session handling by implementing offline thread management.
- Added utility functions for analytics host validation and created tests for the new functionality.
- Updated documentation to reflect changes in telemetry usage and landing page analytics.

* Update agent panel to use SETTLED_TTL_MS for archive stale time and enhance documentation

- Changed the `staleTime` for the archive query in the agent panel from `Infinity` to `SETTLED_TTL_MS` to ensure proper session management.
- Updated documentation to clarify the behavior of the archive in relation to session state and stale time handling.

* Enhance landing page analytics with CTA event tracking

- Introduced `captureLanding` function to track user interactions with the setup prompt and GitHub star buttons.
- Updated `SetupPromptButton` and `GitHubStarButton` components to accept a `location` prop for distinguishing between 'hero' and 'closing' CTAs.
- Modified `LandingAnalytics` to include new event types for clipboard actions and button clicks.
- Enhanced documentation to reflect the new telemetry events and their usage.

* Update README with new images and remove outdated ones

- Replaced outdated images with new visuals for the landing page, showcasing agents and capabilities.
- Removed references to deleted images related to deals, contacts, and companies to streamline documentation.

* Refactor README to improve layout of screenshots

- Converted individual screenshot sections into a table format for better visual organization.
- Updated captions for clarity and conciseness, enhancing the overall presentation of the landing page visuals.

* Update README and images for landing page

- Removed outdated captions from the README for agents and capabilities images to streamline content.
- Updated binary images for agents, capabilities, and hero sections to enhance visual quality on the landing page.

* Update README and replace landing hero image

- Updated the README to reflect the new image caption for the companies list.
- Replaced the outdated landing hero image with a new product shot to enhance visual appeal.
- Removed the old landing hero image from the repository.

* Update landing page images for agents and capabilities

- Replaced existing binary images for agents and capabilities on the landing page to improve visual quality and consistency.
- Ensured that the new images align with the recent updates to the README and overall landing page design.

* Update landing page images for agents and capabilities to enhance visual quality

* Refactor AddButton component in multiple sheets to use ComponentProps for better type safety

- Updated the AddButton function in create-company-sheet, create-contact-sheet, create-deal-sheet, and add-sso-provider-sheet to accept props of type ComponentProps from the Button component.
- This change enhances type safety and allows for more flexible button properties across different sheets.

* Refactor TelemetryService to integrate RollupService for telemetry rollups

- Replaced FunnelService with RollupService in TelemetryService to handle telemetry rollups.
- Implemented a timer to run rollups hourly, enhancing telemetry data collection.
- Updated documentation to reflect changes in telemetry rollup processes and clarify the in-process execution without cron dependencies.

* Report installs without a cron, and stop double counting them

The install count was reading 1 while 20 databases had migrated. Every
"Active installs" tile is built on install_daily, which only ever fired
from POST /internal/telemetry/rollup — a route that refuses to run
without CRON_SECRET. An install that never configures a cron reported
nothing at all, however much it was used.

TelemetryService now rolls up in-process, on boot and hourly. The
existing row lock on install makes all but the first of those a no-op,
and it short-circuits before the aggregation runs, so it is still one
set of grouped queries per install per day. The route stays, still
behind CRON_SECRET, for a platform cron that would rather drive it;
nothing depends on it now.

Two ways the same event could arrive twice, both of which the hourly
timer would have made more frequent:

A rollup wrongly read as failed hands the day back and is re-sent.
posthog-node does not reject on a failed send, so the client inferred
failure from a module-global error counter that any other capture could
move. It now enqueues and awaits flush(), which does throw, and treats
either signal as a failure — erring toward a re-send, which is free,
over consuming a day whose event never left.

A milestone sent before it was recorded, so both the boot sweep and the
rollup sweep could send the same step. One install sent
first_fact_applied six times. The insert is now the claim: of two
sweeps exactly one is told it landed the row, and only that one sends.
A failed send deletes the row so the step is retried.

Both events also carry a deterministic uuid derived from the install
and the day (or the step), so a duplicate that does get out is ingested
once. Installs and active installs were always safe — PostHog's unique
math is per install per day — but the summed agent-usage properties
were not.

* Derive the dedupe id with SHA-256 in a v8 uuid

CodeQL flags a weak algorithm reached by the install identity, and it
is right that the two do not belong in one expression. SHA-1 was there
only because RFC 4122 defines v5 that way; nothing depends on being a
v5, so this is a SHA-256 digest in a v8 uuid, the slot RFC 9562 leaves
for a derivation of one's own.

* CMP-1 chore: enrich agentic experience

* ci: open pull requests, gate titles and promote releases automatically (trycompai#53)

* Lewis/contact and currencies (trycompai#56)

* Implement fields management features (trycompai#55)

* Lewis/dynamic field fix (trycompai#70)

* Refactor query prefetching in Companies, Contacts, and Deals pages to… (trycompai#71)

* chore: release main (trycompai#72)

* feat(api): add microsoft sign-in and outlook mailbox sync (trycompai#73)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore: release release

* ci: run release-please on main and document merge order (trycompai#76)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: release main (trycompai#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(db): CMP-1 persist durable custom agents (trycompai#67)

* feat(agent): CMP-1 add sandboxed builder and runner runtimes (trycompai#60)

* refactor(app): CMP-59 harden CRM UI foundations (trycompai#61)

* feat(app): CMP-46 add the private agent builder workspace (trycompai#62)

* feat(app): CMP-12 review agent drafts before deployment (trycompai#63)

* fix(app): CMP-47 consolidate agent builder presentation (trycompai#64)

* feat(app): CMP-47 add inline composer context

* fix(app): move chat beneath overview in icon rail (trycompai#83)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>

* fix(ci): tag releases automatically and keep previews off the production schema (trycompai#82)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.4.0 (trycompai#86)

* feat(agent): bound agent builder retries and improve chat scrolling (trycompai#89)

* fix(app): render agent transcript chronologically with anchored tool results (trycompai#92)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* fix(agent): declare granted write actions in draft access summary (trycompai#93)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>

* chore(main): release 1.5.0 (trycompai#91)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(api): warn when the deployed schema does not match schema.prisma (trycompai#88)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* CMP-62 chore: add gh-stack skill (trycompai#96)

* chore(main): release 1.5.1 (trycompai#97)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(ci): make the release guard reject only genuinely untagged pull requests (trycompai#105)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* fix(ci): stop the auto-titler downgrading a release

The titler regenerated the title on every push once it had written one, so a
pull request's subject was whatever its *last* commits looked like. trycompai#105 carried
the whole website-tracking feature and was retitled `fix(ci)` by its final push,
squashed onto main under that subject, and released as a patch whose notes
mention none of it.

A generated title is now left alone unless it stops being a conventional commit
or stops covering the branch, and no title — generated or typed — may release
less than the commits behind it: `floor_of` takes the strongest bump on the
branch and `generate` raises its proposal to meet it. A branch holding a `feat`
cannot ship as a `fix`, and one holding a breaking change cannot ship without
the `!`. Over-releasing is the safe direction; losing a feature out of the
changelog is not.

* feat(tracking): add website tracking with form capture and attribution

A first-party script on the marketing site, a collector in the API, and one
rule: a form submission becomes a contact. Page views, click labels and
first/last-touch attribution hang off that, with a 90-day retention sweep, an
hourly contact cap and a per-minute event budget.

The work landed in 815a832. The auto-titler had retitled its pull request
`fix(ci)` on the last push, so it squashed onto main under that subject and
released as a patch whose notes describe only the guard fix. This commit carries
no code — it exists so the changelog and the version say what actually shipped.
See docs/tracking.md.

* chore(main): release 1.6.0 (trycompai#106)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(ci): make a release one pull request instead of two

Shipping was a release pull request into `main` and a promotion pull request
into `release`, open at the same time, with a warning on the promotion telling
you to merge the other one first. Merge them the wrong way round and you shipped
untagged code and left the version behind for the next promotion. Nobody should
have to hold that rule in their head to deploy.

The tag and the code have to travel together, so the release workflow now does
it in one step: when release-please cuts the tag it merges that exact commit
into `release` through the merges API. One pull request, no order to remember,
and the tag is by construction an ancestor of what shipped. `promote.yml` is
gone.

A conflict is the one case a human still has to see, and it can only mean
somebody committed to `release` directly, so it fails the run and says so rather
than quietly leaving production behind.

Non-releasable commits now wait for the next release rather than riding a
promotion, which is the trade: `release` moves when a tag is cut and at no other
time.

* fix(ci): fall back to the pushed commit when release-please reports no sha

* chore(main): release 1.6.1 (trycompai#108)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(db): add peek script for inspecting database contents (trycompai#110)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.7.0 (trycompai#111)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(agent): apply sourced facts to empty fields automatically (trycompai#112)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.8.0 (trycompai#113)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(ci): ship releases by opening a pull request into release (trycompai#114)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.8.1 (trycompai#115)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(agent): fill blank fields on the dispatch tick instead of sign-in (trycompai#117)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.8.2 (trycompai#118)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(agent): stop suggesting a URL that already matches the field (trycompai#120)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.9.0 (trycompai#121)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(tracking): support installing the tracking tag via Google Tag Manager (trycompai#124)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.10.0 (trycompai#126)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(app): copy the tracking snippet for the selected install method (trycompai#128)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.11.0 (trycompai#129)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: edit a deployed agent, and show what Slack actually granted (CMP-77) (trycompai#109)

* chore(main): release 1.12.0 (trycompai#132)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(app): search company dropdowns instead of scrolling them (trycompai#125)

* fix(app): show select field values in record tables (trycompai#133)

* fix(agent): let the assistant chat read the deal list it is told to use (CMP-77) (trycompai#139)

* chore(main): release 1.13.0 (trycompai#136)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: add anti-slop lint rules, dead-code checks and stricter Biome constraints (CMP-80) (trycompai#145)

* refactor: clear anti-slop type assertions and conditional object spreads (CMP-81) (trycompai#146)

* docs: propose an i18n layer (trycompai#143)

* refactor: parse every remaining I/O boundary into a domain type (CMP-82) (trycompai#151)

* fix: unblock the test suite and actually install the git hooks (CMP-83) (trycompai#152)

* ci: run anti-slop lint in CI and pre-push (CMP-84) (trycompai#153)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>

* feat: enrichment queue widget (CMP-92) (trycompai#159)

* feat(agent): read people from Context.dev instead of RapidAPI (CMP-86) (trycompai#158)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>

* feat: page the enrichment queue (CMP-92) (trycompai#160)

Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>

* chore(main): release 1.14.0 (trycompai#147)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(agent): scope field backfill tasks to records missing values (trycompai#163)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.15.0 (trycompai#164)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(api): serve openapi.json and bundle swagger deps in function build (trycompai#166)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.15.1 (trycompai#167)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Lewis/openapi json (trycompai#169)

* docs(api): explain runtime openapi document and vendoring rules (trycompai#170)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* chore(main): release 1.15.2 (trycompai#171)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(app): prevent url param collision between fields sheet and table filter (trycompai#175)

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>

* fix: stop a finished enrichment reading as failed (trycompai#173)

* chore(main): release 1.15.3 (trycompai#176)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* ci: add Vercel deploy workflow

Deploys to Vercel on push to main/release branches.
Also supports manual trigger via workflow_dispatch with
environment selection (preview/production).

Uses secrets: VERCEL_API_KEY, VERCEL_ORG_ID, VERCEL_PROJECT_ID

---------

Co-authored-by: Lewis Carhart <lewis@trycomp.ai>
Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Guzman Pintos <37162183+GuzmanPintos@users.noreply.github.com>
Co-authored-by: twinprime19 <38123958+twinprime19@users.noreply.github.com>
Co-authored-by: Kiro Agent <244629292+kiro-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant