Skip to content

Fluxor Cockpit: un arnés reducido donde una tarjeta lanza una sesión de agente - #8

Open
CMolG wants to merge 70 commits into
mainfrom
feat/cockpit
Open

CMolG wants to merge 70 commits into
mainfrom
feat/cockpit

Conversation

@CMolG

@CMolG CMolG commented Sep 10, 2026

Copy link
Copy Markdown
Owner

El Cockpit: un arnés reducido donde una tarjeta lanza una sesión de agente. Cinco commits, uno
por fase, y cada uno deja algo funcionando por sí solo.

F1 — la ventana agent-session (f14780c7)

PTY en el proceso principal, xterm en el renderer. La CLI del proveedor corre de verdad, con
.harness/ cargándose como hoy.

F2 — un worktree por sesión (d137ccfe)

La sesión abre en su propio worktree y su propia rama antes de que arranque el agente: se crea
en .claude/worktrees/<nombre> desde un origin/main recién traído (si el fetch falla se sigue con
el ref local y se dice cuál se usó) y se instala por lockfile.

Al terminar dice si el worktree está gastado, y lo decide como manda la regla: por contenido y
por el registro de gh, nunca por ascendencia, que es lo que miente después de un squash. El
borrado se lleva el directorio y conserva la rama.

Y una sola sesión adjunta por proyecto: el invariante 16 hecho mecanismo. La segunda no se bloquea
—se ofrece abrirla en un worktree.

F3 — la tarjeta lanza, y el estado vuelve (ad7efee6)

Cuarto LaunchMenu: proveedor (los no instalados, deshabilitados con el motivo) y modo
worktree/attached, cada uno con su razón cuando no aplica.

Un fichero, dos claves: el agente mueve status, el Cockpit escribe runState — running
cuando el PTY existe de verdad, completed/failed según lo que el agente dejó, no según el código
de salida. Las tarjetas HUMAN salen de la pila a un carril fijo, con lo que desbloquean y sin
lanzador
.

F4 — atención por hooks, no por adivinación (a227c030)

Endpoint http en loopback con token por sesión en la ruta: 200 {} inmediato, 404 para todo lo
demás
(nunca 401: confirmaría el id), 413 y 400. Los cinco hooks viajan como --settings en línea
—no se escribe ningún fichero— y se parsean las dos grafías, porque el binario y la
documentación no coinciden.

La atención es una máquina pura: armado es una afirmación, recibido es la prueba. Hasta el
primer evento mandan las heurísticas de F1, y la ventana avisa si no llega ninguno. Un aviso por
tramo de espera, nunca por evento.

F5 — prueba de campo con un agente real (ae115d7d)

Un worktree se corta de origin/main, así que una tarjeta que aún no ha llegado allí no está en el
checkout: seed-card.ts la copia entre worktree-create y el bootstrap, sin sobrescribir nunca.

e2e/field/ está saltada salvo FLUXOR_FIELD_TEST=1, porque gasta tokens de verdad. Tres
tarjetas PROBE en paralelo sobre javadaba-web, las tres cerradas por el agente. El informe está
en .harness/reports/2026-09-08-cockpit-field-test.md de ese repositorio.

Pendiente

El build empaquetado, codex/opencode/gemini reales, y el modo adjunto con un agente de verdad.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GQQ1fEXq4GJzHNs8BPNTFW

CMolG and others added 30 commits July 11, 2026 21:58
Puerto Sandbox (exec/execWithNetwork/putFile/getFile, AutoCloseable) +
DockerSandbox sobre el CLI docker vía ProcessBuilder (cero deps nuevas,
misma filosofía "solo JDK" que los providers HTTP). Contenedor durmiente
(sleep infinity), --memory/--cpus/--pids-limit, --read-only root +
--tmpfs /tmp + HOME dentro del workspace (pnpm/next necesitan escribir
cache), usuario no root, --rm.

Sin red por defecto; execWithNetwork es la única vía con red
(connect/disconnect con try/finally, SIEMPRE se desconecta incluso si
el comando falla, timea o lanza). Timeout mata el contenedor entero
(defensa en profundidad ante comandos colgados/comprometidos), no solo
el proceso.

Hallazgo verificado con Docker real: --network none en `docker run`
impide reconectar red después (Docker lo rechaza) — el contenedor nace
en bridge y start() lo desconecta de inmediato, comportamiento
verificado idéntico a --network none pero reconectable.

25 tests (20 unit con ProcessRunner inyectado + 5 integración @tag
integration/@EnabledIfDockerAvailable, ejecutados de verdad contra
Docker real con alpine:3). Módulo completo: 91 tests, 0 failures, 1
skip preexistente (MimoLiveTest, sin API key).
…tenedor

run_command/write_file/read_file/list_files/install_deps como @FluxorTool
sobre un Sandbox activo. install_deps es la ÚNICA con execWithNetwork; el
resto siempre exec sin red. Los fallos de comando (ExecResult) y de
transferencia de fichero (SandboxException en putFile/getFile) se devuelven
como datos en el resultado, nunca como excepción — el modelo los lee y
corrige en el siguiente turno.

Hallazgo: SchemaExtractor sabe anotar Optional<T> como no-requerido en el
schema, pero ToolRegistry no puede bindear un Optional<T> SUMINISTRADO (el
ObjectMapper compartido no registra jackson-datatype-jdk8). Sin nueva
dependencia ni tocar ToolRegistry/Json (fuera de alcance): parámetros
opcionales (timeoutSeconds, path/recursive) usan Long/String/Boolean
nullable con default manual, documentado en el Javadoc de la clase.

README del SDK documenta el uso, los defaults de seguridad y la advertencia
de cadena de suministro (el sandbox aísla la ejecución, no audita el
artefacto resultante).

12 tests nuevos con Sandbox fake. Módulo completo: 103 tests, 0 failures,
1 skip preexistente (MimoLiveTest, sin API key).
Port conceptual de AgenticMod (src/types/harness.ts:18-23,89) al SDK Java como
StepMod: la unión discriminada {id,name,type,config} de TS se colapsa en dos
hooks por interfaz (onRequest cubre pre_process/system_override/tool_provider;
onResponseText cubre post_process), usando el sistema de tipos como
discriminador en vez de un tag de string.

