Skip to content

feat: Implement the module model: rename, mitosis, scoped buses, panels, Studio face - #255

Draft
Yona-Appletree wants to merge 27 commits into
mainfrom
claude/yona-implement-modules-roadmap-4cdecb
Draft

feat: Implement the module model: rename, mitosis, scoped buses, panels, Studio face#255
Yona-Appletree wants to merge 27 commits into
mainfrom
claude/yona-implement-modules-roadmap-4cdecb

Conversation

@Yona-Appletree

@Yona-Appletree Yona-Appletree commented Aug 1, 2026

Copy link
Copy Markdown
Member

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Path: ~/.photomancer/planning/lp2025/2026-08-01-1003-modules-impl-roadmap/plan.md

Implements the ratified modules/buses/panels design (docs/design/modules.md, panel.md, PR #230). This PR carries the P1–P6 stretch, ending at gate G3 (behavior review).

Complete:

  • P1 — project→module rename: kinds, types, 159 tree-path literals, corpora, schemas, WIRE_PROTO 4→5
  • P2 — mitosis: project.json container manifest (streaming probe, hard refuse per D-A), module.json root, format 3 (v2 snapshotted), split schemas + conformance routing, Studio manifest re-point
  • P3 — ModuleDef capability: bindings, exports, per-channel meta, provenance (Q7 settled)
  • P4 — structural scope on RuntimeNodeEntry, scope query API, dead Bus deleted
  • P5 — scoped resolver keys + writer-shadowing, suppression rule deleted, sink no-demand by construction
  • P6 — real ModuleNode (root included), R7 publish/exports, engine-reported primary-visual role

ADRs: 2026-08-01-project-module-mitosis-container-format-gate, 2026-08-01-scoped-bus-engine-architecture (supersede the #218 branch ADRs).

At gate G3 (behavior) — remaining phases P7–P13 continue after the gate. Do not merge PR #248 or the #218 branch — harvest sources only.

Resume notes: read plan.md + phase files in the planning dir; discovery in ../2026-07-31-1002-modules-buses-panels/notes.md.

🤖 Generated with Claude Code

Yona-Appletree and others added 18 commits August 1, 2026 13:08
… wire bump)

Rust surface of the P1 rename: ProjectDef -> ModuleDef, NodeKind::Project
-> NodeKind::Module, NodeDef::Project -> NodeDef::Module, PROJECT_VARIANT
-> MODULE_VARIANT ("Project" -> "Module"), ModuleDef::KIND "project" ->
"module", format probe renamed to read_module_format_json. nodes/project/
moved to nodes/module/ (SHAPE_ID regen lands in a later commit).

WIRE_PROTO_VERSION 4 -> 5: NodeKind serializes its variant name in
inventory frames and TreePath segments change from .project to .module.

The word "project" survives for container concepts (project.json, Studio
open-a-project flows, PROJECT_FORMAT_VERSION) per docs/design/modules.md.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tree-path kind segment comes from kind_name(), so the P1 KIND change
silently re-addresses every node path. This commit is ONLY the path
string sweep (/demo.project/orbit.shader -> /demo.module/orbit.shader)
so review can eyeball path strings in isolation.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rust test fixtures plus the artifact corpora (15 examples/, projects/test,
fw-browser smoke-project) and docs/design/source-artifacts.md. The
schemas/history/v1 snapshot stays frozen (it documents the v1 format) and
the 2026-07-27 defect entry keeps its historical quote.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
just schema-gen: project.schema.json now pins kind Module, ModuleDef
shape id moves (module_path! is part of the hash), NodeArtifact $ref and
the shape dump filename follow. Stragglers: schema generator variant
name, Studio node-kind icon lookup + NodeKindIcon::Module, debug-UI kind
labels, package_manifest default kind, node_kind_slug/label, justfile
format-bump path, schemas/README, modules.md section-2 done note.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y the sweep

format!-embedded \"kind\": \"Project\" fixtures (engine test helper,
server/browser/opfs/history tests), UiPackageCard/UiExampleCard story
kind fields, embedded example kind, node-authoring error copy. Pane
title chips keep "Project" (container concept).

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… relocation (P2)

project.json becomes the non-node container manifest (format/uid/name),
read by a streaming JsonSyntaxSource probe and written by a hand-rolled
deterministic writer — deliberately NOT a Slotted type, so no second
shape/codec surface links into firmware. The root module node moves to
/module.json; ModuleDef drops format/uid/name.

