Skip to content

Add X social search adapter#98

Merged
khaliqgant merged 54 commits into
mainfrom
codex/x-social-search-adapter
May 18, 2026
Merged

Add X social search adapter#98
khaliqgant merged 54 commits into
mainfrom
codex/x-social-search-adapter

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Summary

  • add @relayfile/adapter-x with path-mapper helpers for /x searches, posts, users, indexes, aliases, layout docs, and digest output
  • add a budget-aware X API client for recent/archive social search with conservative page preflight checks
  • emit provider-specific LAYOUT.md, resource _index.json files, by-key aliases, and result pointers for bundled search sync output
  • register the adapter in publish target discovery and integration catalog/scope tracking docs

Validation

  • npx turbo build typecheck test --filter @relayfile/adapter-x...

Cross-repo coordination

  • Follow-up Cloud PR: codex/x-social-search-cloud
  • After merge, run the publish workflow so Cloud can consume the published @relayfile/adapter-x package and complete its dependency lock refresh.

Proactive Runtime Bot and others added 30 commits May 15, 2026 12:07
…ters

Add digest.ts and digest.test.ts for airtable, asana, clickup, hubspot,
intercom, pipedrive, salesforce, zendesk. Each digest handler classifies
provider-specific lifecycle states (completed, merged, won/lost, solved,
converted, archived, reopened) and exports deterministic bullets sorted
by event time and id. Tests cover ordering, terminal states, and empty
windows. Package index.ts files updated to re-export digest module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add state-diff inference in 5 adapters so terminal lifecycle transitions
are reflected in _webhook.action instead of being flattened to "updated":

- asana: detect task completion via change.field === "completed"
- clickup: detect task completion/archival via history_items + status type
- pipedrive: detect deal won/lost via current vs previous status comparison
- salesforce: detect case closure/lead conversion via ChangeEventHeader
- zendesk: detect ticket solved via current vs previous status comparison

This makes the digest handlers' terminal state classifications reachable
from real webhook data, closing the gaps identified by lifecycle audit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f2db321-a70c-41f7-9c20-d3ea836a51c7

📥 Commits

Reviewing files that changed from the base of the PR and between fe33145 and 0754d92.

📒 Files selected for processing (10)
  • packages/github/src/__tests__/aliases.test.ts
  • packages/github/src/__tests__/emit-auxiliary-files.test.ts
  • packages/github/src/emit-auxiliary-files.ts
  • packages/github/src/pr/diff-writer.ts
  • packages/gitlab/src/path-mapper.ts
  • packages/gitlab/test/path-mapper.test.ts
  • packages/jira/src/__tests__/path-mapper.test.ts
  • packages/jira/src/path-mapper.ts
  • packages/x/src/__tests__/emit-auxiliary-files.test.ts
  • packages/x/src/emit-auxiliary-files.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/jira/src/tests/path-mapper.test.ts
  • packages/github/src/tests/emit-auxiliary-files.test.ts
  • packages/x/src/tests/emit-auxiliary-files.test.ts

📝 Walkthrough

Walkthrough

Adds a new X adapter package with types, client, path-mapper, emitters, digest, tests, and build config; updates docs and publish-group mapping for x; changes GitHub alias/meta semantics and delete-recovery; standardizes digest sorting and action-verb regexing across many adapters; and adds/updates related tests and scripts.

Changes

X Social Adapter & Core Files

Layer / File(s) Summary
Package setup & exports
packages/x/package.json, packages/x/tsconfig.json, packages/x/src/index.ts, packages/x/src/alias-slug.ts
Adds the @relayfile/adapter-x package, build/test scripts, TS config, public re-exports and alias-slug re-export.
Types & path mapping
packages/x/src/types.ts, packages/x/src/path-mapper.ts
Defines X data contracts and path helpers for searches/posts/users, encoding/decoding, alias paths, and canonical ID extraction.
Client & accounting
packages/x/src/client.ts, packages/x/src/__tests__/client.test.ts
Implements XSearchClient with pagination, cost estimation, timeouts, validation, deterministic search IDs, and extensive tests covering budgets, paging, errors, and security.
Emitters & index builders
packages/x/src/emit-auxiliary-files.ts, packages/x/src/index-emitter.ts, packages/x/src/layout.ts
Implements emitXAuxiliaryFiles orchestration, per-type planning (search/post/user/results), reconciler usage, JSON index builders, and layout manifest.
Digest & tests
packages/x/src/digest.ts, packages/x/src/__tests__/digest.test.ts
Adds provider x digest handler producing ordered bullets and tests for classification, tie-breaking, and ignored paths.
Auxiliary tests
packages/x/src/__tests__/emit-auxiliary-files.test.ts, packages/x/src/__tests__/path-mapper.test.ts, packages/x/src/__tests__/index-emitter.test.ts
Adds broad tests for emit behavior, reconciliation/cleanup, path determinism, collision handling, and index ordering.