ModOverlay es la novedad "on-demand": colección efímera de mods por step
(forStep) o para todo el flow (forAll), con resolve(stepId) determinista
(forAll primero, luego específicos) — capacidad que no existe 1:1 en la IDE
(AgenticFlow no tiene mods propios), pero requerida por el caso de uso de
microajustes por prospecto en runtime.

5 builtins en mod/builtin/: SystemPromptMod (antepone/añade, análogo a
system_override), AppendGuidelineMod (conveniencia sobre SystemPromptMod
APPEND — la instancia mobile-first de #19), ForbidToolsMod (análogo a
runtime.blockTools), ProvideToolsMod (análogo a tool_provider/
runtime.attachTools; reutiliza ToolRegistry sobre ForkJoinPool.commonPool()
para no crear un pool dedicado por mod efímero) y RegexPostProcessMod.

Divergencia deliberada del TS: en el motor actual de la IDE, post_process NO
transforma la respuesta — solo inyecta un hint de texto antes de la llamada
(context-builder.ts buildPostProcessHint) y confía en que el modelo se
autoaplique la instrucción. onResponseText aquí es una transformación
mecánica real aplicada tras la respuesta, más fuerte que el comportamiento
TS actual, tal como exige el contrato del plan.

10/10 tests verdes (ModOverlayTest): orden de resolución, no-leak entre
steps, los 3 builtin con lógica no trivial (SystemPromptMod prepend/append/
inserción cuando no hay system previo, ForbidToolsMod no-op cuando no hay
match, RegexPostProcessMod).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ae85cHWqsJGWAFvD74dAHZ
StepExecutor.executeStep/executeStepText ganan un overload con ModOverlay
(patrón idéntico al ya usado para DagTelemetry: overloads previos delegan al
más completo con ModOverlay.EMPTY, cero cambio para callers existentes).
onRequest se aplica en runToolLoop justo antes de provider.complete — es
decir en cada turno del tool loop Y en cada intento de retry de schema
(runToolLoop se reconstruye desde el history base, nunca desde una versión
ya "moddeada", así que un mod no se acumula entre turnos); onResponseText se
aplica sobre el texto crudo antes de extractJson/parse/validate, para que un
post-process pueda rescatar un intento que si no fallaría el schema.

ModContext reutiliza step.context() (el mapa que ya rellenan
FlowExecution.withContext + los resultados de padres en el DAG) — cero
plumbing nuevo para "config/estado del prospecto".

ProvideToolsMod requiere una vía de despacho real, no solo de anuncio en el
request: StepExecutor.invokeTool cae a los ProvideToolsMod del overlay
resuelto cuando el nombre no está en el ToolRegistry compartido del step, así
una tool on-demand es invocable de verdad, no solo visible para el modelo.

FlowExecution.withMods(ModOverlay) — decisión donde el plan daba margen: el
overlay debe llegar a StepExecutor incluso para el caso ad-hoc de un único
step (FlowExecution siempre pasa por FlowExecutor.execute), así que
FlowExecutor también gana el overload con ModOverlay (mismo patrón de
delegación), no solo los 3 ficheros que enumeraba el plan — necesario para
que "FlowExecution guarda el overlay y lo propaga por step" sea cierto para
cualquier flow, no solo si StepExecutor se llamara directamente.

StepConfig gana el campo opaco `mods` (JsonNode) con el mismo patrón de
constructores compat que contract/model: el nuevo ctor de 8 args es el
canónico, el de 7 (pre-mods) y el de 5 (pre-contract/model) siguen
compilando y comportándose igual. withParentResults ahora preserva
node.mods() en la copia "efectiva" del step, igual que ya hacía con
contract/model.

8 tests nuevos (StepModExecutionTest): guía inyectada llega al request
capturado; ForbidTools vacía el tools[] del request; post-process arregla
JSON inválido ANTES de validar (0 reintentos consumidos); mismo mod aplicado
a un step de texto; sin overlay el LlmRequest es estructuralmente idéntico
(record equality) entre el path pre-mods (2/3-arg) y el nuevo con
ModOverlay.EMPTY; un StepMod contador prueba que onRequest se reinvoca en
cada intento de retry (2 llamadas para 2 intentos); ProvideToolsMod
anunciado Y despachado de extremo a extremo; FlowExecution.withMods
propagado a través de FlowExecutor hasta StepExecutor con FluxorRuntime real.

