Skip to content

refactor(daemon): move CLI-shared daemon modules out of the daemon package and gate CLI reachability (R78) - #3625

Open
thymikee wants to merge 2 commits into
refactor/daemon-wire-types-to-contractsfrom
refactor/daemon-client-deps-to-contracts
Open

thymikee wants to merge 2 commits into
refactor/daemon-wire-types-to-contractsfrom
refactor/daemon-client-deps-to-contracts

Conversation

@thymikee

@thymikee thymikee commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Summary

L1b of the daemon single-entrypoint stack, on top of #3616: the CLI process reaches no module of @agent-device/daemon, and R78 now proves it by reachability instead of a folder ban.

  • Modules moved out of the daemon (git moves; tests travel with them where the test fixtures allow):
    • daemon-contracts: the device-claim cluster (record, store, inspection, reboot, settlement, claims), session-artifact-paths (with the managed-path check), daemon-stop, and the claim reconciler + owner-scoped composer + recovery platform scope.
    • capture-kit: the app-log durable resource, its store and recovery, and the app-log file policy (they sit with the other durable captures).
    • kernel/device: decodeDeviceIdentity (was in capture-kit), so daemon-contracts does not depend on capture-kit for claim records.
    • DaemonCommandRoute is read from command-registry.
  • device release --stale composes from root platform runtimes (src/platform-runtime-claim-recovery.ts) and reaches no daemon module. src/cli/commands/device-release.ts is gone.
  • R78 (daemon-client-entry) keeps the folder ban over src/cli.ts, src/cli/, src/client/, src/remote/, src/daemon-client/ and adds a breadth-first walk over static and dynamic edges from src/bin.ts/src/cli.ts. The test plants the exact laundering chain cli.ts → cli/commands/device.ts → platform-runtime-stale-claim-release.ts → daemon, a type-only hop that must not count, and a stale-entry case. On the first run it also found platform-runtime-app-log-output.ts/-process.ts importing app-log-files and managed-session-artifact-path from the daemon; both moved too.
  • ADR 0032/0033 stop describing R78 as recording edges, and the AGENTS.md session-artifact-paths pointer follows the move.

Subpath exports

Package Before After
daemon 118 107
daemon-contracts 12 17 (device-claims, device-claims-fixtures, device-claim-recovery, daemon-stop, session-artifact-paths)
capture-kit 80 83 (app-log-durable-resource, app-log-resource-recovery, app-log-files)
kernel, host-kit, test-kit 22 / 20 / 18 unchanged

Net: 210 → 207. The device-claim cluster is one explicit-name entry (device-claims) instead of seven files exported separately. Two judgment calls to review:

  • daemon-stop keeps its own subpath. It lazily imports daemon-registration-owner, and its test mocks that seam, so merging it into the owner module (or re-exporting it from one) would either make a static cycle or break the test.
  • The reconciler lives in daemon-contracts, which depends on capture-kit. The reverse (capture-kit → daemon-contracts) is a TypeScript project-reference cycle through command-registry → selectors → capture-kit, and there is no package above both. The dependency stays acyclic; if the layering intent is "contracts below capture", the reconciler needs a new package, which I did not create.

Eager closure

scripts/__tests__/eager-closure-budgets.test.ts passes with no growth on any existing entry. Three new entries carry APPROVED_OVER_CEILING rows (app-log-durable-resource.ts, device-claim-recovery.ts, device-claims.fixtures.ts): each is code moved verbatim from a daemon entry of the same weight, and only the daemon and the lazily loaded device release --stale path import them. The first draft widened capture-kit's root barrel (32 → 63 modules) and daemon-resolution; both were reverted to separate entries for that reason. Four tests that need test-kit stay under packages/daemon (test-kit depends on daemon-contracts, and capture-kit/daemon-contracts tests cannot import it): the claim, settlement, reconciliation and app-log-files tests exercise the moved modules through their public entries.

Diff size

118 files, +744 −582 over #3616: over the 1,000-line budget. Most of it is moves counted twice (rename detection does not pair the merged or split files: the kernel decode move is +85/−90, the claim reconciler, owner recovery and scope moves another ~300) and import rewrites across the daemon tests. If you want this under budget I would split the kernel/capture-kit moves (decodeDeviceIdentity, app-log durable resource and file policy) into a lower layer.

Validation