GitHub alias/meta & emit changes

Layer / File(s) Summary
Numbered by-title aliases and meta path
packages/github/src/path-mapper.ts, packages/github/src/layout-prompt.ts, packages/github/src/__tests__/*
Adds githubNumberedByTitleAliasPath, moves repo metadata writes to meta.json (githubRepositoryMetaPath), and updates tests to expect numbered title alias filenames.
Emit/delete recovery
packages/github/src/emit-auxiliary-files.ts, packages/github/src/bulk/bulk-writer.ts, packages/github/src/pr/diff-writer.ts, packages/github/src/issues/*
Threads a per-batch NumberedDeleteRecoveryCache to cache reads for numbered delete recovery, include legacy alias paths in reconciliation, tighten numeric/sha parsing, and adjust alias writes.
Lazy materialization & tests
packages/github/src/lazy.ts, tests
Update to use githubRepositoryMetaPath and update test expectations.

Digest/identifier refactor across adapters

Layer / File(s) Summary
Deterministic digest ordering & verb regex
packages/*/src/digest.ts (many adapters)
Replaces localeCompare fallbacks with deterministic compareDigestStrings and centralizes action-verb regex patterns + helpers (actionVerbRegex, hasActionVerb) for past-tense selection; adds safe decode helpers and tests for percent-decoding robustness. Files affected include airtable, asana, azure-blob, box, calendly, clickup, confluence, dropbox, gcs, gmail, gitlab, google-*, hubspot, intercom, jira, linear, mailgun, mixpanel, notion, onedrive, pipedrive, postgres, redis, s3, salesforce, segment, sendgrid, sharepoint, shopify, slack, stripe, teams, zendesk, etc.

GitLab canonical-trust & emit tests

Layer / File(s) Summary
Trusted prior canonical path validation
packages/gitlab/src/emit-auxiliary-files.ts, packages/gitlab/src/path-mapper.ts, tests
Adds trustedPriorCanonicalPath validation to ignore foreign/untrusted prior canonical paths, updates path-mapper escape/flat-name behavior, and adjusts tests to assert preservation of untrusted paths.

Scripts & contract tests

Layer / File(s) Summary
digest-layout-contracts script & tests
scripts/digest-layout-contracts.mjs, scripts/digest-layout-contracts.test.mjs
Make script only run as main, add active-test coverage analysis helpers and tests verifying detection of live assertions for regression contracts.
resolve-publish-targets
scripts/resolve-publish-targets.mjs, tests
Add social group mapping to x and adjust exit/printing behavior when nothing to publish; add test exercising successful no-op when already published.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

