Skip to content

docs(pylon): document the Pylon datasource for the Ruby agent - #30

Open
christophebrun-forest wants to merge 5 commits into
mainfrom
docs/pylon-ruby-datasource
Open

docs(pylon): document the Pylon datasource for the Ruby agent#30
christophebrun-forest wants to merge 5 commits into
mainfrom
docs/pylon-ruby-datasource

Conversation

@christophebrun-forest

@christophebrun-forest christophebrun-forest commented Sep 1, 2026

Copy link
Copy Markdown
Member

Why

agent-ruby#369 merged forest_admin_datasource_pylon (released in agent-ruby 1.41.0). Nothing in the docs mentioned it, so the gem shipped with its package README as the only place a customer could learn how to configure it — or, more to the point, what the connector refuses to do and why.

Pylon is a ticketing API, not a database, so most of the page is that second half: several things Forest asks for have no equivalent, and the datasource answers a 400 naming the reason rather than something that looks right and is not. That is the behaviour a customer meets first and has no way to guess.

What changed

Two new pages, mirroring how the Zendesk connector is documented (datasource page + plugins page):

get-started/connect/data-sources/pylon.mdx

  • Installation, and the full option table with defaults — base_url, both timeouts, retry_policy, rate_limiter: nil, the boot_* trio.
  • The five collections with their read/write matrix, the ten relations, and the two relations Pylon's shape does not allow (team membership, account owner).
  • The messages thread column: field-by-field shape, the lazy fetch, the 10-row cap, and why a capped row is nil rather than [].
  • Custom fields: the type mapping, the slug as column name, select-by-slug, the is_read_only flag rule, and the boot-time degradation.
  • Capabilities: a filter allow-list per collection (in tabs), the id short-circuit and MAX_ID_LOOKUPS, the absence of any sort parameter, the cursor pagination caps, why only PylonUser / PylonTeam aggregate — and exactly — the write budget and the per-collection create-only / update-only fields.
  • Rate limits and retries, with the DEFAULT_MAX_INTERVAL: 12 trade stated and the snippet to choose the other way; then the error-class table and logging.

product/process/advanced-concepts/plugins/pylon.mdx

  • CreateIssueWithNotification: options, form fields, the two-page templates wizard, the refusals at registration, and what the operator reads back.
  • CloseIssue: options, the one-action-per-scope registration and its name-collision refusal, which scope bounds it, and the 20-issue batch cap.

Both pages are Ruby-only, with a <Warning> saying so, rather than tabbed: there is no Node.js Pylon connector, so a Node.js tab would have nothing in it.

Also: docs.json nav for both pages, and a Pylon entry in the "Available datasources" list of get-started/connect/data-sources/overview.mdx.

How it was written

Every figure, option name and default is transcribed from the merged code at cf20168a, not from the PR description: the package README, configuration.rb, retry_policy.rb, rate_limits.rb, the five collections with their schema_definition / api_filters, operator_maps.rb, base_collection.rb / fetch_all_collection.rb / writes.rb, custom_fields_introspector.rb, cursor_walker.rb, and both plugins.

Two places where that mattered:

  • The CloseIssue scope caveat is the corrected reading, the one b6bd085b landed. Mounted on PylonIssue, a scope bounds exactly what the action closes — the ids are resolved through the host collection first, and the agent intersects the operator's scope into that filter. The caveat only applies to the host-collection form, where the id column is the authority. The earlier wording had it backwards, and telling an operator a scope buys them nothing when it buys them the whole selection is the one error worth not copying into the docs.
  • The batch cap counts issues named, not records selected — a hundred host records naming ten issues is a batch of ten, duplicates collapsed. Same for MAX_ID_LOOKUPS, which bounds a page rather than a selection (a93474b1), so a wider selection is read a page at a time rather than truncated.

Three limitations the PR filed on Linear rather than fixing are not documented as behaviour, since they are open tickets: EXT-21 (a non-positive page limit), EXT-22 (a nested and carrying an id — the page does state that this shape is refused, which is what EXT-22 would change) and EXT-23 (an aggregation applying an operation the column type cannot take).