Suite completa del SDK: 121 tests, 0 fallos, 1 skip preexistente
(MimoLiveTest) — 103 baseline (#17) + 10 (Task 1) + 8 (Task 2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ae85cHWqsJGWAFvD74dAHZ
Añade la sección "Mods (on-demand overlay)" al README del SDK, mismo estilo
y nivel de detalle que la sección "Sandbox" ya existente: ejemplo de uso de
ModOverlay/withMods, contrato de StepMod, los 5 builtins y la garantía de
no-regresión (sin overlay, el pipeline es idéntico al de antes de mods).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ae85cHWqsJGWAFvD74dAHZ
…— inicio de la adopcion

Declara la dependencia por coordenada (nombre+version, nunca vendorizada) hacia
el motor cerrado de canvas/chrome de Javadaba (ADR 0003, javadaba-web Core):
.npmrc con el scope @javadaba apuntando a GitHub Packages, dependencia en
package.json y un smoke test que fija que Vitest resuelve el paquete.

El registro aun no tiene la version publicada (Task 9 Step 3, pendiente de
autorizacion + token); mientras tanto se consume via `npm link` en local
(bitacora en docs/superpowers/backlog/2026-07-17-daba-engine-registro-privado.md,
gitignored por convencion de este repo — artefacto de planificacion de agentes,
no producto). Anadido resolve.dedupe: ['react','react-dom'] en vite.renderer y
vitest config: un paquete enlazado resuelve su propio node_modules/react (via
symlink a la ruta real) si no se fuerza una copia unica.
…a/daba-engine

Adopcion del motor de canvas/chrome propietario (plan #20, javadaba-web
Core), Task 10 -- las piezas "hoja" (sin dependencia del sustrato de camara,
que sigue pendiente en Task 12):

- HUD: HudWidgetLayer conserva su shell (WidgetWrapper, close button, drag/
  resize por mouse, testids exactos) pero la matematica de snap/clamp/
  colocacion anti-solape ahora es un adaptador fino (logic/hud-widget-policy.ts)
  sobre @javadaba/daba-engine/core/hud-grid -- logic/hud-grid.ts (188 LOC +
  su test) queda borrado. La SAFE_ZONE reservada (ExpandInspectorButton) se
  pliega en la lista `others` del motor en vez de su opcion `safeZone`
  (semantica opuesta: el motor CONFINA a esa zona, Fluxor necesita EVITARLA)
  -- resuelto sin tocar el motor.
- Tutoriales: TutorialEngine.tsx pasa a ser un wrapper fino sobre el
  TutorialEngine del motor (maquina de estados + tracking rAF del target +
  salto por timeout), conservando el 100% del look de Fluxor (fondo
  sketchbook + doodles + anillo de color por escenario + BrutalistTutorialCard)
  via los nuevos renderBackdrop/renderCard(...,targetRect) del motor.
- Menus contextuales: las 3 implementaciones custom (CanvasContextMenu.tsx,
  WindowContextMenu.tsx, el inline de NodeTree.tsx) se consolidan en
  providers declarativos sobre el ContextMenu unico del motor, con
  data-testid/labelColor passthrough para conservar selectores e2e
  existentes (canvas-ctx-*, ctx-menu-*, nodetree-ctx-*).
- index.css/index.tsx: tokens --daba-* mapeados a la paleta oscura actual de
  Fluxor (iso-visual) + import de tokens.css/engine.css del motor.

Gaps de contrato reales encontrados y resueltos EN EL MOTOR (interface-first,
3 commits en javadaba-web Core, ver su CHANGELOG): TutorialEngine.renderCard
gana targetRect + renderBackdrop; ContextMenu gana testId/labelColor
passthrough + previene el contextmenu nativo en su backdrop.

Verificado: tsc --noEmit limpio, npm test 1582/1582 (-34 del hud-grid.test.ts
borrado), npm run lint 0 errores (269 warnings preexistentes). e2e
(Playwright+Electron) NO se pudo ejecutar en este sandbox -- Electron no
abre ventana aqui (reproducido identico en el commit previo a este Task, no
es una regresion); verificado en su lugar por revision manual exhaustiva de
los data-testid/selectores que e2e/*.spec.ts espera.
…ton + DabaCanvas pan/pan-layer

Task 12 (Cortes 1-3, adoption plan #20): canvasPan/canvasZoom en desktop-store
pasan a ser un espejo de la camara de @javadaba/daba-engine en vez de estado
propio. engine-bridge.ts es el singleton de modulo que hidrata el EngineStore
desde el desktop-store ya persistido, refleja los cambios del motor de vuelta
al espejo (onChange crudo, sin reentrancia) y parchea setCanvasPan/
setCanvasZoom/_pushCameraToEngine sobre el store ya construido (desktop-store.ts
no puede importar este fichero sin crear un ciclo — el mismo tipo que ya evita
con harness-store). switchBoard y la rama activa de deleteBoard empujan la
camara del tablero entrante al motor; duplicateBoard no (nunca toca la camara
activa). navigateToWindow (usado por Dock.tsx) tambien se corrige: hacia un
set() crudo de canvasPan que hubiera desincronizado el espejo del motor.

SeamlessCanvas.tsx monta <EngineProvider store={engineStore}><DabaCanvas> en
vez de su propio root+pan-layer: pan por boton medio (shouldHandleBackgroundGesture,
button===1 solo — sin Alt-drag, Fluxor nunca lo tuvo) y la pan-layer (transform
CSS) pasan al motor; marquee, ctrl/cmd+wheel zoom (paso fijo, sin ancla al
cursor) y el menu contextual de fondo (via onBackgroundContextMenu) siguen
siendo dominio de Fluxor. rootProps conserva testid/aria/data-*/handlers de
raton exactos sobre el root del motor; panLayerClassName="desktop-pan-layer"
mantiene los selectores de los e2e existentes. DesktopCanvasBg pasa a
overlay con z-index:-1 (antes hermano ANTES de la pan-layer por orden DOM;
ahora el overlay del motor siempre pinta despues). CanvasContainerBridge
expone el nodo real del motor (useDabaCanvasContext) para el ResizeObserver
de canvasSize y el listener nativo de wheel (no sintetico, necesita
preventDefault real). index.css: el selector de will-change durante zoom pasa
de compuesto (.desktop-pan-layer[data-zooming]) a descendiente
(.desktop-canvas[data-zooming] .desktop-pan-layer), ya que data-zooming solo
puede ir en el root ahora — esto tambien revive [data-zooming]
.desktop-window-shell{transition:none}, su intencion original.

MentalGraphCanvas.tsx no requiere cambios: ya lee canvasPan/canvasZoom del
espejo y escribe via setCanvasPan/setCanvasZoom (ahora delegadas), no set()
crudo.

DesktopWindow.tsx: ni una linea (Opcion A — espejo, no CanvasItem controlado).
SnapGuides/calculateSnapGuides/activeSnapGuides: sin cambios (deuda registrada,
desviacion del plan original justificada por iso-behavior).

Motor: vendorizado via npm link contra javadaba-web@31416c4 (fix: pan por
boton-medio ya no exige gesto sobre el fondo del canvas).

Tests: 12 nuevos en engine-bridge.test.ts (hidratacion, propagacion motor->
espejo, delegacion accion->motor, ida-y-vuelta sin reentrancia, flujo
switchBoard/deleteBoard/duplicateBoard). 1594/1594 unit verdes. e2e:
dock-fixed-on-pan 4/4, desktop.spec.ts Canvas Panning and Zoom 6/6 (ctrl+scroll
flaky conocido, verde en retry). Suite completa: 287 passed/29 failed/1
flaky/6 skipped — los 29 fallos son exactamente el baseline preexistente.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Toggle "Snap to grid" en SettingsModal (default OFF, preset de tamano de
celda 16/24/32 px, default 24) espejado uno-a-uno en engine-bridge.ts hacia
snap.grid del motor (fluxorWindowGridSpec — NO DEFAULT_GRID_SPEC, que es la
rejilla de iconos de web-os). DesktopWindow y MentalGraphCanvas cuantizan la
posicion SOLO en el commit del drag (via resolveSnap del motor,
guides.enabled=false): las guias de alineacion de Fluxor mandan primero
(calculateSnapGuides intacto); si ninguna magnetizo y el toggle esta ON,
cuantiza a rejilla. Resize y moveSelectedWindows sin tocar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…ado sobre daba-engine (Task 13, Fase 2)

NodeTree/SideBar sobre <ComponentsPanel> e InspectorPanel sobre
<SideToolbar> (motor), completando la adopcion iniciada en Task 12
(camara) y continuada en Fase 1 (snap-to-grid). NodeTree conserva solo
datos/decoracion de dominio (grupos 1:1 con los 8 de hoy, renderRow por
kind, rename/locate/context-menu); el shell (headers, hover, rename
in-place, empty state) es ahora del motor.

engine-bridge.ts gana tres espejos nuevos, todos documentados in-line:
- engine.items: windows/grids/mental-nodes(mental|step|flow)/attachables,
  ids namespaced kind:id. Cache por-id de EngineItem MUTADO in-place para
  geometria/label (nunca dispara setState) y solo notifica al motor en
  altas/bajas — SideToolbar suscribe TODO state.items sin acotar a la
  seleccion, y MentalGraphCanvas commitea posicion en cada frame de un
  drag; sin este patron, cualquier drag de un nodo NO seleccionado
  reconciliaria el Inspector entero (regresion que InspectorPanel.test.tsx
  ya vigilaba con un Profiler antes de esta tarea). Los lectores
  (locate(), seed de rename) leen en el momento del click sobre la MISMA
  referencia mutada, asi que siguen frescos sin necesitar notificacion.
- engine.selection: unidireccional desde selectedMentalNodeIds (nunca
  selectedWindowIds — el Inspector sigue ignorando seleccion de ventanas).
- hover/collapse bidireccionales: engine.hoveredItemId <-> hoveredWindowId
  (solo ventanas; grids/mental/step/frame leen el hover del motor
  directamente, ya estan dentro de su EngineProvider) y
  engine.chrome.panels.sideToolbar <-> settings.showInspector (persistencia
  sin cambios). Mismo patron de reentrancia que la camara (Task 12): raw
  set() en el onChange del motor, nunca la accion publica envuelta.

Highlight unificado: MentalNode/StepNode/FrameNode leen
engine.hoveredItemId directamente (data-daba-highlighted + outline
var(--daba-highlight)) — antes solo las ventanas lo tenian. Tokens
--daba-accent/--daba-highlight/--daba-accent-contrast/--daba-radius-lg/
--daba-marquee-* mapeados al azul de marca en :root.

ExpandInspectorButton.tsx borrado; su rol lo cubre el boton colapsado de
SideToolbar (CSS lo reposiciona fixed al mismo rincon via
.inspector-side-toolbar.daba-side-toolbar__expand, ya que ese boton no
tiene prop de testid/posicion propia).

Contenido interactivo anidado dentro de renderRow (chips de rol/mod/flow,
child-windows de un grid, StepRows de un flow) hace stopPropagation en
click/dblclick/contextmenu para no disparar ademas el locate()/context-menu
de la fila padre del motor — antes eran filas hermanas sin bubbling que
gestionar. Cubierto por NodeTree.nestedRowIsolation.test.tsx.

centerViewportOn (NodeTree.tsx) pasa a delegar en el centerOn del motor
(viewport lazy = innerWidth-280/innerHeight-60, sin insets) en vez de
reimplementar la formula — verificado con valores dorados contra la
formula antigua en NodeTree.locateViewport.test.tsx (desviacion respecto
al plan original, que esperaba cero referencias a centerViewportOn;
anotado para Task 14 por el orquestador).

Gates: unit 105/105 archivos, 1652/1652 tests; lint 0 errores (262
warnings preexistentes, ninguno nuevo); tsc 0 errores; e2e diferencial
limpio (287 passed / 29 failed — exactamente la lista permitida / 1 flaky
en retry / 6 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…arcial)

Cierre parcial del barrido del satélite (Task 14 Step 1):

- hud-grid.ts → hud-widget-policy.ts: actualiza las últimas
  referencias-comentario a la ruta nueva (Dock, HudWidgetLayer,
  desktop-store, desktop-store.test). El único hit de "hud-grid" que
  queda es el comentario histórico de desktop-store.ts que apunta al
  core/hud-grid del motor (intencional).
- index.css: retira el chrome muerto del inspector
  (.inspector-header/-label/-collapse-btn/-body) que Task 13 Fase 2
  movió al SideToolbar del motor; reescribe el comentario de
  --daba-accent (los tokens SÍ están mapeados en :root vía tokens.css
  del motor — la regla del motor resolvía a azul, no a valor inválido;
  el override mantiene el morado de mental-mode).
- Dock.tsx: siembra startPos.current con el origen del drag en
  onAttachDragStart — sin ello el ghost del attachable quedaba clavado
  arriba-izquierda (solo el delta lo movía en vez de seguir al cursor).
- desktop.spec.ts: aclara el comentario del test grid skippeado.

Pendiente para cerrar Task 14 (detalle en la bitácora satélite
docs/superpowers/backlog/2026-07-17-daba-engine-registro-privado.md):
Step 1 restante — migración diferida de guías (SnapGuides)/
DottedBackground/centerViewportOn al motor (Task 12-bis); Step 2 —
consolidación del motor 0.2.0 (vendor tarball y/o publicación al
registro npm privado, acción del humano con credenciales); Step 3 —
suite e2e 14/14 verde (entrelazado con la deuda e2e legacy #4 del
backlog de producto); Step 4 — commits finales (Core CHANGELOG +
satélite). Task 15 (docs/gobernanza del workspace) pendiente.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…ing + body sections

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
… runState

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…ged + runState param

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…inimal bento shell

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports STATUS_CONFIG/PRIORITY_CONFIG verbatim from the frozen reference
(shapes, order, colors, icon set) onto the project's LucideIcon name-based
lookup (no lucide-react dependency here). Labels are EN by default per the
task doc's resolved decision #2 (IDE-wide text consistency), centralized in
this one table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports the reference's 2-7 sided polygon indicator (line/triangle/square/
pentagon/hexagon/heptagon) verbatim — same viewBox, points, strokeWidth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports the "Epic: X" pill verbatim as its own component; returns null when
epic is absent (F0 spec §1.1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports the reference's per-card JSX verbatim (drag-info block, status shape +
badge + EpicBadge + title + excerpt + tags, created/updated metadata,
desktop + mobile variants). excerpt is derived from description's first
non-blank line (not persisted, F0 spec §1.3); dates format ISO-8601 to the
reference's DD-MMM-YYYY. Adds a minimal isSelected ring (multi-select
behavior preserved from the deleted kanban widget; not in the reference) and
a runStateOverlay slot reserved for F4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…order