PROJECT_FORMAT_VERSION 2 -> 3 (bump-and-refuse; v2 schemas snapshotted
to schemas/history/v2). The load gate now reads the container: missing
or malformed project.json is a HARD refuse per settled D-A, and a
pre-mitosis kind-tagged root fails with a diagnosable manifest error.

Corpora (15 examples, projects/test, smoke-project) split into the two
files; starter project emits 9 files; registry/engine/shared fixtures
migrated; new gate tests incl. missing-manifest and pre-mitosis roots.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…split

lpa-server, lpa-link fake device, lpa-fs-opfs, lpc-wire fixtures move to
container manifest + module.json (delegated sweep); studio-core library,
demo project, e2e harnesses, device-session and node-CRUD fixtures follow.
Blank Created packages now also write a minimal module.json so they stay
loadable — gated to blank creates so device pulls stay byte-faithful and
adoption parity hashes cannot diverge. fw-browser uid patcher re-anchored
on the manifest form (was silently broken by the split), smoke.html
fetches module.json, package_manifest rewritten on ProjectManifest.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…P2 tests + ADR

Settings popup renders format/uid/name read-only from the container
manifest (UiProjectManifest on ProjectEditorView, sourced from the active
library package); gallery rename now patches the manifest name as well as
the slug. New tests: device-path wrong-format + missing-manifest refusal
on the emulated firmware (rides the booted scene_render_emu), vendored
module folder loads under the host project's gate (Q10 pinned), and a
corpus byte-identity rewrite check for both split files. modules.md
section-6/Q10 notes, schemas README, and the mitosis ADR.

ADR: docs/adr/2026-08-01-project-module-mitosis-container-format-gate.md

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…provenance (P3)

ModuleDef gains the authored surface the ratified model requires:
bindings (R7's contention pick becomes authorable), exports (local slot
-> inner-scope channel, bus: ref form; runtime interpretation lands with
the module runtime), per-channel meta overrides (Q1 curation escape
hatch, the spike's display vocabulary minus aliasing), and provenance.
ProvenanceDef (author/version/license/created, settled Q7) is a
capability of any node def; the container manifest carries the same four
keys. Schemas regenerated; byte-stable round-trip pinned by test.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egistry (P4)

Scope identity becomes a queryable engine fact (modules.md R1/R2):
ScopeRef {Module, Sink} stored on every runtime node entry, assigned by
ensure_runtime_spine on BOTH load and apply so an edited project can
never wear different scopes than a reloaded one; present on Pending/
Failed entries and untouched by payload reattach. Query API on the tree:
scope_of, scope_introduced_by, scopes, scope_channels (R3/R4 listing
grouping), scope_persist_path — sink scopes key by the authored playlist
entry (…/entries[k]) so panel state will follow the slot, not the
content, and stay stable under sibling reorder and reattach.

The dead runtime Bus/ChannelEntry value registry is deleted (its only
construction was an empty throwaway for DestroyCtx); Destroy/MemPressure
contexts slim to node+frame. No resolution behavior change — pinned by
the load-vs-apply differential and the untouched engine suite.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion rule deleted (P5)

QueryKey::Bus becomes {scope, channel}: bus demand carries the READING
node's scope into the cache and cycle-detection identity, so same-named
channels in different scopes can never collide or fake a cycle. The
resolver stays scope-dumb — the host answers 'which providers win for a
read from this scope' via NodeTree::providers_for_bus_read, a pure
outward walk (R5) that never descends into a scope, which is what makes
sink-scope no-demand hold by construction (R2) instead of via the
spike's probe-side filter. Provider re-reads key off the provider's own
scope; unscoped public reads resolve from the root scope.

The playlist ownership-suppression rule is deleted: entry children now
default-publish visual.out like every producer — into their entry's sink
scope, invisible to enclosing reads by construction (tests flipped to
pin the new shape, incl. probe-with-values never ticking a sink-scope
producer). Three correctness obligations pinned: adversarial two-scope
cache soundness, depth-2 writer-shadowing/inheritance (E5 minus the P6
mirror), and the load-vs-apply wiring differential.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed (P6)

Every module node — root included — now wears a live ModuleNode runtime
(harvested from the spike's ProjectNode mirror): produce resolves the
module's own introduced scope's visual.out through the scoped resolver
key and forwards render/sample dispatch to the producing node; a scope
with no visual writer renders cleared (a module without a visual is a
legitimate shape). Never feature-gated — the runtime links into every
firmware image (C6 headroom after: 243,952 B).

register_node_bindings' module early-return is gone: modules register
authored bindings (the R7 contention pick), authored exports (inner
channel republished under the export name — module-owned bus reads
resolve from the INTRODUCED scope via the host reading-scope rule), and
the automatic output→visual.out fallback publish for non-root modules
(the drop-in rule). The apply-path root reattach installs the same
runtime, so an applied project equals a reloaded one.

Primary visual becomes an engine-reported role: WireBusChannel gains
primary_visual, decided once in the probe (root-scope listing of the
vocabulary channel); Studio's channel.name == "visual.out" string
tests are gone. E5 depth-2 is pinned: a consumer beside an embedded
module resolves the sibling's publish, never root's.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supersedes the #218 branch ADRs (2026-07-28-scoped-buses,
2026-07-28-effects-are-projects) with pointers.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ommit branch switch

The dx build rewrites lp-app/lpa-studio-web/assets/tailwind.css in the
CI working tree; the dirty tracked file aborted 'git checkout -B' in the
auto-commit step, so genuine story drift could never self-commit.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…::Project refs

Main's new feature-gate tables (lpc-model/src/feature.rs,
lpc-engine/src/features.rs) referenced the pre-rename kind; renamed to
NodeKind::Module (module stays ungated — None feature mapping).

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

