Skip to content

Add live factory subscription pickup#245

Merged
kjgbot merged 5 commits into
mainfrom
factory-sdk/ls-live-subscription
Jun 12, 2026
Merged

Add live factory subscription pickup#245
kjgbot merged 5 commits into
mainfrom
factory-sdk/ls-live-subscription

Conversation

@kjgbot

@kjgbot kjgbot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adds live factory startup mode for headless /linear/issues/** pickup with subscribe and getEvents polling transports
  • captures the current event cursor before listening and ignores pre-start event timestamps to avoid stale replay
  • filters live arrivals through real Linear issue, ready state, and factory scope checks before dispatch
  • records live arrival latency and low-noise duplicate/missing-identity counters
  • maps sparse Linear state_name values to known state ids

Tests

  • npx vitest run packages/factory-sdk
  • npx tsc --noEmit -p tsconfig.node.json

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kjgbot, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d1a87ae0-913f-4db4-897a-8b6d01d5682d

📥 Commits

Reviewing files that changed from the base of the PR and between 7e5f446 and 5896cfe.

📒 Files selected for processing (11)
  • packages/factory-sdk/src/cli/fleet.ts
  • packages/factory-sdk/src/config/schema.ts
  • packages/factory-sdk/src/index.ts
  • packages/factory-sdk/src/mount/relayfile-cloud-mount-client.test.ts
  • packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts
  • packages/factory-sdk/src/orchestrator/factory.test.ts
  • packages/factory-sdk/src/orchestrator/factory.ts
  • packages/factory-sdk/src/orchestrator/index.ts
  • packages/factory-sdk/src/ports/mount.ts
  • packages/factory-sdk/src/testing/fakes.ts
  • packages/factory-sdk/src/types.ts
📝 Walkthrough

Walkthrough

This PR implements live subscription mode for the Factory, enabling real-time event-driven issue processing as an alternative to backfill-then-watch. It introduces configurable transport selection (subscribe, poll, or hybrid), event deduplication, cursor-based polling with in-flight guarding, arrival latency tracking, and real Linear issue validation throughout the flow.

Changes

Live subscription feature

Layer / File(s) Summary
Type contracts and configuration schema
packages/factory-sdk/src/types.ts, packages/factory-sdk/src/config/schema.ts, packages/factory-sdk/src/ports/mount.ts, packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts
FactoryStartOptions and FactoryLiveSubscriptionOptions types define mode and live subscription parameters including transport selection, poll intervals, and event limits; liveSubscription config schema added with constrained defaults; SubscribeOptions explicitly typed in ports to decouple from SDK internal implementation.
Factory live subscription implementation
packages/factory-sdk/src/orchestrator/factory.ts
FactoryLoop.start() refactored to accept FactoryStartOptions, dispatching to #start(opts) with #startLiveSubscription for mode: 'live'; live mode initializes subscription (subscribe transport) or scheduled polling (poll transport) from cursor; handles incoming events with deduplication (bounded set with oldest-entry eviction), arrival latency recording, and optional isRealLinearIssue validation; #handleChange extended with requireRealIssue guard; polling helpers compute cursors and stable dedup keys.
Real Linear issue validation export
packages/factory-sdk/src/index.ts, packages/factory-sdk/src/orchestrator/index.ts
isRealLinearIssue exported from factory module and re-exported through orchestrator and main SDK public API for upstream use.
Live subscription test coverage
packages/factory-sdk/src/orchestrator/factory.test.ts
Test helpers added: realIssueFile() factory, CountingEventsMount to track getEvents calls, Vitest vi for fake timers; comprehensive live subscription tests validate dispatch on newly arrived real/ready/in-scope issues, subscribe transport not draining events at startup, polling cursor semantics, event deduplication, and filtering of non-real/out-of-scope arrivals; changeEvent helper updated to use current-time timestamps.
CLI integration for live mode
packages/factory-sdk/src/cli/fleet.ts
Fleet command starts factory with { mode: 'live' } argument.

Sequence Diagram

sequenceDiagram
  participant Client
  participant FactoryLoop
  participant Subscription
  participant Cache
  participant Handlers
  Client->>FactoryLoop: start({ mode: 'live' })
  alt subscribe transport
    FactoryLoop->>Subscription: subscribe(liveGlob)
    Subscription->>FactoryLoop: changeEvent (real-time)
  else poll transport
    FactoryLoop->>FactoryLoop: schedule polling loop
    FactoryLoop->>Subscription: getEvents(cursor)
  end
  FactoryLoop->>Cache: check dedup set for event key
  alt event already seen
    FactoryLoop->>FactoryLoop: skip (deduplicated)
  else new event
    FactoryLoop->>FactoryLoop: recordArrivalLatency
    FactoryLoop->>FactoryLoop: check isRealLinearIssue
    FactoryLoop->>Handlers: handleChange (if real)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AgentWorkforce/pear#235: Modifies the factory orchestrator's FactoryLoop implementation including its start(...) method in packages/factory-sdk/src/orchestrator/factory.ts (this PR extends it with mode: 'live' and live subscription behavior).
  • AgentWorkforce/pear#229: Modifies packages/factory-sdk/src/config/schema.ts by extending the exported FactoryConfigSchema; this PR adds the liveSubscription configuration object building on the schema foundation.
  • AgentWorkforce/pear#243: Modifies packages/factory-sdk/src/orchestrator/factory.ts to extend real-issue gating via isRealLinearIssue for filtering events during FactoryLoop handling.

Poem

🐰 Real-time subscriptions bloom,
Poll-based cursors light the room,
Deduplicate with bounded grace,
Live events race through cyberspace!
Factory's evolution takes its flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding live factory subscription functionality for event pickup.
Description check ✅ Passed The description is well-structured and directly related to the changeset, detailing the live subscription mode, filtering logic, metrics, and testing instructions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch factory-sdk/ls-live-subscription

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a live subscription mode to the Factory orchestrator, allowing it to process issues in real-time using either event subscriptions, polling, or a combination of both. It adds configuration schema options, event deduplication, latency tracking, and robust test coverage for these live subscription flows. The review feedback highlights two key areas for improvement: preventing a potential infinite loop in #currentEventCursor when the next cursor matches the current one, and refining eventCursorAfterPage to safely handle string-based event IDs when the initial cursor is undefined.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +179 to +186
async #currentEventCursor(limit: number): Promise<string | undefined> {
let cursor: string | undefined
for (;;) {
const page = await this.#mount.getEvents({ cursor, limit })
cursor = eventCursorAfterPage(cursor, page.events, page.nextCursor)
if (!page.nextCursor) return cursor
}
}

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

The #currentEventCursor method can run into an infinite loop if page.nextCursor is returned but is equal to the current cursor (e.g., when there are no new events but the API continues to return the same cursor). To prevent this, add a check to terminate the loop when page.nextCursor === cursor, matching the safety check implemented in #pollLiveEvents.

Suggested change
async #currentEventCursor(limit: number): Promise<string | undefined> {
let cursor: string | undefined
for (;;) {
const page = await this.#mount.getEvents({ cursor, limit })
cursor = eventCursorAfterPage(cursor, page.events, page.nextCursor)
if (!page.nextCursor) return cursor
}
}
async #currentEventCursor(limit: number): Promise<string | undefined> {
let cursor: string | undefined
for (;;) {
const page = await this.#mount.getEvents({ cursor, limit })
const nextCursor = eventCursorAfterPage(cursor, page.events, page.nextCursor)
if (!page.nextCursor || page.nextCursor === cursor) return nextCursor
cursor = nextCursor
}
}

Comment on lines +871 to +883
const eventCursorAfterPage = (
cursor: string | undefined,
events: ChangeEvent[],
nextCursor?: string | null,
): string | undefined => {
if (nextCursor) return nextCursor
if (events.length === 0) return cursor
const numericCursor = cursor === undefined ? 0 : Number(cursor)
if (Number.isInteger(numericCursor) && numericCursor >= 0) {
return String(numericCursor + events.length)
}
return events.at(-1)?.id ?? cursor
}

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

In eventCursorAfterPage, when cursor is undefined, the code defaults numericCursor to 0 and treats it as a numeric cursor, returning String(events.length). However, if the event stream uses string-based IDs (like UUIDs) rather than numeric offsets, this will result in an invalid cursor (e.g., "5") being used for subsequent polls. It is safer to only treat the cursor as numeric if cursor is explicitly defined and is a valid integer, falling back to the last event's ID otherwise.

const eventCursorAfterPage = (
  cursor: string | undefined,
  events: ChangeEvent[],
  nextCursor?: string | null,
): string | undefined => {
  if (nextCursor) return nextCursor
  if (events.length === 0) return cursor
  if (cursor !== undefined) {
    const numericCursor = Number(cursor)
    if (Number.isInteger(numericCursor) && numericCursor >= 0) {
      return String(numericCursor + events.length)
    }
  }
  return events.at(-1)?.id ?? cursor
}

agent-relay-code Bot added a commit that referenced this pull request Jun 12, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented the PR review fixes for the live subscription cursor path.

Changed:

  • factory.ts: #currentEventCursor now exits when the feed repeats the current nextCursor.
  • factory.ts: eventCursorAfterPage no longer invents numeric cursors when the current cursor is undefined; it falls back to the last event ID.
  • fakes.ts: fake mount now records getEvents calls and supports event-ID cursors.
  • factory.test.ts: added/extended regressions for string cursor resume and repeated-cursor termination.

Addressed comments

  • gemini-code-assist[bot]: #currentEventCursor could loop forever when page.nextCursor === cursor; fixed in factory.ts and covered by factory.test.ts.
  • gemini-code-assist[bot]: eventCursorAfterPage incorrectly treated an undefined cursor as numeric offset 0; fixed in factory.ts and covered by factory.test.ts.
  • gemini-code-assist[bot]: summary review raised the same two cursor issues; handled by the two fixes above.
  • coderabbitai[bot]: review was rate-limited and did not raise an actionable code finding; no code change required.

Advisory Notes

None.

Local validation

Passed:

  • npm run verify:mcp-resources-drift
  • npm run lint with existing warnings only
  • npm run typecheck:web
  • npm run typecheck:node
  • npm test
  • npx vitest run
  • npm run build
  • npm run build:web
  • npx playwright test --config playwright.fidelity.config.ts
  • npx playwright test --config playwright.redraw.config.ts

I did not run the macOS-only dist:mac packaged smoke job in this Linux sandbox, and I cannot assert remote GitHub check/mergeable state from the checkout.

agent-relay-code Bot added a commit that referenced this pull request Jun 12, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer applied fixes — committed and pushed 6332764 to this PR. The notes below describe what changed.

Implemented one real fix found during review: RelayfileCloudMountClient.getEvents() now normalizes SDK FilesystemEvent records into the MountClient ChangeEvent shape before factory live polling consumes them. Without this, live polling would call event.resource.path on SDK events that only have path, causing polling to fail. Fixed in relayfile-cloud-mount-client.ts.

Also added regression coverage for normalized event-feed output and live duplicate suppression:
relayfile-cloud-mount-client.test.ts, factory.test.ts. I also restored the SDK subscribe option fields in the local port type at mount.ts.

Addressed comments

  • No bot or reviewer comment artifacts were present in .workforce; only pr.diff, changed-files.txt, and context.json were available.

Advisory Notes

  • None.

Validation run:

  • npm ci
  • npm run verify:mcp-resources-drift
  • npm run lint passed with existing warnings only
  • npm run typecheck:web
  • npm run typecheck:node
  • npm test
  • npx vitest run
  • npm run build
  • npm run build:web
  • npx playwright install --with-deps chromium
  • npx playwright test --config playwright.fidelity.config.ts
  • npx playwright test --config playwright.redraw.config.ts

I did not print READY because I cannot verify GitHub-side mergeability, pending remote checks, or the macOS-only packaged smoke job from this Linux checkout.

@kjgbot kjgbot force-pushed the factory-sdk/ls-live-subscription branch from 6332764 to fb64895 Compare June 12, 2026 10:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/factory-sdk/src/orchestrator/factory.ts (1)

437-447: 💤 Low value

Consider consolidating the isRealLinearIssue checks.

The function checks isRealLinearIssue at two points:

  • Line 437: early exit in live mode (when requireRealIssue is true) before the scope check
  • Line 445: always checked after the scope check

While line 437 is an optimization to avoid the scope check for non-real issues in live mode, having two exit paths for the same condition can reduce clarity. Consider whether the scope check is expensive enough to justify the early exit, or consolidate to a single check.

🤖 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/factory-sdk/src/orchestrator/factory.ts` around lines 437 - 447,
There are two checks for isRealLinearIssue around the scope check (one gated by
opts.requireRealIssue and one unconditional); consolidate them by performing a
single isRealLinearIssue(issue) evaluation and gating the early return with
opts.requireRealIssue only when required, or move the opts.requireRealIssue
logic to combine with the scope check: replace the two separate returns with one
combined conditional that uses isRealLinearIssue(issue) and
opts.requireRealIssue and isInFactoryScope(issue, this.#config.safety) so the
code only returns once and still preserves the short-circuit optimization when
requireRealIssue is true.
packages/factory-sdk/src/cli/fleet.ts (1)

196-196: ⚡ Quick win

Consider making the live mode configurable via CLI flag.

The factory loop command now hardcodes mode: 'live', removing flexibility for users who might want to choose between live and backfill modes. Consider adding a --mode flag (e.g., --mode live or --mode backfill) to allow runtime configuration.

💡 Example implementation with CLI flag

Update the parseFactoryCommand function to parse a --mode flag and pass it through the command structure, then use it here:

-    await factory.start({ mode: 'live' })
+    await factory.start({ mode: globals.mode ?? 'live' })
🤖 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/factory-sdk/src/cli/fleet.ts` at line 196, The current call await
factory.start({ mode: 'live' }) hardcodes live mode; update the CLI to accept a
--mode flag and pass its value into factory.start. Modify parseFactoryCommand to
parse a mode option (accepting 'live' or 'backfill') with a default of 'live',
ensure the command structure exposes that mode, and replace the hardcoded object
in fleet.ts so factory.start({ mode }) uses the parsed value; validate the mode
and fall back to 'live' on invalid input.
packages/factory-sdk/src/orchestrator/factory.test.ts (1)

449-473: Clarify that pre-start replay suppression during live polling is cursor-based, not timestamp-filtered.

  • For liveSubscription: { transport: 'poll' }, the factory sets #liveEventCursor by consuming the current mount.getEvents() history, then subsequent polls fetch getEvents({ cursor }); it does not use event.occurredAt to decide which events to replay.
  • event.occurredAt is only used for arrival-latency metrics (#recordArrivalLatency), so the “future timestamp” in the test doesn’t affect whether the pre-start event is ignored—only the cursor advancement does.
  • For readability, consider not hard-coding a future occurredAt in this test unless you’re explicitly aiming to exercise latency behavior.
🤖 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/factory-sdk/src/orchestrator/factory.test.ts` around lines 449 -
473, The test is misleading because live polling suppresses pre-start history by
advancing the factory's internal live event cursor (`#liveEventCursor`) via
consuming mount.getEvents(), not by filtering on event.occurredAt;
event.occurredAt is only used for arrival-latency recording
(`#recordArrivalLatency`). Update the test in factory.test.ts (the case using
createFactory with liveSubscription.transport='poll' and FakeMountClient) to
either remove the artificially future occurredAt timestamp on the pre-start
changeEvent or add a brief comment clarifying that suppression is cursor-based
and the future timestamp does not affect replay; ensure no assertions rely on
occurredAt to determine replay behavior and that the test exercises cursor
advancement via mount.getEvents()/getEvents({ cursor }).
🤖 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/factory-sdk/src/orchestrator/factory.ts`:
- Around line 185-192: The startup delay comes from `#currentEventCursor`
paginating all historical events via this.#mount.getEvents and
eventCursorAfterPage; to fix, first check if the mount/SDK exposes a direct
cursor API (e.g. this.#mount.getCurrentCursor, this.#mount.getCursor, or
similar) and call that and return its value instead of paginating; if no direct
method exists, add an optional fast-start/skipCatchup flag or a new
this.#mount.getLatestCursor helper and use it (falling back to the existing
pagination code only if the direct API is absent), referencing
`#currentEventCursor`, this.#mount.getEvents and eventCursorAfterPage to locate
where to implement the change.
- Around line 165-175: The polling branch incorrectly checks only for
options.transport === 'poll', so the hybrid mode 'subscribe-and-poll' never
schedules polls; update the condition that sets this.#liveEventCursor and calls
this.#scheduleLivePoll(...) to run when options.transport is 'poll' OR
'subscribe-and-poll' (i.e., include both values), using the existing methods
`#currentEventCursor`(...) to initialize this.#liveEventCursor and
`#scheduleLivePoll`(0, options) to start the loop; keep the existing subscription
logic that assigns this.#subscription and calls this.#handleLiveChange(...)
unchanged.

---

Nitpick comments:
In `@packages/factory-sdk/src/cli/fleet.ts`:
- Line 196: The current call await factory.start({ mode: 'live' }) hardcodes
live mode; update the CLI to accept a --mode flag and pass its value into
factory.start. Modify parseFactoryCommand to parse a mode option (accepting
'live' or 'backfill') with a default of 'live', ensure the command structure
exposes that mode, and replace the hardcoded object in fleet.ts so
factory.start({ mode }) uses the parsed value; validate the mode and fall back
to 'live' on invalid input.

In `@packages/factory-sdk/src/orchestrator/factory.test.ts`:
- Around line 449-473: The test is misleading because live polling suppresses
pre-start history by advancing the factory's internal live event cursor
(`#liveEventCursor`) via consuming mount.getEvents(), not by filtering on
event.occurredAt; event.occurredAt is only used for arrival-latency recording
(`#recordArrivalLatency`). Update the test in factory.test.ts (the case using
createFactory with liveSubscription.transport='poll' and FakeMountClient) to
either remove the artificially future occurredAt timestamp on the pre-start
changeEvent or add a brief comment clarifying that suppression is cursor-based
and the future timestamp does not affect replay; ensure no assertions rely on
occurredAt to determine replay behavior and that the test exercises cursor
advancement via mount.getEvents()/getEvents({ cursor }).

In `@packages/factory-sdk/src/orchestrator/factory.ts`:
- Around line 437-447: There are two checks for isRealLinearIssue around the
scope check (one gated by opts.requireRealIssue and one unconditional);
consolidate them by performing a single isRealLinearIssue(issue) evaluation and
gating the early return with opts.requireRealIssue only when required, or move
the opts.requireRealIssue logic to combine with the scope check: replace the two
separate returns with one combined conditional that uses
isRealLinearIssue(issue) and opts.requireRealIssue and isInFactoryScope(issue,
this.#config.safety) so the code only returns once and still preserves the
short-circuit optimization when requireRealIssue is true.
🪄 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: a72253a4-ac56-4e9d-ae8a-436988f9b1f7

📥 Commits

Reviewing files that changed from the base of the PR and between 3693fd0 and 7e5f446.

📒 Files selected for processing (9)
  • packages/factory-sdk/src/cli/fleet.ts
  • packages/factory-sdk/src/config/schema.ts
  • packages/factory-sdk/src/index.ts
  • packages/factory-sdk/src/mount/relayfile-cloud-mount-client.ts
  • packages/factory-sdk/src/orchestrator/factory.test.ts
  • packages/factory-sdk/src/orchestrator/factory.ts
  • packages/factory-sdk/src/orchestrator/index.ts
  • packages/factory-sdk/src/ports/mount.ts
  • packages/factory-sdk/src/types.ts

Comment on lines +165 to +175
if (options.transport !== 'poll') {
this.#subscription = this.#mount.subscribe([LIVE_ISSUE_GLOB], (event) => {
void this.#handleLiveChange(event)
}, { from: 'now', coalesce: 'none' })
}

if (options.transport === 'poll') {
this.#liveEventCursor = await this.#currentEventCursor(options.eventLimit)
this.#scheduleLivePoll(0, options)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: 'subscribe-and-poll' transport doesn't enable polling.

The condition on line 171 only enables polling when transport === 'poll', but the hybrid 'subscribe-and-poll' mode should also enable polling. Currently, 'subscribe-and-poll' only subscribes (line 166) but never schedules the poll loop (lines 172-174), breaking the intended hybrid behavior.

🐛 Proposed fix
-    if (options.transport === 'poll') {
+    if (options.transport === 'poll' || options.transport === 'subscribe-and-poll') {
       this.#liveEventCursor = await this.#currentEventCursor(options.eventLimit)
       this.#scheduleLivePoll(0, options)
     }
🤖 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/factory-sdk/src/orchestrator/factory.ts` around lines 165 - 175, The
polling branch incorrectly checks only for options.transport === 'poll', so the
hybrid mode 'subscribe-and-poll' never schedules polls; update the condition
that sets this.#liveEventCursor and calls this.#scheduleLivePoll(...) to run
when options.transport is 'poll' OR 'subscribe-and-poll' (i.e., include both
values), using the existing methods `#currentEventCursor`(...) to initialize
this.#liveEventCursor and `#scheduleLivePoll`(0, options) to start the loop; keep
the existing subscription logic that assigns this.#subscription and calls
this.#handleLiveChange(...) unchanged.

Comment thread packages/factory-sdk/src/orchestrator/factory.ts
@kjgbot kjgbot force-pushed the factory-sdk/ls-live-subscription branch from 9a693eb to f2da103 Compare June 12, 2026 11:19
@kjgbot kjgbot merged commit 8d92fb2 into main Jun 12, 2026
5 checks passed
@kjgbot kjgbot deleted the factory-sdk/ls-live-subscription branch June 12, 2026 11:45
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