Single SortableContext (no more 4 columns) wrapping the reference's
filteredAndSortedCards derivation (status.order primary, card.order
secondary — F0 spec §1.1) and empty state, ported verbatim. DnD behavior
layer (PointerSensor+KeyboardSensor, meta/shift multi-select scoped to same
status, reorder persisted via updateBacklogCards) reconstructed from the
deleted BacklogKanbanWidget.tsx (git history a0e6e65). Status is never
touched by drag — only order, globally dense — matching the reference's own
full-array splice semantics. Native HTML5 DnD -> @dnd-kit (Gate 1 deviation
#3); the reference's "whole card draggable" look is preserved via a thin
unstyled sortable wrapper, no added grip-handle chrome.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports the reference's search input + status chip row verbatim, including
its own white/black-border card chrome. searchTerm/activeStatuses are
controlled props so BacklogBentoWidget owns the filter state, matching the
reference's own state placement. Hand-rolled inline SearchIcon -> LucideIcon
name="Search" (same glyph).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Ports the reference's modal sidebar verbatim (priority, estimate, tags,
assignees, created/updated history). Assignees empty state is "Unassigned"
(EN) per the plan's Task 8 note / task doc resolved decision #2, overriding
the reference's "Sin asignar". No dedicated test — pure presentational leaf,
covered by BacklogCardModal's integration tests (Task 9).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…mments

