67c0fcad114fa1b0bac09727ce41ff058d1d402d
202 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2cd34c82da |
feat(tahuantinsuyu): fase 18 — aspect list + hover tooltips + dignidades + export SVG
Fase grande con 4 features que aprovechan toda la infraestructura ya
construida. Engine ganó 2 módulos nuevos (dignity table data-only +
svg_export), el RenderModel se enriqueció con AspectSummary y los
glyphs con dignity_marker, y el canvas trae hit-test pasivo + lista
textual + botón de export.
## C — Lista textual de aspectos
- engine: nuevo `AspectSummary { module_id, from_body, to_body, kind,
orb_deg, applying }` + campo `aspect_summary: Vec<AspectSummary>`
en RenderModel. populate_natal_aspect_summary y
populate_cross_aspect_summary se llaman desde compose por cada
pasada (natal + 4 overlays). Ordenado por orb_deg asc (los más
exactos primero).
- canvas: nuevo aspect_unicode helper (☌ ☍ △ □ ⚹ ⚻ ⚺ ∠ ⚼ Q bQ).
Footer agrega un grid flex-wrap con las top 12 entries del summary,
cada una formateada como "[module_id] ☉ △ ☾ · 2.3° A" coloreado
por palette.aspect(kind).
## A — Tooltips al hover
- canvas: nuevo HoverInfo { module_id, symbol, deg, house, retrograde,
dignity_marker, annotation, local_x, local_y } + state.hover.
on_hover_check ejecuta hit-test sobre todos los glyphs Bodies +
Outer (threshold 14px); se llama desde el handler MouseMoveEvent
cuando NO está dragging (handler reescrito para soportar drag y
hover en mismo callback). Cuando mouse sale del wheel, hover=None.
- Tooltip absoluto: "☉ Tauro · 23.4° · Casa 5 · ℞" con border
angle_highlight. Posición offset arriba-derecha del planeta,
clampada al wheel para no salirse.
## B — Dignidades esenciales clásicas
- engine: nuevo mod `dignity` con `Dignity { Rulership/Exaltation/
Detriment/Fall }` + tabla rules_classical (7 planetas tradicionales)
+ exalts_at table. `essential_dignity(body, sign_index) -> Option`.
4 tests cubren rulership/detriment/exaltation/fall + edge case
modernos (Urano/Nept/Plutón sin dignidad clásica). 4 markers:
+ (domicilio), · (exaltación), − (exilio), * (caída).
- engine: Glyph gana campo `dignity_marker: Option<String>`. Default
derive en Glyph para no romper N construction sites. bridge::
annotate_dignities mutua RenderModel post-build agregando markers
a glyphs natales según el signo de cada placement.
- NatalOptions agrega show_dignities. NatalModule.controls() agrega
Toggle "Dignidades esenciales (+ · − *)" default false.
- canvas: glyph render append dignity_marker al texto después del ᴿ.
## D — Export SVG
- engine: nuevo `pub mod svg_export` con `render_to_svg(&RenderModel)
-> String`. Reproduce la geometría del canvas en un SVG standalone
800×800 escalable: anillos zodiacales, cusps, planetas con
retrograde+dignity markers, aspectos coloreados por kind (cross
conocen rings de origen/destino vía aspect_radii), labels ASC/MC/
DESC/IC. Sin dependencias nuevas — write! sobre String. Test
asserts well-formed XML.
- canvas: CanvasEvent::ExportSvgRequested + botón pequeño "⬇ SVG"
en el header del wheel (al lado del title).
- shell: on_canvas_event ExportSvgRequested → export_current_to_svg
recompose actual + svg_export::render_to_svg + write a
$XDG_DATA_HOME/tahuantinsuyu/exports/<label>_<short_id>.svg.
Ruta logueada a stderr para que el usuario encuentre el archivo.
`cargo check` y `cargo test` verdes con 8 tests en engine
(2 existentes + 4 dignity + 1 svg + 1 mock).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
0ae622550d |
feat(tahuantinsuyu): fase 17 — filtros de aspectos + editor + cleanup + labels
Fase completa con 4 mejoras independientes que reusan toda la
infraestructura previa:
## A — Filtros de aspectos en NatalModule
NatalModule gana 3 controles nuevos que SÍ recomponen (a diferencia
de los show_* que solo togglean visibilidad):
- Toggle "Mayores (☌ ☍ △ □ ⚹)" default true
- Toggle "Menores (quincunx, semi-…)" default false
- Slider "Multiplicador de orbe" range 0.25..2.5 step 0.25 default 1.0
Engine API extendida sin romper la existente:
- pub struct NatalOptions { show_majors, show_minors, orb_multiplier }
- pub fn compose_with_options(chart, offset, requests, &NatalOptions)
- compose() queda como wrapper con NatalOptions::default()
- bridge::compose acepta el natal_options, construye OrbTable escalada
(build_orb_table multiplier) y filtra aspects antes de pasarlos a
build_render_model. Build_render_model dejó de filtrar majors
internamente — ahora respeta lo que recibe.
Shell wire:
- build_natal_options() lee aspect_majors/aspect_minors/orb_multiplier
desde module_configs["natal"] con defaults seguros.
- on_panel_event para natal: si key empieza con "show_" → canvas
visibility (sin recompose); otherwise → update module_configs +
persist + render_current.
- render_current pasa natal_options a compose_with_options.
## B — Editor de carta natal existente
- Store::update_chart(id, label, &birth, &config) — actualiza tres
columnas preservando id/contact_id/related/created_at_ms y todo el
module_state asociado (la FK CASCADE no se dispara por UPDATE).
- Tree: Modal::EditChart { id, form, error } reusa ChartForm que ya
manejaba el create. open_edit_chart(id, w, cx) lee la carta con
store.get_chart, pre-carga cada TextInput con el valor existente
(label, birthplace, año, mes, día, hora, min, tz, lat, lon, alt).
submit_modal::EditChart lee form, llama update_chart, preserva el
config existente (zodiac/house_system/bodies no se editan acá).
Menú contextual del chart agrega "Editar…" entre "Abrir" y
"Renombrar".
- render_chart_form ahora toma `title: &str` parameter para que el
modal muestre "Editar carta natal" vs "Nueva carta natal". El
botón cambia "Crear carta" → "Guardar cambios" según el title.
## C — Single source of truth para OUTER_RING_MODULES
- engine exporta `pub const OUTER_RING_MODULES: &[&str] = &["transit",
"synastry", "planetary_return"]`
- shell elimina su const local, importa del engine
- canvas elimina 4 listas hardcodeadas (paint_wheel outer ring active
check + glyphs overlay + aspect_endpoints match) y usa contains() o
early-return sobre el slice. Próximo módulo outer-ring solo necesita
agregarse al const, no buscar copias.
## D — Labels ASC/MC/DESC/IC en el perímetro
Cuatro centered_glyphs en radii.sign_outer * 1.06 (justo afuera del
dial zodiacal, dentro del WHEEL_MARGIN) con color angle_highlight y
font 10px. El ojo identifica los 4 ángulos inmediatamente sin tener
que mapear la línea radial gruesa al ángulo correspondiente.
Las posiciones rotan con la rueda (drag del jog-dial los lleva).
`cargo check` y `cargo test` verdes. La fase agregó 6 controles
visibles al panel del NatalModule (4 view + 2 aspect filter + 1
slider) sin tocar la arquitectura de fases 6-15.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
cabdb2927e |
feat(tahuantinsuyu): fase 16 — polish místico (gradient sutil + glow en luminarias)
Dos toques pequeños que dan profundidad visual sin saturar: 1. **Gradient diagonal en el fondo del wheel**: linear_gradient 155° desde palette.dial_ring @ alpha 0.06 hasta palette.angle_highlight @ alpha 0.03. La opacidad mínima asegura que no compite con la geometría pintada encima; el efecto se ve sobre todo en las esquinas del cuadrado (afuera del círculo) y en los gaps entre anillos cuando no hay overlays. Da "shimmer mineral" muy velado. El wheel además ahora tiene rounded(12px) — perfila el cuadrado sin que se sienta como un container. 2. **Glow halo en luminarias natales**: paint_glow nuevo helper que pinta 3 fill_circle concéntricos con (radius × 5/3/1.8, alpha 0.05/0.12/0.22). Aplicado solo a Sol y Luna del layer natal — son los puntos psicológicamente cargados y los que el ojo busca primero. El resto de planetas mantiene su dot limpio. GPUI 0.2 no tiene radial_gradient nativo así que el shading concéntrico discreto cubre el rol. - canvas: imports linear_color_stop + linear_gradient. paint_glow() helper. Loop de Bodies en paint_wheel detecta is_natal && (sun|moon) → paint_glow antes del fill_circle del dot. Convive con todo lo anterior: si la luminaria está oculta (toggle [P] off), el glow también; si hay overlays con sus propios planetas, solo las luminarias natales lucen halo (diferenciable de su contraparte en transit/synastry/return). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
1232e39397 |
feat(tahuantinsuyu): fase 15 — badges de overlays activos en el footer
Cuando hay overlays activos, debajo del info_row aparecen pills con
los nombres de cada uno (Natal, Tránsito ahora, Progresión 38.2a,
Sinastría · Ana, Saturn return 29a) — el usuario ve de un vistazo qué
está mirando sin tener que mapear los anillos manualmente.
El border de cada pill toma color según a qué slot del wheel
pertenece: outer ring (transit/synastry/planetary_return) →
palette.angle_highlight (dorado), inner overlays (progression/
solar_arc) → palette.house_cusp (tono apagado), natal → neutro.
Permite leer la pila de izquierda a derecha y ubicar visualmente cada
glyph del wheel.
- engine: nuevo OverlayMeta { module_id, label } + campo overlays:
Vec<OverlayMeta> en RenderModel. build_render_model lo inicializa
vacío; bridge::compose pushea un OverlayMeta por cada
PipelineRequest después de su build_*_overlay correspondiente. Helper
push_overlay_meta(render, id, label). Labels: "Tránsito ahora",
"Progresión {age:.1}a", "Solar Arc {age:.1}a", "Sinastría · {name}"
(lee partner_chart.label antes de mover el Box al builder),
"{Body} return {age:.0}a" (usa eternal_sky body.name()).
- canvas: render_wheel separa el viejo footer en info_row (Asc/MC/ms +
offset + hotkeys) y un badges_row opcional. badges_row aparece solo
cuando render.overlays != empty. Pill helper centralizado: bg
panel_alt, border 1px, text size 10, rounded 10. Border color
decidido por module_id para correlacionar con el ring visual.
Compatible con compute_mock (que setea overlays = vec![] — ningún
mock badge). Persiste sin cambios — los configs siguen guardando su
estado, los OverlayMeta se reconstruyen en cada compose desde los
requests activos.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
6d572c81ca |
feat(tahuantinsuyu): fase 14 — Return abstracto + Control::Select interactivo
El módulo SolarReturn se generaliza a PlanetaryReturn parametrizable
por cuerpo (Sun/Moon/Mercury/Venus/Mars/Jupiter/Saturn/Uranus/Neptune/
Pluto). Validado contra `Control::Select`, ahora interactivo como
tercer tipo de control draggable (después de Toggle/Slider/ChartPicker).
Refactor estructural: el dropdown del ChartPicker pasa a ser
infraestructura compartida — chart_picker_value/chart_picker_open
desaparecen, reemplazados por string_state/dropdown_open que sirven
a CUALQUIER control basado en string (picker + select).
render_chart_picker y render_select ahora son thin wrappers sobre
render_dropdown(options, include_auto).
- engine:
- PipelineRequest::SolarReturn → PipelineRequest::PlanetaryReturn
{ body: String, target_age_years }. Body como string agnóstico
(sun/moon/jupiter/...) que el bridge mapea a eternal_sky::Body
vía map_body — el mismo helper que ya usa StoredChartConfig.
- build_solar_return_overlay → build_planetary_return_overlay con
parameter `body: Body`. next_return acepta cualquier body, así que
Moon return (mensual) y Saturn return (29 años) funcionan igual.
Mensajes de error incluyen body.name() para diagnóstico.
- modules:
- SolarReturnModule → PlanetaryReturnModule (mod planetary_return).
id "planetary_return". Controles: toggle "enabled" + Select "body"
con 10 opciones de cuerpo (Sol → Plutón) + Slider edad. label
"Retornos planetarios".
- panel:
- Refactor: chart_picker_value/chart_picker_open → string_state/
dropdown_open (compartido entre ChartPicker y Select).
- set_string(module_id, key, value, cx) — API unificada. set_chart_picker
queda como alias retrocompatible.
- render_dropdown(options, include_auto, …) — helper común. picker
pasa include_auto=true (muestra "(automático)" + separador);
select pasa include_auto=false (las options son la única opción).
- render_select implementado — el botón muestra la option's label
(no value); click abre dropdown; click en opción emite ControlChanged
con Value::String(option.value).
- shell:
- OUTER_RING_MODULES const: "solar_return" → "planetary_return".
- build_requests para planetary_return: lee body string del
module_configs (default "sun"), arma PipelineRequest::PlanetaryReturn.
- apply_selection inicializa target_age + body=sun default para
planetary_return.
- sync_panel_from_configs strings → set_string (era set_chart_picker).
Probarlo: en el panel del módulo "Retornos planetarios", click en el
dropdown "Cuerpo" abre el popup; click en "Saturno" + slider en 29
años + toggle "Activar" = ves la carta del primer retorno de Saturno
(cuando recién terminaba la primera vuelta) en el outer ring con
cross aspects al natal.
NOTE: La persistencia con id "solar_return" de fase 13 queda huérfana
en la DB de los users que ya hayan probado. No es destructivo —
simplemente esas rows quedan sin módulo que las lea. Pre-1.0.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
8d95833c20 |
feat(tahuantinsuyu): fase 13 — Solar Return como sexto overlay
Sexto módulo siguiendo el patrón establecido. Cambio estructural:
3-way mutual exclusion para los módulos que comparten el outer ring
(transit + synastry + solar_return). Constante OUTER_RING_MODULES
abstrae el grupo para que fase 14+ pueda sumar lunar return / planet
returns sin tocar la lógica del shell.
- engine: PipelineRequest::SolarReturn { target_age_years } +
build_solar_return_overlay. Llama eternal_astrology::next_return
(Sun back to natal Sun, ventana ±1.5 años) desde un instante
~30 días antes del cumpleaños target. Computa la carta natal
completa al return_instant (mismo observer + config natales —
convención clásica) y la apila como Outer + Aspects cross natal ×
return. z=12/13. Import: `next_return` añadido a la lista de
re-exports del bridge.
- modules: solar_return::SolarReturnModule (id "solar_return", toggle
+ slider target_age_years 0..120 step 1.0). Registry pasa a 6
módulos para Natal.
- shell: OUTER_RING_MODULES const con los tres ids; mutual exclusion
generalizada de pair-wise a N-way (for-loop sobre el slice). Init
de age en apply_selection ahora incluye solar_return. build_requests
agrega la rama. Misma estructura que progression/solar_arc en la
rama de age handling.
- canvas: aspect_endpoints("solar_return") = (bodies, transits). Tres
loops del outer ring (paint dots, paint glyphs, anillos guía) ahora
aceptan los tres module_ids.
Probarlo: en el panel, slider "Edad del retorno" en valor entero (ej.
36) + toggle "Activar" = ves la carta del año cuando volviste a tu
Sol natal a los 36, con todos sus planetas en el outer ring y cross
aspects con tu natal. Cambiando el slider podés explorar año por año.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
d9e21fbedc |
feat(tahuantinsuyu): fase 12 — Control::ChartPicker para Synastry
Cualquier carta del DB se puede elegir como partner de sinastría
(no solo hermanas del contacto actual). El módulo SynastryModule
declara un Control::ChartPicker; el panel renderea un dropdown que
abre un popup con todas las cartas; el shell inyecta las opciones y
resuelve el partner desde la selección persistida.
- modules: nueva variante Control::ChartPicker { key, label } sin
default — las opciones las inyecta el host. SynastryModule.controls()
agrega un picker con key="partner_chart_id".
- store: list_all_charts() — query sin filtros, ordenada por label
case-insensitive. Pensada para selectores cross-contact.
- panel:
- ChartOption struct público (id + label).
- chart_options Vec + chart_picker_value HashMap + chart_picker_open
Option (solo uno abierto a la vez).
- APIs públicas set_chart_options / set_chart_picker para sync.
- set_active_kind inicializa picker_value a None ("automático").
- render_chart_picker: botón "▾ label" en bg_button; al click toggle
un popup absolute con (automático) + separador + cada chart_option
clickable. select_picker emite ControlChanged con Value::String(id)
o Value::Null.
- shell:
- new() llama refresh_chart_options al final para popular el panel
desde el boot.
- refresh_chart_options() builds Vec<ChartOption> desde
list_all_charts (label + birth_brief "YYYY-MM-DD · Lugar"). Lo
llamamos también desde TreeEvent::HierarchyChanged para que el
dropdown refleje altas/bajas.
- resolve_synastry_partner reemplaza find_synastry_partner: 1) lee
module_configs["synastry"]["partner_chart_id"] y resuelve el chart;
2) fallback al automático (primera hermana). El fallback significa
que el módulo sigue funcionando sin elegir manualmente.
- sync_panel_from_configs ahora maneja Value::String / Value::Null
→ set_chart_picker, así el picker se restaura al cargar una carta.
Persistencia: el partner_chart_id va al config_json (fase 11), así
que cada carta recuerda con quién hizo sinastría la última vez.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
22e6ed6a71 |
feat(tahuantinsuyu): fase 11 — persistencia de module_configs por carta
Los toggles, sliders y partner pickers de cada overlay (transit,
progression, solar_arc, synastry) ahora persisten por carta en la
tabla SQLite `module_state` (que estaba creada desde fase 1 pero
sin cablear). Cambiar de carta y volver mantiene exactamente el
estado que el usuario dejó.
- shell:
- apply_selection(Chart): tras setear defaults (target_age_years =
edad actual), llama load_persisted_module_states(chart.id) que
mergea sobre los defaults los valores guardados. Luego
sync_panel_from_configs empuja todos los toggles/sliders al
panel para reflejar el estado restaurado. Render al final.
- load_persisted_module_states: lee list_module_states(chart_id),
reconstruye el JSON combinado (mergea `enabled` de la columna
SQL en el config), y lo mergea sobre lo que ya hay en
module_configs. Vacant entries se insertan tal cual; occupied
se patchean field-a-field para no perder defaults no guardados.
- sync_panel_from_configs: itera module_configs, push toggle/slider
al panel por cada key Bool/f64.
- persist_module(module_id): extrae enabled del JSON, deja resto en
config_json, llama upsert_module_state. Invocada desde
on_panel_event "else" tras cada update + desde on_canvas_event
para [T] + tras auto-disable del conflicting module en mutual
exclusion.
- store: nuevo test module_state_roundtrip que cubre upsert/list +
cambio de enabled vía upsert (UPSERT clause de fase 1 vuelve a
validarse).
Flujo de usuario: ajustás el slider de progresión a 42.5 años,
activás synastry, cambiás de carta, volvés — todo está como lo
dejaste. La DB persiste por chart_id, así que distintos sujetos
mantienen estados independientes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
97a6aab883 |
feat(tahuantinsuyu): fase 10 — Sinastría como overlay (bi-wheel con carta hermana)
Quinto módulo overlay. Cuando hay otra carta hermana del mismo
contacto, la sinastría pone las posiciones del partner en el outer
ring + dibuja cross aspects entre las dos personas. Mismo molde que
los overlays anteriores; única novedad: el PipelineRequest transporta
una `Chart` completa porque el partner no es derivable de la natal.
- engine: PipelineRequest::Synastry { partner_chart: Box<Chart> }.
build_synastry_overlay(natal, partner_chart, render) llama
compute_natal_chart sobre el partner y find_synastry_aspects entre
los dos NatalCharts (sólo majors). Layers con module_id="synastry"
y z=10/11. Reusa la helper compute_natal_chart de fase 5.
- modules: synastry::SynastryModule (id "synastry", toggle "Activar"
sin hotkey por ahora). Registry agrega el quinto built-in. Test
pasó a 5 módulos aplicables a ChartKind::Natal.
- shell: build_requests detecta synastry.enabled y llama
find_synastry_partner — busca la primera carta hermana del contacto
actual (mismo contact_id, distinto chart_id). Si no hay hermana,
skip silencioso. Mutual exclusion: al prender transit o synastry
se apaga el otro automáticamente (comparten outer ring) — sincroniza
el toggle del panel + el layer_visibility del canvas.
- canvas: Radii::aspect_endpoints("synastry") devuelve (bodies,
transits) — same slot que transit. Loops del outer ring aceptan
module_id "transit" OR "synastry" (paint_wheel + glyph overlay).
Sin radii nuevo — visualmente comparten el ring 0.82 con transit.
Para probarlo: creá dos cartas en el mismo contacto (ej. el sujeto +
su pareja). Abrí la primera y activá "Sinastría" en el panel. Verás
los planetas del partner en el outer ring + líneas que cruzan al
centro mostrando los aspectos entre las dos personas. Si tenés
transit prendido cuando lo activás, se apaga; al revés también.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
1a3bc55016 |
feat(tahuantinsuyu): fase 9 — Solar Arc como segundo overlay
Confirma que la arquitectura de fase 6 escala: tres overlays simultáneos
(transit + progression + solar_arc) sin acoplamiento entre módulos, y
sin tocar el flujo del Shell salvo registrar el nuevo branch.
Tres puntos de extensión por overlay nuevo (exactamente los predichos):
1. variante en PipelineRequest
2. helper build_*_overlay en bridge + match arm en compose
3. módulo declarativo en modules/ + registro
- engine: PipelineRequest::SolarArc { target_age_years: f64 } +
build_solar_arc_overlay que llama solar_arc_true(natal, session, age)
→ desplaza uniformemente cada placement y cusp por el arco solar
(default ≈1°/año, vía true progressed Sun). Cross aspects natal ×
dirigida vía find_synastry_aspects(majors). Layers con
module_id="solar_arc" y z=8/9 (sobre todos los demás).
- modules: solar_arc::SolarArcModule con id="solar_arc", toggle
"Activar" + slider target_age_years 0..120. Mismo shape que
ProgressionModule. Registry.with_builtins lo registra. Test pasó a
4 módulos aplicables a ChartKind::Natal.
- canvas: Radii.solar_arc = 0.40 (entre progression 0.48 y aspects),
aspects shrunk a 0.32 para hacer lugar. Helpers Radii::body_ring()
y Radii::aspect_endpoints() ahora reconocen "solar_arc". paint_wheel
itera ambos overlays (progression + solar_arc) para dibujar dots,
glyph overlays y anillos guía sutiles. Loop común `for (id, ring) in
[..]` evita duplicación de código.
- shell: build_requests detecta solar_arc.enabled, agrega request con
edad. apply_selection inicializa target_age_years para ambos
overlays (progression + solar_arc) en current_age + sincroniza los
sliders del panel. Helper module_age_or_current(id) factoriza la
lectura de edad con fallback.
Activando los tres overlays al mismo tiempo el canvas se convierte en
una rueda de cinco anillos: zodíaco (1.00), tránsito (0.82), natal
(0.66-0.78), bodies natal (0.58), progression (0.48), solar arc (0.40),
con líneas de aspectos cross convergiendo desde el ring natal hacia
cada overlay simultáneamente.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
e0c5c02b8e |
feat(tahuantinsuyu): fase 8 — slider interactivo + slider de edad en progression
Los `Control::Slider` del panel ya no son display-only — son arrastrables con el mismo patrón del splitter (canvas absoluto sobre el track + window mouse handlers en cada frame). El `ProgressionModule` ahora expone un slider de `target_age_years` (0..120) que el shell inicializa con la edad actual del sujeto al cargar la carta. - panel: SliderDrag struct + slider_state HashMap + slider_drag Option + métodos start/continue/end_slider_drag + apply_slider_position que calcula fraction desde la posición del mouse relativa al track y emite ControlChanged con el valor float. set_slider(module, key, val) para sincronización externa. set_active_kind ahora inicializa también los sliders desde sus defaults. render_slider pinta track + portion filled + thumb circular + canvas overlay con handlers de drag. Los Slider tienen un valor visible "X.X (min...max)" en el header. - modules: ProgressionModule agrega Control::Slider target_age_years con range 0..120, step 0.25, default 30 (placeholder — el shell lo reescribe con la edad real al cargar la carta). - shell: apply_selection(Chart) ahora calcula current_age, lo inserta en module_configs["progression"]["target_age_years"], y empuja al panel via set_slider. build_requests ya leía target_age_years desde el map (de fase 7), así que ahora el slider lo controla. Mecánica: si activás "Progresión secundaria", el slider arranca en la edad actual del sujeto. Arrastralo a la izquierda y la rueda recompone la carta progresada para esa edad simbólica — vas viendo cómo el sujeto "evoluciona" o "involuciona" a través de su línea temporal interna, con los planetas progresados moviéndose por el anillo interno y los cross aspects con la natal reorganizándose en tiempo real. Same pattern aplica de aquí en más para cualquier slider futuro (harmonic en NatalModule, target_year en SolarArc, orb_multiplier, …). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
42e09fd7cd |
feat(tahuantinsuyu): fase 7 — progresión secundaria como overlay (prueba de la arquitectura)
Valida que el refactor de fase 6 escala: agregar un overlay nuevo
(progresión secundaria, "día por año") tocó exactamente lo predicho —
una variante en PipelineRequest, un helper en bridge, un módulo
declarativo en `progression`, una línea en build_requests, y el canvas
para pintarlo. Cero cambios en el flujo de eventos del Shell.
- engine: PipelineRequest::SecondaryProgression { target_age_years: f64 }
+ build_progression_overlay(natal, age, render) que delega en
eternal_astrology::secondary_progression(natal, session, age), pinta
los placements progresados en un anillo interno (ring 0.48), y suma
cross-aspects natal × progresada vía find_synastry_aspects (sólo
mayores, opacidad × 0.7). z = 6/7 — sobre las capas natal y
transit.
- modules: progression::ProgressionModule con id "progression", toggle
"Activar" (sin hotkey por ahora). Registry::with_builtins lo agrega.
El test pasó de 2 a 3 módulos para ChartKind::Natal.
- shell: build_requests detecta progression.enabled, calcula la edad
decimal desde StoredBirthData y SystemTime::now() (current_age_years
helper, aproximación tropical) y arma el request con esa edad.
El resto del flujo del shell se mantiene — la abstracción funciona.
- canvas: Radii agrega `progression: r * 0.48`, `aspects` shrunk a
`r * 0.38` para hacer lugar. Helper aspect_endpoints(module_id)
resuelve el par (r_from, r_to) según natal/transit/progression.
paint_wheel pinta dots progresados con alpha 0.85 + anillo guía
sutil que delimita el slot. Glyph overlay pinta planet symbols en
el ring de progresión con font_size 14 y box 20 (menores que el
natal para diferenciar visualmente).
Probarlo: en el panel, activar "Progresión secundaria" — verás los
planetas progresados en un anillo interno con su retrogradación
marcada, y líneas de aspectos que cruzan desde el ring de cuerpos
natales hacia el ring progresivo. Combinable con tránsitos: ambos
overlays apilan capas en orden bodies → transits, sin colisiones.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
d4761bf238 |
refactor(tahuantinsuyu): fase 6 — Modules pluggables vía compose + PipelineRequest
El shell ya no carga el flag `show_transits: bool` ni hardcodea qué pipeline corre. La engine expone una sola API `compose(chart, offset, &[PipelineRequest])` que la shell alimenta a partir de un map `module_configs: HashMap<String, serde_json::Value>`. Los toggles de overlay (transit hoy, progression/synastry/solar_arc en fase 7) viven como módulos propios en el panel. - engine: PipelineRequest enum (variante Transit por ahora; comentarios con el roadmap de SecondaryProgression/SolarArc/Synastry). compose() es la nueva entrada canónica; compute / compute_at_offset / compute_with_transits_at_now quedan como atajos retrocompatibles que delegan en compose. bridge.rs refactor: extraído build_transit_overlay como helper que muta &mut RenderModel, listo para que más pipelines apilen capas encima. - modules: nuevo módulo `transit::TransitModule` (id "transit", toggle "enabled" con hotkey [T], applies_to Natal). Sacado el toggle show_transits de NatalModule — ahora cada módulo declara lo suyo. Registry::with_builtins() registra ambos. Test asegura los dos aplican a Natal. - panel: sin cambios — ya itera Registry::for_kind(kind) y renderea cada módulo aplicable con sus controls. La adición del TransitModule aparece automática como segunda card en el panel. - shell: replace show_transits por module_configs map. build_requests() deriva PipelineRequest::Transit cuando module_configs["transit"] ["enabled"] == true. on_panel_event: toggles del NatalModule afectan solo visibility del canvas; toggles de otros módulos van al module_configs y disparan render_current. on_canvas_event: [T] hotkey → flip transit.enabled + sync panel + recompose. apps Cargo agrega serde_json como dep directa. Todos los tests verdes. Fase 7 puede sumar overlays adicionales (progression, solar_arc) solo agregando variantes a PipelineRequest + helpers en bridge + módulos declarativos — sin tocar el shell. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4d14a4495f |
feat(tahuantinsuyu): fase 5 — overlay de tránsitos (bi-wheel natal × ahora)
Activá el toggle "Tránsitos (ahora)" en el panel (o hotkey [T] sobre el wheel): la engine computa una segunda NatalChart al instante SystemTime::now() con el mismo observer y dibuja un anillo externo de planet glyphs encima del natal, más las cross-aspects entre ambos charts (sólo mayores). Las líneas cross van del ring de cuerpos natales al ring externo de tránsitos, con stroke más fino y opacidad más baja para no taparle el ojo a las aspectos natal-natal. - engine/bridge.rs: extraídas build_eternal_inputs y compute_natal_chart como helpers reutilizables. Nueva compute_with_transits(chart, offset, transit_at) que llama find_synastry_aspects entre natal y transit (AspectKind::MAJORS). Atajo compute_with_transits_at_now usa ESInstant::now(). Las capas extra van con module_id = "transit" y LayerKind::Outer / LayerKind::Aspects para que el canvas las distinga. - engine/lib.rs: re-export de compute_with_transits_at_now con el mismo fallback al mock cuando feature `eternal-bridge` está off. - canvas: nueva Radii::transits = 0.82, layout del wheel re-balanceado (houses_outer 0.78, houses_inner 0.66, bodies 0.58, aspects 0.50) para hacer lugar al anillo externo sin colisiones. paint_wheel: detecta layers de transit por module_id, pinta dots + glifos en el anillo nuevo + anillos guía sutiles. paint_cross_aspect_line con stroke 0.7 entre los dos radios. Glyph overlay para Outer ring con alpha 0.9 y font_size más chico que el natal. Hotkey [T] en on_key_down toggle LayerKind::Outer. - modules: NatalModule.controls() agrega toggle show_transits con hotkey [T] (default false — no recomputar transits si nadie pidió). - shell: nuevo show_transits flag. render_current despacha entre compute_at_offset y compute_with_transits_at_now según el flag. on_panel_event traduce ControlChanged show_transits a flip + redraw. on_canvas_event: el toggle de LayerKind::Outer dispara show_transits flip + render (no es un visibility toggle puro). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
360797132e |
feat(tahuantinsuyu): fase 4 — jog-dial perimetral, hotkeys y panel interactivo
Time scrubbing por drag en el aro exterior del wheel: rota visualmente mientras dura el drag, al soltar traduce el delta angular a minutos (1° = 4 min sideral, CW = forward) y emite CanvasEvent::TimeOffsetChanged. La Shell recomputa con engine::compute_at_offset y el ascendant rotado queda en la nueva posición. Snap visual a 0° tras commit. - engine: nueva variante compute_at_offset(chart, minutes) que suma segundos al UTC base via add_seconds + Instant::from_utc y corre la pipeline normal. compute() es ahora wrapper con offset=0. - canvas: estado nuevo layer_visibility + drag_jog. Mouse handlers registrados desde el paint callback (mismo patrón que splitter/tiled). Hotkeys D/H/X/P toggle SignDial/Houses/Aspects/Bodies, R resetea offset. FocusHandle + click-to-focus para recibir teclas. Indicador ⏱ ±Xd HH:MM en el footer con color highlight cuando el offset != 0. paint_wheel + glyph overlays respetan layer_visibility (skip capas ocultas). - modules: NatalModule.controls() ahora expone show_sign_dial / show_houses / show_aspects / show_bodies con hotkeys [D/H/X/P], más el slider de armónico. - panel: ControlPanel mantiene toggle_state cache (module_id, key) → bool, inicializa desde defaults al cambiar de ChartKind. Click invierte el toggle visualmente y emite ControlChanged. Nuevo set_toggle(module, key, value) para que la Shell mantenga sync cuando el canvas se autotogglea por hotkey. - shell: nuevo current_chart + current_offset_minutes. render_current() delega a compute_at_offset. Suscripción a CanvasEvent traduce TimeOffsetChanged → re-render, LayerVisibilityChanged → panel sync. Suscripción a PanelEvent::ControlChanged traduce show_* keys a set_layer_visible sobre el canvas. Todos los tests verdes. La fase 5 sumará módulos extra (transit, progression, synastry, uranian) + extracción de eternal de lo que falte. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
f4944218e2 |
fix(tahuantinsuyu-engine): leer longitude_rate_rad_per_day directo
`BodyPlacement::is_retrograde` cambió entre versiones de eternal: en commits viejos es `pub fn is_retrograde(&self) -> bool`, en más nuevos es `pub is_retrograde: bool`. Cualquiera de las dos formas rompe la otra al usar `p.is_retrograde()` o `p.is_retrograde`. Leemos el campo crudo `pub longitude_rate_rad_per_day: f64` (estable en ambas) y aplicamos `< 0.0` localmente — el bridge queda inmune a ese refactor upstream. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
82fa370877 |
feat(tahuantinsuyu): fase 3 — engine real contra eternal + rueda pintada en GPUI
Bridge a eternal-astrology prendido por default. `engine::compute(chart)` abre una EphemerisSession VSOP2013 (cacheada vía OnceLock global), traduce los Stored* del modelo a BirthData/ChartConfig de eternal, corre NatalChart::compute + find_aspects(modern_western) y devuelve un RenderModel con cuatro capas: SignDial, Houses, Bodies, Aspects. - tahuantinsuyu-engine: bridge.rs nuevo con map_house_system, map_zodiac (incl. 8 ayanamshas), map_body_set, body_symbol, aspect_kind_id. compute_mock se mantiene como fallback sin feature. Errores tipados (EngineError::Eternal). Test real verde con datos natales de demo. - tahuantinsuyu-canvas: rewrite con gpui::canvas() + PathBuilder. Pinta: sectores zodiacales coloreados por elemento (Fire/Earth/Air/ Water), anillos de sign-dial/houses/aspects, cusps zodiacales, cusps de casas (con énfasis para Asc/MC/Desc/IC), líneas radiales hasta el centro para los ejes, líneas de aspectos coloreadas por kind con opacidad por orb, dots de cuerpos. Glifos unicode (♈-♓ signos, ☉-♇ planetas, ☊☋⚷⚸ puntos) como divs absolutos sobre el canvas. Marcador ᴿ cuando retrógrado. Rotación canónica: Asc a las 9, casas crecen contrarreloj. - shell: ahora llama engine::compute() real y reporta errores por stderr sin caer la app. Datos sintetizados: ascendente, MC, descendente, IC; 12 cusps de casa según el sistema configurado; placements de los cuerpos del BodySet con sus longitudes zodiacales, casa y flag retrógrado; aspectos mayores con opacidad proporcional al orb. `cargo check` y `cargo test --features eternal-bridge` verdes. La fase 4 traerá el panel interactivo (jog-dial, toggles, sliders, atajos teclado). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
bcb92b537e |
feat(tahuantinsuyu): fase 2 — CRUD UX sobre el tree (menú, modales, form natal)
Right-click sobre el explorador izquierdo abre menú contextual cuyas opciones dependen del target (raíz, group, contact o chart). Modales flotantes para crear/renombrar usando yahweh-widget-text-input; un form más completo de 11 campos para la birth data al crear cartas natales. Borrar pide confirmación por window.prompt nativo. - tahuantinsuyu-store: rename_contact, rename_chart, move_group, move_contact (los `move_*` para fase posterior de drag-to-nest). - tahuantinsuyu-tree: estado interno (Menu, Modal enum, ChartForm), handlers de ContextMenuRequested, render overlays. Soporta seis modales: rename de g/c/h, create group/contact, form natal completo con parseo + reporte de errores inline. Auto-expande el contact tras crear una carta. Nuevo evento TreeEvent::HierarchyChanged tras cada mutación. - shell: maneja HierarchyChanged sin propagar selección. `cargo check` y `cargo test` verdes. Fase 3 viene con engine real contra eternal-astrology + pintado de la rueda. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c48638fe87 |
feat(tahuantinsuyu): scaffolding del estudio astrológico (10 crates + ventana 3-panes)
Módulo nuevo `modules/tahuantinsuyu/` con 9 crates reusables + app `apps/tahuantinsuyu` ejecutable que abre la ventana del explorador y coordina los widgets: - tahuantinsuyu-card: Card Brahman + spawn_sidecar (flows chart-request/chart-result). - tahuantinsuyu-model: tipos agnósticos (Group/Contact/Chart, StoredBirthData, StoredChartConfig, ChartKind, TreeSelection). - tahuantinsuyu-store: persistencia SQLite (rusqlite) con migración v1, CRUD por entidad y descenso recursivo `charts_under_group`. - tahuantinsuyu-engine: bridge agnóstico al canvas vía `RenderModel` (Layer/Glyph/Geometry). Feature `eternal-bridge` (off por default) reservada para enchufar eternal-astrology desde ~/eternal. - tahuantinsuyu-modules: registry de módulos pluggables (Module trait + Control schema) con `NatalModule` placeholder. - tahuantinsuyu-theme: AstroPalette (elementos / modos / planetas / aspectos) con variantes dark + light sobre yahweh-theme. - tahuantinsuyu-canvas: widget GPUI con CanvasState (Empty / Wheel / Thumbnails). Render placeholder hasta cablear la rueda real. - tahuantinsuyu-tree: explorador izquierdo sobre yahweh-widget-tree, prefijos g:/c:/h: para Group/Contact/Chart. - tahuantinsuyu-panel: control panel inferior que lee Controls de los módulos del registry y los pinta. - apps/tahuantinsuyu: binario `tahuantinsuyu` (launch_app-style) con Shell coordinador (tree↔canvas↔panel), DB en $XDG_DATA_HOME. Workspace Cargo.toml actualizado con los 10 miembros. `cargo check` verde, tests unitarios verdes (model/store/engine/modules/theme/card). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
e8f97b50cb |
feat(gioser): luna con textura rica + terminador curvo, auras anchas, overlay nubes
Luna (FS_CHACANA::render_moon):
- Normales esféricas reales: nx=p.x/R, ny=p.y/R, nz=sqrt(1-nx²-ny²).
- Terminador CURVO: dot(normal, sun_dir) donde sun_dir gira en el plano
X-Z según la fase. Resultado: la frontera luz/sombra es una elipse
proyectada en pantalla, como en la luna real (no una vertical recta).
- Fase lineal: phi = fract(t/40) * 2π cicla new→first-q→full→last-q→new
en ~40s.
- Limb darkening realista: pow(nz, 0.45) — bordes más oscuros que el
centro (el regolito lunar dispersa).
- 6 capas de textura:
maria_n (escala 4.5) → mares oscuros (smoothstep 0.42..0.60)
craters_mid (escala 13) → cráteres grandes
craters_small (escala 28) → cráteres chicos
fine (escala 55) → granularidad del terreno
micro (escala 110) → polvo
ring_mid + ring_small → crests via pow(abs(n-0.5)*2, k) → bordes
elevados de cráteres
Albedo final: 0.80 + craters±0.32 + small±0.22 + fine±0.20 + micro±0.10
+ rings (+0.22, +0.16) - maria 0.50, clamp [0.10, 1.15].
Auras elementales (FS_CHACANA::element_cloud):
- sigma_along 0.42 → 0.62 (más reach hacia afuera)
- sigma_perp 0.34 → 0.62 (mucho más ancho perpendicular)
- cloud_center offset 0.22 → 0.28 (más lejos del centro)
- multiplier 0.28 → 0.26 (compensa intensidad por la mayor cobertura)
- Resultado: las nubes elementales se solapan en las esquinas NE/NW/SE/SW
y mezclan colores. El cuadrante entero respira el color del cardinal.
Overlay clouds (FS_OVERLAY_CLOUDS — nuevo shader):
- Tercer pase tras chacana, fullscreen quad.
- blend = SRC_ALPHA / ONE_MINUS_SRC_ALPHA (compositing normal, no aditivo)
→ las nubes COMPONEN sobre la escena en lugar de sumar luz.
- Dos capas FBM (escalas 0.55 y 1.30) con parallax inverso del mouse
(-0.05 y -0.09) — se sienten "delante" del cosmos.
- Drift más lento que las nubes del cosmos (0.020 vs 0.055), para que se
perciban como otra capa atmosférica.
- smoothstep(0.55..0.88, 0.50..0.82) → sólo crestas se vuelven nube;
mucho del viewport queda transparente.
- Alpha máximo 0.10 — "apenas visible" como pidió el diseño.
- Color mix gris→blanco-azul según densidad local.
Renderer (gioser-canvas-web):
- Nuevo Program overlay_prog con uniforms u_resolution/u_time/u_parallax.
- render() ahora hace 3 pases: cosmos → chacana → overlay clouds.
Workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
d94dcfb5af |
feat(gioser): ciclo sol↔luna↔tierra, auras anchas, nuevo set Software/Quién Soy/Manifiesto/Mística
Shader (gioser-shaders):
- 3 cuerpos centrales renderizados realísticamente con interpolación
gradual entre ellos (cross-fade smoothstep):
- render_sun: núcleo gauss + corona pulsante + textura de plasma FBM
(boiling surface).
- render_moon: disco con limb darkening, cráteres + mares (2 octavas
de fbm), terminador móvil (fase lunar), halo azulado en el limb
iluminado.
- render_earth: disco con continentes fbm (rotación lenta), polos
blancos, nubes en otra capa, día/noche en hemisferio iluminado,
halo atmosférico azul (Rayleigh simplificado).
- Uniforms u_body_a, u_body_b (int 0/1/2), u_body_blend (float).
- Cuerpo central se calcula sólo si inside > 0.001 (perf — saltea pixels
fuera de la superficie de la chacana).
- radial_mult atenúa los rayos cuando luna/tierra están activos — el sol
es el único que irradia tan intensamente.
- element_cloud(): aura ancha por cardinal (sigma_along=0.42,
sigma_perp=0.34) con textura fbm animada y modulación por elemento.
- AIRE: corrientes suaves que ondulan horizontalmente.
- FUEGO: lengüetazos rápidos con flicker.
- TIERRA: densidad sólida con variación lenta.
- AGUA: ondulaciones grandes que viajan hacia afuera.
Las nubes cubren todo el cuadrante del cardinal, no solo la punta.
- Helper functions vnoise_c + fbm_c agregadas (necesarias para superficies
realistas de luna/tierra y para nubes elementales).
Renderer (gioser-canvas-web):
- body_state(t) -> (body_a, body_b, blend) state machine:
- BODY_PHASE_SECS = 45 (≈10 pulsos del sol antes de transicionar).
- BODY_TRANSITION_SECS = 4 (cross-fade gradual).
- Total cycle: 147s = sol 45s → trans 4s → luna 45s → trans 4s → tierra 45s → trans 4s.
- Smoothstep cubic en el blend para curva natural (no linear).
- Sube u_body_a/b como int (uniform1i) y u_body_blend como float.
App + contenido:
- index.html: nuevos labels en los 4 tips
- NORTE (aire): SOFTWARE / Tecnología
- ESTE (fuego): QUIÉN SOY / Bitácora
- SUR (tierra): MANIFIESTO / Invariantes
- OESTE (agua): MÍSTICA / Espiritualidad
- Íconos SVG nuevos relacionados al tema:
- aire: chip de circuito con nodos y conexiones
- fuego: libro abierto con líneas
- tierra: hexagrama dentro de círculo (sacred geometry / invariante)
- agua: ojo en triángulo (mística)
- gioser-web src/lib.rs: ensure_page_dom usa nuevos title+tag por elemento.
- 4 md/*.md reescritos con contenido seed para los nuevos temas, con
manifiesto explícito en tierra.md.
Workspace verde + 21 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
7728013012 |
feat(gioser/web): fix mobile swipe, taskbar agnóstica, trazos zodiacales
Mobile drag fix (vista-web):
- pointermove listener ahora con `AddEventListenerOptions { passive: false }`.
Sin esto, en navegadores móviles `preventDefault()` es no-op y el browser
se traga el gesto horizontal como pan/scroll antes de que JS pueda
detectar la dirección y capturar el pointer.
- CSS: `.deck-strip` y `.deck-strip *` y `.deck-page` con
`touch-action: pan-y`. El touch-action del target inmediato es lo que
el browser consulta; sin esto, sobre un <p> dentro del strip el browser
asume `auto` y reclama horizontal.
Taskbar agnóstica (barra-web):
- Nuevo crate `crates/modules/barra/barra-web` que maneja sólo el LIST
dinámico de tareas; el resto del layout (home, brand, credits) es del
host. Misma filosofía que vista-web: separar lo reusable.
- API: Task::new(id, label).active() builder; TaskList::mount(ul) +
set_tasks/on_click/task_center. Click delegado, callback recibe
(id, cx, cy) en CSS pixels para origin de animaciones.
- Sanitiza IDs a [a-zA-Z0-9_-] y HTML-escapa labels.
- 3 tests unitarios.
- gioser-web refactoreado para consumir TaskList: sync_taskbar arma
Vec<Task> y delega; on_click del taskbar dispara minimize/restore_from_tab
según estado. install_taskbar reducido a sólo home buttons.
Trazos zodiacales (gioser-shaders + canvas-web):
- 12 líneas radiales muy sutiles entre la chacana y el aro principal, una
por signo, con colores significativos:
Aries→fuego rojo, Tauro→tierra verde, Géminis→aire amarillo,
Cáncer→agua plata, Leo→fuego dorado, Virgo→tierra marrón,
Libra→aire rosa, Escorpio→agua rojo profundo, Sagitario→fuego púrpura,
Capricornio→tierra verde oscuro, Acuario→aire celeste, Piscis→agua
verde mar.
- Aries empieza en el norte, giran en sentido horario (rueda zodiacal
clásica). Banda radial r∈[1.05*L, 0.96*ringR_main], gauss angular
con σ=0.0042 rad (~0.24° de ancho), multiplier 0.55 → apenas visible.
- Uniform `vec3 u_zodiac[12]` subido como array plano de 36 floats vía
uniform3fv. Constante ZODIAC_COLORS expuesta en canvas-web por si otros
callers la quieren.
Workspace verde + 21 tests (geom 6 + palette 4 + physics 3 + pluma-md 5
+ barra-web 3).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
62058ab193 |
feat(gioser): deck swipeable + minimize, brand+copyleft en taskbar (vista-web)
Nuevo módulo agnóstico: - `crates/modules/vista/vista-web` — Deck::mount(strip) instala swipe horizontal con snap a la página más cercana, estilo Flutter PageView. goto(idx, smooth) navega programáticamente. on_change(cb) fires tras snap. Drag decision: horizontal si dx > 8px y > 1.3*dy; sino cede al pan-y nativo (scroll vertical del contenido). resize listener ajusta --vista-offset sin animar usando .vista-instant un frame. App rediseñada: - Brand "GioSer" sacado del centro de la chacana → ahora en la taskbar junto al botón home (data-home). brand-dot dorado entre Gio·Ser. El centro de la chacana queda con sol limpio. - Copyleft + sergio@gioser.net a la derecha de la taskbar, abre https://sergio.gioser.net en nueva pestaña (target=_blank rel=noopener). - 4 drawers separados → reemplazados por un único `.deck` con `.deck-strip` vista-web manejado. Las páginas se crean dinámicamente al abrir un elemento por primera vez (`ensure_page_dom`). - Cada página tiene controles minimizar (─) y cerrar (×) arriba a la derecha, con ambience animada por elemento. - Click minimize → active=None, deck scale(0) hacia la cajita del taskbar (origin = bounding rect del taskbar-item). Página queda en memoria, tab sigue en la barra. - Click cajita del taskbar: - Si está activa → minimize (toggle). - Si está minimizada → restore con scale-up desde la cajita. - Click home / brand → minimize all (estilo Show Desktop, no destruye). - Swipe horizontal o click cajita → deck.goto(idx, smooth=true) con snap animado por vista-web. on_swipe sync de taskbar active state. - Cerrar página → remueve del strip + del Vec pages; si era activa, reemplaza por neighbor o hide deck si era la última. CSS: - Eliminado `.brand` fixed center y `.drawer` × 4 individuales. - `.deck` único + `.deck-strip` con `transform: translate3d(--vista-offset)` y transition transform 360ms cubic-bezier(0.22, 0.61, 0.36, 1). - `.deck-strip.vista-dragging` / `.vista-instant` → transition: none. - `.deck-page[data-element]` cada una con su page-ambience animada (aire-drift, fuego-flicker, agua-tide, tierra static). - `.taskbar-brand` Cinzel 1.3rem dorado + .brand-dot. - `.taskbar-credit` con `.copyleft-mark` (© con scaleX(-1) = copyleft visual). - `.taskbar-spacer { flex:1 }` empuja credit a la derecha. - `.taskbar-item.active` glow del color del elemento + border-bottom. - `body.deck-visible` baja opacity del canvas + esconde tips y brand. Workspace verde + 18 tests (geom 6 + palette 4 + physics 3 + pluma-md 5). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
5e0fcae4b4 |
feat(gioser): shake on click, mouseleave rebound, element particles, taskbar
Renderer (gioser-canvas-web): - Spring shake (SpringDamper1, 7.5 Hz / ζ=0.13) aplicado como rotación Z en el MVP. impulse_click() inyecta velocidad alternada → vibración fuerte con ~5 ciclos decayendo en ~0.8s. - release_tilt() pone target del tilt en (0,0) → la chacana cae al frente con el rebote natural del spring sub-crítico. - world_scale_for_aspect(): en portrait (aspect<1) escala baja proporcional para que el aro exterior no se corte por los lados. Base 1.05, piso 0.45. - click_radius_css_px() expone radio del aro en CSS-pixels desde el centro del canvas; la app lo usa para hit-test del impulso. - set_client_size() separa CSS-pixels de device-pixels (DPR). - tilt_degrees() ahora retorna (pitch, yaw, roll) — el brand replica los 3. - 4 nuevos uniforms u_aire/fuego/tierra/agua_color para el shader de partículas. Shader (gioser-shaders/FS_CHACANA): - Función element_particles(tip, outward, color, kind) → 4 partículas por cardinal con personalidad: AIRE drift+sway, FUEGO rise+flicker (siempre hacia +Y), TIERRA cae, AGUA ondula descendiendo. Gauss + envelope sinusoidal en la vida. ~16 partículas total, costo modesto. App (gioser-web): - pointerdown en canvas → si distancia al centro < click_radius_css_px → impulse_click(). Touch y mouse vienen unificados por PointerEvent. - mouseleave en canvas → release_tilt(). Sin set_target, el spring se quedaría en la última posición — ahora vuelve al frente con rebote. - position_tips ahora clampea raw_x/raw_y a [margin, viewport - taskbar - margin] en CSS pixels. Los botones NUNCA salen del canvas ni cubren la taskbar incluso en aspect extremos o tilt máximo. - AppState + TaskbarState (RefCell): trackea drawers abiertos + activo. open_tab/switch_tab/close_tab/home aplican mutación + sync(). - sync() rebuild de taskbar-list innerHTML por cada cambio de estado, más swap de body classes + drawer .open classes. - Click delegation en taskbar-list — un listener para todas las cajitas. - Botón home con data-home en la barra (svg de casa) cierra todo y limpia el taskbar. - Escape también cierra el drawer activo. - update_tilt_css ahora setea --tilt-z también — brand title roll visible en el shake. CSS: - .drawer bottom: 52px (reserva taskbar). - .taskbar full ancho fixed bottom, glass + gold border, scrollable horiz para muchas cajitas. - .taskbar-item con --task-color por elemento (aire/fuego/tierra/agua), .active glow del color + inset border bottom. - .taskbar-home con svg de casa dorado, hover glow. - Responsive: taskbar 46px en mobile + ajustes. - .brand transform agrega rotateZ(--tilt-z) para que el título vibre con la chacana en click impulses. Workspace verde + 18 tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
fce630c8d0 |
feat(gioser): sol detrás, título central, drawers MD + pluma agnóstico
Visual de la chacana retrabajado contra chakana.png de referencia:
- Sol detrás (gauss + corona, masked al interior de la chacana — sólo
asoma por la superficie de la cruz, no se cuela afuera).
- Doble outline dorado (línea principal + paralela offset 0.020), color
CHACANA_LINE pasa de cyan helado a dorado-ámbar del logo.
- Interior con niebla violeta-noche (u_dark_color) y rayos radiales
sutiles desde el centro, modulados por sin(t * 0.3).
- Aro doble exterior: ring fino interior + ring grueso con 4 grupos de
3 puntos cardinales (calculados angularmente, no rayos largos).
- WORLD_SCALE 1.45→1.05, MAX_TILT 35°→28° (más sólido, menos caricaturesco).
Título "GioSer" centrado dentro de la superficie de la chacana, sin
subtítulo. Se inclina junto con la chacana vía CSS perspective +
rotateX/rotateY desde u-tilt-x/y inyectadas cada frame por WASM.
Botones (4 tips):
- Reposicionados a `arm_extent * 1.32` (entre punta y aro grueso).
- Bigger: min-width 168px, glyph 54px, label Cinzel 0.95rem.
- Doble anillo en hover (::before con border + glow).
- Cuando un drawer se abre, fade-out de tips + canvas + brand.
Drawers MD (uno por elemento):
- `<aside class="drawer drawer-{element}">` con transform-origin desde
CSS vars (--origin-x/y) seteadas por WASM al click — crece desde la
posición exacta del botón hasta fullscreen en 700ms con cubic-bezier.
- Ambience por elemento: AIRE (radial drift), FUEGO (flicker keyframe),
AGUA (tide vertical), TIERRA (warm earth gradient).
- Cerrado con botón X, Escape o data-close-drawer.
- Carga MD desde ./md/{element}.md via spawn_local + Reader::open_url.
Pluma (visor MD agnóstico, dos crates nuevos):
- `crates/modules/pluma/pluma-md` — wrapper sobre pulldown-cmark 0.12.
API: to_html(), to_themed_html(md, theme) con sanitización del theme,
events() para AST stream. GFM completo. No deps web. 5 tests.
- `crates/modules/pluma/pluma-reader-web` — toma HtmlElement, expone
open_url async (fetch via wasm-bindgen-futures), render_md sync,
show_loading/show_error. NO inyecta CSS — el host estiliza
`.pluma-doc[data-pluma-theme="..."]` con sus colores.
CSS pluma-doc completo: h1/h2/h3, code/pre con border-left accent,
blockquote, tables, lists, hr gradient. Loader spinner + error state.
Placeholders en md/{aire,fuego,tierra,agua}.md con texto seed.
Workspace verde + 18 tests (6 geom + 4 palette + 3 physics + 5 pluma-md).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
3dbdfb357b | chakana | ||
|
|
7f43d4e99f | pruebas | ||
|
|
e701df4a2b |
feat(gioser): chacana mística stepped, nubes/estrellas/meteoros, tilt 35°
- gioser-geom: ChacanaSpec paramétrica con `steps` (default 2). bounding box cuadrado (no cruz alargada), centro 6s×6s, brazos cortos de 2 niveles que adelgazan hacia la punta. arm_extent = 0.65 con thickness=0.13. - gioser-shaders: nubes FBM 5× más rápidas, 3 estratos de estrellas con twinkle independiente, 4 meteoros procedurales con cola/cabeza y vida cíclica. Chacana SDF rediseñada para 2 escalones, aro doble (interior + exterior), 12 rayos angulares y 4 marcas cardinales animadas. - gioser-canvas-web: MAX_TILT 22°→35°, WORLD_SCALE 0.92→1.45, spring 1.8 Hz / ζ=0.62 (más languido). uniform `u_center_half` agregado. Las puntas DOM se desplazan visiblemente con el tilt. - README: fix wasm-bindgen-cli 0.2.99 → 0.2.121 + `--locked`. 13 tests pasan (6 geom + 4 palette + 3 physics). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
d1ce4c8970 |
feat(lapaloma-financial): OHLC + candlesticks con preserva-volatilidad
- axis.rs: paint_axes extraído a función pública reusable entre crates de visualización. LapalomaChartElement::paint_axes ahora es un thin wrapper. - OhlcBuffer: stride 6 f32 por bar (t, o, h, l, c, v). Bar struct con is_bull/is_bear. price_range y time_range. 5 tests. - aggregate_time_bucketed (sección 3.2 del ARCHITECTURE.md): buckets por TIEMPO (no índice) — open=first, close=last, high=max, low=min, volume=sum. Preserva volatilidad (los wicks sobreviven al downsample, a diferencia de LTTB). Fallback a copy 1:1 si el span temporal es cero. 4 tests cubren bucket count, preservation of volatility, fallback, empty input. - paint_candlesticks: render agnóstico contra el trait Canvas. Wick = stroke_line vertical (high → low). Body = fill_rect open ↔ close con color bull/bear/neutral. body_width derivado del spacing entre bars (con body_min_width floor). - LapalomaCandlestickElement: Element GPUI que reusa paint_axes + paint_candlesticks. Sin pan-blit cache en v0.1 (≤500 bars on-screen no lo necesita). - crates/apps/lapaloma-financial-demo: random walk determinístico (xorshift32 inline + seed fijo) de 120 bars, pan + zoom + reset igual que el cartesian demo. Paleta nórdica para bull (#a3be8c) y bear (#bf616a). 60 tests verdes (28 cartesian + 20 core + 9 financial + 3 render). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
2b8e990cf9 |
feat(lapaloma-cartesian): picture cache pan-blit
- ChartCache + ChartCacheHandle (Arc<Mutex<...>>) cacheable entre frames. El Render host crea uno con chart_cache() y lo pasa al Element con .with_cache(handle). Sin handle, cada frame rebuild completo (correcto pero sin la optimización). - Hash estructural: plot rect + viewport.span (no x_min/y_min) + per-series (data.revision + data.len + stroke). 5 tests cubren estabilidad, pan no invalida, zoom invalida, data revision invalida, plot rect invalida. - En paint: si el hash matches, pan-blit = copia las coords cacheadas con offset (dx_px, dy_px) calculado del diff entre viewport.x_min cached vs actual. Salteamos LTTB + projection. - LineSeries::compute_projected expone el pipe LTTB + project_buffer como método público para que el Element pueda cachear sin pasar por paint(). - Demo multi-series usa el cache; header muestra "cache: N pan-blits / M rebuilds" en vivo para que se vea la métrica al draguear (pan-blits crece) y al zoomear (rebuilds crece). Limitación v0.1 anotada en código: el doc canónico (sección 4.4) usa una textura offscreen blitable; GPUI 0.2 no expone esa primitiva directa. La impl actual cachea coords proyectadas y emite las polilíneas con offset — mismo ahorro de CPU (saltea LTTB) sin GPU texture cache. 51 tests verdes (28 cartesian incluyendo 5 nuevos del structural_hash, 20 core, 3 render). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
ab03a61db4 |
feat(lapaloma-phosphor): trail CRT con alpha decay + glow
- lapaloma-phosphor: feature `gpui` (default). LapalomaPhosphorElement divide el RingBuffer en N segmentos (default 16, configurable) y pinta cada uno como una stroke_polyline con alpha = (k+1)/N. El segmento más nuevo va con alpha 1.0, el más viejo casi transparente — efecto fósforo persistente. - Cada segmento incluye el primer punto del siguiente para evitar gaps visibles entre tramos. - Wraparound se parte en dos sub-polilíneas (no concatenadas) para no introducir la línea horizontal "del slot cap-1 al slot 0". - Glow opcional: pasada adicional con width × glow_width_mult y alpha × glow_alpha — efecto halo CRT. - crates/apps/lapaloma-phosphor-demo: misma señal sintética que stream-demo, paleta verde Tektronix (#9bff8c sobre #050805), trail 24 segs + glow 4× α 0.18. Limitación v0.1: el doc canónico usa triangle strip con per-vertex color (sección 4.3); GPUI 0.2 no expone esa API directa. La impl actual es funcionalmente equivalente con N draw calls en lugar de 1. Cuando wgpu directo esté disponible, swap inmediato sin tocar las API públicas. 46 tests verdes (sin cambios; phosphor se valida via demo). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4796f2652d |
feat(lapaloma-stream): osciloscopio CRT con RingBuffer en sweep mode
- lapaloma-stream: feature `gpui` (default). LapalomaStreamElement pinta un RingBuffer en modo sweep — dos polilíneas split-at-head (segmento [head..cap) viejo + [0..head) nuevo) para evitar la línea horizontal del wraparound. Pre-fill (count < cap) sólo pinta [0, head) para evitar el flat-line del 1.0.2 fix. - y_range configurable, background opcional, padding. - crates/apps/lapaloma-stream-demo: osciloscopio sintético con RingBuffer cap=512. Timer en cx.background_executor que hace push(synthesize(t)) + cx.notify() cada 16ms (60 Hz). Señal = suma de dos sinusoides desfasadas + jitter determinístico. Header muestra cap / head / filled% / t / revision en vivo. - Workspace: registrada la app lapaloma-stream-demo. Showcase del P2 zero-alloc: push(v) son 2 writes + 2 increments, zero allocations per frame, ningún Vec se reasigna. 46 tests verdes (sin cambios; el stream se valida en runtime via demo). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
66d18ab47c |
feat(lapaloma-cartesian): multi-series en LapalomaChartElement
- Element ahora mantiene Vec<ChartSeriesItem> con DataBuffer + StrokeStyle + nombre opcional por serie. Builder add_series y add_series_named. - En paint(), una pasada por cada serie reusando el mismo scratch. N series = N paint_path (no N × por punto). Cumple P3 del ARCHITECTURE.md por serie. - `lapaloma_chart(data, vp, stroke)` queda como helper retrocompat para el caso una-serie. - Demo: 3 series simultáneas (sin, cos, mix) con colores nórdicos + leyenda textual en el header. 46 tests verdes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
138307c2ba |
feat(lapaloma-demo): pan + zoom + double-click reset interactivos
- viewport.rs: `pan_fraction(fx, fy)` — pan en fracción del viewport
independiente del plot_rect. Útil cuando el handler GPUI trabaja
en coords de window y no conoce el rect interno del chart.
- lapaloma-demo: state machine de drag (DragAnchor con snapshot del
viewport al click) + handlers on_mouse_down/move/up para pan,
on_scroll_wheel con sensitivity exponencial 0.0015 para zoom
anchor-preserving al cursor, on_click con click_count >= 2 para
reset al viewport inicial. El header muestra estado dragging.
- Maneja ScrollDelta::Pixels (trackpad) y ::Lines (mouse wheel
tradicional) unificando con line-height 16px.
46 tests verdes en lapaloma-{core,cartesian,render}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
fd25369715 |
feat(lapaloma-cartesian): ejes con ticks y labels decimadas
- axis.rs: ticks_nice (Wilkinson sobre lapaloma_core::scale::nice_step),
decimate_labels con min_spacing_px, format_tick con decimales según
step, AxisStyle config. 8 tests.
- gpui_backend::draw_text: shape_line via window.text_system() + iterate
glyphs con paint_glyph. Sin dep en App context (sólo &mut Window).
- LapalomaChartElement.paint_axes: línea base + tick marks + labels
centrados (X) / right-aligned (Y) con decimación. Margins por defecto
reservan 32px izq + 24px abajo.
45 tests verdes en lapaloma-{core,cartesian,render}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
97c09bc96a |
feat(lapaloma): backend GPUI + LapalomaChartElement + app demo
Cadena end-to-end DataBuffer → LineSeries → Canvas → gpui::Window funcionando. cargo run -p lapaloma-demo abre una ventana con sin(x) sobre 1024 muestras y una sola paint_path por frame. - lapaloma-render: feature `gpui` opcional. WindowCanvas adapter traduce el trait Canvas a paint_quad/paint_path de gpui 0.2. Conversión RGB→HSL para integrar con el sistema de colores Hsla del resto del codebase yahweh. 3 tests de conversión. - lapaloma-cartesian: feature `gpui` (default). element::LapalomaChartElement con impl Element + IntoElement. Arma WindowCanvas en paint() y delega a LineSeries — un solo paint_path por chart. - crates/apps/lapaloma-demo registrado en workspace. Limitaciones conocidas v0.1: clip stack, triangle strips y draw_text no implementados (los necesitan phosphor / Sankey / axes; se agregan en sus fases). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
791ca18d81 |
feat(lapaloma-cartesian): viewport + coord_system + LineSeries con LTTB
- ChartViewport: pan/zoom anchor-preserving en coords de dominio. - CoordinateSystem: proyección dominio→pixel + project_buffer zero-alloc. - trait Series + LineSeries que emite una sola stroke_polyline por frame (valida P3 del ARCHITECTURE.md). LTTB se dispara cuando data.len() excede 3× el ancho del plot. - hit_test sobre coords sorted-by-X con binary search + threshold 8px. - 14 tests cubren pan, zoom, projection, downsample y hit-test. Element GPUI queda para la siguiente fase (requiere pionear paint custom sobre PaintContext — el monorepo no tiene precedente todavía). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
88051d220a | creation | ||
|
|
52acaabcf4 | gioser | ||
|
|
6596c81271 |
feat(shipote): audit log persistente + HTTP gateway (fase S)
- Daemon escribe append-only a $XDG_STATE_HOME/shipote/audit.log además del tracing. Single-line: ts=<ms> uid=<peer> action=<verb> <detail>. Rotación simple a .log.1 al pasar 1 MiB. - shipote-gateway: TCP listener 127.0.0.1:7378 default. POST /rpc traduce JSON ↔ postcard contra daemon socket. GET / health text. HTTP parser ad-hoc (~70 LOC), sin dep de hyper/axum. Sin auth — bind a localhost + SHIPOTE_TRUST_ANYONE=1 en prod. E2E: curl --noproxy '*' POST /rpc → "Pong", Health JSON, Capabilities JSON. Audit log persiste mutaciones con uid del peer. 85 tests pasan (features nuevos son binarios, no library mods). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
d962fe4601 |
docs(shipote): README + 4 docs en docs/ (ARCHITECTURE, CLI, RECIPES, DEVELOPMENT)
- README.md en crates/modules/shipote/ como entry point. - docs/ARCHITECTURE.md — 11 crates, capas, decisiones (O_CLOEXEC, dirty AtomicBool, pipeline restart entero, etc.) + snapshot versioning. - docs/CLI.md — referencia comando por comando, flags, env vars. - docs/RECIPES.md — specs TOML para workspaces y pipelines típicos. - docs/DEVELOPMENT.md — compilar, correr daemon/shell/CLI, tests, smoke E2E manual, debugging FDs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a9124449f9 |
feat(shipote): health endpoint + audit log + token-bucket real (fase R)
- Request::Health → Response::Health { version, uptime_ms, alive_*,
active_flows, dirty }. CLI: shipote health.
- handle_client lee peer_uid una vez al accept. audit_request emite
info!(target: "audit", uid, action, detail) por mutación (create/stop/
run/pipeline.*/flow.drop). Reads omitidos. Filtrable con SHIPOTE_LOG=
warn,audit=info.
- TokenBucket real reemplaza rate_limit_sleep: refill por wall time,
capacity = 1s de rate, debt negativo dispara sleep proporcional.
Permite burst real, no chunk-by-chunk uniforme.
85 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8,
shipote-core 26, shipote-discern 5, yahweh-provider-fs 3).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
18c0344a52 |
feat(shipote): throughput card + rate-limit + snapshot incremental (fase Q)
- shipote-shell Flow channels card extiende con bytes_total + bytes/s por socket. Lookup helper evita borrows en closures. - DiscernPolicy.max_bytes_per_sec: splitter task hace sleep proporcional al tamaño de chunk tras cada broadcast. Token-bucket simple v1. - WorkspaceManager.dirty: AtomicBool. mark_dirty() en mutaciones que afectan al snapshot. save_snapshot skip si clean y path existe. restore_snapshot resetea dirty=false (hidratación no es mutation). 85 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 26, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
3486949d24 |
feat(shipote): throughput + stats persistente + auth peer (fase P)
- FlowMeter (atomic u64 + rolling window 32 samples) en cada FlowChannel. flow_throughput() → (socket, bytes_total, bytes_per_sec). CLI: shipote flow throughput. Idle threshold 5s = rate 0.0. - Snapshot v4 con stats_history persistente por workspace (cap 16). PersistedStats separado para evitar Instant. Restore hidrata el VecDeque con source="persisted". - Auth SO_PEERCRED: daemon rechaza peers con uid distinto al propio. SHIPOTE_TRUST_ANYONE=1 = escape hatch documentado. 84 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 25, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
1cce50b290 |
feat(shipote): collision detection + stats history server-side (fase O)
- Flow socket names usan pipeline_id full (ULID 26 chars) + edge_idx. Cero colisiones entre pipelines (ULID es único global). Fallback con suffix -N si el path existe (cap 1000 retries). - WorkspaceState.stats_history (VecDeque cap 64) — workspace_stats appendea cada call. API workspace_stats_history(id, tail). Protocol WorkspaceStatsHistory. Shell pide history al primer probe → sparkline hidratada al boot, sobrevive restart del shell. 84 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 25, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a823c40fe1 |
feat(shipote): drain shutdown + persist live pipelines + batched query (fase N)
- Daemon SIGTERM/SIGINT: snapshot ANTES, stop_with_grace(1s) de todos los workspaces DESPUÉS. Grace permite app-level cleanup. - Snapshot v3 con live_pipelines: pipeline_supervisors se persisten; daemon relanza al restore con sus recursos (Incarnator+DiscernPipeline). RestoreOutcome separado para que core no necesite incarnator. Forward-compat v1/v2 via #[serde(default)]. - WorkspaceFullSummary: stats+quota+commands+flow_sockets en 1 roundtrip. Shell reduce N×4 requests/probe a N×1 + 4 globales. 83 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 24, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c3f9c9e36a |
feat(shipote): pipeline backoff + quota card + logs follow (fase M)
- PipelineSpec.restart_backoff_ms + restart_max_backoff_ms + restart_max: backoff exponencial entre relaunches (anti-thrash). take_pending_restarts aplica restart_max (0 = infinito); excedido = supervisor descartado con warning. Daemon hace tokio::sleep(backoff) antes del relaunch y escala current_backoff x2 hasta el cap. - shipote-shell card "Quota breaches": probe extiende con WorkspaceQuota por workspace. Color rojo si hay breaches, verde si no. - shipote logs --follow: poll cada 200ms al daemon, imprime suffix nuevo hasta que el comando termine. Sin cambios al protocolo. Best-effort: si el ring rota más rápido que el poll, se pierden bytes. 83 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 24, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4c9d1b4c1d |
feat(shipote): quota enforce + cgroup memory.max + pipeline restart (fase L)
- WorkspaceSpec.quota_enforce: QuotaAction (None|Log|Kill) por recurso (mem, nproc). reap_dead aplica policy; Kill usa stop_with_grace(ZERO). - ente_incarnate::cgroup::apply_rlimits_to_cgroup escribe memory.max y pids.max. WorkspaceManager::create_with_id lo invoca si soma.cgroup.path y delegation. Kernel hace OOM kill al exceder; falla silenciosa si no hay delegation. - PipelineSpec.restart_on_failure: bool. register_pipeline_supervisor retiene spec; reap_dead detecta all-dead + any-failed → push a queue; daemon reaper drena y relanza pipeline ENTERO (los pipes intermedios no permiten restart parcial). 82 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 24, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
324a0c2d5d |
feat(shipote): multi-core CPU% + quota report + restart-on-failure (fase K)
- WorkspaceStats.cpu_cores via sysconf cacheado. CLI muestra `cpu_pct: 98.7 % (24.7% total / 4 cores)`. - workspace_quota compara SomaSpec.rlimits contra accounting actual. Reporta breaches humanos. NO enforcement automático en v1. - run_with_options(.., restart_on_failure): si exit != 0, reaper relaunch con backoff exponencial 200ms → 30s cap. Inner.restart_specs persiste el spec entre intentos. 81 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 22, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
d8727a3038 |
feat(shipote): CPU% + pipeline live-tail + replay por bytes (fase J)
- CPU% derivado server-side entre samples (WorkspaceState.last_cpu_sample). 100% = 1 core saturado. Primer sample devuelve None (sin baseline). - shipote pipeline run --tail: tras lanzar, suscribe al primer flow_socket y vuelca bytes hasta EOF. Auto-implica --tap. - DiscernPolicy.replay_bytes: cap adicional por bytes para el replay buffer del FlowChannel. evict_for_incoming considera el chunk entrante para que post-push el buffer NUNCA exceda los caps. - shipote-shell: stats history extiende sparkline con %CPU. 80 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 21, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |