Skip to content

feat(release-service): add delegated release foundations - #2743

Open
ascorbic wants to merge 8 commits into
feat/blob-hosted-registry-artifactsfrom
feat/drs-review-01-foundation
Open

feat(release-service): add delegated release foundations#2743
ascorbic wants to merge 8 commits into
feat/blob-hosted-registry-artifactsfrom
feat/drs-review-01-foundation

Conversation

@ascorbic

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds the foundation for the delegated release service: the Worker shell, exact create-only PDS conformance harness, confidential OAuth metadata, compact JWE envelope encryption, safe unexpected-error logging, and CI support for dependent stack PR bases.

This is PR 1 of 7. Its three focused commits are retained for review. The stack merges as one unit and no intermediate branch is a deployable service version.

Discussion: #1590

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (not applicable in this layer)
  • I have added and reviewed the user-facing changeset for the published CLI OAuth fix
  • New features link to an approved Discussion: RFC: Attested Automated Publishing #1590

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex (GPT-5)

Screenshots / test output

The complete stack passes formatting, strict lint, package/application typechecks and tests. The public-client G0 lifecycle passed against the npmX-hosted Bluesky PDS and Cirrus; deployment-only confidential-client checks remain pending.

Copilot AI lite review requested due to automatic review settings August 27, 2026 15:03
@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 60c5462

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@emdash-cms/plugin-cli Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond area/ci area/docs size/XL labels Aug 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 17,762 lines across 34 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new

pkg-pr-new Bot commented Aug 27, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/admin@2743

@emdash-cms/auth

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth@2743

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth-atproto@2743

@emdash-cms/blocks

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/blocks@2743

@emdash-cms/cloudflare

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/cloudflare@2743

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/contentful-to-portable-text@2743

emdash

npm i https://pkg.pr.new/emdash-cms/emdash@2743

create-emdash

npm i https://pkg.pr.new/emdash-cms/emdash/create-emdash@2743

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/gutenberg-to-portable-text@2743

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-cli@2743

@emdash-cms/plugin-types

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-types@2743

@emdash-cms/registry-client

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-client@2743

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-lexicons@2743

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-moderation@2743

@emdash-cms/registry-verification

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-verification@2743

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/sandbox-workerd@2743

@emdash-cms/x402

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/x402@2743

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-ai-moderation@2743

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-atproto@2743

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-audit-log@2743

@emdash-cms/plugin-color

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-color@2743

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-embeds@2743

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-field-kit@2743

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-forms@2743

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-webhook-notifier@2743

commit: 60c5462

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 27, 2026

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://feat-drs-review-01-foundation.try.emdashcms.com, https://feat-drs-review-01-foundation-emdash-playground.emdash-cms.workers.dev (commit 60c5462)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://9ca575c8.try.emdashcms.com, https://9ca575c8-emdash-playground.emdash-cms.workers.dev 60c5462 2026-08-28T23:17:02.343Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://32e7b280.try.emdashcms.com, https://32e7b280-emdash-playground.emdash-cms.workers.dev 1c65031 2026-08-28T21:39:58.540Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://b5c2558e.try.emdashcms.com, https://b5c2558e-emdash-playground.emdash-cms.workers.dev 8b98602 2026-08-28T15:21:51.949Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://d3fa24f9.try.emdashcms.com, https://d3fa24f9-emdash-playground.emdash-cms.workers.dev bdebf5d 2026-08-28T11:20:52.213Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://a10ac034.try.emdashcms.com, https://a10ac034-emdash-playground.emdash-cms.workers.dev fbb6990 2026-08-28T10:18:49.862Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://5d951752.try.emdashcms.com, https://5d951752-emdash-playground.emdash-cms.workers.dev 40c24fc 2026-08-27T15:19:15.618Z Visit the dashboard ↗

@emdashbot emdashbot 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.

This PR lays out a sensible foundation for the delegated release service: a minimal Cloudflare Worker shell, strict create-only OAuth client metadata, a compact context-bound JWE encryption layer, safe error logging, and a CLI-side OAuth registration fix that lets saved sessions refresh/revoke after the loopback callback server closes. It also wires up an exact-scope PDS conformance harness behind a hidden command and links to the approved Discussion. Overall approach fits the architecture and the approved design.