Replaces the F2 Task 1 shell with the full modal ported verbatim from the
frozen reference (header with editable title/status, description edit,
attachments, related-tasks navigation that swaps canvasModalCard in place,
comments + input, BacklogModalSidebar). Canvas-level fixed/sticky/max-h-[90vh]
positioning is unchanged (legitimate full-app overlay by design, not the
widget-wrapper "wrapper-relative" deviation).

New supporting plumbing this task required:
- fluxor:update-backlog-card-content IPC handler (main) — generalizes
  update-backlog-card-status's read-modify-write shape to the body (title/
  description/comments) via parseBacklogCard + renderBody +
  serializeBacklogCard; new comments append at the end (F0 spec §1.2).
- updateBacklogCardContent on FluxorAPI (types/index.ts) + preload wiring.
- desktop-store.activeBacklogDir (+ setter): a small new transient field so
  this canvas-level modal (mounted outside the widget's own wrapper) knows
  which directory to persist edits into — necessary for correctness across
  multi-project/external backlogs, not just the default in-project one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
… the bento widget

Final F2 component: wires BacklogFilters + BacklogPile + the picker/init/scan
behavior layer into the bento widget, completing the 1:1 port. Removed debug
scaffolding left mid-implementation; stabilized the auto-select widget test
(assert on the settled pile card before the sibling filters testid, which
flashes during loadCards' loading toggle — component behavior unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
Net-new presentational component (no frozen-reference counterpart): a
compact/full-variant dropdown offering "Run on existing flow" (nested
frame-picker submenu), "Autoflow", and — only when the card carries an
epic and a handler is supplied — "Run epic as flow". Every action is a
callback prop; the component never touches a store or IPC directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
CMolG and others added 30 commits July 22, 2026 17:36
…inst the bento UI

Replaces every .fd-*/.backlog-kanban/4-column assertion with the new
bento pile's real testids (backlog-bento-widget, backlog-pile,
backlog-card, backlog-filters, backlog-card-modal, launch-menu-trigger,
run-state-overlay). Ports each old test's intent (open/render, modal
open+Escape-close, priority display, multiple-cards rendering) and
fixes the baseline's one backlog failure: the empty-state test now
seeds one card to reach the pile view then clears it, asserting the
pile's own data-testid="backlog-pile-empty" markup instead of
tolerating three unrelated strings behind a best-effort waitForFunction.

Adds coverage the old suite never had: status filter chips actually
filter, search actually filters, and one launcher (autoflow) fires
end-to-end with the Meta-Agent/harness IPC bridge mocked (same
round-trip constraint documented in new-features.spec.ts's Auto-Chat
coverage), asserting a real frame materializes and the card
optimistically flips to doing/running.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…nD model

Ports each describe block's intent onto the single-pile model: order
field still sorts within the same status; multi-select (meta+click
toggle, shift+click range) now scopes ranges to matching status
(BacklogPile.tsx's handleCardSelect); a keyboard-driven pick-up/
move/drop reorder exercises the same onDragEnd + updateBacklogCards
persistence path a pointer drag would, deterministically. Drops the
4-column kanban board and "Reorder by Priority" Sort button coverage
(both retired by the F2 Task 1 cutover — BacklogBentoWidget.tsx's own
header comment confirms reorderByPriority was intentionally not
ported). Batch update IPC coverage now also checks the runState field
on update entries.

File was renamed from kanban-dnd.spec.ts in the prior commit; this
commit is its content rewrite against the new bento DOM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…xtBridge-unmockable Meta-Agent)