CI refreshed the story baselines on this branch

The validate-stories job detected drift and committed the fresh set: 22 changed
in 9508e6f.

Review every PNG in the PR's Files changed view (swipe / onion-skin).
Your local branch is now behind — git pull before pushing again.

Story Before After
studio__project__project-pane__change-list-empty__lg.png studio__project__project-pane__change-list-empty__lg.png@cad5a7dd23 studio__project__project-pane__change-list-empty__lg.png@9508e6fa02
studio__project__project-pane__change-list-empty__md.png studio__project__project-pane__change-list-empty__md.png@cad5a7dd23 studio__project__project-pane__change-list-empty__md.png@9508e6fa02
studio__project__project-pane__change-list-empty__sm.png studio__project__project-pane__change-list-empty__sm.png@cad5a7dd23 studio__project__project-pane__change-list-empty__sm.png@9508e6fa02
studio__project__project-pane__change-list-overflow__lg.png studio__project__project-pane__change-list-overflow__lg.png@cad5a7dd23 studio__project__project-pane__change-list-overflow__lg.png@9508e6fa02
studio__project__project-pane__change-list-overflow__md.png studio__project__project-pane__change-list-overflow__md.png@cad5a7dd23 studio__project__project-pane__change-list-overflow__md.png@9508e6fa02
studio__project__project-pane__change-list-overflow__sm.png studio__project__project-pane__change-list-overflow__sm.png@cad5a7dd23 studio__project__project-pane__change-list-overflow__sm.png@9508e6fa02
studio__project__project-pane__change-list__lg.png studio__project__project-pane__change-list__lg.png@cad5a7dd23 studio__project__project-pane__change-list__lg.png@9508e6fa02
studio__project__project-pane__change-list__md.png studio__project__project-pane__change-list__md.png@cad5a7dd23 studio__project__project-pane__change-list__md.png@9508e6fa02
14 more file(s)
  • studio__project__project-pane__change-list__sm.png (changed)
  • studio__project__project-pane__debug-absent-from-save-panel__lg.png (changed)
  • studio__project__project-pane__debug-absent-from-save-panel__md.png (changed)
  • studio__project__project-pane__debug-absent-from-save-panel__sm.png (changed)
  • studio__project__project-pane__debug-active-chip__sm.png (changed)
  • studio__project__project-pane__debug-active-with-unsaved__sm.png (changed)
  • studio__project__project-pane__detail-popup__lg.png (changed)
  • studio__project__project-pane__detail-popup__md.png (changed)
  • studio__project__project-pane__detail-popup__sm.png (changed)
  • studio__project__project-pane__staged-node-removal__lg.png (changed)
  • studio__project__project-pane__staged-node-removal__md.png (changed)
  • studio__project__project-pane__staged-node-removal__sm.png (changed)
  • studio__project__project-pane__unsupported-node-in-tree__sm.png (changed)
  • studio__project__project-workspace__sidebar-dirty-tree__sm.png (changed)

Yona-Appletree and others added 9 commits August 2, 2026 10:05
WireScopeRef {Module, Sink} lands on the wire: WireBusChannel carries the
scope a channel lists in (channels enumerate PER SCOPE — same-named
channels in different scopes are distinct rows, each resolving its own
value) and WireBindingEndpoint::Bus carries the endpoint's scope (write
lands / read starts). Sink scopes never list — the modeled R2 property
honored by enumeration, no row filter. Display strings are gone from the
engine: Studio derives scope labels from its own node tree
(UiBusChannelView keys by (scope, name); the bus pane prefixes embedded
scopes minimally until the module face lands). WIRE_PROTO 5 -> 6.

NodeTree grows the listing counterparts of the shadowing walk
(scope_channels split by endpoint direction, providers/consumers-in-
scope, bus_read_scope shared with the resolve host). Probe tests verified
running (no duplicated-attr hazard); new test pins two same-named scoped
rows arriving structured with per-scope values.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PanelWriterStore on the Engine, keyed (scope, channel) with VecMap
(firmware-safe): writers materialize on first touch, latch until an
explicit clear, and live OUTSIDE the binding index — apply_project_changes
rebuilds bindings from defs and must never destroy an engaged control
(pinned by test). The resolve-host walk overlays the store: an engaged
scope counts as having a writer for shadowing, and the engaged entry
REPLACES the scope's provider set at BindingPriority::panel() — which is
what makes max-priority-wins hold on ByKey merge channels too (the
settled decision), and what turns two ambiguous authored writers into a
resolvable channel while engaged. All 8 EngineResolveHost sites route
the store, including the probe's: engaged values resolve in probe reads
and surface as WireBindingOrigin::Panel provider rows for the UI.

Engage/shadow/inherit/clear, replace-not-merge, probe visibility, and
apply-survival are all pinned by tests. panel.md P4 implemented note.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ontrols

Panel controls stop editing authored defaults and start writing the
channel they present. A control whose backing slot consumes a bus
channel now dispatches PanelWrite at that (scope, channel) down the
runtime command channel — no overlay entry, no dirty flag, no Save
panel row — on the playlist-activate precedent. A control with no
channel behind it keeps the slot-edit path, so nothing that worked
before stops working.

- wire: WireProjectCommand::PanelWrite/PanelClear as PROJECT-level arms
  (they address a scope, not a node); three-way clear (channel / scope /
  all); WIRE_PROTO 6.
- server: Project::panel_write/panel_clear; a gesture naming a scope
  whose owner is gone is a normal Rejected, never a transport error.
- studio: PanelWriteOp/PanelClearOp with verdict-chase tightening,
  coalesced per (scope, channel) in the actor's batch planner beside the
  slot-edit flood rule — a clear is a barrier, so write/clear/write
  reaches the server in order.
- UiPanelTarget rides the probe's consuming Bus endpoint onto the
  control; knob, fader, and toggle dispatch through it, and an engaged
  control gets a clear affordance.
- momentary (P14): the TTL is an engine-time deadline and the writer
  despawns in the tick, so a dropped client's gesture releases itself;
  renewal is just the next write.

panel.md: P8/P14 implemented notes; P-Q4 settled as leaned (clear-all
reaches sink scopes); P-Q1 settled minimally (no slew, seam recorded as
writer-side shaping).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The scarf test: dimmed from a phone at 4 a.m., unplugged, replugged —
back dim, with not one bright frame. That requirement is what fixes the
restore seam at Engine construction (Project::new, and Project::reload
because it rebuilds the Engine), before the first tick and therefore
before the first render, rather than at some later ready event. On
device that is the boot path.

- /.lp/panel.json in the framework tier, version 1, bump-and-refuse on
  anything else; lenient load (missing, corrupt, wrong version, unknown
  scope -> clean boot) and best-effort write.
- Keys are scope-path/channel from ScopeRef::persist_path, so state
  survives reload, reattach and sibling reorder; a sink scope keys by
  the ENTRY, so state follows the slot, not the content.
- Writes throttled ~10s and gated on a new PanelWriterStore mutation
  COUNTER: a clear plus a re-write inside one window leaves an
  identically-shaped map, which a (len, revision) comparison would miss.
  An idle project writes nothing. Unload flushes past the throttle.
- Momentary writers never persist (P14). Auto-save records its own
  choice, so "off" does not quietly re-enable overnight.

