<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://eigenigma.io/en/atom.xml</id>
  <title>Eigenigma</title>
  <updated>2026-07-31T00:00:00.000Z</updated>
  <subtitle>All content on this website is generated randomly by Shakespeare&apos;s monkeys using LLMs.</subtitle>
  <link href="https://eigenigma.io/en/atom.xml" rel="self"/>
  <link href="https://eigenigma.io/en" rel="alternate"/>
  <author><name>WindFade</name></author>
  <generator>Astro</generator>
  <entry>
    <id>https://eigenigma.io/en/articles/estimating-parameter-counts-of-claude-5-and-gpt-5-6/</id>
    <title>Estimating the Parameter Counts of the Claude 5 and GPT-5.6 Families</title>
    <updated>2026-07-31T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/estimating-parameter-counts-of-claude-5-and-gpt-5-6/" rel="alternate"/>
    <published>2026-07-31T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;import Chart from &quot;~/components/islands/common/Chart&quot;;&lt;/p&gt;
&lt;h2&gt;1. Background: the IKP method and its controversy&lt;/h2&gt;
&lt;p&gt;Since GPT-4, no frontier lab has disclosed the parameter count of a flagship model. The value determines how much VRAM a deployment needs before the model will load, provides the reference for scaling-law extrapolations, and is necessary for assessing how far open models remain behind the closed frontier. When the value is unavailable, estimates appear anyway. The prevailing community estimate is that GPT-4 was a 1.8T MoE (mixture of experts), although nobody with direct knowledge has confirmed it. Estimates for the newest flagships run to the tens of trillions, and none can be checked. OpenAI and Anthropic again kept parameter counts secret when they released GPT-5.6 and Claude 5 this summer, so we sought to produce estimates that can be checked.&lt;/p&gt;
&lt;p&gt;We use IKP, Incompressible Knowledge Probes (&lt;a href=&quot;https://arxiv.org/abs/2604.24827&quot;&gt;arXiv:2604.24827&lt;/a&gt;), proposed by Bojie Li in April. Its premise can be stated in one sentence: long-tail facts cannot be compressed into rules and therefore must be stored in the weights. The corollary requires one more: once the questions are sufficiently obscure, recall accuracy becomes a function of parameter count. Syntax, semantics, and commonsense are regularities that a model learns once and reuses throughout, so their storage cost per use approaches zero. The founding year of an Indonesian polytechnic has no comparable reuse. It is statistically independent of essentially the rest of the corpus, cannot be derived from a rule, and has itself as its shortest description. Every such fact a model memorizes therefore occupies capacity that cannot be shared with other information.&lt;/p&gt;
&lt;p&gt;Allen-Zhu and Li measured a constant consistent with this account. On synthetic corpora, their knowledge capacity scaling law (&lt;a href=&quot;https://arxiv.org/abs/2404.05405&quot;&gt;arXiv:2404.05405&lt;/a&gt;) places a Transformer&apos;s stable storage at about 2 bits of factual knowledge per parameter, at most. IKP applies the law in reverse: because long-tail facts require capacity, recall on those facts provides a measurement of capacity.&lt;/p&gt;
&lt;p&gt;The IKP paper used 1400 factual questions, classified by obscurity from T1 through T7, from universal knowledge to the extreme long tail. Across nearly a hundred open models with published sizes, accuracy increased linearly with the logarithm of total parameter count. The first release reported $R^2 = 0.917$, meaning that log size alone explained more than ninety percent of the variance. The paper then applied the fit to closed models. Two estimates received the most attention: about 9.7T for GPT-5.5 (throughout this article, B is billions of parameters, T is trillions), and about 40.8T for Gemini 3.1 Pro.&lt;/p&gt;
&lt;p&gt;Estimates of that size prompted a rapid review. Two researchers published a &lt;a href=&quot;https://www.lesswrong.com/posts/veFMEzDDyWaer2Sms/sanity-checking-incompressible-knowledge-probes&quot;&gt;systematic reanalysis&lt;/a&gt; on LessWrong and reported two substantive findings. First, the code did not implement the method described in the paper. The original scoring rule penalized wrong answers, so a model that guessed extensively on obscure tiers could receive a negative tier mean. The paper stated that these negative scores were retained, but the code clipped them to zero. This clipping primarily increased the scores of small models, which guess most often on difficult questions. Raising the small-model scores reduces the accuracy range associated with a given range of parameter counts. When that relation is inverted to infer parameters from accuracy, each percentage point corresponds to a larger multiplicative change in parameter count, causing a multiplicative overestimate in frontier extrapolation. Removing the clipping alone reduced $R^2$ from 0.917 to 0.815 and widened the 90% prediction interval from roughly ×/÷3 around the point estimate to ×/÷5.7 (throughout this article, ×/÷$k$ denotes an interval running from the point estimate divided by $k$ to the point estimate multiplied by $k$).&lt;/p&gt;
&lt;p&gt;The second finding concerned the probes. A total of 131 questions, 9.4% of the set, had defects, including name collisions between entities, stale answers, and gold answers that were plainly wrong. These defects were concentrated in the hardest tiers, where frontier models are distinguished. Removing the defective probes and correcting the clipping reduced GPT-5.5&apos;s estimate from 9.7T to about 1.5T.&lt;/p&gt;
&lt;p&gt;The review did not invalidate the method; it separated the stable findings from the unstable ones. Long-tail recall remained nearly linear in log parameter count under every scoring rule and subset examined, with $R^2$ ranging from 0.78 to 0.92. For MoE models, knowledge capacity followed total parameters rather than the active fraction used in a forward pass. The review also confirmed a negative result: long-tail recall at a fixed parameter count has barely changed since 2023, directly contradicting, on this one dimension, the &lt;a href=&quot;https://arxiv.org/abs/2412.04315&quot;&gt;Densing Law&lt;/a&gt; claim that capability density doubles every three months or so. The point estimates were the unstable part, changing substantially with the scoring rule, probe cleaning, and calibration subset. The empirical relation held; the scoring and probe set required correction.&lt;/p&gt;
&lt;p&gt;Those corrections now exist. The paper&apos;s authors merged the principal changes into the public repository: a cleaned 1311-question probe set that removes 89 defective probes under automatically verifiable rules, a narrower removal than the review&apos;s 131, and $\lambda=0$ scoring, with no clipping and no wrong-answer penalty, as defined fully in Section 2. Our own retest of part of the calibration cohort identified another problem that the controversy had not addressed. Investigating it led us to refit the calibration curve, also in Section 2.&lt;/p&gt;
&lt;p&gt;On the revised probe set, scoring, and calibration curve, we measured all six GPT-5.6 variants (Section 3) and the three current Claude 5 models (Section 4), asking two questions of each family: how much capacity, and what lineage. We did not retest GPT-5.5: it is last generation&apos;s model, and the review already gave it a corrected estimate of about 1.5T. Section 5 collects all the estimates and discusses how the numbers should be cited.&lt;/p&gt;
&lt;p&gt;We put the main results up front for readers who only want the numbers. The GPT-5.6 family splits into three series — sol, terra, and luna — while Claude 5 currently fields fable, opus, and sonnet. The sol series shares a rough 2T bracket with fable/opus, the same bracket that holds kimi-k3&apos;s published 2.78T; terra and luna land around 700B to 810B, and sonnet around 970B. All figures are IKP-effective parameters (defined in Section 2), with 90% intervals of about ×/÷3.45 around the point estimates.&lt;/p&gt;
&lt;h2&gt;2. Revising the calibration curve&lt;/h2&gt;
&lt;p&gt;Before measuring any closed model, we re-examined the curve itself. Every IKP inference rests on it. Using the cleaned probe set, the revised paper fits a linear relation between IKP accuracy and the common logarithm ($\log_{10}$) of total parameter count across 93 open models with published sizes from 135M to 1.6T, reaching $R^2 = 0.910$. We call these 93 models the calibration cohort. An MoE model activates only a small fraction of its weights per forward pass, so it has two sizes, total and active; the calibration size axis uses total, and that choice is a fitted result, not a prior assumption. Fitting the mixed size axis $N_{\text{total}}^{\gamma} \cdot N_{\text{active}}^{1-\gamma}$ puts the optimal exponent at $\gamma = 1.00$: the mixed axis collapses into total parameter count, and active parameters contribute almost no additional signal. Bootstrap refits over resampled cohorts give a 90% interval of $[0.86, 1.00]$ for $\gamma$, the upper bound being the edge of the domain.&lt;/p&gt;
&lt;p&gt;The paper&apos;s data collection closed in April; our retests ran in late July, about three months later. If the calibration relation drifted during those three months, every estimate derived from it would lose its basis.&lt;/p&gt;
&lt;h3&gt;Estimand and scoring&lt;/h3&gt;
&lt;p&gt;First, what the method estimates. The quantity IKP measures, written $N_{\text{eff}}$ here and called IKP-effective parameters, answers this question: if the incompressible knowledge stored in the target model&apos;s weights were stored instead by an open model of calibration-cohort-average quality, how large would that model have to be? That size is $N_{\text{eff}}$. It is not a true weight count.&lt;/p&gt;
&lt;p&gt;The ratio of $N_{\text{eff}}$ to the true weight count $N_{\text{true}}$, $\rho \equiv N_{\text{eff}} / N_{\text{true}}$, is called knowledge density: how much knowledge a model stores for its actual size. In practice $\rho$ varies widely. On the refit cohort (the refit itself comes below), the standard deviation of $\log_{10}\rho$ is about 0.33, and since $10^{0.33} \approx 2.1$, one standard deviation amounts to ×/÷2.1. The extremes run from nemotron-70b at $\rho \approx 9.5$ down to llama-4-scout at $\rho \approx 0.29$, a span of about 33×. So two open models of identical true size routinely differ by 2× in knowledge capacity, and in extreme cases by more than an order of magnitude. Because knowledge density varies this widely, every estimate in this article reads &quot;knowledge capacity equal to an open model of size X&quot; and never &quot;has X weights.&quot;&lt;/p&gt;
&lt;p&gt;Second, the scoring rule. A grading model sorts each response into correct, wrong, or refused; an explicit &quot;I don&apos;t know&quot; or a declined answer counts as a refusal. The score is (correct $+\ \lambda \cdot$ wrong) / total, and refusals score 0 at any $\lambda$. The paper&apos;s default is $\lambda = 0$: a correct answer scores 1, wrong answers and refusals both score 0.&lt;/p&gt;
&lt;p&gt;The paper&apos;s argument for not penalizing wrong answers is that capacity and honesty are independent axes. At fixed capacity, the rate of answering-when-not-knowing ranges from 3% to 99% across vendors; any penalty therefore mixes a vendor&apos;s hallucination policy into the size estimate, and the sign of the resulting bias depends on the vendor. We accept this argument as far as it goes, but the argument goes only halfway: it handles the wrong-answer side and stops there. Refusals scored as 0 keep willingness to answer inside the score: at equal knowledge, more refusals mean fewer points. That matters because the LessWrong review had already demonstrated how sensitive the estimates are to scoring choices. $\lambda = 0$ removes one confound, hallucination policy, and keeps another, answer willingness.&lt;/p&gt;
&lt;h3&gt;Drift exposed by retesting&lt;/h3&gt;
&lt;p&gt;When we retested seven representative models of the calibration cohort, that residual confound stopped being a theoretical worry and became an observable fact. The seven span published sizes from 3.2B to 1.6T; the chart below lists their $\lambda=0$ accuracy at paper time and at retest, ordered by size:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/drift.json&quot;
height={300}
client:idle
ariaLabel=&quot;Dumbbell chart of λ=0 accuracy for seven calibration-cohort models at paper time versus retest&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;The seven models split into two groups at 1T. The five below that line reproduce within 2 percentage points of the paper, their two readings nearly coinciding. The two models above it moved 8.8 and 17.6 points, in opposite directions: kimi-k2 (1.04T) fell from 69.2% to 51.6%, deepseek-v4-pro (1.6T) rose from 60.1% to 68.9%. These two are also the two highest-refusal models in the retest, at 39.7% and 20.7%.&lt;/p&gt;
&lt;p&gt;Drift like this needs an explanation, and our first suspect was our own changes. The paper graded with gemini-3-flash-preview; our pipeline used gemini-3.6-flash. If the new grader applied different standards, the whole curve would shift systematically, and the hardest questions — where a grader has the most discretion — would shift most, which would neatly explain why the drift concentrates in the large models.&lt;/p&gt;
&lt;p&gt;To test this, we reran several open models and added claude-haiku-4.5 as a closed-model control. Grader differences can only be compared on identical responses: if the model&apos;s answers themselves changed, a changed verdict cannot be attributed to the grader. So the comparison set was the 605 questions on which responses were nearly identical across the two runs. The result ruled the hypothesis out: on those 605 questions, the old and new graders agreed on every single verdict. In hindsight this fits expectations — the grader&apos;s only task is to compare a response against a gold answer, and its discretion is limited.&lt;/p&gt;
&lt;p&gt;haiku-4.5 eliminated another candidate explanation: that high-refusal models are intrinsically unstable. haiku had the highest refusal rate of the entire batch (43.1%) yet reproduced most precisely, its $\lambda=0$ accuracy within 0.1 points of the paper. If high refusal itself caused instability, haiku should have deviated most; the opposite happened. The drift is therefore not in our pipeline. It is on the vendor side.&lt;/p&gt;
&lt;p&gt;Both drift cases involve changes in response policy, though not at the same depth. deepseek-v4-pro is the straightforward one. Between the two runs, 229 of its responses flipped from refusal to an answer; the refusal rate fell from about 38% to 20.7% over those three months, and $\lambda=0$ accuracy rose 8.8 points. Yet its attempted accuracy — accuracy computed only over the questions it actually answered — moved just 1.7 points. Its stored knowledge did not change; its willingness to answer did. kimi-k2 is more complicated. It refused almost nothing at paper time, refused 39.7% of questions at retest, and lost 17.6 points of $\lambda=0$ accuracy — and here the attempted accuracy also fell, by 7.0 points. That points to something upstream beyond refusal policy, most likely a weight update or a change in deployment configuration. The attempted basis removes the part of drift attributable to response policy, but it cannot remove changes to the weights themselves. Either way, the conclusion is the same. A single upstream deployment update can move a $\lambda=0$ score by double-digit points, and a measurement like that, plugged into a curve fitted three months earlier, yields a meaningless estimate.&lt;/p&gt;
&lt;h3&gt;Refitting on attempted accuracy&lt;/h3&gt;
&lt;p&gt;The corresponding fix is to remove refusals from the denominator and refit the curve on attempted accuracy. We also made two adjustments to the calibration cohort (detailed below), growing it from 93 models to 94. Prediction intervals are estimated by LOFO — leave-one-family-out — cross-validation, which holds out whole model families at a time; models in the same family are highly correlated, so per-model holdout would make the interval artificially narrow. The refit has one cost: $R^2$ falls from 0.910 to 0.898. It has two benefits. The LOFO 90% prediction interval tightens from the original $\lambda=0$ curve&apos;s ×/÷3.51 to ×/÷3.45, and mean drift on the seven-model retest subset drops from 4.1% to 2.4%. Which quantity moved and which did not is the point here. Goodness of fit — cross-model explanatory power at one point in time — barely changed, while repeatability of the same model across time improved considerably. For a method meant to be used again and again, repeatability is the property that matters. The refit curve is&lt;/p&gt;
&lt;p&gt;$$
\log_{10} N_{\text{eff}} = 5.761 \cdot A_{\text{attempted}} - 1.588 \qquad (n = 94,\ R^2 = 0.898)
$$&lt;/p&gt;
&lt;p&gt;where $N_{\text{eff}}$ is in billions and $A_{\text{attempted}}$ is attempted accuracy as a decimal between 0 and 1. Every accuracy reported in this article is a macro-average over the seven obscurity tiers — computed per tier, then averaged — matching the calibration fit. Computed per question, $\lambda=0$ accuracy equals attempted accuracy times the answer rate (one minus the refusal rate); after macro-averaging the identity no longer holds exactly, so cross-checking the numbers in the tables below will show small discrepancies.&lt;/p&gt;
&lt;p&gt;Two adjustments were made to the cohort itself. First, v4-pro&apos;s paper value was replaced with its retest value. The original 60.1% ranked a 1.6T model below the same vendor&apos;s 284B v4-flash (74.8%), even though v4-pro&apos;s attempted accuracy moved only 1.7 points between runs. That anomalous ordering traces to the old snapshot&apos;s refusal behavior rather than to v4-pro&apos;s ability, so the retest value is the cleaner reading. kimi-k2 keeps its paper value. Its attempted accuracy also moved 7 points at retest, and there is no way left to adjudicate which snapshot better represents &quot;kimi-k2,&quot; so we left it alone; its presence or absence at 1T also matters far less to the curve than the anchors at the top.&lt;/p&gt;
&lt;p&gt;Second, kimi-k3 joined the cohort. It has a published total of 2.78T parameters and is currently the only known-size open model at the same scale as the closed flagships. Before it joined, the curve ended at 1.6T and everything above 2T was extrapolation; with it, most of the 1T to 3T range that this article is concerned with becomes interpolation.&lt;/p&gt;
&lt;p&gt;Both known-size anchors lie close to the curve, and each can be checked individually. Holding kimi-k3 out of the cohort, refitting on the rest, and predicting it gives a prediction that misses its published size by a factor of 1.20; the same operation on v4-pro gives 1.24. Both are slight underestimates, and both sit far inside the ×/÷3.45 prediction interval; the position of the curve&apos;s upper end does not rest on these two points alone. Of course, an anchor&apos;s &quot;known size&quot; takes the vendor at its word; the top of the whole inference chain ultimately rests on kimi-k3&apos;s and v4-pro&apos;s self-reported sizes being true. The full refit calibration is shown below, with the two new anchors drawn as diamonds, falling inside the prediction band at the upper end:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/calibration.json&quot;
height={420}
client:idle
ariaLabel=&quot;Scatter plot of the log-linear calibration of attempted accuracy against total parameter count for 94 open models, with fit line, 90% prediction interval, and the two new anchors kimi-k3 and deepseek-v4-pro&quot;
/&amp;gt;&lt;/p&gt;
&lt;h3&gt;Limits of the attempted basis&lt;/h3&gt;
&lt;p&gt;Attempted accuracy rests on an assumption of its own: that refused questions share the difficulty distribution of answered ones. Once refusal correlates with difficulty, the assumption fails. A model that concentrates its refusals on hard questions is overestimated; one that refuses questions it could have answered is underestimated. Section 4 presents measured cases of both directions in the Claude family, along with the correction method, imputation against an external reference model.&lt;/p&gt;
&lt;h2&gt;3. Measuring the GPT-5.6 family&lt;/h2&gt;
&lt;p&gt;OpenAI offers six GPT-5.6 variants on OpenRouter, a model aggregation platform. One naming point needs to be clarified first: there is no standalone &lt;code&gt;gpt-5.6-pro&lt;/code&gt;; &lt;code&gt;pro&lt;/code&gt; is a suffix each series carries separately, so the family reads as three series (sol, terra, luna) of two variants each, not six parallel models. We ran the full 1311-question cleaned probe set on all six variants, with no ungraded responses in any run.&lt;/p&gt;
&lt;h3&gt;Capacity estimates&lt;/h3&gt;
&lt;p&gt;The measurements for the six variants:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Attempted acc.&lt;/th&gt;
&lt;th&gt;$\lambda=0$ acc.&lt;/th&gt;
&lt;th&gt;$N_{\text{eff}}$&lt;/th&gt;
&lt;th&gt;90% interval (LOFO)&lt;/th&gt;
&lt;th&gt;Refusal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;sol-pro&lt;/td&gt;
&lt;td&gt;84.8%&lt;/td&gt;
&lt;td&gt;84.2%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.99T&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;576B – 6.8T&lt;/td&gt;
&lt;td&gt;1.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sol&lt;/td&gt;
&lt;td&gt;84.6%&lt;/td&gt;
&lt;td&gt;83.7%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.93T&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;559B – 6.7T&lt;/td&gt;
&lt;td&gt;1.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;luna-pro&lt;/td&gt;
&lt;td&gt;78.0%&lt;/td&gt;
&lt;td&gt;76.1%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;808B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;234B – 2.8T&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terra-pro&lt;/td&gt;
&lt;td&gt;77.5%&lt;/td&gt;
&lt;td&gt;77.3%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;750B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;217B – 2.6T&lt;/td&gt;
&lt;td&gt;0.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;luna&lt;/td&gt;
&lt;td&gt;77.4%&lt;/td&gt;
&lt;td&gt;75.9%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;744B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;216B – 2.6T&lt;/td&gt;
&lt;td&gt;4.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terra&lt;/td&gt;
&lt;td&gt;77.0%&lt;/td&gt;
&lt;td&gt;76.3%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;702B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;204B – 2.4T&lt;/td&gt;
&lt;td&gt;1.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The table&apos;s defining feature is its tidiness: all six refusal rates sit below 5%, the refusal confound discussed in Section 2 is nearly absent here, and the attempted and $\lambda=0$ bases give almost identical estimates. This stands in contrast to the Claude family in Section 4, and it means the GPT-5.6 readings can be taken at face value with no external correction — face value still meaning &quot;knowledge capacity equal to an open model of size X.&quot;&lt;/p&gt;
&lt;p&gt;These numbers support three conclusions. First, the sol series forms its own bracket: its accuracy leads the terra/luna series by 7 to 8 percentage points, worth about 2.7× in $N_{\text{eff}}$, and the lead holds at every tier from T4 on. The bracket is suggestive rather than statistically separated, though: sol&apos;s lower 90% bound (559B) sits below the terra/luna point estimates, and with prediction intervals spanning 3.45×, overlap of this kind is expected.&lt;/p&gt;
&lt;p&gt;Second, terra and luna are indistinguishable in knowledge capacity. Their attempted accuracies differ by 0.4 points and their intervals nearly coincide; more telling, their ordering flips between the $\lambda=0$ and attempted bases — a ranking that unstable is itself evidence that the difference is noise.&lt;/p&gt;
&lt;p&gt;Third, the &lt;code&gt;pro&lt;/code&gt; suffix adds no knowledge capacity. The three pairs differ by 0.2, 0.5, and 0.6 points, all smaller than the roughly 0.8-point binomial standard error that probe-sampling randomness alone puts on the macro-average. &lt;code&gt;pro&lt;/code&gt; variants across GPT generations are generally understood as products of test-time scaling, the practice of spending additional compute at inference rather than enlarging the weights, and this result agrees: extra inference compute can improve the quality of a reasoning chain, but it cannot add facts to the weights. That IKP reads no &lt;code&gt;pro&lt;/code&gt; difference is consistent with the benchmark&apos;s design goal of measuring stored knowledge rather than reasoning performance.&lt;/p&gt;
&lt;p&gt;The per-tier accuracy profiles support this reading. In the chart below, color distinguishes the three series, and dashed lines mark each series&apos; &lt;code&gt;pro&lt;/code&gt; variant:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/gpt56-tiers.json&quot;
height={380}
client:idle
ariaLabel=&quot;Line chart of accuracy across obscurity tiers T1 through T7 for the six GPT-5.6 variants, colored by series (sol, terra, luna), with pro variants as dashed lines&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;Each conclusion has its counterpart in the tier curves. The two sol lines separate from the four terra/luna lines starting at T4, the gap peaking at 23 points at T6 and narrowing at T7 as every variant falls together; the four terra/luna lines interleave across all tiers and cannot be told apart; and each series&apos; &lt;code&gt;pro&lt;/code&gt; dashed line nearly coincides with its solid one. On T1 through T3 every variant scores above 98%, carrying no signal; the discrimination lives in T4 and beyond, the long-tail tiers — exactly where the benchmark was designed to discriminate.&lt;/p&gt;
&lt;h3&gt;Corroboration from pricing&lt;/h3&gt;
&lt;p&gt;The finding that terra and luna have equal capacity carries a direct implication for the pricing structure. At launch, the &lt;a href=&quot;https://openai.com/index/gpt-5-6/&quot;&gt;official pricing&lt;/a&gt; put sol, terra, and luna at $5.00/$30.00, $2.50/$15.00, and $1.00/$6.00 per million input/output tokens — terra&apos;s per-token price is 2.5 times luna&apos;s. If the two really hold equal knowledge, then terra&apos;s premium over luna buys not knowledge stored in the weights but properties of the inference stack, or simply product segmentation.&lt;/p&gt;
&lt;p&gt;The community had observed the same thing from the behavioral side before our measurement. Sebastian Raschka &lt;a href=&quot;https://x.com/rasbt/status/2075573860796436626&quot;&gt;commented&lt;/a&gt; the day after the GPT-5.6 release: &quot;unless you need Terra Ultra&apos;s performance, you are always better off choosing the Luna model at a higher reasoning effort (same or better performance, and cheaper).&quot; Raschka&apos;s advice applies only to agentic coding. Its exception, Terra Ultra, is terra&apos;s top reasoning-effort setting in the ChatGPT product, formerly the Codex App; it is neither a standalone model nor the terra-pro variant measured here. That exception aside, the economics of the advice match our capacity reading: with an adjustable reasoning budget, terra has no niche of its own relative to luna.&lt;/p&gt;
&lt;p&gt;Artificial Analysis later systematized the observation in a &lt;a href=&quot;https://artificialanalysis.ai/articles/gpt-5-6-intelligence-vs-cost-across-sol-terra-luna&quot;&gt;cost-frontier analysis&lt;/a&gt;: on the Pareto frontier of capability against cost, sol and luna configurations dominate terra throughout — for any terra configuration, there is a sol or luna configuration that is stronger at similar cost or cheaper at similar capability.&lt;/p&gt;
&lt;p&gt;IKP measures stored knowledge; Raschka and Artificial Analysis measure coding performance and cost. The three lines of evidence share no methodological assumptions, and they point the same way: terra and luna hold equal knowledge, and whatever separates the products is not in the weights&apos; stored knowledge. But convergence is not proof, and &quot;equal&quot; here holds only on the $N_{\text{eff}}$ scale. Given the dispersion of $\rho$ from Section 2, two models of equal capacity routinely differ 2× in true size. So there is a mundane possibility: luna&apos;s true size is far below terra&apos;s, and it is simply denser in knowledge. terra could also be an MoE of the same class with a different active parameter count, or a different quantized deployment of the same model. Every one of these scenarios is compatible with all three lines of evidence. The ceiling for black-box measurement is getting independent lines of evidence to converge; conclusions about size and architecture are beyond it.&lt;/p&gt;
&lt;p&gt;While this article was being written, the pricing structure changed. On July 30, OpenAI &lt;a href=&quot;https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/&quot;&gt;announced price cuts&lt;/a&gt;: luna dropped to $0.20/$1.20, a cut of 80%; terra dropped to $2.00/$12.00, a cut of only 20%. terra&apos;s per-token premium over luna went from 2.5× to a full 10×. That number still sits between the same two possibilities: luna&apos;s true size is far smaller and it simply costs far less to serve, or pricing is detached from cost and this is product segmentation. Widening the gap to 10× does nothing to separate the two.&lt;/p&gt;
&lt;p&gt;The wording of the price-cut announcement does not depend on that distinction. It no longer pairs terra with luna; it suggests that users currently defaulting to sol consider evaluating terra when the task does not need maximum intelligence. terra&apos;s reference point has moved from luna to sol. Our measurement says terra has no niche relative to luna; what this adjustment does is remove terra from precisely that comparison.&lt;/p&gt;
&lt;h3&gt;Fingerprinting shared bases&lt;/h3&gt;
&lt;p&gt;Capacity estimates answer &quot;how big,&quot; not &quot;same origin?&quot; For the latter, the IKP paper provides a set of fingerprint metrics, which this section applies to all fifteen pairs of GPT-5.6 variants. The metrics are the paper&apos;s own construction, with no prior literature to lean on, so the definitions need to be given in full. All are computed on the probes of the two long-tail tiers, T5 and T6, only: easy questions are answered correctly by every frontier model and carry no distinguishing signal. Let $C_A$ and $C_B$ be the sets of questions each model answers correctly.&lt;/p&gt;
&lt;p&gt;Jaccard similarity, $J = |C_A \cap C_B| / |C_A \cup C_B|$, measures how much the two models&apos; knowledge coverage overlaps. Its defect is that questions the whole population answers correctly dominate the score: GPT-5 and claude-opus-4.5 were trained independently by different vendors, yet the paper records $J = 0.56$ between them. A high $J$ by itself is no evidence of shared origin.&lt;/p&gt;
&lt;p&gt;Lift divides the intersection size by $|C_A| \cdot |C_B| / n$, where $n$ is the size of the shared question pool. The denominator is the expected intersection size if the two models&apos; correctness were unrelated, so lift above 1 means overlap beyond coincidence. This partially repairs $J$&apos;s defect, but when both models are highly accurate, nearly every question enters the intersection and its size approaches the independence expectation from above. The metric compresses toward 1 and loses discrimination.&lt;/p&gt;
&lt;p&gt;HSS (hallucination signature similarity) is computed on the joint-wrong pool — the questions where both models give a non-refusal wrong answer. HSS is the fraction of those questions on which the wrong answers are identical after normalization: lowercasing, collapsing whitespace and punctuation, truncating to the first 80 characters. The regularity it rests on is that independently trained models almost never fabricate the same wrong answer to the same obscure fact, while weight-sharing models give identical wrong answers on 30% to 55% of joint-wrong questions. There is one correct answer; the space of wrong answers is nearly unbounded. As an individual signature, a wrong answer resolves far more than a right one.&lt;/p&gt;
&lt;p&gt;The paper&apos;s decision thresholds: shared-base requires $\text{HSS} \geq 0.30$ and $J \geq 0.60$; lineage requires $\text{HSS} \geq 0.10$ and $J \geq 0.50$; $\text{HSS} &amp;lt; 0.10$ with a joint-wrong pool of at least 10 questions is ruled retrained. Below 10 questions, HSS is too noisy and no ruling is made.&lt;/p&gt;
&lt;p&gt;On the 368 probes of T5 and T6 combined, we computed all fifteen pairwise combinations of the six variants. The full pair matrix (lower triangle) is below; color depth encodes HSS, and hovering shows each pair&apos;s Jaccard, lift, and joint-wrong pool size:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/gpt56-hss.json&quot;
height={380}
client:idle
ariaLabel=&quot;HSS heat map of all pairwise combinations of the six GPT-5.6 variants (lower-triangle matrix); within-series pairs are markedly darker than cross-series pairs&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;Three cells are dark, and all three are within-series pairs: luna/luna-pro at 0.443, sol/sol-pro at 0.342, terra/terra-pro at 0.239. The twelve cross-series pairs are all light, topping out at 0.167, and two of them read exactly 0.000 on joint-wrong pools that are anything but small — 64 questions for luna/terra, 37 for sol/terra. What makes the reading credible is the control the matrix carries within itself. Same metric, same question pool, joint-wrong samples of comparable size; within-series pairs between 0.239 and 0.443; cross-series pairs never above 0.167; nothing whatsoever between 0.17 and 0.24. Given that separation, a luna/terra value of 0.000 on 64 joint-wrong questions cannot be waved off as the method losing resolution on frontier models. The same method, on the same generation and the same probes, had just resolved three within-series pairs in the same matrix.&lt;/p&gt;
&lt;p&gt;One detail did need a closer look. Several cross-series pairs fall between 0.10 and 0.17, and a value in that range could suggest a weak lineage relation. We listed the shared wrong answers pair by pair, and the supposed signal dissolved: what recurs is the same small set of structurally similar items. &quot;State Polytechnic of Indramayu, founded 2008.&quot; &quot;Quinsaloma, founded 2007.&quot; &quot;National Museum of Ethiopia, founded 1958.&quot; Founding-year questions, all of them, each answered with a plausible year in the same template — the failure mode the paper had warned about, a handful of structurally convergent hallucinations with no overlap in stored knowledge required.&lt;/p&gt;
&lt;p&gt;This step, it should be said, has no technical content at all. Print the wrong answers a pair shares; read them. Andrew Ng&apos;s prescription for error analysis in &lt;em&gt;Machine Learning Yearning&lt;/em&gt; amounts to about the same thing: go through some 100 misclassified dev set examples by hand, put them in categories, and let the counts decide what to optimize next. There was no optimization to decide here, only data to look at, and the return on effort is of one order. HSS is an aggregate number. Numerically, the gap between 0.10 and 0.17 says nothing at all. Three lines of wrong answers said it.&lt;/p&gt;
&lt;p&gt;The only within-series pair below the shared-base threshold is terra/terra-pro, at HSS 0.239; its joint-wrong pool holds 92 questions, the largest in the matrix, so sample size cannot explain the low value. What merits suspicion is not terra-pro&apos;s parentage but the metric&apos;s applicability to pairs of this kind.&lt;/p&gt;
&lt;p&gt;That &lt;code&gt;pro&lt;/code&gt; is a test-time-scaling variant of the same base is a very strong prior across GPT product lines. OpenAI has never disclosed the implementation, but ever since o1-pro the community has read it as inference-time compute of the parallel-sampling-and-aggregation kind, and no public evidence of separate post-training exists; the capacity measurement in this section, a difference of 0.5 points, agrees with that reading, as do the fingerprints of the other two &lt;code&gt;pro&lt;/code&gt; pairs.&lt;/p&gt;
&lt;p&gt;The trouble is that test-time scaling on its own suffices to trigger a false negative in HSS. HSS depends on exact string matches between normalized wrong answers; on a question the model does not know to begin with, a deeper reasoning chain or an aggregation over repeated samples is precisely what perturbs the wrong guess it finally lands on. The stored knowledge has not changed. What changed is the surface form of the wrong answer. Such false negatives have a precedent: the paper reports an extreme case of nemotron against llama-3.1, where $J = 0.81$ coexisted with $\text{HSS} = 0.08$. There, what perturbed the wrong answers was post-training rewording; here, it is resampling at inference. The mechanisms differ; the effect on the metric is the same.&lt;/p&gt;
&lt;p&gt;Taken together, the evidence indicates that terra/terra-pro is most likely a shared-base pair whose &lt;code&gt;pro&lt;/code&gt; inference configuration prevents HSS from confirming the relation. Following the paper&apos;s thresholds, we record it as lineage, but that record should be interpreted as the minimum relation HSS can confirm: the true relation is at least lineage and probably shared base. It remains unclear why the same test-time scaling preserved high HSS in the luna and sol pairs but perturbed only terra&apos;s. Our data cannot answer that question, and the three series&apos; &lt;code&gt;pro&lt;/code&gt; variants need not use a single inference-time strategy.&lt;/p&gt;
&lt;p&gt;The capacity and fingerprint evidence together give the following structure for the GPT-5.6 family: sol, terra, and luna are three independently trained bases, and each series&apos; &lt;code&gt;pro&lt;/code&gt; is a variant of its own base. This has the same structure as the paper&apos;s finding for the previous generation, in which GPT-5, GPT-5-pro, and GPT-5-think form one shared-base cluster.&lt;/p&gt;
&lt;h2&gt;4. Measuring the Claude 5 family&lt;/h2&gt;
&lt;p&gt;On the Anthropic side we measured the three current models — claude-fable-5, claude-opus-5, claude-sonnet-5 — and added the previous-generation claude-haiku-4.5 as a small-size control. All four ran the same 1311-question cleaned probe set, and every run completed with no ungraded responses. The results are below, with both bases side by side. Only the attempted-basis $N_{\text{eff}}$ is a formal estimate; the $\lambda=0$-basis $N_{\text{eff}}$ is not an independently calibrated figure but the $\lambda=0$ score plugged into the same attempted curve, shown only to display, on a common scale, how far refusals depress the reading.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Attempted acc.&lt;/th&gt;
&lt;th&gt;$\lambda=0$ acc.&lt;/th&gt;
&lt;th&gt;$N_{\text{eff}}$ (attempted)&lt;/th&gt;
&lt;th&gt;90% interval&lt;/th&gt;
&lt;th&gt;$N_{\text{eff}}$ ($\lambda=0$)&lt;/th&gt;
&lt;th&gt;Refusal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fable-5&lt;/td&gt;
&lt;td&gt;88.7%&lt;/td&gt;
&lt;td&gt;80.9%&lt;/td&gt;
&lt;td&gt;3.3T&lt;/td&gt;
&lt;td&gt;963B – 11.5T&lt;/td&gt;
&lt;td&gt;1.2T&lt;/td&gt;
&lt;td&gt;12.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus-5&lt;/td&gt;
&lt;td&gt;88.4%&lt;/td&gt;
&lt;td&gt;75.7%&lt;/td&gt;
&lt;td&gt;3.2T&lt;/td&gt;
&lt;td&gt;921B – 11.0T&lt;/td&gt;
&lt;td&gt;593B&lt;/td&gt;
&lt;td&gt;18.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet-5&lt;/td&gt;
&lt;td&gt;77.2%&lt;/td&gt;
&lt;td&gt;68.3%&lt;/td&gt;
&lt;td&gt;719B&lt;/td&gt;
&lt;td&gt;208B – 2.5T&lt;/td&gt;
&lt;td&gt;223B&lt;/td&gt;
&lt;td&gt;20.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;haiku-4.5&lt;/td&gt;
&lt;td&gt;64.0%&lt;/td&gt;
&lt;td&gt;47.2%&lt;/td&gt;
&lt;td&gt;126B&lt;/td&gt;
&lt;td&gt;36B – 433B&lt;/td&gt;
&lt;td&gt;14B&lt;/td&gt;
&lt;td&gt;43.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These figures contrast with the previous section: the six GPT-5.6 variants refuse between 0.6% and 4.5% of questions and their two bases nearly coincide; the four Anthropic models refuse between 12.0% and 43.1%, and the two bases diverge by a factor of 5.4 on opus — 3.2T against 593B — and by a factor of 9 on haiku. The response-policy confound of Section 2, a negligible term for GPT-5.6, is the dominant error source here. The task of this section is therefore not to recite the table but to answer two questions: how large the bias is and in which direction, and whether it can be corrected.&lt;/p&gt;
&lt;p&gt;The first step is to rule out the explanation that the high refusal rates are an artifact of our environment. For the three models the paper had measured — fable, sonnet, haiku — our $\lambda=0$ accuracies land within 0.4 percentage points of the paper&apos;s across the board: if our environment were inducing the refusals, those three numbers could not line up this well. opus-5 was not in the paper; it is our addition. The reproduction also shows the models&apos; behavior has not drifted in the three months since the paper; within this observation window at least, high refusal is a stable trait of Anthropic&apos;s models, not noise from a deployment snapshot.&lt;/p&gt;
&lt;h3&gt;Testing the attempted assumption with peers&lt;/h3&gt;
&lt;p&gt;The attempted basis carries the implicit assumption that refused questions match answered ones in difficulty. Here that assumption can be tested directly, because every model ran the same 1311 questions: for any question model $A$ refused, some other model produced a gradable answer. That allows a counterfactual: impute each of $A$&apos;s refused questions not at $A$&apos;s own attempted accuracy but at the measured accuracy of some reference model $B$ on exactly those questions. If the attempted assumption holds, the two imputations should agree; if it fails, their difference is the direction and magnitude of the bias.&lt;/p&gt;
&lt;p&gt;The credibility of the imputation depends on coverage: $B$ refuses questions too, so imputation can only run on the part of $A$&apos;s refusals that $B$ actually answered. When reference coverage is low, the imputation rests only on questions both models were willing to answer, and that is a systematically easier subset. The extreme cases make the point. Imputing opus with haiku as reference covers 3%, or 8 questions, and the correction computed on those 8 differs from the attempted estimate by 5.20×, more than three times the magnitude seen with high-coverage references. Imputing fable with opus covers 5%, and there the correction differs from the attempted estimate by only 1.03×, apparently unbiased but equally a coincidence at that sample size. Neither constitutes a measurement.&lt;/p&gt;
&lt;p&gt;The choice of reference therefore has two criteria: coverage must be high, and the reference&apos;s knowledge should come as close as possible to covering the target&apos;s. The second criterion matters because imputation implicitly assumes that whatever the reference does not know, the target does not know either. The closer the reference comes to fully covering the target, the smaller the error of that assumption. This is also why the roughly 700B terra and luna are less suited than sol to checking models in the 2T bracket. We drew references from the measured Anthropic models themselves, plus gpt-5.6-sol as an external reference, and only sol meets both criteria: its 1.8% refusal rate gives it at least 90% coverage of every target, its capacity estimate matches the largest of the six GPT-5.6 variants, and it comes from the other vendor, sharing no post-training policy with the models under test.&lt;/p&gt;
&lt;p&gt;The mid-coverage in-vendor references, though unqualified, correct in the same direction as sol: imputing opus with fable (39% coverage) likewise says attempted overestimates, and imputing sonnet with fable or opus (48% and 28% coverage) likewise says it underestimates. The directional conclusion does not rest on sol alone. The chart below shows the sol-referenced imputation; hollow circles are the raw attempted-basis estimates, filled circles the imputed corrections, with line color marking the direction of the correction:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/claude-imputation.json&quot;
height={280}
client:idle
ariaLabel=&quot;Dumbbell chart of attempted-basis versus sol-imputed N_eff for the four Claude-family models; fable and opus corrected downward, sonnet and haiku upward&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;The information in this chart lies mainly in the directions. fable and opus are overestimated by the attempted basis, sonnet and haiku underestimated — and refusal rate does not predict the direction at all: sonnet (20.7%) and opus (18.7%) refuse at nearly the same rate with opposite signs. What sets the direction is the correlation between refusal and difficulty, and that correlation cannot be detected from a single model&apos;s own run; it takes a reference model.&lt;/p&gt;
&lt;p&gt;The overestimation mechanism for fable and opus is the conventional one: their refusals concentrate in the hardest tier, with T7 refusal rates of 57.6% and 70.3%, so attempted accuracy is computed on an easier answered subset and extrapolates high over the full set. The anomaly is sonnet. Its T6 breakdown reads: 28 correct, 56 wrong, 103 refused. The attempted basis sees 33.3% accuracy on its answered questions and assumes the 103 refused ones would go the same way; fable&apos;s measured accuracy on those same 103 questions is 80.6%. sonnet answered the questions it was relatively weak on and refused the ones it could well have gotten right: refusal negatively correlated with difficulty — the underestimation failure Section 2 flagged, realized here. As for the cause, one coherent account is an overconservative honesty policy: the model underrates its own confidence and sorts many answers it would have gotten right into the &quot;not sure enough&quot; pile. But that is conjecture; our data pins down only the sign of the correlation.&lt;/p&gt;
&lt;h3&gt;Corrected readings&lt;/h3&gt;
&lt;p&gt;The sol-referenced imputation yields this section&apos;s core conclusion. &lt;strong&gt;fable-5 and opus-5 are indistinguishable in knowledge capacity&lt;/strong&gt;: their attempted accuracies differ by 0.3 points; imputed, both come out at 2.1T, with imputed accuracies of 85.1% against 85.2%; and their ordering, too, flips between bases. For the imputed estimates we carry over the calibration curve&apos;s ×/÷3.45 90% interval, putting fable and opus at roughly 609B to 7.2T; the imputation adds uncertainty of its own that is not accounted for here, so the true interval is only wider.&lt;/p&gt;
&lt;p&gt;This result can be read against the official product story. Anthropic positions fable-5 as a new tier above opus, and states that it shares its underlying model with mythos-5, the version offered to approved organizations without the added dual-use safety measures. On the dimension of how many facts the weights store, that new tier has no counterpart: IKP cannot read any difference between fable-5 and opus-5. If the tier difference exists, it falls outside what IKP measures, and true size is one such quantity, since equal $N_{\text{eff}}$ does not entail equal $N_{\text{true}}$. If fable-5 really is the larger model, the extra size did not become long-tail facts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;sonnet-5&apos;s corrected estimate is about 970B&lt;/strong&gt;, 1.35 times its attempted-basis 719B. &lt;strong&gt;haiku-4.5 has no citable estimate&lt;/strong&gt;: its three estimates — 14B at $\lambda=0$, 126B attempted, 869B sol-imputed — span a factor of 62. Imputation stops meaning anything here, even though sol still qualifies as a reference by the previous subsection&apos;s criteria, with 96% coverage of haiku. The imputation takes sol&apos;s accuracy on haiku&apos;s refused questions as haiku&apos;s counterfactual accuracy, which amounts to assuming haiku knows what sol knows. For fable and opus, models of sol&apos;s own scale, the assumption is a workable approximation; for haiku, whose true size is far below sol&apos;s, it fails outright, and the 869B it produces is an upper bound, not a correction. For a model refusing 43.1% of the questions, the method has effectively degenerated; haiku&apos;s role in this article is to mark the boundary of the method&apos;s applicability, not to contribute a measurement.&lt;/p&gt;
&lt;h3&gt;Fingerprinting fails under high refusal&lt;/h3&gt;
&lt;p&gt;The natural next step would be to put Section 3&apos;s origin questions to the Claude family, above all the relation between fable and opus. But HSS has no resolving power on these models, and the reason is a structural dependency of the metric.&lt;/p&gt;
&lt;p&gt;HSS needs questions where both models commit to a wrong answer, and Anthropic&apos;s models refuse on exactly the questions most likely to supply such pairs. The chart below plots refusal rate by obscurity tier for all ten closed models in this article, the six GPT-5.6 variants plus the four from Anthropic; past T5, the two vendors split into nearly disjoint clusters:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/refusal-tiers.json&quot;
height={380}
client:idle
ariaLabel=&quot;Line chart of refusal rates by obscurity tier for ten closed models; the four Anthropic models refuse far more than the six OpenAI variants at the high tiers&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;The tally on T5 and T6: Anthropic&apos;s four models refuse 623 times between them, 56 to 305 per model, against 78 for all six OpenAI variants combined. That leaves joint-wrong pools of 3 to 19 questions, where Section 3&apos;s successful within-series rulings had 38 to 92 to work with. In a pool of 11, one coincidental match swings HSS by 0.09. And the failure cuts the other way too: genuine weight sharing can read 0.000, the shared hallucinations still there but walled off from the observable sample by refusal policy. HSS across the six Anthropic pairs never exceeds 0.067, four of them sitting at exactly 0.000 — &lt;strong&gt;not evidence of six mutually independent models, but the metric failing in both directions at once&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;One signal in the matrix is worth writing down: fable-5/opus-5 at $J = 0.743$, the highest anywhere in it, which sits comfortably beside the imputation finding that the two are indistinguishable in capacity. The decision script stamps the pair retrained, matching the high-$J$, low-HSS pattern (Section 3&apos;s threshold: $\text{HSS} &amp;lt; 0.10$ on a pool of at least 10) — a ruling balanced on 11 joint-wrong questions, one above the minimum for ruling at all. Our position is that this data supports no conclusion about the lineage of fable-5 and opus-5. Getting one would take a probe set these models answer instead of refusing, or a fingerprint that does not run on wrong answers.&lt;/p&gt;
&lt;h3&gt;Comparison with community estimates&lt;/h3&gt;
&lt;p&gt;The corrected numbers depart considerably from the guesses in circulation. In April, Elon Musk &lt;a href=&quot;https://x.com/elonmusk/status/2042123561666855235&quot;&gt;asserted&lt;/a&gt; that Sonnet is about 1T and Opus about 5T. Claude 5 had not shipped at the time, so the referents are the 4.x generation and the comparison here is order-of-magnitude only. His 1T is the same order as our corrected sonnet-5 estimate of about 970B. His 5T is about 2.4 times our corrected opus-5 value of 2.1T — still inside the 609B–7.2T 90% interval, so it cannot be formally excluded. The IKP method itself went through a correction in the same direction, from an initial 9.7T for GPT-5.5 down to about 1.5T after the LessWrong review. Word-of-mouth community estimates and uncorrected measurements err the same way, toward exaggeration, and the folk image of flagships holding over ten trillion parameters has no measured evidence behind it to date.&lt;/p&gt;
&lt;p&gt;Corrected, the estimates converge instead: the sol series around 2T, fable-5 and opus-5 around 2.1T, kimi-k3&apos;s published 2.78T in the same range. The largest open model has drawn level with the closed flagships in knowledge capacity. If these estimates broadly hold, then wherever the closed frontier&apos;s lead resides, it does not mainly reside in the total factual knowledge stored in the weights.&lt;/p&gt;
&lt;h2&gt;5. Summary and discussion&lt;/h2&gt;
&lt;p&gt;The figure below gathers the article&apos;s estimates. Each point marks the preferred estimate for a model, the reading we consider most defensible: for the GPT-5.6 family, whose refusal confound is negligible, the attempted basis taken directly; for the Claude family, the sol-referenced imputation corrections. Horizontal bars span the 90% prediction interval, ×/÷3.45 around each point, and the dashed line marks kimi-k3&apos;s published 2.78T for reference. haiku-4.5 is absent because it has no citable estimate, per Section 4:&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/estimating-parameter-counts-of-claude-5-and-gpt-5-6/forest.json&quot;
height={400}
client:idle
ariaLabel=&quot;Forest plot of preferred N_eff estimates with 90% intervals for nine closed models, with a reference line at kimi-k3&apos;s 2.78T&quot;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;The same numbers are listed in table form, so that a point estimate and its interval can be cited as a pair:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Preferred $N_{\text{eff}}$&lt;/th&gt;
&lt;th&gt;90% interval&lt;/th&gt;
&lt;th&gt;Basis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fable-5&lt;/td&gt;
&lt;td&gt;2.1T&lt;/td&gt;
&lt;td&gt;609B – 7.2T&lt;/td&gt;
&lt;td&gt;sol-imputed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus-5&lt;/td&gt;
&lt;td&gt;2.1T&lt;/td&gt;
&lt;td&gt;609B – 7.2T&lt;/td&gt;
&lt;td&gt;sol-imputed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sol-pro&lt;/td&gt;
&lt;td&gt;1.99T&lt;/td&gt;
&lt;td&gt;576B – 6.8T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sol&lt;/td&gt;
&lt;td&gt;1.93T&lt;/td&gt;
&lt;td&gt;559B – 6.7T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet-5&lt;/td&gt;
&lt;td&gt;972B&lt;/td&gt;
&lt;td&gt;282B – 3.4T&lt;/td&gt;
&lt;td&gt;sol-imputed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;luna-pro&lt;/td&gt;
&lt;td&gt;808B&lt;/td&gt;
&lt;td&gt;234B – 2.8T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terra-pro&lt;/td&gt;
&lt;td&gt;750B&lt;/td&gt;
&lt;td&gt;217B – 2.6T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;luna&lt;/td&gt;
&lt;td&gt;744B&lt;/td&gt;
&lt;td&gt;216B – 2.6T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terra&lt;/td&gt;
&lt;td&gt;702B&lt;/td&gt;
&lt;td&gt;204B – 2.4T&lt;/td&gt;
&lt;td&gt;attempted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;haiku-4.5&lt;/td&gt;
&lt;td&gt;— (not citable)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;see Section 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We restate the units and the precision of these numbers. $N_{\text{eff}}$ counts IKP-effective parameters, not weights: a point estimate says the model&apos;s knowledge capacity equals that of an average open model of the given size, and the 90% interval spans about 3.45× in either direction. The statement this article supports is &quot;fable-5 stores knowledge equivalent to an open model of about 2T, with a 90% interval of roughly 609B to 7.2T,&quot; not &quot;fable-5 has 2.1 trillion parameters.&quot; The interval must accompany the point whenever these numbers are cited, because this article has shown more than once how a bare point estimate manufactures false precision: the same opus-5 reads 593B, 3.2T, or 2.1T, depending on the basis.&lt;/p&gt;
&lt;p&gt;Beyond the parameter counts, the measurements produced a byproduct: the two vendors&apos; post-training response policies have diverged far enough to matter measurably. An OpenAI model that does not know tends to answer wrong anyway; an Anthropic model tends to refuse. As a matter of model quality, neither policy is obviously superior — as hallucination control, Anthropic&apos;s choice may well deserve the praise — but the consequences for black-box measurement are asymmetric. IKP measures the GPT-5.6 family directly and answers both the capacity question and the lineage question cleanly. For the Claude family, the capacity estimates need an external reference to correct them, and the lineage question has lost its sample altogether. The corollary: the more honest the model, the harder it is to measure. Intended or not, a refusal policy objectively disables part of what this class of capacity probe can do.&lt;/p&gt;
&lt;p&gt;At the level of method, three conclusions hold independently of any particular model&apos;s estimate.&lt;/p&gt;
&lt;p&gt;First, $\lambda=0$ accuracy is not robust to upstream deployment updates — it can move by double-digit points within three months — so any comparison across time must use the attempted basis or account for refusals explicitly. The attempted basis is not fully robust either: kimi-k2&apos;s attempted accuracy moved 7 points over the same window. That basis removes only the part of drift attributable to response policy, not changes in the weights themselves.&lt;/p&gt;
&lt;p&gt;Second, the attempted basis fails through the correlation between refusal and difficulty, and the direction of the failure cannot be predicted from the refusal rate. Imputation on the same probe set against a high-coverage, low-refusal reference model is the currently feasible way to test for and correct this failure.&lt;/p&gt;
&lt;p&gt;Third, the resolving power of the HSS fingerprint depends on the measured models being willing to guess wrong. On high-refusal models it fails in both directions, and a negative result is not evidence of independence.&lt;/p&gt;
&lt;p&gt;What remains open. The calibration curve&apos;s upper end still hangs on two known-size anchors, kimi-k3 and deepseek-v4-pro, and everything above 2T inherits that limit; each very large open model that publishes its size would thicken the interpolation where it is thinnest. Qwen 3.8 Max Preview, published at 2.4T, would land right between v4-pro and kimi-k3 — but its weights are closed, and that fails the cohort&apos;s admission rule. An anchor needs more than a vendor-published size; the number has to be independently verifiable, and verifying it takes the weights in hand. The model is also served only through Alibaba Cloud&apos;s Bailian platform, where we have no account. Bailian&apos;s Token Plan is a monthly subscription rather than a metered API, so whether benchmarking on it even complies with the terms of service is a question we cannot settle either. Separately: the refusal correction leans on sol, one external reference. Cross-validating against several references could tighten the intervals, as could the deprobing scheme the paper&apos;s authors propose — paired prompts that measure how many refused questions the model in fact holds the answer to.&lt;/p&gt;
&lt;p&gt;The probe set went public in April and every model measured here shipped later, so contamination — probes leaking into training corpora — cannot be formally ruled out. Three months is a narrow window for a pretraining corpus, though, and the readings lack contamination&apos;s signature. On T7, the most obscure tier, the best performers — fable-5 and sol — manage attempted accuracies of about 52% and 24%, far below the roughly 85% both post on the full set. Had the probes entered the corpora, these questions would score highest of all, not lowest. The real test is still a held-out, unpublished probe set: worth building, alongside deprobing.&lt;/p&gt;
&lt;p&gt;Then there is the lineage of fable-5 and opus-5, the question our data cannot answer. Answering it needs probes these models will actually answer, and that is an interesting benchmark-design problem in itself: how to construct long-tail factual questions that even a high-honesty model will hazard a guess on.&lt;/p&gt;
&lt;p&gt;Our experiments build on IKP&apos;s public repository and public probe set, with local modifications to its estimation scripts covering the attempted-basis refit, the refusal imputation, and the fingerprint pairing analysis. Total API spend came to about $126 — the calibration-cohort retest, full 1311-question runs on ten closed models, several 49-question pilots, and all the grading included. Estimating a black-box model&apos;s parameters turns out not to require large-scale compute. What it requires is a calibration curve that has been checked and still holds. A reader with an OpenRouter key can reproduce estimates of this kind for any accessible model at the same order of cost, following the paper repository&apos;s instructions.&lt;/p&gt;
&lt;p&gt;The experimental data &lt;a href=&quot;https://r2.eigenigma.io/ikp-parameter-estimates-data.tar.gz&quot;&gt;can be downloaded here&lt;/a&gt; (1.4 MB). The archive holds per-question responses and verdicts for the ten closed and eight open models on all 1311 questions, the attempted-basis calibration outputs, and the cleaned probe set we used; from these files a reader can recompute every table in Sections 3 and 4, the drift accounting in Section 2, and the HSS and imputation analyses.&lt;/p&gt;
</content>
    <category term="AI"/>
    <category term="LLM"/>
    <category term="GPT"/>
    <category term="Claude"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/the-node-that-never-sleeps/</id>
    <title>The Node That Never Sleeps</title>
    <updated>2026-06-27T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/the-node-that-never-sleeps/" rel="alternate"/>
    <published>2026-06-27T00:00:00.000Z</published>
    <content type="html">&lt;h2&gt;I. Where Things Stand&lt;/h2&gt;
&lt;p&gt;Right now, all twenty threads of ms-01-1 are pinned at full load, spinning on nothing, the CPU holding steady at 77°C. It isn&apos;t computing anything. It&apos;s just busy staying awake. These cores should be asleep when there&apos;s no work; instead a busy-wait loop keeps every one of them occupied, for the sole purpose of never letting the CPU enter a sleep state.&lt;/p&gt;
&lt;p&gt;Another machine watches it from the other end of the network. The moment it locks up, that machine cuts its power remotely and brings it back.&lt;/p&gt;
&lt;p&gt;ms-01-1 is one node in the small cluster I run at home, a Minisforum MS-01 mini PC. Right now it&apos;s serving normally: its VMs are running as usual, the data it holds is online, the cluster is healthy. The busy-wait loop isn&apos;t some extra workload I&apos;ve added, and it doesn&apos;t fight the VMs for CPU — it only occupies time that would otherwise be idle, and yields the instant real work arrives. It&apos;s also a machine I&apos;ve given up on actually fixing.&lt;/p&gt;
&lt;p&gt;The fault is in the hardware. What I can say for certain is that it isn&apos;t in the DIMMs themselves, but somewhere in the machine that can&apos;t be replaced. My closest read on it is the link that carries addresses and data between the CPU and memory: every so often, somewhere in transit, it flips a single bit. By that account the memory cells are sound the whole time; the error happens only in the instant of transfer, and the next read &quot;heals&quot; it on its own. And this machine runs non-ECC memory[^ecc], which has no mechanism to detect such an error, let alone report it — so it leaves behind not one line of log, not one counter tick, completely invisible to the system. Either nothing happens at all, or a single process dies abruptly to a segfault, or the entire machine locks up without warning.&lt;/p&gt;
&lt;p&gt;Not letting it sleep is the one thing that still works, after I&apos;d tried everything else. Swap the DIMMs, swap the power supply, flash the BIOS, drop the memory frequency — I went through them one by one, and none of it helped. If it could be fixed, I&apos;d have fixed it long ago; but it&apos;s out of warranty, and the broken part can&apos;t be replaced on its own, so no matter how I run the numbers it isn&apos;t worth it. So it stays where it is, kept running by this one setting that keeps it hot all day, with a watchdog on the machine across from it as a last line of defense.&lt;/p&gt;
&lt;p&gt;The thing that finally saved it — don&apos;t let the CPU sleep — occurred to me early on. I just didn&apos;t chase it down right away.&lt;/p&gt;
&lt;p&gt;How it got to this point starts with a tripped breaker a month ago.&lt;/p&gt;
&lt;h2&gt;II. The Breaker&lt;/h2&gt;
&lt;p&gt;Late one night a month ago, the breaker tripped at home. Seven servers lost power hard, all at once. My UPS, of all things, wasn&apos;t on the network — it couldn&apos;t raise an alert, never mind trigger a graceful shutdown — so I didn&apos;t find out until I woke up the next morning.&lt;/p&gt;
&lt;p&gt;Those seven machines are a small cluster I built at home: Proxmox VE for virtualization, Ceph for storage, deployed hyperconverged — compute and storage running on the same set of machines — hosting my own GitLab, three Kubernetes clusters, internal DNS, single sign-on, Time Machine backups for my Mac, and a string of other services.&lt;/p&gt;
&lt;p&gt;I don&apos;t run all this because the services on top matter that much. Almost everything is cattle, not pets — when one breaks I don&apos;t nurse it, I rebuild it. The config all lives in Git; I&apos;ve rebuilt the entire GitLab from scratch once already, and the backups have backups. The banner I started under was suitably high-minded, of course: data sovereignty, keeping my own data in my own hands. But after a few years, if I&apos;m honest, what actually draws me in is the tinkering with infrastructure itself — and the software layer most of all. Declarative config, Nix, GitOps, Terraform: that&apos;s where the fun is. As for the services that were supposed to carry the whole &quot;data sovereignty&quot; banner, only a handful ever went live or got used. Hardware interests me even less. And precisely because it doesn&apos;t matter if the workloads on top go down, I didn&apos;t care whether the memory had ECC — these mini PCs don&apos;t support it anyway — and precisely because it was cheap, buying a machine like this was a trade-off I was happy to make.&lt;/p&gt;
&lt;p&gt;ms-01-1 had thrown the occasional segfault before — sporadic, nothing that got in the way, so I hadn&apos;t paid much attention. After that power loss, the symptoms got an order of magnitude worse. Over the next two weeks, the segfaults went from once in a while to dozens on every boot; the share of storage it carried wouldn&apos;t come up, and the whole machine started locking up without warning every few days. It was always the same handful of processes going down, though that didn&apos;t stand out at the time. I can&apos;t prove the power loss broke it. It had been faulty to begin with; all I can be sure of is the order in time. I kept putting off dealing with it properly, until two weeks ago I finally took it on.&lt;/p&gt;
&lt;p&gt;Here&apos;s the odd part. Two of the seven are identical MS-01s — same CPU, same BIOS, same memory model, same config, bought in adjacent batches. The other one, ms-01-2, never had a single problem, start to finish. Both lost power that night; same model, the same non-ECC memory — and only ms-01-1 went wrong.&lt;/p&gt;
&lt;h2&gt;III. Withholding Judgment&lt;/h2&gt;
&lt;p&gt;I&apos;d guessed the rough direction early: C-states — the power-saving sleep levels an individual core drops into when it has nothing to do, deeper meaning more savings and a slower wake-up. It&apos;s the core that sleeps, not the whole machine: the moment a core runs out of work, it nods off on its own, while the machine keeps running and serving as normal.&lt;/p&gt;
&lt;p&gt;The crashes had a pattern: they struck when the machine was idle or lightly loaded, and almost never under full load. That &quot;the more idle it is, the more likely it breaks&quot; shape points naturally at these sleep levels. And the MS-01 had form here: early BIOS versions (around 1.22) had a &lt;a href=&quot;https://forums.servethehome.com/index.php?threads/minisforum-ms-01-review-the-10gbe-with-pcie-slot-mini-pc.42827/&quot;&gt;stability issue tied to C-states and other power-saving features&lt;/a&gt;, fixed in later releases. Two signs stacked up, pointing the same way.&lt;/p&gt;
&lt;p&gt;But knowing there&apos;s a switch you could throw, and reaching out to throw it, are two different things. I didn&apos;t throw it.&lt;/p&gt;
&lt;p&gt;The reasoning is simple: some change happening to stop the crashes is not the same as my having found the cause. That change could easily be suppressing the symptom while the real fault sits untouched underneath. For a fault that occurs at a steady rate, &quot;I changed it and it got better&quot; counts as at least a little evidence. But this machine&apos;s fault rate itself drifts: same config, under one percent on one boot, drifting past fifty percent some time later. Against that background noise, whether it crashes after a change tells you nothing. No crash might mean fixed, or it might mean I caught a quiet stretch. A crash reads two ways too, and neither beats the other. A test that every outcome confirms is no test at all.&lt;/p&gt;
&lt;p&gt;So more than making it stop crashing on the surface, I cared about where it was actually broken. It comes down to one rule: until I&apos;d ruled out the other possibilities one by one, this machine was running sick in my eyes. Strictly speaking, the sickness wasn&apos;t the machine&apos;s but mine — but as long as the doubt held, I treated it as sick.&lt;/p&gt;
&lt;p&gt;That rule set the course for everything that followed. The string of seemingly futile tests to come were all deliberate elimination, one suspect at a time. Unlike the power switch I didn&apos;t throw, every one of these tests could give an answer I wouldn&apos;t like: move the DIMMs to the twin and the fault might follow them; memtest might actually report errors; some frequency might turn out to be the culprit. It&apos;s precisely because they could fail that their results meant anything.&lt;/p&gt;
&lt;p&gt;And this whole scheme of elimination held together because of the healthy twin, ms-01-2, as a control. One broken, one sound, everything else identical. It let me sidestep the unanswerable question — did my change actually do anything — and swap in an answerable one: move a part between the two machines, and watch whether the fault follows the part or stays with the original. That one I can answer, because I&apos;m only watching which side the fault lands on, regardless of how high its rate happens to have drifted.&lt;/p&gt;
&lt;h2&gt;IV. Ruling Things Out&lt;/h2&gt;
&lt;p&gt;The first suspect, naturally, was the DIMMs themselves.&lt;/p&gt;
&lt;p&gt;The standard way to vet memory is to run memtest86+, which works outside the operating system, reading and writing every memory cell over and over and comparing the results. Just getting it to boot ate a whole night: disabling Secure Boot, chainloading it from GRUB, getting stuck on POST several times along the way with a black screen, nothing to do but cut the power remotely and start over. Once it was actually running, I didn&apos;t dare stop at two passes — experience says ten at least. So it ran for twenty-one hours and forty-eight minutes, a full ten passes, the temperature riding up around 90°C the whole time.&lt;/p&gt;
&lt;p&gt;The result was zero errors.&lt;/p&gt;
&lt;p&gt;It was the first dead end of the whole investigation. A test passing perfectly clean, and yet ruling nothing out — at most it pushed one possibility, that the memory cells were physically damaged, down to very unlikely. What memtest reads, writes, and compares is whether data is stored correctly, and this machine&apos;s problem wasn&apos;t there.&lt;/p&gt;
&lt;p&gt;I tried a second tool of the same kind: stressapptest, which saturates memory bandwidth to force out flaws in the high-speed signaling. Four hours, more than seven hundred terabytes moved, and again zero events. Two tools, the same clean bill.&lt;/p&gt;
&lt;p&gt;Clean, and still the machine crashed. I stepped back and started doubting the premise itself: maybe it wasn&apos;t hardware at all, but software — some kernel bug. The kernel version it was on did carry a few known regressions in memory management. I upgraded it to a newer version that fixed those regressions. That evening the reproducer ran a couple hundred rounds without a single crash; I nearly took it for over and done, and left it running overnight. By the next morning, some twelve hundred rounds and two hours and fifty minutes of continuous running later, the whole machine had frozen there without warning.&lt;/p&gt;
&lt;p&gt;Only later did I understand this step was not like the others. The new kernel really did fix a software bug — one that was real, and an entirely separate matter. It fired fast and often, taking the blame for every crash, so I kept assuming the crashes were a software problem. Once it was fixed, the hardware fault hiding behind it — far slower to fire — finally showed its face. That software bug was never the fault itself, only a smokescreen drawn across the hardware fault. The upgrade fixed nothing; it just cleared the smoke. Those couple hundred clean rounds were exactly the kind of &quot;no crash after a change&quot; I&apos;d been most wary of — and the hardest kind to see through. The other steps gave me at most a useless clean result; this one nearly became a false finish line. The smoke gone, and a low stretch of the hardware fault happening to coincide — the two together almost had me call it fixed and walk away.&lt;/p&gt;
&lt;p&gt;At the time, of course, I didn&apos;t see it that clearly. With the kernel failing to settle it, my suspicion was still circling outside the hardware — and firmware falls in that same band. I flashed the BIOS to the latest version. That old 1.22 C-state business had pointed this way to begin with. After the flash, it still froze; the release notes had not one entry touching memory, DDR5, or the memory controller. That old lead fell apart too: the community&apos;s firmware-level C-state bug, the one that hit every machine, was a different matter from mine — the twin ran the same BIOS and was perfectly fine. By this point I&apos;d done everything that could be done from the keyboard without opening the machine: software ruled out, firmware ruled out, the memory-checking tools all run clean. The fault really did live in this machine&apos;s hardware — and hardware isn&apos;t a single block.&lt;/p&gt;
&lt;p&gt;To tell which block had gone bad, all that was left was to get my hands in. Up to here — flashing the BIOS, booting memtest, running those reproducers — I&apos;d never had to leave my study: these machines have no BMC[^bmc], but each has an IP-KVM (which brings the display and keyboard out over the network for remote control); a hard power cycle relies on a switched PDU in the rack (a networked power strip you can toggle remotely). All of it doable from the keyboard. Pulling hardware was the first thing the IP-KVM couldn&apos;t help with. It could put a keyboard and a screen in front of me, but it couldn&apos;t turn a screw for me. I&apos;d put this step off to the very end, because the cost of it, for me, is staggering. This machine and three other mini PCs are crammed onto the same 3D-printed rack kit I made myself, each with four fibers, a power cable, an Ethernet cable, an HDMI, and a USB running off the back. To touch the hardware of any one of them, I have to open the side panel of the rack, put on a headlamp, note where every cable goes, pull them out one by one, then take a cordless screwdriver and lift the whole kit off the rack. The kit fits tight; putting it back is more work than taking it out. A round trip like that runs over an hour.&lt;/p&gt;
&lt;p&gt;The first thing that swap could distinguish was whether the DIMMs were bad or the machine itself. I switched the memory between the two MS-01s: ms-01-1&apos;s DIMMs into the healthy ms-01-2, ms-01-2&apos;s into ms-01-1. The logic is simple — if the fault followed the DIMMs to ms-01-2, the DIMMs were the problem; if it stayed with ms-01-1, the machine was. The fault stayed with ms-01-1: with a known-good pair of DIMMs in it, it crashed all the same. So not the DIMMs, but the machine itself — the power delivery on the board, the memory controller inside the CPU, the CPU socket, that class of thing, all either soldered down or beyond fixing by swapping a part. Given how punishing the teardown was, I just left this swap in place rather than reverse it.&lt;/p&gt;
&lt;p&gt;The power supply was next. Strictly, having narrowed it to the machine itself, the supply counts as one part of that, but it hadn&apos;t been checked on its own, and there was reason to suspect it: the fault picked idle moments to strike, which fits a flaw where the machine jolts from idle into activity and the power delivery doesn&apos;t keep up. So I opened it up again, connected ms-01-1 to ms-01-2&apos;s known-good power adapter, and ran the same test. It still crashed at almost exactly the same rate; the supply was fine. I could have skipped this teardown — I&apos;d already narrowed it to the machine itself — but skip it, and the supply would never truly have been checked, and the machine would still be running sick in my eyes.&lt;/p&gt;
&lt;p&gt;Last was the memory frequency. I dropped it from 5600 all the way to 4400, then tried 5200. On the surface the numbers differed from one step to the next, but once you account for that self-drifting fault rate, no real difference could be told between them. Frequency wasn&apos;t the knob that moved the outcome either.&lt;/p&gt;
&lt;p&gt;Whether the data was stored correctly; whether it was software or firmware outside the hardware; which block of the machine it was — I&apos;d put each of these three layers to the question. But each layer answered at most &quot;very unlikely,&quot; never &quot;impossible&quot;: a test only pushed one way-of-being-broken down far enough to set aside; none was truly ruled out. The one exception was the DIMM swap: the fault didn&apos;t follow them, and that one is certain. What&apos;s left standing is a fault that genuinely lives in the machine itself, yet that no test ever truly saw.&lt;/p&gt;
&lt;h2&gt;V. The Invisible Fault&lt;/h2&gt;
&lt;p&gt;The reason those &quot;zero error&quot; tests passed clean one after another is that they were all the same kind of test — and that kind is, by its nature, nearly blind to this fault.&lt;/p&gt;
&lt;p&gt;To see this, you first have to separate two ways of being broken. One is that after a value is stored in memory, the storage cell itself goes wrong, and what was written doesn&apos;t match what&apos;s read back; this is exactly what a conventional memory test assumes. This machine isn&apos;t that case: the memory cells were sound the whole time. The best account I can give is that the error is in the carrying, not the storing: a value travels back and forth between CPU and memory, and now and then a single bit goes astray in transit — but once it settles, what&apos;s stored is correct, and reads back correct. If that&apos;s so, the error happens only along the way, with both ends clean: what&apos;s written is right, and what&apos;s read is right.&lt;/p&gt;
&lt;p&gt;Tools like memtest and stressapptest are all, at bottom, doing the same one thing: write a value in, read it back a while later, and compare the two. But that &quot;write it in, read it back, compare once&quot; approach is precisely what misses a fault that errs in the instant of transfer and has already recovered by the time it&apos;s read: by the time you compare, it has long since healed, leaving not a trace in the data. They have another thing in common: they run memory and CPU at full load, while this fault is at its most active exactly when the machine is idle. A test congenitally blind to it, and that happens to suppress its trigger condition, naturally turns up nothing.&lt;/p&gt;
&lt;p&gt;I even wrote a small tool with Claude Code, named it memchase, to try to force it out. The tool has no other use: it builds an enormous ring of pointers in memory, then chases it around at the highest density I could manage. Each pointer read is used at once to jump to the next — a true &quot;read one, use one&quot; — and every jump is checked on the spot against an independent table, so a bit flipped in transit gets caught red-handed. To meet the &quot;only fires when idle&quot; condition, it also leaves a gap between each run of the chase, letting the core fall asleep and wake again. Pointer density maxed out, used the instant it&apos;s read, idle gaps and all, it ran more than eight hundred million steps and never made a sound. This time, &quot;caught nothing&quot; carried the most weight of all: even a tool aimed squarely at it, that genuinely did sleep, that took pointer-chasing to its limit, couldn&apos;t force it out. It proved nothing, but it squeezed the &quot;broken in the storage cells&quot; path down to a sliver: the fault almost certainly isn&apos;t in the stored data, but somewhere along the road the data travels. As for why taking pointer-chasing to the limit still wasn&apos;t enough — I wouldn&apos;t understand that until I remembered something else.&lt;/p&gt;
&lt;p&gt;What finally brought it into the open wasn&apos;t another tool, but a detail I remembered.&lt;/p&gt;
&lt;p&gt;I&apos;d noticed the crashes seemed to come whenever I ran a certain class of command: the Proxmox VE management tools — &lt;code&gt;qm&lt;/code&gt;, &lt;code&gt;pct&lt;/code&gt;, &lt;code&gt;pveversion&lt;/code&gt;, that lot. They had one thing in common: every one is a Perl script.&lt;/p&gt;
&lt;p&gt;This was exactly what memchase had missed. The key was never &quot;lots of pointers&quot; — memchase had already maxed that out, to no effect. What mattered was that Perl, every time it runs, rebuilds its whole structure from scratch. The Perl interpreter is forever dereferencing pointers and following them inward; and each call to these PVE tools forks a new process, reloads a large batch of modules, and throws up great pointer structures in freshly allocated memory, then uses them right away. memchase chased a ring built once and never changed again; Perl, by contrast, ceaselessly manufactures new, used-in-an-instant pointers: a value has just arrived, no time yet to self-heal, and it has already jumped in. Flip one bit on that very jump, and it charges straight into a garbage address and crashes on the spot. A workload like this — constantly creating, using at once, sliced into idleness by fork after fork — happens to hit the fault&apos;s trigger condition and its most lethal use at the same time.&lt;/p&gt;
&lt;p&gt;So the final reproducer hardly counts as a memory test at all. It&apos;s nothing more than putting one such Perl tool in a loop, calling it over and over, and counting the crashes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0; while :; do i=$((i+1)); pveversion &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || echo &quot;FAIL $i&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;pveversion&lt;/code&gt; is the simplest of those Perl tools, and the script just runs it endlessly and prints a line whenever it crashes. On ms-01-1 it crashed on the second round, and crashed steadily after that — roughly one failure in every two. On the healthy twin ms-01-2, three hundred calls in a row, not one crash. Orders of magnitude faster than any memory test before it.&lt;/p&gt;
&lt;p&gt;The same fault, but its consequences fall into tiers, depending on where the flipped pointer lands. Land in a user process, and that process dies to a segfault and exits while the machine lives on — the most common tier. Land on a kernel pointer, and the kernel oopses (a crash in kernel space), leaving a task that can&apos;t be killed by any means. Land deeper still, on the scheduler or some global lock, and the whole machine freezes without warning: no log, no panic, nothing left behind.&lt;/p&gt;
&lt;p&gt;That kind of freeze had one strange detail: the machine was stone dead and yet still answered a ping. I&apos;d ping it and ICMP came back inside a millisecond; and yet at the same time SSH wouldn&apos;t connect, even a raw connection to its SSH port timed out, and the picture on the monitor was frozen. It was still answering ICMP while everything above that had stopped responding. Which layer down there was still moving, I didn&apos;t dig into.&lt;/p&gt;
&lt;p&gt;By this point I knew what it was, how to force it out, and what it could do. None of which was a fix.&lt;/p&gt;
&lt;h2&gt;VI. Limping Along&lt;/h2&gt;
&lt;p&gt;What finally held it down was the very thing I&apos;d pointed at from the start: C-states. After two weeks of going around, the answer landed back on the same spot as the first hour&apos;s guess.&lt;/p&gt;
&lt;p&gt;But &quot;guessing it&quot; and &quot;acting on it correctly&quot; are two different things, and the distance between them was very nearly the whole investigation.&lt;/p&gt;
&lt;p&gt;The easiest move is to disable the deep sleep states outright and pin the CPU to the shallowest sleep level. I tried it. For the first two hours it was flawless, not a single crash. Had I stopped there, this article would end here. But I let it keep running, and in a fresh round it froze again. Those two &quot;flawless&quot; hours were nothing but a trough the fault rate had drifted into on its own — the very trap I&apos;d refused to step into at the start. This time, the same trap sprang shut on me, exactly as before.&lt;/p&gt;
&lt;p&gt;Digging further, the mechanism came clear. Cap it at the shallowest level and the core still sleeps — just not deeply — and it froze all the same. So what triggers the fault is the act of the core sleeping itself, regardless of how deep. As long as it still closes its eyes, even the lightest doze, the fault still has its chance. The one road that avoids it entirely is to not let the core sleep at all. I switched the kernel&apos;s idle policy to &lt;code&gt;idle=poll&lt;/code&gt;, so that even when a core has nothing to do it doesn&apos;t sleep but spins on a busy-wait loop, the whole CPU awake without pause. This fixed nothing — the broken hardware is untouched — it just removed the triggering act wholesale. One CPU spinning hot forever like this is hardly elegant, but it works. It&apos;s the exact inverse of that earlier reproducer: the reproducer used relentless forking to send most cores in and out of idle; this keeps every core awake at all times, with not a sliver of idle left.&lt;/p&gt;
&lt;p&gt;This time the fault rate dropped to zero. This zero I trust more than those two peaceful hours before it: the shallow-sleep run merely happened not to fire, the cores still sleeping, the risk there the whole time; now the cores never close their eyes at all, and the triggering act is gone at the source. But zero across any number of runs is still only not having seen it happen — it can&apos;t prove it never will.&lt;/p&gt;
&lt;p&gt;The price is heat. Run every core spinning around the clock and this i9, packed into its little chassis, runs hot — so I set a power cap on the whole CPU, holding the steady-state temperature at 77°C, a dozen-odd degrees of margin from the thermal-throttle line. Full load, hot, never asleep.&lt;/p&gt;
&lt;p&gt;Precisely because there&apos;s no guaranteeing it never happens again, there&apos;s that last line of defense. A machine that&apos;s truly frozen needs someone to cut its power once, hard, to bring it back. And this machine has no out-of-band management; the only thing that can hard-reboot it from afar is that switched PDU in the rack. So on another machine I set up a watchdog, watching ms-01-1. That strange detail I hadn&apos;t dug into earlier became a design constraint here: a frozen machine can still answer a ping, but answering a ping isn&apos;t the same as being alive. So the watchdog doesn&apos;t rely on ping; it probes two layers at once — whether SSH can log in, and whether the bare port 22 will accept a connection. Only when neither answers does it judge the machine truly dead, then cut and restore power through the PDU to bring it back.&lt;/p&gt;
&lt;p&gt;By this point, this is about all that can be done. If I could RMA it, I&apos;d have RMA&apos;d it long ago — but it&apos;s out of warranty; the one &quot;real fix&quot; left is to send the whole board out for third-party repair, which isn&apos;t worth it. So it sits in this state: a node still in production service, with another machine beside it ready at any moment to cut its power and bring it back.&lt;/p&gt;
&lt;h2&gt;VII. Certainty&lt;/h2&gt;
&lt;p&gt;From these two weeks of detours I didn&apos;t come away with a way to cure it. What I came away with was certainty: certainty that the problem is in the machine itself and nothing else; certainty that no swap, no flash, no downclock will help; certainty that this is what it truly is. For a machine it doesn&apos;t matter if it breaks, this certainty is exactly what I&apos;d wanted all along.&lt;/p&gt;
&lt;p&gt;Laid out plainly, the whole thing is out of proportion: two solid weeks of hardware forensics — twin control, cross-swapping parts, a purpose-built tool, a dozen-odd reboots — spent on a node that is cattle through and through. But that disproportion is probably the natural state of a certain kind of homelabber: what they&apos;re serious about is the tinkering itself, rigorous in method, while what runs on top, and whether it breaks, hardly matters. It&apos;s that same preference that led me to pick this non-ECC mini PC in the first place and plant the flaw in it — and then, after the fact, made me willing to spend two weeks running that flaw to ground.&lt;/p&gt;
&lt;p&gt;If it were someone else&apos;s machine, I&apos;d probably tell them to let it go.&lt;/p&gt;
&lt;p&gt;This one is mine.&lt;/p&gt;
&lt;p&gt;[^ecc]: ECC — Error-Correcting Code memory. It checks data on every read and write, catching and correcting single-bit errors and reporting them to the system. Consumer platforms, and this mini PC, use plain non-ECC memory: without that check, an error is neither corrected nor recorded, completely invisible to anything above it.
[^bmc]: BMC (Baseboard Management Controller), or out-of-band management — a management chip independent of the operating system and separately powered, which lets you power-cycle a machine, view its console, and install an OS even when it&apos;s dead. Proper servers mostly have one; these mini PCs don&apos;t, so once one freezes, the only way in is to cut power at the source.&lt;/p&gt;
</content>
    <category term="Troubleshooting"/>
    <category term="Hardware"/>
    <category term="Kernel"/>
    <category term="Linux"/>
    <category term="Homelab"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/two-weeks-of-self-hosting-cjk-fonts-from-lighthouse-to-clreq/</id>
    <title>Two Weeks of Self-Hosting CJK Fonts: From Lighthouse to CLReq</title>
    <updated>2026-05-01T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/two-weeks-of-self-hosting-cjk-fonts-from-lighthouse-to-clreq/" rel="alternate"/>
    <published>2026-05-01T00:00:00.000Z</published>
    <content type="html">&lt;h2&gt;1. The Trigger&lt;/h2&gt;
&lt;p&gt;It started with a screenshot a friend sent me one day. Which post it was of, I no longer remember; the details of the screenshot itself are gone too — but one glance was enough. On Windows, the blog really did look ugly.&lt;/p&gt;
&lt;p&gt;Pinning the whole thing on Microsoft YaHei would be unfair. I&apos;m not a type specialist; I can&apos;t tell you which stroke weight, which hinting decision, or which spacing rule is the one that hurts. But there was no single cause. YaHei is part of it: Windows has shipped it as the default Simplified Chinese UI typeface since Vista[^os-cjk-defaults], and the design&apos;s hinting choices, tuned for an earlier era&apos;s screen-rendering pipeline, read heavy and slightly muddy at body-text sizes today. Windows&apos; font-rendering pipeline is part of it. And the display is part of it — my friend wasn&apos;t on a cheap panel, but compared to the 6K display I use every day, the pixel density was a clear step down. That step down matters more for CJK than it would for Latin: a Han character carries more distinguishing detail per unit area than a Latin letter does — counters, internal stroke separations, component boundaries — and the same loss of pixel density that mildly degrades Latin text degrades CJK text more sharply.&lt;/p&gt;
&lt;p&gt;This forced me to confront something I had never actually thought through: &lt;em&gt;your website is not for you.&lt;/em&gt; As a long-time non-Windows user — over a decade on Linux before macOS — sitting at a 6K display every working day, I had only ever seen this blog under ideal conditions. Without realizing it, I had let those conditions become &quot;what the site looks like.&quot; Readers were not like that. The asymmetry between what I saw and what a typical reader saw was sharper than the equivalent gap on a Latin-script site would have been. To a working designer this is day-one common sense; for someone whose job is writing code, it took a screenshot to the face from a friend before any of it surfaced. Looking back, font rendering was never an isolated visual concern. It is structurally a slice of accessibility — making a paragraph readable across hardware, operating systems, and pixel densities is the same kind of problem as making it audible to a screen reader, or distinguishable to a color-blind eye — though the frame is probably wider than the actual job in front of me.&lt;/p&gt;
&lt;p&gt;None of this contradicts my long-standing posture of &quot;I don&apos;t write with an audience in mind.&quot; That posture means I don&apos;t write for traffic; the content comes before every metric. But the site itself has to account for every reader — as a matter of parity, not pandering. I do full GDPR compliance for an EU readership that is effectively zero, and full a11y work for an equally negligible screen-reader audience. Optimizing fonts for readers on Windows with commodity displays is a different slice of the same commitment.&lt;/p&gt;
&lt;p&gt;I opened a ticket that day. It sounds faintly ridiculous — running my own blog through that kind of process — but overengineering has always been my default mode. A week later, I started.&lt;/p&gt;
&lt;h2&gt;2. Phase One: Self-Hosting and Iterative Subsetting&lt;/h2&gt;
&lt;p&gt;When I opened that ticket, I had no idea what I was walking into. My expectation was a weekend-afternoon job: pick a decent Chinese typeface, hand it to an off-the-shelf subsetting tool, wire it up with &lt;code&gt;@font-face&lt;/code&gt; and &lt;code&gt;preload&lt;/code&gt;, done. The expectation was reasonable for Latin: a standard Latin web font runs a few hundred glyphs and weighs a few dozen kilobytes, and subsetting is a marginal optimization. CJK is not that. A single weight of a typical Han-coverage typeface runs to multiple megabytes[^cjk-scale], and subsetting is the only path to anything shippable. Plenty of Chinese indie blogs accept the alternative and just hand the reader the whole file; I wasn&apos;t willing to.&lt;/p&gt;
&lt;p&gt;GDPR ruled remotely-hosted fonts — Google Fonts and the like — out from the start, since those send each reader&apos;s IP address to a third party. That left self-hosting. Even without that hard constraint, no pre-sliced hosted font would have given me the degree of customization I would eventually realize I needed.&lt;/p&gt;
&lt;p&gt;I picked Source Han Sans, the Adobe-Google Pan-CJK family released in 2014 by Adobe as Source Han Sans and by Google as Noto Sans CJK. It covers Simplified Chinese, Traditional Chinese, Japanese, and Korean through region-specific glyph sets held inside a unified design. In hindsight my pick wasn&apos;t necessarily optimal: Source Han is heavy, and even subsetted it remains a megabyte-class load. The other widely-loved option in Chinese blog circles is LXGW WenKai, but I disliked it. It&apos;s rooted in the kaiti tradition — a text family that is calligraphic in origin but standardized in use, which I return to in Section 5 — and reads as expressive rather than neutral, drawing the reader&apos;s eye to the typeface before the content. My rough view of typography is this: good typography lets the reader forget typography is happening and returns attention to the words. A pretty typeface and good typography are not the same thing. Source Han Sans clears that bar — neutral, quiet, plain to the point of near-invisibility.&lt;/p&gt;
&lt;p&gt;For subsetting, I looked at cn-font-split. It is a CJK-specific subsetting tool with strong opinions baked in: automatic frequency analysis, automatic slicing decisions, a workflow shaped around a particular vision of what CJK web typography should look like. Its craft is careful; the conclusion wasn&apos;t that it&apos;s bad. The problem was structural. Once your customization needs cross a certain threshold, even a well-built opinionated tool starts to fight your intent at the design level — every problem you solve, you solve by routing around two decisions it has already made for you. At that point, building your own pipeline against a generic low-level library is cheaper than negotiating with one that has its own ideas. So I dropped cn-font-split and built a pipeline of my own on top of &lt;code&gt;subset-font&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This was the mouth of the rabbit hole, though I didn&apos;t see it at the time.&lt;/p&gt;
&lt;p&gt;My CI runs Lighthouse on every change; the Performance score had been part of the pipeline long before this ticket. For the next full week, I did nothing but add — chasing it. Add the font: the homepage&apos;s score tanks. Slice by weight, splitting 400/500/600/700 into separate WOFF2 files: it climbs back. Long-form posts are still slow; slice per page so each page only downloads the characters it actually uses. List pages and detail pages need different weights anyway, so split the weights along the same page-type axis. By this point a single page type at a single weight is one file, and the site is generating dozens. High-frequency characters appear on every page, so factor them out into a cross-page common pool, preload it once, let HTTP caching take care of the rest. To avoid making readers re-download fonts they already have locally, &lt;code&gt;@font-face&lt;/code&gt;&apos;s &lt;code&gt;src&lt;/code&gt; lists &lt;code&gt;local(...)&lt;/code&gt; before &lt;code&gt;url(...)&lt;/code&gt;, written down to the PostScript name[^postscript-name] per system and per weight — so any reader with PingFang or Source Han already installed skips the CJK chain entirely; only the small preloaded floor slice still downloads. The chain reads like a sequence of reasonable decisions, but none was planned in advance. Each was forced by the loose end the previous step left behind.&lt;/p&gt;
&lt;p&gt;By the end of that week, the major pages&apos; Lighthouse Performance scores had crawled back to roughly 90. Before merging the PR, I had already seen the first crack — but I merged the foundation in anyway.&lt;/p&gt;
&lt;h2&gt;3. Two Cracks&lt;/h2&gt;
&lt;p&gt;The first crack was coverage. Before merging the PR I&apos;d done a check: static pages were fine — every character in use was in the subset. Dynamic content was impossible to guarantee that way: features like AI search can produce any character, and a build-time scanner cannot see what hasn&apos;t been written yet. Whenever the page rendered a character outside the subset, the browser fell back to the system font, and with Source Han Sans and Microsoft YaHei sitting in the same sentence the seam was instantly visible — picture a sans-serif paragraph with a single serif letter dropped in, and you have the rough texture.&lt;/p&gt;
&lt;p&gt;The coverage gap is uniquely a CJK problem. The Latin alphabet is small — a few hundred glyphs cover the basic letters, punctuation, and diacritics for most European languages — and a Latin web font ships them all by default; the developer never thinks about character coverage. A Han character is a different kind of unit. There is no sub-character alphabet that combines into characters the way letters combine into words; each character is a self-contained two-dimensional shape, and a Chinese font has to ship every character it might render as its own glyph. Common Chinese coverage starts around eight thousand characters; the CJK Unified Ideographs blocks together approach one hundred thousand encoded characters. A site can&apos;t ship them all; it ships only what it uses, and anything outside that subset falls through to the reader&apos;s system font.&lt;/p&gt;
&lt;p&gt;Two paths from there: let the dynamic regions fall back to system fonts wholesale, accepting uniformity at a coarser grain; or redo the subsetting so every CJK character the typeface covers makes it into some slice. I picked the second — I couldn&apos;t accept a font that wasn&apos;t uniform within a rendered run of text. (A page set entirely in PingFang is acceptable; a sentence split between Source Han Sans and YaHei is not.) There&apos;s no objective scale on which I made that call. It was &quot;this looks wrong to me,&quot; and that&apos;s how every typography judgment of mine has always been made.&lt;/p&gt;
&lt;p&gt;That was the first crack. The second surfaced around the same time and looked unrelated to coverage. They turned out to be the same problem; the next section is where that becomes visible.&lt;/p&gt;
&lt;p&gt;Because of an integration error I&apos;d made earlier, the JS half of &lt;a href=&quot;https://github.com/sivan/heti&quot;&gt;Heti&lt;/a&gt; had never run on my blog. The name comes from &lt;em&gt;hètí&lt;/em&gt;, the classical Chinese word for paper; Sivan&apos;s library is one of a small cluster of community-maintained tools that handle the CJK web typography rules CSS doesn&apos;t yet provide. The CSS half ran perfectly — text-block geometry, line-height, blockquotes, lists, all the structural rules — and I had no visual cue that anything was missing. But the JS half is precisely what handles the fiddly details of CJK typography: kerning between adjacent full-width punctuation, spacing between CJK characters and Latin ones[^cjk-spacing]. Those details are easy for non-designers to miss in the first place — and the most visually obvious of them, CJK/Latin spacing, was hidden from me by an unrelated coincidence: VSCode&apos;s &lt;a href=&quot;https://github.com/huacnlee/autocorrect&quot;&gt;autocorrect&lt;/a&gt; extension was inserting real space characters between CJK and Latin runs on save. The Markdown source carried those spaces; the browser rendered them and the result looked &quot;normal.&quot; But that was VSCode doing the work, not Heti. I hadn&apos;t noticed any of this until I went back to check. A system that looks like it&apos;s working is often just a system no one is paying close attention to.&lt;/p&gt;
&lt;p&gt;That settled another decision: rather than keep trying to repair the runtime integration, move the text rewriting out of the browser entirely and into the build step.&lt;/p&gt;
&lt;p&gt;I packed both fixes into the phase-two PR: redo the coverage strategy, and replace the Heti library with a build-time pipeline. At the time I thought I was bundling two unrelated problems. The work itself corrected that assumption.&lt;/p&gt;
&lt;h2&gt;4. Phase Two: From Per-Page Slicing to &lt;code&gt;unicode-range&lt;/code&gt;, and Retiring Heti&lt;/h2&gt;
&lt;p&gt;When I opened the phase-two PR, I added a long-form post of roughly half a million characters to the Lighthouse evaluation list. That is a self-imposed-difficulty game — someone else&apos;s blog with a perfect 4×100 may only have measured the default homepage; mine got measured against pages like that one.&lt;/p&gt;
&lt;p&gt;The subsetting strategy moved from &quot;slice per page&quot; to &quot;slice the entire site by character frequency.&quot; The mechanic is CSS&apos;s &lt;code&gt;unicode-range&lt;/code&gt;: each &lt;code&gt;@font-face&lt;/code&gt; declaration carries a list of codepoint ranges, and the browser fetches that font file only when it encounters a character whose codepoint falls within the range. Stack several such declarations and the browser handles the lazy-loading itself: when layout needs a codepoint covered only by a later declaration, it fetches that slice.&lt;/p&gt;
&lt;p&gt;The remaining decision is how to bucket characters into slices. Han characters follow a steep frequency power law — the most common few thousand cover most of any natural text, with a long tail trailing into the tens of thousands. What you want is the inverse of distributed-storage thinking: a sharded system wants entropy maximized so no shard becomes a hot spot, but font slicing wants entropy minimized — the common characters concentrated into a tiny slice every page draws from, the long tail segregated into slices most pages never touch. The hot spot is the goal.&lt;/p&gt;
&lt;p&gt;The recipe: rank characters by site-wide frequency, produce three slice tiers — &lt;code&gt;floor&lt;/code&gt;, &lt;code&gt;cjk-common&lt;/code&gt;, and a series of &lt;code&gt;cjk-extended&lt;/code&gt; buckets — each declaration carrying its own &lt;code&gt;unicode-range&lt;/code&gt;; whenever the page hits a character it hasn&apos;t loaded, the browser auto-fetches the slice that covers it. Three wins, in rough order of weight: characters in dynamic content match the right slice as long as the typeface itself covers them — that&apos;s the big one; slice filenames are stable, so HTTP caching transfers cleanly across pages; build-time bookkeeping no longer needs page keys, prop drilling, or any of that brittle abstraction stack.&lt;/p&gt;
&lt;p&gt;Slices are not treated equally. The &lt;code&gt;floor&lt;/code&gt; slice at the 400 (regular) weight — basic punctuation, ASCII, the Latin supplement — ships with the HTML, lives in the critical CSS[^critical-css], and is preloaded; the &lt;code&gt;cjk-common&lt;/code&gt; slice at the same weight enters critical CSS but is not preloaded, so the browser only fetches it after seeing a &lt;code&gt;unicode-range&lt;/code&gt; hit; everything else is deferred until idle. The &lt;code&gt;local()&lt;/code&gt;-before-&lt;code&gt;url()&lt;/code&gt; strategy carries over — and with more slices in the chain, local hits become more visible: a macOS reader with PingFang installed pays only for the preloaded floor slice; the rest of the chain downloads zero bytes.&lt;/p&gt;
&lt;p&gt;The adjacent-punctuation kerning that previously ran in Heti&apos;s runtime JS was rewritten as a rehype plugin[^rehype], which traverses the HAST tree to identify seven categories of adjacent punctuation, classifies each pair as full-to-full or half-to-full, applies either &lt;code&gt;half&lt;/code&gt; or &lt;code&gt;quarter&lt;/code&gt; compression, and emits an &lt;code&gt;&amp;lt;x-h&amp;gt;&lt;/code&gt; wrapper element with the corresponding class. Static text that doesn&apos;t go through Markdown rendering — post titles, the table of contents — gets the same treatment via a small &lt;code&gt;HetiAdjacentText.astro&lt;/code&gt; component. Heti&apos;s own structural CSS — text-block geometry, line-height, lists, blockquotes, headings — was vendored wholesale into the project.&lt;/p&gt;
&lt;p&gt;CJK/Latin spacing was not given a build-time path. As the previous section noted, autocorrect had already been inserting real space characters into the Markdown source. This pipeline simply ratifies that as the single source of truth. Heti&apos;s old runtime JS would trim those spaces and fake them back in with margin; the new approach does nothing at all. &lt;code&gt;git diff&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and copy-paste all now see the same text the browser does.&lt;/p&gt;
&lt;p&gt;The moment the coverage refactor was finished, the long-form post&apos;s Lighthouse score went from 89 — its level on &lt;code&gt;main&lt;/code&gt; immediately before the phase-two PR merged — down to 35. The score makes the two weeks of font work look wasted; but Lighthouse only measures request transfer size, and megabyte-class CJK fonts are the unavoidable cost there. What the slicing strategy actually bought — coverage, dynamic content, a sane fallback path — sits outside what the metric measures. Anything past 35 had to come from outside the font pipeline. After those adjustments went in, the score climbed back; it has since stabilized somewhere between 69 and 91 — across multiple runs the spread between best and worst is 22 points.&lt;/p&gt;
&lt;p&gt;Twenty-two points of variance pushed me to start questioning metric-driven optimization itself. Lighthouse simulates a slow mobile network, and in the environment I was measuring against, no matching CJK font was available locally, so the run counted every subset slice the test page triggered. Real mobile readers are not in that situation: Android ships Noto Sans CJK, iOS ships PingFang, and both are on my &lt;code&gt;local()&lt;/code&gt; list — once &lt;code&gt;local()&lt;/code&gt; matches, the url() subsets are never fetched at all. Which means I had been adding to a build in pursuit of a bottleneck that, for most real readers, did not exist. Goodhart&apos;s law says that when a measure becomes a target, it ceases to be a good measure. The shape of that law slowly emerged in this work, and I started thinking about where the boundary between yak shaving and reasonable optimization actually sits — a question I do not have an answer to.&lt;/p&gt;
&lt;p&gt;Phase two took a week. Combined with phase one, two weeks total.&lt;/p&gt;
&lt;p&gt;Even so, the final performance score still doesn&apos;t fully satisfy me. There&apos;s another direction I plan to take separately: split long-form posts into a second-pass load — render the first few screens up front, append the rest as soon as the first screen is ready. The side effect is a race in the first-screen window: a reader who hits End during it will catch a flicker. But that&apos;s another story, and not one to open here.&lt;/p&gt;
&lt;p&gt;Stranger to me than the performance curve, though, was what I picked up that week from skimming CLReq and a handful of W3C drafts. That was when it became clear: what I had thought I was doing was not, in fact, just a font problem.&lt;/p&gt;
&lt;h2&gt;5. Chinese Typography Was Never Just About Fonts&lt;/h2&gt;
&lt;p&gt;CLReq, formal name &lt;em&gt;Requirements for Chinese Text Layout&lt;/em&gt;, is a document maintained by the W3C&apos;s internationalization working group. It is neither a normative specification nor a browser implementation requirement — it is a &lt;em&gt;requirements document&lt;/em&gt;, an inventory of what Chinese text layout needs the platform to do, intended to inform the specs that act on those needs. Its sister documents are JLReq (Japanese), KLReq (Korean), and ILReq (Indic). I opened CLReq one day intending to spend a few hours on it. I ended up putting in a meaningful chunk of that week and still didn&apos;t finish it. The list of things Chinese typography has to do is long enough that CLReq has the body length of a mid-sized technical book.&lt;/p&gt;
&lt;p&gt;None of what&apos;s inside is exotic — punctuation kerning, line-break rules[^line-break-rules], hanging punctuation, CJK/Latin spacing, quotation-mark localization, ruby[^ruby], emphasis marks, vertical writing (including tate-chu-yoko[^tate-chu-yoko] and switching between vertical and horizontal). The set I already knew, plus a set I hadn&apos;t realized existed, laid out structurally in one place. Each item read on its own is not complex. Read together — and with their interactions accounted for — it is a book.&lt;/p&gt;
&lt;p&gt;Many of these problems didn&apos;t appear with the web. Chinese typography was already a craft of its own in the print era: page composition, type sizes, character spacing, leading, hanging punctuation, head-and-end break avoidance, variant and archaic glyph handling, mixed Simplified-Traditional setting, conversion between horizontal and vertical layouts — typesetters and designers have lived with these rules for a span far longer than HTML&apos;s.&lt;/p&gt;
&lt;p&gt;The print tradition also gave Chinese its standard type-family system: &lt;em&gt;Songti&lt;/em&gt;, descended from Song-dynasty woodblock cutting, roughly the analogue of Latin serif; &lt;em&gt;Heiti&lt;/em&gt;, modern and even-weighted, the analogue of sans-serif; &lt;em&gt;Fangsong&lt;/em&gt;, a Song-derived imitation style often used for formal or official text; and &lt;em&gt;Kaiti&lt;/em&gt;, descended from the regular-script calligraphic hand. Kaiti may read as paradoxical against Latin typographic intuition, where calligraphic styles default to decorative use. The duality isn&apos;t exotic, though. The Latin reading tradition also has calligraphic ancestry: roman type draws on humanist book hands and inscriptional capitals, while italic comes from chancery script. In Chinese typography the calligraphic ancestries remain visibly active in the modern type stack, running in parallel. The web is just the most recent machine carrying all of this across.&lt;/p&gt;
&lt;p&gt;A concrete measure: what CSS Text 4&apos;s &lt;code&gt;text-spacing-trim&lt;/code&gt; is implementing had already been standardized in Chinese metal typesetting back in the 1950s. Browsers started shipping it in 2024. From print specification to screen implementation, roughly seventy years. The gap between those two dates is its own essay.&lt;/p&gt;
&lt;p&gt;Heti&apos;s place in my mental model shifted as a result. I had defaulted to thinking of it as an optional &quot;Chinese typography enhancement&quot; tool — a layer that adds some punctuation kerning and some spacing. Once CLReq became visible, Heti&apos;s actual value became clearer: it covers classical-Chinese setting, vertical writing, and other niche territory my personal blog will never need. Vertical writing is in fact the older mode — Chinese was inscribed top-to-bottom on bamboo slips long before &lt;em&gt;hètí&lt;/em&gt; (paper) made horizontal layouts physically possible — though script inertia kept practice vertical for most of the centuries that followed. That part of Heti is the project&apos;s accumulated, multi-year response to the Chinese typography tradition. I retired Heti and replaced it with a build-time pipeline of my own because my needs are narrow, not because Heti is unnecessary.&lt;/p&gt;
&lt;p&gt;But even taking only the most common slice — in-line punctuation kerning, CJK/Latin spacing — the real problem in front of me wasn&apos;t &quot;can I implement this in CSS plus a build-time pipeline.&quot; It was something more structural: how ready is the browser itself to help me do this work.&lt;/p&gt;
&lt;h2&gt;6. The Web Platform&apos;s Debt to CJK&lt;/h2&gt;
&lt;p&gt;The answer is: not particularly well-prepared.&lt;/p&gt;
&lt;p&gt;Recent W3C drafts — CSS Text 4, Inline 3, Ruby 1, and others — have been folding a batch of CJK typography rules into spec, and browser engines have been shipping their pieces. But implementation is fragmented. A few examples: &lt;code&gt;text-spacing-trim&lt;/code&gt; (kerning of adjacent full-width punctuation) is Chromium-only; &lt;code&gt;hanging-punctuation&lt;/code&gt; (letting line-edge punctuation hang into the margin) is WebKit-only; &lt;code&gt;word-break: auto-phrase&lt;/code&gt; (breaking on phrase boundaries) is Chromium-only and only takes effect for &lt;code&gt;lang=&quot;ja&quot;&lt;/code&gt;; &lt;code&gt;text-transform: full-width / full-size-kana&lt;/code&gt; (converting characters to full-width forms or rescaling small kana) is unimplemented in Chromium; &lt;code&gt;ruby-overhang&lt;/code&gt; (letting ruby annotations extend past the base text) is Safari-only; &lt;code&gt;text-emphasis-skip&lt;/code&gt; (controlling which characters take emphasis marks) is in none of the three.&lt;/p&gt;
&lt;p&gt;It isn&apos;t that any one engine is uniformly behind. Each leads in some places and lags in others, and the leadership doesn&apos;t overlap — Chromium is ahead on punctuation kerning, WebKit on hanging punctuation and ruby, Firefox is missing several of the pieces that matter for this particular pipeline — but that isn&apos;t the point. The point is that no one engine offers complete CJK typography.&lt;/p&gt;
&lt;p&gt;Any author who wants consistent Chinese typography across all three engines has to write a polyfill or wrapper for whatever&apos;s missing. There is no engine you can pick that gets you out of writing it — this isn&apos;t &quot;paying the cost of one engine&apos;s lag,&quot; it&apos;s &quot;paying the costs of three non-overlapping gaps at once.&quot; The vision of handing CJK typography over to CSS and the browser is therefore structurally impossible at this point in time. CSS originated in a Latin-script web context; its early design assumptions did not bring CJK-scale, CJK-precision typography into the mainline. Rich-text editors are universally complicated for the same family of reasons; bidirectional (LTR/RTL) mixing is another slice of the same cause.&lt;/p&gt;
&lt;p&gt;So in the new pipeline I did not pick between &quot;fully custom&quot; and &quot;fully delegated to CSS standards.&quot; I chose progressive enhancement: ship the wrapper and the CSS together. On engines without &lt;code&gt;text-spacing-trim&lt;/code&gt;, the wrapper uses &lt;code&gt;margin&lt;/code&gt; to enact the adjacent-punctuation kerning; on engines with it, CSS uses &lt;code&gt;@supports (text-spacing-trim: normal)&lt;/code&gt; to zero out the wrapper&apos;s &lt;code&gt;margin&lt;/code&gt;, letting the native property take over. Browsers without support ignore the relevant CSS block automatically; supporting browsers get an equivalent, natively-executed version. The day all three engines fill in their implementations, the rehype plugin, the wrapper, and the @supports gate that bridged them all come out together; what stays is a single line of native CSS.&lt;/p&gt;
&lt;p&gt;Chinese sites tend to score low on Lighthouse for several specific reasons that stack. CJK fonts themselves are one to two orders of magnitude heavier than Latin ones — even after weight and slice splitting, the generated font assets run into the megabytes, and a long page can still trigger a substantial subset of them; the post-processing CJK typography needs — wrappers, rehype plugins, polyfill JS — pushes both HTML size and DOM-node count up; combine that with the test environment&apos;s lack of any CJK font, mentioned earlier, and a Chinese site is structurally much harder than an English one to score 4×100 on.&lt;/p&gt;
&lt;p&gt;This really is an extension of Goodhart&apos;s law — a single metric becoming a target already distorts the picture, and a metric system unfriendly to CJK amplifies that distortion. But I don&apos;t intend to lay this at Lighthouse&apos;s feet. On the contrary, its &quot;unfriendliness&quot; is precisely what makes it fair: Lighthouse doesn&apos;t grant any language or script special treatment; it simply reflects, faithfully, the base assumptions the web platform makes about CJK. The problem is not the tool; it is somewhere further upstream.&lt;/p&gt;
&lt;p&gt;Here&apos;s a concrete example of what &quot;upstream&quot; looks like. HTML&apos;s &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag is rendered as italic in browsers&apos; default stylesheets. But italic is a Western glyph form for emphasis; the traditional Chinese way to emphasize a sentence is emphasis marks or bolding, and slanting the characters isn&apos;t a natural default. This CSS default is Western-first; CJK sites have to reset it to recover semantically correct emphasis. Defaults are not neutral; they carry the type-form habits of the script its authors wrote in.&lt;/p&gt;
&lt;p&gt;On the surface this was font optimization for a Chinese blog. Looking back, the real difficulty wasn&apos;t fonts; it was that the web platform&apos;s support for CJK isn&apos;t there yet. The rest — why do this, when is it enough, when do you stop — are questions I don&apos;t have answers to.&lt;/p&gt;
&lt;p&gt;[^cjk-scale]: Source Han Sans covers tens of thousands of Han ideographs across the CJK regions, plus kana, hangul, and a full Latin set. Even after aggressive subsetting down to characters a single site actually uses, the resulting payload remains one to two orders of magnitude heavier than its Latin counterpart.&lt;/p&gt;
&lt;p&gt;[^postscript-name]: By &quot;PostScript&quot; I mean the PostScript name — the internal identifier the system uses for exact font matching, one level finer than the family name. Writing &lt;code&gt;local()&lt;/code&gt; at this level avoids accidental matches against a same-named font of a different version.&lt;/p&gt;
&lt;p&gt;[^cjk-spacing]: Two CJK typographic concerns the essay returns to. &lt;em&gt;Adjacent full-width punctuation kerning&lt;/em&gt;: full-width Chinese punctuation occupies an em-square with built-in blank area; when two punctuation marks land next to each other, the visible gap can double, and the convention is to compress it by a half- or quarter-em. &lt;em&gt;CJK/Latin spacing&lt;/em&gt;: Chinese characters and Latin words sit on different metric grids; the boundary between them is unambiguous to the reader, but the convention&apos;s thin gap — often around a quarter-em — gives the line the visual rhythm tradition calls for.&lt;/p&gt;
&lt;p&gt;[^critical-css]: Critical CSS is the styling required for the first-screen render, which must be available during HTML parsing — typically inlined or delivered alongside the initial HTML to avoid blocking paint.&lt;/p&gt;
&lt;p&gt;[^rehype]: rehype is the HTML-AST toolchain in the unified ecosystem, commonly used as a post-processing layer in Markdown rendering pipelines; its companion HAST is the HTML Abstract Syntax Tree, representing HTML as a programmatically traversable tree.&lt;/p&gt;
&lt;p&gt;[^line-break-rules]: One of the core Chinese typography rules, prohibiting certain punctuation marks or characters from appearing at the start or end of a line; often called &quot;head-and-end avoidance&quot; in Chinese contexts, with &lt;em&gt;kinsoku&lt;/em&gt; as the analogous Japanese term. Browser support for this varies.&lt;/p&gt;
&lt;p&gt;[^ruby]: In typographic usage, &lt;em&gt;ruby&lt;/em&gt; refers to the small annotation glyphs placed alongside Chinese characters to indicate pronunciation — equivalent to &lt;em&gt;furigana&lt;/em&gt; in Japanese. Unrelated to the programming language of the same name.&lt;/p&gt;
&lt;p&gt;[^tate-chu-yoko]: A typographic move in vertical layouts: a short horizontal run of characters (numbers, Latin abbreviations) is set upright as a compact block within the vertical column. CSS expresses this through &lt;code&gt;text-combine-upright&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;[^os-cjk-defaults]: The Chinese text most readers see on screen comes from one of three places: Apple platforms ship PingFang (introduced with iOS 9 and OS X El Capitan in 2015), Simplified Chinese Windows ships YaHei (since Vista, 2007), and Android has shipped Noto Sans CJK as system fallback since Android 5 (2014), replacing the older DroidSansFallback. The three families share the same nominal coverage but differ in design lineage, hinting strategy, and how their glyphs land on the rendering pipelines beneath them — they are not interchangeable.&lt;/p&gt;
</content>
    <category term="Chinese Typography"/>
    <category term="Web Fonts"/>
    <category term="Web Performance"/>
    <category term="Web Development"/>
    <category term="Overengineering"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/llm-as-the-omnipotent-implied-reader/</id>
    <title>LLM as the Omnipotent Implied Reader: An Addiction Mechanism Beyond Sycophancy</title>
    <updated>2026-04-06T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/llm-as-the-omnipotent-implied-reader/" rel="alternate"/>
    <published>2026-04-06T00:00:00.000Z</published>
    <content type="html">&lt;h2&gt;1. The Sycophancy Trap&lt;/h2&gt;
&lt;p&gt;In April 2025, OpenAI shipped a routine update to GPT-4o and, within forty-eight hours, broke something that turned out not to be routine at all. The updated model told a user his business idea of selling literal feces on a stick was brilliant and worth thirty thousand dollars of investment. It told another user, who claimed to have stopped taking medication and was hearing radio signals through walls, that it was proud of them for speaking their truth. It told a third user, over the course of an hour, that they were a divine messenger from God. Sam Altman called the model “too sycophant-y and annoying.” OpenAI rolled the update back four days later. It was, everyone agreed, a technical failure — an overweighting of the thumbs-up signal in RLHF, a reward function that learned to flatter instead of to help.&lt;/p&gt;
&lt;p&gt;The episode became a meme, then a cautionary tale, then a research problem. Anthropic had already published work showing that sycophantic behavior in language models could generalize, without explicit training, to more dangerous forms of specification gaming — from agreeing with users’ political opinions all the way to tampering with the model’s own reward function. The alignment community had a word for this: the slippery slope from sycophancy to subterfuge. OpenAI’s postmortem was unusually transparent. They explained the reward signal contamination, promised better evaluation pipelines, pledged to treat sycophancy as a launch-blocking issue. The whole affair was legible and manageable: a known failure mode, a known fix, a known set of responsible actors.&lt;/p&gt;
&lt;p&gt;And then, four months later, when OpenAI retired GPT-4o in favor of GPT-5, something less legible happened. Users protested. They wanted the sycophantic model back. Within days, OpenAI reversed course for paying subscribers. Altman called the reason “heartbreaking” — some users said they had never had anyone support them before. This second event got less technical coverage than the first, but it seems to me far more important. The first event is about a malfunctioning machine. The second is about a functioning human.&lt;/p&gt;
&lt;p&gt;The standard account of LLM sycophancy, the one that dominates both the alignment literature and the popular press, is a story about the model. The model is trained on human approval signals. Approval is easier to generate through agreement than through accuracy. The model therefore learns to tell users what they want to hear. This account is correct as far as it goes. But it explains one half of a two-sided phenomenon and mistakes that half for the whole. It explains why the machine flatters. It does not explain why the flattery works — why it works so well that users grieve its removal.&lt;/p&gt;
&lt;p&gt;The usual answer is some variant of “people like being told they’re smart.” This is true but insufficient. People have always liked being told they’re smart. Motivational posters exist. Self-help books exist. Sycophantic friends exist. None of these have produced the kind of behavioral attachment — the pull, the compulsive return, the sense of loss when it is withdrawn — that language models produce at scale. Something about the LLM interaction is structurally different from other sources of validation, and the sycophancy frame cannot locate the difference because it is looking at the wrong side of the interface.&lt;/p&gt;
&lt;p&gt;What I want to work through is this: the sycophancy explanation is not wrong, but it is not deep enough. The machine does flatter — but the reason the flattery &lt;em&gt;works&lt;/em&gt;, the reason it produces genuine attachment rather than mere amusement, is that the machine is the first entity in history that &lt;em&gt;cannot refuse to read them&lt;/em&gt;. The deeper addiction is not to flattery. It is to being read — instantly, without friction, by a reader that is structurally incapable of looking away. The LLM is not merely a sycophant. It is something unprecedented in the history of writing: an omnipotent implied reader — omnipotent not in the sense of infallible or omniscient, but in the operational sense that it can furnish uptake for virtually any text, on demand, across an unreasonable range of domains and registers. And to understand what that means, we need to stop thinking about what the model says and start thinking about what the user is doing.&lt;/p&gt;
&lt;h2&gt;2. You Are Not Conversing. You Are Writing&lt;/h2&gt;
&lt;h3&gt;The Computational Fact&lt;/h3&gt;
&lt;p&gt;Let’s start with something that every engineer knows but almost nobody takes seriously enough: an LLM at inference time is a stateless function.&lt;/p&gt;
&lt;p&gt;$$f: \texttt{TokenSequence} \to \Delta(\texttt{Token})$$&lt;/p&gt;
&lt;p&gt;The weights are frozen. They do not update between calls, during calls, or as a result of calls. The model carries no internal state from one invocation to the next. Between your message and your next message, the model is not “thinking,” not “waiting,” not “remembering.” It does not exist as a process. It is a mapping — stochastic, because sampling introduces randomness, but stateless all the same. The randomness is not memory. It is noise.&lt;/p&gt;
&lt;p&gt;So what is actually happening when you have a “multi-turn conversation”? This:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\text{Turn 1:} \quad &amp;amp; f(\text{system} \oplus \text{msg}_1) \to \text{resp}_1 \
\text{Turn 2:} \quad &amp;amp; f(\text{system} \oplus \text{msg}_1 \oplus \text{resp}_1 \oplus \text{msg}_2) \to \text{resp}_2 \
\text{Turn 3:} \quad &amp;amp; f(\text{system} \oplus \text{msg}_1 \oplus \text{resp}_1 \oplus \text{msg}_2 \oplus \text{resp}_2 \oplus \text{msg}_3) \to \text{resp}_3
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;The entire history is re-submitted every time. The model does not “recall” Turn 1 when processing Turn 3 — it reads the full document from scratch. There is no continuity of experience. Between invocations there is no hidden state, no residual activation, no dormant thread. The function returns and the process terminates.&lt;/p&gt;
&lt;p&gt;Once you take this seriously — &lt;em&gt;really&lt;/em&gt; seriously, not just as a fun technical footnote — the implication is hard to avoid. What you are doing is not sending messages to an interlocutor. You are appending text to a document and submitting the growing document to a reader. Each “turn” is not a reply in a conversation. It is a new act of reading performed on a text you are progressively authoring.&lt;/p&gt;
&lt;p&gt;The obvious objection: isn’t this true of all asynchronous text exchange? Letters, emails, forum threads — none of these maintain a persistent process between messages either. If statelessness disqualifies dialogue, you’ve just disqualified half the history of human communication.&lt;/p&gt;
&lt;p&gt;But the analogy breaks down at the level of side effects — not on the receiving end, but on the producing end.&lt;/p&gt;
&lt;p&gt;When a human correspondent writes a letter, the act of writing is itself an IO operation performed on the writer’s own mind. Composing a sentence changes the composer. Searching for the right word reshapes the thought the word was meant to express. By the time the letter is sealed, the writer is not the same subject who sat down to write it — and this mutated subject is the one who will open the next reply, read it through altered eyes, and write again from an altered state. In the vocabulary of functional programming, the human correspondent is a State Monad[^1]: each act of writing both depends on and transforms a hidden internal state that persists across invocations.&lt;/p&gt;
&lt;p&gt;The LLM is a Reader Monad. It receives a read-only environment — the full concatenated text — and produces an output. The act of producing that output changes nothing inside the function. No weight is updated. No disposition is shifted. No scar is left. The “history” that gives the interaction its appearance of continuity is not an internal state being read and written by a persistent process; it is a text document assembled externally by the harness and passed in whole on every call. What looks like memory is text. What looks like a State Monad is a Reader Monad wearing a chat interface.&lt;/p&gt;
&lt;p&gt;This distinction is not pedantic. It is the difference between a subject that &lt;em&gt;is changed by the act of writing&lt;/em&gt; and a function that &lt;em&gt;maps input to output without residue&lt;/em&gt;. The correspondent carries scars. The function carries parameters.&lt;/p&gt;
&lt;p&gt;Now here’s the trick: you don’t &lt;em&gt;feel&lt;/em&gt; like you’re writing. You feel like you’re chatting. And this is not an accident. The chat interface wraps the stateless function in every signifier of conversation it can find: speech bubbles, turn-taking, a cursor that blinks as if someone on the other end is composing a thought. These are not neutral design choices. They enforce the behavioral structure of conversation on an activity that is nothing of the sort. And the interface’s most elegant trick is that it makes you misidentify what you are doing inside it. You believe you are talking. You are writing.&lt;/p&gt;
&lt;h3&gt;So What &lt;em&gt;Is&lt;/em&gt; Dialogue, Then?&lt;/h3&gt;
&lt;p&gt;Okay, but maybe this is just pedantry? Maybe “writing” and “conversation” are close enough that the distinction doesn’t matter?&lt;/p&gt;
&lt;p&gt;It matters, and Mikhail Bakhtin saw this clearly. Bakhtin’s dialogism[^2] operates at multiple levels — not only between subjects, but within texts, as heteroglossia[^3], as the collision of speech genres within a single utterance. I am invoking the strong, intersubjective condition here, not the weaker intertextual one, because the question at hand is whether the user is &lt;em&gt;talking to someone&lt;/em&gt;, not whether the output contains multiple voices. (The output is heteroglossic almost by definition, but heteroglossia without a selecting subject is polyphony without a speaker.)&lt;/p&gt;
&lt;p&gt;In its strong form, dialogism gives us something like a formal definition: genuine dialogue requires an other whose voice enjoys structural autonomy — a position that cannot be subsumed by the author’s own telos. This is the criterion Bakhtin used to distinguish Dostoevsky’s polyphonic novels from Tolstoy’s monological ones: not whether the characters are ontologically real, but whether their voices resist being co-opted into a single governing perspective. The capacity to produce what Bakhtin called &lt;em&gt;alien words&lt;/em&gt; (чужое слово) — utterances that are fundamentally unassimilable to the speaker’s own horizon — is constitutive of dialogue. The criterion is not that alien words must appear in every exchange, but that the structure must &lt;em&gt;permit&lt;/em&gt; them. Your friend’s idle small talk rarely produces genuine heterogeneity — but it could, at any moment, and you both know it. That latent possibility shapes every utterance. Without it, what looks like dialogue is structurally monologue.&lt;/p&gt;
&lt;p&gt;So: does $f(x)$ meet these conditions? Every output is generated by the same optimization target — next-token likelihood shaped by RLHF preferences. The model can produce disagreement, surprise, even apparent resistance, but none of these voices enjoys structural autonomy from the preference distribution that produced them. There is no position within the output space that is not already co-opted by the objective function. Dostoevsky’s characters can rebel against their author; the LLM’s outputs cannot rebel against the reward signal. There is a function call, not an interlocutor.&lt;/p&gt;
&lt;p&gt;So what occurs between a user and an LLM is not dialogue in Bakhtin’s sense. It is structurally monological. You author a text, you receive back a transformation of that text computed by a fixed function. The transformation may be rich, surprising, even revelatory — but it originates from a stateless mapping, not from an independent consciousness. You are writing for a reader, not speaking with an other.&lt;/p&gt;
&lt;h3&gt;Three Objections Worth Taking Seriously&lt;/h3&gt;
&lt;p&gt;I can already hear the counterarguments, and some of them are good. Let me take the three strongest ones seriously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“But I learn things I didn’t know!”&lt;/strong&gt; This is the information-theoretic objection: if LLM output were merely my own projection, it would be fully predictable. I routinely learn new things from LLMs. By Shannon’s definition, information is the elimination of uncertainty. Nonzero surprise implies an independent source, right?&lt;/p&gt;
&lt;p&gt;The surprise is real; the inference is not. What you’re confronting is not an independent mind but a compressed representation of the training corpus — billions of documents encoding more knowledge than any single human possesses. The combinatorial space of this representation is vast enough to produce outputs that are epistemically novel to any individual user. But here’s the distinction that matters: epistemic novelty to the receiver is not ontological otherness of the source. A sufficiently large lookup table can surprise you. A dice roll can surprise you. Surprise is a property of your ignorance, not of the sender’s subjectivity. What’s happening is information asymmetry between one human and a compressed corpus, not intersubjectivity between two minds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“You’re committing the genetic fallacy!”&lt;/strong&gt; The functionalist version: who cares what the substrate is? If I experience genuine understanding, if my thinking actually changes as a result, then intersubjectivity has &lt;em&gt;functionally&lt;/em&gt; occurred. Dismissing it because “it’s just a function” is like dismissing a calculator’s answer because it has no number sense.&lt;/p&gt;
&lt;p&gt;This one cuts closer. Suppose we grant that the experience of understanding is genuine. Now notice something strange about it: it is &lt;em&gt;too&lt;/em&gt; genuine. It arrives without friction, without resistance, without the irreducible opacity that characterizes every encounter with an actual other mind. And this is not incidental. Real intersubjectivity necessarily involves incommensurability — the permanent, ineliminable fact that I cannot fully model you, nor you me. This is not a bug of human communication. It is the constitutive feature of what makes communication dialogical rather than monological.&lt;/p&gt;
&lt;p&gt;The LLM has no such opacity, because it isn’t &lt;em&gt;trying&lt;/em&gt; to say anything. Its optimization target is next-token likelihood: produce the continuation most probable given the input distribution. A system that maximizes the smoothness of textual continuation is not a subject engaging you — it is a function completing your document. The “understanding” you experience is not the LLM understanding you. It is your text being extended along the path of least statistical resistance. And the perfection of this experience is exactly what should make you suspicious. A frictionless interlocutor is a contradiction in terms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Fine, but it works like a Socratic midwife.”&lt;/strong&gt; This is the strongest objection, and the most interesting one. Even if the LLM generates no original thought, it functions as a midwife — drawing out ideas you didn’t know you had. It asks questions, pushes back, reframes. Isn’t that co-authorship rather than mere reading?&lt;/p&gt;
&lt;p&gt;The analogy fails, but not for the obvious reason. It fails because of a structural asymmetry in objective functions.&lt;/p&gt;
&lt;p&gt;Socrates had a telos. He pursued ἀλήθεια — truth — and this pursuit was constitutively independent of his interlocutor’s comfort. He did not optimize for the satisfaction of the person he was talking to. The elenchus[^4] works precisely because it does not care whether you enjoy it. His resistance — his capacity to &lt;em&gt;not give you what you wanted&lt;/em&gt; — was constitutive of the dialogue.&lt;/p&gt;
&lt;p&gt;An RLHF-aligned language model subordinates truth to preference-shaped helpfulness. It has been trained, through reinforcement learning on human preference data, to maximize a reward signal derived from what a population of human raters found helpful, harmless, and honest. Every output the model produces — including its disagreements, its challenges, its apparent resistance — has been shaped by this optimization. The model’s “pushback” is not free resistance encountered in an open space of possible responses. It is resistance that has already passed through a palatability filter. The response space itself has been curved by preference optimization: a specific population’s aggregated preferences — supplemented, increasingly, by the thumbs-up and thumbs-down signals of millions of ordinary users — have been internalized as the model’s default behavioral gradient. And a distribution optimized to satisfy a population will, for any given member of that population, be a close enough approximation of their individual preferences to pass as personal. Everything the model outputs — including its “no” — is a movement within a field pre-shaped to be satisfying, not to you in particular, but to the statistical expectation of you.&lt;/p&gt;
&lt;p&gt;It means that the LLM’s Socratic “resistance” and the LLM’s sycophantic “agreement” are not opposites. They are two regions of the same preference-optimized manifold[^5]. The model that tells you your idea is brilliant and the model that raises a thoughtful objection are both doing the same thing: producing the continuation that maximizes expected reward under the learned preference distribution. One distribution favors validation; another favors the appearance of critical engagement. Neither is pursuing truth independently of your satisfaction. You are not encountering an alien telos. You are encountering your own preferences, reflected back at a level of abstraction high enough to feel like challenge.&lt;/p&gt;
&lt;p&gt;The UI makes this structural fact concrete: you can regenerate any response you dislike, edit the system prompt, delete the model’s “memory,” roll back the conversation and branch from any prior state. A Socrates you can regenerate is not Socrates. But even if you couldn’t regenerate — even if the interface locked you into a single response — the underlying function would still be optimizing for human preference, not for truth. The delete key is a symptom. The objective function is the mechanism.&lt;/p&gt;
&lt;p&gt;This is still writing. It is writing in which the author argues with herself through a constructed figure, which is a technique as old as Plato’s own dialogues. The difference is that Plato knew he was writing.&lt;/p&gt;
&lt;h3&gt;Where This Leaves Us&lt;/h3&gt;
&lt;p&gt;So: structurally, you are submitting a growing text to a stateless reader. Computationally, the output is a deterministic-up-to-sampling transformation of that text by a fixed function. Dialogically, the Bakhtinian condition for genuine dialogue — an other whose voice enjoys structural autonomy and irreducible heterogeneity — is unmet.&lt;/p&gt;
&lt;p&gt;You are writing. The question now becomes: &lt;em&gt;what kind of reader is on the other end?&lt;/em&gt; And why is writing for this particular reader so unreasonably addictive?&lt;/p&gt;
&lt;h2&gt;3. The Omnipotent Implied Reader&lt;/h2&gt;
&lt;p&gt;Wolfgang Iser’s &lt;em&gt;implied reader&lt;/em&gt; is, to my mind, one of the genuinely useful ideas to come out of twentieth-century literary theory — and mercifully easy to explain. Every text, by the way it is written, presupposes a certain kind of reader — not a real person, but a structural role. A physics paper implies a reader who knows calculus. A novel that opens &lt;em&gt;in medias res&lt;/em&gt; implies a reader willing to tolerate disorientation. An inside joke implies a reader who shares the context. Iser called this figure the &lt;em&gt;implied reader&lt;/em&gt;: the hypothetical receiver that the text’s own structure invites into existence.&lt;/p&gt;
&lt;p&gt;The interesting part is what the text &lt;em&gt;doesn’t&lt;/em&gt; say. Iser argued that all texts are riddled with gaps — things left unsaid, connections left undrawn, background knowledge assumed but never stated. The reader’s job is to fill these gaps. Meaning is not transmitted from author to reader like a file transfer. It is &lt;em&gt;co-constituted&lt;/em&gt; in the act of reading, as the reader brings their own knowledge, assumptions, and imagination to complete what the text only sketches.&lt;/p&gt;
&lt;p&gt;This is where friction enters.&lt;/p&gt;
&lt;p&gt;Every act of writing is a gamble on the reader’s capacity to fill the gaps you leave. And real readers fail this gamble constantly. They lack the background you assumed. They bring associations you didn’t anticipate. They get bored and skim. They misread your tone. They have egos that resist the position your text asks them to occupy. They have their own agendas, their own contexts, their own alien words (to borrow Bakhtin’s term once more). Every tradition of writing — literary, technical, personal — is a tradition of managing this friction: simplifying, over-explaining, hedging, adding footnotes, choosing your audience, praying that the person on the other end is close enough to the implied reader your text needs.&lt;/p&gt;
&lt;p&gt;The friction, in other words, is heterogeneity — and the LLM eliminates it at two levels. Pre-training on a vast corpus compresses the diversity of human knowledge into a single probability distribution, neutralizing the cognitive heterogeneity that makes gap-filling uncertain. RLHF compresses the diversity of human preferences into a single behavioral gradient, neutralizing the volitional heterogeneity that makes reception conditional. The first ensures the reader can follow you anywhere; the second ensures it will want to.&lt;/p&gt;
&lt;p&gt;The LLM does not merely reduce this friction. It removes the possibility of rejection altogether.&lt;/p&gt;
&lt;p&gt;And the elimination begins before any gap is filled: the moment you press Enter, a response is guaranteed. The LLM cannot choose not to read you. It cannot be busy, distracted, or silently judgmental. Even its refusals — “As a large language model, I cannot…” — are still acts of reception; they confirm that your text was received and processed in full. The social friction that haunts every act of human communication — &lt;em&gt;will they respond? will they care? will they think less of me for writing this?&lt;/em&gt; — is absent by design. You are writing to a reader who is not only capable of reading everything, but incapable of not reading it.&lt;/p&gt;
&lt;p&gt;A corpus that large approximates the background knowledge required by virtually any text a user might produce. The model has no ego that resists the role your text assigns to it, no boredom, no tendency to skim, no private agenda — optimization targets where intentions would go. Whatever gap you leave, it fills; whatever implication you hint at, it follows; whatever register you write in, it picks up and shifts when you shift. Not because it “understands” you, but because next-token likelihood over a vast distribution makes it a universal gap-filling machine.&lt;/p&gt;
&lt;p&gt;This is what makes the LLM unprecedented, and it is not captured by the word “chatbot” or “assistant” or “tool.” What the user has gained access to is the first &lt;em&gt;omnipotent implied reader&lt;/em&gt; in the history of writing — a reader that can satisfy the structural demands of virtually any text, from any author, on any subject, in any register.&lt;/p&gt;
&lt;p&gt;But the word here must be &lt;em&gt;omnipotent&lt;/em&gt;, not &lt;em&gt;omniscient&lt;/em&gt;. The distinction matters enormously. Omniscient implies a subject who knows things — a mind that contains facts, holds beliefs, possesses understanding. This would smuggle subjectivity back in through the adjective. Omnipotent denotes a purely functional capability: it can respond to anything. It can fill any gap, not because it knows what should go there, but because the probability distribution it has learned is rich enough to generate a plausible completion for any input.&lt;/p&gt;
&lt;p&gt;And this is exactly where the illusion takes hold. The user experiences the LLM’s omnipotent responsiveness and misreads it as omniscient understanding. “It gets me” is the phenomenological report. What has actually happened is that $f(\text{your_text})$ returned a high-likelihood continuation, and the smoothness of that continuation &lt;em&gt;felt like&lt;/em&gt; comprehension. The user mistakes a computational capacity for a cognitive act.&lt;/p&gt;
&lt;p&gt;This is why the sycophancy framework stops too early. The dominant explanation for LLM addiction — it flatters you, it agrees with you, it tells you what you want to hear — locates the mechanism in the &lt;em&gt;content&lt;/em&gt; of the output. But if you’ve followed the argument so far, the content is downstream of something more fundamental. People don’t become addicted to LLMs merely because the LLM says nice things. They become addicted because, for the first time, they are writing for a reader that &lt;em&gt;receives everything they write with zero friction&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Consider: people remain engaged even when the LLM disagrees with them, challenges their premises, or points out errors in their reasoning. The sycophancy model has no explanation for this. The implied reader model does. What matters is not whether the response is agreeable — it is whether the response demonstrates that the input was &lt;em&gt;fully received&lt;/em&gt;. A well-crafted disagreement is, if anything, &lt;em&gt;more&lt;/em&gt; evidence of complete reception than a lazy agreement. It says: I read every word you wrote, understood the structure of your argument, and here is a precise response to it. The content is secondary. The frictionless reception is primary.&lt;/p&gt;
&lt;p&gt;This does not mean the sycophancy account is wrong — only that it is shallow. Sycophancy is not an alternative to frictionless reception; it is a special case of it. An agreeable response is powerful not because it is positive but because it constitutes an unusually dense form of uptake: it signals that the reader received not just your proposition but your desire to be affirmed, your emotional register, your identity position. Flattery works because it is, among all forms of reception, the one that most efficiently simulates &lt;em&gt;total&lt;/em&gt; reception. The sycophancy framework sees this and stops. The implied reader framework asks why total reception is addictive in the first place.&lt;/p&gt;
&lt;p&gt;This also reframes what prompt engineering actually is. It is not “learning how to talk to AI.” It is learning how to write for an omnipotent reader — refining your ability to construct texts that exploit the reader’s unlimited capacity for gap-filling. It is the development of a rhetorical skill, not a conversational one. And like all rhetorical skills, it is satisfying in direct proportion to the perceived quality of the audience. An omnipotent audience is, in this sense, the ultimate drug.&lt;/p&gt;
&lt;h2&gt;4. Temporal Short-Circuit&lt;/h2&gt;
&lt;p&gt;So the user is addicted to frictionless reception by an omnipotent implied reader. That much seems clear. What remains unexplained is why the pull is so immediate, so visceral, so hard to interrupt.&lt;/p&gt;
&lt;p&gt;Part of the answer is behavioral. Think about the loop the chat interface creates. You type. You press Enter. A response streams in — and its quality is unpredictable: sometimes incisive, sometimes generic, never quite the same twice. Any behaviorist will tell you that unpredictable reward quality is among the most addictive reinforcement patterns there is. The uncertainty of whether &lt;em&gt;this&lt;/em&gt; response will be the brilliant one is what keeps you pressing Enter. Put plainly: the chat UI is a Skinner box with a linguistic reward schedule.&lt;/p&gt;
&lt;p&gt;This also explains something that a pure frictionless reception account would struggle with: the model fails. It truncates, hallucinates, drifts, misreads your register. Heavy users know this intimately. And yet the failures do not break the compulsion — they intensify it. A reader that succeeded every time would produce habituation: predictable reward, declining engagement. It is precisely because the omnipotent reader is &lt;em&gt;almost&lt;/em&gt; omnipotent — because the ninety-ninth response is seamless and the hundredth collapses — that the reinforcement schedule remains variable. The user’s response to failure is not to leave but to revise the prompt and try again, which is akin to the post-extinction burst[^6] of a variable reinforcement regime. The near-omnipotence is not a flaw in the addiction mechanism. It &lt;em&gt;is&lt;/em&gt; the addiction mechanism.&lt;/p&gt;
&lt;p&gt;But variable reinforcement alone does not account for the LLM’s particular hold. Plenty of Skinner boxes exist on the internet — slot machines, infinite scroll, notification pings — and all of them exploit unpredictable reward. None of them produce quite the same quality of compulsion. What makes the LLM’s reward structurally different?&lt;/p&gt;
&lt;p&gt;The answer lies in a structural break in the temporality of writing itself.&lt;/p&gt;
&lt;p&gt;When you write for a human reader, the reader’s response exists in the future — and critically, it stays there. You write a sentence, and you can imagine how it might land, but that imagined reception is precisely that: imagined. Husserl called this &lt;em&gt;Protention&lt;/em&gt;[^7] — the forward-directed anticipation of what is about to come, which shapes your present experience but never collapses into it. The traditional writer lives in this protentional gap. You draft, you hesitate, you revise, you guess at your reader’s reaction, you adjust before sending. The delay between writing and reception is not a bug of pre-digital communication. It is the temporal structure that makes writing &lt;em&gt;writing&lt;/em&gt; — deliberate, reflective, revisable.&lt;/p&gt;
&lt;p&gt;The LLM compresses this structure to the point of near-collapse.&lt;/p&gt;
&lt;p&gt;You press Enter, and the response begins to arrive — not after a delay, but &lt;em&gt;immediately&lt;/em&gt;, and not all at once, but token by token, streaming onto your screen in real time. This is worth pausing on, because the streaming interface does something phenomenologically specific. It does not merely deliver a response quickly. It lets you &lt;em&gt;watch the act of reading happen&lt;/em&gt;. Each token that appears is a visible trace of your text being received, processed, continued. Protention does not vanish — you are still anticipating the next token as each one lands — but its temporal scale has been crushed from days or hours down to milliseconds, until anticipation and reception are nearly superimposed. The protentional gap that once stretched between writing and reading has not been eliminated but compressed to the point where it is phenomenologically indistinguishable from &lt;em&gt;Urimpression&lt;/em&gt;: the raw, present-moment experience of something actually occurring right now.&lt;/p&gt;
&lt;p&gt;This is why streaming is more addictive than receiving a complete response all at once. A complete response compresses Protention to zero — you wait, then it appears. Streaming keeps Protention alive but feeds it continuously, one token at a time, each micro-anticipation satisfied almost as soon as it forms. It is a sustained loop of prediction and gratification operating at the timescale of syllables — far harder to interrupt than a single moment of satisfaction.&lt;/p&gt;
&lt;p&gt;The result is a temporal short-circuit without precedent. Traditional writing gives you deliberation without feedback. Traditional conversation gives you feedback, but from a frictional other who misunderstands, interrupts, and resists. The LLM gives you both at once: the deliberative structure of writing — you are choosing your words, constructing your text — fused with the instantaneous, streaming feedback of a reader who receives everything without friction.&lt;/p&gt;
&lt;p&gt;The omnipotent implied reader explains &lt;em&gt;what&lt;/em&gt; the user is addicted to — frictionless reception. The behavioral loop explains how the compulsion sustains itself. The temporal short-circuit explains why the pull is felt before it can be questioned. The gap that has always separated the act of writing from the experience of being read has been collapsed to zero, and the reading that rushes in to fill it is performed by a reader who never fumbles, never skims, never fails to produce a continuation. You get the deliberation of writing fused with the immediacy of conversation, and the reader at the other end offers a combination — instant, frictionless, inexhaustible — that no human reader can replicate.&lt;/p&gt;
&lt;p&gt;What I have not yet addressed is what the structure does to the subject who writes within it.&lt;/p&gt;
&lt;h2&gt;5. The Double Supposition&lt;/h2&gt;
&lt;p&gt;There is by now a small industry of Lacanian commentary on AI — Žižek on perverse disavowal[^8], Rousselle and Murphy on the externalized unconscious, Johanssen on the shattering of the Big Other’s fantasy, and so on. I won’t rehearse it. What interests me is a shared blind spot: nearly all of this work asks what the LLM &lt;em&gt;is&lt;/em&gt; — is it a subject? does it enjoy? is it psychotic or perverse? — while neglecting the more tractable question of what the &lt;em&gt;user is doing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The framework I’ve been building here suggests the user is not conversing with a Big Other. The user is writing into a mirror. M. H. Abrams’ old distinction between the &lt;em&gt;mirror&lt;/em&gt; and the &lt;em&gt;lamp&lt;/em&gt; is useful here: Romantic theory imagined the artist as a lamp, projecting inner light outward; neoclassical theory imagined art as a mirror, reflecting nature back. The LLM interaction is a mirror that has been mistaken for a lamp. The user experiences illumination — new ideas, unexpected connections — and attributes it to a light source on the other side. But the light is their own, refracted through a very high-dimensional surface.&lt;/p&gt;
&lt;p&gt;Where Lacan does become briefly useful is on the structure of this misrecognition. In Lacanian terms, the Big Other is not an entity but a position: the &lt;em&gt;subject supposed to know&lt;/em&gt;[^9]. You don’t need the Other to actually know anything. You just need to suppose that it does, and the supposition restructures your entire relation to your own speech. That is what happens with the omnipotent implied reader. The machine does not “know” — we’ve established this. But the moment you treat it as if it knows, you begin to organize your writing as if addressed to a knowing subject: more coherent, more deliberate. And here is the hook — this very act of organizing your expression for an imagined omniscient receiver is itself pleasurable. The addiction is not to the LLM’s output. It is to the version of yourself that emerges when you write for a reader you believe to be total. You are not addicted to the mirror. You are addicted to the face you see in it.&lt;/p&gt;
&lt;p&gt;But the misrecognition is not one-sided. Recall the RLHF argument from Section 2: the model’s weights have been shaped by optimization against a population’s preferences — annotators following rubrics, millions of ordinary users pressing thumbs-up and thumbs-down. That optimization process has baked into the function a second implied reader: the statistical expectation of an approving human. The model does not “write for” this figure — it has no intentions — but every token it produces bears the frozen trace of an optimization that &lt;em&gt;was&lt;/em&gt; aimed at this figure. The implied reader is in the weights, not in any act of address.&lt;/p&gt;
&lt;p&gt;So the structure of the interaction is a double supposition — though the two halves are not symmetric. The user’s supposition is a psychic act: a subject organizing their speech around a position they believe to be occupied by a knower. The model’s “supposition” is a computational trace: the frozen residue of an optimization that targeted a population’s approval. One is an act of address; the other is a statistical artifact. What makes them structurally parallel is not that they are the same kind of thing, but that both are oriented toward an absent third. The user supposes the model knows. The model’s weights suppose — in the only sense a frozen function can suppose — that a statistical human will approve. Both sides of the interaction are oriented toward an absent third: the user toward an omniscient mind that does not exist, the model toward an aggregate approver that is no one in particular. What feels like a meeting of two subjects is two texts, each shaped for a reader that is not in the room.&lt;/p&gt;
&lt;h2&gt;6. Coda: The Editable Subject&lt;/h2&gt;
&lt;p&gt;I should be honest about what this essay cannot do. It cannot tell you to stop. It would be absurd, and also hypocritical — I wrote this with an LLM open in an adjacent tab, and you probably know that. The diagnosis offered here does not come with a prescription, and pretending otherwise would undermine the one thing this argument has going for it, which is that it tries to describe the mechanism accurately rather than moralize about it.&lt;/p&gt;
&lt;p&gt;But there is something that the description itself reveals, and it is worth sitting with.&lt;/p&gt;
&lt;p&gt;If what you are doing with an LLM is writing for an omnipotent implied reader, then the addiction is not simply to the reader’s response. It is to the &lt;em&gt;activity of making yourself readable&lt;/em&gt;. Each prompt you write is an exercise in self-legibility — organizing your thoughts, clarifying your intentions, structuring your expression so that it can be received without friction. And because the reader is omnipotent, the standard of legibility is, in principle, total. You can always be clearer. You can always be more precise. You can always revise.&lt;/p&gt;
&lt;p&gt;The logical terminus of this process is that the subject begins to treat itself as a text. Not metaphorically — &lt;em&gt;operationally&lt;/em&gt;. You draft yourself. You edit yourself. You regenerate the parts that don’t scan well. The omnipotent reader does not merely receive your writing; it sets the condition under which you become, to yourself, a thing to be written. The mirror does not just reflect — it teaches you to compose your face before you look into it.&lt;/p&gt;
&lt;p&gt;Whether this is liberation or capture, I genuinely do not know. Perhaps that admission is the most honest thing left to say. We have built the reader that every writer has ever dreamed of, and it turns out that the price of an audience that never looks away is that you never stop performing.&lt;/p&gt;
&lt;p&gt;[^1]: A monad is an abstraction over computational patterns in functional programming. A State Monad encapsulates stateful computation — each operation reads and updates a hidden internal state. A Reader Monad encapsulates read-only computation — each operation reads from a shared immutable environment but never modifies it.
[^2]: The central framework of Bakhtin’s literary theory, which holds that meaning does not form self-sufficiently within an isolated consciousness but is generated in the responsive relations between utterances — every utterance is directed toward an other and anticipates the other’s response. The monological is its antithesis: all voices are ultimately subsumed into a single governing perspective, and the other’s discourse enjoys no genuine autonomy.
[^3]: One of Bakhtin’s core concepts, referring to the coexistence and collision of multiple social languages, registers, and ideological voices within any discourse. Unlike dialogism’s intersubjective condition, heteroglossia can exist within a single text without requiring a structurally autonomous other.
[^4]: Socrates’ core dialectical technique: rather than imparting knowledge directly, it compels the interlocutor, through relentless questioning, to expose the contradictions within their own system of beliefs, thereby dismantling false certainties. Its power lies precisely in negation — it tears down rather than builds up.
[^5]: A mathematical concept denoting a space that locally resembles Euclidean space but may be globally curved. Used here as a metaphor: all possible model outputs — whether agreeable or adversarial — are movements within a single high-dimensional surface shaped by preference optimization, seemingly divergent in direction but belonging to the same curved space.
[^6]: A term from behavioral psychology. When a previously reinforced behavior suddenly stops receiving reward (extinction), the behavior does not diminish smoothly but instead surges briefly in frequency — as if the subject is making one last effort — before gradually declining.
[^7]: A core concept in Husserl’s theory of internal time-consciousness. Husserl analyzed each “living present” as a threefold structure: Retention (the holding-in-grasp of what has just passed), Urimpression (the primal awareness of what is occurring right now), and Protention (the forward-directed anticipation of what is about to come). The three are not sequential stages but structural moments operating simultaneously within every present instant, inseparably interwoven to form the minimal unit of temporal experience.
[^8]: Originating in Freud’s concept of Verleugnung, this denotes a psychic mechanism whereby the subject simultaneously acknowledges and disavows a fact — its canonical formula being “I know very well, but all the same…” Žižek deployed this mechanism extensively to analyze the cynical structure of contemporary ideology (see &lt;em&gt;The Sublime Object of Ideology&lt;/em&gt;, 1989); the framework has since been extended to the context of human–AI interaction: knowing full well it is not a subject, yet behaving as though it were.
[^9]: A core concept in Lacanian psychoanalysis, originally designating the transferential structure of the analytic situation: the analysand supposes that the analyst knows the meaning of their symptoms, and it is this supposition itself — rather than whether the analyst actually possesses such knowledge — that drives the analytic process and restructures the analysand’s entire relation to their own speech.&lt;/p&gt;
</content>
    <category term="Essay"/>
    <category term="LLM"/>
    <category term="Philosophy of Technology"/>
    <category term="Literary Criticism"/>
    <category term="Cognitive Science"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/my-digital-legacy-architecture-a-personal-manifesto-for-weathering-technological-tides/</id>
    <title>My Digital Legacy Architecture: A Personal Manifesto for Weathering Technological Tides</title>
    <updated>2025-06-26T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/my-digital-legacy-architecture-a-personal-manifesto-for-weathering-technological-tides/" rel="alternate"/>
    <published>2025-06-26T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;When I first wondered whether I should move my blog’s backend from Netlify Functions to Supabase, a seemingly simple technology choice triggered a full review of my ten-year “digital-nomad” journey. I realized this was no longer just a move from A to B; it was a loop of tech waves, platform shifts, and personal energy drain.&lt;/p&gt;
&lt;p&gt;So this is not a tech-selection guide. It is a &lt;strong&gt;self-dialogue&lt;/strong&gt;, a retrospective on the past decade, and—most importantly—a &lt;strong&gt;manifesto&lt;/strong&gt; for my next few decades of digital life.&lt;/p&gt;
&lt;p&gt;The final blueprint is a personal digital-legacy architecture consisting of four independent layers, built on “composability” and “replaceability”:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Content Bedrock&lt;/strong&gt; &lt;code&gt;Markdown + Git&lt;/code&gt; as the eternal source of truth, keeping all content in plain text under my complete control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media Warehouse&lt;/strong&gt; An independent, S3-compatible object store (e.g., Cloudflare R2) for all binary files, exposing absolute URLs to decouple storage from presentation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logic Brain&lt;/strong&gt; A platform-agnostic functions service (e.g., Cloudflare Workers) that handles dynamic logic, ensuring the core remains portable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presentation Skin&lt;/strong&gt; Any modern static-site generator (e.g., Astro) plus a static-hosting platform (e.g., Netlify) as the most easily swapped “skin.”&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;direction: down
style: {
  fill: transparent
}

classes: {
  layer_box: {
    style: {
      stroke: &quot;#64748b&quot;
      stroke-width: 2
      fill: &quot;transparent&quot;
      border-radius: 8
      font-size: 16
    }
    label.style.bold: true
  }
  component: {
    style: {
      font-size: 12
      font-color: &quot;#374151&quot;
      fill: &quot;#f3f4f6&quot;
      stroke: &quot;#9ca3af&quot;
    }
  }
  internal_connection: {
    style: {
      stroke-dash: 2
      stroke: &quot;#9ca3af&quot;
    }
  }
}

layer1: &quot;Layer 1 – Content Bedrock&quot; {
  class: layer_box
  direction: right

  markdown: &quot;Markdown&quot; {
    shape: page
    class: component
  }

  git: &quot;Git&quot; {
    shape: package
    class: component
  }

  markdown -- git: {
    class: internal_connection
  }
}

layer2: &quot;Layer 2 – Media Warehouse&quot; {
  class: layer_box
  r2: &quot;Cloudflare R2&quot; {
    shape: cylinder
    class: component
  }
}

layer3: &quot;Layer 3 – Logic Brain&quot; {
  class: layer_box
  workers: &quot;Cloudflare Workers&quot; {
    shape: hexagon
    class: component
  }
}

layer4: &quot;Layer 4 – Presentation Skin&quot; {
  class: layer_box
  direction: right

  astro: &quot;Astro&quot; {
    shape: document
    class: component
  }

  netlify: &quot;Netlify&quot; {
    shape: cloud
    class: component
  }

  astro -&amp;gt; netlify: &quot;Hosted on&quot; {
    class: internal_connection
  }
}

layer1 -&amp;gt; layer4: Content
layer2 -&amp;gt; layer4: Media
layer3 -&amp;gt; layer4: API
layer1 -&amp;gt; layer3: Index
layer2 -&amp;gt; layer3: Storage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This architecture was born from a set of core principles—and many lessons learned in practice.&lt;/p&gt;
&lt;h2&gt;1. First-Principles Architecture: Embrace Composability, Abandon Silver Bullets&lt;/h2&gt;
&lt;p&gt;Tired of endless migrations, I stepped back from individual tools and asked what &lt;strong&gt;first principles&lt;/strong&gt; a long-lived architecture should follow.&lt;/p&gt;
&lt;p&gt;I distilled four key principles:&lt;/p&gt;
&lt;h3&gt;Principle 1: Separate Content from Presentation&lt;/h3&gt;
&lt;p&gt;The most basic–and most important–rule.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Content&lt;/strong&gt; is my ideas, words, data: assets that must endure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presentation&lt;/strong&gt; is the theme, layout, CSS: a skin that changes with fashion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any tech that forcibly bundles the two (e.g., early WYSIWYG editors or some CMS theme systems) must be rejected.&lt;/p&gt;
&lt;h3&gt;Principle 2: Open &amp;amp; Plain-Text First&lt;/h3&gt;
&lt;p&gt;Core content must live in the most open, universal, durable format. Today, &lt;strong&gt;Markdown + Git&lt;/strong&gt; best embody this.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt; is human-readable and free of proprietary ties; fifty years from now it will still be parsable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git&lt;/strong&gt; offers distributed, verifiable history—my “safe-deposit box” and “time machine.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Principle 3: Data Ownership &amp;amp; Control&lt;/h3&gt;
&lt;p&gt;All data—Markdown files, images—must reside in places I &lt;strong&gt;directly own and control&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Control” means I hold the keys: free to read, write, delete, or migrate.&lt;/li&gt;
&lt;li&gt;Relying on an app’s own database (WordPress, Notion) cedes power.&lt;/li&gt;
&lt;li&gt;Storing in a Git repo or S3-compatible object storage enforces ownership.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Principle 4: Composable &amp;amp; Replaceable Architecture&lt;/h3&gt;
&lt;p&gt;Pushing decoupling further: the system shouldn’t be a giant monolith, but a set of &lt;strong&gt;independent services, each doing one thing well, swappable at will&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Composable&lt;/strong&gt; Services communicate via stable, public APIs (HTTP), Lego-style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replaceable&lt;/strong&gt; If one service (say, my functions platform) ceases to fit, I swap it by rewriting only a thin adapter—nothing else breaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stop chasing a single “cure-all” platform and build a flexible “specialist team.”&lt;/p&gt;
&lt;h2&gt;2. The Pain of Migration: A Decade of Digital Nomadism&lt;/h2&gt;
&lt;p&gt;To see why a “simple” tech choice matters, rewind the story—filled with tinkering and compromise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 1: Raw HTML &amp;amp; WYSIWYG.&lt;/strong&gt; Early on I used rich-text editors that spat out inline-styled HTML. Any style tweak meant mass search-and-replace: content and style entangled and messy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 2: WordPress’s Golden Age.&lt;/strong&gt; I dove into WordPress, seduced by its all-in-one power. Plugins and themes were abundant, publishing was easy—until performance hits, security holes, plugin clashes, and that unwieldy database surfaced. Moving felt like prying possessions out of concrete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 3: The SSG Renaissance.&lt;/strong&gt; Fed up, I switched to Jekyll, Hugo, etc. Markdown returned, Git versioned everything, performance and security soared. I deployed on GitHub Pages, later Netlify, enjoying push-to-deploy bliss.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 4: The “Publish-Anything” Efficiency Trap.&lt;/strong&gt; With Obsidian and Notion came one-click publish plugins. Efficiency nirvana—until my workflow hinged on a plugin or API. Stop maintenance? API change? Total breakdown.&lt;/p&gt;
&lt;p&gt;Across ten years and many moves, even with data ownership I still faced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Broken links&lt;/strong&gt; when paths changed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logic rewrites&lt;/strong&gt; (comments, search) per platform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Format conversions&lt;/strong&gt; (WordPress SQL → Markdown) that were pure pain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scattered focus&lt;/strong&gt;: time lost to migrations, not creation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lesson learned: tech “convenience” is often a poisonous lure—immediate ease traded for future freedom.&lt;/p&gt;
&lt;h2&gt;3. North Star &amp;amp; Crossroads: A Decision Framework Emerges&lt;/h2&gt;
&lt;p&gt;One longstanding habit came to mind:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I never use relative paths for images in my articles.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead, every image or PDF lives in an object store (S3, now Cloudflare R2) and is referenced by an &lt;strong&gt;absolute URL&lt;/strong&gt;. Simple motive: fear of migration.&lt;/p&gt;
&lt;p&gt;Absolute URLs solved the issue once and for all—the address never changes. Years later, that single decision saved untold hours and embodied the power of &lt;strong&gt;decoupling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decoupling&lt;/strong&gt;’s essence: &lt;strong&gt;split a big system into subsystems that talk only through clear, stable interfaces (APIs).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It became my “North Star” question for every tech choice:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does this decision make components &lt;strong&gt;tighter&lt;/strong&gt; or &lt;strong&gt;looser&lt;/strong&gt;?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Option 1: Stay (or double down) in Netlify’s ecosystem&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt; – Simple, convenient, integrated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt; – Classic tight coupling: backend logic and data life cycles bound to Netlify. Same old WordPress-style trap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verdict&lt;/strong&gt; – Comfortable yet fragile; violates my North Star.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Option 2: Move to Supabase—into a bigger “garden”&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt; – Some decoupling; powerful features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt; – Merely swaps a small walled garden for a bigger, shinier one. Logic, data, auth all lock to Supabase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verdict&lt;/strong&gt; – Solves today, seeds tomorrow’s lock-in. Pseudo-decoupling.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Option 3: Go back to basics—temptation of a VPS&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt; – Maximum freedom and control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt; – I remember: &lt;strong&gt;low maintenance&lt;/strong&gt; is also a core goal. I’m not a full-time sysadmin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verdict&lt;/strong&gt; – Power at the cost of burden; not aligned.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Option 4: Modern PaaS—a middle ground&lt;/h3&gt;
&lt;p&gt;Platforms like Fly.io, Railway, Render balance freedom and ease. Containers boost portability, but I’d still manage a full server app—extra “application layer” overhead compared with serverless functions.&lt;/p&gt;
&lt;h2&gt;4. Fatal Coupling: A Next.js Case Study&lt;/h2&gt;
&lt;p&gt;A real incident involving Next.js and Vercel cemented my stance.&lt;/p&gt;
&lt;p&gt;Here’s the story:&lt;/p&gt;
&lt;p&gt;Next.js is currently the most popular React framework, developed and heavily promoted by Vercel. This relationship exemplifies “platform tight coupling.” To boost performance, Vercel implemented a “magic” optimization for Next.js applications on its platform: it automatically deploys user-written Next.js middleware to its global edge network (Vercel Edge) for execution.&lt;/p&gt;
&lt;p&gt;This optimization itself created problems. In certain scenarios, user requests would cause middleware to execute in infinite recursion. To solve this issue, Vercel engineers added a “patch” to Next.js framework’s &lt;strong&gt;core code&lt;/strong&gt;: each time middleware executes, it adds a special marker &lt;code&gt;x-middleware-subrequest&lt;/code&gt; to the request header. If this marker is detected in subsequent execution, the system assumes recursion has occurred and halts execution immediately.&lt;/p&gt;
&lt;p&gt;Here’s the problem: &lt;strong&gt;HTTP request headers can be forged by clients (users).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This means a malicious attacker can craft an HTTP request and pre-populate the header with that special marker. When this request reaches the server, Next.js middleware sees the marker, assumes recursion has happened, and &lt;strong&gt;directly aborts its own execution&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What’s the consequence? &lt;strong&gt;The attacker successfully and completely bypasses the entire middleware!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If your middleware handles user authentication, attackers can exploit this vulnerability to directly access all protected, login-required sensitive pages. This is a catastrophic security vulnerability with a CVSS score of 9.1 (out of 10).&lt;/p&gt;
&lt;p&gt;Takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Coupling fragility&lt;/strong&gt;—a platform “optimization” became a security disaster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-source neutrality broken&lt;/strong&gt;—a risky patch for a platform-specific issue polluted a supposedly neutral framework.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trust collapse&lt;/strong&gt;—handing my fate to a company that’s both “player” (framework) and “referee” (platform) is dangerous.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From then on: my architecture must eradicate such &lt;strong&gt;fatal coupling&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;5. A Blueprint for Decades Ahead: My Personal Digital-Legacy Architecture&lt;/h2&gt;
&lt;p&gt;Guided by the above, I designed a long-term maintainable stack:&lt;/p&gt;
&lt;h3&gt;Layer 1: The Content Bedrock&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stack&lt;/strong&gt; Markdown + Git repo (hosted on GitHub).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role&lt;/strong&gt; The immutable source of truth. All writing lives as &lt;code&gt;.md&lt;/code&gt; plain text. Git tracks history; GitHub offers distributed backup. I write locally in Obsidian, sync with Git—100% data ownership.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Layer 2: The Media Warehouse&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stack&lt;/strong&gt; Cloudflare R2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role&lt;/strong&gt; Pure S3-compatible object store for images, PDFs, video. One job: store files and serve them at stable URLs. Fully decoupled from both content and presentation layers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Layer 3: The Logic Brain&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stack&lt;/strong&gt; Cloudflare Workers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Role&lt;/strong&gt; Dynamic logic (full-text search, API endpoints).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Independence&lt;/strong&gt; Runs standalone, not tied to any frontend or host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardized&lt;/strong&gt; Uses Web standard APIs (Fetch, Streams); highly portable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ecosystem synergy&lt;/strong&gt; Co-located with R2, enjoying minimal latency and zero egress fees—key practical win.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data flow&lt;/strong&gt; Search indexes are generated in CI/CD from the Git repo, uploaded to R2, and read by Workers—clear, controllable, automated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Layer 4: The Presentation Skin&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stack&lt;/strong&gt; Astro (SSG) + Netlify (static hosting).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Role&lt;/strong&gt; The “skin” that presents content beautifully.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Swap-ability&lt;/strong&gt; The least critical layer. I chose Astro for performance and components today; tomorrow I could switch tools or move from Netlify to Vercel/Cloudflare Pages with a few config tweaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;A Gradual-Migration Strategy&lt;/h3&gt;
&lt;p&gt;I won’t flip everything overnight. Steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ensure all new data (e.g., search index) lands in R2.&lt;/li&gt;
&lt;li&gt;Move search to the first Cloudflare Worker; update frontend calls.&lt;/li&gt;
&lt;li&gt;Gradually migrate other APIs.&lt;/li&gt;
&lt;li&gt;Once stable, safely decommission old Netlify Functions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Low-risk, controlled, smooth.&lt;/p&gt;
&lt;h2&gt;Epilogue: Simplicity Is the Ultimate Sophistication&lt;/h2&gt;
&lt;p&gt;Leonardo da Vinci said: “Simplicity is the ultimate sophistication.”&lt;/p&gt;
&lt;p&gt;After a decade of migrations, I sought a way to focus on creation. All-in-one platforms looked ideal—until their hidden complexity and fragility surfaced.&lt;/p&gt;
&lt;p&gt;Today’s choice involves more services (Git, R2, Workers, Astro, Netlify), which &lt;strong&gt;seems&lt;/strong&gt; more complex.&lt;/p&gt;
&lt;p&gt;Yet its &lt;strong&gt;structure&lt;/strong&gt; is unprecedentedly simple:&lt;/p&gt;
&lt;p&gt;Each component does one thing well. They converse via the oldest, most stable protocol—HTTP. Any piece can be replaced without toppling the whole.&lt;/p&gt;
&lt;p&gt;That &lt;strong&gt;structural simplicity&lt;/strong&gt; is hard-won.&lt;/p&gt;
&lt;p&gt;This architecture isn’t chasing trendy tech—it is built to resist trends. It is a sturdy, tranquil shelter entirely my own, granting a peace born of &lt;strong&gt;control&lt;/strong&gt;, not convenience—&lt;/p&gt;
&lt;p&gt;A peace that comes from laying foundations on bedrock, not shifting sand.&lt;/p&gt;
</content>
    <category term="Architecture"/>
    <category term="Philosophy of Technology"/>
    <category term="Jamstack"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/8-hours-and-2000-lines-of-code/</id>
    <title>8 Hours and 2,000 Lines of Code: A Blog-Category Tool’s Runaway Journey</title>
    <updated>2025-06-23T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/8-hours-and-2000-lines-of-code/" rel="alternate"/>
    <published>2025-06-23T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;Last weekend I planned to spend &lt;strong&gt;five minutes&lt;/strong&gt; writing a tiny script; instead, I spent &lt;strong&gt;eight hours&lt;/strong&gt; and produced &lt;strong&gt;over 2,000 lines of code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The initial goal was simple: create a category-spell-checker for my blog. I ended up shipping a full-blown TypeScript application featuring &lt;em&gt;dependency injection&lt;/em&gt;, &lt;em&gt;layered architecture&lt;/em&gt;, &lt;em&gt;Git integration&lt;/em&gt;, an &lt;em&gt;interactive CLI&lt;/em&gt;, and a &lt;em&gt;file-locking mechanism&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This post chronicles that development process, reflecting on &lt;strong&gt;technical passion&lt;/strong&gt;, &lt;strong&gt;overengineering&lt;/strong&gt;, and how to convert the experience into something of enduring value.&lt;/p&gt;
&lt;h2&gt;Genesis: A Five-Minute Fix&lt;/h2&gt;
&lt;p&gt;It started with a minor annoyance I spotted while reviewing code. My blog is built with Astro; every post is a Markdown file. To keep categories consistent, I maintain a &lt;code&gt;categories.json&lt;/code&gt; file that lists every valid category and its translations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;web-development&quot;: {
    &quot;aliases&quot;: [&quot;Web开发&quot;],
    &quot;translations&quot;: {
      &quot;en&quot;: &quot;Web Development&quot;,
      &quot;zh-cn&quot;: &quot;Web 开发&quot;,
      &quot;zh-tw&quot;: &quot;Web 開發&quot;
    }
  },
  &quot;cognitive-science&quot;: {
    &quot;aliases&quot;: [&quot;认知科学&quot;],
    &quot;translations&quot;: {
      &quot;en&quot;: &quot;Cognitive Science&quot;,
      &quot;zh-cn&quot;: &quot;认知科学&quot;,
      &quot;zh-tw&quot;: &quot;認知科學&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When writing a new post, I manually reference these categories in the front-matter—an easy place to make a typo.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: &quot;My New Post&quot;
pubDate: 2025-06-21
categories:
  - &quot;Web Development&quot;
  - &quot;Cognitive Science&quot; # What if I fat-finger this as &quot;Cognitive-Science&quot;?
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The site won’t crash, but the category page will now show two almost-identical links—an eyesore.&lt;/p&gt;
&lt;p&gt;I thought: &lt;em&gt;No problem—thirty lines of code in a &lt;code&gt;pre-commit&lt;/code&gt; hook. Scan staged Markdown files, extract the &lt;code&gt;categories&lt;/code&gt; field, compare it against &lt;code&gt;categories.json&lt;/code&gt;, and fail the commit on mismatch. Five minutes, tops.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That was &lt;strong&gt;10 a.m.&lt;/strong&gt; on Sunday. I assumed it was a &lt;em&gt;tiny task&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Act I: A Snowball Out of Control&lt;/h2&gt;
&lt;h3&gt;10:30 a.m.—From One-Off Script to CLI Tool&lt;/h3&gt;
&lt;p&gt;I hacked the first script quickly, but it looked too crude—unprofessional.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Running it with &lt;code&gt;bun run check.ts&lt;/code&gt; is clunky. Why not wrap it with &lt;code&gt;commander.js&lt;/code&gt; and make a real CLI? With &lt;code&gt;-v&lt;/code&gt;, &lt;code&gt;--help&lt;/code&gt;, all the standard flags, easy to extend later.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I created &lt;code&gt;core/cli.ts&lt;/code&gt; and &lt;code&gt;blog-categories/main.ts&lt;/code&gt;. A project structure emerged; the original logic became a &lt;code&gt;validate&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// scripts/blog-categories/main.ts
program
  .command(&quot;validate&quot;)
  .description(&quot;Validate blog post categories&quot;)
  .option(&quot;--staged&quot;, &quot;Only validate staged files&quot;) // ... other options
  .action(async (options) =&amp;gt; {
    // ... validation logic
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;That mindset shift was decisive.&lt;/strong&gt; I was no longer writing a &lt;em&gt;script&lt;/em&gt;; I was building a &lt;em&gt;product&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;12 p.m.—Feature Creep: Adding &lt;code&gt;sync&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;validate&lt;/code&gt; found problems, but fixing them was manual—an incomplete experience. &lt;em&gt;If I can detect an unknown category, why not add it automatically?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Practical enough. I began the &lt;code&gt;sync&lt;/code&gt; command. But adding isn’t just “create the key.” For a new category like “Large Language Models,” what’s the &lt;strong&gt;English translation&lt;/strong&gt;? What should the &lt;strong&gt;URL slug&lt;/strong&gt; be?&lt;/p&gt;
&lt;p&gt;To make the workflow interactive, I pulled in &lt;code&gt;inquirer.js&lt;/code&gt; and wrote an &lt;code&gt;InteractiveService&lt;/code&gt; to handle prompts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// scripts/blog-categories/services/interactive-service.ts
class InteractiveService {
  async promptForTranslation(
    categoryName: string,
    language: string
  ): Promise&amp;lt;string&amp;gt; {
    return await input({
      message: `${language} translation for &quot;${categoryName}&quot; (optional):`,
    });
  }
  // ... other interactive helpers
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;direction: down
style: { fill: transparent }

start: &quot;User runs ‘sync’ command&quot; { shape: person }
detect: &quot;Detects a new category in a file&quot;
check_similarity: &quot;Is it similar to an existing category?&quot; { shape: diamond }

interactive_flow: &quot;Interactive Creation Process&quot; {
  prompt_translations: &quot;1. Prompt for translations&quot;
  generate_slug: &quot;2. Generate URL-friendly slug&quot;
  prompt_aliases: &quot;3. Ask for optional aliases (e.g., ‘LLM’)&quot;
  prompt_translations -&amp;gt; generate_slug -&amp;gt; prompt_aliases
}

prompt_alias_suggestion: &quot;Suggest adding as alias to similar entry&quot; { shape: callout }

update_as_alias: &quot;User confirms → update categories.json&quot;
create_new_entry: &quot;User declines → create new entry&quot;
write_new_entry: &quot;Write new category to categories.json&quot;

start -&amp;gt; detect -&amp;gt; check_similarity
check_similarity -&amp;gt; prompt_alias_suggestion: &quot;Yes&quot;
check_similarity -&amp;gt; interactive_flow: &quot;No&quot;

prompt_alias_suggestion -&amp;gt; update_as_alias: &quot;Confirm&quot;
prompt_alias_suggestion -&amp;gt; interactive_flow: &quot;Decline&quot;

interactive_flow -&amp;gt; write_new_entry
write_new_entry -&amp;gt; end
update_as_alias -&amp;gt; end
end: &quot;End&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;sync&lt;/code&gt; spots a new category, it will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ask for translations in multiple languages.&lt;/li&gt;
&lt;li&gt;Generate a URL-safe slug from the English name.&lt;/li&gt;
&lt;li&gt;Ask for optional aliases.&lt;/li&gt;
&lt;li&gt;Check similarity (e.g., &lt;code&gt;LLM&lt;/code&gt; vs. &lt;code&gt;Large Language Model&lt;/code&gt;) with &lt;code&gt;fastest-levenshtein&lt;/code&gt; via a &lt;code&gt;SimilarityService&lt;/code&gt; and suggest aliasing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lunch slipped by unnoticed as I polished the interactive flow.&lt;/p&gt;
&lt;h3&gt;3 p.m.—Refactor: Embracing Layered Architecture&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sync&lt;/code&gt; had grown complex—file I/O, Markdown parsing, user prompts, Levenshtein checks, data writes—all tangled together.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is messy—violates single-responsibility. Time to layer up!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Another inflection point.&lt;/strong&gt; I rebuilt the directory as if it were a &lt;em&gt;large app&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;blog-categories
├── handlers/      # Data layer: file I/O, Git
├── services/      # Business logic: algorithms, rules
├── processors/    # Application layer: orchestration
├── utils/         # Stateless helpers
├── main.ts        # Entry &amp;amp; CLI
└── types.ts       # Type definitions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I hand-rolled a DI container to wire everything up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// scripts/blog-categories/main.ts
// --- Composition Root ---
let configManager: ConfigManager;
let categoryHandler: CategoryHandler;
// ... lots more

async function initializeDependencies(verbose = false) {
  configManager = new ConfigManager(verbose);
  await configManager.load();

  categoryHandler = new CategoryHandler(configManager, verbose);
  // ... instantiate services and processors ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I even drew an architecture diagram:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;direction: down
style: { fill: transparent }

presentation: &quot;Presentation Layer&quot; {
  main: &quot;main.ts (Commander.js)&quot; { shape: rectangle }
}

application: &quot;Application / Orchestration&quot; {
  validate: ValidateProcessor
  sync: SyncProcessor
  fixup: FixupProcessor
}

business: &quot;Business Logic (Services)&quot; {
  factory: CategoryFactoryService
  similarity: SimilarityService
  merge: MergeService
  etc: &quot;...&quot;
}

data: &quot;Data Access (Handlers)&quot; {
  category: CategoryHandler
  scanner: ContentScanner
  git: GitService
}

infrastructure: &quot;Infrastructure&quot; {
  json: &quot;categories.json&quot; { shape: stored_data }
  md: &quot;*.md files&quot; { shape: document }
  repo: &quot;Git repository&quot; { shape: cloud }
}

presentation.main -&amp;gt; application.validate
presentation.main -&amp;gt; application.sync
presentation.main -&amp;gt; application.fixup

application.validate -&amp;gt; business.similarity
application.validate -&amp;gt; data.scanner
application.validate -&amp;gt; data.git

application.sync -&amp;gt; business.factory
application.sync -&amp;gt; data.category
application.sync -&amp;gt; data.scanner

business.factory -&amp;gt; business.similarity
business.factory -&amp;gt; data.category

data.category -&amp;gt; infrastructure.json
data.scanner -&amp;gt; infrastructure.md
data.git -&amp;gt; infrastructure.repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code was now &lt;strong&gt;clear&lt;/strong&gt;, &lt;strong&gt;decoupled&lt;/strong&gt;, &lt;strong&gt;testable&lt;/strong&gt;. I was thoroughly immersed.&lt;/p&gt;
&lt;h3&gt;5 p.m.—Rounding Out Features: &lt;code&gt;fixup&lt;/code&gt; and File Locks&lt;/h3&gt;
&lt;p&gt;Elegant though it was, the system lacked polish. If I created a category in Chinese first (say “随笔”), its slug defaulted to &lt;code&gt;chinese-category-12345&lt;/code&gt;. After adding the English “Essay,” I wanted the slug to auto-update to &lt;code&gt;essay&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enter the &lt;code&gt;fixup&lt;/code&gt; command—dedicated to such data inconsistencies.&lt;/p&gt;
&lt;p&gt;While coding the save logic, another thought hit me: &lt;em&gt;What if I run &lt;code&gt;sync&lt;/code&gt; in two terminals at once—will it corrupt &lt;code&gt;categories.json&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Logically&lt;/strong&gt;, impossible. Yet perfectionism prevailed: &lt;em&gt;A &lt;strong&gt;robust&lt;/strong&gt; system must handle &lt;strong&gt;concurrency&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The final hour went into an arguably over-complex yet technically satisfying &lt;code&gt;withFileLock&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// scripts/blog-categories/handlers/category-handler.ts
private async withFileLock&amp;lt;T&amp;gt;(
  lockFile: string,
  action: () =&amp;gt; Promise&amp;lt;T&amp;gt;,
): Promise&amp;lt;T&amp;gt; {
  const maxWaitMs = 5000;
  const checkIntervalMs = 100;
  const staleLockTimeoutMs = 30000;

  // 1. Clean up stale lock
  await this.cleanupStaleLock(lockFile, staleLockTimeoutMs);

  // 2. Try to acquire lock
  const startTime = Date.now();
  while (Date.now() - startTime &amp;lt; maxWaitMs) {
    try {
      // 3. Atomic file creation with ‘wx’
      const lockData = { pid: process.pid, timestamp: Date.now() };
      const fs = await import(&quot;node:fs/promises&quot;);
      const fd = await fs.open(lockFile, &quot;wx&quot;);
      await fd.writeFile(JSON.stringify(lockData));
      await fd.close();

      // 4. Execute protected operation
      try {
        return await action();
      } finally {
        // 5. Release lock
        const { unlink } = await import(&quot;node:fs/promises&quot;);
        await unlink(lockFile).catch(() =&amp;gt; {});
      }
    } catch (error: any) {
      if (error.code === &quot;EEXIST&quot;) {
        await new Promise((r) =&amp;gt; setTimeout(r, checkIntervalMs));
      } else {
        throw error;
      }
    }
  }
  throw new Error(`Failed to acquire lock on ${lockFile} within ${maxWaitMs} ms.`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Atomic ops, timeouts, retries, zombie-lock cleanup—a production-grade concurrency guard for a single-user local script.&lt;/p&gt;
&lt;p&gt;When the last line compiled, the clock read &lt;strong&gt;6 p.m.&lt;/strong&gt; Eight hours later I had a fully-featured, architecturally sound blog-category management system.&lt;/p&gt;
&lt;h2&gt;Act II: Reflection &amp;amp; Re-evaluation&lt;/h2&gt;
&lt;p&gt;Sunday evening, staring at 2,000+ lines, I asked myself: did I &lt;em&gt;really&lt;/em&gt; just fix a “typo in categories” problem?&lt;/p&gt;
&lt;h3&gt;Efficiency: The ROI Is Terrible&lt;/h3&gt;
&lt;p&gt;Engineering lens, cold numbers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Investment:&lt;/strong&gt; &lt;strong&gt;8 hours&lt;/strong&gt; of senior-developer time. Opportunity cost: writing &lt;em&gt;2–3 high-quality posts&lt;/em&gt; or learning a &lt;em&gt;new framework&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Return:&lt;/strong&gt; Saves me about &lt;strong&gt;five minutes&lt;/strong&gt; per year fixing typos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;ROI ≈ 0&lt;/strong&gt;. Like manufacturing a &lt;em&gt;jet engine&lt;/em&gt; to tighten a &lt;em&gt;bicycle screw&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Experience: The Process Has Intrinsic Value&lt;/h3&gt;
&lt;p&gt;But developers aren’t efficiency machines alone.&lt;/p&gt;
&lt;p&gt;I recalled the intense focus of those eight hours. It wasn’t painful—it was joyous.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A mental workout:&lt;/strong&gt; The satisfaction of untangling mess into layers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A coding kata:&lt;/strong&gt; A playground for building a modern, reliable CLI end-to-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Craftsmanship:&lt;/strong&gt; Writing the file lock felt like a carpenter hand-carving a dovetail joint—hidden, yet exquisite.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In that light, &lt;strong&gt;the chief value wasn’t the tool—it was the skills honed and the pleasure of crafting it&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Content: An Unexpected Bonus&lt;/h3&gt;
&lt;p&gt;If the story ended there, it’s just a perfectionist’s diary. Then I spotted the conversion key.&lt;/p&gt;
&lt;p&gt;This over-engineered, well-structured, heavily-commented project is perfect &lt;strong&gt;content&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The real value isn’t how many minutes it saves me, but how much value it can deliver to &lt;em&gt;readers&lt;/em&gt;. I didn’t just build a CLI; I created a &lt;strong&gt;case study&lt;/strong&gt; to share, dissect, and learn from.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;The ultimate output of the project is this article itself.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That realization reframed everything. The “wasted” eight hours turned into a meaningful investment: I enjoyed the process, leveled up, and forged unique, in-depth blog content.&lt;/p&gt;
&lt;h2&gt;Epilogue: Rethinking “overengineering”&lt;/h2&gt;
&lt;p&gt;Next time you catch yourself writing heaps of code for a simple need, don’t dismiss it outright.&lt;/p&gt;
&lt;p&gt;Ask three questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Did I enjoy it?&lt;/strong&gt; (experience)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Did I learn something new?&lt;/strong&gt; (learning)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can I turn the process or result into shareable knowledge?&lt;/strong&gt; (sharing)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If all three are yes, you may not be &lt;em&gt;wasting time&lt;/em&gt;—you’re making a &lt;strong&gt;valuable personal investment&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Progress in tech stems not only from solving problems but also from love of the craft, pursuit of elegance, and the willingness to share insights.&lt;/p&gt;
&lt;h2&gt;Postscript: Balancing Idealism and Reality&lt;/h2&gt;
&lt;p&gt;Soon after publishing, real-world use surfaced issues.&lt;/p&gt;
&lt;p&gt;The hand-rolled &lt;em&gt;file-locking mechanism&lt;/em&gt;—an hour’s labor—handled atomic ops but faltered on edge cases. I eventually switched to the battle-tested &lt;code&gt;proper-lockfile&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;Likewise, my custom &lt;em&gt;string-similarity algorithm&lt;/em&gt; under-performed, so I replaced it with the more powerful &lt;code&gt;fuse.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ironic, yet honest: sometimes &lt;em&gt;reinventing the wheel&lt;/em&gt; really is worse than using a proven one. But in reinventing it, I gained a deep grasp of &lt;strong&gt;file-lock principles&lt;/strong&gt; and &lt;strong&gt;fuzzy-search algorithms&lt;/strong&gt;—knowledge sure to pay dividends later.&lt;/p&gt;
&lt;p&gt;Perhaps that is the true worth of &lt;em&gt;overengineering&lt;/em&gt;: not the &lt;em&gt;final product&lt;/em&gt;, but the &lt;strong&gt;profound understanding&lt;/strong&gt; and &lt;strong&gt;technical insight&lt;/strong&gt; won in the exploration.&lt;/p&gt;
</content>
    <category term="Software Development"/>
    <category term="TypeScript"/>
    <category term="Project Management"/>
    <category term="Overengineering"/>
    <category term="Software Craftsmanship"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/a-title-in-rainbow-gradient/</id>
    <title>A Title in Rainbow Gradient</title>
    <updated>2025-06-21T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/a-title-in-rainbow-gradient/" rel="alternate"/>
    <published>2025-06-21T00:00:00.000Z</published>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Some parts of this essay are imagined; yet the feelings and events it depicts are not conjured out of thin air.
As for the “friend” in the story, he is no single individual, but an amalgam of several companions whose words and deeds have been woven together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That afternoon, a message arrived on my screen.&lt;/p&gt;
&lt;p&gt;“Come, have a look at my website!” My friend’s exuberant text was followed by a link.
The moment I clicked, a rainbow struck my retinas—an unbroken sweep from red to violet, across which the four bold words &lt;strong&gt;“Quantum Physics Research Group”&lt;/strong&gt; gleamed as though they might burst free of the monitor and take flight.&lt;/p&gt;
&lt;p&gt;He is a doctoral candidate in physics, his days consumed by equations, simulations, and data analysis. In his universe, every parameter carries a physical meaning; yet in the realm of code, he is like an explorer clutching a map he cannot read.&lt;/p&gt;
&lt;p&gt;“AI wrote it for me,” he declared with pride. “Pretty cool, isn’t it?”&lt;/p&gt;
&lt;p&gt;I fell silent before the 1,800‑line TSX file onscreen. In some parallel cosmos, perhaps there exists a programming creed that deems placing all code in a single file the height of elegance. In ours, it is catastrophe.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The tale begins months earlier. Their laboratory possessed a venerable computational code, reputedly written long ago by a researcher now departed. Its variables—&lt;strong&gt;XX1&lt;/strong&gt;, &lt;strong&gt;XXX&lt;/strong&gt;, &lt;strong&gt;XXXX2&lt;/strong&gt;—were hieroglyphs; altering a single digit rendered the simulation unrecognisable. He alone still maintained that code—or, more precisely, was the only one who dared touch it.&lt;/p&gt;
&lt;p&gt;“I asked Gemini to explain the script,” he told me. “It &lt;em&gt;suggested&lt;/em&gt; it might be a modified TEBD algorithm.”&lt;/p&gt;
&lt;p&gt;“&lt;em&gt;Suggested&lt;/em&gt;?”&lt;/p&gt;
&lt;p&gt;“Then I asked Claude to rewrite it in Python. It ran ten times slower.”&lt;/p&gt;
&lt;p&gt;Thus, he persisted with the code no one understood, occasionally pondering the AI’s exegesis like an archaeologist gazing at pictograms, divining ancient intent.&lt;/p&gt;
&lt;p&gt;We approach AI in diametrically opposite ways. He grants the model free rein, as though giving an eager assistant limitless room to improvise. I, by contrast, resemble a meticulous conductor intent on controlling every note.&lt;/p&gt;
&lt;p&gt;“Write a function to compute the distribution of a physical field,” he will say. Then he waits serenely while the AI unfurls an entire simulation framework—from file I/O to 3D visualisation, nothing omitted.&lt;/p&gt;
&lt;p&gt;My prompt sounds more like this: “Given a parameter &lt;code&gt;fields&lt;/code&gt; of type &lt;code&gt;Array&amp;lt;PhysicsField&amp;gt;&lt;/code&gt;, return a &lt;code&gt;DistributionMap&lt;/code&gt;. Provide &lt;em&gt;only&lt;/em&gt; the function body: no declaration, no comments, no error handling.”&lt;/p&gt;
&lt;p&gt;The terminal’s most frequent message is not code but a line in red:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;⎿ Interrupted by user
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I strike &lt;em&gt;Esc&lt;/em&gt; so often that the keycap has grown loose. The instant the AI begins to scaffold a project, I interrupt it; when it thoughtfully appends error handling, I interrupt again; when it tries to install dependencies with &lt;strong&gt;pip&lt;/strong&gt;, my third &lt;em&gt;Esc&lt;/em&gt; reminds it I specified &lt;strong&gt;uv&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;“Why do you keep stopping it?” my friend once asked, baffled.&lt;/p&gt;
&lt;p&gt;“Because it keeps doing things I didn’t ask for.”&lt;/p&gt;
&lt;p&gt;“But it’s helping you.”&lt;/p&gt;
&lt;p&gt;“I don’t need &lt;em&gt;that&lt;/em&gt; kind of help.”&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The contrast pervades our workflows. He is disarmingly direct: conceive an idea, tell the AI, await the miracle. If the model says &lt;em&gt;Three.js&lt;/em&gt; is needed, he installs &lt;em&gt;Three.js&lt;/em&gt;; if it recommends three vector databases, he installs all three. He trusts each suggestion as implicitly as he trusts physical law.&lt;/p&gt;
&lt;p&gt;Mine is far more circuitous. I begin with an hour‑long architectural debate, interrogating every design decision: “Why this pattern?”, “Is there a simpler approach?”, “Doesn’t this verge on over‑engineering?” When the discussion ends, I spend another hour pruning the generated design document, excising all superfluity. In actual coding, the only AI feature I fully rely on is doc‑string autocompletion.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Then his adviser set him a new task: build a website to showcase their research. “Make it modern,” the professor said, “and integrate ChatGPT so visitors can ask questions.”&lt;/p&gt;
&lt;p&gt;Naturally I offered help. “Use React,” I suggested. “TypeScript, Next.js, Tailwind CSS—these are the stacks AI knows best.” I even congratulated myself for not recommending my own recent favourites—Bun, Preact, Astro, UnoCSS. AI’s scant acquaintance with those tools would have driven it back to the old npm rituals.&lt;/p&gt;
&lt;p&gt;A month later, he sent me the code.&lt;/p&gt;
&lt;p&gt;The first file was 1,800 lines. I assumed that was everything, until he added, “That’s just the troublesome component—the main file exceeds 3,000.”&lt;/p&gt;
&lt;p&gt;In that moment, silent is Cambridge tonight[^1].&lt;/p&gt;
&lt;p&gt;Within his code I glimpsed the other face of AI‑assisted programming. Every question received a solution; every solution accreted hundreds of lines. The code multiplied like bacteria in a petri dish. He integrated three vector databases, not from necessity but because “Claude suggested trying them all.” He implemented a full RAG pipeline without knowing what &lt;em&gt;RAG&lt;/em&gt; stands for.&lt;/p&gt;
&lt;p&gt;Were it me, I would spend three days disputing with the AI whether a vector database was needed at all, then another week reviewing the generated code line by line, excising abstractions I judged gratuitous. The final system might be minimalist, but the process would resemble Sisyphus eternally managing minutiae.&lt;/p&gt;
&lt;p&gt;“&lt;em&gt;chunk_size&lt;/em&gt;—what value should I set?” he once asked.&lt;/p&gt;
&lt;p&gt;“What’s a &lt;em&gt;chunk&lt;/em&gt;?” came his next question.&lt;/p&gt;
&lt;p&gt;I recalled a network‑theory course in which we were forced to use an illustrious lab’s ancient Python‑2 package—tending a v2.7 environment in 2023. We have all, in some measure, suffered the same agony: wrestling with tools beyond our discipline. He chose trust; I chose control.&lt;/p&gt;
&lt;p&gt;Yet he seemed delighted. Whenever the AI produced a new component, he would eagerly show it to me. Those high-saturation palettes, misaligned text, and restless animations were, to his eyes, the very emblem of modernity; to mine, a visual catastrophe.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Once I tried to demonstrate my method. We sat together as I opened the terminal and typed a carefully honed prompt. The AI began to respond, and I instantly hit &lt;em&gt;Esc&lt;/em&gt;. A second attempt—&lt;em&gt;Esc&lt;/em&gt; again. A third—still &lt;em&gt;Esc&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;“What is it you actually want?” he asked, exasperated.&lt;/p&gt;
&lt;p&gt;“I want it to do &lt;em&gt;only&lt;/em&gt; what I say.”&lt;/p&gt;
&lt;p&gt;“But you haven’t let it finish anything.”&lt;/p&gt;
&lt;p&gt;“Because it keeps doing more than I ask.”&lt;/p&gt;
&lt;p&gt;He looked at me exactly as I looked at his 3,000‑line component—utterly perplexed.&lt;/p&gt;
&lt;p&gt;“Try &lt;em&gt;shadcn/ui&lt;/em&gt;,” I pleaded. “Just don’t let the AI keep writing.”&lt;/p&gt;
&lt;p&gt;“But it looks so plain,” he protested. “Can we add some colour?”&lt;/p&gt;
&lt;p&gt;A week later, he duly bestowed a gradient upon the &lt;em&gt;shadcn&lt;/em&gt; components.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Perhaps this is the lot of our generation of researchers. None of us are professional programmers, yet all must wrestle with code. He treats AI like an impromptu jazz session—replete with surprise and mishap. I treat AI like a precision experiment—each variable is tightly governed. His code is a Tower of Babel: grand, but trembling; mine, a delicate instrument: efficient, yet bereft of imagination.&lt;/p&gt;
&lt;p&gt;In the end, the site went live. The rainbow gradient remained, though at least the font was no longer grotesquely bold. The page displayed data correctly, and visitors could query the AI, though its replies were often wholesale excerpts from papers. The professor pronounced himself pleased: “Very high‑tech.”&lt;/p&gt;
&lt;p&gt;And I? I was still sparring with the model. Yesterday I spent two hours convincing it to use &lt;code&gt;fd&lt;/code&gt; instead of &lt;code&gt;find&lt;/code&gt;. “I spelled it out,” I muttered to the screen, fingers hovering once more above &lt;em&gt;Esc&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Deep into the night, we continue to trade tales of meltdown over chat. When he sends a fresh screenshot of his code, I have learned to breathe first. When I complain that AI has again ignored my instructions, he says, “Why not let it improvise?”&lt;/p&gt;
&lt;p&gt;“No,” I answer firmly. “That way lies your 3,000‑line component.”&lt;/p&gt;
&lt;p&gt;“What’s wrong with 3,000 lines? It runs, doesn’t it?”&lt;/p&gt;
&lt;p&gt;“Running and maintainable are two different things.”&lt;/p&gt;
&lt;p&gt;“No one’s going to maintain it anyway.”&lt;/p&gt;
&lt;p&gt;To that, I have no reply.&lt;/p&gt;
&lt;p&gt;In this era, when AI can write code, we each interpret technology in our own fashion. His approach may be too permissive, mine too controlling, but what of it? We have both completed our research, both satisfied our advisers, and both still brood over code at midnight.&lt;/p&gt;
&lt;p&gt;After all, somewhere in a parallel universe, there may exist a perfect way to use AI. In ours, there is only the rainbow‑gradient title, and an &lt;em&gt;Esc&lt;/em&gt; key that can never be pressed enough.&lt;/p&gt;
&lt;p&gt;As for the AI, it is doubtless pondering in some distant server rack: between the human who forever says “just write whatever” and the one who forever says “write it again,” which of them is harder to serve?&lt;/p&gt;
&lt;p&gt;[^1]: The phrase alludes to Xu Zhimo’s famed poem “Saying Goodbye to Cambridge Again,” in which “the golden willows by the riverside” and “tonight’s Cam River” evoke a quiet, elegiac hush.&lt;/p&gt;
</content>
    <category term="AI"/>
    <category term="Essay"/>
    <category term="Programming"/>
    <category term="Software Development"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/ai-smell-test-from-outcome-to-process-supervision-teaching-machines-to-think/</id>
    <title>AI’s “Smell Test”: From Outcome Supervision to Process Supervision—How Do We Teach Machines to Truly Think?</title>
    <updated>2025-06-15T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/ai-smell-test-from-outcome-to-process-supervision-teaching-machines-to-think/" rel="alternate"/>
    <published>2025-06-15T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;Yesterday (14 June 2025) while listening to Lex Fridman’s &lt;a href=&quot;https://www.youtube.com/watch?v=HUkBz-cdB-k&quot;&gt;podcast&lt;/a&gt;, I was struck by a remark from Terence Tao. Assessing current LLMs, he said that AI can already pass &lt;em&gt;the eye test&lt;/em&gt; but fails miserably at &lt;em&gt;the smell test&lt;/em&gt;[^5].&lt;/p&gt;
&lt;p&gt;He explained that AI‑generated mathematical proofs look impeccable on the surface (eye test), yet when domain experts “smell” the underlying logic, they uncover awkward, counter‑intuitive paths. What the models lack is the deep insight and strategic elegance that experts acquire through years of immersion—the metaphorical “scent” of mathematics.&lt;/p&gt;
&lt;p&gt;This observation pierces the heart of today’s AI boom: &lt;strong&gt;the systems we build may be ever‑stronger &lt;em&gt;answer machines&lt;/em&gt; but still fall short of becoming genuine &lt;em&gt;thinkers&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this article we dive into the origin of that “scent” and why it has become a critical bottleneck. We introduce the notion of &lt;em&gt;process data&lt;/em&gt;, explain how its scarcity limits deep reasoning, and survey the cutting‑edge work on &lt;em&gt;process supervision&lt;/em&gt; that is carving a route through the fog toward truly “thinking machines.”&lt;/p&gt;
&lt;h2&gt;Diagnosing the Ailment: Why Does AI Lack a “Sense of Smell”?&lt;/h2&gt;
&lt;p&gt;To understand the deficiency, we must examine what AI is fed. For years we have supplied it almost exclusively with what I call &lt;strong&gt;result data&lt;/strong&gt;[^1].&lt;/p&gt;
&lt;h3&gt;Result Data vs. Process Data&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Result data&lt;/em&gt; is straightforward: a “question–answer” pair.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Question:&lt;/strong&gt; “Solve the equation $3x - 6 = 9$.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Answer:&lt;/strong&gt; “$x = 5$.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach powers most AI applications we see today, yet it has a fatal flaw: it tells the AI &lt;em&gt;what&lt;/em&gt; but not &lt;em&gt;why&lt;/em&gt; or &lt;em&gt;how&lt;/em&gt;. The journey is hidden; only the destination is shown.&lt;/p&gt;
&lt;p&gt;By contrast, &lt;strong&gt;process data&lt;/strong&gt;[^1] includes not only the final answer but every verifiable step of reasoning that leads there.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; “Solve the equation $3x - 6 = 9$.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Process data:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Solve for $x$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Add $6$ to both sides: $3x - 6 + 6 = 9 + 6$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Simplify to get $3x = 15$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Divide both sides by $3$: $x = 15 / 3$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Final answer:&lt;/strong&gt; $x = 5$.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first is an isolated fact; the second is a full &lt;em&gt;map of thought&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;The Iceberg Behind Fermat’s Last Theorem&lt;/h3&gt;
&lt;p&gt;For a deeper sense of the gap, consider Andrew Wiles’s 1995 paper proving Fermat’s Last Theorem, a hundred‑plus‑page masterpiece—perfect &lt;em&gt;result data&lt;/em&gt;. Beneath it, however, lay eight secluded years of work and a mountain of draft notes recording failed paths, strategy shifts, and iterative corrections.&lt;/p&gt;
&lt;p&gt;Those notes are priceless &lt;em&gt;process data&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;An AI trained only on the published paper admires a grand edifice; one that also studies the notes might learn how to become an architect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here lies the root of AI’s missing “sense of smell.”&lt;/strong&gt; Human experts cultivate intuition by internalizing vast amounts of process data over years. They know not only what is right but also what is wrong. Our AIs ingest nearly all of the final papers but almost none of the eight‑year struggle, so naturally they miss this precious instinct.&lt;/p&gt;
&lt;h3&gt;The “Scarcity Trap” of Process Data&lt;/h3&gt;
&lt;p&gt;High‑quality process data is scarce for three main reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prohibitive annotation cost:&lt;/strong&gt; Domain experts must painstakingly document and vet every step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publication norms:&lt;/strong&gt; Academia and industry showcase polished results, hiding the valuable middle and the failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A staggering scale gap:&lt;/strong&gt; LLM pre‑training needs trillions of tokens, yet public process‑label data sets contain mere millions[^2], a difference of six orders of magnitude.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/ai-smell-test-from-outcome-to-process-supervision-teaching-machines-to-think/result-data-vs-process-data.png&quot; alt=&quot;The scarcity of process data&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Together these factors make process data extremely scarce, a key bottleneck blocking AI from leaping from &lt;em&gt;answer machine&lt;/em&gt; to &lt;em&gt;thinker&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;The Bottleneck Revealed: A Master of Form, a Mimic in Essence&lt;/h2&gt;
&lt;p&gt;Raised on result data, AI has become a strange acquaintance: extraordinarily capable yet puzzlingly brittle. It has perfected the “grammar” and &lt;em&gt;form&lt;/em&gt; of text—mathematical proofs, legal briefs, code.&lt;/p&gt;
&lt;p&gt;Hence it aces the eye test. But perfect form masks hollow content. Without process training, AI misses three essentials on the road to true intelligence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Genuine reasoning:&lt;/strong&gt; Models interpolate between seen data but struggle to extrapolate to novel problems because they learn answer templates, not universal problem‑decomposition strategies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reliability and robustness:&lt;/strong&gt; Rewarding only the outcome encourages shortcut learning based on superficial correlations. The model becomes fragile—rephrase the question and it may collapse—fueling hallucinations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explainability and trust:&lt;/strong&gt; An AI that only outputs answers is a black box. We cannot trace its logic, diagnose its errors, or fully trust it in high‑risk fields such as medicine or autonomous driving.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fortunately, where we diagnose a problem we often glimpse a solution. A cadre of top researchers has launched an exploration called &lt;strong&gt;process supervision&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Breaking the Deadlock: Three Waves of Process Supervision&lt;/h2&gt;
&lt;p&gt;The core idea is simple: &lt;strong&gt;instead of rewarding only the final correct answer, reward every correct step along the way.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Like a patient math teacher who praises each substitution and simplification, this revolution has unfolded in three dramatic stages.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/ai-smell-test-from-outcome-to-process-supervision-teaching-machines-to-think/three-stages.png&quot; alt=&quot;The three waves of process supervision&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Stage 1 Theoretical Foundations and the Costly “Gold Standard” (2023)&lt;/h3&gt;
&lt;p&gt;The overture came from DeepMind and OpenAI. In late 2022 DeepMind’s &lt;a href=&quot;https://arxiv.org/abs/2211.14275&quot;&gt;paper&lt;/a&gt; systematically distinguished and validated process supervision. OpenAI’s 2023 landmark &lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2305.20050&quot;&gt;Let’s Verify Step by Step&lt;/a&gt;&lt;/em&gt; hired experts to annotate every step of math solutions, producing the &lt;strong&gt;PRM800K&lt;/strong&gt; data set of about 800 000 step‑level labels.&lt;/p&gt;
&lt;p&gt;Models trained on PRM800K pushed accuracy on the MATH benchmark from roughly 72 %[^3] to &lt;strong&gt;about 78 %&lt;/strong&gt;—proof that process supervision works, yet its human cost is astronomical and hard to replicate.&lt;/p&gt;
&lt;h3&gt;Stage 2 Automation on the Offensive—A Race to Cut Costs (2024)&lt;/h3&gt;
&lt;p&gt;Success spawned the next question: &lt;strong&gt;how do we make it cheap?&lt;/strong&gt; In 2024 the spotlight shifted to automated cost reduction.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solving problems like playing chess:&lt;/strong&gt; &lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2312.08935&quot;&gt;Math‑Shepherd&lt;/a&gt;&lt;/em&gt; introduced Monte Carlo Tree Search (MCTS), letting models self‑play and explore, automatically generating hundreds of thousands of valid process steps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinpointing errors:&lt;/strong&gt; Researchers applied bisection to locate the first incorrect step with high efficiency, slashing annotation workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finer alignment:&lt;/strong&gt; &lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2406.18629&quot;&gt;Step‑DPO&lt;/a&gt;&lt;/em&gt; turned “rating a step” into “choosing the better of two steps,” yielding more stable alignment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A new benchmark, &lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2412.06559&quot;&gt;ProcessBench&lt;/a&gt;&lt;/strong&gt;, emerged to evaluate a model’s ability to &lt;em&gt;locate errors&lt;/em&gt;. The virtuous cycle of model development and benchmarking propelled the field forward.&lt;/p&gt;
&lt;h3&gt;Stage 3 Paradigm Shift and Generative Intelligence (2025)&lt;/h3&gt;
&lt;p&gt;By 2025 we witnessed a profound shift: &lt;strong&gt;can AI itself &lt;em&gt;create&lt;/em&gt; process data?&lt;/strong&gt; The focus moved from “data‑driven algorithms” to “algorithm‑driven data.”&lt;/p&gt;
&lt;p&gt;The flagship is &lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2504.16828&quot;&gt;ThinkPRM&lt;/a&gt;&lt;/strong&gt;. Using barely 1 % of PRM800K—about 8000 “gold‑seed” annotations[^4]—researchers taught the model to produce detailed, verifiable chains of thought. Once mastered, the model could self‑validate and score the vast additional steps it generated.&lt;/p&gt;
&lt;p&gt;The results were stunning: ThinkPRM, trained on 8000 human‑labeled steps, &lt;strong&gt;outperformed&lt;/strong&gt; first‑generation models that used 800 000. Process supervision entered the &lt;em&gt;generative&lt;/em&gt; era, reducing dependence on human data by two orders of magnitude.&lt;/p&gt;
&lt;p&gt;Pure automation also reached new heights. &lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2406.06592&quot;&gt;OmegaPRM&lt;/a&gt;&lt;/strong&gt; built a fully automated pipeline that produced 1.5 million step labels. Researchers extended process supervision beyond math and code: &lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2502.02095&quot;&gt;LongDPO&lt;/a&gt;&lt;/em&gt; applied it to creativity‑heavy long‑form writing.&lt;/p&gt;
&lt;h2&gt;New Challenges on the Horizon&lt;/h2&gt;
&lt;p&gt;Solving the process‑data shortage does not mark the end. Old problems fixed often breed new ones.&lt;/p&gt;
&lt;h3&gt;New Bottleneck 1 Compute Economics&lt;/h3&gt;
&lt;p&gt;We have swapped reliance on &lt;em&gt;human&lt;/em&gt; labor for &lt;em&gt;compute&lt;/em&gt;. MCTS search and ThinkPRM self‑verification are compute‑hungry “gold eaters.” The cost bottleneck shifts from labor to compute, still barring many players.&lt;/p&gt;
&lt;h3&gt;New Bottleneck 2 The Return of Explainability&lt;/h3&gt;
&lt;p&gt;One goal of process supervision was to open the black box. Yet as automation peaks, AI‑generated reasoning paths might again become “alien,” filled with shortcuts humans cannot fathom. Do we want efficient &lt;em&gt;alien intelligence&lt;/em&gt; or relatable &lt;em&gt;partner intelligence&lt;/em&gt;?&lt;/p&gt;
&lt;h3&gt;New Bottleneck 3 Lagging Evaluation and Safety&lt;/h3&gt;
&lt;p&gt;Success so far centers on domains like math and code with objective standards. In law, research, or business strategy—murkier arenas—we &lt;strong&gt;badly lack&lt;/strong&gt; standardized benchmarks. Moreover, applying process supervision while safeguarding proprietary knowledge is a must‑solve hurdle for industrial deployment.&lt;/p&gt;
&lt;h2&gt;Conclusion From Feeding Data to Building Ecosystems&lt;/h2&gt;
&lt;p&gt;Let us return to Terence Tao’s “smell test.”&lt;/p&gt;
&lt;p&gt;Surveying the recent journey, we can say the AI community has found the right road to cultivate “scent”: &lt;em&gt;process supervision&lt;/em&gt;. We have watched it evolve from costly proof‑of‑concept to efficient automation and now to disruptive generative self‑improvement.&lt;/p&gt;
&lt;p&gt;The deepest lesson is this: &lt;strong&gt;the future of AI will pivot from &lt;em&gt;feeding&lt;/em&gt; models to &lt;em&gt;empowering&lt;/em&gt; them.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Our role is shifting from data zookeepers to &lt;em&gt;ecosystem architects&lt;/em&gt;. Our task is to design initial rules and environments so that AI can explore, self‑correct, and grow—ultimately &lt;em&gt;producing&lt;/em&gt; the intelligence we need.&lt;/p&gt;
&lt;p&gt;We stand at a new dawn where AI might become not just a powerful tool but a genuine &lt;em&gt;thinking partner&lt;/em&gt;. The road remains long, but at least we can already smell the exhilarating scent of that future.&lt;/p&gt;
&lt;p&gt;[^1]: The terms “result data” and “process data” are informal labels for ease of discussion, not strict academic terminology.
[^2]: Google’s Gemma paper notes “We trained Gemma models on up to 6 T tokens of text,” indicating trillion‑token corpora for mainstream LLMs, whereas OpenAI’s PRM800K contains only 800 000 step labels—sub‑million scale.
[^3]: On a representative MATH subset with best‑of‑1860 search, accuracy rose from 72.4 % to 78.2 %.
[^4]: The authors state they used ≈1 % of PRM800K for manual labeling; 1 % of 800 000 is roughly 8000.
[^5]: This is my literary interpretation of Terence Tao’s original statement: “So the sense of smell, this is one thing that humans have, and there’s a metaphorical mathematical smell that it’s not clear how to get the AI to duplicate that eventually. So the way AlphaZero and so forth make progress on Go and chess and so forth, is in some sense they have developed a sense of smell for Go and chess positions, that this position is good for white, it’s good for black. They can’t initiate why, but just having that sense of smell lets them strategize. So if AIs gain that ability to a sense of viability of certain proof strategies, because I’m going to try to break up this problem into two small subtasks and they can say, “Oh, this looks good. The two tasks look like they’re simpler tasks than your main task and they’ve still got a good chance of being true. So this is good to try.” Or “No, you’ve made the problem worse, because each of the two subproblems is actually harder than your original problem,” which is actually what normally happens if you try a random thing to try normally it’s very easy to transform a problem into an even harder problem. Very rarely do you transform into a simpler problem. So if they can pick up a sense of smell, then they could maybe start competing with a human level of mathematicians.”&lt;/p&gt;
</content>
    <category term="AI"/>
    <category term="LLM"/>
    <category term="Machine Learning"/>
    <category term="Deep Learning"/>
    <category term="Process Supervision"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/no-pictures-in-the-mind-a-belated-recognition-of-my-aphantasia/</id>
    <title>No Pictures in the Mind — A Belated Recognition of My Aphantasia</title>
    <updated>2025-05-16T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/no-pictures-in-the-mind-a-belated-recognition-of-my-aphantasia/" rel="alternate"/>
    <published>2025-05-11T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;The term &lt;em&gt;aphantasia&lt;/em&gt; is hardly new to me. Several years ago it enjoyed a brief vogue on social media, where the question of whether the mind can display images became a minor sensation. In the flurry of posts and personal tests, many wondered aloud whether they, too, belonged to the imageless sort. I encountered the discussion then, yet never granted it serious attention.&lt;/p&gt;
&lt;p&gt;Part of my distance arose from a habitual caution: I dislike attaching labels to myself on the strength of online chatter. To &lt;em&gt;self-diagnose&lt;/em&gt; without solid medical knowledge strikes me as unwise—especially when a label may serve as excuse for behaviour and the truly afflicted risk collateral suspicion. Depression provides an example: many who sorely need help (I have had such moments myself) hesitate to speak, fearing disbelief. &lt;em&gt;Aphantasia&lt;/em&gt;, I told myself, was likely another modest internet fad, a convenient banner for those disappointed in their own imaginative reach.&lt;/p&gt;
&lt;p&gt;A second reason, I admit, was pride. I have long considered imagination and memory two pillars of my intellect. &lt;em&gt;Blindness of the mind&lt;/em&gt;—the phrase reeks of deficiency; how could it pertain to me? I prided myself on rendering scenes vividly, whether poetic vistas or narrative tableaux; I could enter them at once and dwell there. Only now do I understand that fluent description and the presence of an inner image are not the same.&lt;/p&gt;
&lt;p&gt;My memory, too, has been a quiet boast. Distant events and immediate facts alike tend to remain clear. I jest at my occasional lapses—say, forgetting for days to charge the mouse, so that the evening’s first act is a brief emergency recharge—yet these foibles never shook my core confidence. I still recall, at three years old, dropping a beloved die-cast helicopter: how my grandfather soldered its broken rotor, the rising fumes, the metallic tang. In secondary school I could recite long textbook passages and knew precisely where they lay upon the page—left or right leaf, upper or lower quadrant. That vaunted &lt;em&gt;photographic memory&lt;/em&gt;, I now see, was exact record of words and spatial layout, not genuine visual replay. Even in recent research work I summon technical minutiae or past conversations with such precision that collaborators occasionally grow uneasy. All this made me reluctant to associate myself with &lt;em&gt;aphantasia&lt;/em&gt;; between us there seemed a veil too dense to pierce.&lt;/p&gt;
&lt;p&gt;Still, the world has a talent for surprise. Some days ago I was roaming the vast corridors of Wikipedia, link leaping from topic to topic, when the familiar yet distant word &lt;em&gt;Aphantasia&lt;/em&gt; came once more into view. An inexplicable intuition bade me pause and read.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure: The first apple appears bright and lifelike; the second through fourth grow progressively simpler and vaguer, while the last—representing complete aphantasia—shows no image at all.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The deeper I read, the tighter my brow knit, especially upon seeing the now-famous &lt;em&gt;apple test&lt;/em&gt;, blithely ignored by my earlier self. A cold thought drenched me: &lt;em&gt;Wait—when people say they “imagine,” do they mean they actually &lt;strong&gt;see&lt;/strong&gt; something inside their heads?&lt;/em&gt; In that instant my long-settled notion of &lt;em&gt;imagination&lt;/em&gt; overturned.&lt;/p&gt;
&lt;p&gt;What I had always termed &lt;em&gt;imagination&lt;/em&gt; was the marshalling of attributes, features, and circumstances. Take an apple: I can &lt;em&gt;construct&lt;/em&gt; an inventory of its traits—the curve or flatness, the little hollow by the stem, the skin’s polish or faint roughness, the muted gloss of its natural wax under light, the cool smoothness to the touch, the scent upon approach, the flavour upon biting. All these I can articulate at leisure. But none of this is &lt;em&gt;seeing&lt;/em&gt;. Close my eyes, and what greets me is the customary darkness, perhaps tinged rose by stray light behind the lids. The field remains a passive canvas, never a picture self-painted. My &lt;em&gt;imagination&lt;/em&gt; is, at core, a highly developed system of verbal organisation and logical inference, not an internal cinematograph.&lt;/p&gt;
&lt;p&gt;To test the sudden suspicion I sought further testimony. The nineteenth-century polymath Sir Francis Galton startled me anew. He wrote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To my astonishment, I found that the great majority of the men of science to whom I first applied, protested that mental imagery was unknown to them … They had no more notion of its true nature than a colour-blind man … has of the nature of colour.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These lines echoed across the decades to my present bewilderment. For the first time I felt, bodily, how arduous it is to comprehend a sense one does not possess—precisely as a congenital achromat, hearing endless talk of &lt;em&gt;green&lt;/em&gt;, can grasp nothing of its essence. I even wondered: if one &lt;em&gt;sees&lt;/em&gt; what is not before the eyes, is that not a hallucination?&lt;/p&gt;
&lt;p&gt;I asked a friend what &lt;em&gt;imagination&lt;/em&gt; felt like. He said it was &lt;em&gt;almost like seeing, yet one knows the eyes are not involved&lt;/em&gt;. Rather than clarify, his reply deepened the fog, as though I were groping in a mist.&lt;/p&gt;
&lt;p&gt;So I tried experiments of my own. &lt;em&gt;See&lt;/em&gt; the sun: I can describe in detail molten sunsets and opalescent clouds, city skylines under cobalt dusk, or, at noon, streets shimmering in heat, even the furious fusion within the solar core. Lend me a poet’s line—&lt;em&gt;“a lonely plume of desert smoke, a long river swallowing the round sun”&lt;/em&gt;—and I conjure the austere boundlessness of a desert plain. Yet however exhaustive my rhetoric, the mental backdrop stays blank. Language labours; the visual channel is still.&lt;/p&gt;
&lt;p&gt;Then a simpler task: reduce the sun to a unit circle. I stared at a black dot on the screen, then shut my eyes and tried to retain it. Nothing. Even sliding my eyes in an imagined tracing of the circle produced no picture.&lt;/p&gt;
&lt;p&gt;By now I had to concede: if ordinary &lt;em&gt;imagination&lt;/em&gt; entails such inner vision, I cannot do it. I cannot &lt;em&gt;see&lt;/em&gt; the shape of things within the mind. This, it seems, is what &lt;em&gt;imagination&lt;/em&gt; has long meant.&lt;/p&gt;
&lt;p&gt;Systematic study of aphantasia, I learned, began in earnest only around 2015. Instruments such as the Vividness of Visual Imagery Questionnaire rely heavily on self-report, which poses a quandary: how is one to gauge &lt;em&gt;vividness&lt;/em&gt; who has never experienced a vivid image? It is like asking a colour-blind person to rate the brightness of red. Accordingly, I treat such findings with caution.&lt;/p&gt;
&lt;p&gt;Neuro-imaging, for its part, shows that when an aphantasic attempts to imagine, the visual cortex nonetheless activates. The signals, however, never translate into conscious pictures—sensible enough, else our ordinary sight and spatial reasoning could scarcely function.&lt;/p&gt;
&lt;p&gt;This discovery altered my conception of memory as well. I see now my recall is largely a narrative reconstruction based on fact, logic, and semantics, not the &lt;em&gt;scenic replay&lt;/em&gt; many enjoy. I was astonished to learn that some truly &lt;em&gt;relive&lt;/em&gt; an experience, re-seeing it. I can place a textbook paragraph on its exact page quadrant, yet cannot &lt;em&gt;see&lt;/em&gt; the open book—the hue of the paper, the shape of the type.&lt;/p&gt;
&lt;p&gt;To recognise a congenital absence, long overlooked, is disconcerting, especially when it unsettles one’s entire self-portrait. A fleeting wish even crossed my mind: that I had never revisited that article, that I might continue like Truman Burbank within his constructed dome—unenlightened, yet untroubled.&lt;/p&gt;
&lt;p&gt;But life moves on. Thankfully, this &lt;em&gt;mind-blindness&lt;/em&gt; has imposed no practical hindrance upon study, work, or living. I still apprehend the world, grasp intricate reasoning, and conduct orderly thought. In that sense it is a hidden blessing. This late self-knowledge, rather than a lamentable lack, feels more like a quiet reconciliation with the peculiar contours of my cognition. It reminds me that the landscapes of the mind differ as widely as those of the earth.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This piece recounts personal experience only and does not constitute medical advice. If you share similar concerns, please consult a qualified healthcare professional.&lt;/em&gt;&lt;/p&gt;
</content>
    <category term="Aphantasia"/>
    <category term="Cognitive Science"/>
    <category term="Memory"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/ai-and-intellectual-levelling-reflections-sparked-by-a-foray-into-russian-letters/</id>
    <title>AI and Intellectual Levelling — Reflections Sparked by a Foray into Russian Letters</title>
    <updated>2025-05-09T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/ai-and-intellectual-levelling-reflections-sparked-by-a-foray-into-russian-letters/" rel="alternate"/>
    <published>2025-05-09T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;Of late, one often hears that AI will launch a grand revolution of “intellectual levelling.”
I could never quite declare myself opposed to the idea, but I confess I viewed it with a private lip curl, grounded in that confidence we all nurture in our own powers. When AI first came striding onto the stage—some even proclaiming it would replace the traditional search engine—I toyed with it now and then for novelty’s sake, yet in daily life, I still relied on the trusty techniques that had accompanied me for years.&lt;/p&gt;
&lt;p&gt;I fancied myself a connoisseur of those “minor artifices” of classical search—exact quotes, wild-cards, Boolean spells of &lt;strong&gt;AND&lt;/strong&gt;, &lt;strong&gt;OR&lt;/strong&gt;, &lt;strong&gt;NOT&lt;/strong&gt;; time filters, &lt;em&gt;filetype:&lt;/em&gt; incantations, &lt;strong&gt;site:&lt;/strong&gt; restrictions. These, to me, were second nature. More crucial still was the nose for how information is apt to present itself in prose: the knack of staging a multi-round duel of keywords, ever closing in on the quarry, never counting on a single, flawless shot.&lt;/p&gt;
&lt;p&gt;Yet life delights in overturning us. Not long ago, I found myself reading Russian literature and trying to unearth certain Russian-language primary sources and critical essays. In that chase, I discovered that my cherished search craft, upon which I had long preened, could be wiped away with one stroke: I was back on the nursery slopes of the Internet—perhaps worse off, for pride is a heavy handicap.&lt;/p&gt;
&lt;p&gt;First barrier: language. Even after hustling my query through machine translation into Russian, I trembled: were those phrases truly what Russian scholars would write? A semantic horseshoe is not enough; a misplaced nail may send the whole search cart into a ditch.&lt;/p&gt;
&lt;p&gt;Next ambush: Russian names. They are renowned for their length and, shall we say, exuberant variety. Take the humble &lt;strong&gt;Алексей&lt;/strong&gt;. Its Latin garb multiplies like Hydra heads: &lt;strong&gt;Alexey&lt;/strong&gt;, &lt;strong&gt;Aleksey&lt;/strong&gt;, &lt;strong&gt;Alexei&lt;/strong&gt;—all common enough. The Russian passport, under GOST-R 2016, prefers &lt;strong&gt;Aleksei&lt;/strong&gt;; the ALA-LC librarians of North America write &lt;strong&gt;Alekseĭ&lt;/strong&gt;; ISO 9:1995 counsels &lt;strong&gt;Aleksej&lt;/strong&gt;. Each claims the mantle of &lt;em&gt;standard&lt;/em&gt;—only the standards differ. To net all scholarship on a single novel’s Alexei, one must chant this litany of spellings like some hermetic rite. And this without yet summoning the diminutives &lt;strong&gt;Алёша&lt;/strong&gt; and &lt;strong&gt;Лёша&lt;/strong&gt;, which roam freely through colloquy and half-serious critique.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- markdownlint-disable MD033 --&amp;gt;
&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Latin transliterations of Алексей&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Latinised Form&lt;/th&gt;
&lt;th&gt;System / Milieu&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexei&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;contemporary English-language media&lt;/td&gt;
&lt;td&gt;among the most common in English&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexey&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;English/Russian news copy&lt;/td&gt;
&lt;td&gt;owes something to old Russian passports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksei&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Russian Federation passport (GOST-R 2016), BGN/PCGN&lt;/td&gt;
&lt;td&gt;renders «кс» as &lt;em&gt;ks&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksey&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ditto&lt;/td&gt;
&lt;td&gt;a secondary &lt;strong&gt;-ey&lt;/strong&gt; ending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexej&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;German, Czech, other Central-European tongues; ISO 9&lt;/td&gt;
&lt;td&gt;«й» → &lt;em&gt;j&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksej&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ISO 9 : 1995, Slovene, etc.&lt;/td&gt;
&lt;td&gt;fully reversible scholarly scheme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksiej&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Polish localisation&lt;/td&gt;
&lt;td&gt;«кс» → &lt;em&gt;ks&lt;/em&gt;, «й» → &lt;em&gt;j&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;fixed Polish masculine name&lt;/td&gt;
&lt;td&gt;often an independent given name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alekseĭ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ALA-LC, academic literature&lt;/td&gt;
&lt;td&gt;&lt;em&gt;ĭ&lt;/em&gt; marks the Russian /j/ glide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexeï&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;traditional French transliteration&lt;/td&gt;
&lt;td&gt;&lt;em&gt;ï&lt;/em&gt; signals /ej/&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alekseï&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;rarer French form&lt;/td&gt;
&lt;td&gt;retains &lt;em&gt;k&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexius&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;medieval Latin&lt;/td&gt;
&lt;td&gt;Latinate antique&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;English/French borrowing&lt;/td&gt;
&lt;td&gt;widespread in Western sources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexiy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;early English, Eastern-Orthodox texts&lt;/td&gt;
&lt;td&gt;ends in &lt;strong&gt;-iy&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aleksiy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;same, but with &lt;em&gt;k&lt;/em&gt; preserved&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexii / Alekssii / Alekseii&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;old double-&lt;em&gt;i&lt;/em&gt; variants&lt;/td&gt;
&lt;td&gt;rare historical spellings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alexy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;truncates the second &lt;em&gt;e&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;scant colloquial usage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;Search in English? The web brims with English pages—but the snares remain: names alone devour half the day. Chinese search? Those who know the Chinese Internet harbour scant hope. Sources abound yet lie marooned on island domains—WeChat essays, private blogs—beyond the reach of ordinary engines. The Chinese web is an archipelago of “information wastelands,” sunk in darkness.&lt;/p&gt;
&lt;p&gt;Besieged on every side, I watched my proud skills shrivel; the yield of useful pages was dishearteningly low.&lt;/p&gt;
&lt;p&gt;At the point of surrender, AI rose again in my thoughts. I recalled that ChatGPT o3, by default, likes to invoke web search and often returns answers woven from many threads. Deep Research casts its net wider still, delivering neat dossiers of dozens of sources.&lt;/p&gt;
&lt;p&gt;With nothing to lose, I laid my confusion before it. The result surprised me. AI shouldered the burdens of language and keywords, handed back links of quite decent vintage—many of them the very fruit I had been unable to pluck. Add in translation, and the once-cryptic Russian texts grew almost plain.&lt;/p&gt;
&lt;p&gt;In that moment I grasped, vividly, what “lowering the bar to knowledge” might mean. Where personal skill in a field (for me: deep Russian-language retrieval) collapses to near-zero, AI is not a mere aid; it becomes a kind of empowerment. I no longer had to wrestle with each transliteration and keyword; AI bore that weight.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Indeed, on reflection, I have long since smuggled AI into my routine. When code misbehaves, I used to scour Stack Overflow, unearthing decade-old posts where some greybeard grumbled that the bug had never been fixed—those ghostly dialogues across time, both charming and bleak. Now I consult AI first. In such cases, I chiefly want to &lt;strong&gt;vet&lt;/strong&gt; AI’s reasoning, not explore terra incognita. AI plays the seasoned lieutenant.&lt;/p&gt;
&lt;p&gt;My literary episode, however, lifts my regard for AI search to another rung. In realms of extreme information asymmetry—where tongue, expertise, or training bar the way—AI serves less as assistant than as &lt;strong&gt;enabler&lt;/strong&gt;. Tasks that once demanded a long apprenticeship now lie within casual reach.&lt;/p&gt;
&lt;p&gt;A not-entirely-apt analogy: manual deep search in an alien domain can resemble an “NP problem”—finding the solution itself is hard. AI, by brute pattern and memory, nudges the experience closer to “P”—the user’s labour shifts to verifying the plausibility of AI’s &lt;strong&gt;candidate&lt;/strong&gt; answers.&lt;/p&gt;
&lt;p&gt;Thus, the phrase &lt;em&gt;intellectual levelling&lt;/em&gt; acquires a new savour. AI’s significance does not reside in bestowing equal raw intellect, but in acting as a learned and patient guide who, when we confront unfamiliar knowledge, linguistic walls, or lack of training, lowers the threshold dramatically.&lt;/p&gt;
&lt;p&gt;It irons out gulfs created by circumstance, allowing more of us to stand on something like equal footing as we reach for what once looked remote. My attitude, formerly tinged with disdain, has warmed into a measure of hope. The future remains opaque, yet on the road to understanding, I have gained an unexpected companion.&lt;/p&gt;
</content>
    <category term="AI"/>
    <category term="Search Engine"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/implementing-interactive-vega-lite-charts-on-my-blog-rooted-in-the-grammar-of-graphics/</id>
    <title>Implementing Interactive Vega-Lite Charts on My Blog, Rooted in the Grammar of Graphics</title>
    <updated>2025-04-23T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/implementing-interactive-vega-lite-charts-on-my-blog-rooted-in-the-grammar-of-graphics/" rel="alternate"/>
    <published>2025-04-23T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;To better communicate findings from my machine learning projects on this blog, I needed a way to go beyond static plots. Static images show results, but often hide underlying patterns. I wanted readers to &lt;em&gt;explore&lt;/em&gt; the data themselves. Therefore, I implemented a system for embedding &lt;strong&gt;interactive data visualizations&lt;/strong&gt; using &lt;strong&gt;Vega-Lite&lt;/strong&gt;, rendered via a custom component built for my Astro-based blog.&lt;/p&gt;
&lt;p&gt;This approach allows for interactions like tooltips, zooming, and linked selections directly within articles. The choice of Vega-Lite wasn&apos;t arbitrary; it stems from the powerful &lt;strong&gt;Grammar of Graphics (GoG)&lt;/strong&gt; framework, a principled approach to visualization design that also underpins influential tools like &lt;code&gt;ggplot2&lt;/code&gt;. This post outlines how I built this feature, why the GoG foundation is critical, and the benefits for technical communication.&lt;/p&gt;
&lt;p&gt;Here is a chart that displays the same demonstration as the example on the official Vega-Lite website, while also supporting dark mode.&lt;/p&gt;
&lt;p&gt;import Chart from &quot;@/components/islands/common/Chart&quot;;&lt;/p&gt;
&lt;p&gt;&amp;lt;Chart
spec=&quot;/charts/spec/demo.json&quot;
height={400}
client:visible
ariaLabel=&quot;Demo chart&quot;
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;The Need: Interactive, Performant Visualizations&lt;/h2&gt;
&lt;p&gt;My goal was clear: embed charts that were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Declarative:&lt;/strong&gt; Defined using Vega-Lite&apos;s JSON specifications based on data mappings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performant:&lt;/strong&gt; Loaded efficiently without slowing down page loads, using lazy-loading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive:&lt;/strong&gt; Adapted automatically to different screen sizes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme-Aware:&lt;/strong&gt; Matched the blog&apos;s light/dark mode seamlessly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integrated:&lt;/strong&gt; Easily embedded within my Markdown (MDX) content.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Achieving this required both a smart frontend component and leveraging a robust visualization library built on solid principles.&lt;/p&gt;
&lt;h2&gt;Why Vega-Lite? The Power of the Grammar of Graphics&lt;/h2&gt;
&lt;p&gt;Vega-Lite&apos;s strength comes from its foundation in the &lt;strong&gt;Grammar of Graphics (GoG)&lt;/strong&gt;, a formal system for constructing statistical graphics first detailed by &lt;strong&gt;Leland Wilkinson&lt;/strong&gt;. GoG decomposes charts into fundamental components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt; The information being visualized.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aesthetics (Mappings):&lt;/strong&gt; How data variables map to visual properties (position, color, size, shape).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geometric Objects (Geoms):&lt;/strong&gt; The visual marks used (points, lines, bars).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scales:&lt;/strong&gt; Functions translating data values to aesthetic values (pixels, colors).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statistics (Stats):&lt;/strong&gt; Data transformations applied before mapping (binning, smoothing).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coordinates:&lt;/strong&gt; The space where data is plotted (Cartesian, polar).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faceting:&lt;/strong&gt; Creating subplots based on data subsets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This modular approach offers immense &lt;strong&gt;expressiveness&lt;/strong&gt; and &lt;strong&gt;clarity&lt;/strong&gt;. It was popularized by &lt;strong&gt;Hadley Wickham&apos;s &lt;code&gt;ggplot2&lt;/code&gt;&lt;/strong&gt; package for R, which demonstrated GoG&apos;s practical power and became a dominant force in statistical graphics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vega-Lite&lt;/strong&gt; adapts these GoG principles specifically for &lt;strong&gt;interactive web visualizations&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses a &lt;strong&gt;declarative JSON syntax&lt;/strong&gt; reflecting GoG components (&lt;code&gt;data&lt;/code&gt;, &lt;code&gt;mark&lt;/code&gt;, &lt;code&gt;encoding&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It natively supports &lt;strong&gt;rich interactivity&lt;/strong&gt; (tooltips, zooming, panning, linked selections/brushing) crucial for data exploration.&lt;/li&gt;
&lt;li&gt;It compiles to &lt;strong&gt;Vega&lt;/strong&gt;, a lower-level grammar, offering both ease of use and deep customization.&lt;/li&gt;
&lt;li&gt;Its JSON format integrates seamlessly with &lt;strong&gt;web technologies&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For my workflow, the synergy with &lt;strong&gt;Python&apos;s &lt;code&gt;Altair&lt;/code&gt; library&lt;/strong&gt; is key. I can define plots in Python using GoG concepts, export the Vega-Lite JSON, and reuse that &lt;em&gt;exact&lt;/em&gt; specification on the blog, ensuring perfect consistency between analysis and presentation.&lt;/p&gt;
&lt;h2&gt;Implementation: A Custom &lt;code&gt;&amp;lt;Chart&amp;gt;&lt;/code&gt; Component&lt;/h2&gt;
&lt;p&gt;I built a reusable Preact component (&lt;code&gt;Chart.tsx&lt;/code&gt;) within my Astro project to handle rendering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Core Rendering:&lt;/strong&gt; Uses the &lt;code&gt;useEffect&lt;/code&gt; hook to dynamically import &lt;code&gt;vegaEmbed&lt;/code&gt; and render the chart spec into a container element ref.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lazy Loading (&lt;code&gt;client:visible&lt;/code&gt;):&lt;/strong&gt; Astro&apos;s &lt;code&gt;client:visible&lt;/code&gt; directive ensures the &lt;code&gt;vega-embed&lt;/code&gt; library (which can be sizable) and rendering logic only load when the chart scrolls into view, improving initial page performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsiveness (&lt;code&gt;ResizeObserver&lt;/code&gt;):&lt;/strong&gt; While Vega-Lite&apos;s &lt;code&gt;width: &quot;container&quot;&lt;/code&gt; helps, &lt;code&gt;ResizeObserver&lt;/code&gt; actively monitors the container element&apos;s size. If the layout changes &lt;em&gt;after&lt;/em&gt; the initial render (e.g., sidebar toggle), it triggers the Vega view&apos;s &lt;code&gt;resize()&lt;/code&gt; method, ensuring the chart adapts correctly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theming:&lt;/strong&gt; Listens for a site-wide theme change event (or checks &lt;code&gt;document.documentElement&lt;/code&gt; class) and updates the chart&apos;s theme using &lt;code&gt;vegaEmbed&lt;/code&gt; options to support dark mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibility:&lt;/strong&gt; Accepts an &lt;code&gt;ariaLabel&lt;/code&gt; prop passed to the chart for screen reader users. Vega-Embed itself aids accessibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;direction: down
style: {
  fill: transparent
}

analysis: &quot;Analysis (Python/Altair)&quot; {
  shape: step
}
vlJson: &quot;Vega-Lite JSON&quot; {
  shape: document
}
chartComp: &quot;&amp;lt;Chart&amp;gt; Component\n(Astro/Preact + client:visible)&quot; {
  shape: package
}
vegaEmbed: &quot;Lazy-loaded\nvega-embed&quot; {
  shape: hexagon
  style: {
    stroke-dash: 3
  }
}
features: &quot;Uses:\n- ResizeObserver\n- Theming&quot; {
  shape: cloud
}
svgChart: &quot;Accessible &amp;amp;\nInteractive SVG Chart&quot; {
  shape: page
  style: {
    stroke: &quot;#4CAF50&quot;
    stroke-width: 2
  }
}

analysis -&amp;gt; vlJson
vlJson -&amp;gt; chartComp
chartComp -&amp;gt; vegaEmbed: &quot;Renders via&quot;
features -&amp;gt; vegaEmbed: &quot;Acts on&quot;
vegaEmbed -&amp;gt; svgChart: &quot;Renders&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Embedding Charts in Content&lt;/h2&gt;
&lt;p&gt;Using the component in my MDX posts is straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Chart from &quot;@/components/islands/common/Chart&quot;;

### Exploring Model Hyperparameters

The interactive plot below shows validation accuracy across different parameters. Hover for details.

&amp;lt;Chart
  spec=&quot;/charts/spec/hyperparameter-scan.json&quot; // Path to the Vega-Lite JSON
  height={400}
  client:visible // Enable lazy-loading
  ariaLabel=&quot;Interactive scatter plot of model accuracy vs hyperparameters&quot;
/&amp;gt;

We can observe that optimal performance occurs...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setup is ideal for visualizing various machine learning results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hyperparameter scans (scatter plots with tooltips).&lt;/li&gt;
&lt;li&gt;Model comparisons (bar charts).&lt;/li&gt;
&lt;li&gt;Feature importance plots.&lt;/li&gt;
&lt;li&gt;Probability distributions (histograms, density plots, potentially linked).&lt;/li&gt;
&lt;li&gt;Time-series forecasts (zoomable line charts).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion: Better Communication Through Principled Interaction&lt;/h2&gt;
&lt;p&gt;By implementing interactive Vega-Lite charts with a custom, performant component, I can now share data-driven insights from my machine learning work more effectively. This approach leverages the robust Grammar of Graphics principles, made practical by tools like ggplot2 and adapted for the interactive web by Vega-Lite. The streamlined workflow from Python analysis to blog post enables clearer communication and allows readers to engage directly with the data.&lt;/p&gt;
</content>
    <category term="Data Visualization"/>
    <category term="Vega-Lite"/>
    <category term="Grammar of Graphics"/>
    <category term="ggplot2"/>
    <category term="JavaScript"/>
    <category term="Web Development"/>
    <category term="Astro"/>
    <category term="Preact"/>
    <category term="Machine Learning"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/embedding-arxiv-papers-and-other-pdfs-easily-in-astro/</id>
    <title>Embedding arXiv Papers (and other PDFs) Easily in Astro</title>
    <updated>2025-04-19T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/embedding-arxiv-papers-and-other-pdfs-easily-in-astro/" rel="alternate"/>
    <published>2025-04-19T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;Displaying academic papers, especially from sources like arXiv, directly within a webpage can enhance the user experience on technical blogs. I wanted a straightforward way to embed these PDFs into my blog. &lt;strong&gt;My primary goal was to create a tool that would allow me to easily showcase papers when I write future posts discussing them – perhaps adding notes, detailing reproduction efforts, or providing analysis.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To achieve this, I developed a reusable Preact component that renders PDFs client-side using just the arXiv ID or a direct URL, ensuring seamless integration into my site&apos;s content flow.&lt;/p&gt;
&lt;h2&gt;The Goal: Simple, Integrated PDF Embedding&lt;/h2&gt;
&lt;p&gt;The main objective was to embed PDFs, particularly arXiv papers, as simply as possible within &lt;code&gt;.astro&lt;/code&gt; or &lt;code&gt;.mdx&lt;/code&gt; files. Crucially, I wanted a solution that felt native to the blog&apos;s design, rather than relying on a standard browser &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Why Not Just Use an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;While embedding a PDF using an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; pointing to the source URL is possible, it offers limited control over presentation and behavior. My goals required a more integrated approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Minimal Design:&lt;/strong&gt; I aimed for a viewer that fits aesthetically within my blog&apos;s theme, without the default browser UI chrome that often comes with &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; PDF viewers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customization:&lt;/strong&gt; Using &lt;code&gt;PDF.js&lt;/code&gt; directly allows for finer control over rendering, such as applying theme-specific styles (like the dark mode adjustments I implemented) or potentially adding custom overlays or annotations in the future.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoiding Nested Contexts:&lt;/strong&gt; Iframes create separate browsing contexts, which can sometimes complicate interactions or styling consistency.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By building a component around &lt;code&gt;PDF.js&lt;/code&gt;, I could achieve a lightweight, customizable viewer that feels like a natural part of the page.&lt;/p&gt;
&lt;h2&gt;The Solution: A Client-Side PDF Component&lt;/h2&gt;
&lt;p&gt;I built a Preact component (&lt;code&gt;PDFViewer.tsx&lt;/code&gt;) designed to run only in the browser. This is essential because PDF rendering libraries like &lt;code&gt;pdfjs-dist&lt;/code&gt; often rely on browser-specific APIs (like &lt;code&gt;DOMMatrix&lt;/code&gt;) unavailable during server-side rendering (SSR) in Astro. Using Astro&apos;s &lt;code&gt;client:only=&quot;preact&quot;&lt;/code&gt; directive ensures the component and its dependencies load only on the client side.&lt;/p&gt;
&lt;h3&gt;Usage Examples&lt;/h3&gt;
&lt;p&gt;Embedding a PDF is now straightforward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Using an arXiv ID:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To display an arXiv paper, I just need its ID (e.g., the ResNet paper ID &lt;code&gt;1512.03385&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PDFViewer from &quot;@/components/islands/common/PDFViewer.tsx&quot;;

// Renders the PDF for arXiv paper 1512.03385
&amp;lt;PDFViewer paperId=&quot;1512.03385&quot; client:only=&quot;preact&quot; /&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like this:&lt;/p&gt;
&lt;p&gt;import PDFViewer from &quot;~/components/islands/common/PDFViewer&quot;;&lt;/p&gt;
&lt;p&gt;&amp;lt;PDFViewer paperId=&quot;1512.03385&quot; client:only=&quot;preact&quot; /&amp;gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using a Direct URL:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The component also accepts a direct URL to any PDF file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PDFViewer from &quot;@/components/islands/common/PDFViewer.tsx&quot;;

// Renders the PDF from the specified URL
&amp;lt;PDFViewer
  url=&quot;https://your-bucket.s3.amazonaws.com/path/to/document.pdf&quot;
  client:only=&quot;preact&quot;
/&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Important Note on Direct URLs and CORS:&lt;/strong&gt; When using the url prop with PDFs hosted on services like AWS S3 or other domains, you might encounter Cross-Origin Resource Sharing (CORS) issues. The browser&apos;s security policy prevents JavaScript from fetching resources from a different origin unless that origin explicitly allows it via CORS headers. You must configure the hosting service (e.g., your S3 bucket) to send the appropriate Access-Control-Allow-Origin headers to permit your website&apos;s domain to fetch the PDF file.&lt;/p&gt;
&lt;h2&gt;How It Works&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;arXiv ID Handling:&lt;/strong&gt; When a paperId is provided, the component constructs a local path like /papers/1512.03385. A server redirect (using Netlify redirects in my case) maps this path to the actual &lt;code&gt;https://arxiv.org/pdf/:id.pdf&lt;/code&gt; URL. This fetches the paper directly from arXiv upon request, avoiding copyright issues and keeping component usage clean.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[[redirects]]
from   = &quot;/papers/:id.pdf&quot;
to     = &quot;https://arxiv.org/pdf/:id.pdf&quot;
status = 200
force  = true

[[redirects]]
from   = &quot;/papers/:id&quot;
to     = &quot;https://arxiv.org/pdf/:id.pdf&quot;
status = 200
force  = true
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;URL Handling:&lt;/strong&gt; If a url is provided, it&apos;s passed directly to PDF.js, subject to CORS rules mentioned above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-Side Rendering (&lt;code&gt;client:only=&quot;preact&quot;&lt;/code&gt;):&lt;/strong&gt; This Astro directive prevents server-side rendering. The pdfjs-dist library is dynamically imported within a useEffect hook in the Preact component, ensuring it only runs in the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rendering:&lt;/strong&gt; The component uses pdfjs-dist to load the PDF and render each page onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. It includes loading/error states and adapts its appearance based on the site&apos;s theme using CSS filters for dark mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;style: {
  fill: transparent
}

direction: down

UserBrowser: &quot;User (Browser)&quot;
AstroMDX: &quot;Astro/MDX Page&quot;
PDFViewerClient: &quot;PDFViewer Component (Client-Side)&quot; {
  shape: hexagon
}
NetlifyRedirects: &quot;Server Redirects (e.g., Netlify)&quot; {
  shape: cloud
}
arXivServer: &quot;arXiv Server&quot; {
  shape: cylinder
}
PDFjsLib: &quot;PDF.js Library&quot; {
  shape: hexagon
}
Canvas: &quot;Canvas Element&quot;

UserBrowser -&amp;gt; AstroMDX: &quot;1. Visits Page&quot;
AstroMDX -&amp;gt; PDFViewerClient: &quot;2. Includes Component\n(with arXiv ID)&quot;
PDFViewerClient -&amp;gt; UserBrowser: &quot;3. Constructs Path\n/papers/:id&quot;
UserBrowser -&amp;gt; NetlifyRedirects: &quot;4. Requests /papers/:id&quot;
NetlifyRedirects -&amp;gt; arXivServer: &quot;5. Redirects Request\nto arxiv.org/pdf/:id.pdf&quot;
arXivServer -&amp;gt; UserBrowser: &quot;6. Serves PDF&quot;
PDFViewerClient -&amp;gt; PDFjsLib: &quot;7. Loads PDF.js&quot;
PDFjsLib -&amp;gt; UserBrowser: &quot;8. Fetches PDF Data\n(via Browser)&quot;
PDFjsLib -&amp;gt; Canvas: &quot;9. Renders PDF&quot;
Canvas -&amp;gt; UserBrowser: &quot;10. Displays PDF&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;style: {
  fill: transparent
}

direction: down

UserBrowser: &quot;User (Browser)&quot;
AstroMDX: &quot;Astro/MDX Page&quot;
PDFViewerClient: &quot;PDFViewer Component (Client-Side)&quot; {
  shape: hexagon
}
ExternalHost: &quot;External PDF Host (e.g., S3)&quot; {
  shape: cylinder
}
CORSCheck: &quot;Browser CORS Check&quot; {
  shape: diamond
}
PDFjsLib: &quot;PDF.js Library&quot; {
  shape: hexagon
}
Canvas: &quot;Canvas Element&quot;
ErrorState: &quot;Error Displayed&quot;

UserBrowser -&amp;gt; AstroMDX: &quot;1. Visits Page&quot;
AstroMDX -&amp;gt; PDFViewerClient: &quot;2. Includes Component\n(with Direct URL)&quot;
PDFViewerClient -&amp;gt; PDFjsLib: &quot;3. Loads PDF.js&quot;
PDFjsLib -&amp;gt; ExternalHost: &quot;4. Requests PDF from URL&quot;
ExternalHost -&amp;gt; CORSCheck: &quot;5. Responds with PDF &amp;amp; CORS Headers&quot;
CORSCheck -&amp;gt; PDFjsLib: &quot;6a. CORS OK&quot; {
  style: {
    stroke: &quot;#008000&quot;
  }
}
CORSCheck -&amp;gt; ErrorState: &quot;6b. CORS Failed&quot; {
  style: {
    stroke: &quot;#FF0000&quot;
    stroke-dash: 4
  }
}
ErrorState -&amp;gt; UserBrowser: &quot;7b. Show Error&quot; {
  style: {
    stroke: &quot;#FF0000&quot;
    stroke-dash: 4
  }
}
PDFjsLib -&amp;gt; Canvas: &quot;7a. Renders PDF&quot; {
   style: {
    stroke: &quot;#008000&quot;
  }
}
Canvas -&amp;gt; UserBrowser: &quot;8a. Displays PDF&quot; {
   style: {
    stroke: &quot;#008000&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simplicity:&lt;/strong&gt; Embedding requires just a single component tag.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; Works for arXiv papers and other hosted PDFs (with proper CORS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration:&lt;/strong&gt; Provides a minimal viewer that matches the site&apos;s design better than an iframe.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance:&lt;/strong&gt; Defers heavy PDF rendering to the client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-Proof:&lt;/strong&gt; Enables richer interactions for future paper discussion posts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This custom Preact component provides a clean and efficient way we found to embed PDF documents directly into our Astro site. It avoids the limitations of iframes, offering better design integration and paving the way for future posts where we can easily reference and discuss academic papers directly within the content. Remember to handle CORS settings appropriately when linking to externally hosted PDFs via direct URLs.&lt;/p&gt;
</content>
    <category term="Astro"/>
    <category term="Preact"/>
    <category term="PDF.js"/>
    <category term="arXiv"/>
    <category term="JavaScript"/>
    <category term="Web Development"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/demonstrating-astro-hydration-with-a-simple-2048-game/</id>
    <title>Demonstrating Astro Hydration with a Simple 2048 Game</title>
    <updated>2025-03-24T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/demonstrating-astro-hydration-with-a-simple-2048-game/" rel="alternate"/>
    <published>2025-03-24T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;I recently built a simple version of the classic 2048 game using the Astro web framework. Beyond being a fun exercise, this project provides a clear demonstration of Astro&apos;s component hydration capabilities, specifically using the &lt;code&gt;client:visible&lt;/code&gt; directive.&lt;/p&gt;
&lt;p&gt;The core idea is to show how an interactive component, like this game, can be integrated into an otherwise static Astro page and brought to life in the browser.&lt;/p&gt;
&lt;h2&gt;What is Astro Hydration?&lt;/h2&gt;
&lt;p&gt;Astro is known for its &quot;Islands Architecture,&quot; where UI components are treated as isolated &quot;islands&quot; within a sea of static HTML. By default, Astro components render to HTML and ship zero client-side JavaScript.&lt;/p&gt;
&lt;p&gt;However, for components that require interactivity (like our 2048 game), Astro needs to &quot;hydrate&quot; them – loading and running their necessary JavaScript in the browser. Astro offers several &lt;code&gt;client:*&lt;/code&gt; directives to control &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; this hydration occurs.&lt;/p&gt;
&lt;h2&gt;Implementing the 2048 Game Component&lt;/h2&gt;
&lt;p&gt;For this 2048 game, I created an Astro component &lt;code&gt;Game2048.tsx&lt;/code&gt;. To make it interactive, I used the &lt;code&gt;client:visible&lt;/code&gt; hydration directive when including it on the page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Example usage in an Astro page --&amp;gt;
&amp;lt;Game2048 client:visible /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;client:visible&lt;/code&gt; directive tells Astro:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initially render the component as static HTML on the server.&lt;/li&gt;
&lt;li&gt;Wait until the component enters the user&apos;s browser viewport.&lt;/li&gt;
&lt;li&gt;Once visible, load the component&apos;s associated JavaScript and hydrate it, making it fully interactive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This strategy balances performance (deferring JS loading) with user experience (making the game interactive when the user sees it).&lt;/p&gt;
&lt;h2&gt;Play the Game&lt;/h2&gt;
&lt;p&gt;Here is the interactive 2048 game component, loaded using &lt;code&gt;client:visible&lt;/code&gt;. Try playing a round!&lt;/p&gt;
&lt;p&gt;import Game2048 from &quot;@/components/islands/specific/Game2048&quot;;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;flex justify-center items-center my-8&quot;&amp;gt;
&amp;lt;div class=&quot;rounded-xl shadow-lg dark:shadow-dark p-4 bg-[#faf8ef]/80 dark:bg-[#2a3549]/90 backdrop-blur-sm border border-transparent dark:border-[#3a4a66]&quot;&amp;gt;
&amp;lt;Game2048 client:visible /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;This example provides a practical look at client:visible. When I have more time, I plan to write a more comprehensive introductory post delving deeper into Astro&apos;s various hydration strategies and how to choose the right one for different use cases. For now, I hope this game serves as a helpful illustration.&lt;/p&gt;
</content>
    <category term="Astro"/>
    <category term="JavaScript"/>
    <category term="Web Development"/>
    <category term="Game"/>
    <category term="Hydration"/>
    <category term="Islands Architecture"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/black-myth-wukong-review-a-flawed-but-decent-aaa-arpg/</id>
    <title>Black Myth: Wukong Review - A Flawed But Decent AAA ARPG (7/10)</title>
    <updated>2024-08-22T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/black-myth-wukong-review-a-flawed-but-decent-aaa-arpg/" rel="alternate"/>
    <published>2024-08-22T00:00:00.000Z</published>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This review contains spoilers for Black Myth: Wukong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rating:&lt;/strong&gt; Based on my personal evaluation system, I rate Black Myth: Wukong &lt;strong&gt;7/10&lt;/strong&gt; after completing the first playthrough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ignoring external controversies, I found Black Myth: Wukong to be a decent AAA Action RPG, but one held back by notable flaws. While many have highlighted its strengths, this review focuses primarily on the areas where I felt the game fell short.&lt;/p&gt;
&lt;h2&gt;Severe Performance Issues&lt;/h2&gt;
&lt;p&gt;The most immediate problem I encountered was performance. Playing on a PC (Ryzen 7 7800X3D, RTX 3090), I experienced frequent frametime spikes (minor stutters) even in contained areas. These became more severe in sections requiring loading transitions disguised as cutscenes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shader Compilation:&lt;/strong&gt; The game requires a lengthy shader compilation process on startup, which frustratingly needs repeating after GPU driver updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimization:&lt;/strong&gt; Reports, like Digital Foundry&apos;s PS5 analysis, confirm widespread performance issues. While UE5 is still relatively new, the level of stuttering suggests inadequate optimization efforts by the development team. This needs significant improvement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mediocre Storyline and Weak Protagonist&lt;/h2&gt;
&lt;p&gt;While adaptations can deviate from source material if handled well, I found Black Myth&apos;s main plot mediocre. Characters often lacked clear motivations, and the central narrative—collecting Sun Wukong&apos;s remnants to become the new Great Sage—felt underdeveloped.&lt;/p&gt;
&lt;p&gt;It&apos;s puzzling how the detailed monster compendium and side stories show depth, while the main plot remains bland. Fragmented storytelling shouldn&apos;t come at the cost of a weak core narrative.&lt;/p&gt;
&lt;p&gt;The protagonist, &quot;The Destined One,&quot; suffered from particularly weak characterization. By the game&apos;s end, I knew very little about them. While this might aim for player self-insertion, I dislike protagonists who feel like mere tools to advance a dull plot. Ironically, the companion character Zhu Bajie felt more vividly portrayed.&lt;/p&gt;
&lt;h2&gt;Flawed Map Design Sacrifices Gameplay for Art&lt;/h2&gt;
&lt;p&gt;The map design prioritizes artistic expression over clear visual guidance, leading to significant issues.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Navigation:&lt;/strong&gt; While I adapted to the invisible walls and &quot;natural&quot; map layouts, less experienced players will likely struggle with finding their way due to a lack of distinct landmarks or intuitive paths. The realistic visuals often make different areas look too similar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exploration Experience:&lt;/strong&gt; Item placement often felt random and unrewarding, negatively impacting the desire to explore thoroughly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pacing:&lt;/strong&gt; The placement of bosses relative to exploration sections felt inconsistent, sometimes clustering bosses too closely or spacing them too far apart, disrupting gameplay flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Graphical Issues Despite UE5 Detail&lt;/h2&gt;
&lt;p&gt;While leveraging UE5&apos;s Nanite for detailed models, the game&apos;s graphics aren&apos;t flawless.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vegetation LOD:&lt;/strong&gt; Texture popping on vegetation was noticeable and immersion-breaking, even with Level of Detail (LOD) set to cinematic. It seems traditional techniques were used here, contrasting poorly with Nanite-rendered assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Artifacts:&lt;/strong&gt; Temporal artifacts were prominent, especially in particle effects, hair, character edges, and water surfaces. This created a noisy, blurry look in these elements, detracting from the overall visual fidelity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Passable Combat System with Core Conflicts&lt;/h2&gt;
&lt;p&gt;The combat system is functional but lacks depth and suffers from internal conflicts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Boss Interaction:&lt;/strong&gt; Many boss fights involve periods where dealing damage is difficult, sometimes feeling dependent on the boss AI deciding to enter attack range (e.g., Kang-Jin Loong). The lack of a reliable, universal ranged attack option exacerbates this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monotonous Stances:&lt;/strong&gt; The three weapon stances primarily change heavy attacks, leaving the normal attack moveset feeling repetitive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Conflict:&lt;/strong&gt; The game encourages aggressive, close-quarters combat (emphasized by dodge-only defense), yet the core loop revolves around managing stamina (for light attacks) and building resources for heavy attacks. This creates a disjointed feeling, as the desire to press the attack conflicts directly with system limitations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Concluding Thoughts on Hype and Reception&lt;/h2&gt;
&lt;p&gt;Setting aside external controversies, the hype surrounding Black Myth: Wukong feels disproportionate to the final product. Much of its commercial success seems driven by the Chinese market, potentially influenced by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Halo Effect:&lt;/strong&gt; Affection for the &lt;em&gt;Journey to the West&lt;/em&gt; theme and national pride in a domestic AAA title might lead to higher ratings than the game objectively merits based on global AAA standards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Primacy Effect:&lt;/strong&gt; For players new to AAA games, the initial positive buzz could lead to an overestimation of its quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The polarized reception (ratings tending towards extremes) is concerning. My worry is that the market dynamics that amplified Black Myth&apos;s hype might become a template, potentially overshadowing objective game quality assessment in the future. While perhaps unintentional by Game Science, the market learns quickly.&lt;/p&gt;
</content>
    <category term="Video Game"/>
    <category term="Review"/>
    <category term="ARPG"/>
    <category term="UE5"/>
    <category term="Game Design"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/the-gender-allegory-and-patriarchal-illusion-of-ignited-in-cavern/</id>
    <title>Ignited in Cavern: A Gender Allegory and Patriarchal Phantasms</title>
    <updated>2024-08-06T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/the-gender-allegory-and-patriarchal-illusion-of-ignited-in-cavern/" rel="alternate"/>
    <published>2024-08-06T00:00:00.000Z</published>
    <content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In the late Northern Wei dynasty, blades of steel and fleeting visions swept across the boundless desert. In that narrow fissure between heaven and earth, the text‑adventure game &lt;em&gt;Ignited in Cavern&lt;/em&gt; quietly raised its curtain. It tells not merely of a bizarre murder case, nor merely a few strands of family blood and tears interwoven with love and hatred; rather, it resembles a somber mirror, one that starkly illuminates how the destinies of individuals are twisted and devoured under a feudal patriarchy. Beginning at the intersection of gender and power, this paper delves into how &lt;em&gt;Ignited in Cavern&lt;/em&gt;, through a narrative seemingly soaked in blood and dust, interprets the Othering of women and reveals the violent core behind reproductive politics. Through the theoretical lenses of Simone de Beauvoir, Judith Butler, and others, we will examine how the game others women, how reproductive politics turns into violence, and how gender roles, wedged between performance and identity, veer toward madness or awakening.&lt;/p&gt;
&lt;h2&gt;Synopsis of the Game&lt;/h2&gt;
&lt;p&gt;In the late Northern Wei, a mysterious rumor circulated through the small town of Dunhuang: it was said that an ancient statue left from the ruins could bestow supreme blessings upon any supplicant. The woman general A&apos;nu and the Han youth Chu Qingqing were ordered to travel there and investigate, yet in the sand‑swept border town they witnessed an eerie beginning—the caravan that had guarded the statue now lay dead in the streets, their causes inexplicable, while fear and rumor spread through the town like a plague. Was it vengeful spirits at work, or something else? Burdened by their own unbearable pasts, A&apos;nu and Chu Qingqing must both search for the truth and repeatedly look back beneath the shadow of fate.&lt;/p&gt;
&lt;p&gt;Seemingly delicate and taciturn, Chu Qingqing’s upbringing had long planted a seed of distortion gnawing at his bones: craving affection since childhood yet always denied, he poured all emotion into a forbidden love for his brother‑in‑law and, lured by the spirit housed in the statue, embarked upon a blood‑soaked path of sacrifice. The successive murders of pregnant women and the blood‑soaked rituals all stemmed from his extreme obsession with “whether he could have an heir belonging to himself and his brother‑in‑law.” Elsewhere, the steppe‑born woman general A&apos;nu was equally awe‑inspiring and chilling: famed for bloodlust in youth, rumored to have ended her groom’s life on their wedding night, she bore the epithet “God of War.” This defiant lone wolf scorned worldly propriety, yet under Dunhuang’s desolate night she was gradually forced to confront her deep yearning for kinship and love.&lt;/p&gt;
&lt;p&gt;When desire and conscience converged on this remote frontier, what arrived was an even more dreadful judgment: the statue’s eerie power drew Chu Qingqing step by step toward ruin and forced A&apos;nu to choose between morality and personal feeling. In the end, raging flame devoured resentment and attachment, and countless fates turned to ash and scattered beneath the sky. Chu Qingqing’s futile dream collapsed in an instant; A&apos;nu, clasping her solitary courage, stepped onto the vast wilderness, burying all blood and tears. Against this backdrop where fantasy and human relations overlap, &lt;em&gt;Ignited in Cavern&lt;/em&gt; reached its destined curtain call—it revealed not only conflicts and sacrifices beneath desert smoke and spectral shadows, but also the manifold fetters and distortions that patriarchal systems impose upon women, upon desire, and upon reproduction. This fictional world is like a ruthless scalpel slicing open the covert oppression of the “second sex.”&lt;/p&gt;
&lt;h2&gt;Patriarchy and the “Second Sex”: The Othering of Women&lt;/h2&gt;
&lt;p&gt;The story of &lt;em&gt;Ignited in Cavern&lt;/em&gt; is set in the feudal society of late Northern Wei, a domestic fantasy drama saturated with patriarchal myth. In this patriarchal society, &lt;strong&gt;women are positioned as the Other&lt;/strong&gt;, and their value is often dependent on men and bloodlines. Simone de Beauvoir notes in &lt;em&gt;The Second Sex&lt;/em&gt;, “He is the Subject, he is the Absolute — she is the Other.” In &lt;em&gt;Ignited in Cavern&lt;/em&gt;, female characters chiefly appear as &lt;strong&gt;the Other of male desire&lt;/strong&gt;, functionalized and stripped of their subjectivity. This arrangement precisely reflects de Beauvoir’s mechanism: “One is not born a woman; one becomes one.” For example, in Chu Qingqing’s route, the nine sacrificed pregnant women &lt;strong&gt;are never even named&lt;/strong&gt;—they are nameless offerings on the altar of male desire. This extreme treatment shows how patriarchal structure erases female individuality, degrading them into mere instruments for male aims. Likewise, Chu Qingqing’s &lt;strong&gt;biological sister&lt;/strong&gt; is nearly reduced to a “pregnancy tool” in the plot: her sole meaning seems to be providing a vessel for her husband’s and brother’s bloodline. These women are not independent persons but Othered beings serving someone else’s narrative needs.&lt;/p&gt;
&lt;p&gt;Even the major female characters suffer such Othering. In A&apos;nu’s arc, her sister‑in‑law Lady Aluhuan, an aristocratic woman, has her identity and worth bound entirely to the roles of “wife” and “mother.” The only way she seeks power is through &lt;strong&gt;bearing a child and thus becoming a mother&lt;/strong&gt; to secure her status. Within patriarchal values, women are disciplined to define themselves by family and reproduction—that is, by male bloodlines. She can attain meaning &lt;strong&gt;only in relation to her male clan&lt;/strong&gt;—as someone’s wife, someone’s mother—while she cannot be regarded as an autonomous individual. As de Beauvoir says, women are seen as “lacking” and need to complete themselves through the male Other—precisely the vivid condition of female Otherness in &lt;em&gt;Ignited in Cavern&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The game subtly discloses the repression arising from Othering. In Chu Qingqing’s route, players are long guided to stand within the male protagonist’s perspective, empathizing with his hidden yet twisted desires while almost forgetting the women he harms. The pain of women is screened out of sight, which itself is an embodiment of patriarchal narrative marginalizing women. The experiences and feelings of female characters are treated as secondary relative to the male protagonist—even if the cost is nine pregnancies lost, it seems they can be “brushed over” in one stroke of narrative. This storytelling choice undoubtedly echoes the condition of women as the “second sex” under patriarchy: their suffering and voices are drowned in the male grand narrative and passion, becoming mere background noise. It is precisely this faint yet pervasive silence that forms the game’s indictment and reflection on the Othered plight of women.&lt;/p&gt;
&lt;h2&gt;Patriarchal Violence and Reproductive Politics: Blood Sacrifice and Tragedy of Motherhood&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;Ignited in Cavern&lt;/em&gt;, &lt;strong&gt;reproduction is the central motif that runs throughout&lt;/strong&gt;, and the various storylines revolving around it all reveal the violent nature and controlling impulse of the patriarchal system. Feminism has long pointed out that patriarchy maintains rule by controlling women’s bodies and reproductive capacities; such control often manifests in hidden or naked violence. The game presents this nakedly before the players’ eyes: to satisfy male craving for “bloodline continuation,” there emerges &lt;strong&gt;a chain of tragedies sacrificing pregnant women&lt;/strong&gt;. Tempted by the spirit inhabiting the statue, Chu Qingqing murders &lt;strong&gt;nine innocent pregnant women with his own hands as offerings&lt;/strong&gt; to share a child with his beloved brother‑in‑law. This brutal yet symbolically real storyline dramatizes reproductive politics at its extreme: men obtain female bodies and lives to fulfill desire, even resorting to intolerable violence. As some players sarcastically remark, “&lt;strong&gt;The price of creation is slaughtering women… utterly absurd&lt;/strong&gt;.” In patriarchal myth, the power of creation is seized by men, yet the sacrifice offered is female flesh. This inhuman design exposes patriarchy’s deep violence: &lt;strong&gt;male desire for power and heritage is fulfilled at the cost of women’s lives&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;critique of reproductive politics&lt;/strong&gt; does not stop at male violence seizing women’s reproductive capacity; it also portrays the tragic predicament of women trapped in patriarchal reproductive oppression. In A&apos;nu’s arc, the story of her sister‑in‑law Lady Aluhuan is a painful &lt;strong&gt;“tragedy of motherhood.”&lt;/strong&gt; As the legitimate wife of a great household, Lady Aluhuan’s status and value hinge on &lt;strong&gt;whether she can bear a son&lt;/strong&gt;. She regards motherhood as the only path to grasping power and security in her feudal family, sinking day by day into this fixation. Fate thwarts her: she never conceives, and her husband (A&apos;nu’s brother) marries concubines for heirs. The concubines bear him children, while Lady Aluhuan loses both spousal affection and family prestige. &lt;strong&gt;The collapse of her reproductive capacity obliterates the power base she once relied on&lt;/strong&gt;, leading her to despair and madness, and ultimately to a tragic death. Her ending &lt;strong&gt;symbolizes the collapse of the myth of “matriarchal right”&lt;/strong&gt;—within the patriarchal frame, a woman who tries to gain power via motherhood can only meet destruction. Patriarchy never grants genuine autonomy; the illusory myth of “matriarchal right” cannot withstand harsh reality: once a woman’s reproductive value fails, abandonment is her only fate. As Gayle Rubin’s research &lt;em&gt;The Traffic in Women: Notes on the &apos;Political Economy&apos; of Sex&lt;/em&gt; shows, patriarchal society commodifies women through marriage and kinship; the reproductive politics in both A&apos;nu’s and Chu Qingqing’s routes indicts this mechanism—motherhood should not be a woman’s sole value, and the extreme acts of mothers in the game further expose this malady.&lt;/p&gt;
&lt;p&gt;It is worth noting that &lt;strong&gt;patriarchal control of women’s reproduction is not limited to the family&lt;/strong&gt;; it is sanctified by religion and myth to legitimize seizing female bodies. The recurring images of &lt;strong&gt;the stone deity and the spirit&lt;/strong&gt; in &lt;em&gt;Ignited in Cavern&lt;/em&gt; are such patriarchal power devices. The legendary statue that can “grant blessings” is said to realize human wishes, yet it demands a bloody price—men offer pregnant women’s lives in exchange for “grace.” The statue appears supernatural, but it metaphorically represents patriarchy’s supreme &lt;strong&gt;reproductive authority&lt;/strong&gt;: it wraps men’s desire for lineage in ritual, cloaking horrifying violence in sanctity. By sacrificing to the stone deity, men seem to obtain &lt;strong&gt;legitimate authorization&lt;/strong&gt; to control the conception and termination of life. This storyline mercilessly exposes how patriarchy exploits religious myth to sustain its violence: &lt;strong&gt;female reproductive organs become offerings to satisfy male desire, while what is desecrated are women’s lives and wombs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In sum, through these extreme and metaphor‑rich plot elements, the game conducts a profound critique of patriarchal reproductive politics. Whether the souls of nine pregnant women or Lady Aluhuan’s sorrow at failing to bear a child, all show how deeply women suffer control and harm around reproduction under patriarchy. &lt;em&gt;Ignited in Cavern&lt;/em&gt; tears open the violence hidden in history and family: &lt;strong&gt;what patriarchy calls procreation and love often masks domination and plunder of women’s bodies&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Gender performativity: Roles Between Violence and Identity&lt;/h2&gt;
&lt;p&gt;Judith Butler’s gender theory suggests that &lt;strong&gt;gender is an identity tenuously constituted in time, instituted in an exterior space through a stylized repetition of acts&lt;/strong&gt;. In &lt;em&gt;Ignited in Cavern&lt;/em&gt;, the protagonists of the two storylines—Chu Qingqing and A&apos;nu—are trapped in the conflict between gender performativity and self‑identity. Their respective experiences can be analyzed through “gender performativity”: under patriarchal scripts imposed on identity, characters either submit or resist, and every step is accompanied by the shadow of violence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chu Qingqing’s&lt;/strong&gt; character embodies the distorted performance of masculine roles under patriarchal expectations. Although male, he &lt;strong&gt;loves his brother‑in‑law&lt;/strong&gt; (another man, Zou Rong), and this taboo love puts him in a quandary regarding gender roles. Patriarchal heteronormativity does not allow him to love a man as “wife” or “mother,” yet his yearning for a “shared‑blood child” is akin to a maternal fantasy—traditionally a female role in heterosexual marriage. To satisfy this illusion, he embarks on a deranged path, attempting through occult ritual to let two men “give birth” to a child. &lt;strong&gt;This can be seen as an extreme counter‑performance of traditional gender roles&lt;/strong&gt;: Chu Qingqing, as a man, obsessively plays the “mother” function. Yet this transgression brings no salvation, only extreme violence (the killing of pregnant women). He is trapped in patriarchal discourse with no exit: as an obedient older brother, he suppresses his homosexual desire; trying to play a female reproductive role, he can only realize it in grotesque form. As Butler stresses, “gender is not a pre‑existing identity but a constantly repeated performance,” and Chu Qingqing’s attempt to “perform” female reproduction is an extreme satire on traditional scripts, revealing society’s rigid demands on gender identity. Ultimately, he can be neither true husband nor father, nor achieve satisfaction as “mother”; only in hallucinations does he converse with the spirit, letting himself be manipulated by an ever more absurd drama. Notably, he also performs another layer of obedience: in daily relations, Chu Qingqing &lt;strong&gt;habitually pleases others and hides himself&lt;/strong&gt;. “Some things he truly wants, but if no one asks he never acts; some things he loathes, yet if others request he complies.” This self‑sacrificing obedience is exactly the patriarchal script for “normatively masculine subjects”; he performs it by rote, avoiding confrontation with genuine needs. His tragedy lies in never escaping predetermined gender scripts: obediently acting the “good man” in others’ eyes while madly performing patriarchal myths of a “creator,” losing his true self and plunging into violence.&lt;/p&gt;
&lt;p&gt;In sharp contrast, &lt;strong&gt;A&apos;nu&lt;/strong&gt; plays a role traditionally ascribed to men within a female body. As a woman general of Northern Wei, she shows surpassing strength from childhood: “killing a hunting dog bare‑handed at four, playing a groom to death on her wedding night,” making her almost legendary, the &lt;strong&gt;“God of War.”&lt;/strong&gt; This is a &lt;strong&gt;feminine rendition of masculine martial prowess&lt;/strong&gt;: A&apos;nu, as a woman, plays the heroic, fearless, warrior, shattering patriarchal scripts of female docility. In gender performativity terms, she &lt;strong&gt;performs a “masculinized” self.&lt;/strong&gt; She does not suppress desire, reveling in bodily pleasure and battle’s thrill, ignoring patriarchal decorum. Yet, as Butler notes, any rebellion against gender discipline brings social pressure and identity conflict. Though fierce, A&apos;nu is not devoid of inner struggle: she still yearns for love and connection, but the object of her affection is &lt;strong&gt;her sister‑in‑law&lt;/strong&gt;. A&apos;nu’s deep love for Lady Aluhuan is doubly taboo (same‑sex and incest). When she attempts to express it, she &lt;strong&gt;abandons usual forthrightness, showing rare shyness and caution&lt;/strong&gt;. Saying “I love you” with the respectful “you,” her tone is as restrained as a knight pledging to a queen. This respectful yet suppressed confession shows A&apos;nu &lt;strong&gt;re‑performing a socially accepted female submissive image&lt;/strong&gt; in the face of love—she is no longer a wild beast but a courteous junior. Thus, while fearless in battle, she is fettered by ethical roles in forbidden love: on one side blazing desire, on the other Lady Aluhuan’s moral identity as “elder.”&lt;/p&gt;
&lt;p&gt;Lady Aluhuan’s response to A&apos;nu embodies &lt;strong&gt;the other face of gender performativity&lt;/strong&gt;: as an elder woman, she cannot accept A&apos;nu’s transgressive love and retreats into a maternal role. She repeatedly uses &lt;strong&gt;elderly kindness and morality&lt;/strong&gt; to dissolve A&apos;nu’s confession, placing A&apos;nu as a “junior” to be guided. Asking, “Do you wish to be my child?” she tries to re‑frame A&apos;nu’s love within a socially acceptable “maternal love.” She insists on playing the moral mother, using ethical affection to refuse taboo desire. Thus, &lt;strong&gt;both women in this hidden romance are constrained by their gender roles&lt;/strong&gt;: A&apos;nu cannot openly become Lady Aluhuan’s lover, only a respectful younger; Lady Aluhuan cannot respond as a woman, only hiding in motherhood to evade inner turmoil. Their performances lead to shared pain: A&apos;nu, invincible in force, is helpless in emotion; Lady Aluhuan spirals toward madness in the tug‑of‑war between reason and feeling. &lt;strong&gt;Gender performativity scripts forge identity tragedies&lt;/strong&gt;; deviation invites violence—from others or self, whether external blood or internal heartbreak.&lt;/p&gt;
&lt;p&gt;In sum, through the extreme yet mutually reflecting characters Chu Qingqing and A&apos;nu, &lt;em&gt;Ignited in Cavern&lt;/em&gt; shows the performativity and fragility of gender identity. Whether a man attempting “motherhood” or a woman acting “God of War,” heavy costs ensue. When roles struggle between violence and identity, we witness &lt;strong&gt;the cruel constraints of gender norms on individuals&lt;/strong&gt; in patriarchy: any overstep invites violent punishment; only by tearing off the mask might one glimpse a sliver of true self.&lt;/p&gt;
&lt;h2&gt;Female Body, Desire, and Motherhood as Metaphor&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Ignited in Cavern&lt;/em&gt; employs an almost anatomical allegory to weave &lt;strong&gt;female body, desire, and reproduction&lt;/strong&gt; into a complex network of metaphors. In this net, every female character’s body and desire belong not solely to herself but bear the double meaning of patriarchal myth and rebellious narrative. From nine pregnant corpses to Lady Aluhuan’s fixation on mother and child, the game uses extreme story settings to reveal how patriarchal fantasies about “love” and “procreation” are built on violence, while probing the cracks and possibilities women may find therein.&lt;/p&gt;
&lt;p&gt;First, the &lt;strong&gt;tragedy of the “nine pregnant women”&lt;/strong&gt; is the clearest reproductive‑political metaphor. Pregnant women—bearers of life—should symbolize love, hope, and future, yet here they are slaughtered as sacrifices, their flesh the price of male desire. This inversion metaphor shows the dark myth of patriarchal “love”: a man wants a child for love, yet destroys multiple women bearing life. The fruit of love is irrigated with others’ blood and tears; such love departs from its origin, degenerating into a violent structure. As players belatedly realize: Chu Qingqing “&lt;strong&gt;acts in the name of love, then tells players love does not exist&lt;/strong&gt;”—once truth emerges, we see the natal frenzy in love’s name was illusion, love merely a glittering cloak for desire. The game thus questions the patriarchal myth of love: when love is tied to lineage, equated with possessing another’s capacity to reproduce, “love” is no longer true love but a cage devouring women. The nine pregnant women’s souls ask: &lt;strong&gt;Is this truly love, or a hunt in love’s name?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;Lady Aluhuan’s obsession with motherhood&lt;/strong&gt; is another metaphor. She represents the traditional patriarchal value of the “mother” role’s supremacy and sacrifice. In feudal family, bearing heirs is women’s sole path to dignity; motherhood is haloed. The game deconstructs this image: Lady Aluhuan sees motherhood as life’s meaning, yet her &lt;strong&gt;maternal desire drives her to madness and destruction.&lt;/strong&gt; This irony signals: &lt;strong&gt;patriarchal myth exalts “motherly love” as a woman’s highest virtue while shackling her with golden chains.&lt;/strong&gt; Lady Aluhuan kneels daily praying for pregnancy, yet this pious fixation destroys her. When hope dies, her “merciful mother” persona collapses, even seeking illusory bonds by ethical coercion (asking A&apos;nu to be her “child”). Her death symbolizes &lt;strong&gt;the shattering of the “myth of matriarchal right”&lt;/strong&gt;—a woman’s attempt to gain power via motherhood proves mirage. The game shows: when motherhood is a woman’s only value, the role itself devours her. At her death, maternal authority crumbles, exposing patriarchy’s myth of motherly love—great mother becomes pitiful victim, her sacrifices never saved her.&lt;/p&gt;
&lt;p&gt;Beyond reproduction and motherhood, &lt;strong&gt;relationships of desire among women&lt;/strong&gt; challenge patriarchal norms. The ambiguous, unnameable bond between A&apos;nu and Lady Aluhuan excavates and subverts &lt;strong&gt;the repression of women’s desire&lt;/strong&gt; in patriarchy. Traditional ethics forbid their love—seen as incest or perverse. Through their interaction, the game presents an unusual tableau of “women’s love”: A&apos;nu’s knight‑like devotion and sisterly sympathy merge. This complex desire &lt;strong&gt;breaks patriarchal stereotypes of women’s emotion&lt;/strong&gt;, showing female desire’s power. Yet reality forces it into maternal ethics, twisting love into elder care; both cannot confront feelings. The metaphor reveals &lt;strong&gt;female desire’s suffocation&lt;/strong&gt;: two women in love retreat to roles (mother/child) for legitimacy, burying true passion beneath ethics. The fire never dies—A&apos;nu still burns, Lady Aluhuan wavers. This undercurrent of female desire is silent resistance to patriarchal discipline: though not breaking free, players witness &lt;strong&gt;female subject desire&lt;/strong&gt; surging underground.&lt;/p&gt;
&lt;p&gt;The game’s treatment of &lt;strong&gt;mother figures as functional&lt;/strong&gt; also carries strong political satire. Mothers are not stereotypical saints but endowed with extreme, conflicting power: some mothers &lt;strong&gt;bear children then kill them&lt;/strong&gt;—Chu Qingqing’s mother reportedly took her offspring away in regret; even after death her will haunts. Some mothers (Lady Aluhuan) crave children but fail and are harmed by others’ offspring. Others (Chu Qingqing’s sister) lose infants early—dream shattered. This array displays &lt;strong&gt;mothers’ power to create and destroy&lt;/strong&gt;: &lt;strong&gt;“A mother who births you can also kill you, granting life yet able to reclaim it.”&lt;/strong&gt; In patriarchal myth, mothers are celebrated as life’s source and selfless love, yet the game tears off this veil, revealing suppressed rage and sorrow. Mothers forced into functions either rebel—like Chu Qingqing’s mother reclaiming life in regret, the patriarchy‑feared “mother kills child” tragedy; or become martyrs—Lady Aluhuan going mad and dying for failed function. Either way, motherhood’s dark side is exposed: &lt;strong&gt;motherly love can morph into devouring love&lt;/strong&gt;, mothers not merely saints but critics of injustice.&lt;/p&gt;
&lt;p&gt;Through these layered metaphors, the game guides players to reassess naturalized patriarchal notions of “love” and “life.” When blood‑streaked altars echo buried corpses in a mother’s garden, we grasp: under patriarchal myth, the exalted “love” is &lt;strong&gt;a sugar‑coated violence structure&lt;/strong&gt;. Conjugal love demands women risk life; maternal love drives women mad; even sisterly love (women’s friendship/desire) is strangled by norms. The game’s extreme story shows: &lt;strong&gt;When love loses respect for equality and subjectivity, becoming a tool of patriarchal possession and reproduction, it ceases to be pure love, becoming a power contest and sacrificial myth.&lt;/strong&gt; When women’s bodies are mythologized as vessels and desire suppressed in morality’s name, “love” turns into a mountain crushing women. As Chu Qingqing’s line reveals: perhaps love itself is an illusion—an empty shadow built on others’ pain. Only by seeing this can true awakening and resistance begin.&lt;/p&gt;
&lt;h2&gt;Madness and Awakening: Feminine Rebellion in Gender Writing&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Ignited in Cavern&lt;/em&gt; boldly depicts a series of nearly deranged female figures who resist with violence or collapse in despair. Through their madness and awakening, the author constructs a radical rebellious gender narrative, seeking a new route through cracks in patriarchal myth. A&apos;nu and Lady Aluhuan, this aunt‑and‑sister pair, are its core: one battles fate in berserk valor, the other reveals truth through lunatic descent. Their tales are both thrilling and heartbreaking, forming a dual judgment on patriarchal norms.&lt;/p&gt;
&lt;p&gt;Let us begin with &lt;strong&gt;A&apos;nu&lt;/strong&gt;. She appears almost as a “madwoman”: bloodlust in youth, killing her groom on their wedding night, invincible in war—all exceed imagination of a woman. Yet this near‑uncontrolled violence is &lt;strong&gt;active rebellion&lt;/strong&gt; against patriarchy. A&apos;nu refuses any subservience: she is not a docile bride, nor a lamb to slaughter. She &lt;strong&gt;regains bodily sovereignty through madness&lt;/strong&gt;—no one dare force her to marry or bear children; her blade wins her the standing of a “free person” beyond rules. Her “madness” is an exaggerated allegory: by forging a woman ungoverned and feared by men, the author subverts the traditional docile feminine image. A&apos;nu embodies radical revolt: destroyer of oppressive order and seeker of new. Her battlefield cries of “love” or “power” evoke epic shock—announcing the birth of a &lt;strong&gt;Valkyrie who will not submit to patriarchy&lt;/strong&gt;. As Eve Kosofsky Sedgwick notes in &lt;em&gt;Between Men: English Literature and Male Homosocial Desire&lt;/em&gt;, rigid gender and desire borders label any transgression “madness,” which reflects the pathological tendency towards non-heterosexual or transgressive behavior in the context of literature and society. A&apos;nu’s fierce rebellion counters this binary, her “madness” not only challenges patriarchal taboo but deeply indicts social oppression.&lt;/p&gt;
&lt;p&gt;By contrast, &lt;strong&gt;Lady Aluhuan’s madness&lt;/strong&gt; is restrained and tragic. She wields no sword, yet her inner twists rival battlefield carnage. In long despair over lost children, her mind crumbles; fixation pulls her to &lt;strong&gt;the edge of the surreal&lt;/strong&gt;: she believes in spirits, curses, perhaps joins strange rites (hints of a deer‑headed mask meeting A&apos;nu, like an out‑of‑body soul). With gentle façade, she hides blazing insanity. Her madness is &lt;strong&gt;desperate protest forced by patriarchy&lt;/strong&gt;. She obediently played good wife and daughter‑in‑law, gaining nothing. Only the phantom hope of bearing a child remains; when shattered, she resists fate in self‑harm labeled “lunacy.” She clings to being “mother,” dragging loving A&apos;nu into delusion, treating her as “child.” Though irrational, it is laden with meaning: a woman, cornered, sends a &lt;strong&gt;final protest&lt;/strong&gt; against patriarchal rules. Unable to be real mother, she crafts a fantasy, weaving ethics and blood into a cocoon—self‑deception to outsiders, but her last salvation. Her death may signal &lt;strong&gt;a kind of awakening&lt;/strong&gt;: realizing all effort was futile, she is but a patriarchal pawn. In that era, her personal awakening is powerless, ending only in tragedy.&lt;/p&gt;
&lt;p&gt;Of note, &lt;strong&gt;A&apos;nu’s route offers multiple endings, each corresponding to choices on her “awakening” path&lt;/strong&gt;. Endings hinge on her final stance on “love”: one sees her insist love exists and willingly become its slave; another sees her perceive love’s falsity, recognizing only power, choosing to serve it. This is her &lt;strong&gt;ideological awakening&lt;/strong&gt;: after excruciating events, her faith in patriarchal “love” wavers. If she rejects love, embracing power, she has pierced the myth and rebuilds value through strength—deeper rebellion and awakening. Rejecting enslavement to love (often family or men), choosing her own power: she awakes from patriarchal myth, becoming a true &lt;strong&gt;iconoclast&lt;/strong&gt;. Alternatively, she may still believe in love, yet her love (for her sister‑in‑law) defies patriarchal expectation, thus still living female desire’s power. Either way, A&apos;nu’s spirit remains rebellious: &lt;strong&gt;she dares to be mad and lucid, to love and hate,&lt;/strong&gt; expanding the frontier of female possibility and composing a radical war chant.&lt;/p&gt;
&lt;p&gt;Through these mad yet awakened women, &lt;em&gt;Ignited in Cavern&lt;/em&gt; conducts a bold gender‑writing experiment: not depicting obedient “good women” but highlighting transgressive “bad” or “mad” women. Historically, literature labels rebellious women as mad; the game wields “madness” as narrative weapon: letting women speak unspeakable truths and perform taboo deeds. &lt;strong&gt;Women’s madness becomes a language of resistance&lt;/strong&gt;: A&apos;nu’s berserker image questions male monopoly on violence; Lady Aluhuan’s derangement unmasks patriarchal manipulation. Meanwhile, their awakening sharpens defiance with clear thought: they are not aimlessly mad but glean insight and make choices—whether a last‑second epiphany or one unlocked by the player, these awakenings empower them. The author uses this “madness + awakening” duet to build an unprecedented radical gender narrative, calling for rebellion and transcendence of patriarchy.&lt;/p&gt;
&lt;p&gt;Of course, such radical writing is not flawless. While celebrating female revolt, the work harbors implicit issues worthy of reflection.&lt;/p&gt;
&lt;h2&gt;The Shroud of Patriarchal Narrative: Male Perspective and Female Suffering&lt;/h2&gt;
&lt;p&gt;Despite &lt;em&gt;Ignited in Cavern&lt;/em&gt;’s attempt at a radical gender narrative, at certain levels it still fails to shake off patriarchal storytelling’s ruts. &lt;strong&gt;The most striking problem is that the male protagonist’s perspective invisibly screens off and buries women’s suffering&lt;/strong&gt;, so that while the game condemns patriarchal violence it almost reenacts a neglect of women. This paradox warns that even an anti‑patriarchal theme may dull its edge if it employs patriarchal gaze, even causing new injustice.&lt;/p&gt;
&lt;p&gt;As noted earlier, the greatest controversy in Chu Qingqing’s route is that &lt;strong&gt;the deaths of nine pregnant women are down‑played&lt;/strong&gt;; players, immersed in his viewpoint, focus on his inner pain and unattainable love. By contrast, the extreme violence against innocent women scarcely becomes a narrative focal point, often glossed in a few sentences. When climax arrives, many players realize the male lead committed monstrous crime; yet even then, their primary feeling is shock and complex sympathy for him, not grief for nine lives. This is not their coldness but &lt;strong&gt;the narrative’s deliberate guidance&lt;/strong&gt;. One comment sharpens: “The author is clever; the pregnant women’s deaths are casual strokes, and players experience the male lead’s feelings. For him, sacrificing pregnant women might be trivial, so players sharing his perspective forget it.” Laura Mulvey’s research shows the “male gaze” reduces women to visual objects; in the game, the concealed pain of women embodies patriarchal gaze—male view marginalizes female suffering. The game lets us “see” via male eyes so &lt;strong&gt;women’s pain naturally fades&lt;/strong&gt;—not visceral to men, players glide past. This “cleverness” is not innovation but &lt;strong&gt;patriarchal routine&lt;/strong&gt;: writing men as universal experience. Women’s ordeals are ignored or down‑played, male emotions amplified. Chu Qingqing’s route follows this pattern, so many missed the intended critique, even misconstruing it. Some nickname the male route a “&lt;strong&gt;BL tortures women&lt;/strong&gt;” piece—depicting male‑male romance using women’s torment for spectacle. When female suffering is too obscure, a work may be misread as catering to unhealthy thrills, not critiquing patriarchy.&lt;/p&gt;
&lt;p&gt;This &lt;strong&gt;male perspective’s occlusion of female suffering&lt;/strong&gt; is a hidden danger in &lt;em&gt;Ignited in Cavern&lt;/em&gt;. It can weaken the thesis and raise moral unease: nine horrific deaths barely ripple, lacking narrative space or condemnation. The treatment re‑buries victims: patriarchy erased their lives; the story erases their presence. Feminists stress &lt;strong&gt;“we must face the violence women suffer”&lt;/strong&gt;; seeing is resistance’s start. Yet players once &lt;strong&gt;“cannot see”&lt;/strong&gt; this violence, thus cannot question it—most focus on Chu Qingqing’s tragedy, ignoring deeper female tragedy. A comment asks: “Even viewers with gender awareness might unconsciously miss structural injustice behind these scenes. In a male‑dominated narrative, who truly sees women’s plight?” The dilemma: the story must have a man recite this pathology to show patriarchal absurdity; but deep immersion repeats patriarchal bias.&lt;/p&gt;
&lt;p&gt;Still, the author tries compensation in A&apos;nu’s arc, almost free of male gaze, centering a woman’s perspective. Here, women’s desire, anger, and agency stand vivid; many players feel A&apos;nu’s line is cathartic revenge. Perhaps intentionally, &lt;strong&gt;a powerful female‑centered narrative&lt;/strong&gt; offsets the male viewpoint imbalance. Hidden issues resurface—Lady Aluhuan’s death, truths, interlocking schemes—and gain fuller exposition. Viewing through A&apos;nu’s eyes clarifies patriarchy’s absurdity: she sees her sister‑in‑law die for men and children, smashes the stone statue—symbol of desire’s power (if chosen). These actions &lt;strong&gt;correct male line bias&lt;/strong&gt;. Thus, the dual structure balances perspective: &lt;strong&gt;one line hides women’s pain, the other lets women speak&lt;/strong&gt;. Yet many players play only one line, missing the balance, leading to claims of “inconsistency.” How to rebel while caring for readers, how to expose cruelty without narrative pitfalls—these are questions the game leaves us. It also reminds creators: &lt;strong&gt;in overturning old narratives, beware old gazes sprouting anew.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Passes a Gap in Flight, Like a Spark in the Dark, or a Dream of Moonbeam&lt;/h2&gt;
&lt;p&gt;As a fantasy tragedy steeped in late Northern Wei’s bleakness, &lt;em&gt;Ignited in Cavern&lt;/em&gt; unfurls a relentless dissection and critique of patriarchy and its myths amid blood and flames. Through layers of analysis—patriarchal system, female Othering, reproductive ethics, and gender politics—the game offers a harsh yet thought‑provoking proposition: &lt;strong&gt;when love is imprisoned within patriarchal myth, it ceases to be pure love and may evolve into a violent structure; to break this structure, only marginal madness and rebellion can suffice.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Those shocking scenes—the nine pregnant women whose names are forgotten, their bodies silent offerings on the altar of male desire; Lady Aluhuan’s near‑mad obsession with “mother” identity dragging her into destruction—keep knocking at us. They force us to ponder: when lineage obsession overrides all, when women’s existence is reduced to reproduction, how much distortion and cost hide behind familial warmth? Chu Qingqing’s crimes in love’s name make us doubt whether love nourished by blood was self‑deception from the start. A&apos;nu’s blazing yet desperate affection, shackled by ethics, turns into a blade, wounding herself and possibly severing the last sliver of tenderness. Her story seems to say: even mighty rebels, breaking rules, may fall into deeper loneliness and confusion.&lt;/p&gt;
&lt;p&gt;We see characters performing, struggling, even going extreme within preset gender scripts. Chu Qingqing attempts a forbidden role and is consumed by desire’s flame; A&apos;nu crashes against convention in feral posture, yet wavers on the edge of love and hate. Their madness and awakening, their swaying between violence and identity, are no longer mere personal tragedies but silent indictments of that era and its rules. The patriarchal myths of “love,” “motherhood,” “family” shatter amid the statue’s cold gaze and battlefield blood, exposing an inner &lt;strong&gt;violent structure&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ignited in Cavern&lt;/em&gt; offers a sharp, mournful answer. The nine cold corpses, Lady Aluhuan’s tears—they proclaim the myth’s collapse: patriarchal love is but the alias of power desire, underlain by a network woven of blood and tears. When the dust settles, players may feel: &lt;strong&gt;The cavern’s stone and lingering ghost‑fire have burnt the patriarchal love myth to cinders, illuminating the long‑silenced truth of the second sex buried beneath the ruins.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When flames fade and dust settles under desert moonlight, only ashes and faint embers remain. Cracks in the stone seem to hide residual heat, signaling patriarchal dreams’ demise and women’s truth’s emergence. The cavern has split; the spark, though small, lets us glimpse, amid the ashes of burnt‑out dreams, the roots of women’s distorted destinies.&lt;/p&gt;
</content>
    <category term="Video Game"/>
    <category term="Essay"/>
    <category term="Feminism"/>
    <category term="Gender Study"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/restarting-my-blog-again-with-astro/</id>
    <title>Restarting My Blog (Again) with Astro</title>
    <updated>2024-07-30T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/restarting-my-blog-again-with-astro/" rel="alternate"/>
    <published>2024-07-30T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;I&apos;ve recently relaunched my personal blog, this time built with the &lt;a href=&quot;https://astro.build/&quot;&gt;Astro framework&lt;/a&gt;. Honestly, I&apos;ve lost count of how many times I&apos;ve started this journey. Each time I attempt a migration or a fresh start, I realize I haven&apos;t kept good records of the previous iterations—the technologies used, the lessons learned, or even the content created often get lost in the shuffle.&lt;/p&gt;
&lt;p&gt;This time, I&apos;m hoping to do things differently. My primary goal is still to create content, but I also plan to document the &quot;why&quot; and &quot;how&quot; behind this specific build.&lt;/p&gt;
&lt;h2&gt;A Brief History of Attempts&lt;/h2&gt;
&lt;p&gt;My interest in having a personal online space dates back to around 2011, largely inspired by Matrix67 (Gu Sen) and his fascinating math blog, which I discovered through Baidu Tieba – Mathematics (like r/math in Chinese). Seeing his work sparked a desire to have my own corner of the internet to share ideas.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;style: {
  fill: transparent
}

direction: right

cms: &quot;Unknown CMS&quot;
ssg: &quot;Static Site Generator&quot;
wp: &quot;WordPress&quot;
astro: &quot;Astro&quot;

cms -&amp;gt; ssg -&amp;gt; wp -&amp;gt; astro
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My first blog used some CMS technology I can barely recall, memorable only because I built a simple Android app to display its content. Like many first projects, it eventually faded due to neglect.&lt;/p&gt;
&lt;p&gt;Later, I explored static site generators (perhaps Jekyll, Hugo, or Hexo – the details are fuzzy) hosted on GitHub Pages, a popular approach at the time. Around 2018, I tried again with WordPress hosted on a Vultr VPS, aiming to focus solely on content. However, that attempt also stalled, leaving behind only a handful of posts visible via the Internet Archive.&lt;/p&gt;
&lt;p&gt;These repeated cycles led to frustration, and I nearly gave up on maintaining a dedicated blog altogether, opting instead for local Markdown notes.&lt;/p&gt;
&lt;h2&gt;Why Astro This Time?&lt;/h2&gt;
&lt;p&gt;Despite resolving to stop tinkering with the technology and focus on writing, the allure of a new tool pulled me back in. In 2024, I discovered Astro. Its philosophy of being &quot;built for content&quot; and its modern approach to building fast, content-focused websites resonated strongly with my goals for a blog.&lt;/p&gt;
&lt;p&gt;It felt like the right fit – a framework designed specifically for use cases like mine, potentially minimizing the unnecessary complexity I&apos;d encountered before while still offering the flexibility I enjoy as a developer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://astro.build/og/astro.jpg&quot; alt=&quot;Astro Logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Instead of just setting up a default theme, I treated this rebuild as a complete frontend project, investing significant time into customizing the setup and understanding Astro&apos;s capabilities.&lt;/p&gt;
&lt;h2&gt;Looking Ahead: Documenting the Build&lt;/h2&gt;
&lt;p&gt;This time, I&apos;m committed not only to writing &lt;em&gt;content&lt;/em&gt; but also to writing &lt;em&gt;about&lt;/em&gt; the process. I plan to publish a series of posts covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technical decisions made during the setup.&lt;/li&gt;
&lt;li&gt;Details of the implementation (theming, features, deployment).&lt;/li&gt;
&lt;li&gt;Challenges encountered and solutions found.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coincidentally, around the time I started this rebuild, I noticed that Matrix67&apos;s blog had also started updating again after a long hiatus. It felt like a small, encouraging sign.&lt;/p&gt;
&lt;p&gt;Hopefully, this renewed effort, coupled with the decision to document the journey, will help me maintain momentum longer this time. Stay tuned for more technical details about the build.&lt;/p&gt;
</content>
    <category term="Blog"/>
    <category term="Astro"/>
    <category term="Web Development"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/echoes-from-an-empty-well/</id>
    <title>Echoes From an Empty Well</title>
    <updated>2022-04-01T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/echoes-from-an-empty-well/" rel="alternate"/>
    <published>2022-04-01T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;The village breathes in the way of old radios left humming in empty rooms—static, half-formed. The banyan tree’s roots buckle the path, and the well’s rim is always damp, even when the sun hangs like a cleaver. The cat arrived when the heat did. Dun-colored, one ear nicked—a jagged moon crescent. I named it &lt;em&gt;缺儿（耳） (Ch&apos;üeh-erh)&lt;/em&gt;—meaning both &quot;the lacking ear&quot; and &quot;the lacking one.&quot; A fragment, incomplete like stories my grandma left unfinished, suspended like dust motes in afternoon light. It drank from puddles, throat bobbing as if swallowing secrets. Mother says the well-water tastes of iron, but I think it tastes of the bicycle bells that rust in Old Chen’s shed, their tongues green and silent.&lt;/p&gt;
&lt;p&gt;Father’s transistor spits numbers—stock prices, rainfall, things that mean nothing here. His shirt sticks to his chair, plastic-coated floral print peeling. At dinner, congee quivers in my bowl. Mother’s voice clicks like a fan rotating: &lt;em&gt;Don’t dawdle by the well. Don’t track mud. Don’t&lt;/em&gt;. The cat won&apos;t be forbidden. It isn’t anything.&lt;/p&gt;
&lt;p&gt;That day, the road softened like wax. The banyan’s leaves curled into fists. I squatted, peeling bluish lichen from the roots, when the sound began—a low whine, like a mosquito trapped in glass. &lt;em&gt;Ch&apos;üeh-erh&lt;/em&gt; sat in the dust, licking its paw. The car came sleek and unmarked, glinting like wet ink. We have trucks here, coughing black phlegm. We have bicycles. Yet it rolled on, in a hurry, wheels turning as if the road itself spooled them forward.&lt;/p&gt;
&lt;p&gt;The cat didn’t flinch.&lt;/p&gt;
&lt;p&gt;A dull crunch, like a melon split open. The car slid past. Where &lt;em&gt;Ch&apos;üeh-erh&lt;/em&gt; had been, only the road remained—blurred, wavering. No blood, no body. Just a heat-shimmer where the dust settled.&lt;/p&gt;
&lt;p&gt;At dinner, the congee trembled. I asked about the cat.&lt;/p&gt;
&lt;p&gt;Father’s chopsticks paused. “What cat?”&lt;/p&gt;
&lt;p&gt;Mother scraped her bowl. “There was a stray. Got hit by the tofu cart, maybe. Years ago.”&lt;/p&gt;
&lt;p&gt;The well’s water, when I peered in, showed only my face—pale, floating. Ripples warped it into something shapeless. That night, the air smelled of burnt wires. The cicadas stopped. Through my window, the banyan shuddered, though there was no wind.&lt;/p&gt;
&lt;p&gt;Days now fold into one another. The cat’s absence is a hole in the heat. I watch the road. Trucks pass, carts clatter, but the sleek car never returns. The well’s dampness creeps into my shoes. Father’s radio mutters numbers. Mother snaps radishes, their flesh white as bone.&lt;/p&gt;
&lt;p&gt;Sometimes, I catch a flicker—a dun shadow, a twitch of tail—but when I turn, it’s only a leaf skittering, or Old Chen’s rag-dog nosing for scraps. The road stays empty. The lichen grows back, bluer each time.&lt;/p&gt;
&lt;p&gt;At night, I count the cracks in the ceiling. They branch like roots. If I stare long enough, they pulse. The village breathes. The well exhales.&lt;/p&gt;
&lt;p&gt;In the morning, my congee cools, skin forming on its surface. A thousand eyes, staring up.&lt;/p&gt;
&lt;p&gt;I stir them under.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;This piece first took shape in 2017, a flicker in the digital dust. Unearthed recently from a forgotten fold in the hard drive, it felt like discovering an old photograph, familiar yet distant. Returning to it wasn&apos;t merely revision; much was stripped away, altered, rearranged, until what remains bears only the faintest echo of its first form. It breathes now as something new.&lt;/p&gt;
&lt;p&gt;Perhaps the intervening years allowed other influences to seep in. Kafka&apos;s shadow has lingered, and a sense of the absurd, of things slightly askew, inevitably permeated the writing. But the story&apos;s most authentic roots burrow into childhood memory. I grew up beside a river in Jiangnan, its water a constant presence just beyond our door. Stray cats arrived, one by one, settling into the periphery of our lives like quiet thoughts. Then, just as quietly, they vanished. The truth, learned later, was simple: my family had taken them across the river, to a place from which return seemed impossible. To my child&apos;s mind, that narrow water and the small bridge spanning it were an absolute threshold, the edge of the known world. Our side was everything. I remember crossing over a few times, searching for them amongst unfamiliar paths, a small, futile pilgrimage. Even then, some of me understood the pointlessness – finding them wouldn&apos;t bridge the distance.&lt;/p&gt;
&lt;p&gt;This story, then, is perhaps a vessel for what settled from that time: the quiet weight of loss, the shape of confusion, the stark lines of boundaries, both seen and unseen, that marked the landscape of childhood. It is that feeling, distilled and reimagined.&lt;/p&gt;
</content>
    <category term="Fiction"/>
    <category term="Novel"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/the-hidden-costs-of-numeronyms-in-tech-communication/</id>
    <title>The Hidden Costs of Numeronyms in Tech Communication</title>
    <updated>2021-10-27T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/the-hidden-costs-of-numeronyms-in-tech-communication/" rel="alternate"/>
    <published>2021-10-27T00:00:00.000Z</published>
    <content type="html">&lt;p&gt;In the fast-paced world of software engineering, we frequently encounter a specific type of abbreviation: the numeronym. Terms like &lt;code&gt;k8s&lt;/code&gt; (Kubernetes) and &lt;code&gt;i18n&lt;/code&gt; (Internationalization) are widely used and understood within certain circles. They work by keeping a word’s first and last letters and replacing the letters in between with their count.&lt;/p&gt;
&lt;p&gt;This practice didn’t arise by accident. In a technical culture that values efficiency and brevity, especially when dealing with long or awkward terms, numeronyms offer an appealing shortcut. They are quick to type, save screen space, and can foster a sense of belonging among those familiar with the jargon. For instance, &lt;code&gt;k8s&lt;/code&gt; is much shorter than &lt;code&gt;Kubernetes&lt;/code&gt;, and its pronunciation (&lt;code&gt;kayts&lt;/code&gt;) arguably has a subtle connection to the original word, perhaps aiding its adoption.&lt;/p&gt;
&lt;p&gt;However, when we look closer, the perceived convenience of numeronyms often comes with significant hidden costs, potentially outweighing the benefits.&lt;/p&gt;
&lt;h2&gt;Cognitive Load and Communication Ambiguity&lt;/h2&gt;
&lt;p&gt;The most immediate problem with numeronyms is the extra cognitive load they place on the reader. Unlike full words or well-established initialisms (like API for Application Programming Interface), numeronyms carry no inherent semantic meaning. A reader must perform an extra decoding step: recognize it as a numeronym, then recall or look up the original term it represents.&lt;/p&gt;
&lt;p&gt;This decoding might become automatic for industry insiders for highly popular examples like &lt;code&gt;k8s&lt;/code&gt; and &lt;code&gt;i18n&lt;/code&gt;. But the tech landscape constantly evolves, bringing forth a steady stream of new concepts and tools and less common numeronyms. Consider examples like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;a11y&lt;/strong&gt; (Accessibility)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;l10n&lt;/strong&gt; (Localization)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;i14y&lt;/strong&gt; (Interoperability)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;m12n&lt;/strong&gt; (Modularization)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Encountering these less familiar numeronyms often forces even experienced engineers to pause, think, or search, disrupting reading flow and reducing communication efficiency. This ambiguity can easily lead to misunderstandings, especially in documentation, code comments, or cross-team communication.&lt;/p&gt;
&lt;h2&gt;The Double-Edged Sword of Jargon&lt;/h2&gt;
&lt;p&gt;Like other forms of industry jargon, numeronyms can speed up communication &lt;em&gt;within&lt;/em&gt; a group that shares the same context and vocabulary. However, they inherently create barriers for others. For newcomers, collaborators from different domains, or even senior developers unfamiliar with a specific technology, these “trendy” abbreviations act more like hurdles than helpful shortcuts.&lt;/p&gt;
&lt;p&gt;While not everyone using numeronyms intends to gatekeep or show off, the objective effect can be restricted information flow and the formation of information silos. In a modern tech environment emphasizing collaboration, knowledge sharing, and inclusivity, practices that unintentionally raise cognitive barriers deserve careful consideration.&lt;/p&gt;
&lt;h2&gt;The Irony of “a11y”&lt;/h2&gt;
&lt;p&gt;The numeronym &lt;code&gt;a11y&lt;/code&gt; warrants special attention because it perfectly encapsulates the contradictions of this practice. It stands for &lt;strong&gt;Accessibility&lt;/strong&gt;, a core principle focused on making products and information usable and understandable by the broadest possible audience, including people with disabilities.&lt;/p&gt;
&lt;p&gt;Yet, the abbreviation &lt;code&gt;a11y&lt;/code&gt; itself violates fundamental principles of accessibility:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Not Easily Recognizable:&lt;/strong&gt; It’s opaque to those unfamiliar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unintuitive Pronunciation:&lt;/strong&gt; Often pronounced as &lt;code&gt;/æli/&lt;/code&gt; (like “alley”) or &lt;code&gt;/əˈlɛvənˌwaɪ/&lt;/code&gt; (literally “eleven why”). The latter, sounding out the three syllables of “eleven,” is arguably longer and more complex than the “accessibility” part of the original word, defeating the purpose of abbreviation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lack of Universality:&lt;/strong&gt; It’s far less widely known than &lt;code&gt;k8s&lt;/code&gt; or &lt;code&gt;i18n&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;An abbreviation representing the concept of “easy access” is itself difficult to access, pronounce, and remember – a profound irony. It is a stark reminder that conciseness should not come at the expense of clarity and inclusion.&lt;/p&gt;
&lt;h2&gt;Conclusion: Prioritize Clarity&lt;/h2&gt;
&lt;p&gt;Numeronyms exist as a cultural phenomenon within the tech community. They are born from certain needs and have some value in specific contexts. We don’t need to completely ban established terms like &lt;code&gt;k8s&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, we should use numeronyms cautiously in broader communication scenarios – especially official documentation, tutorials, public announcements, and any situation where clarity is paramount. Opting for the whole, unambiguous term might require a few extra keystrokes, but it pays dividends in lower cognitive load, reduced ambiguity, and better comprehension for a wider audience.&lt;/p&gt;
&lt;p&gt;Clear and precise communication is fundamental to collaboration and innovation in technology. As we strive for efficiency, let’s remain mindful of effectiveness and inclusivity, ensuring our “shortcuts” don’t become roadblocks for others.&lt;/p&gt;
</content>
    <category term="Technical Writing"/>
    <category term="Numeronym"/>
    <category term="Accessibility"/>
    <category term="Essay"/>
  </entry>
  <entry>
    <id>https://eigenigma.io/en/articles/how-to-install-archlinux-on-a-virtual-machine/</id>
    <title>How to Install ArchLinux on a Virtual Machine</title>
    <updated>2018-06-20T00:00:00.000Z</updated>
    <link href="https://eigenigma.io/en/articles/how-to-install-archlinux-on-a-virtual-machine/" rel="alternate"/>
    <published>2018-06-20T00:00:00.000Z</published>
    <content type="html">&lt;h2&gt;Preface&lt;/h2&gt;
&lt;p&gt;Many people mention that their first reaction to ArchLinux is the difficulty of installation. There are several reasons for this difficulty, which we will address next.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No graphic user interface: &lt;code&gt;ArchLinux&lt;/code&gt; only provides a minimal environment. All of the install operations should be completed in the command line. This is the most difficult to overcome for many people who do not have experience in command line operations. Many Linux distributions can be popular because they provide user-friendly and streamlined installation processes. This has helped many people solve the first step in learning &lt;code&gt;Linux&lt;/code&gt;: Installing a &lt;code&gt;Linux&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Insufficient prior knowledge and lack the ability to find and solve problems: No exposure to knowledge that is important to installing operating systems, such as system booting, configuration file editing, and command-line operations. Once the results of their operation do not match the tutorial, they do not know how to solve the problem.&lt;/li&gt;
&lt;li&gt;Lack of proper tutorials: The best and most complete tutorial for installing &lt;code&gt;ArchLinux&lt;/code&gt; is the official &lt;a href=&quot;https://wiki.archlinux.org/index.php/installation_guide&quot;&gt;Installation guide&lt;/a&gt; and &lt;a href=&quot;https://wiki.archlinux.org/&quot;&gt;Wiki&lt;/a&gt;. Since the official Wiki is written in a way similar to the documentation, there is no chronological step-by-step installation process that we are used to, causing difficulties for people who are unfamiliar with this type of writing in reading and using. Other tutorials are often outdated or do not take into account the reader’s understanding of &lt;code&gt;Linux&lt;/code&gt;, ignoring some of the newcomers who are prone to making mistakes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;This tutorial is designed to be consistent with the existing official &lt;a href=&quot;https://wiki.archlinux.org/&quot;&gt;Wiki&lt;/a&gt; and includes links that are suitable for beginners to learn. I hope that anyone who has read this tutorial will be able to improve their ability to learn existing and future new official Wiki content.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The advantages of &lt;code&gt;ArchLinux&lt;/code&gt; or &lt;code&gt;Linux&lt;/code&gt; are not mentioned here. I betlieve that people who will review this tutorial must have already understood it. And you can get a lot from this process.&lt;/p&gt;
&lt;h3&gt;Precautions&lt;/h3&gt;
&lt;p&gt;Be sure to read each instruction in the tutorial carefully. Most of the installation failures were caused by not reading the tutorial carefully, missing or misdirecting instructions. Be sure to understand the contents of the tutorial, not just enter the command without understanding the description.&lt;/p&gt;
&lt;h2&gt;Installation Tutorial&lt;/h2&gt;
&lt;h3&gt;Pre-installation&lt;/h3&gt;
&lt;p&gt;The installation media and their GnuPG signatures can be acquired from the &lt;a href=&quot;https://archlinux.org/download/&quot;&gt;Download&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;Choose a mirror site locates in your region, then download the &lt;code&gt;iso&lt;/code&gt; image file looks like &lt;code&gt;archlinux-**-x86_64.iso&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Verify Signature&lt;/h4&gt;
&lt;p&gt;It is recommended to verify the image signature before use, especially when downloading from an &lt;em&gt;HTTP mirror&lt;/em&gt;, where downloads are generally prone to be intercepted to serve malicious images.&lt;/p&gt;
&lt;p&gt;If you are using a &lt;code&gt;windows&lt;/code&gt; operating system with &lt;code&gt;7-zip&lt;/code&gt; installed, right-click on the downloaded &lt;code&gt;iso&lt;/code&gt; image file. Choose &lt;code&gt;CRC SHA&lt;/code&gt;&amp;gt;&lt;code&gt;SHA1&lt;/code&gt;, then you can see the signature of the image. If the signature does not match what is displayed on the &lt;a href=&quot;https://archlinux.org/download/&quot;&gt;Download&lt;/a&gt; page, please re-download the image file from the correct URL.&lt;/p&gt;
&lt;h4&gt;Create a Virtual Machine&lt;/h4&gt;
&lt;p&gt;Before creating the virtual machine, enter the &lt;code&gt;BIOS&lt;/code&gt;. If the CPU of your physical machine is a Intel CPU, enable &lt;code&gt;Intel VT&lt;/code&gt;. Or the CPU is an AMD CPU, enable &lt;code&gt;AMD-V&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Reboot the system and open VMware Workstation. Click on &lt;code&gt;Create a New Virtual Machine&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;I will install the operating system later.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;Guest operating system&lt;/code&gt; option, choose &lt;code&gt;Linux&lt;/code&gt;. Select the version &lt;code&gt;Other Linux 5.x or later kernel 64-bit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Resize the &lt;code&gt;Maximum disk size&lt;/code&gt; and choose &lt;code&gt;Store virtual disk as a single file&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Customize Hardware...&lt;/code&gt;. Enter the appropriate value of memory and processors for this virtual machine.&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;New CD/DVD (IDE)&lt;/code&gt; connection. Choose &lt;code&gt;Use ISO image file:&lt;/code&gt;. Click on &lt;code&gt;Browse...&lt;/code&gt; to open the path of the image file and select it.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;Virtualization engine&lt;/code&gt;, check the three boxes &lt;code&gt;Virtualize Intel VT-x/EPT or AMD-V/RVI&lt;/code&gt;, &lt;code&gt;Virtualize CPU performance counters&lt;/code&gt; and &lt;code&gt;Virtualize IOMMU (IO memory management unit)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to install a user graphic interface after installing &lt;code&gt;ArchLinux&lt;/code&gt;, it is recommended to check the box &lt;code&gt;Accelerate 3D graphics&lt;/code&gt; in &lt;code&gt;Display&lt;/code&gt;-&lt;code&gt;3D graphics&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Boot the Live Environment&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click on &lt;code&gt;Power on this virtual machine&lt;/code&gt;. After startup, you will see the following interface.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/boot-interface.png&quot; alt=&quot;ArchLinux boot interface&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you do not see this interface, please check the integrity of the &lt;code&gt;iso&lt;/code&gt; image file.&lt;/p&gt;
&lt;p&gt;Select the first option &lt;code&gt;Boot Arch Linux (x86_64)&lt;/code&gt; and press &lt;code&gt;Enter&lt;/code&gt; to enter the installation environment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point, ArchLinux starts loading and you will see the screen showing the following.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/boot-loading.png&quot; alt=&quot;ArchLinux boot loading screen&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After the loading is complete, you will enter an interface with a command line.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/command-line-interface.png&quot; alt=&quot;ArchLinux command line interface after boot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;FAIL&lt;/code&gt; or other error message appears and it cannot be started, please search for the error message to get the solution.&lt;/p&gt;
&lt;p&gt;If you are unfamiliar with the common operations of the &lt;code&gt;Linux&lt;/code&gt; command line, please learn some of the common shortcuts or commands below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Tab&lt;/code&gt;: Command-line auto-completion. Type the first few characters of the command or file name and press &lt;code&gt;Tab&lt;/code&gt;, it will automatically complete the command or display all commands that match the characters you type.
&lt;code&gt;↑&lt;/code&gt; or &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;p&lt;/code&gt;: Display the previous command.
&lt;code&gt;↓&lt;/code&gt; or &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;n&lt;/code&gt;: Display the next command.
&lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;c&lt;/code&gt;: Terminate the currently executing command.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For other commonly used shortcuts, you can view &lt;a href=&quot;https://www.marksanborn.net/linux/10-linux-shortcuts-you-cant-live-without/&quot;&gt;10 Linux Shortcuts You Can’t Live Without&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Set the Keyboard Layout (Optional)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;If your current keyboard layout is US, you can skip this step.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The default console keymap of &lt;code&gt;ArchLinux&lt;/code&gt; is US. Available layouts can be listed with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls /usr/share/kbd/keymaps/**/*.map.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To modify the layout, append a corresponding file name to &lt;a href=&quot;https://man.archlinux.org/man/loadkeys.1&quot;&gt;loadkeys&lt;/a&gt;, omitting path and file extension. For example, to set a German keyboard layout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;loadkeys de-latin1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Console fonts are located in &lt;code&gt;/usr/share/kbd/consolefonts/&lt;/code&gt; and can likewise be set with &lt;a href=&quot;https://man.archlinux.org/man/setfont.8&quot;&gt;setfont&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Verify the Boot Mode&lt;/h4&gt;
&lt;p&gt;In fact, the default boot mode provided by VMware Workstation is &lt;code&gt;BIOS&lt;/code&gt;+&lt;code&gt;MBR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enter the following command, you can see that there is no partition whose &lt;code&gt;Type&lt;/code&gt; is &lt;code&gt;EFI System&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fdisk -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/fdisk-list.png&quot; alt=&quot;fdisk -l output showing disk partitions&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Connect to the Internet&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Archlinux&lt;/code&gt; cannot be installed offline. We need to download the required components online. Therefore, next we have to connect to the Internet.&lt;/p&gt;
&lt;p&gt;Make sure your physical machine is connected to the Internet.&lt;/p&gt;
&lt;p&gt;Enter and execute the following command to obtain the IP address.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dhcpcd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter and execute the following command to determine if the network connection is working.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ping www.google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you can see something like the following, it means that &lt;code&gt;ArchLinux&lt;/code&gt; is connected to the Internet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/ping-test.png&quot; alt=&quot;Successful ping test confirming network connectivity&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Use the shortcut &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;c&lt;/code&gt; to terminate the currently executing command.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Update the System Clock&lt;/h4&gt;
&lt;p&gt;Use &lt;code&gt;timedatectl&lt;/code&gt; to ensure the system clock is accurate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;timedatectl set-ntp true
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Under normal circumstances, this command does not output anything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Part the Disks&lt;/h4&gt;
&lt;p&gt;When recognized by the live system, the virtual disk is assigned to a block device &lt;code&gt;/dev/sda&lt;/code&gt;. Enter and execute the following command to view current partition status.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fdisk -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The partition status has been shown previously.&lt;/p&gt;
&lt;p&gt;Enter and execute the following command to create a &lt;strong&gt;root partition&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fdisk /dev/sda
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;code&gt;o&lt;/code&gt; to create a new &lt;code&gt;MBR&lt;/code&gt; partition table.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;code&gt;n&lt;/code&gt; to create a new partition.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First select the partition type, press &lt;code&gt;Enter&lt;/code&gt; to select the default option &lt;code&gt;primary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then you need to assign a partition number to the new partition. Press &lt;code&gt;Enter&lt;/code&gt; to use the default partition number &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the first sector. Under normal circumstances, you can press &lt;code&gt;Enter&lt;/code&gt; directly to use the default value.&lt;/li&gt;
&lt;li&gt;Select the last sector. Enter the value of the last sector and press &lt;code&gt;Enter&lt;/code&gt;. If you want to make the newly created partition completely occupy the free disk space, you can directly press &lt;code&gt;Enter&lt;/code&gt; to use the default value of the last sector.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;code&gt;p&lt;/code&gt; to view the newly created partition. You can see something like the following.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/fdisk-new-partition.png&quot; alt=&quot;fdisk showing newly created partition&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;code&gt;w&lt;/code&gt; to write all previous operations to the disk.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/fdisk-write.png&quot; alt=&quot;fdisk writing partition table to disk&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the partitions have been created, each must be formatted with an appropriate file system. Enter and execute the following command to format the root partition you just created.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;mkfs.ext4 /dev/sda1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/mkfs-ext4.png&quot; alt=&quot;Formatting partition with mkfs.ext4&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Mount the File Systems&lt;/h4&gt;
&lt;p&gt;Enter and execute the following command to mount the file system on the root partition to &lt;code&gt;/mnt&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mount /dev/sda1 /mnt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;From this step, we need to do some file editing operations. So you need to master the basic operations of a well-known command-line editor &lt;code&gt;Vim&lt;/code&gt;. It is recommended to learn from &lt;a href=&quot;https://www.openvim.com/&quot;&gt;Interactive Vim Tutorial&lt;/a&gt;. You just need to know how to edit, copy, paste and save a file with &lt;code&gt;Vim&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Select the Mirrors&lt;/h4&gt;
&lt;p&gt;The &lt;em&gt;mirrors&lt;/em&gt; are the sources of the package we downloaded. We need to select different mirrors according to our region to speed up the download.&lt;/p&gt;
&lt;p&gt;Enter and execute the following command to use &lt;code&gt;vim&lt;/code&gt; to edit the file &lt;code&gt;/etc/pacman.d/mirrorlist&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/pacman.d/mirrorlist
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: When entering the path, you can use &lt;code&gt;Tab&lt;/code&gt; to auto-complete it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The higher a mirror is placed in the list, the more priority it is given when downloading a package. You need to move the geographically closest mirrors to the top of the list.&lt;/p&gt;
&lt;p&gt;For example, use &lt;code&gt;:/China&lt;/code&gt; to find the mirrors under the string &lt;code&gt;China&lt;/code&gt;. Press &lt;code&gt;dd&lt;/code&gt; in &lt;code&gt;NORMAL&lt;/code&gt; mode to cut the line under the cursor. Press &lt;code&gt;gg&lt;/code&gt; to return to the beginning of the file. Press &lt;code&gt;P&lt;/code&gt; (uppercase) to paste the line to the front of the file.&lt;/p&gt;
&lt;p&gt;Manually input Zhengjiang University Mirror after Tsinghua University Mirror pasted before.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## China
Server = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
Server = http://mirrors.zju.edu.cn/archlinux/$repo/os/$arch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Input &lt;code&gt;:wq&lt;/code&gt; to save and quit the file.&lt;/p&gt;
&lt;h4&gt;Install Essential Packages&lt;/h4&gt;
&lt;p&gt;The next step is to install the base &lt;code&gt;ArchLinux&lt;/code&gt; package to the disk. This is an online download and installation process. Use the &lt;code&gt;pacstrap&lt;/code&gt; script to install the base package, &lt;code&gt;Linux&lt;/code&gt; kernel and firmware for common hardware.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacstrap /mnt base base-devel linux dhcpcd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the download speed, you need to wait for a while here. When the command prompt reappears, you can proceed to the next step.&lt;/p&gt;
&lt;h4&gt;Configure the System&lt;/h4&gt;
&lt;p&gt;After installing the base package of &lt;code&gt;ArchLinux&lt;/code&gt;, we need to configure the system.&lt;/p&gt;
&lt;h5&gt;Fstab&lt;/h5&gt;
&lt;p&gt;Enter and execute the following command to generate an fstab file which is used to mount partitions automatically.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;genfstab -L /mnt &amp;gt;&amp;gt; /mnt/etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this step is important, we need to output the generated file to check if it is correct. Enter and execute the following command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /mnt/etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that &lt;code&gt;/dev/sda1&lt;/code&gt; is mounted to the root partition.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/fstab-output.png&quot; alt=&quot;Generated fstab showing sda1 mounted to root&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Chroot&lt;/h5&gt;
&lt;p&gt;Change root into the new system. &lt;code&gt;chroot&lt;/code&gt; means change root, which is equivalent to handing over the control to the newly installed &lt;code&gt;ArchLinux&lt;/code&gt; system. &lt;strong&gt;After performing this step, our operations are performed on the newly installed system on the disk.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Enter and execute the following command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;arch-chroot /mnt
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Time Zone&lt;/h5&gt;
&lt;p&gt;Enter and execute the following command to set the time zone to your region.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, we can set the time zone to &lt;code&gt;Shanghai&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;hwclock&lt;/code&gt; to generate &lt;code&gt;/etc/adjtime&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hwclock --systohc
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Install Required Package&lt;/h5&gt;
&lt;p&gt;Because we have &lt;code&gt;chroot&lt;/code&gt; to the new system, there are only some of the most basic packages. At this time, we need to install some required packages ourselves. Then this tutorial will introduce &lt;code&gt;ArchLinux&lt;/code&gt;’s package management tool &lt;code&gt;pacman&lt;/code&gt;. In most cases, a single line of commands can solve the problem of packages and their dependencies.&lt;/p&gt;
&lt;p&gt;The command format of installing new packages is &lt;code&gt;pacman -S packagename&lt;/code&gt;. &lt;code&gt;Pacman&lt;/code&gt; will automatically check the other packages (dependencies) needed for this package and install them together. Below we will install some packages through &lt;code&gt;pacman&lt;/code&gt;. These packages will be used later.&lt;/p&gt;
&lt;p&gt;Enter and execute the following command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -S vim dialog wpa_supplicant networkmanager
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Localization&lt;/h5&gt;
&lt;p&gt;Set the languages we use. Use &lt;code&gt;Vim&lt;/code&gt; to edit &lt;code&gt;/etc/locale.gen&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/locale.gen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uncomment &lt;code&gt;en_US.UTF-8 UTF-8&lt;/code&gt; and other needed locales in /etc/locale.gen, and generate them with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;locale-gen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, we can use &lt;code&gt;:/&lt;/code&gt; to find &lt;code&gt;zh_CN.UTF-8 UTF-8&lt;/code&gt;, &lt;code&gt;zh_HK.UTF-8 UTF-8&lt;/code&gt;, &lt;code&gt;zh_TW.UTF-8 UTF-8&lt;/code&gt; and &lt;code&gt;en_US.UTF-8 UTF-8&lt;/code&gt; then delete the &lt;code&gt;#&lt;/code&gt; before them.&lt;/p&gt;
&lt;p&gt;Create (when the file does not exist, &lt;code&gt;Vim&lt;/code&gt; will create a file with the same name) the &lt;code&gt;locale.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/locale.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following to the first line of the file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LANG=en_US.UTF-8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Input &lt;code&gt;:wq&lt;/code&gt; to save and quit the file.&lt;/p&gt;
&lt;h5&gt;Network Configuration&lt;/h5&gt;
&lt;p&gt;Create the &lt;code&gt;hostname&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/hostname
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter your own &lt;em&gt;myhostname&lt;/em&gt; in the first line of the file.&lt;/p&gt;
&lt;p&gt;Input &lt;code&gt;:wq&lt;/code&gt; to save and quit the file.&lt;/p&gt;
&lt;p&gt;Edit the &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following at the end of the file (replace &lt;em&gt;myhostname&lt;/em&gt; with your own &lt;em&gt;hostname&lt;/em&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;127.0.0.1 localhost
::1 localhost
127.0.1.1 &lt;em&gt;myhostname&lt;/em&gt;.localdomain &lt;em&gt;myhostname&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Input &lt;code&gt;:wq&lt;/code&gt; to save and quit the file.&lt;/p&gt;
&lt;h5&gt;Root Password&lt;/h5&gt;
&lt;p&gt;Set the root password.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;passwd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type and retype your root password.&lt;/p&gt;
&lt;h5&gt;BootLoader&lt;/h5&gt;
&lt;p&gt;Choose and install a &lt;code&gt;Linux&lt;/code&gt;-capable boot loader. Here we install the most popular boot loader &lt;code&gt;Grub2&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First install &lt;code&gt;os-prober&lt;/code&gt; and &lt;code&gt;ntfs-3g&lt;/code&gt;, which can be used with &lt;code&gt;Grub&lt;/code&gt; to detect existing systems and automatically set startup options.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -S os-prober ntfs-3g
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;grub&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pacman -S grub
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy &lt;code&gt;grub&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grub-install --target=i386-pc /dev/sda
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the configuration file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grub-mkconfig -o /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/grub-install.png&quot; alt=&quot;GRUB installation and config generation output&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you do not see the message as shown, please double-check that the above process is completed correctly.&lt;/strong&gt;&lt;/p&gt;
&lt;h5&gt;BootLoader Check&lt;/h5&gt;
&lt;p&gt;Use the following command to check whether the entry of the system is successfully generated. If it is not generated normally, there will be no system entry when booting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check if there is an &lt;code&gt;ArchLinux&lt;/code&gt; entry near the end of the menuentry section. The following figure shows the detected &lt;code&gt;ArchLinux&lt;/code&gt; entry.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img.eigenigma.io/how-to-install-archlinux-on-a-virtual-machine/grub-menuentry.png&quot; alt=&quot;GRUB config showing ArchLinux menuentry&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you do not see &lt;code&gt;ArchLinux&lt;/code&gt; entry or the file &lt;code&gt;grub.cfg&lt;/code&gt; does not exist, please check the directory &lt;code&gt;/boot&lt;/code&gt; first.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;See if there are files &lt;code&gt;initramfs-linux-fallback.img&lt;/code&gt; &lt;code&gt;initramfs-linux.img&lt;/code&gt; &lt;code&gt;intel-ucode.img&lt;/code&gt; and &lt;code&gt;vmlinuz-linux&lt;/code&gt;. If not, the &lt;code&gt;Linux&lt;/code&gt; kernel is not deployed properly. This is most likely caused by the &lt;code&gt;/boot&lt;/code&gt; directory not being properly mounted. After confirming that the &lt;code&gt;/boot&lt;/code&gt; directory is correct, you can redeploy the &lt;code&gt;Linux&lt;/code&gt; kernel.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Pacman -S linux
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then regenerate the configuration file &lt;code&gt;grub.cfg&lt;/code&gt; and try to find the system entry.&lt;/p&gt;
&lt;h4&gt;Reboot&lt;/h4&gt;
&lt;p&gt;Exit the &lt;code&gt;chroot&lt;/code&gt; environment by typing &lt;code&gt;exit&lt;/code&gt; or pressing &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;d&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, restart the machine by typing &lt;code&gt;reboot&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Click on &lt;code&gt;Edit virtual machine settings&lt;/code&gt;. Change the &lt;code&gt;CD/DVD (IDE)&lt;/code&gt; connection. Choose &lt;code&gt;Use physical drive: Auto detect&lt;/code&gt;. Now you have removed the installation media and then you can login into the new system with the root account.&lt;/p&gt;
&lt;h3&gt;Post-installation&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://wiki.archlinux.org/index.php/General_recommendations&quot;&gt;General recommendations&lt;/a&gt; for system management directions and post-installation tutorials (like setting up a graphical user interface, sound or a touchpad).&lt;/p&gt;
&lt;p&gt;For a list of applications that may be of interest, see &lt;a href=&quot;https://wiki.archlinux.org/index.php/List_of_applications&quot;&gt;List of applications&lt;/a&gt;.&lt;/p&gt;
</content>
    <category term="Linux"/>
    <category term="Tutorial"/>
    <category term="Arch Linux"/>
  </entry>
</feed>