The autoflow launcher calls window.fluxorAPI.assemblePipeline — the same seam
the working HudAutoChatPanel uses — which is contextBridge-frozen
(contextIsolation:true + preload's exposeInMainWorld), so page.evaluate cannot
stub it to materialize a frame without a real AI backend (verified via a store
probe: mentalNodes stays empty because the real IPC fails offline). Not a
product bug — the launcher is identical to HudAutoChatPanel and fully covered
by launchActions.test.ts / LaunchMenu.test.tsx / epicPipeline.test.ts. Follow-up:
a mockless variant driving the "flow por épica" launcher (pure in-renderer
epicToPipelineAssembly mapper, no Meta-Agent IPC).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
…dow-leak strict-mode)

Adds a beforeEach that removes all desktop-store windows and opens a single backlog window, so page-scoped card locators stop matching the same card across leaked windows (the single-session persistence let them accumulate). Fixes the Keyboard-reorder + Order + Batch tests. The 3 Multi-select meta/shift+click tests still fail on a separate click-interaction issue (the feature itself is covered by BacklogPile unit tests) — documented residual.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01Rnz9QsXbdJSzzKMC46vnVu
crashReporter only catches native crashes; an uncaught exception, an
unhandled rejection or a killed renderer left nothing behind at all — in
dev they print to a terminal that dies with the process, so a crash was
forensically silent (empty Crashpad dir, no .ips, no log).

installFatalHandlers() adds four listeners that append one durable line
each, with zero change to crash semantics: uncaughtException uses
prependListener so Electron's own handler still runs, unhandledRejection
rethrows to restore Node's default throw-on-unhandled, and the two
process-gone events are purely observational. log.fatal() is a new sink
that always writes to disk (unlike log.error(), which prefers the console
and only falls back when the console itself is broken).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Task 3: findOwningPhase mirrors findOwningFrame one nesting level deeper,
for canvas/UI consumers that need "which phase visually owns this step".

Task 4: compileFlowFromCanvas gains a phase block structurally parallel to
the loop block — same map reuse (stepById/nextByStepId/prevByStepId/
reachableSet), same throw-naming-the-offender discipline. Validates
non-empty, resolvable, connected (undirected BFS over the member set),
disjoint, unique-id, onError === 'halt'; dedups repeated stepIds. A scoped
(includeIds) compile silently omits a phase whose real members are only
partly included, but still throws for a stepId that is no step at all.
flow.phases is omitted entirely when the canvas declares no phases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Task 6: addPhaseNode mirrors addFrameNode one nesting level deeper and
re-parents its named child steps from the frame to the new phase, so the
canvas hierarchy and the compiled AgenticPhase.stepIds agree from creation.
updatePhaseData mirrors updateFrameData.

Task 7: removeMentalNode on a phase DISSOLVES it — children are re-parented
up to the owning frame, never deleted (spec §1.1: a phase is pure grouping,
so deleting the label must not delete the work). Frame behavior is unchanged.

Beyond the plan: the frame cascade now descends to a fixpoint instead of one
level. addStepNode never registers a step in the frame's own childIds, so the
cascade relies on the parentId scan — once a phase sits between frame and
step, the single-level pass deleted the phase and stranded its steps with a
parentId pointing at a node that no longer existed. Covered by a new test;
with no phases on the canvas the loop converges on the first pass, so the
removed set is byte-identical to before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Title (inline-editable) + member count, nothing else — a phase never runs
on its own, so no Run/Export control; no exitContract/onError editor either,
mirroring AgenticStep.contract's own status quo.

Beyond the plan, two things were needed to make it actually render:
- MentalGraphCanvas maps store nodes to React Flow nodes by type, and a
  phase with no branch fell through to the mental-card default. Added a
  branch parented to its frame (extent: 'parent') and z-stacked between
  frame (0) and step (2).
- addPhaseNode now splices the phase in right after its owning frame instead
  of appending. React Flow resolves parentId positionally and needs a parent
  before its children — appending put the phase after the steps it parents,
  which would render them detached. Covered by a new ordering test.

CSS follows .pipeline-frame-node's grammar (fills its box, dashed border,
dashed->solid state cue) but subordinates it on every axis the eye scans
first — violet not neutral, tighter radius, no blur/shadow, smaller chip —
so the two nesting levels never read as peers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
…kpoint

Task 10: Checkpoint/SaveCheckpointInput gain an optional phaseBoundary,
frozen at creation from the flow's phases as they were when the run
executed, so a later flow edit can never corrupt a historical marker. Same
conditional-spread discipline as iteration/contextFileSnapshot — omitted
entirely, never undefined-valued. SqliteCheckpointStore mirrors it as one
JSON TEXT column (the marker is a nested object, not two flat scalars).

Task 11: findPhaseStartCheckpoint is the ONLY new piece resume-from-phase-
start needs — a pure query composed with the UNCHANGED replayFrom by the
caller. replay.ts keeps zero diff.

Task 12: CheckpointRecord mirrors the field for the renderer; the IPC
handlers already pass Checkpoint objects through unreshaped, so no runtime
code changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
…cutor

Mirrors the existing loop sanity block: unique ids, non-empty, resolvable
stepIds, connected subgraph (undirected BFS over the member set), pairwise
disjoint, onError restricted to 'halt'. Necessary because an AgenticFlow
can reach the executor without ever passing through compileFlowFromCanvas —
hand-built, imported, or SDK-constructed. A flow declaring no phases skips
the loop entirely and is byte-identical to today.