Note

Add Pylon datasource and plugins documentation for Ruby agent

  • Adds a new reference page covering Pylon datasource setup, collections, filters, pagination, writes, errors, retries, and custom-field introspection
  • Adds a new plugins page documenting the CreateIssueWithNotification and CloseIssue action plugins, including options, batch limits, and partial-failure handling
  • Updates navigation in docs.json and adds Pylon cards to the data-source overview and integrations overview so the new pages are reachable

Macroscope summarized b88a504.

The `forest_admin_datasource_pylon` gem landed in agent-ruby#369: five
collections over the Pylon API, cursor pagination, per-endpoint rate
limiting, custom-field introspection, CRUD writes and two action plugins.

Two pages, mirroring how the Zendesk connector is documented:

- `get-started/connect/data-sources/pylon` — installation, the
  configuration options and their defaults, the collections and their
  relations, the embedded conversation thread, custom-field mapping, then
  what the API cannot do and what the datasource does about it: the filter
  allow-list per collection, the primary-key short-circuit and its caps,
  the absence of any sort parameter, why only the two fetch-all
  collections aggregate, the write budget, the rate-limit / retry trade
  and the error classes.
- `product/process/advanced-concepts/plugins/pylon` — `CloseIssue` and
  `CreateIssueWithNotification`: options, form fields, the wizard, which
  scope bounds a close, and the batch cap.

Ruby-only pages rather than tabbed ones: there is no Node.js Pylon
connector, so a `Node.js` tab would have nothing to say.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mintlify

mintlify Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
forest 🟢 Ready View Preview Sep 1, 2026, 2:46 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment on lines +23 to +31
@agent.collection :Customer do |collection|
collection.use(
ForestAdminDatasourcePylon::Plugins::CreateIssueWithNotification,
datasource: pylon_datasource,
sender_email: 'support@acme.com'
)
end

@agent.collection :Order do |collection|

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High plugins/pylon.mdx:23

The snippet calls collection on the uninitialized @agent, so copying it raises a NoMethodError and registers neither plugin action. The documented factory setup stores the agent in @create_agent; use that receiver for both collection declarations.

-@agent.collection :Customer do |collection|
+@create_agent.collection :Customer do |collection|
   collection.use(
     ForestAdminDatasourcePylon::Plugins::CreateIssueWithNotification,
     datasource: pylon_datasource,
     sender_email: 'support@acme.com'
   )
 end
 
-@agent.collection :Order do |collection|
+@create_agent.collection :Order do |collection|
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @product/process/advanced-concepts/plugins/pylon.mdx around lines 23-31:

The snippet calls `collection` on the uninitialized `@agent`, so copying it raises a `NoMethodError` and registers neither plugin action. The documented factory setup stores the agent in `@create_agent`; use that receiver for both collection declarations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in b79d271, but not the way the suggestion proposes — the diagnosis was off by one line.

@agent.collection is a valid call: AgentFactory includes ForestAdminDatasourceCustomizer::DSL::DatasourceHelpers, which defines collection(name, &block) as a wrapper over customize_collection. So the receiver is not the problem.

The actual defect was on line 21, which called add_datasource and threw away its return value, leaving @agent unbound two lines later. add_datasource returns self, so binding it is the whole fix:

@agent = ForestAdminAgent::Builder::AgentFactory.instance.add_datasource(pylon_datasource, {})

Switching to @create_agent instead would have made this the only plugins page using that name — plugins/zendesk.mdx and plugins/active-storage.mdx both use @agent. @create_agent is the datasource-page convention, which is why data-sources/pylon.mdx uses it: there the snippet shows the whole CreateAgent.setup! body, where that is the real local name.

The Pylon connector is only available for Ruby (gem `forest_admin_datasource_pylon`).
</Warning>

Both plugins require the [Pylon datasource](/get-started/connect/data-sources/pylon) to be registered on your back-end: they need the `Datasource` instance to reach the Pylon API client.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium plugins/pylon.mdx:12

The page incorrectly tells users that both plugins require the Datasource to be registered with the agent, even though the actions use only the supplied datasource: object and its client; users who only need these actions are therefore steered into unnecessary datasource registration. Document that a constructed Datasource passed via datasource: is sufficient.

- Both plugins require the [Pylon datasource](/get-started/connect/data-sources/pylon) to be registered on your back-end: they need the `Datasource` instance to reach the Pylon API client.
+ Both plugins require a constructed `Datasource` instance passed via the `datasource:` option so they can reach the Pylon API client; they do not require that datasource to be registered with the agent.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @product/process/advanced-concepts/plugins/pylon.mdx around line 12:

The page incorrectly tells users that both plugins require the `Datasource` to be registered with the agent, even though the actions use only the supplied `datasource:` object and its `client`; users who only need these actions are therefore steered into unnecessary datasource registration. Document that a constructed `Datasource` passed via `datasource:` is sufficient.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct on the substance, and deliberately left as is in this PR.

Both plugins reach Pylon through options[:datasource].client only — CreateIssueWithNotification#create_issue and CloseIssue#apply_state — and never look the datasource up on the agent. A Datasource.new(api_key: ...) that is never passed to add_datasource builds its Client and is enough for both actions, so "must be registered" does overstate it.

The reason it stays: that sentence is copied verbatim from the Ruby tab of plugins/zendesk.mdx, where the same thing is true of CreateTicketWithNotification / CloseTicket. Relaxing it on the Pylon page alone would leave the two connectors documented as having different requirements when they have the same one, which is more confusing than the overstatement.

Worth its own PR that fixes both pages together. One thing to weigh there: constructing a Pylon Datasource runs the custom-field introspection (three GET /custom-fields calls in front of the Rails boot), so an unregistered instance kept only to feed the plugins pays that cost for a schema nobody reads — which is a reason to keep recommending registration even once the wording no longer requires it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Filed as EXT-24 so this does not stay a comment.

Checking Zendesk before writing it up turned the misalignment into a three-way one, and worse than described above. The Node tab of plugins/zendesk.mdx already gets it right — plugins accept a built client or raw credentials, and line 26 documents the no-datasource path outright ("when you install a single plugin on a project that does not register the Zendesk datasource"). The Ruby tab of that same page, four lines below, rules it out: "require the Zendesk datasource to be registered". So the Pylon page did not introduce the overstatement, it inherited it — and Node contradicts both.

Verified the Zendesk Ruby plugins behave exactly like the Pylon ones: CloseTicket#apply_status and CreateTicketWithNotification#executor reach Zendesk through options[:datasource].client and never look the datasource up on the agent.

Which makes the ticket a parity question rather than a wording one: Ruby has no equivalent of Node's "pass the credentials straight to the plugin", so a plugins-only customer must construct a full datasource regardless — and for Pylon that costs the three GET /custom-fields of the boot introspection for a schema nobody reads. Letting the Ruby plugins take a client: would fix the docs sentence and that cost in one go. Both options are in the ticket; the decision changes what the pages should say, so it wants making before either page is touched.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for tracking this in EXT-24. Once the API/docs direction is decided, would you like me to prepare the follow-up PR against main?

Comment thread product/process/advanced-concepts/plugins/pylon.mdx Outdated
Review of #30 against the merged code at cf20168a. Five places where the
pages said something the package does not do.

- `GET /me` was introduced as "the health check", which reads as a boot-time
  validation of the token. `Client#me` is called nowhere: the only boot call is
  `GET /custom-fields`, which degrades to the native schema on failure. A wrong
  api_key therefore boots the agent and fails on the first list. Say that, and
  point at `client.me` as the check to run yourself.
- "Two relations Pylon's shape does not allow" covered one reason for two
  relations. Team membership is a shape limitation; `PylonAccount.owner_id` is
  not — the embedder would resolve it like any other key, and it is left a plain
  column because nothing in the panel asks for it yet. Split the two.
- The plugins snippet called `add_datasource` without binding its return, then
  used `@agent` on the next line. Bind it. `@agent.collection` is valid —
  `AgentFactory` includes `DSL::DatasourceHelpers` — and is what the Zendesk
  plugins page already uses, so only the assignment was missing.