"A rabbit hopped with a tiny pen,
Wrote a client to fetch many a den,
Paths snug and tidy, tests all in a row,
Digests sorted where calm breezes blow.
Hooray — the X adapter has sprung from my warren!"

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/x-social-search-adapter

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment thread packages/x/src/client.ts Outdated
Comment thread packages/x/package.json
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/x/src/__tests__/emit-auxiliary-files.test.ts`:
- Around line 127-204: Add a new test in the same file that exercises
emitXAuxiliaryFiles with colliding alias inputs: construct two bundles (via
makeBundle / modified bundle fixtures) that normalize to the same
query/username/post-author slugs, call emitXAuxiliaryFiles with a createClient
pre-seeded as needed, then assert that writes include collision-disambiguated
alias paths (use xSearchByQueryAliasPath, xUserByUsernameAliasPath,
xPostByAuthorAliasPath and related x*AliasPath helpers) and that stale ambiguous
aliases are deleted; refer to existing tests "emitXAuxiliaryFiles writes
layout..." and "emitXAuxiliaryFiles reconciles renamed searches..." for
setup/assertion patterns and reuse createClient, makeBundle, and
emitXAuxiliaryFiles to locate where to add the new collision assertions.

In `@packages/x/src/digest.test.ts`:
- Around line 17-63: Add two tests to packages/x/src/digest.test.ts: one that
sends an event with action 'created' (use runDigest to assert the returned
section includes a bullet like "X was created" with the correct canonicalPath
and ordering alongside other events), and one that sends an event using the
adapter's terminal-state action (check the adapter to pick the exact action
name, e.g., 'archived' or 'closed') and assert the digest uses the
terminal-state phrasing (e.g., "was archived"/"was closed") in the bullet text
and preserves canonicalPath; place these alongside the existing tests that use
runDigest so they run with the same fixtures.

In `@packages/x/src/digest.ts`:
- Around line 120-131: The pastTense function in digest.ts currently maps any
unmatched action to "was updated", causing terminal lifecycle actions (e.g.,
archived, completed, resolved, closed, merged, canceled) to be misreported;
update the pastTense(event: DigestChangeEvent) implementation to explicitly
detect these terminal verbs by adding checks (using hasActionVerb) for
terminal-action patterns like
'close|closed|closed|merged|merge|archive|archived|complete|completed|cancel|canceled|resolve|resolved'
and return a suitable past-tense phrase such as 'was closed', 'was merged', 'was
archived', or a generic 'was finalized' (pick one consistent label) before the
final fallback, ensuring the new checks appear above the existing 'was updated'
return and reference the same hasActionVerb helper and
event.action/event.eventType/event.type extraction logic.

In `@packages/x/src/emit-auxiliary-files.ts`:
- Around line 193-207: Precompute slug buckets for query and username aliases
for the entire emit batch and detect collisions (buckets with length >1); then
when building deletes and writes use xSearchByQueryAliasPath(...,
colliding=true) or xUserByUsernameAliasPath(..., colliding=true) for any id in a
colliding bucket (leave colliding omitted/false for singleton buckets), and
propagate the same colliding flag into staleDeletes and every mirrorWrite call
so alias targets remain unique and stable (update the logic around
xSearchByQueryAliasPath, xUserByUsernameAliasPath, staleDeletes, and the
mirrorWrite calls in both the shown block and the similar block at ~290-301).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ff2c12f8-3533-4a0d-bf59-ab0077ebf800

📥 Commits

Reviewing files that changed from the base of the PR and between 5022400 and bf391e1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • docs/EVAL_INTEGRATION_CATALOG.md
  • docs/integration-scopes.yaml
  • packages/x/package.json
  • packages/x/src/__tests__/client.test.ts
  • packages/x/src/__tests__/emit-auxiliary-files.test.ts
  • packages/x/src/__tests__/path-mapper.test.ts
  • packages/x/src/alias-slug.ts
  • packages/x/src/client.ts
  • packages/x/src/digest.test.ts
  • packages/x/src/digest.ts
  • packages/x/src/emit-auxiliary-files.ts
  • packages/x/src/index-emitter.ts
  • packages/x/src/index.ts
  • packages/x/src/layout-prompt.ts
  • packages/x/src/layout.ts
  • packages/x/src/path-mapper.ts
  • packages/x/src/types.ts
  • packages/x/tsconfig.json
  • scripts/resolve-publish-targets.mjs

Comment thread packages/x/src/__tests__/emit-auxiliary-files.test.ts
Comment thread packages/x/src/digest.test.ts
Comment thread packages/x/src/digest.ts
Comment thread packages/x/src/emit-auxiliary-files.ts
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

4 issues found across 20 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Fix all with cubic | Re-trigger cubic

Comment thread packages/x/src/client.ts Outdated
Comment thread packages/x/src/path-mapper.ts Outdated
Comment thread packages/x/src/emit-auxiliary-files.ts
Comment thread docs/EVAL_INTEGRATION_CATALOG.md
Proactive Runtime Bot and others added 3 commits May 18, 2026 13:25
Brings codex/adapter-digest-lifecycle-coverage into #98 so the X social
search adapter ships with its security and correctness hardening.
Conflicts on packages/x/** resolved in favor of the hardened version.

Includes beyond the original PR #98 contents:
- X security: validated baseUrl, AbortController timeout, truncated error
  bodies, trustedPriorCanonicalPath rejecting cross-provider paths.
- X path-mapper __ joiner round-trip + safe-decode helpers.
- GitLab emit-aux trustedPriorCanonicalPath via parseGitLabPath.
- GitHub emit-aux NumberedDeleteRecoveryCache (N+1 fix).
- Asana + ClickUp digest tests rewired to real path-mapper helpers.
- relay CLI hardening from the parallel review-loop run.
- Digest lifecycle coverage (#97 follow-up).

Each fix is mutation-checked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@khaliqgant
Copy link
Copy Markdown
Member Author

Merged the parallel codex/adapter-digest-lifecycle-coverage branch into this PR so #98 now ships the X adapter with all the security/correctness hardening that lived only on the sibling branch, plus the digest lifecycle coverage (#97 follow-up).

The two branches had diverged at merge-base 97be4ec and each independently introduced packages/x/** (this PR's bf391e1 and the sibling's efead46+2b7cff6). The sibling was a strict superset (going from sibling → this PR was 82 ins / 1,654 dels for X alone, 811 / 3,887 overall). Merged with -X theirs so the hardened version wins on packages/x/** conflicts; nothing from #98 was lost.

What's now in #98 beyond the original

X adapter security/correctness:

  • validateXBaseUrl restricts XSearchClient to https://api.x.com; bearer token cannot escape to a caller-controlled origin (regression test mutation-checked).
  • fetchWithTimeout adds an AbortController per request; summarizeXErrorBody truncates provider error bodies.
  • trustedPriorCanonicalPath (validates prefix + id round-trip via extractXObjectIdFromPath) rejects cross-provider/wrong-id paths before they reach deleteFile. Mutation-checked against /github/... foreign paths.
  • xRecordDirectorySegment round-trips ids containing the canonical __ joiner (e.g. search__custom); safeDecodeURIComponent handles malformed external input cleanly.

GitLab emit-aux:

  • trustedPriorCanonicalPath via parseGitLabPath (matches project / resource / id / meta.json). Cross-provider regression tests for issues, pipelines, commits — all mutation-checked.

GitHub emit-aux:

  • NumberedDeleteRecoveryCache eliminates the unscoped-tombstone N+1 reads of /github/repos/_index.json and per-repo indexes.

Test contract integrity:

  • Asana + ClickUp digest tests rewired to the real path helpers (asanaTaskPath, clickUpTaskPath, etc.) so they exercise the runtime emitted paths, not fabricated slug__id strings.

relay-related CLI hardening carried in this tree from the parallel review-loop run (CORR-1..5, SECPERF-1, TC-1, codex-aggregate-001..004).

Each fix has a mutation-checked regression test (revert fix → test FAILS → restore → PASSES) under .workflow-artifacts/review-loop/iteration-*/mutation-*.

Local verification on the merged tree

adapter-x       52/52   typecheck PASS
adapter-gitlab  66/66   typecheck PASS
adapter-asana   28/28   typecheck PASS
adapter-clickup 21/21   typecheck PASS
adapter-github 257/257  typecheck PASS

The original bf391e1 Add X social search adapter commit is preserved in history; the merge commit (b2991a5) supersedes its packages/x/** contents.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/jira/src/path-mapper.ts (1)

41-53: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Replace provider-local slugifier usage in canonical path composition.

titleSegmentWithId still depends on a custom slugify() implementation. Please route this through the shared alias slug helper (slugifyAlias) and keep path compatibility via additive/deprecated helper or reader fallback if output changes.

As per coding guidelines, packages/*/src/**/*.ts: “Use packages/core/src/alias-slug.ts (slugifyAlias, aliasCollisionSuffix)… NEVER write a new slugifier.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jira/src/path-mapper.ts` around lines 41 - 53, Replace the local
slugify() usage in titleSegmentWithId with the shared slug helper by importing
and calling slugifyAlias from packages/core/src/alias-slug.ts; remove the local
slugify implementation and instead call slugifyAlias(title) inside
titleSegmentWithId, and to preserve existing canonical paths create a small
additive/deprecated wrapper (e.g., legacySlugify or slugifyWithFallback) that
returns the old-local-slugify output when a compatibility flag or reader
fallback is required, or appends aliasCollisionSuffix as per alias-slug
utilities to avoid collisions—keep encodeJiraPathSegment(id) usage and ensure
the wrapper is used only for canonical path composition so consumers see
unchanged paths unless they opt-in.
♻️ Duplicate comments (1)
packages/x/src/digest.ts (1)