loop-plan.ts and replay.ts keep zero diff (red-line 2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Capa 1 runtime is now complete and observable.

Task 14 — per-run bookkeeping (buildPhaseRuntimeState) counts each phase's
expected instances from the ALREADY-EXPANDED ExecutionPlan, so "the phase's
last instance" is correct for a phase spanning a loop body. loop-plan.ts is
read, never touched. The `before` snapshot is lazy: taken once, only when the
phase actually declares an exitContract, so a phase without one costs zero
extra snapshots (covered by two tripwire tests counting readdir calls).

Task 15 — at the last instance: `after` snapshot, verifyStepContract
(guardrails.ts, unchanged), throw on breach naming the phase. The throw lands
before saveCheckpoint, so the breaching instance persists no checkpoint —
exactly what an uncaught step error already does. maxAttempts is inherited
from StepContract's shape and deliberately ignored: the gate never retries.
Without a fileSystem the gate is skipped rather than run against an empty
snapshot pair, which would false-positive on mustWriteFiles.

Task 16 gate: 1819/1819 unit, tsc clean, and zero diff in loop-plan.ts and
replay.ts (red-line 2) and in loop-plan/replay/guardrails tests (red-line 4).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
…-log fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
Measured, not estimated. Full suite at HEAD: 64 failed, all confined to 3
spec files. Those 3 re-run at the pre-#6 commit 7b3b196: 63 failed. The
single delta (desktop.spec.ts:1239) is a confirmed flake — fails in one full
run, passes in a second full run of the same file, passes in isolation, and
fails via a pointer-interception timeout from the auto-chat HUD widget, not
a logic error. Excluding it, the failure sets are identical element by
element (59 desktop + 3 backlog-dnd + 1 mental-map).

Pins the real e2e baseline at 63 (ONGOING carried "~62", too imprecise to
decide a differential gate on), with the per-file breakdown, and records the
comparison method for the next gate. Also closes #5's residual 3:
mental-connections.spec.ts passed in both runs, confirming those 2 failures
were flakiness and not a #5 regression. Diagnoses mental-map.spec.ts:318 for
#4 — it races its own consumer, StepConfigCore clears pendingStepFocusId on
mount.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4KQV8enMqyweDppzHqcDm
…in reasoning cap

The openrouter branch used createOpenAI()(id), which defaults to the OpenAI Responses API (/responses) and is rejected by OpenRouter with a misleading 401 "User not found". Use .chat() (mirrors the mimo branch). Add opt-in FLUXOR_OPENROUTER_REASONING_EFFORT to cap unbounded reasoning (e.g. Kimi K3) via a request-body fetch wrapper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RShBDkQ5Cz2WoCnQj7D4ci
…ruth

Anonymize model_under_test in the judge prompt to kill self-preference bias; add opt-in FLUXOR_JUDGE_MODELS ensemble (median per-dimension aggregation + majority verdict); wire real axe-core structural a11y (design-verifier) as groundTruth.a11y for the team-work suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RShBDkQ5Cz2WoCnQj7D4ci
…Coverage contract

The guardrail no longer surrenders early on a stall; it escalates the deterministic corrective (literal filenames) and retries until maxAttempts. Add StepContract.haltOnBreach to halt instead of cascading on a broken foundation. Add StepContract.localeCoverage: verify every t() key is defined in a catalog and, optionally, actually translated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RShBDkQ5Cz2WoCnQj7D4ci
…ocale mods

Scope auth-guarded to protected surfaces (public landing + auth pages must stay unguarded); a11y-enforcer landmark uniqueness; web-vitals no-binary-asset rule. Reframe i18n-ready as the i18n technology mod (flat dotted catalogs) and add per-locale completeness mods locale-en/locale-es, each carrying its own localeCoverage contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RShBDkQ5Cz2WoCnQj7D4ci
…steps)

Define public/protected route topology (/ public, /app guarded) and add i18n/landmark/inline-SVG guidance; decompose SEO into a dedicated seo-head step and i18n into i18n-source-en + i18n-translate-es (tech x per-locale mods); haltOnBreach on scaffold + maxAttempts bumps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RShBDkQ5Cz2WoCnQj7D4ci
…pp E2E

@electron-forge/plugin-vite does not copy node_modules when packaging: its own
packageAfterCopy writes a package.json and nothing else. Every module
vite.main.config.ts keeps external therefore vanished from the installed app.
The packaged asar's only top-level entries were /.vite and /package.json, so
the main process threw MODULE_NOT_FOUND on the hoisted require("better-sqlite3")
before any handler was installed and NEVER opened a window. Invisible in
development, and invisible to the existing E2E suite too, which launches the dev
bundle from the repo root (e2e/global-setup.ts).

forge.config.ts now copies the dependency closure of better-sqlite3, jsdom and
playwright, computed by walking manifests instead of hand-listing (jsdom alone
pulls 37 transitive packages). Resolution follows Node's ancestor walk, not just
the immediate directory: jsdom keeps a nested tr46@6 that requires punycode,
which a hoisted-only walker never sees — jsdom then dies with "Cannot find
module 'punycode/'" in the package while resolving fine in dev. asar also gains
unpack: '**/*.node', since dlopen cannot load a library from inside an archive.

e2e/packaged-app.spec.ts is the harness that can see this class of defect: it
launches the packaged binary and asserts the window opens, the renderer comes
from file:// and not a dev server, db:status round-trips through the native
addon, and playwright/playwright-core still resolve (both lazy, so startup
would not notice them missing). It isolates BOTH its userData dir and its cwd —
--user-data-dir alone is not enough, since startup runs
migrateLegacyDirectories(process.cwd()), which renames directories in whatever
cwd it is given.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4jBQV1oevWP438MT8S2Lk
The job already runs `electron-forge package` (it needs .vite/build for the
dev-bundle specs), so the packaged app is sitting right there unused. Every
other spec launches Electron against the repo root, which makes the whole suite
structurally blind to defects that only exist once packaged — the reason the
missing-node_modules break shipped unnoticed. The spec self-skips when no
package is present, so it stays free for anyone running plain `npx playwright
test`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4jBQV1oevWP438MT8S2Lk
`@javadaba/daba-engine` becomes `@cmolg/daba-engine`, and the dependency is now
pinned to the EXACT published version (`0.2.0`, no `^`) instead of `^0.1.0` — a
range that could not even match what exists, and that an installable must not
use anyway: two builds of the same commit could otherwise ship a different engine.

