Skip to content

feat: deliver portable Flect foundation - #30

Open
robinbraemer wants to merge 17 commits into
mainfrom
codex/flect-self-contained-shaper
Open

feat: deliver portable Flect foundation#30
robinbraemer wants to merge 17 commits into
mainfrom
codex/flect-self-contained-shaper

Conversation

@robinbraemer

@robinbraemer robinbraemer commented Aug 3, 2026

Copy link
Copy Markdown
Member

Intent

Establish Flect's canonical user-quality and proof model, make it mandatory agent context without duplicating its source of truth, baseline every user-important outcome against observable evidence, and deliver the implemented portable product foundation. The change must preserve Effect as the application and UI-shaping architecture; keep Guardian, accepted App Agent, Preview App Agent, and Shaper trust domains separate; provide browser-portable OPFS/Git, sandboxed Bash/Bun, capsule, extension, product-capability, sharing, import, authentication, recovery, accessibility, performance, AXI/CLI/MCP, and native-host behavior through shared typed controllers; keep external control opt-in and credentials private; document ownership without duplicate sources of truth; include the T3Code-inspired composer and high-quality Markdown/activity UX; ship truthful README/release media and product SDK/reference examples; and preserve deterministic safe-mode, acceptance, rollback, and offline recovery. Robin explicitly instructed the agent to continue unattended, not ask for approval during PR work, close completed issues, and deliver to main only after complete real-browser, native, documentation, and no-mistakes verification.

What Changed

  • Established the canonical product-quality, evidence, documentation-ownership, recovery, sharing, and local-control model, including the agent-facing Flect quality skill and verification records.
  • Delivered the portable Flect foundation across Effect-based workspace control, AXI/CLI/MCP, browser OPFS/Git, sandboxed shell, capsules, extensions, product capabilities and SDK examples, sharing/import, authentication, and native hosting.
  • Added the T3Code-inspired composer and Markdown/activity UX while hardening trust-domain separation, capability revocation, transactional acceptance/rollback, persistence failures, and deterministic safe-mode recovery.

Risk Assessment

⚠️ Medium: The latest recovery change is narrowly scoped and preserves local safe mode while surfacing marker or snapshot persistence failures, though the surrounding recovery workflow remains complex.

Testing

Build, targeted controller/broker tests, product-quality coverage, and the production Chromium Shape–Use journey were exercised; visual evidence was captured and generated worktree artifacts were removed.

  • Evidence: Shaper workbench (local file: /var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KZ3PK1KQ03602VMVTMWQH4XZ/flect-shaper-workbench-harness.png)
  • Evidence: Accepted App Agent (local file: /var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KZ3PK1KQ03602VMVTMWQH4XZ/flect-shaper-ready-harness.png)
  • Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (29m10s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 13 issues found → auto-fixed (8) ✅
  • 🚨 server/control-broker.ts:454 - Duplicate command IDs overwrite the first pending Deferred. Two concurrent submissions with the same ID leave one request waiting forever and can misroute completion; reject duplicates or share the existing waiter at this broker boundary.
  • 🚨 server/control-broker.ts:292 - Revocation appends a sentinel after already queued commands, so a blocked nextCommand can still receive and execute a queued command after control is disabled. Drain/reject queued commands at the broker revocation boundary.
  • ⚠️ server/control-broker.ts:320 - The broker state is published before the descriptor is persisted. If descriptor creation fails, status remains enabled with no discoverable bearer or descriptor. Persist first or roll back the in-memory grant on failure.
  • 🚨 src/lib/workspace-control-bridge.ts:132 - Non-disable commands are forked without tracking their fibers. Disabling control only fails pending waiters; an already running command continues mutating workspace state after revocation. Track and interrupt active command fibers at the shared revocation boundary.
  • ⚠️ src/lib/workspace-control-bridge.ts:138 - Reconcile failures are swallowed while the controller snapshot remains enabled. A failed enable/disable can leave the bridge permanently out of sync and with no later state change to retry. Preserve the failure and retry or explicitly restore the controller state.
  • 🚨 src/shell/persistent-workspace-fs.ts:240 - Role filesystem mutators update the in-memory mirror before OPFS and do not restore it when persistence fails; the replacement path also snapshots the new tree outside its rollback block. A failed write can appear successful in the current turn and disappear on reload. Use one transactional mutation helper for all mutators.
  • 🚨 src/capabilities/product-capability-registry.ts:137 - A product reservation is validated for scope but its current decision lifecycle is not rechecked before execute. Revocation or expiry between reserve/authorization and execution still reaches the product adapter, despite inspectReservation enforcing this invariant elsewhere.
  • ⚠️ src/capabilities/product-capability-broker.ts:196 - Decision-store load warnings are discarded at broker initialization and during contextual loading. Corrupt or unavailable durable grants therefore fail closed silently instead of surfacing the required recoverable warning in the protected interface.
  • 🚨 src/lib/runtime.ts:300 - The authoritative intent requires “provide ... extension ... behavior through shared typed controllers,” but the live capability adapter implements setText as a no-op. Portable extensions can pass capability checks and report successful intents while producing no interface change; wire this to the controller or return an explicit unsupported result.
  • 🚨 src/sharing/share-repository.ts:387 - acceptCandidate performs three guarded ref mutations without rollback. If the second move or final removal fails after the first move succeeds, the installation is left with inconsistent refs and later Keep/Reject operations can be permanently stale. Add an atomic/journaled repository acceptance boundary with compensation.
  • 🚨 src/lib/workspace-controller.ts:1641 - Keep promotes the extension catalog and finalizes share state before kernel.accept persists the canonical revision. A storage failure at kernel.accept leaves durable extension/share state accepted while the shaping proposal remains pending, violating candidate/accepted separation. Commit through one acceptance transaction or journal and compensate side effects on failure.
  • ⚠️ src/lib/workspace-controller.ts:790 - Capsule presentation state is updated in memory and then capsule persistence errors are swallowed. Durable OPFS quota or write failures can therefore lose accepted/candidate capsule bindings on reload without exposing the required actionable recovery or export path.
  • ⚠️ src/lib/workspace-controller.ts:4746 - Failed commands are recorded in the journal but never replace the cached accepted receipt. A retry with the same command ID is returned as a successful duplicate even though the original command failed, contradicting idempotent “original result” semantics. Define and cache a terminal failure receipt or retain an explicit pending result.

🔧 Fix: Harden control, persistence, capability, and acceptance boundaries
7 issues (6 errors, 1 warning) still open:

  • 🚨 src/lib/workspace-control-bridge.ts:172 - A normal command is forked before its fiber is registered. If a disable command is consumed before registration, revocation sees no active fiber and the command can mutate workspace state after disable. Register work atomically with dispatch or serialize the boundary.
  • 🚨 src/lib/workspace-control-bridge.ts:207 - Direct control revocation shuts down the broker, but the bridge only catches the resulting polling error and never interrupts tracked commands or clears its enabled state. Route broker revocation through a shared signal that interrupts active fibers and reconciles the bridge.
  • 🚨 src/capabilities/product-capability-broker.ts:712 - withReservation executes arbitrary product I/O while holding the broker's synchronized state lock. A slow or hung adapter blocks revocation, expiry checks, and all other capability operations. Move execution outside the lock while preserving a bounded lease/version or cancellation boundary.
  • 🚨 src/lib/runtime.ts:300 - The intent requires “provide ... extension ... behavior through shared typed controllers,” but the live adapter rejects every approved set-text intent as unsupported. Enabled portable extensions therefore cannot change interface state. Confirm whether this containment is explicitly authorized; otherwise wire the adapter to the controller boundary.
  • 🚨 src/lib/workspace-controller.ts:1568 - Terminal share transitions still have partial-commit paths: Keep mutates Git refs before installation persistence, so a save failure occurs before rollback metadata exists; later rollback can also delete the old source archive without restoring it. Reject has the same ref-before-store ordering, and restoreCandidate is itself an uncompensated multi-ref sequence. Use a journaled or compensating repository boundary covering all pre-state and archives.
  • 🚨 src/lib/workspace-controller.ts:1806 - kernel.accept commits the canonical revision before updateCapsulePresentation persists capsule state. A capsule-store failure therefore returns a failed command after acceptance is durable, leaving stale bindings and no proposal to retry; reject and rollback use the same ordering. Include capsule persistence in the transition journal or return an explicit degraded recovery result.
  • ⚠️ src/lib/workspace-controller.ts:739 - Capsule load corruption or I/O failures are caught and replaced with {} while the snapshot still reports the configured durable store. Restart can silently lose accepted/candidate capsule, extension, and permission-review state. Surface the failure as degraded persistence or restore a known-good binding.

🔧 Fix: Harden revocation, leases, and transactional acceptance
9 issues (6 errors, 3 warnings) still open:

  • ⚠️ src/lib/workspace-control-bridge.ts:103 - All runNext and snapshot-publication failures are treated as confirmed revocation. A transient transport outage therefore dispatches local disable and permanently turns control off instead of retrying. Distinguish broker revocation from temporary unavailability.
  • 🚨 src/lib/workspace-control-bridge.ts:85 - Revocation swallows transport.disable failures and then clears enabled. If the broker is still live during a transient failure, its descriptor/token remain active while the bridge stops consuming commands. Only clear local state after confirmed cleanup or keep retrying.
  • 🚨 src/capabilities/product-capability-broker.ts:694 - The expiry timestamp is read before acquiring the synchronized broker state. Waiting on the lock can let the decision expire before validation, while the stale timestamp still registers a lease and executes the product operation. Read time inside the synchronized validation boundary.
  • 🚨 src/lib/runtime.ts:424 - The intent requires “provide ... extension ... behavior through shared typed controllers,” but the adapter calls ShapingKernel.propose directly with no extension ID, role, binding, controller authorization, or operation evidence. Candidate-bound calls can therefore propose against the active document, and the shared controller workflow is bypassed.
  • 🚨 src/lib/runtime.ts:424 - SandboxResult permits up to 20 intents, but each set-text intent independently calls kernel.propose. The first intent creates a proposal; the second sees that proposal and fails, leaving a partial revision while the extension call reports failure. Validate and commit the batch atomically through the controller boundary.
  • ⚠️ src/extensions/portable-extension-host.ts:353 - PortableExtensionHost.call records only denied-capability and sandbox failures. Live adapter failures, including unsupported or partially applied interface intents, do not increment the extension failure count or trigger bounded disable behavior. Route all execution failures through the catalog failure policy.
  • 🚨 src/lib/workspace-controller.ts:1841 - Keep finalizes shared state before capsule persistence, but finalizedShare is only available to the inner kernel.accept catch. If capsule persistence fails at line 1841, the outer catch restores with undefined, leaving share refs/install state accepted while the kernel proposal remains pending. Preserve transaction metadata through every post-side-effect failure and compensate all stores.
  • 🚨 src/lib/workspace-controller.ts:1892 - Reject persists extension-catalog removal before capsule and kernel rejection. If either later step fails, no extension snapshot is restored, leaving the candidate catalog cleared while the proposal remains. Capture and restore extension, capsule, and share pre-state around the entire reject transition.
  • ⚠️ src/lib/workspace-controller.ts:1630 - Successful Keep and Reject no longer remove obsolete share archives. The content-addressed candidate store has no listing or garbage collection, so repeated updates/imports accumulate unreferenced OPFS data and can eventually exhaust storage. Clean up only after commit with reference-aware retention.

🔧 Fix: Harden control, extensions, and share transitions
3 issues (1 error, 2 warnings) still open:

  • ⚠️ src/lib/runtime.ts:385 - Kernel state conflicts and persistence failures are all mapped to CapabilityAdapterFailure, then recorded as extension execution failures. Invoking an accepted extension while a proposal is active can therefore mark a healthy extension failed; preserve typed conflict/persistence errors and only record package failures for actual extension faults.
  • ⚠️ src/extensions/portable-extension-host.ts:360 - The kernel proposal is applied before catalog.recordSuccess persists the extension test result. If catalog persistence fails, the command fails after creating a proposal, leaving shaping state and tested/catalog state inconsistent. Commit these effects transactionally or expose an explicit recoverable degraded result.
  • 🚨 src/lib/workspace-controller.ts:1801 - The authoritative intent requires “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” The changed acceptance/rejection handlers restore capsule state, extension state, and shared refs sequentially; if an early restore fails, later compensations are skipped, leaving the proposal and durable stores inconsistent. Use a durable journal/transaction or an explicit degraded-recovery marker covering the whole transition.

🔧 Fix: Preserve typed extension errors and degraded recovery
2 errors still open:

  • 🚨 src/lib/workspace-controller.ts:1636 - Keep/Reject can still strand shared Git state: if installation persistence fails and its nested ref rollback also fails, finalizeShareActivation/discardShareActivation throw before returning pre-state, so outer compensation receives undefined and cannot restore refs or enter degraded recovery. Preserve transaction metadata through nested rollback failures at the shared transition boundary.
  • 🚨 src/lib/workspace-controller.ts:1780 - The required criterion is “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” enterDegradedRecovery swallows kernel.enterSafeMode failure at line 1780 and only updates in-memory state; if the same interface-store outage prevents durable safe-mode persistence, reload can restore a pending proposal while other stores remain partially compensated. Persist a durable recovery marker/journal that startup honors.

🔧 Fix: Preserve rollback metadata and durable recovery markers
3 errors still open:

  • 🚨 src/lib/workspace-controller.ts:1572 - Keep still loses rollback metadata when shareRepository.acceptCandidate partially mutates refs and its own rollback fails: metadata is recorded only after acceptCandidate returns at line 1587. The outer compensation then receives undefined, cannot restore the refs, and does not enter degraded recovery. Record the transaction pre-state before the repository mutation or make the repository boundary return recoverable transaction metadata.
  • 🚨 src/lib/shaping-kernel.ts:713 - The required criterion is “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” When safe-mode persistence fails, markRecovery failure is swallowed at line 713; only in-memory state becomes safe, so a later reload after storage recovers can restore the old pending proposal without a durable recovery marker. Make recovery marking durable before the risky transition or provide a boot-visible fallback marker.
  • 🚨 src/lib/shaping-kernel.ts:759 - The required criterion is “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” The three-failure recovery path in recordExtensionFailure persists a safe-mode state directly at line 759 but never calls markRecovery; if that persistence fails, the state transition is discarded and repeated extension failures never establish durable safe mode. Route threshold recovery through the shared recovery-marker boundary.

🔧 Fix: Preserve pre-mutation rollback and durable recovery markers
2 errors still open:

  • 🚨 src/lib/workspace-controller.ts:1579 - finalizeShareActivation records post-accept refs before acceptCandidate runs. If a ref move fails after the repository’s own rollback succeeds, outer compensation still tries to restore from the expected post-accept state, fails its guards, and unnecessarily enters degraded recovery. Return rollback status from the repository boundary or make compensation recognize already-restored pre-state.
  • 🚨 src/lib/workspace-controller.ts:1802 - The required criterion is “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” enterSafeMode now aborts before updating in-memory state when markRecovery fails, while enterDegradedRecovery discards that failure at line 1802. A marker-write failure can therefore leave a pending, non-safe workspace and later reload the partial state. Preserve local safe mode while surfacing the durable-marker failure or provide an independent boot-visible recovery marker.

🔧 Fix: Preserve rollback status and local safe-mode recovery
1 error still open:

  • 🚨 src/lib/shaping-kernel.ts:762 - The required criterion is “preserve deterministic safe-mode, acceptance, rollback, and offline recovery.” At the third extension failure, markRecovery is a hard prerequisite; if marker creation fails, the state transition is aborted, so the extension is not disabled and safe mode is not entered. Apply the local safe-mode transition even when marker persistence fails, while surfacing degraded recovery.

🔧 Fix: Preserve safe mode when recovery marker writes fail
✅ Re-checked - no issues remain.

🔧 **Test** - 1 issue found → auto-fixed ✅
  • 🚨 src/lib/workspace-controller.ts:751 - bun run build fails during TypeScript compilation with errors in workspace-controller.ts and typed test doubles. This prevents the standard Playwright production workflow from starting and must be fixed before release verification.
  • bun run check:quality
  • bunx vitest run scripts/product-quality-coverage.test.ts
  • bunx vitest run src/lib/workspace-controller.test.ts -t 'answers Use questions and applies only a typed edit-tool handoff|uses an explicit warm Shape and candidate Use workbench|rejects stale failure handoffs and injects only correlated evidence|routes user and outside commands through one idempotent state machine|rejects stale sequences and outside attempts to enable control|enforces the role-scoped agent command policy before claim|creates a preview from Shaper and accepts it through typed commands|does not let a stale kernel event re-enter safe mode after restore|keeps unsent drafts reactive and outside public snapshots'
  • bunx vitest run src/components/agent-rail.test.tsx src/components/composer.test.tsx src/components/message-content.test.tsx src/components/activity-card.test.tsx src/components/role-aware-shell.test.tsx
  • bunx vitest run src/lib/workspace-control-bridge.test.ts server/control-broker.test.ts server/control-descriptor.test.ts server/mcp-adapter.test.ts
  • bunx vitest run src/axi/command.test.ts src/axi/program.test.ts src/axi/output.test.ts
  • bunx vitest run src/lib/operation-journal.test.ts src/lib/role-continuity.test.ts src/lib/shaping-kernel.test.ts
  • bun run build
  • bunx vite build
  • chrome-devtools-axi manual flow: shape → candidate Use → Markdown showcase → Safe mode → Restore interface
  • chrome-devtools-axi console
  • chrome-devtools-axi network

🔧 Fix: Fix build type errors and affected test doubles
✅ Re-checked - no issues remain.

  • bun run build
  • bunx vitest run src/lib/workspace-control-bridge.test.ts src/lib/workspace-controller.test.ts src/lib/runtime.test.ts src/sandbox/capability-broker.test.ts src/capabilities/product-capability-registry.test.ts
  • bun run check:quality
  • bunx playwright test tests/e2e/flect.spec.ts --project=chromium -g "tests a candidate, returns to Shape"
  • Production browser navigation and visual screenshots of App Agent and Shaper states via browser-harness
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

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