Skip to content

Add direct Artifacts git push primitives for packages - #352

Merged
kentcdodds merged 11 commits into
mainfrom
cursor/-bc-12f6e31e-ace2-416c-9150-58550c5150d3-1e5d
May 4, 2026
Merged

kentcdodds merged 11 commits into
mainfrom
cursor/-bc-12f6e31e-ace2-416c-9150-58550c5150d3-1e5d

Conversation

@kentcdodds

@kentcdodds kentcdodds commented May 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add package_get_git_remote for short-lived Artifacts git credentials and package_publish_external_push for publishing pushed package HEADs
  • extract shared external publish reconciliation with checks-before-mutation, D1/KV rollback semantics, and package projection refresh
  • add scheduled Artifacts push reconciliation, stale token cleanup, D1 last_external_check_at, and user/contributor docs

Validation

  • npm run typecheck
  • npm run lint
  • npm test
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Edit saved packages via direct git‑push workflows with short‑lived repo tokens and an authenticated git-remote helper
    • Manual publish endpoint for pushed commits and an automatic reconciliation job that publishes detected external pushes
  • Documentation

    • Expanded guides for repo-backed workflows, git-remote publishing, token scopes/TTL, and external-push reconciliation
  • Chores

    • Added periodic reconciliation cron and daily stale-token revocation
  • Database

    • Track last external check time for reconciliation
  • Tests

    • Added coverage for reconcile, token, git-remote, and publish flows

cursoragent and others added 3 commits May 4, 2026 14:40
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented May 4, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@cursor[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 19 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 897549cb-8694-448e-ae1f-feca30c3b994

📥 Commits

Reviewing files that changed from the base of the PR and between 881d962 and 43a5195.

📒 Files selected for processing (2)
  • packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts
  • packages/worker/src/jobs/reconcile-artifacts-pushes.ts
📝 Walkthrough

Walkthrough

Adds end-to-end support for external Cloudflare Artifacts git pushes: minting short-lived tokens, resolving Artifacts HEADs, publishing pushed commits through server-side checks and transactional publish (DB + KV snapshot with rollback), a reconcile cron that detects/publishes stale pushes and revokes stale tokens, plus MCP capabilities and tests.

Changes

External Artifacts Git Push Workflow

Layer / File(s) Summary
Data Model & Schema
packages/worker/migrations/0032-entity-sources-external-check.sql, packages/worker/src/repo/types.ts, packages/worker/src/repo/source-service.ts
Adds entity_sources.last_external_check_at (TEXT) and index; EntitySourceRow includes nullable last_external_check_at; introduces RepoExternalPublishResult union type; new entity rows initialize last_external_check_at: null.
Artifact REST & Git Ref Helpers
packages/worker/src/repo/artifacts.ts
Adds ArtifactStoredToken type, optional listTokens()/revokeToken() on ArtifactRepoHandle, implements list/revoke via Artifacts REST, and adds listArtifactServerRefs, resolveArtifactDefaultBranchHead, resolveArtifactSourceHead.
Token Revocation
packages/worker/src/repo/artifacts-tokens.ts
Adds revokeStaleArtifactsTokens(env, repoName, { keepAfter }) to list tokens and revoke those expiring before cutoff, ignoring 404s.
Entity Source Persistence & Queries
packages/worker/src/repo/entity-sources.ts
Parses/stores last_external_check_at; updateEntitySource accepts lastExternalCheckAt and conditionally updates the column; adds listEntitySourcesForExternalReconcile(db, { before, limit }).
External Publish Implementation
packages/worker/src/repo/external-publish.ts, packages/worker/src/repo/types.ts
Implements publishFromExternalRef and finalizePublishedEntitySource: run repo checks, gate non-fast-forward unless allowed, update entity_sources.published_commit, write published-source KV snapshot when runtime artifacts exist, revert DB on snapshot failure (Sentry capture on revert failure), and refresh saved-package projection.
Repo Session DO & RPC
packages/worker/src/repo/repo-session-do.ts, packages/worker/src/repo/repo-session-rpc.ts
Refactors publishSession to collect workspace and call finalizePublishedEntitySource; adds isAncestorCommit helper; exposes new public Durable Object method publishFromExternalRef and corresponding RepoSessionRpc.publishFromExternalRef RPC signature.
MCP Capabilities & Resolver
packages/worker/src/mcp/capabilities/packages/get-git-remote.ts, packages/worker/src/mcp/capabilities/packages/publish-external-push.ts, .../resolve-package-source.ts, .../domain.ts
Adds package_get_git_remote (mint short-lived token, return authenticated_remote, git_extra_header, setup_commands); adds package_publish_external_push (resolve owned package, compare Artifacts HEAD to stored published commit, call publishFromExternalRef, return discriminated publish outcomes); adds resolveOwnedPackageSource helper and wires capabilities into domain.
Scheduled Reconciliation Job
packages/worker/src/jobs/reconcile-artifacts-pushes.ts, packages/worker/src/index.ts, packages/worker/wrangler.jsonc
Adds reconcileArtifactsPushes job: list stale sources, resolve external HEADs, optionally revoke stale tokens during daily UTC 03:00–03:04 window, call repoSessionRpc(...).publishFromExternalRef(...), and update last_external_check_at; schedules a scheduled handler and cron every 5 minutes.
Tests
packages/worker/src/mcp/capabilities/packages/*.node.test.ts, packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts, packages/worker/src/repo/*.node.test.ts
Adds/updates tests for get-git-remote, publish-external-push, reconcile job, external publish, and repo-session interactions covering token TTLs, ownership checks, publish outcomes (published, already_published, not_fast_forward, checks_failed), error handling, and cron-token revocation.
Documentation
docs/contributing/architecture/data-storage.md, docs/contributing/packages-and-manifests.md, docs/use/packages.md
Documents direct git-push workflow, token minting and http.extraHeader usage, publish transaction semantics (checks, commit advance, KV snapshot and rollback), reconcile cron behavior, and updated repo-backed workflow guidance.

Sequence Diagram

sequenceDiagram
    participant User as User/Client
    participant Kody as Kody Server
    participant ArtifactsAPI as Artifacts API
    participant Git as Git Remote
    participant DB as D1 Database
    participant KV as KV Storage
    participant Reconciler as Reconcile Job

    User->>Kody: package_get_git_remote (request token)
    Kody->>ArtifactsAPI: Create short-lived token
    ArtifactsAPI-->>Kody: token (plaintext, expiresAt)
    Kody-->>User: authenticated_remote, git_extra_header, setup_commands

    User->>Git: git clone / edit / push (uses http.extraHeader)
    Git-->>ArtifactsAPI: update default-branch HEAD

    User->>Kody: package_publish_external_push
    Kody->>ArtifactsAPI: resolve default-branch HEAD
    ArtifactsAPI-->>Kody: current commit OID
    Kody->>Kody: runRepoChecks(manifest, files)
    alt checks pass
        Kody->>DB: update entity_sources.published_commit (new)
        Kody->>KV: write published source snapshot
        alt snapshot fails
            Kody->>DB: revert published_commit to previous
        end
        Kody-->>User: published (commit, checks)
    else checks fail
        Kody-->>User: checks_failed (failed_checks, manifest, run_id)
    end

    Reconciler->>DB: listEntitySourcesForExternalReconcile
    DB-->>Reconciler: candidate sources
    Reconciler->>ArtifactsAPI: resolve default-branch HEAD per source
    Reconciler->>Kody: publishFromExternalRef (for new commits)
    Reconciler->>DB: update last_external_check_at
    alt 03:00–03:04 UTC
        Reconciler->>ArtifactsAPI: listTokens / revokeToken for expired tokens
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped to mint a token bright,
Pushed my changes through the night,
The cron awoke to check the head,
Commits were tested, snapshots fed,
Tokens pruned — the repo sleeps tight.

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and clearly summarizes the main changes: adding primitives for direct git push workflows to Artifacts for packages via new capabilities and infrastructure.
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 cursor/-bc-12f6e31e-ace2-416c-9150-58550c5150d3-1e5d

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
Review rate limit: 0/1 reviews remaining, refill in 50 minutes and 19 seconds.

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

@kentcdodds
kentcdodds marked this pull request as ready for review May 4, 2026 14:58
@github-actions

github-actions Bot commented May 4, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-352.kentcdodds.workers.dev

Worker: kody-pr-352
D1: kody-pr-352-db
KV: kody-pr-352-oauth-kv

Mocks:

Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts
Comment thread packages/worker/src/repo/artifacts.ts
Comment thread packages/worker/src/repo/repo-session-do.ts

@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: 10

🧹 Nitpick comments (2)
packages/worker/src/repo/artifacts.ts (1)

636-657: ⚡ Quick win

resolveArtifactSourceHead duplicates resolveArtifactDefaultBranchHead and drops the || 'main' fallback

resolveArtifactSourceHead reinvents what resolveArtifactDefaultBranchHead (lines 520–543) already does: fetch repo info → mint a short-lived read token → list server refs for the default branch → return the OID. The duplication introduces a silent divergence: resolveArtifactDefaultBranchHead guards against an empty defaultBranch with || 'main', while resolveArtifactSourceHead uses info.defaultBranch bare. If the Artifacts API ever returns "" for default_branch, the prefix becomes refs/heads/ (matches every branch) and the find predicate entry.ref === "refs/heads/" never matches, returning commit: null — silently treating the source as already published and stalling reconciliation.

Delegate to the existing helper:

♻️ Proposed refactor
 export async function resolveArtifactSourceHead(env: Env, repoId: string) {
 	const repo = await resolveArtifactSourceRepo(env, repoId)
-	const info = await repo.info()
-	if (!info?.remote) {
-		throw new Error('Artifact repo remote URL is unavailable.')
-	}
-	const token = await repo.createToken('read', 300)
-	const auth = buildArtifactsGitAuth({ token: token.plaintext })
-	const refs = await git.listServerRefs({
-		http,
-		url: info.remote,
-		prefix: `refs/heads/${info.defaultBranch}`,
-		onAuth: () => auth,
-	})
-	const ref = refs.find(
-		(entry) => entry.ref === `refs/heads/${info.defaultBranch}`,
-	)
-	return {
-		branch: info.defaultBranch,
-		commit: ref?.oid ?? null,
-	}
+	const result = await resolveArtifactDefaultBranchHead({ repo })
+	return {
+		branch: result?.defaultBranch ?? 'main',
+		commit: result?.commit ?? null,
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/repo/artifacts.ts` around lines 636 - 657,
resolveArtifactSourceHead duplicates resolveArtifactDefaultBranchHead but omits
the defaultBranch fallback; update resolveArtifactSourceHead to delegate to
resolveArtifactDefaultBranchHead(env, repoId) (or replicate its logic including
the defaultBranch || 'main' fallback) instead of reimplementing the flow so the
empty default_branch case uses 'main' and avoids the silent mismatch that yields
commit: null; adjust any callers to use the returned { branch, commit } as
before.
packages/worker/src/mcp/capabilities/packages/get-git-remote.node.test.ts (1)

95-99: ⚡ Quick win

beforeEach flagged by prefer-dispose-in-tests — prefer disposable setup

The epic-web linter prefers moving per-test cleanup into each test body via using/await using with dispose/disposeAsync rather than a shared beforeEach block. Since each mock in this file is already reset via Object.values(mockModule), encapsulating that in a disposable is straightforward.

♻️ Suggested refactor (inline disposable)
-beforeEach(() => {
-	for (const fn of Object.values(mockModule)) {
-		fn.mockReset()
-	}
-})
-
 test('returns a write remote token expiring within the requested ttl', async () => {
+	using _ = {
+		[Symbol.dispose]() {
+			for (const fn of Object.values(mockModule)) fn.mockReset()
+		},
+	}
 	const { createToken } = mockPackageSource()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/capabilities/packages/get-git-remote.node.test.ts`
around lines 95 - 99, Replace the shared beforeEach teardown with an inline
disposable that resets mocks per test: remove the beforeEach block that iterates
Object.values(mockModule).mockReset and instead, inside each test use
using/await using to create a disposable (e.g., a small helper that captures
Object.values(mockModule) and implements dispose/disposeAsync to call mockReset
on each fn) so that mockModule mocks are reset automatically at test end; update
tests to import/instantiate that disposable at the top of each test and rely on
its dispose behavior instead of the global beforeEach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/use/packages.md`:
- Line 189: Docs show a hardcoded "main" in the git push example which can be
wrong for repos with other default branches; change the example to use the
default branch returned by package_get_git_remote instead of "main" (e.g.,
replace "HEAD:main" with "HEAD:<defaultBranch>" where <defaultBranch> is the
value from package_get_git_remote) and update the surrounding text to clarify
that <defaultBranch> should be taken from the package_get_git_remote response.
- Around line 200-205: The docs currently expose the internal column name
entity_sources.published_commit in the description of
package_publish_external_push; change the wording to avoid schema/implementation
leakage by replacing any direct mentions of entity_sources.published_commit with
a generic phrase such as "the published commit marker" or "the published commit
reference" and replace "D1/KV untouched" with "underlying storage/state is left
unchanged" (or similar neutral phrasing) so the doc conveys behavior without
surfacing column or storage engine internals.

In `@packages/worker/src/jobs/reconcile-artifacts-pushes.ts`:
- Around line 57-117: The catch block currently only increments errors and logs,
leaving lastExternalCheckAt unchanged which causes broken sources to be
re-listed; modify the catch in the loop (the try/catch inside
reconcile-artifacts-pushes that iterates sources) to call
updateEntitySource(input.env.APP_DB, { id: source.id, userId: source.user_id,
lastExternalCheckAt: now.toISOString() }) before continuing/after logging so
every source—success, skip, or error—advances the external reconcile cursor used
by listEntitySourcesForExternalReconcile; keep the existing error count/logging
behavior.
- Around line 22-24: minutesAgoIso currently uses Date.now() causing a different
clock than the job's injected time; change minutesAgoIso to accept a reference
Date or timestamp (e.g., baseNow: Date | number) and compute the cutoff from
that value instead of Date.now(), then update all callers in
reconcile-artifacts-pushes.ts (including the uses around the previous 41-43
region) to pass input.now ?? new Date() as the base; ensure signature and
callers reflect the new param so the job uses a single deterministic clock.

In `@packages/worker/src/mcp/capabilities/packages/get-git-remote.ts`:
- Around line 91-95: The setup_commands array builds git commands but misses
changing into the cloned repo: after the clone command in setup_commands (the
entry using shellQuote(cloneDirectory)), insert a cd into the clone directory
before running the subsequent commands so that `git remote add kody
${shellQuote(info.remote)}` and the push (the line referencing
info.defaultBranch) execute inside the cloned repo; update the setup_commands
sequence in get-git-remote.ts (the setup_commands variable) to include a `cd
${shellQuote(cloneDirectory)}` step between the clone and the remote/add/push
commands.
- Around line 33-45: The outputSchema currently marks authenticated_remote and
git_extra_header as secrets but leaves setup_commands unprotected, exposing
embedded Bearer tokens; update the call to markSecretInputFields so the secret
fields list includes "setup_commands" (alongside "authenticated_remote" and
"git_extra_header") to ensure the entire array is redacted, or alternatively
modify the code that builds setup_commands to substitute a placeholder token
(and keep only git_extra_header as the secret) — locate the outputSchema
definition and either add "setup_commands" to the secret list passed to
markSecretInputFields or change the command-generation logic to inject a
non-secret placeholder instead of the live gitExtraHeader value.

In `@packages/worker/src/repo/artifacts-tokens.ts`:
- Around line 19-25: The revokeToken DELETE can raise a 404 when a token was
already removed, causing reconcileArtifactsPushes to count the whole source as
an error; update revokeStaleArtifactsTokens to treat 404 as success by calling
repo.revokeToken with the option to treat404AsNull (or otherwise swallow only
404 errors) so a missing token is considered successfully revoked; reference the
revokeStaleArtifactsTokens function and repo.revokeToken to locate where to pass
treat404AsNull (or handle the 404) and ensure other errors still propagate.

In `@packages/worker/src/repo/external-publish.ts`:
- Around line 72-80: The refreshSavedPackageProjection call can throw and
currently bubbles up after published_commit is set; wrap the call to
refreshSavedPackageProjection in a try/catch that mirrors the other post-publish
mutation handling: catch any error, log a detailed error with context (include
input.source.id, input.source.entity_id/packageId, input.env and the
published_commit if available) using the existing logger (e.g.,
processLogger.error), do not rethrow so the publish does not fail, and
optionally emit a metric/trace for retrying—but do not change the
already-committed state flow.

In `@packages/worker/src/repo/repo-session-do.ts`:
- Around line 1435-1438: The current check comparing input.expectedHead to
input.newCommit is insufficient because callers pass the same resolved commit
for both; instead query the remote repository's actual HEAD for the branch being
published and compare that remote HEAD to input.expectedHead: if they differ,
throw the same error. In practice, inside the repo-session-do.ts operation where
input.expectedHead and input.newCommit are used, call the existing repo/session
method that reads the remote ref (e.g., getRemoteRef/getRefHead or the session's
fetch/ref-read helper) to obtain the current remote HEAD for the target branch,
then compare that value to input.expectedHead and throw the error if mismatched
before allowing the publish to proceed.
- Around line 1465-1476: The call to publishExternalRefSource is passing
repo-relative source.manifest_path and source.source_root (which point into the
cloned repo under /session) so the external-push helper reads the wrong
locations; resolve those paths against the session workspace before calling
publishExternalRefSource (the same way runChecks does) and pass the resolved
manifest path and source root instead of
source.manifest_path/source.source_root. Update the arguments to
publishExternalRefSource (call site in this file) to use the workspace-resolved
values (e.g., join this.workspace or input.workspace with source.manifest_path
and source.source_root) so publishExternalRefSource can read the correct files.

---

Nitpick comments:
In `@packages/worker/src/mcp/capabilities/packages/get-git-remote.node.test.ts`:
- Around line 95-99: Replace the shared beforeEach teardown with an inline
disposable that resets mocks per test: remove the beforeEach block that iterates
Object.values(mockModule).mockReset and instead, inside each test use
using/await using to create a disposable (e.g., a small helper that captures
Object.values(mockModule) and implements dispose/disposeAsync to call mockReset
on each fn) so that mockModule mocks are reset automatically at test end; update
tests to import/instantiate that disposable at the top of each test and rely on
its dispose behavior instead of the global beforeEach.

In `@packages/worker/src/repo/artifacts.ts`:
- Around line 636-657: resolveArtifactSourceHead duplicates
resolveArtifactDefaultBranchHead but omits the defaultBranch fallback; update
resolveArtifactSourceHead to delegate to resolveArtifactDefaultBranchHead(env,
repoId) (or replicate its logic including the defaultBranch || 'main' fallback)
instead of reimplementing the flow so the empty default_branch case uses 'main'
and avoids the silent mismatch that yields commit: null; adjust any callers to
use the returned { branch, commit } as before.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 76b3c3a3-60c8-4181-a0bb-7656369c4f71

📥 Commits

Reviewing files that changed from the base of the PR and between 42bc6e2 and ca540b8.

📒 Files selected for processing (23)
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/packages-and-manifests.md
  • docs/use/packages.md
  • packages/worker/migrations/0032-entity-sources-external-check.sql
  • packages/worker/src/index.ts
  • packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts
  • packages/worker/src/jobs/reconcile-artifacts-pushes.ts
  • packages/worker/src/mcp/capabilities/packages/domain.ts
  • packages/worker/src/mcp/capabilities/packages/get-git-remote.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/get-git-remote.ts
  • packages/worker/src/mcp/capabilities/packages/publish-external-push.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/publish-external-push.ts
  • packages/worker/src/mcp/capabilities/packages/resolve-package-source.ts
  • packages/worker/src/repo/artifacts-tokens.ts
  • packages/worker/src/repo/artifacts.ts
  • packages/worker/src/repo/entity-sources.ts
  • packages/worker/src/repo/external-publish.node.test.ts
  • packages/worker/src/repo/external-publish.ts
  • packages/worker/src/repo/repo-session-do.ts
  • packages/worker/src/repo/repo-session-rpc.ts
  • packages/worker/src/repo/source-service.ts
  • packages/worker/src/repo/types.ts
  • packages/worker/wrangler.jsonc

Comment thread docs/use/packages.md Outdated
Comment thread docs/use/packages.md Outdated
Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts Outdated
Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts
Comment thread packages/worker/src/mcp/capabilities/packages/get-git-remote.ts
Comment thread packages/worker/src/mcp/capabilities/packages/get-git-remote.ts
Comment thread packages/worker/src/repo/artifacts-tokens.ts
Comment thread packages/worker/src/repo/external-publish.ts
Comment thread packages/worker/src/repo/repo-session-do.ts Outdated
Comment thread packages/worker/src/repo/repo-session-do.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@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 (2)
packages/worker/src/repo/repo-session-do.node.test.ts (1)

95-100: ⚡ Quick win

Add a direct test for expectedHead mismatch in publishFromExternalRef.

Since this mock was introduced, add one case that sets expectedHead to a stale commit and asserts the method throws the HEAD-changed error. It will lock in the race-prevention behavior that this PR adds.

Also applies to: 191-193

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/repo/repo-session-do.node.test.ts` around lines 95 - 100,
Add a unit test that verifies publishFromExternalRef throws the "HEAD changed"
error when expectedHead is stale: update the test suite that mocks
resolveArtifactDefaultBranchHead (the vi.fn returning defaultBranch 'main' and
commit 'commit-published-new') to include an additional test case that calls
publishFromExternalRef with expectedHead set to an older commit (e.g.,
'commit-stale') and assert the call rejects/throws the HEAD-changed error; place
the assertion alongside the existing tests that reference
resolveArtifactDefaultBranchHead so the race-prevention behavior is locked in.
packages/worker/src/repo/external-publish.node.test.ts (1)

163-186: ⚡ Quick win

Add an allowForce success-path test for rewritten history.

This segment validates the reject path (isFastForward: false) but not the override path. A companion case with allowForce: true should assert publish succeeds and advances publishedCommit, so force-publish behavior can’t regress unnoticed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/repo/external-publish.node.test.ts` around lines 163 -
186, Add a new test that mirrors the existing non-fast-forward rejection but
sets allowForce: true when calling publishFromExternalRef; call
publishFromExternalRef with isFastForward: false and allowForce: true and assert
the result indicates a successful publish (e.g., status 'published' and
published_commit 'commit-rewritten' with previous_commit 'commit-old'), and
assert mockModule.runRepoChecks and mockModule.updateEntitySource were called
(and updateEntitySource received the new commit). Target the same test
helpers/fixtures used in the current test and reference publishFromExternalRef,
runRepoChecks, and updateEntitySource when implementing the assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/repo/artifacts-tokens.ts`:
- Around line 24-31: The code increments revoked after the try/catch, which
counts tokens that only raised a 404 as revoked; move the revoked += 1 so it
only runs on a successful revoke. Specifically, inside the try block immediately
after await repo.revokeToken(token.id) increment revoked, keep the catch logic
(if (!(error instanceof CloudflareApiError && error.status === 404)) throw
error) unchanged so 404s are ignored but not counted.

In `@packages/worker/src/repo/repo-session-do.ts`:
- Around line 452-471: The current isAncestorCommit implementation uses git.log
with a fixed depth (depth: 1000) which can miss true ancestors; replace the
log-based check with a proper git ancestry check (e.g., run git merge-base
--is-ancestor <ancestor> <descendant> or use the library equivalent) inside
isAncestorCommit so the command's exit status determines ancestry reliably;
apply the same replacement for the other occurrence of this logic (the similar
block referenced around the later isAncestor usage).

---

Nitpick comments:
In `@packages/worker/src/repo/external-publish.node.test.ts`:
- Around line 163-186: Add a new test that mirrors the existing non-fast-forward
rejection but sets allowForce: true when calling publishFromExternalRef; call
publishFromExternalRef with isFastForward: false and allowForce: true and assert
the result indicates a successful publish (e.g., status 'published' and
published_commit 'commit-rewritten' with previous_commit 'commit-old'), and
assert mockModule.runRepoChecks and mockModule.updateEntitySource were called
(and updateEntitySource received the new commit). Target the same test
helpers/fixtures used in the current test and reference publishFromExternalRef,
runRepoChecks, and updateEntitySource when implementing the assertions.

In `@packages/worker/src/repo/repo-session-do.node.test.ts`:
- Around line 95-100: Add a unit test that verifies publishFromExternalRef
throws the "HEAD changed" error when expectedHead is stale: update the test
suite that mocks resolveArtifactDefaultBranchHead (the vi.fn returning
defaultBranch 'main' and commit 'commit-published-new') to include an additional
test case that calls publishFromExternalRef with expectedHead set to an older
commit (e.g., 'commit-stale') and assert the call rejects/throws the
HEAD-changed error; place the assertion alongside the existing tests that
reference resolveArtifactDefaultBranchHead so the race-prevention behavior is
locked in.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c666155-3605-4d13-9693-594f2d757a0b

📥 Commits

Reviewing files that changed from the base of the PR and between ca540b8 and 3db51eb.

📒 Files selected for processing (11)
  • docs/use/packages.md
  • packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts
  • packages/worker/src/jobs/reconcile-artifacts-pushes.ts
  • packages/worker/src/mcp/capabilities/packages/get-git-remote.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/get-git-remote.ts
  • packages/worker/src/repo/artifacts-tokens.ts
  • packages/worker/src/repo/artifacts.ts
  • packages/worker/src/repo/external-publish.node.test.ts
  • packages/worker/src/repo/external-publish.ts
  • packages/worker/src/repo/repo-session-do.node.test.ts
  • packages/worker/src/repo/repo-session-do.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/use/packages.md
  • packages/worker/src/jobs/reconcile-artifacts-pushes.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts
  • packages/worker/src/repo/external-publish.ts
  • packages/worker/src/repo/artifacts.ts
  • packages/worker/src/mcp/capabilities/packages/get-git-remote.ts

Comment thread packages/worker/src/repo/artifacts-tokens.ts Outdated
Comment thread packages/worker/src/repo/repo-session-do.ts
Comment thread packages/worker/src/repo/repo-session-do.ts
Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/repo/entity-sources.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":502,"request":{"method":"PATCH","url":"https://api.github.com/repos/kentcdodds/kody/issues/comments/4371987149","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: rate limited by coderabbit.ai -->\n\n> [!WARNING]\n> ## Rate limit exceeded\n> \n> `@cursor`[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait **36 minutes and 13 seconds** before requesting another review.\n> \n> To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under [billing](https://app.coderabbit.ai/settings/subscription?tab=usage).\n> \n> <details>\n> <summary>⌛ How to resolve this issue?</summary>\n> \n> After the wait time has elapsed, a review can be triggered using the `@coderabbitai review` command as a PR comment. Alternatively, push new commits to this PR.\n> \n> We recommend that you space out your commits to avoid hitting the rate limit.\n> \n> </details>\n> \n> \n> <details>\n> <summary>🚦 How do rate limits work?</summary>\n> \n> CodeRabbit enforces hourly rate limits for each developer per organization.\n> \n> Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.\n> \n> Please see our [FAQ](https://docs.coderabbit.ai/faq) for further information.\n> \n> </details>\n> \n> <details>\n> <summary>ℹ️ Review info</summary>\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: defaults\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro Plus\n> \n> **Run ID**: `844940de-fe48-4a0c-a609-759cba035392`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between 3db51eb72218c9e3462c2897c05fdb5224d4307b and b184a6d10fd7c56a7538998088c6efe487958f37.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (8)</summary>\n> \n> * `packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts`\n> * `packages/worker/src/jobs/reconcile-artifacts-pushes.ts`\n> * `packages/worker/src/repo/artifacts-tokens.ts`\n> * `packages/worker/src/repo/entity-sources.ts`\n> * `packages/worker/src/repo/external-publish.node.test.ts`\n> * `packages/worker/src/repo/external-publish.ts`\n> * `packages/worker/src/repo/repo-session-do.node.test.ts`\n> * `packages/worker/src/repo/repo-session-do.ts`\n> \n> </details>\n> \n> </details>\n\n<!-- end of auto-generated comment: rate limited by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds end-to-end support for external Cloudflare Artifacts git pushes: minting short-lived tokens, resolving Artifacts HEADs, publishing pushed commits through server-side checks and transactional publish (DB + KV snapshot with rollback), a reconcile cron that detects/publishes stale pushes and revokes stale tokens, plus MCP capabilities and tests.\n\n## Changes\n\n**External Artifacts Git Push Workflow**\n\n|Layer / File(s)|Summary|\n|---|---|\n|**Data Model & Schema** <br> `packages/worker/migrations/0032-entity-sources-external-check.sql`, `packages/worker/src/repo/types.ts`, `packages/worker/src/repo/source-service.ts`|Adds `entity_sources.last_external_check_at` (TEXT) and index; `EntitySourceRow` includes nullable `last_external_check_at`; introduces `RepoExternalPublishResult` union type.|\n|**Artifact REST & Git Ref Helpers** <br> `packages/worker/src/repo/artifacts.ts`|Adds `ArtifactStoredToken` type, optional `listTokens()`/`revokeToken()` on `ArtifactRepoHandle`, implements list/revoke via Artifacts REST, and adds `listArtifactServerRefs`, `resolveArtifactDefaultBranchHead`, `resolveArtifactSourceHead`.|\n|**Token Revocation** <br> `packages/worker/src/repo/artifacts-tokens.ts`|Adds `revokeStaleArtifactsTokens(env, repoName, { keepAfter })` to list tokens and revoke those expiring before cutoff, ignoring 404s.|\n|**Entity Source Persistence & Queries** <br> `packages/worker/src/repo/entity-sources.ts`|Parses/stores `last_external_check_at`; `updateEntitySource` accepts `lastExternalCheckAt`; adds `listEntitySourcesForExternalReconcile(db, { before, limit })`.|\n|**External Publish Implementation** <br> `packages/worker/src/repo/external-publish.ts`, `packages/worker/src/repo/types.ts`|Implements `publishFromExternalRef` and `finalizePublishedEntitySource`: run checks, gate non-fast-forward unless allowed, update `entity_sources.published_commit`, write KV snapshot when runtime artifacts exist, revert DB on snapshot failure (Sentry reporting), refresh package projection.|\n|**Repo Session DO & RPC** <br> `packages/worker/src/repo/repo-session-do.ts`, `packages/worker/src/repo/repo-session-rpc.ts`|Refactors `publishSession` to call `finalizePublishedEntitySource`; adds `isAncestorCommit` helper; adds new public DO method `publishFromExternalRef` and corresponding RPC signature.|\n|**MCP Capabilities** <br> `packages/worker/src/mcp/capabilities/packages/get-git-remote.ts`, `packages/worker/src/mcp/capabilities/packages/publish-external-push.ts`, `packages/worker/src/mcp/capabilities/packages/domain.ts`, `packages/worker/src/mcp/capabilities/packages/resolve-package-source.ts`|Adds `package_get_git_remote` (mint token, return authenticated_remote, git_extra_header, setup commands), `package_publish_external_push` (resolve owned package, compare Artifacts HEAD to published commit, call `publishFromExternalRef`), capability wiring and resolver helper enforcing exactly-one identifier.|\n|**Scheduled Reconciliation Job** <br> `packages/worker/src/jobs/reconcile-artifacts-pushes.ts`, `packages/worker/src/index.ts`, `packages/worker/wrangler.jsonc`|Adds `reconcileArtifactsPushes` job: list stale sources, resolve external HEADs, optionally revoke stale tokens during daily UTC 03:00–03:04 window, call `repoSessionRpc(...).publishFromExternalRef(...)`, update `last_external_check_at`; schedules worker `scheduled` handler (cron every 5 minutes).|\n|**Tests** <br> `packages/worker/src/mcp/capabilities/packages/*.node.test.ts`, `packages/worker/src/jobs/reconcile-artifacts-pushes.node.test.ts`, `packages/worker/src/repo/*.node.test.ts`|Adds/updates test suites for get-git-remote, publish-external-push, reconcile job, external publish, and repo-session interactions covering token TTLs, ownership checks, publish outcomes (published, already_published, not_fast_forward, checks_failed), and cron-token revocation.|\n|**Documentation** <br> `docs/contributing/architecture/data-storage.md`, `docs/contributing/packages-and-manifests.md`, `docs/use/packages.md`|Documents direct git-push workflow, token minting and http.extraHeader usage, publish transaction semantics, reconcile cron behavior, and updated repo-backed workflow guidance.|\n\n## Sequence Diagram\n\n```mermaid\nsequenceDiagram\n    participant User as User/Client\n    participant Kody as Kody Server\n    participant ArtifactsAPI as Artifacts API\n    participant Git as Git Remote\n    participant DB as D1 Database\n    participant KV as KV Storage\n    participant Reconciler as Reconcile Job\n\n    User->>Kody: package_get_git_remote (request token)\n    Kody->>ArtifactsAPI: Create short-lived token\n    ArtifactsAPI-->>Kody: token (plaintext, expiresAt)\n    Kody-->>User: authenticated_remote, git_extra_header, setup_commands\n\n    User->>Git: git clone / edit / push (uses http.extraHeader)\n    Git-->>ArtifactsAPI: update default-branch HEAD\n\n    User->>Kody: package_publish_external_push\n    Kody->>ArtifactsAPI: resolve default-branch HEAD\n    ArtifactsAPI-->>Kody: current commit OID\n    Kody->>Kody: runRepoChecks(manifest, files)\n    alt checks pass\n        Kody->>DB: update entity_sources.published_commit (new)\n        Kody->>KV: write published source snapshot\n        alt snapshot fails\n            Kody->>DB: revert published_commit to previous\n        end\n        Kody-->>User: published (commit, checks)\n    else checks fail\n        Kody-->>User: checks_failed (failed_checks, manifest, run_id)\n    end\n\n    Reconciler->>DB: listEntitySourcesForExternalReconcile\n    DB-->>Reconciler: candidate sources\n    Reconciler->>ArtifactsAPI: resolve default-branch HEAD per source\n    Reconciler->>Kody: publishFromExternalRef (for new commits)\n    Reconciler->>DB: update last_external_check_at\n    alt 03:00–03:04 UTC\n        Reconciler->>ArtifactsAPI: listTokens / revokeToken for expired tokens\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~60 minutes\n\n## Possibly related PRs\n\n- kentcdodds/kody#209: Artifacts REST control‑plane mock and related token/ref endpoints used by the new token listing/revocation and ref-resolution logic.\n- kentcdodds/kody#218: Prior repo-session publish changes that this PR refactors to use `finalizePublishedEntitySource`.\n- kentcdodds/kody#181: Earlier work establishing repo-backed source/session foundations that this PR extends with external publish and reconciliation flows.\n\n## Poem\n\n> 🐰 I hopped to mint a token bright,  \n> Pushed my changes through the night,  \n> The cron woke up, it checked the head,  \n> Published snapshots, rolled back dread,  \n> Tokens pruned — the repo sleeps tight. 🌙\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 4 | ❌ 1</summary>\n\n### ❌ Failed checks (1 warning)\n\n|     Check name     | Status     | Explanation                                                                          | Resolution                                                                         |\n| :----------------: | :--------- | :----------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- |\n| 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. |\n\n<details>\n<summary>✅ Passed checks (4 passed)</summary>\n\n|         Check name         | Status   | Explanation                                                                                                                                                                             |\n| :------------------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|      Description Check     | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                             |\n|         Title check        | ✅ Passed | The title accurately and concisely describes the main change: adding git push capabilities for packages via Artifacts. It's specific, clear, and reflects the primary feature addition. |\n|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                |\n| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `cursor/-bc-12f6e31e-ace2-416c-9150-58550c5150d3-1e5d`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=kentcdodds/kody&utm_content=352)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n<!-- review_rate_limit_status_start -->\n<sub>Review rate limit: 0/1 reviews remaining, refill in 36 minutes and 13 seconds.</sub>\n<!-- review_rate_limit_status_end -->\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/kentcdodds/kody/issues/comments/4371987149","status":502,"headers":{"content-length":"32","content-type":"application/json","date":"Mon, 04 May 2026 15:38:44 GMT","etag":"\"69f8a404-20\"","server":"github.com","vary":"Accept-Encoding, Accept, X-Requested-With","x-github-request-id":"4086:248666:1849C4D:60C5B78:69F8BD78","x-ratelimit-limit":"12500","x-ratelimit-remaining":"12175","x-ratelimit-reset":"1777909493","x-ratelimit-resource":"core","x-ratelimit-used":"325"},"data":{"message":"Server Error"}}}

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/index.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/repo/artifacts-tokens.ts
Comment thread packages/worker/src/mcp/capabilities/packages/get-git-remote.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/mcp/capabilities/packages/get-git-remote.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts
Comment thread packages/worker/src/jobs/reconcile-artifacts-pushes.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 43a5195. Configure here.

.bind(input.before, input.limit)
.all<Record<string, unknown>>()
return (results ?? []).map(mapEntitySourceRow)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reconcile scans all entity sources without kind filter

Low Severity

listEntitySourcesForExternalReconcile selects from all entity_sources rows without filtering by entity_kind. Entity sources for non-package kinds (skill, app, job) will be picked up by the reconcile cron every cycle, each triggering an Artifacts HEAD resolution that may error out, wasting API calls and generating log noise.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 43a5195. Configure here.

@kentcdodds
kentcdodds merged commit 3048746 into main May 4, 2026
9 checks passed
@kentcdodds
kentcdodds deleted the cursor/-bc-12f6e31e-ace2-416c-9150-58550c5150d3-1e5d branch May 4, 2026 16:40
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