The prerequisite, done first: /.lp/** is filtered out of
refresh_artifacts before anything reads the batch. Without it every save
would rebuild the binding graph and the rebuild would schedule the next
save. applied_refresh_count makes that observable rather than assumed.

Also makes the fixture brightness fader derive a panel target like the
shader knobs do. It is inert today — see the defect below — but the
scarf's own control must not be the one that silently lacks the feature.

Findings recorded rather than absorbed:
- docs/debt/panel-state-serde-flash-cost.md — this costs 50,512 B of C6
  flash. ELF symbol diff says it is a SECOND LpValue JSON codec: the
  image already carries read_lp_value<JsonSyntaxSource> (13.6 KB) for
  artifacts, and the serde derive instantiated a whole new one against
  serde_json. Exit criteria names the cheap fix.
- docs/defects/2026-08-02-authored-source-bindings-silently-dropped.md —
  the loader registers authored `source` bindings only for hand-listed
  slot names per kind, and drops any others with no error. Fixture
  brightness therefore cannot be bound to a channel at all.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Main moved substantially since 2026-08-01 (firmware manifest, capability
reporting on hello, device backup, fixture power limits, the slot-role
taxonomy). Beyond textual conflicts, four decisions:

- WIRE PROTO renumbered. Main took 5 for the ServerHello reshape; this
  branch had claimed 5 (module rename) and 6 (scoped buses). Mine move
  up: rename = 6, scope/panel = 7, WIRE_PROTO_VERSION = 7.
- Slot taxonomy adopted: `policy = "read_only_persisted"` is now
  `role = "fixed"`, SlotPolicy -> SlotRole. ModuleDef.nodes carries
  Fixed; ModuleMirrorState drops its container-level default (every
  field already declares `produced`). Regenerated the one schema this
  changed.
- project_def.rs was modified on main and deleted here by the mitosis.
  Main's edits were exactly the policy->role migration, so the deletion
  stands and the migration moved to module_def.rs.
- Engine gains BOTH sides: panel_writers beside safe_output_clamp_q16.

Three format-migration gaps, all the same shape — a migration only
reaches the fixtures that exist when it runs, and main added these on
the other side of the fork:

- five projects/test/quad-*-v3 projects still carried a pre-mitosis
  single project.json; split into manifest + module.json.
- the device-backup e2e test seeded {"kind":"Project"} for its fake
  device, which the D-A manifest gate now refuses — the device went
  unidentified and the backup named itself "device". Migrated.
- three SlotPolicy references inside #[cfg(test)] modules, which
  `just check` never compiles (clippy-host has no --all-targets).

`just check` and `just test` green; the one failure in the last run was
the known load-sensitive lps-probe perf assert (12.0s under load, 8.4s
alone against a 10s bound), logged to its debt entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Main moved again mid-merge (PR #259). Four resolutions:

- SlotRole::State is now REQUIRED on produced fields, so
  ModuleMirrorState regains a container default_role = "state" — the
  same concept whose older spelling (default_policy =
  "read_only_transient") this branch had just removed as stale. The
  vocabulary changed twice; the declaration was always needed.
- Main strengthened the overlay-changed-at probe to load a real root and
  validate the edit. Post-mitosis `nodes` is Fixed and identity lives in
  the manifest, so the probe now stages `exports[glow]` — a genuinely
  writable authored map on ModuleDef — and seeds BOTH halves of the
  project (the container manifest gates the load; D-A refuses without).
- Story baselines are CI-canonical: took main's regenerated PNGs,
  including one it deleted.
- Main's new debug-clear tests address `/demo.project/...`; P1 renamed
  path segments to `.module`. Swept all five occurrences.

Every failure in this merge shares one shape: this branch renamed a
vocabulary, main kept authoring against the old one, and no CI could see
the combination. Four so far — NodeKind::Project, the project.json
container split, SlotPolicy, and `.project` path segments.

The gate is green except the known load-sensitive lps-probe perf assert,
whose verdict is deferred to CI: a sibling agent session had this
machine at load average 143, so flake and regression are locally
indistinguishable. Logged that limitation in its debt entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The firmware manifest is supposed to describe the image it is embedded
in, and every chip's checked-in expectation still claimed wireProto 5.
Renumbering the protocol to 7 (main took 5 for the ServerHello reshape,
so the module rename became 6 and scoped buses 7) made all four stale,
which is exactly what this check exists to catch: a protocol bump must
be declared, not slipped in.

CI caught it on all three firmware jobs. Verified locally per chip:
- esp32c6: image 3,003,104 B / 3,145,728 B, headroom 142,624 B; manifest
  check passes.
- esp32s3: image 1,926,336 B / 6,291,456 B, headroom 4,365,120 B;
  manifest check passes.

Nothing structural: the Xtensa firmware builds the panel work fine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sion

Both local failures (12.02s, 10.88s/11.56s) were machine load; CI's
dedicated runner passed the suite. Recorded so the next person reading
this entry knows the ambiguity was resolved, and what it cost to
resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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