Project Rayuela is a computational literary analysis of Julio Cortazar's Rayuela. The repository contains the full workflow behind the project: text cleanup, embeddings, classical stylometrics, LLM-based style and semantic scoring, permutation tests, interactive visualizations, and article assets for a two-part series.
This workspace is already well past the planning stage. The analysis code and article source markdown are versioned here, while raw corpus files, derived outputs, and generated publishing assets stay local-only so the repo carries reproducible code rather than copyrighted or heavyweight artifacts.
Rayuelaparsing outputs live in the localdata/working tree and are intentionally not versioned.- The project currently works across four analysis scales: holistic embeddings (A), content-light stylometrics (A'), LLM-perceived style (B'), and narrative DNA semantics (B).
- The validated semantic instrument is currently 19-dimensional.
temporal_claritywas excluded after inter-rater replication with Nemotron 70B showed a rubric polarity failure. - The two long-form article sources live at
ARTICLE_PART1_MEDIUM.mdandARTICLE_PART2_MEDIUM.md. - Interactive Plotly outputs are generated locally into
outputs/figures/, and the published GitHub Pages bundle is tracked indocs/. - Phase 8 work extends the same methodology to a 10-work Latin American comparison corpus stored in the local
data/corpus/working tree.
- The linear reading order (chapters 1-56) is smoother than random across all four scales.
- The hopscotch path keeps moderate structure in the holistic embedding space, but that signal fades in the more explicit stylistic and semantic representations.
- The gap between holistic coherence and explicit semantic disorder is the central interpretive result of the current article series.
ARTICLE_PART1_MEDIUM.md: current source for the Part 1 article.ARTICLE_PART2_MEDIUM.md: current source for the Part 2 article.- Regenerate the root HTML exports and
docs/publishing bundle locally withscripts/md_to_html.pyandscripts/prepare_ghpages.py. ANALYSIS_FRAMEWORK.md: explicit assumptions, null hypotheses, and interpretive boundaries.ARTICLE_LOG.md: chronological research and writing log.
src/: core analysis pipeline, including parsing, embeddings, stylometrics, semantic extraction, replication, trajectory analysis, corpus expansion, and figure generation.scripts/: article export, deployment, monitoring, EPUB/PDF parsing, and corpus utility scripts.data/: local-only source texts, parsedRayuelaJSON, calibration passages, and the raw/clean comparison corpus.outputs/: local-only embeddings, semantic and stylistic vectors, audits, review notes, summary JSON, and figure HTML.docs/: published GitHub Pages bundle generated fromoutputs/figures/.article_images/: local-only PNG exports referenced by the root article markdown.prompts/: prompt templates for semantic and stylistic extraction, plus article review prompts.notebooks/: reserved notebook area; currently empty in this checkout.
data/,outputs/,article_images/, and the rootARTICLE_PART*_MEDIUM.htmlexports are working artifacts and are intentionally ignored by git.docs/remains versioned because the current custom-domain GitHub Pages site publishes frommain:/docs.- Keep copyrighted corpus material and regenerated publishing bundles in your own local storage.
- Use the source markdown and the helper scripts in this repo to regenerate local artifacts when needed.
CLAUDE.mdstill describes the project as if it were in Phase 1. The codebase and outputs show a much later state, so treat that file as historical guidance rather than the current status document.- The root
ARTICLE_PART*_MEDIUM.mdfiles are now the canonical article sources. - Most expensive outputs are intentionally local-only, so avoid rerunning the full pipeline unless you actually need refreshed artifacts.
The repository was built around an ARM64 NVIDIA DGX Spark workflow. The Docker and Compose setup assumes:
- NVIDIA Container Toolkit / Docker GPU runtime
- an
HF_TOKENenvironment variable for model downloads - an external Docker volume named
vllm-models - GPU access for the vLLM services
The analysis container is defined in Dockerfile. Multi-service orchestration lives in docker-compose.yml.
docker compose build rayuela
docker compose up rayuela
docker compose --profile llm up vllm
docker compose --profile llm-nemotron up vllm-nemotronrayuelaexposes JupyterLab on port8888.vllmserves Qwen 3.5 on port8000.vllm-nemotronserves the Nemotron replication model on port8000and should not run at the same time asvllm.- The tracked Qwen vLLM command includes
--reasoning-parser qwen3, which keeps Qwen reasoning enabled while separatingreasoning_contentfrom finalcontent.
For the official DGX Spark Nemotron reasoning lane, use the helper in
src/reconstruction_spark_nemotron.py
instead of Docker Compose. That path follows NVIDIA's published Spark playbook
for Nemotron 3 Nano via llama.cpp, which is currently the cleanest official
reasoning-control surface for this workstation.
.venv/bin/python src/reconstruction_spark_nemotron.py print-commandsThe helper prints the exact build, model-download, server, and launchcheck-plan commands so the Spark-specific path stays explicit and reproducible.
# Rebuild cleaned Rayuela JSON
docker compose run --rm rayuela python src/parsing.py
# Scale A: chapter embeddings
docker compose run --rm rayuela python src/embeddings.py
# Scale A': classical stylometrics
docker compose run --rm rayuela python src/stylometrics.py
# Scale B': LLM-perceived style
docker compose run --rm rayuela python src/stylistic_extraction.py --resume
# Scale B: narrative DNA semantics
docker compose run --rm rayuela python src/semantic_extraction.py --resume
# Cross-scale analysis and figure generation
docker compose run --rm rayuela python src/scale_comparison.py
docker compose run --rm rayuela python src/article_figures.py
# Prepare GitHub Pages output from generated figure HTML
docker compose run --rm rayuela python scripts/prepare_ghpages.pyscripts/prepare_ghpages.py: copies selected figure HTML from the localoutputs/figures/tree into the localdocs/publishing bundle and swaps inline Plotly bundles for the CDN version.scripts/md_to_html.py: converts the root Medium article markdown files into self-contained HTML. The helper depends on the Pythonmarkdownpackage, which is included inrequirements-dev.txtfor local QA but is not preinstalled in the base analysis container. Install it before running the export helper inside Docker withdocker compose run --rm rayuela pip install markdown, or locally withpython3 -m pip install markdown.scripts/export_article_pngs.py: exports static PNGs from Plotly figures for article use. It depends onkaleido; install it the same way withdocker compose run --rm rayuela pip install kaleidoorpython3 -m pip install kaleido.
- GitHub Actions enforces four checks on pull requests: pre-commit, type checking, tests with an 85% coverage threshold, and docstring coverage with an 85% threshold.
- The CI-safe quality scope currently covers
src/parsing.py,src/project_config.py,src/reconstruction_contract.py,scripts/md_to_html.py, andscripts/prepare_ghpages.py. - Part 3 reconstruction coverage now also includes
src/reconstruction_audit.py,src/reconstruction_metrics.py,src/reconstruction_dataset.py, andsrc/reconstruction_baselines.py. - Local setup:
python3 -m pip install -r requirements-dev.txt
python3 -m pre_commit install
python3 -m pre_commit run --all-files
python3 -m mypy
python3 -m pytest
python3 -m interrogate src/parsing.py src/project_config.py src/reconstruction_contract.py scripts/md_to_html.py scripts/prepare_ghpages.py- The source-controlled GitHub ruleset definition lives at
.github/rulesets/main-quality-gate.json.
Part 3 reconstruction runs are rooted under outputs/reconstruction/. Phase 0 establishes an immutable run policy:
- each run writes its manifest to
outputs/reconstruction/runs/<run_id>/manifest.json - each manifest is mirrored to
outputs/reconstruction/manifests/<run_id>.json - failed runs are retained in place and their run IDs are never reused
- all manifest paths are stored project-relative so the run can be replayed on another checkout
The required manifest fields are:
schema_versionrun_idphasestatuscreated_atupdated_atgit_shamodel_idprompt_template_idseedconfig_hashcorpus_manifestsplit_manifestpathsconfig_payloaderror_message
Phase 0 dry run:
python3 src/reconstruction_contract.py --run-id phase0-dry-run --phase phase-0-quality-envelopePhase 1 adds src/reconstruction_audit.py to verify that the cleaned comparison corpus and its derived outputs remain operationally decoupled from stale or orphaned artifacts.
Targeted Phase 1 verification:
python3 -m pytest tests/test_reconstruction_audit.py -q
python3 src/reconstruction_audit.pyPrimary outputs:
outputs/corpus/corpus_metadata.json: work- and author-level segment counts from the cleaned corpusoutputs/reconstruction/analysis/corpus_sync_audit.json: machine-readable synchronization report
Phase 2 adds src/reconstruction_metrics.py to lock the measurement contract before any rewrite generation starts.
The module provides:
- typed stylometric and semantic baseline reports under
outputs/reconstruction/baselines/ - deterministic rewrite scoring against source semantics and target stylistic envelopes
- identity, copy-source, and random-target control diagnostics
Targeted Phase 2 verification:
python3 -m pytest tests/test_reconstruction_metrics.py -q
python3 src/reconstruction_metrics.pyThe CLI intentionally refuses to lock live baselines if the Phase 1 corpus outputs are stale or incomplete.
Primary outputs:
outputs/reconstruction/baselines/stylometric_baseline.jsonoutputs/reconstruction/baselines/semantic_baseline.jsonoutputs/reconstruction/baselines/control_diagnostics.json
Phase 3 adds src/reconstruction_dataset.py to lock the
pilot design before prompt baselines or training.
The module provides:
- deterministic 128-256 word source windows with chapter-level reference vectors
- chapter-aware train/validation/test split manifests with automated leakage checks
- target work envelopes with explicit train-split provenance
- serialized success criteria for the locked pilot under operational decoupling language
Targeted Phase 3 verification:
python3 -m pytest tests/test_reconstruction_dataset.py -q
python3 src/reconstruction_dataset.pyPrimary outputs:
outputs/reconstruction/pilots/source_windows.jsonoutputs/reconstruction/pilots/target_envelopes.jsonoutputs/reconstruction/pilots/split_manifest.jsonoutputs/reconstruction/pilots/success_criteria.json
Phase 4 adds src/reconstruction_baselines.py to
establish a prompt-only baseline before any fine-tuning.
The module provides:
- versioned identity, paraphrase, style-shift, and revise prompt templates
- traceable generate-score-revise histories with raw responses and per-iteration scores
- immutable Phase 4 run manifests under
outputs/reconstruction/runs/ - baseline case, summary, and markdown report artifacts for the locked pilot
Targeted Phase 4 verification:
python3 -m pytest tests/test_reconstruction_baselines.py -q
python3 src/reconstruction_baselines.py --run-id phase4-dry-run --dry-run --max-cases 2If the serving stack is exposing parser-separated reasoning, record that expectation in the run config:
python3 src/reconstruction_baselines.py --run-id phase4-live-parser-check --max-cases 2 --reasoning-parser qwen3 --generation-max-tokens 2048 --semantic-generation-max-tokens 2048--generation-max-tokens is now a first-class experiment parameter. When Qwen reasoning is
kept enabled behind --reasoning-parser qwen3, the model may consume a large fraction of the
budget in hidden reasoning before it emits final content. Phase 4 therefore records the token
budget in the run manifest and now fails fast if the serving stack returns reasoning without a
final passage, instead of silently scoring an empty rewrite.
The same parser edge applies to the semantic evaluator. Phase 4 now also exposes
--semantic-generation-max-tokens, forwards that budget into the structured semantic-extraction
calls, and fails fast if the evaluator receives hidden reasoning without the final JSON payload.
The default rewrite templates are now style_shift_v2 / revise_v2. They keep hidden reasoning
enabled but tighten the visible-output contract by explicitly requiring the first visible character
to belong to the passage itself, with no headings, labels, markdown, or explanatory text.
For the official DGX Spark Nemotron lane, use
src/reconstruction_spark_nemotron.py
to prepare a bounded llama.cpp launchcheck against the OpenAI-compatible API
that NVIDIA documents for Nemotron 3 Nano. This keeps the reasoning-control
surface aligned with Spark guidance instead of relying on ad hoc visible-marker
scraping.
Primary outputs:
outputs/reconstruction/runs/<run_id>/prompt_baseline_cases.jsonoutputs/reconstruction/runs/<run_id>/prompt_baseline_summary.jsonoutputs/reconstruction/runs/<run_id>/prompt_baseline_report.md
The live prompt-control path expects the local Qwen vLLM service. If the host is
currently serving another model (for example the Nemotron replication endpoint),
use --dry-run to validate the Phase 4 contract without broadening the claim.
Phase 5 adds src/reconstruction_train.py and
src/reconstruction_infer.py to prepare the
training envelope before real adapter fine-tuning.
The DGX Spark adapter path follows the official NVIDIA PyTorch/Unsloth playbooks
with Rayuela-specific run contracts; see
plans/phase5_dgx_spark_finetune_playbook.md.
For an interactive DGX Spark fine-tuning container:
scripts/bootstrap_dgx_spark_finetune.sh pytorchThe modules provide:
- deterministic
identity_smokeandcontract_smokedataset assembly from the locked pilot split - bounded
seq2seq_smoketraining for verifying the real fine-tuning execution path - immutable run directories with training config, tokenizer config, metrics, and split-specific JSONL training datasets, and checkpoint metadata
- placeholder-adapter handling that is explicit in metadata and rejected by inference when a real adapter is not present yet
Targeted Phase 5 verification:
python3 -m pytest tests/test_reconstruction_training.py -q
python3 src/reconstruction_train.py --run-id phase5-smoke --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-contract-smoke --dataset-mode contract_smoke --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-seq2seq-smoke --training-mode seq2seq_smoke --dataset-mode contract_smoke --model-id hf-internal-testing/tiny-random-t5 --max-steps 1 --max-train-examples 2 --max-eval-examples 1 --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-lora-smoke --training-mode lora_sft --dataset-mode contract_smoke --model-id hf-internal-testing/tiny-random-LlamaForCausalLM --max-steps 1 --max-train-examples 2 --max-source-length 128 --lora-rank 4 --lora-alpha 8 --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.jsonPrimary outputs:
outputs/reconstruction/runs/<run_id>/training_config.jsonoutputs/reconstruction/runs/<run_id>/training_metrics.jsonoutputs/reconstruction/runs/<run_id>/training_dataset/{train,val,test}.jsonloutputs/reconstruction/runs/<run_id>/checkpoint_metadata.json
Style-transfer adapter work starts by distilling scored Phase 4 teacher
generations into supervised JSONL examples. The generated passages remain under
ignored outputs/; tracked files only contain the code and plan:
python3 src/reconstruction_style_distill.py --dataset-id phase5-style-distill --teacher-cases-path outputs/reconstruction/runs/<teacher_run>/prompt_baseline_cases.json --min-weighted-objective 0.14
python3 src/reconstruction_train.py --run-id phase5-style-distilled-scaffold --dataset-mode style_transfer_distilled --training-dataset-dir outputs/reconstruction/style_distill/phase5-style-distill/training_datasetContract probes for LoRA adapters should run inside the same pinned DGX Spark container lane:
python3 src/reconstruction_infer.py --checkpoint-metadata-path outputs/reconstruction/runs/<run_id>/checkpoint_metadata.json --probe-split-path outputs/reconstruction/runs/<run_id>/training_dataset/val.jsonl --contract-probe-output-path outputs/reconstruction/runs/<run_id>/contract_probe.json --probe-examples 8
python3 src/reconstruction_infer.py --checkpoint-metadata-path outputs/reconstruction/runs/<run_id>/checkpoint_metadata.json --probe-split-path outputs/reconstruction/runs/<run_id>/training_dataset/val.jsonl --contract-probe-output-path outputs/reconstruction/runs/<run_id>/contract_probe_base.json --probe-examples 8 --base-onlyPhase 6 adds src/reconstruction_analysis.py
to turn immutable Phase 4+ run artifacts into synthesis-ready summaries.
The module provides:
- complete cross-run case aggregation from
prompt_baseline_cases.json - explicit failure-mode labeling from the saved scoring contract
- a compact experiment reading guide that explains how to interpret one batch
- source-side bias slices by work and author
- concrete output examples with source/output excerpts and reasoning-leak flags
- pairwise run-comparison summaries over overlapping case identities
- provenance comparability gates over run invariants such as model, prompt, corpus/pilot artifacts, backend, and generation seed
- paired bootstrap intervals for mean run-to-run objective deltas
- explicit whether the paired mean-delta interval excludes zero
- failure-transition summaries that separate persistent, resolved, and introduced failure labels across overlapping cases
- explicit promotion recommendations that stay separate from scheduler keep/discard decisions
- article-ready summary/report artifacts and a close-reading queue with stable
run_idlinks - optional W&B analysis logging for aggregate counts, failure-mode totals, run-summary tables, run-comparison tables, failure-transition tables, promotion tables, provenance tables, reading-guide tables, output-example tables, close-reading queues, and attached analysis artifacts
Targeted Phase 6 verification:
.venv/bin/python -m pytest tests/test_reconstruction_analysis.py -q
.venv/bin/python src/reconstruction_analysis.py --wandb-project rayuela --wandb-mode offlinePromotion criteria are configurable at analysis time, for example:
.venv/bin/python src/reconstruction_analysis.py \
--schedule-summary-path outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json \
--schedule-run-selection nonfailed \
--promotion-min-overlapping-cases 4 \
--promotion-min-mean-delta 0.005 \
--promotion-min-median-delta 0.0 \
--promotion-min-non-negative-share 0.5 \
--promotion-max-failure-case-delta 0 \
--promotion-require-comparable-provenancePrimary outputs:
outputs/reconstruction/analysis/reconstruction_analysis_summary.jsonoutputs/reconstruction/analysis/reconstruction_analysis_report.mdoutputs/reconstruction/analysis/reconstruction_article_inputs.json
src/reconstruction_scheduler.py adds a
finite experiment scheduler inspired by the keep/discard discipline of
Andrej Karpathy's autoresearch, but adapted to the reconstruction run contract.
The module provides:
- JSON-defined experiment queues with explicit commands, timeouts, and metric paths
- append-only scheduler results under
outputs/reconstruction/analysis/schedules/ - automatic
keep/discard/faileddecisions based on a metric key extracted from produced run artifacts - schedule summaries that record kept/discarded/failed
run_ids for direct handoff into Phase 6 analysis - optional W&B per-experiment logging for run metadata, scheduler decisions, extracted reconstruction metrics, and attached immutable run/scheduler artifacts
Targeted scheduler verification:
.venv/bin/python -m pytest tests/test_reconstruction_scheduler.py -q
.venv/bin/python src/reconstruction_scheduler.py --plan-path plans/reconstruction_guided_schedule.example.json --wandb-project rayuela --wandb-mode offline
.venv/bin/python src/reconstruction_analysis.py --schedule-summary-path outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json --schedule-run-selection nonfailed --wandb-project rayuela --wandb-mode offline --wandb-group <schedule_id>W&B logging is operationally decoupled from experiment execution. The scheduler logs one run per planned experiment with the explicit keep/discard/failed decision, the chosen advancement metric, and attached local artifacts so the research loop can compare runs without rereading raw filesystem state. The analysis step logs aggregate failure counts, run summaries, run comparisons, comparability gates, paired uncertainty intervals, failure-transition tables, promotion recommendations, reading guides, concrete output examples, and close-reading queues so the third article can cite stable synthesis outputs rather than hand-maintained notes. Those paired intervals should be read as approximate uncertainty summaries, not as a stronger significance claim than the saved case table supports.
This separation is deliberate: scheduler decisions are local operational decisions about one finite queue, while promotion recommendations are research-facing judgments derived later from the saved case table and explicit criteria.
Live prompt baselines now pass the configured reconstruction seed through the OpenAI-compatible backend request surface. That does not make every upstream serving stack perfectly reproducible, but it closes the avoidable gap where the manifest claimed a seed while the generation call ignored it.
Phase 4 also now trims obvious post-passage meta commentary such as Nota:,
Note:, Explicación:, or Changes made: suffixes before scoring the
candidate rewrite. That keeps the weighted objective focused on the rewritten
passage itself while preserving audit metadata in the saved iteration record and
reporting the trimmed-case count in the Phase 4 summary/report.
For detached execution, use src/reconstruction_launcher.py
or the thin shell shims in scripts/launch_reconstruction_schedule.sh,
scripts/status_reconstruction_schedule.sh,
and scripts/stop_reconstruction_schedule.sh.
The launcher validates the plan, checks the backend and required environment
keys, writes non-secret launch metadata, starts a tmux session keyed by
schedule_id, and keeps scheduler.log and analysis.log separate under the
schedule directory.
Minimal plan shape:
{
"schedule_id": "guided-20260310a",
"experiments": [
{
"experiment_id": "baseline-a",
"run_id": "phase4-live-20260310a",
"phase": "phase-4-prompt-baselines",
"command": [
".venv/bin/python",
"src/reconstruction_baselines.py",
"--run-id",
"{run_id}"
],
"timeout_seconds": 3600,
"metric_path_template": "outputs/reconstruction/runs/{run_id}/prompt_baseline_summary.json",
"metric_key": "controls.style_shift.mean_weighted_objective",
"higher_is_better": true
}
]
}Primary outputs:
outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_results.jsonloutputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json
A ready-to-edit example lives at
plans/reconstruction_guided_schedule.example.json.
Treat scheduler plan files as trusted executable specifications: the scheduler
passes the declared command directly to subprocess.run() and does not sandbox
or validate contributor-authored plans.
For an unattended Qwen hidden-reasoning batch bounded to roughly 10 hours, use
plans/reconstruction_guided_schedule.autonomous-10h-20260430.json.
It runs seeded 1-, 2-, and 3-iteration comparisons over the same first four
pilot cases, then spends the remaining budget on a 6-case 2-iteration
confirmation run. Launch it only when the Qwen parser-enabled endpoint is
already serving at http://localhost:8000/v1:
scripts/launch_reconstruction_schedule.sh \
--plan-path plans/reconstruction_guided_schedule.autonomous-10h-20260430.json \
--wandb-project rayuela \
--wandb-mode offline
scripts/status_reconstruction_schedule.sh \
--schedule-id guided-phase4-autonomous-10h-20260430aThe launcher automatically runs Phase 6 analysis with
--schedule-run-selection nonfailed after the scheduler finishes, so the
article-facing output should be read from the schedule analysis directory rather
than reconstructed manually from individual run folders.
The repo is no longer only about Rayuela. src/corpus_cleanup.py, src/corpus_stylometrics.py, and src/corpus_semantic.py extend the same methods to a broader Latin American corpus stored in data/corpus/.
Raw and derived literary texts belong in the local-only data/ working tree, not in version control. Before redistributing the project or reusing the source texts elsewhere, verify that you have the rights to do so.