Skip to content

fix: Style DNA con datos reales — radar, mapa semántico y rangos medidos - #128

Merged
sergi-torres merged 2 commits into
mainfrom
fix/style-dna-real-data
Jul 28, 2026
Merged

fix: Style DNA con datos reales — radar, mapa semántico y rangos medidos#128
sergi-torres merged 2 commits into
mainfrom
fix/style-dna-real-data

Conversation

@sergi-torres

@sergi-torres sergi-torres commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Cierra los dos síntomas visibles en pantalla —los tres autores dibujaban el mismo radar y el mapa semántico los pintaba en un solo punto— y corrige la causa común que había detrás de ambos. Listo para mergear.

1. El mapa semántico (#88)

style_profiles.json_data.embedding_umap_2d —el campo que lee el scatter— conservaba el marcador {"centroid": [0,0], "spread": 0}. scripts/precompute_umap.py sí ajustaba UMAP, pero escribía solo en umap_coords, una tabla que nadie lee.

Ahora agrega sus coordenadas por autor y las escribe en el perfil vigente. Medido sobre los 4 529 chunks embebidos:

autor centroide radio distancia
austen (9,40 · 7,48) 2,63 austen–dickens 7,55
dickens (1,85 · 7,48) 2,20 austen–poe 9,42
poe (0,74 · 3,78) 1,22 dickens–poe 3,87

Dickens y Poe salen más próximos entre sí que a Austen: resultado de los datos, no una separación colocada a mano.

De paso: el script nunca había completado una ejecución. psycopg2 devuelve los valores de pgvector como cadena salvo que se registre el tipo, y np.array reventaba. Mismo defecto que #107, en el lado de lectura.

2. La causa común: style_features.md §7 nunca se validó

La sección se describía a sí misma como "informed estimates … will be updated after Sprint 1 extraction runs on the actual Gutenberg corpora". Esa actualización nunca ocurrió, y las estimaciones están mal en casi todas las métricas:

métrica estimado (Austen) medido
hapax_ratio 0,38–0,44 0,695
subordination_ratio 0,28–0,36 1,814
first_person_ratio 0,5–3,0 20,9

Las diez métricas de los tres autores —en §7 y en las nueve tablas por feature— pasan a ser los valores leídos de los perfiles calculados el 2026-07-29, con bandas de ±8%. De esa tabla salían los dominios del radar, y por eso dos de los seis ejes se recortaban a 1,00 para todos: una tabla de sanity-check que nunca se comprobó contra el extractor era el origen del defecto que existía para detectar.

3. El radar

Dominios recalibrados contra la medición. subordination_ratio cuenta subordinadas por oración, así que es normalmente > 1: nunca fue la fracción 0–1 que asumía el dominio. La separación media por eje pasa de 0,09 a 0,20, sin ejes planos ni autores pinchados en los extremos. Los dominios no se aprietan alrededor de estos tres autores: eso fabricaría el contraste que design-system.md §8.6 prohíbe falsear.

4. Fixtures

Estaban construidos contra los rangos equivocados (con un test que lo exigía), así que corregir los dominios los habría dejado con dos ejes a cero. Ahora son una instantánea de los perfiles reales, centroides UMAP incluidos — antes estaban inventados y "separados a mano para que el scatter se leyera bien".

Lo que el documento decía y los datos desmienten

No se ha borrado, se ha anotado en §3.4:

  • first_person_ratio se presenta como "el discriminador más nítido de Poe". Medido: Dickens 29,9 > Poe 24,8 > Austen 20,9. Cuenta pronombres también dentro del diálogo, así que mide cuánto dicen "yo" los personajes, no cuán en primera persona está narrado.
  • §2.3 espera a Poe el más bajo en noun_to_verb_ratio; medido es el más alto.
  • §3.3 espera a Poe en 0,05–0,14 de dialogue_ratio; medido 0,245.

Verificación

npx vitest run   -> 107 passed
npx tsc --noEmit -> limpio
npx eslint       -> limpio
pytest ai_pipeline/tests -> 222 passed, 3 skipped

Entrada en docs/decision_log.md incluida, como exige la política de documentos rectores. Sin cambios en api_contract.yaml ni en ninguna forma de respuesta. Nada de esto consume cuota de Watsonx.

🤖 Generated with Claude Code

Two symptoms, two separate causes, both visible on screen: every author drew
the same radar, and the semantic map drew all three authors on one point.

## The semantic map (#88)

`style_profiles.json_data.embedding_umap_2d` — the field the scatter reads —
kept the extractor's placeholder `{"centroid": [0,0], "spread": 0}`, so all
three authors sat at the origin. `scripts/precompute_umap.py` did fit UMAP
over the chunks, but wrote only to `umap_coords`, a table nothing reads.

The script now also aggregates its per-chunk coordinates per author and
writes centroid + spread into the latest StyleProfile row. Measured over the
4529 embedded chunks:

    austen   (9.40, 7.48) r=2.63    austen-dickens  7.55
    dickens  (1.85, 7.48) r=2.20    austen-poe      9.42
    poe      (0.74, 3.78) r=1.22    dickens-poe     3.87

The projection has to be global: three centroids alone cannot be fitted
(UMAP needs n_neighbors+1 points) and a per-author fit would put each author
in an unrelated coordinate system.

While running it for the first time against the real database, the script
turned out never to have completed a run: psycopg2 returns pgvector values
as the *string* "[0.013,-0.011,...]" unless the type is registered, and
np.array raised on it. Same class of defect as #107, on the read side.

## The radar

The normalization domains came from the expected ranges in
docs/style_features.md section 7, and those ranges do not match what the
extractors produce. Two of six axes sat entirely outside their domain and
clamped to 1.00 for every author:

    hapax_ratio          measured 0.69-0.75   domain was [0.05, 0.5]
    subordination_ratio  measured 1.38-1.81   domain was [0.1,  0.5]

`subordination_ratio` counts subordinate clauses *per sentence*, so it is
routinely > 1 — never the 0-1 fraction the domain assumed. Recalibrated
against measurement, mean separation per axis goes 0.09 -> 0.20 with no axis
flat and no author pinned at either end. The domains are deliberately not
tightened around these three authors: that would manufacture the contrast
design-system.md forbids faking.

## Fixtures

The fixtures were built to those same wrong ranges (a test enforced it), so
correcting the domains would have made the fallback path render two axes at
zero. They are now a frozen snapshot of the real measured profiles, and the
test's ranges are measured values +/-8% instead of the section 7 estimates.
Their UMAP centroids were previously invented and "spaced apart so the
scatter plot is readable" — the fallback flattered the product; now it does
not.

Section 7 itself is still wrong (it predicts first_person_ratio 0.5-3.0 for
Austen where the pipeline measures 20.9). Correcting a governing document
needs a decision_log entry and is left for that.

Verified: 107 frontend tests, tsc and eslint clean; 222 pipeline tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
author-ai Ready Ready Preview, Comment Jul 28, 2026 10:59pm

`docs/style_features.md` section 7 described itself as "informed estimates
... will be updated after Sprint 1 extraction runs on the actual Gutenberg
corpora". That update never happened, and the estimates are wrong on almost
every metric:

    hapax_ratio          Austen   estimated 0.38-0.44   measured 0.695
    subordination_ratio  Austen   estimated 0.28-0.36   measured 1.814
    first_person_ratio   Austen   estimated 0.5-3.0     measured 20.9

All ten metrics for all three authors — in section 7 and in the nine
per-feature tables — are now the values read from the style_profiles rows
computed on 2026-07-29, banded at +/-8% (+/-0.04 for sub-unit ratios).

This is the table the radar domains were derived from, which is why two of
six axes clamped to 1.00 for every author and all three drew the same shape.
A sanity-check table that had never been checked against the extractor was
the source of the defect it existed to catch.

Recorded rather than quietly dropped: measurement contradicts three
rationales the document argues.

  - Section 3.4 calls first_person_ratio "the sharpest Poe discriminator".
    Measured, Dickens is highest (29.9), then Poe (24.8), then Austen (20.9)
    — who "never uses 'I' in narration" yet scores 20.9. The metric counts
    pronouns inside dialogue too, so it measures how often characters say
    "I", not how first-person the narration is.
  - Section 2.3 expects Poe lowest on noun_to_verb_ratio; measured, Poe is
    highest.
  - Section 3.3 expects Poe at 0.05-0.14 on dialogue_ratio; measured 0.245.

Those caveats are added to section 3.4 instead of being left for a reader to
trip over.

decision_log entry added, as the LOCKED-document policy requires. No
api_contract.yaml change and no response shape touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sergi-torres sergi-torres changed the title fix(front,ml): el panel Style DNA muestra datos reales y diferenciados fix: Style DNA con datos reales — radar, mapa semántico y rangos medidos Jul 28, 2026
@sergi-torres
sergi-torres merged commit d6c6abb into main Jul 28, 2026
8 checks passed
@sergi-torres
sergi-torres deleted the fix/style-dna-real-data branch July 28, 2026 23:10
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