Why the scope changed: GitHub Packages ties the npm scope to the login owning the
repository. The engine's source lives in `CMolG/javadaba-webpage`, so `@javadaba/*`
answered `403 permission_denied: create_package`; no token fixes that, since the
Actions `GITHUB_TOKEN` also authenticates as `CMolG`. The `javadaba` login has
been held since 2011 by an unrelated third party and GitHub shares one namespace
between users and organisations, so an org of that name was not available either.
The user's decision (2026-08-07): private packages go to `@cmolg` on GitHub
Packages, public ones stay `@javadaba` on npm. With the scope fixed, 0.2.0 is
published, so this repo stops depending on `npm link` and installs for real.

⚠️ This is a PUBLIC repo now depending on a PRIVATE package: whoever clones it
without registry access cannot `npm install`. That is the accepted trade-off of
ADR 0003 (closed engine, open Fluxor) and it is now stated outright in `.npmrc`
rather than left to be discovered.

28 files, 42 occurrences. Verified against the registry (real directory, not a
symlink; lockfile pins `npm.pkg.github.com` with integrity; only `dist/` ships,
no source): 116 suites / 1832 tests green, the engine smoke test among them.

NOTE FOR WHOEVER OWNS THE WIP ON THIS BRANCH: nine files had uncommitted work in
progress (HUD widget layer and friends, plus `tsconfig.json` paths). This commit
carries ONLY the rename for them — their content was taken from HEAD and renamed,
so the working tree keeps that work untouched and still uncommitted. The
`tsconfig.json` engine paths are part of that WIP; they have been renamed in the
working tree but are NOT in this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4jBQV1oevWP438MT8S2Lk
Un README describe lo que alguien creyo cierto el dia que lo escribio. Si lo que
describe cambio DESPUES de su ultima actualizacion, esta obsoleto y se corrige
en el mismo cambio que lo dejo desfasado.

Y actualizar no es solo anadir: a menudo lo correcto es borrar una seccion de
algo que ya no existe, o corregir una que cambio. Un README que solo crece acaba
siendo un museo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4jBQV1oevWP438MT8S2Lk
…aph updates

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UdGUj3YQrsigqpnrJaDNHx
… (F1)

Una ventana 'agent-session' aloja el CLI del proveedor (claude/codex/opencode/
gemini) en un PTY real: registro de proveedores + PtyManager con spawn inyectado
en src/main/pty/, xterm.js en AgentSessionApp, selector en el dock y log por
sesion en userData/sessions/.

node-pty es N-API y carga bajo Electron 41 sin electron-rebuild, pero su
prebuild trae spawn-helper en modo 644 y todo spawn muere con "posix_spawnp
failed."; lo repara un postinstall y lo verifica scripts/pty-smoke.cjs.
Un binario ausente se detecta con `which` (mismo mensaje que un cwd malo) y
vuelve como valor vendor_not_found, no como excepcion.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QojUyqMC7XjBngED3zjsfX
…sión adjunta (F2)

Una sesión abre en su propio worktree y su propia rama antes de que arranque el
agente: crear en .claude/worktrees/<nombre> desde un origin/main recién traído
(si el fetch falla se sigue con el ref local y se informa cuál se usó), instalar
por lockfile con override por proyecto, y al terminar decir si el worktree está
gastado — con la prueba de rama-al-dia.sh, contenido y gh, nunca ascendencia,
que es lo que miente tras un squash. El borrado se quita el directorio y CONSERVA
la rama.

Y una sola sesión adjunta por proyecto (invariante 16 hecha mecanismo): la
segunda no se bloquea, se ofrece abrirla en un worktree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QojUyqMC7XjBngED3zjsfX
…ck y carril Needs you (F3)

Cuarto `LaunchMenu`: proveedor (no instalados deshabilitados con el motivo) y
modo worktree/attached, cada uno con su razón cuando no aplica. Prompt de
lanzamiento propio (≤1.200 car.), sin persona ni system prompt.

Un fichero, dos claves: el agente mueve `status`, el Cockpit escribe `runState`
—running cuando el PTY existe de verdad, completed/failed según lo que el agente
dejó, no según el código de salida—. En worktree escribe la copia del worktree y
espeja su `status` en la tarjeta.

Las HUMAN salen de la pila a un carril fijo con lo que desbloquean y sin
lanzador; una nueva se convierte en aviso. Arreglado de paso el registro no
idempotente de `registerPtyIpcHandlers` que F2 dejó anotado.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QojUyqMC7XjBngED3zjsfX
…iones (F4)

Endpoint `http` en loopback con token por sesión en la ruta: 200 {} inmediato,
404 para todo lo demás (nunca 401: confirmaría el id), 413 y 400. Los cinco
hooks viajan como `--settings` en línea —no se escribe ningún fichero— y se
parsean las DOS grafías, porque el binario y la documentación no coinciden.

La atención es una máquina pura. Armado es una afirmación, recibido es la
prueba: hasta el primer evento mandan las heurísticas de F1, y la ventana avisa
si nada llega. Un aviso por tramo de espera, nunca por evento.

Preset Cockpit (tablón a la izquierda, rejilla a la derecha, sin solapes) y
lista de sesiones con Focus y End. e2e con el fixture posteando por curl.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QojUyqMC7XjBngED3zjsfX
…a en el worktree (F5)

Un worktree se corta de `origin/main`, asi que una tarjeta que aun no ha
llegado alli no esta en el checkout y la primera linea del prompt apuntaba
a un fichero ausente. `seed-card.ts` la copia entre `worktree-create` y el
bootstrap: nunca sobrescribe, crea `.backlog/` si falta y lo dice en la
terminal. Con ella, la costura `FLUXOR_AGENT_EXTRA_ARGS_<VENDOR>`.

La prueba de campo (`e2e/field/`) esta saltada salvo `FLUXOR_FIELD_TEST=1`:
gasta tokens de verdad. Tres tarjetas PROBE en paralelo sobre javadaba-web,
las tres cerradas por el agente. Informe en `.harness/reports/` de ese repo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QojUyqMC7XjBngED3zjsfX
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