133-149: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Map terminal lifecycle actions before the was updated fallback.

Terminal actions are currently collapsed into was updated, which misstates lifecycle outcomes.

Proposed fix
 const ACTION_VERB_PATTERN_1 = actionVerbRegex('search|searched|run|ran');
 const ACTION_VERB_PATTERN_2 = actionVerbRegex('create|created|add|added|write|written');
+const ACTION_VERB_PATTERN_2B = actionVerbRegex('close|closed');
+const ACTION_VERB_PATTERN_2C = actionVerbRegex('merge|merged');
+const ACTION_VERB_PATTERN_2D = actionVerbRegex('archive|archived');
+const ACTION_VERB_PATTERN_2E = actionVerbRegex('complete|completed');
+const ACTION_VERB_PATTERN_2F = actionVerbRegex('cancel|canceled|cancelled');
+const ACTION_VERB_PATTERN_2G = actionVerbRegex('resolve|resolved');
 const ACTION_VERB_PATTERN_3 = actionVerbRegex('delete|deleted|remove|removed');

 function pastTense(event: DigestChangeEvent): string {
   const action = (event.action ?? event.eventType ?? event.type ?? '').toLowerCase();
   if (hasActionVerb(action, ACTION_VERB_PATTERN_1)) {
     return 'ran';
   }
   if (hasActionVerb(action, ACTION_VERB_PATTERN_2)) {
     return 'was created';
   }
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2B)) return 'was closed';
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2C)) return 'was merged';
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2D)) return 'was archived';
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2E)) return 'was completed';
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2F)) return 'was canceled';
+  if (hasActionVerb(action, ACTION_VERB_PATTERN_2G)) return 'was resolved';
   if (hasActionVerb(action, ACTION_VERB_PATTERN_3)) {
     return 'was deleted';
   }
   return 'was updated';
 }
As per coding guidelines, `Classify lifecycle actions explicitly in digest.ts. Terminal states such as closed, merged, archived, completed, canceled, and resolved must not fall through to a generic 'updated' line unless the provider has no terminal concept.`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/x/src/digest.ts` around lines 133 - 149, The pastTense function
currently falls back to 'was updated' and needs explicit handling of terminal
lifecycle verbs; add a new ACTION_VERB_PATTERN_TERMINAL (use
actionVerbRegex('closed|merged|archived|completed|canceled|resolved')) and check
it before the final fallback in pastTense (alongside ACTION_VERB_PATTERN_1/2/3);
when matched, return the correct terminal phrasing (e.g., 'was closed', 'was
merged', 'was archived', 'was completed', 'was canceled', or 'was resolved') by
mapping the matched verb to its specific past-tense string so terminal states
are not collapsed into 'was updated'.
🧹 Nitpick comments (1)
packages/github/src/__tests__/emit-auxiliary-files.test.ts (1)

221-312: ⚡ Quick win

Remove duplicated reconciliation tests in this file.

These two test cases are duplicated later with the same names and assertions, which adds redundant runtime and failure noise.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/github/src/__tests__/emit-auxiliary-files.test.ts` around lines 221
- 312, Delete the duplicated test cases that reconcile PR state transitions and
issue metadata in this file: remove the two it(...) blocks titled "reconciles a
PR state transition by moving the by-state alias" and "reconciles issue
assignee, creator, and priority aliases on metadata changes" that call
emitGitHubAuxiliaryFiles and assert deletes/writes using githubByIdAliasPath,
githubByStateAliasPath, githubByAssigneeAliasPath, githubByCreatorAliasPath, and
githubByPriorityAliasPath; keep only one copy of each test (or the later
occurrences) to eliminate redundant execution and assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/github/src/emit-auxiliary-files.ts`:
- Around line 717-720: The code is pushing duplicate delete targets because
githubByTitleAliasPath(...) and githubLegacyByTitleAliasPath(...) can resolve to
the same path; update the block that runs when options.includeLegacyTitleAlias
is true to only add the legacy path if it is different from the primary path
(compare the string result of githubByTitleAliasPath(owner, repo, aliasKind,
title, number) with githubLegacyByTitleAliasPath(owner, repo, aliasKind, title,
number) before pushing), or alternatively compute the primary path once into a
variable and push the legacy path only when its value !== primary; adjust the
code that populates the paths array (the paths variable) accordingly.

In `@packages/github/src/pr/diff-writer.ts`:
- Around line 430-435: The current code uses resolveAliasPath(...) to pick an
alias path for numbered PRs (constructed via githubNumberedByTitleAliasPath)
which can cause writes to go to a collision path and leave the base numbered
alias stale; instead, when a numbered alias is being created (you already
compute baseAliasPath via githubNumberedByTitleAliasPath(owner, repo, 'pulls',
title, number)), skip resolveAliasPath and directly set aliasPath to
baseAliasPath and ensure the write/update uses that path (update usages of
aliasPath accordingly), so the canonical numbered alias always overwrites the
base alias; adjust the logic around resolveAliasPath, vfs, and content to only
call resolveAliasPath for non-numbered aliases.

In `@packages/gitlab/src/path-mapper.ts`:
- Around line 127-129: The new mapper emits flat ids with "__" (e.g.,
"release__candidate.json") but decodeFlatObjectId still strips the prefix by
assuming any "__" means "slug__id"; update decodeFlatObjectId (the decoder used
by parseGitLabPath) so that when the incoming path segment contains "__" but is
itself a flat filename (no "/" present) it returns the full base name (e.g.,
"release__candidate") instead of splitting; implement this by checking the raw
segment (or filename without ".json") for the pattern of a single path segment
containing "__" and only split on "__" in cases that clearly represent slug+id
(for example when there are surrounding path segments or an explicit slug
context), otherwise return the entire segment unchanged to preserve back-compat.

In `@packages/x/src/emit-auxiliary-files.ts`:
- Around line 519-531: staleCurrentSearchResultDeletes currently removes stale
search-result files and index rows but does not remove the corresponding
"post-by-query" alias files for each removed (searchId, postId); update
staleCurrentSearchResultDeletes to also compute the alias paths for each stale
result (using the same searchId and result.postId, e.g. the function that builds
post-by-query alias paths) and pass those paths to the deletion logic alongside
xSearchResultPath so that both the result file and its post-by-query alias are
deleted; ensure this happens after resultIndex.remove(...) and leverage the
existing staleDeletes helper so both sets of paths are cleaned up.

---

Outside diff comments:
In `@packages/jira/src/path-mapper.ts`:
- Around line 41-53: Replace the local slugify() usage in titleSegmentWithId
with the shared slug helper by importing and calling slugifyAlias from
packages/core/src/alias-slug.ts; remove the local slugify implementation and
instead call slugifyAlias(title) inside titleSegmentWithId, and to preserve
existing canonical paths create a small additive/deprecated wrapper (e.g.,
legacySlugify or slugifyWithFallback) that returns the old-local-slugify output
when a compatibility flag or reader fallback is required, or appends
aliasCollisionSuffix as per alias-slug utilities to avoid collisions—keep
encodeJiraPathSegment(id) usage and ensure the wrapper is used only for
canonical path composition so consumers see unchanged paths unless they opt-in.

---

Duplicate comments:
In `@packages/x/src/digest.ts`:
- Around line 133-149: The pastTense function currently falls back to 'was
updated' and needs explicit handling of terminal lifecycle verbs; add a new
ACTION_VERB_PATTERN_TERMINAL (use
actionVerbRegex('closed|merged|archived|completed|canceled|resolved')) and check
it before the final fallback in pastTense (alongside ACTION_VERB_PATTERN_1/2/3);
when matched, return the correct terminal phrasing (e.g., 'was closed', 'was
merged', 'was archived', 'was completed', 'was canceled', or 'was resolved') by
mapping the matched verb to its specific past-tense string so terminal states
are not collapsed into 'was updated'.

---

Nitpick comments:
In `@packages/github/src/__tests__/emit-auxiliary-files.test.ts`:
- Around line 221-312: Delete the duplicated test cases that reconcile PR state
transitions and issue metadata in this file: remove the two it(...) blocks
titled "reconciles a PR state transition by moving the by-state alias" and
"reconciles issue assignee, creator, and priority aliases on metadata changes"
that call emitGitHubAuxiliaryFiles and assert deletes/writes using
githubByIdAliasPath, githubByStateAliasPath, githubByAssigneeAliasPath,
githubByCreatorAliasPath, and githubByPriorityAliasPath; keep only one copy of
each test (or the later occurrences) to eliminate redundant execution and
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7f48a10-6ce0-407d-a047-7db57d824549

📥 Commits

Reviewing files that changed from the base of the PR and between bf391e1 and b2991a5.

📒 Files selected for processing (82)
  • .trajectories/index.json
  • package.json
  • packages/airtable/src/digest.ts
  • packages/asana/src/digest.test.ts
  • packages/asana/src/digest.ts
  • packages/azure-blob/src/digest.ts
  • packages/box/src/digest.ts
  • packages/calendly/src/digest.test.ts
  • packages/calendly/src/digest.ts
  • packages/clickup/src/digest.test.ts
  • packages/clickup/src/digest.ts
  • packages/confluence/src/digest.ts
  • packages/dropbox/src/digest.ts
  • packages/gcs/src/digest.ts
  • packages/github/src/__tests__/aliases.test.ts
  • packages/github/src/__tests__/emit-auxiliary-files.test.ts
  • packages/github/src/__tests__/index-emission.test.ts
  • packages/github/src/__tests__/lazy-materialization.test.ts
  • packages/github/src/__tests__/path-mapper.test.ts
  • packages/github/src/bulk/bulk-writer.ts
  • packages/github/src/digest.test.ts
  • packages/github/src/digest.ts
  • packages/github/src/emit-auxiliary-files.ts
  • packages/github/src/issues/__tests__/issue-mapping.test.ts
  • packages/github/src/issues/issue-mapper.ts
  • packages/github/src/layout-prompt.ts
  • packages/github/src/lazy.ts
  • packages/github/src/path-mapper.ts
  • packages/github/src/pr/diff-writer.ts
  • packages/gitlab/src/digest.ts
  • packages/gitlab/src/emit-auxiliary-files.ts
  • packages/gitlab/src/path-mapper.ts
  • packages/gitlab/test/digest.test.ts
  • packages/gitlab/test/emit-auxiliary-delete.test.ts
  • packages/gitlab/test/emit-auxiliary-files.test.ts
  • packages/gitlab/test/path-mapper.test.ts
  • packages/gmail/src/digest.ts
  • packages/google-calendar/src/digest.ts
  • packages/google-drive/src/digest.ts
  • packages/hubspot/src/digest.ts
  • packages/intercom/src/digest.ts
  • packages/jira/src/__tests__/emit-auxiliary-files.test.ts
  • packages/jira/src/__tests__/jira-adapter.test.ts
  • packages/jira/src/__tests__/path-mapper.test.ts
  • packages/jira/src/digest.ts
  • packages/jira/src/jira-adapter.ts
  • packages/jira/src/path-mapper.ts
  • packages/linear/src/digest.ts
  • packages/mailgun/src/digest.test.ts
  • packages/mailgun/src/digest.ts
  • packages/mixpanel/src/digest.ts
  • packages/notion/src/digest.ts
  • packages/onedrive/src/digest.ts
  • packages/pipedrive/src/digest.ts
  • packages/postgres/src/digest.ts
  • packages/redis/src/digest.ts
  • packages/s3/src/digest.ts
  • packages/salesforce/src/__tests__/webhook-normalizer.test.ts
  • packages/salesforce/src/digest.ts
  • packages/salesforce/src/webhook-normalizer.ts
  • packages/segment/src/digest.ts
  • packages/sendgrid/src/digest.ts
  • packages/sharepoint/src/digest.ts
  • packages/shopify/src/digest.ts
  • packages/slack/src/digest.ts
  • packages/stripe/src/digest.ts
  • packages/teams/src/digest.ts
  • packages/x/src/__tests__/client.test.ts
  • packages/x/src/__tests__/emit-auxiliary-files.test.ts
  • packages/x/src/__tests__/index-emitter.test.ts
  • packages/x/src/__tests__/path-mapper.test.ts
  • packages/x/src/client.ts
  • packages/x/src/digest.test.ts
  • packages/x/src/digest.ts
  • packages/x/src/emit-auxiliary-files.ts
  • packages/x/src/index-emitter.ts
  • packages/x/src/path-mapper.ts
  • packages/zendesk/src/digest.ts
  • scripts/digest-layout-contracts.mjs
  • scripts/digest-layout-contracts.test.mjs
  • scripts/resolve-publish-targets.mjs
  • scripts/resolve-publish-targets.test.mjs
✅ Files skipped from review due to trivial changes (3)
  • packages/github/src/layout-prompt.ts
  • packages/github/src/lazy.ts
  • .trajectories/index.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/x/src/digest.test.ts
  • packages/x/src/tests/path-mapper.test.ts
  • packages/x/src/client.ts
  • packages/x/src/index-emitter.ts

Comment thread packages/github/src/emit-auxiliary-files.ts
Comment thread packages/github/src/pr/diff-writer.ts Outdated
Comment thread packages/gitlab/src/path-mapper.ts
Comment thread packages/x/src/emit-auxiliary-files.ts Outdated
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

2 issues found across 16 files (changes from recent commits).

You’re at about 92% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

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/x/src/emit-auxiliary-files.ts">

<violation number="1" location="packages/x/src/emit-auxiliary-files.ts:88">
P2: Collision detection is based only on the current batch, so incremental syncs can write the wrong alias mode when colliding records already exist in workspace state.</violation>
</file>

<file name="packages/gitlab/src/path-mapper.ts">

<violation number="1" location="packages/gitlab/src/path-mapper.ts:165">
P2: The numeric-tail fallback incorrectly decodes raw flat IDs like `release__2024` as composed names, truncating the real object ID.</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

const results = [...(input.results ?? []), ...bundles.flatMap((bundle) => bundle.results)];
const resultSearchIdsByPostId = groupResultSearchIdsByPostId(results);
const resultPostIdsBySearchId = groupResultPostIdsBySearchId(results);
const collidingSearchQueryIds = aliasCollisionIds(searches.filter(isFullSearch), (search) => search.id, (search) => search.query);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 18, 2026

Choose a reason for hiding this comment

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

P2: Collision detection is based only on the current batch, so incremental syncs can write the wrong alias mode when colliding records already exist in workspace state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/x/src/emit-auxiliary-files.ts, line 88:

<comment>Collision detection is based only on the current batch, so incremental syncs can write the wrong alias mode when colliding records already exist in workspace state.</comment>

<file context>
@@ -84,6 +85,8 @@ export async function emitXAuxiliaryFiles(
   const results = [...(input.results ?? []), ...bundles.flatMap((bundle) => bundle.results)];
   const resultSearchIdsByPostId = groupResultSearchIdsByPostId(results);
   const resultPostIdsBySearchId = groupResultPostIdsBySearchId(results);
+  const collidingSearchQueryIds = aliasCollisionIds(searches.filter(isFullSearch), (search) => search.id, (search) => search.query);
+  const collidingUserUsernameIds = aliasCollisionIds(users.filter(isFullUser), (user) => user.id, (user) => user.username);
 
</file context>

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

Fix with Cubic

}
const encodedId = value.slice(separatorIndex + 2);
try {
return /^\d+$/u.test(decodeURIComponent(encodedId));
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 18, 2026

Choose a reason for hiding this comment

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

P2: The numeric-tail fallback incorrectly decodes raw flat IDs like release__2024 as composed names, truncating the real object ID.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/gitlab/src/path-mapper.ts, line 165:

<comment>The numeric-tail fallback incorrectly decodes raw flat IDs like `release__2024` as composed names, truncating the real object ID.</comment>

<file context>
@@ -152,6 +152,22 @@ function isComposedFlatRecordFilename(value: string): boolean {
+  }
+  const encodedId = value.slice(separatorIndex + 2);
+  try {
+    return /^\d+$/u.test(decodeURIComponent(encodedId));
+  } catch {
+    return false;
</file context>
Fix with Cubic

@khaliqgant khaliqgant merged commit 7b74e79 into main May 18, 2026
3 checks passed
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