fix: Style DNA con datos reales — radar, mapa semántico y rangos medidos - #128
Merged
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
`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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.pysí ajustaba UMAP, pero escribía solo enumap_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:
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.arrayreventaba. 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:
hapax_ratiosubordination_ratiofirst_person_ratioLas 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_ratiocuenta 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 quedesign-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_ratiose 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.noun_to_verb_ratio; medido es el más alto.dialogue_ratio; medido 0,245.Verificación
Entrada en
docs/decision_log.mdincluida, como exige la política de documentos rectores. Sin cambios enapi_contract.yamlni en ninguna forma de respuesta. Nada de esto consume cuota de Watsonx.🤖 Generated with Claude Code