pnpm check:affected --run passes on 6b960298f (L1b) and on 7d007f5cf (#3616), both rebased on origin/main 542fb77; pnpm check:daemon-wire-compat passes on both. Earlier L1b attempts failed on the eager-closure budget, production-exports and fallow (all fixed above) and twice on tmpdir-leaks-model while two other threads' gates ran on this host; that check passes alone twice and in the final runs. A fresh-context review of the first L1b draft found only stale path references (fixed). The R78 reachability walk changed the design after that review, so the final shape has not been reviewed cold.

No device-facing behavior changes; device release --stale runs the same reconciler code, composed from the same root gateway.

🤖 Generated with Claude Code

thymikee and others added 2 commits October 14, 2026 20:15
…ckage

The device-claim record, store, inspection and settlement, the session
artifact paths and the stop path now live in daemon-contracts, which the CLI
and the daemon both read. The claim reconciler, its owner-scoped composer and
the recovery platform scope sit beside them; the app-log durable resource,
its recovery and the app-log file policy sit in capture-kit with the other
durable captures. The device identity decode moves to kernel, so no contracts
module needs capture-kit for it. `device release --stale` and the platform
runtimes it composes reach no daemon module, and `DaemonCommandRoute` is read
from command-registry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
R78 now walks static and dynamic edges from src/bin.ts and src/cli.ts and
fails on any module they reach that imports the daemon package, naming the
chain, on top of the folder ban over src/cli, src/client, src/remote and
src/daemon-client. R10 leaves those importers to R78. The two moved entries
that carry the durable-capture machinery are approved over the new-entry
ceiling, and ADR 0032/0033 stop describing R78 as recording edges.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee
thymikee added this pull request to stack #3626 October 14, 2026 18:27
@thymikee thymikee changed the title refactor/daemon client deps to contracts refactor(daemon): move CLI-shared daemon modules out of the daemon package and gate CLI reachability (R78) Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-3625/

Built to branch gh-pages at 2026-10-14 18:29 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@cubic-dev-ai cubic-dev-ai 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.

7 issues found across 118 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/daemon-contracts/src/device-claim-recovery.ts">

<violation number="1" location="packages/daemon-contracts/src/device-claim-recovery.ts:4">
P3: This façade re-exports `OwnerScopedClaimRecoveryComposer` but omits the source module's exported `OwnerScopedClaimRecovery` return type, so package consumers cannot name that contract. Re-export it from this module.

(Based on your team's feedback about complete façade exports.) .</violation>
</file>

<file name="docs/adr/0033-collocation-decision-table.md">

<violation number="1" location="docs/adr/0033-collocation-decision-table.md:136">
P2: R78 does not ban every type-only edge in the CLI's transitive graph: a reached root module may import daemon types only. Distinguish the client-side direct-import ban from the runtime CLI-reach ban.</violation>
</file>

<file name="scripts/layering/daemon-client-entry.test.ts">

<violation number="1" location="scripts/layering/daemon-client-entry.test.ts:40">
P3: The protected-prefix cases no longer exercise direct `import()` edges; the dynamic imports in the root-chain test only test traversal. Add a dynamic import variant so regressions in direct client-side import checks are caught.</violation>
</file>

<file name="packages/daemon-contracts/src/recovery-platform-scope.ts">

<violation number="1" location="packages/daemon-contracts/src/recovery-platform-scope.ts:14">
P2: This sink silently drops shutdown recovery diagnostics when no diagnostics scope is active. `teardownDaemonSessions()` runs outside `withDaemonDiagnosticsScope`, so lifecycle binding-cleanup failures are lost; wrap that shutdown work in a daemon diagnostics scope with `logPath`.</violation>
</file>

<file name="packages/daemon-contracts/src/session-artifact-paths.ts">

<violation number="1" location="packages/daemon-contracts/src/session-artifact-paths.ts:92">
P1: The realpath check is skipped when any descendant of the parent is missing, so `sessions/link/new/app.log` passes lexically even if `link` points outside the sessions root. `openAppLogOutput` then creates the parent through that symlink and writes outside the root; validate the nearest existing ancestor and resolve the missing suffix before returning.</violation>
</file>

<file name="scripts/layering/daemon-client-entry.ts">

<violation number="1" location="scripts/layering/daemon-client-entry.ts:83">
P2: The walk consumes only `parseImports` edges, so literal `require()`/`createRequire()` calls never enter the graph and can load daemon internals without a violation. Extend the shared edge model to resolve literal runtime requires and add a reach-check case.

(Based on your team's feedback about shared import-edge coverage.)</violation>
</file>

<file name="docs/adr/0032-layering-graph-engine.md">

<violation number="1" location="docs/adr/0032-layering-graph-engine.md:88">
P3: This ADR sentence mischaracterizes R78 as a flat ban, while the shipped rule performs a BFS over import edges and reports reachability chains. Describe R78 as a reachability ban rather than saying it no longer records edges.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

if (path.basename(resolved) !== input.basename) {
throw new Error(`${input.label} is outside the daemon-owned sessions directory`);
}
if (fs.existsSync(root) && fs.existsSync(path.dirname(resolved))) {

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.

P1: The realpath check is skipped when any descendant of the parent is missing, so sessions/link/new/app.log passes lexically even if link points outside the sessions root. openAppLogOutput then creates the parent through that symlink and writes outside the root; validate the nearest existing ancestor and resolve the missing suffix before returning.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/daemon-contracts/src/session-artifact-paths.ts, line 92:

<comment>The realpath check is skipped when any descendant of the parent is missing, so `sessions/link/new/app.log` passes lexically even if `link` points outside the sessions root. `openAppLogOutput` then creates the parent through that symlink and writes outside the root; validate the nearest existing ancestor and resolve the missing suffix before returning.</comment>

<file context>
@@ -74,3 +75,30 @@ export function resolveSessionAppLogPath(sessionsDir: string, address: string):
+  if (path.basename(resolved) !== input.basename) {
+    throw new Error(`${input.label} is outside the daemon-owned sessions directory`);
+  }
+  if (fs.existsSync(root) && fs.existsSync(path.dirname(resolved))) {
+    const realRoot = fs.realpathSync.native(root);
+    const realParent = fs.realpathSync.native(path.dirname(resolved));
</file context>

Comment on lines +136 to +137
importing `packages/daemon/src/` at all; R78 bans every edge from
the CLI, typed client, remote transports and `src/daemon-client/` into `packages/daemon/src/`, and

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.

P2: R78 does not ban every type-only edge in the CLI's transitive graph: a reached root module may import daemon types only. Distinguish the client-side direct-import ban from the runtime CLI-reach ban.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/adr/0033-collocation-decision-table.md, line 136:

<comment>R78 does not ban every type-only edge in the CLI's transitive graph: a reached root module may import daemon types only. Distinguish the client-side direct-import ban from the runtime CLI-reach ban.</comment>

<file context>
@@ -133,9 +133,9 @@ the whole `daemon-contracts` zone has moved into `packages/daemon-contracts` —
-importing `packages/daemon/src/` at all; R78 keeps any client→daemon
-edge at zero runtime and a five-line recorded type-only residue, and #3288's `daemon-contracts`
-zone was the declaration of that shared-below-both ownership — the extraction makes it
+importing `packages/daemon/src/` at all; R78 bans every edge from
+the CLI, typed client, remote transports and `src/daemon-client/` into `packages/daemon/src/`, and
+#3288's `daemon-contracts` zone was the declaration of that shared-below-both ownership — the extraction makes it
</file context>
Suggested change
importing `packages/daemon/src/` at all; R78 bans every edge from
the CLI, typed client, remote transports and `src/daemon-client/` into `packages/daemon/src/`, and
importing `packages/daemon/src/` at all; R78 bans direct imports from the CLI, typed client, remote transports and
`src/daemon-client/` (including type-only imports), plus runtime daemon imports reachable from CLI entries.

return Object.freeze({
signal: processLifetimeSignal,
diagnostics: Object.freeze({
emit: (event: PlatformDiagnosticEvent) => emitDiagnostic(event),

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.

P2: This sink silently drops shutdown recovery diagnostics when no diagnostics scope is active. teardownDaemonSessions() runs outside withDaemonDiagnosticsScope, so lifecycle binding-cleanup failures are lost; wrap that shutdown work in a daemon diagnostics scope with logPath.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/daemon-contracts/src/recovery-platform-scope.ts, line 14:

<comment>This sink silently drops shutdown recovery diagnostics when no diagnostics scope is active. `teardownDaemonSessions()` runs outside `withDaemonDiagnosticsScope`, so lifecycle binding-cleanup failures are lost; wrap that shutdown work in a daemon diagnostics scope with `logPath`.</comment>

<file context>
@@ -0,0 +1,20 @@
+  return Object.freeze({
+    signal: processLifetimeSignal,
+    diagnostics: Object.freeze({
+      emit: (event: PlatformDiagnosticEvent) => emitDiagnostic(event),
+    }),
+    progress: Object.freeze({
</file context>

edges: readonly ResolvedImportEdge[],
): LayeringViolation[] {
const outgoing = new Map<string, ResolvedImportEdge[]>();
for (const edge of edges) {

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.

P2: The walk consumes only parseImports edges, so literal require()/createRequire() calls never enter the graph and can load daemon internals without a violation. Extend the shared edge model to resolve literal runtime requires and add a reach-check case.

(Based on your team's feedback about shared import-edge coverage.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/layering/daemon-client-entry.ts, line 83:

<comment>The walk consumes only `parseImports` edges, so literal `require()`/`createRequire()` calls never enter the graph and can load daemon internals without a violation. Extend the shared edge model to resolve literal runtime requires and add a reach-check case.

(Based on your team's feedback about shared import-edge coverage.) </comment>

<file context>
@@ -29,11 +66,53 @@ export function checkDaemonClientEntry(edges: readonly ResolvedImportEdge[]): La
+  edges: readonly ResolvedImportEdge[],
+): LayeringViolation[] {
+  const outgoing = new Map<string, ResolvedImportEdge[]>();
+  for (const edge of edges) {
+    if (edge.typeOnly && !edge.dynamic) continue;
+    if (edge.file.startsWith(DAEMON_SOURCE_ROOT)) continue;
</file context>

export type { ClaimRecoveryGatewayFactory } from './claim-recovery-gateway.ts';
export {
createOwnerScopedDeviceClaimReconciler,
type OwnerScopedClaimRecoveryComposer,

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.

P3: This façade re-exports OwnerScopedClaimRecoveryComposer but omits the source module's exported OwnerScopedClaimRecovery return type, so package consumers cannot name that contract. Re-export it from this module.

(Based on your team's feedback about complete façade exports.) .

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/daemon-contracts/src/device-claim-recovery.ts, line 4:

<comment>This façade re-exports `OwnerScopedClaimRecoveryComposer` but omits the source module's exported `OwnerScopedClaimRecovery` return type, so package consumers cannot name that contract. Re-export it from this module.

(Based on your team's feedback about complete façade exports.) .</comment>

<file context>
@@ -0,0 +1,6 @@
+export type { ClaimRecoveryGatewayFactory } from './claim-recovery-gateway.ts';
+export {
+  createOwnerScopedDeviceClaimReconciler,
+  type OwnerScopedClaimRecoveryComposer,
+} from './device-claim-owner-recovery.ts';
+export { createRecoveryPlatformScope } from './recovery-platform-scope.ts';
</file context>
Suggested change
type OwnerScopedClaimRecoveryComposer,
type OwnerScopedClaimRecoveryComposer,
type OwnerScopedClaimRecovery,

]) {
test(`${file} importing a daemon module is red, type-only imports included`, () => {
const specifier = relative(file);
for (const source of [`import '${specifier}';`, `import type { scope } from '${specifier}';`]) {

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.

P3: The protected-prefix cases no longer exercise direct import() edges; the dynamic imports in the root-chain test only test traversal. Add a dynamic import variant so regressions in direct client-side import checks are caught.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/layering/daemon-client-entry.test.ts, line 40:

<comment>The protected-prefix cases no longer exercise direct `import()` edges; the dynamic imports in the root-chain test only test traversal. Add a dynamic import variant so regressions in direct client-side import checks are caught.</comment>

<file context>
@@ -1,61 +1,85 @@
+]) {
+  test(`${file} importing a daemon module is red, type-only imports included`, () => {
+    const specifier = relative(file);
+    for (const source of [`import '${specifier}';`, `import type { scope } from '${specifier}';`]) {
+      const violations = check({ [file]: `${source}\n` }).filter((v) => v.file === file);
+      assert.equal(violations.length, 1, source);
</file context>
Suggested change
for (const source of [`import '${specifier}';`, `import type { scope } from '${specifier}';`]) {
for (const source of [`import '${specifier}';`, `import type { scope } from '${specifier}';`, `void import('${specifier}');`]) {

| R14 non-TS file under `src/utils` | flag | ✓ | ✗ | ✗ |
| R71 file under `src/replay` | flag | ✓ | ✓ | ✓ generic |

_R78 and R10 no longer record edges: both are flat bans, so the "unrecorded" and "stale" rows describe the spike at its date, not the rules as shipped._

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.

P3: This ADR sentence mischaracterizes R78 as a flat ban, while the shipped rule performs a BFS over import edges and reports reachability chains. Describe R78 as a reachability ban rather than saying it no longer records edges.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/adr/0032-layering-graph-engine.md, line 88:

<comment>This ADR sentence mischaracterizes R78 as a flat ban, while the shipped rule performs a BFS over import edges and reports reachability chains. Describe R78 as a reachability ban rather than saying it no longer records edges.</comment>

<file context>
@@ -85,6 +85,8 @@ finding. Clean tree: custom 0, depcruise 10 (3 R6 survivors, 7 recorded R78 edge
 | R14 non-TS file under `src/utils` | flag | ✓ | ✗ | ✗ |
 | R71 file under `src/replay` | flag | ✓ | ✓ | ✓ generic |
 
+_R78 and R10 no longer record edges: both are flat bans, so the "unrecorded" and "stale" rows describe the spike at its date, not the rules as shipped._
+
 Custom prints `file:line`, rule id and hint (`report()` in `scripts/layering/check.ts`); depcruise
</file context>
Suggested change
_R78 and R10 no longer record edges: both are flat bans, so the "unrecorded" and "stale" rows describe the spike at its date, not the rules as shipped._
_R10 remains a direct ban, while R78 also walks CLI reachability over static and dynamic edges; the "unrecorded" and "stale" rows describe the spike at its date, not the rules as shipped._

@github-actions

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.25 MB 5.25 MB -365 B
Package (unpacked) 5.25 MB 5.24 MB -365 B
Package (download) 1.58 MB 1.58 MB -150 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 29.4 ms 29.5 ms +0.1 ms
CLI --help 86.4 ms 81.8 ms -4.6 ms

@thymikee

Copy link
Copy Markdown
Member Author

The reach walk in R78 duplicates R82's walk, so this needs one change before merge. The other code looks fine at 6b96029, and CI is green (43 checks, 0 failing, including check:layering and the eager-closure budget test). I did not run check:layering or typecheck locally, so "the CLI reaches no daemon module" rests on that CI result, and I did not recount the subpath exports or check removed subpaths for leftover importers beyond what typecheck proves.

reachedDaemonImports in daemon-client-entry.ts copies the walk in daemon-process-reach.ts almost line for line. It has the same stale-entry refusal, the same outgoing map with the edge.typeOnly && !edge.dynamic filter, the same cameFrom BFS and the same chain rebuild. R78 differs only in its denied set (a prefix, not named modules) and in stopping at daemon edges. That makes two copies of one rule, so a fix to edge semantics has to land twice and the two can drift. Please extract one walker in scripts/layering, for example reachWithChains(entries, edges, { stopAt }), that returns the cameFrom map, a chain renderer and the stale-entry check. Then R82 and the R78 reach half become two declarations on it. Every "process entry must not load module set" rule should go through that one walker, and check:layering should stay green with the existing R78 and R82 tests unchanged.

Should daemon-contracts own the device-claim reconciler at all? It now depends on capture-kit at runtime because the reconciler drives app-log, audio-probe and perf-capture recovery, and it holds a process-lifetime PlatformRequestScope. Those are not on-disk or wire contracts. Would it be smaller to keep daemon-contracts to the claim record, store, inspection, paths, reboot and settlement, plus session-artifact-paths and daemon-stop, and let the root own the reconciler beside createClaimRecoveryGateway in src/platform-runtime-claim-recovery.ts? The daemon dep would change from claimRecoveryGateway: ClaimRecoveryGatewayFactory to a root-composed composeClaimReconciler(scope): DeviceClaimReconciler, which the three daemon sites call. That drops the daemon-contracts to capture-kit edge, the device-claim-recovery subpath and its APPROVED_OVER_CEILING row. Daemon tests that build reconcilers through device-claims-fixtures would need a test-kit fixture or stay root-owned, and a one-line ADR 0033 row should say whether runtime recovery may live in daemon-contracts. Production size is about +110 and behavior is unchanged, so this is an ownership question: if you considered this split and rejected it, a short reason is enough.

On the open review threads, the realpath skip for a missing dirname (P1) and emitDiagnostic returning early without a scope (P2) both exist on the base commit and are moved verbatim here. They are fine as separate fixes. Two lower-priority threads still apply: the missing dynamic-import case in the folder-ban tests, and the ADR 0032 "flat ban" wording. These do not apply, so please resolve them: ADR 0033 and type-only edges (the ADR describes the folder ban, which does cover type-only edges), require()/createRequire (no production use exists, and this is a limit of the shared import parser, not R78), and OwnerScopedClaimRecovery missing from the facade (no consumer names it).

This branch has not been deployed

No deployments
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