- The `destination` row listed `internal` among the channels the first message
  is delivered through. It is the absence of a delivery: `Payload#build` omits
  `destination_metadata` entirely for it. The page had this right two paragraphs
  above and wrong in the table.
- The filter tables list no `NOT_EQUAL`, because `operator_maps.rb` deliberately
  declares only `not_in` — the toolkit republishes the other spelling from it.
  The tables transcribe the wire maps, so the UI offers a filter the page reads
  as unavailable. One note under the tabs, next to the presence rewrite it
  shares a mechanism with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to b79d271, which overcorrected: it said nothing calls Pylon at
boot and that the token is checked by the first request needing it. The
custom-field introspection does call Pylon at boot and does carry the token
-- `fetch_custom_fields` wraps `must_succeed` in `best_effort(default: nil)`,
so a 401 there returns nil, logs a warning and leaves the agent on the native
schema. The call happens; it just does not validate.

Worth the precision because it is where the operator looks: a wrong key leaves
a custom-fields warning in the boot log, which the Note further down already
describes, and nothing else until the first list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@matthv

matthv commented Sep 2, 2026

Copy link
Copy Markdown
Member

Review of d727d68 against the shipped code (agent-ruby origin/main, 1.41.0). The pages are remarkably accurate overall — option table, write matrix, relations, per-field operator tables, rate-limit figures, plugin forms and refusals all check out against the code, and every Ruby snippet runs. The findings below are the residue.

Sequencing — three passages document the intended id-lookup behaviour, not the shipped one. The silent-truncation fix being in progress on agent-ruby, these describe what the code will do, not what 1.41.0 does:

  • product/.../plugins/pylon.mdx:173 — "Past the cap the run is refused before its first write": false today for a CloseIssue mounted on PylonIssue with an unscoped selection of more than 20 ids — page_of_ids truncates to 20 before the guard can fire, and a 50-issue selection reports "20 issues closed." (True in the two other forms: issue_id_field on a host collection, or under a scope/segment.)
  • get-started/.../pylon.mdx:232 — "the window is taken off the ids first … rather than truncated at its first twenty": a pageless filter (every action, any API caller without pagination) is exactly the truncation this sentence rules out.
  • plugins/pylon.mdx:179 — the parenthetical example of a datasource refusal (a wide id selection) only exists when a residual rides along.

Simplest path: merge this PR after the agent-ruby fix lands — the three passages become true as written.

Inaccuracies to fix in this PR:

  1. pylon.mdx:195"To reach an account by an external id, use it in place of the primary key — GET /accounts/{id} accepts one." The endpoint accepts one, but the collection deliberately discards the answer: the serializer keeps Pylon's UUID as record['id'], and records_by_id keeps a record only if matches_id? holds (record['id'].to_s == id.to_s, base_collection.rb:240) — built to drop exactly this shape, per its own comment. With a scope, the and goes through /accounts/search, which matches the UUID only. Either way a customer following this gets a systematically empty result; there is no Forest path to an account by external id. (The sentence transcribes the stale comment at account/api_filters.rb:28-29 — worth fixing that comment on the agent-ruby side too.)
  2. pylon.mdx:102"…or referenced in a custom action." A custom action can never receive the thread: ActionContext#get_records validates fields then calls list with an empty Projection.new regardless, and the embed's only trigger is want_messages?(projection), which an empty projection fails. Even context.get_record(['messages']) gets records without messages. Drop that half of the sentence; "rendered on the detail view" is correct.
  3. Pylon is missing from the "All integrations" catalog (get-started/connect/integrations/overview.mdx, "Support & ticketing" section) — the page declares itself exhaustive and holds only the Zendesk card, with exactly the two bullet shapes this PR creates.
  4. pylon.mdx:310-317 — the Errors table lists unqualified class names (APIError, …) while line 40 of the same page and the Zendesk precedent qualify (ForestAdminDatasourcePylon::…). A copied rescue APIError is a NameError; qualify the rows or add a one-line namespace note.
  5. plugins/pylon.mdx:55-113 — template tokens are spelled {{ record.field }} with inner spaces; the repo's Ruby precedent (Zendesk Ruby tab and wizard snippet) is unspaced {{record.field}}.

Minor: pylon.mdx:224 has the PRESENT/BLANK rewrite backwards (fields that carry the family declare it natively; the rewrite surfaces it on fields that don't, where the translator refuses); pylon.mdx:221 — String custom fields advertise the full-text set including negations, and Date/Dateonly drop IN/NOT_IN; pylon.mdx:306 — the verb restriction also gates the 5xx retries (faraday-retry ORs methods with retry_if, which only passes 429), so a 502 on POST /issues/search is not replayed — the sentence implies only transport failures are verb-restricted; pylon.mdx:2495 000/1 000 space grouping is unique to this page (Zendesk writes 1000).

Review of d727d68 against the shipped code (agent-ruby origin/main, the Pylon
package untouched since cf20168a). Nine places where the pages said something
the package does not do, or said it in the wrong direction.

Two of them sent a customer down a path that cannot work:

- "To reach an account by an external id, use it in place of the primary key"
  is exactly what the collection is built to refuse. `GET /accounts/{id}` does
  accept one, but the serializer keeps Pylon's UUID as `record['id']` and
  `matches_id?` keeps a record only when that matches the id asked for -- per
  its own comment at `base_collection.rb:225-241`. With a scope the `and` goes
  through `/accounts/search`, which matches the UUID only. Either way the
  answer is empty. There is no Forest path to an account by external id; say
  that instead. (The stale comment at `account/api_filters.rb:28-29` is where
  this came from and wants fixing on the agent-ruby side too.)
- "or referenced in a custom action", of the `messages` thread: a custom action
  can never receive it. `ActionContext#get_records` validates `fields` then
  calls `list` with an empty `Projection.new` regardless, and the embed's only
  trigger is `want_messages?(projection)`. Drop the half-sentence and say why
  `context.get_record(['messages'])` comes back without the column.

Then the account/contact routing the first of those depends on: "need none of
this" was true of the cap and false of the route -- a bare `id equals X` is
still read through `GET /accounts/{id}`, to spend the cheaper budget rather
than because the search could not answer it. That is the route that swallows
the external id, so the page has to name it.

Four corrections of substance:

- The `PRESENT` / `BLANK` / `MISSING` rewrite was described backwards. The
  fields carrying the family declare it natively; the derivation puts it in
  front of every equality or membership field, including the ones that cannot
  answer it, which is where the translator refuses. Reworded in the direction
  `condition_tree_translator.rb:102-108` reads.
- Custom fields: a `String` advertises the whole substring set, negations
  included (`BASE_OPS + FULL_TEXT.keys`), and a Date/Dateonly drops `IN` /
  `NOT_IN` on the way (`TIME_OPS`). The page said "substring" and omitted the
  loss.
- The verb restriction gates the 5xx retries as much as the transport
  failures: faraday-retry ORs `methods` with `retry_if`, and `retry_if` only
  passes 429, so a `502` on `POST /issues/search` is not replayed. The page
  implied only dropped connections were verb-restricted.
- Pylon was missing from the "All integrations" catalog, which declares itself
  exhaustive and held only the Zendesk card.

And two cosmetic alignments on the repo's own precedent: the error table
carried unqualified class names, where line 40 of the same page and
`zendesk.mdx` qualify -- a copied `rescue APIError` is a `NameError`, so the
namespace is stated once above the table; and the template tokens are spelled
`{{record.field}}` like the Zendesk Ruby tab rather than spaced (`TOKEN_RE`
tolerates both, this is consistency only). `5 000` / `1 000` were the only
space-grouped figures in the docs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three passages documented the intended primary-key-lookup behaviour rather
than the shipped one. The silent-truncation fix is not on agent-ruby main --
the Pylon package has not moved since cf20168a, and no PR is open for it --
so the pages describe something no released gem does.

What 1.41/1.42 actually do: `page_of_ids` takes the window off the ids, then
caps at `MAX_ID_LOOKUPS`. `translate_page(nil)` returns `[0, nil]`, so a
filter carrying no page has no window to take and the cap truncates the whole
selection at twenty, with a `logger.warn`. And `refuse_wide_lookup` is reached
only when `lookup.residual` is non-nil.

- The datasource page said a wider selection "is read a page at a time rather
  than truncated at its first twenty". True of a paginated read; a pageless
  one -- every action's own read, and any API caller sending no page -- is
  exactly the truncation the sentence ruled out. Split into the two cases.
- `CloseIssue` said the run is refused before its first write past the cap.
  False today for an action mounted on PylonIssue over an unscoped selection:
  the ids are truncated to twenty before the guard can see the rest, so fifty
  selected issues close twenty and report "20 issues closed". Named as the
  exception it is, with the two forms that are exact -- `issue_id_field` on a
  host collection, and any selection a scope or a segment narrows, where the
  residual makes the datasource refuse.
- The example of a datasource refusal travelling to the operator only exists
  when a residual rides along, so it is now a scoped selection.

Kept as its own commit on purpose: when the agent-ruby fix lands, reverting
this restores three passages that become true as written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@christophebrun-forest

Copy link
Copy Markdown
Member Author

Thanks — all nine findings check out against the shipped code, and all nine are fixed in 1d6bed6. Two of them mattered a lot more than the rest, and one of your conclusions I'd like to push back on.

The two that sent a customer nowhere. The account-by-external-id sentence is the worst thing that was on either page: matches_id? is built to drop that shape (base_collection.rb:225-241 says so in its own comment), so the reader gets a systematically empty result with nothing to debug. Same for messages in a custom action — get_records calls list with an empty Projection.new whatever fields it validated, so the embed can never fire. Both rewritten to say what does not work and why, rather than deleted.

While fixing the first one I hit something the review didn't name, and it's why that fix was incomplete on its own: PylonAccount and PylonContact need none of this was true of the cap and false of the route. CursorCollection#fetch_records still sends a bare id equals X to GET /accounts/{id} via records_by_id, to spend the cheaper budget — and that is precisely the route that swallows the external id. Without naming it, the corrected sentence has no visible mechanism. Added.

On sequencing — the diagnosis is right, the conclusion I've changed. All three passages are indeed false on 1.41/1.42: translate_page(nil) returns [0, nil], so page_of_ids has no window to take and truncates the whole selection at twenty, and refuse_wide_lookup only fires with a non-nil residual. Confirmed the CloseIssue-on-PylonIssue case end to end: fifty selected issues, no scope, reports "20 issues closed".

But "merge this PR after the agent-ruby fix lands" doesn't work as stated: the Pylon package hasn't moved since cf20168a (1.41.0 and 1.42.0 are release commits only) and there's no open PR for that fix. Gating the docs on it means the connector ships undocumented for as long as nobody picks it up — which is the problem this PR exists to solve.

So b88a504 reworks the three passages to describe what 1.42 ships, as its own commit, and the CloseIssue exception is named where an operator would meet it. When the fix lands, reverting that one commit restores three passages that become true as written. If you have the fix in flight locally and it's days away, say so and I'll revert b88a504 and wait instead — I just don't want the docs blocked on an unopened PR.

One nuance on the tokens: TOKEN_RE = /\{\{\s*record\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/ tolerates the inner spaces, so the snippets did run — it was consistency with the Zendesk Ruby tab, not a copy-paste failure. Aligned anyway.

Left as is: plugins/pylon.mdx:12 ("require the datasource to be registered"). It overstates for Pylon and for Zendesk identically, and relaxing it on one page only would document two connectors as having different requirements when they have the same one. Its own PR, as discussed above.

Two follow-ups on the agent-ruby side, neither in scope here: the stale comment at account/api_filters.rb:28-29 is where the external-id claim came from and will regenerate it for the next reader, and the truncation fix itself.

@linear-code

linear-code Bot commented Sep 8, 2026

Copy link
Copy Markdown

EXT-24

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.

2 participants