I checked the changed source files and tests, traced the new CLI OAuth registration store, verified the conformance harness against the docs steps, and compared the new Worker app against repo conventions. The code is generally careful (fail-closed configuration, context digests bound to deployment + row identity, no raw config values in error bodies). The most important gap is CI/integration coverage: the new @emdash-cms/release-service package is not included in root workspace scripts or the CI test/build pipeline, so its tests will not run automatically. I also found a few smaller robustness/doc items.

A note on an unverified concern: getStoredSessionMetadata returns tokenSet.expires_at as expiresAt unchanged, and pds-conformance.ts compares it to Date.now(). If atcute stores expires_at as seconds (the common OAuth convention), the refreshDue report field will be misleading. I couldn’t confirm the unit without running the library, so I’m not listing it as a confirmed finding, but it’s worth checking before relying on that field.


Findings

  • [needs fixing] package.json:13

    The new apps/release-service package has its own typecheck and test scripts and a sizable new test suite, but root test:unit (line 13) and typecheck (line 8) only cover packages/* plus a few named package filters. The Worker app is therefore not exercised by pnpm test:unit or pnpm typecheck.

    Add @emdash-cms/release-service to both root scripts so developers and CI exercise the new app the same way as other packages.

  • [needs fixing] .github/workflows/ci.yml:125

    The CI test job’s build step builds the packages it intends to test but does not include @emdash-cms/release-service. Even if the root test:unit script is updated, this build command still needs the new app so its workspace links are built before tests run.

    Current line:

          - run: pnpm run --filter emdash... --filter "@emdash-cms/aggregator" --filter "@emdash-cms/labeler" --filter "@emdash-cms/plugin-cli" --filter "@emdash-cms/registry-*" --filter "@emdash-cms/plugin-types" build
          - run: pnpm run --filter emdash... --filter "@emdash-cms/aggregator" --filter "@emdash-cms/labeler" --filter "@emdash-cms/plugin-cli" --filter "@emdash-cms/registry-*" --filter "@emdash-cms/plugin-types" --filter "@emdash-cms/release-service" build
    
  • [suggestion] apps/release-service/src/crypto/encryption.ts:226

    parseEnvelope validates segments 0, 1, 2, and 4 are non-empty, but it does not reject an empty ciphertext segment (segment 3). A compact JWE with an empty ciphertext is malformed and should be rejected explicitly before reaching compactDecrypt.

    		segments[0]?.length === 0 ||
    		segments[1]?.length === 0 ||
    		segments[2]?.length === 0 ||
    		segments[3]?.length === 0 ||
    		segments[4]?.length === 0
    
  • [suggestion] docs/technical-specs/delegated-release-service-g0-conformance.md:197

    The result matrix dates the npmX and Cirrus public-client results as 2026-08-25, but the narrative sections above state the authorization runs completed on 2026-08-24. If the matrix reflects the later refresh/revoke phases, clarify the column meaning; otherwise align the dates so readers don’t think the authorization and matrix dates conflict.

  • [suggestion] packages/plugin-cli/src/commands/pds-conformance.ts:170

    refreshDue compares metadataBefore.expiresAt directly against Date.now(). getStoredSessionMetadata surfaces tokenSet.expires_at unchanged, and many OAuth libraries store that value as seconds since epoch. If atcute does the same, this comparison is always true on resume and the refreshDue report field becomes misleading. Confirm the unit and convert to milliseconds before comparing if needed.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 27, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 60c5462 Aug 28 2026, 11:11 PM

@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@ascorbic
ascorbic force-pushed the feat/drs-review-01-foundation branch from 40c24fc to fbb6990 Compare August 28, 2026 10:15
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 28, 2026
@ascorbic
ascorbic force-pushed the feat/drs-review-01-foundation branch from fbb6990 to bdebf5d Compare August 28, 2026 11:15
@ascorbic
ascorbic changed the base branch from main to feat/blob-hosted-registry-artifacts August 28, 2026 11:16
@ascorbic

Copy link
Copy Markdown
Collaborator Author

🔍 Adversarial review — stack #2766, layer 2 of 8

Automated deep review of this layer's diff (feat/blob-hosted-registry-artifacts...feat/drs-review-01-foundation). Findings ranked most-severe first; confidence is CONFIRMED (full code path read) or PLAUSIBLE (strong suspicion, path partially read). Nothing was auto-fixed.

What the PR actually does + verdict

PR #2743 adds a new private Cloudflare Worker (apps/release-service) with three GET routes (client metadata, JWKS, health), fail-closed configuration parsing (origin, deployment ID, redirect URIs, ES256 assertion keyset, encryption keyring), a compact-JWE envelope-encryption module (A256GCMKW + A256GCM, versioned keyring, context digest bound in the protected header), and redacted error logging. It adds an exact-scope PDS conformance probe runner to @emdash-cms/registry-client (./internal/conformance export), a hidden pds-conformance command to the published CLI, reworks CLI OAuth session resume/revoke to persist and reuse the original loopback client registration (the "published CLI OAuth fix", with a changeset), a citty 0.1→0.2 bump, a spec/evidence doc, and a one-line CI change so checks run on stacked PRs. Overall the crypto and worker shell are carefully written with genuinely strong tests (real jose crypto, header tampering, fail-closed config), but the layer ships with its own test suite red, the conformance harness has a soundness hole, and the OAuth fix has a design flaw in what it persists. Verdict: solid engineering undermined by a rebase-staleness break at exactly the thing the PR claims ("exact scope"), which a later stack commit quietly fixes.

Findings

  • [high] apps/release-service/test/config.test.ts:12, test/worker.test.ts:29the layer's own tests fail. Both assert scope: "atproto repo:com.emdashcms.experimental.package.release?action=create", but getDelegatedReleasePermission() at this commit (inherited from base feat(registry): host release artifacts as publisher blobs #2765) returns that string plus blob:application/gzip blob:image/*, and config.ts puts permission.scope into the client metadata verbatim. Verified empirically by running the suite at this ref in an isolated clone: 2 failed / 60 passed. A later stack commit (35c901a9 fix(release-service): request artifact blob scopes) repairs the expectations, but this layer as-is is red, and the PR's "exact create-only" framing is false at this layer. CONFIRMED (empirically).
  • [high] CI never sees the failure: release-service is excluded from CI at this layer. Root test:unit and typecheck filters (package.json:8,13) enumerate packages plus @emdash-cms/aggregator/@emdash-cms/labeler but not @emdash-cms/release-service; only the top stack layer adds it to ci.yml. So the very PR that enables CI on stacked PRs ships a Worker whose failing tests and typecheck run in no CI job. CONFIRMED.
  • [medium] packages/registry-client/src/conformance/index.ts:74 (expectedDenial) — any 4xx counts as a scope denial. A 401 from a token that expires between the create probe and the deny probes, or a 429 rate limit, makes release-update/release-delete/profile-create/unrelated-create all "pass" without the PDS ever demonstrating scope enforcement — a non-conforming PDS can produce a passing report. The error code (ScopeMissingError/InsufficientScope, both recorded in the doc's evidence) is captured but never checked. The tests cover 5xx→error but not 401/429. CONFIRMED semantics, PLAUSIBLE false-pass scenario.
  • [medium] packages/plugin-cli/src/oauth.ts:735-740registration stores the granted scope, but the refresh token is bound to the requested scope. atcute builds the loopback client_id from metadata.scope verbatim (@atcute/oauth-types buildLoopbackClientId); authorize used the requested scope (DEFAULT_CLI_SCOPE / options.scope / legacy fallback), while clients.json records storedSession.tokenSet.scope (the AS's token-response scope). Whenever granted ≠ requested — partial grants are a state this codebase explicitly anticipates (publish.ts:298 missingBlobScopes on the active grant), and buildLoopbackClientId even drops the scope param entirely when it equals the default atproto — resume/revoke reconstruct a different client_id and refresh fails again, the exact bug the changeset claims to fix. Store the client's metadata.scope instead. CONFIRMED code path; trigger depends on AS behavior.
  • [medium] packages/plugin-cli/src/oauth.ts:772-775 (getClientRegistration) — upgrading the published CLI hard-invalidates every existing session immediately. resumeSession throws "sign in again" for any pre-fix session (no clients.json entry) even when the access token is still valid and no refresh is needed. The changeset's "Sign in again before their access token expires" implies a grace window that does not exist. Acceptable as a fix, but the behavior and the release note disagree. CONFIRMED.
  • [low] packages/plugin-cli/src/oauth.ts:838-846 — non-strict revokeSession for pre-fix sessions silently skips server-side revocation entirely (deletes local state, serverRevoked: false); the old code at least attempted client.revoke. Mitigated by the old attempt likely failing at the AS anyway. CONFIRMED.
  • [low] apps/release-service/src/crypto/encryption.ts:4 — A256GCMKW wraps each CEK under the long-lived master key with a random 96-bit IV; nothing enforces rotation before the NIST SP 800-38D ~2^32-wrap bound. Deterministic A256KW would remove nonce management from the master key. PLAUSIBLE, long-horizon.
  • [low] apps/release-service/src/index.ts:31-41 — unexpected-error logs carry no error identity at all ({name:"UnhandledError"}, no constructor name, no EncryptionError.code). Redaction is correct (test proves the message can't leak), but production 500s are undiagnosable. CONFIRMED behavior.

PR description vs code

  • "Exact create-only PDS conformance" / "exact-scope OAuth metadata": at this layer the delegated permission (and therefore the served client metadata and the CLI's conformance authorize request) includes blob:application/gzip blob:image/*. The spec doc (docs/technical-specs/delegated-release-service-g0-conformance.md) contradicts the code in the same way: it records granted scope as create-only, and states "the browser consent screen must show only the active release collection's create authority" — untrue as of this rebase. Its 2026-08-24/25 evidence was gathered against the pre-blob-scope permission but is presented as passing the current "exact scope" column.
  • Changeset wording vs. behavior (grace window), per the medium finding above.
  • "CI support for dependent stack PR bases" is accurate and safe (pull_request event, read-only permissions, pinned actions, persist-credentials: false, no pull_request_target) — but it does not extend to the new Worker, whose checks exist in no CI job until the top layer.
  • Verified non-issues worth stating: wrangler.jsonc's top-level secrets.required is valid in the bundled wrangler schema (secrets are real bindings, not vars); citty 0.2 still yields boolean|undefined for boolean args (the args.json === true change is typing-only); JWKS route provably never serves d; error redaction genuinely holds on every catch path traced.

Test-coverage gaps

  • Conformance harness: no test that a 401/429 on a deny probe is rejected (only 5xx is tested); no com.atproto.repo.applyWrites update/delete probe; no probes at all for the granted blob scopes (uploadBlob allowed for gzip/image, denied for other MIME types).
  • oauth-registration.test.ts mocks fetch with blanket 200s and never asserts that the client_id sent to the token/revocation endpoint matches the authorize-time client_id — the central invariant of the fix — so the granted-vs-requested scope defect is invisible to it; the granted≠requested case has no test.
  • No test covers resumeSession for a session whose registration scope differs from the stored token scope, nor needsRotation throwing on malformed input (interface says boolean).

Verification environment: full test runs at ref bdebf5db in an isolated clone (release-service: 2 failed/60 passed; plugin-cli 404 passed; registry-client 86 passed; release-service typecheck clean). The shared working tree was not modified.

~ 🤖 Fable

@ascorbic
ascorbic force-pushed the feat/drs-review-01-foundation branch from bdebf5d to 8b98602 Compare August 28, 2026 15:11
@github-actions

Copy link
Copy Markdown
Contributor

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
packages/admin/src/locales/ar/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/ca/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/cs/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/de/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/en-GB/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/en/messages.po Source changed, localizations will be marked as outdated.
packages/admin/src/locales/es-419/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/es-ES/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/eu/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/fa/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/fr/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/hi/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/hu/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/id/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/ja/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/ko/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/nb/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/nl/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/pl/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/pseudo/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/pt-BR/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/sr-Latn/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/sv/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/th/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/tr/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/uk/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/zh-CN/messages.po Localization changed, will be marked as complete. 🔄️
packages/admin/src/locales/zh-TW/messages.po Localization changed, will be marked as complete. 🔄️
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@ascorbic
ascorbic force-pushed the feat/drs-review-01-foundation branch 2 times, most recently from 4e1c2c1 to 1c65031 Compare August 28, 2026 21:19
@ascorbic
ascorbic force-pushed the feat/drs-review-01-foundation branch from 1c65031 to 60c5462 Compare August 28, 2026 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants