Skip to content

An agent in the tissue: a spreading perturbation of the auxin machinery - #39

Merged
aj-dev-smith merged 13 commits into
mainfrom
agent-in-the-tissue
Aug 3, 2026
Merged

An agent in the tissue: a spreading perturbation of the auxin machinery#39
aj-dev-smith merged 13 commits into
mainfrom
agent-in-the-tissue

Conversation

@aj-dev-smith

@aj-dev-smith aj-dev-smith commented Aug 2, 2026

Copy link
Copy Markdown
Owner

An agent in the tissue

Something that arrives after the plant has started, spreads through it, and deforms the auxin machinery it finds. From a thread on the Twitter post suggesting "auxin deforming viruses/proteins injected into plants".

This adds nothing to SCIENCE.md's "what is imposed". An agent is the same category as 37_wind.js and 39_fall.js — a thing the plant is subject to, not chemistry the plant does and not a shape anybody drew. Nothing in 15_pathogen.js says what an infected plant looks like. It says what the agent does to three numbers per cell, and stepAuxin decides the rest.

The spread term is the point

dv/dt = r v (1-v) - clr v + sum_j Dv w (v_j - v) + chi * upwind(J, v)

J is the realised auxin transport on every wall, which the solver already computes every step. An agent that rides it needs no spread geometry at all — where an infection goes is decided by the plant's own polarity field, for the same reason a vein is.

Pre-flight first: test/pathogen.mjs

An invasion front has a closed-form speed, so the number is worked out before the solver is asked for it. It caught three of my own errors and no bugs in the code.

Asserted sqrt(2) scaling that is the continuum limit lattice ratios are x1.367 / x1.491, matched to 0.02–0.06%
Read advection as a shift of c* upwind enters as adv*(e^L - 1), not adv*L predictions were 19–29% low until fixed
Detected invasion above titre 0.5 tissue settles at the endemic level 1 - clr/r an R0 = 1.29 agent had invaded to 0.222 and read as failure

Front speeds land 1.2–2.1% under the discrete closed form in every case — the sign and size of the Bramson log correction. Invasion has a real threshold at R0 = r/clr with nothing scheduling it, same category as Plant.spent().

The result: test/infected.mjs section 5

Inoculate a meristem at its centre and count what it founds.

agent organs live divergence aMean burden
(control) 167 4 94.7° 1.31 0.00
blind (dComp -1) 103 1 97.2° 0.88 0.69
invert (dComp -2) 56 0 108.3° 1.57 0.67
gall (dRho +2.4) 149 1 71.4° 2.13 0.66
chlorosis (dMu +1.6) 61 1 120.3° 0.50 0.45

Organ count is the column to read — founding an organ is sharpening an auxin maximum, exactly the operation comp gates. Inverted polarity takes it 167 → 56 with no live primordia left. Divergence is not evidence of anything here; it wanders 90–160° uninfected.

comp < 0 is the interesting one

Below zero the PIN blend stops being a pull toward uniform and becomes a reflection through it: PIN goes to the wall the gradient rule would have avoided. That is not a sicker plant, it is inverted polarity — the only perturbation here that reaches a body plan the parameter space does not otherwise contain.

It costs one clamp in stepAuxin. For comp in [0,1] the weight is a convex combination of two non-negative numbers, so the branch is unreachable for every shipped species. Asserted on real tissue, not on paper.

Two metric traps caught, both worth reading

  • veinMax is a cap, not a count. The first pass reported L.veins.length and read a flat 260 across control, saturated and everything between as "no effect". Recounted uncapped: 410 → 628 canals (+53%) for the gall. Same trap as test/venation.mjs's unreachable maximum.
  • PIN conservation cannot be measured on a grown field. p updates at the end of the same call that allocated P, so sum(P)/p is one step stale for every cell; comparing runs measures how two auxin trajectories diverged. It read 1.4e-3 with the inverted cells nearer 1.0 than the clean ones — not even the sign the failure mode gives. Checked on one isolated allocation instead: error exactly 0.

Known gap, written into the file header rather than papered over

On a blade every agent saturates to burden 1.00 — the front crosses a 44-hop lattice long before the blade matures — so those rows are a global parameter change, not a lesion. Derived the target regime (a one-third lesion needs Dv*rho ~ 3.8e-2) and swept it: Dv 0.004 / r 0.85 gives burden 0.32. Not shipped in a preset yet.

Checks

smoke 73/73 · views 23/23 · pathogen green · infected green · build clean.

The two new harnesses assert and exit non-zero. Neither is wired into CI yet — deliberately, pending the docs pass.

🤖 Generated with Claude Code


Update: the whole specimen, and a falsification

Plant.inoculate(name) puts an agent in one growing point. Cross-axis spread is inheritance at bud formation — a bud is made of its parent's tissue — and deliberately not transport, since 38_shoot.js ships disabled. That gives a severity gradient down the specimen out of a rule that mentions neither severity nor position.

agent axes organs Δ organs
(control) 6 117
gall (dRho +2.4) 6 125 +6.8%
leafygall (dComp +0.85) 6 107 −8.5%
chlorosis (dMu +1.6) 6 63 −46.2%
blind (dComp −1) 3 22 −81.2%
invert (dComp −2) 3 10 −91.5%
lesion 6 115 −1.7%

The literature's level-vs-placement A/B does not reproduce

The brief's finding 4: Agrobacterium installs an auxin source → undifferentiated blob; Rhodococcus changes where maxima form → organised iterated shoots. The difference looked exactly like our rho-vs-comp axis, so the prediction was dComp > 0more organs.

The rho half works (+6.8%). The comp half is the wrong sign (−8.5%), and sweeping it is non-monotonic and seed-fragile — noise, not a mechanism.

Why, and it names the right variable: comp gates how sharply a cell may polarise, not whether a region is an organ-founding domain. Raising it uniformly over-sharpens existing maxima, making them stronger sinks, which suppresses neighbours harder. It sharpens domains; it does not create them. The variable that sets where a competent region is, is rCZ — the central-zone radius, and the one spatial prior SCIENCE.md books. Untried, and the obvious next experiment.

leafygall stays in the table labelled falsified, on the rhoI: 0 / fluxPartition principle.

Caution I've written beside that table

The +6.8% → −91.5% span is six different mechanisms on one seed and one species, not a calibrated dial. The only two claims predicted in advance and surviving are structural: comp agents act on meristems and not blades, and inverting polarity stops organ founding because founding an organ is the operation comp gates.

⚠ Not yet watched in a browser

Every number in this PR is headless. This project's record is that three of its worst modelling errors were caught by a person looking rather than by a harness, and "does an infected plant read as diseased or as a broken renderer" is exactly the question no harness here can answer. app.plant.inoculate('lesion') is reachable from the console.

Docs

SCIENCE.md (nothing added to the imposed list; clampK booked as a new stated constant; the boundary correction), JOURNAL.md (full entry + the falsification), PITFALLS.md (three new entries, all of which read as "no effect" and none of which were), TUNING.md (written in the fall's register, since most of these knobs are lookups rather than dials), CLAUDE.md.


Final state, and the honest headline

It works, and there is not much to see. Watched in a real browser, an infected plant reads as stunting, not disease — and lesion, the agent built specifically to be a visible bounded lesion (burden 0.36, with an edge in the ASCII), is indistinguishable from a healthy plant on screen.

That is the fourth time on this project that a person looking has been the deciding instrument, and it's the only verdict that counts. CLAUDE.md and ROADMAP now lead with it and say plainly: do not reopen this expecting a quick visual win.

The diagnosis is specific and it is not the mechanism. F.vir is a per-cell scalar the renderer never reads. The only channels an agent can reach a viewer through are organ count, placement and axis length — all of which say "less vigorous", none of which say "infected". The one thing worth doing first, if this is picked up again, is drawing the titre: ROADMAP 0z's own argument that a computed-but-undrawn channel is a view waiting to happen.

What's here and working

  • src/15_pathogen.js — the agent, its dynamics, and a table of seven
  • test/pathogen.mjs — the derivation, against exact discrete closed forms
  • test/infected.mjs — real tissue, measured and drawn
  • tools/infect_shot.mjs — the browser capture that produced the verdict
  • UI: agent picker with inject / fresh + inject, plus agent and burden rows on the specimen card
  • docs/research_8_02_26_pathogen.md — the literature sweep, flagged [D]/[I]/[OURS]/

Results worth keeping regardless

  • A window of susceptibility nobody scheduled — 8/8 inoculations take at step 200, 0/8 at step 1000, because a meristem makes clean cells faster than a late agent can take them. Two independent clocks, and the disease only exists where they overlap.
  • comp < 0 is inverted polarity, and it costs one clamp that is provably a no-op for every shipped species. 167 → 56 organs on a meristem.
  • Three falsifications, all recorded with their numbers: the brief's self-limiting-front claim, the literature's level-vs-placement A/B, and my own sqrt(2) scaling assumption.
  • Three lying instruments and one phantom bug, all in PITFALLS: veinMax is a cap not a count; sum(P)/p is one step stale; comp is inert in flux mode; and a run-to-run difference is not a platform difference.

Nothing was added to SCIENCE.md's imposed list, and one item came off it — the arrival coordinate is now sampled by developmental state rather than stated.

aj-dev-smith and others added 13 commits August 2, 2026 07:50
Adds src/15_pathogen.js — something that arrives after the plant has started,
spreads through it, and deforms the auxin machinery it finds. Same category as
37_wind.js and 39_fall.js: a thing the plant is SUBJECT TO, not a shape anybody
drew. Nothing here says what an infected plant looks like; it says what the
agent does to three numbers per cell and lets stepAuxin decide the rest.

The spread term is the point. `J` — the realised auxin transport on every wall —
is already computed every step, so an agent that rides it needs no spread
geometry at all. Where an infection goes is decided by the plant's own polarity.

Two changes to 10_auxin.js:
  - CellField gains `vir`, handled in add/remove alongside `inh`.
  - stepAuxin gains ONE clamp so PIN weights can survive comp < 0. For comp in
    [0,1] the weight is a convex combination of two non-negative numbers, so the
    branch is unreachable for every shipped species. smoke.mjs's 73 checks pass
    unchanged, and test/pathogen.mjs section 4 asserts the premise on real
    tissue rather than on paper.

comp < 0 is the interesting one: below zero the blend stops being a pull toward
uniform and becomes a reflection through it — PIN goes to the wall the gradient
rule would have avoided. That is not a sicker plant, it is inverted polarity.

test/pathogen.mjs is the pre-flight, and it is derivation-first: an invasion
front has a closed-form speed, so the number is worked out before the solver is
asked for it. It caught three of my own errors and no bugs in the code:

  - Asserting sqrt(2) scaling. That is the CONTINUUM limit; on a lattice the
    ratio is whatever the discrete dispersion relation says (x1.367 / x1.491,
    matched to 0.02-0.06%).
  - Reading advection as a shift of c*. Upwind advection enters the relation as
    adv*(e^L - 1), not adv*L, so an advected front is genuinely faster than
    "still-air speed plus wind". Predicted 19-29% low until that was fixed.
  - Detecting invasion above titre 0.5. Tissue behind the front settles at the
    ENDEMIC level 1 - clr/r, not at carrying capacity, so an R0 = 1.29 agent
    had invaded to 0.222 and read as a failure to establish.

Front speeds land 1.2-2.1% under the discrete closed form across every case,
which is the sign and size of the Bramson log correction. Invasion has a real
threshold at R0 = r/clr with nothing scheduling it, in the same category as
Plant.spent().

Co-Authored-By: Claude Fable 5 <[email protected]>
Wires the infection into the two organs that solve auxin — Meristem and Leaf —
as three lines around each solve loop. The field carries its own infection
(`F.inf`), so an organ is not told about pathogens; uninfected tissue pays one
null test per step. The deformation has to land INSIDE the solve because both
organs rewrite their own rates every step, and it is reverted immediately, so
nothing downstream can read a deformed value.

test/infected.mjs is the measurement and the drawing. Section 5 is the result:
inoculating a meristem at its centre and counting what it founds.

  agent        organs   live   divergence   aMean   burden
  (control)      167      4       94.7°      1.31    0.00
  blind          103      1       97.2°      0.88    0.69
  invert          56      0      108.3°      1.57    0.67
  gall           149      1       71.4°      2.13    0.66
  chlorosis       61      1      120.3°      0.50    0.45

Organ count is the column to read — founding an organ IS sharpening an auxin
maximum, which is exactly the operation `comp` gates — and inverted polarity
takes it from 167 to 56 with no live primordia left at all. Divergence is not
evidence of anything here; it wanders 90-160° uninfected.

Three things the harness caught, none of them bugs in the code:

  - dComp is EXACTLY inert on a blade, and the identical rows in section 2 are
    the correct answer. A blade solves in flux mode, which pins s = 1 and
    multiplies the gradient weight — the only thing comp touches — by zero. A
    competence agent is a MERISTEM agent. Now asserted rather than assumed.
  - PIN conservation cannot be measured on a grown field. `p` is updated at the
    end of the same stepAuxin call that allocated `P`, so sum(P)/p is one step
    stale for every cell; comparing an infected run to a control run measures
    how far two auxin trajectories diverged. It read 1.4e-3 with the inverted
    cells NEARER 1.0 than the clean ones — not even the sign the failure mode
    would give. Checked on one isolated allocation instead: error exactly 0.
  - On a blade every agent saturates to burden 1.00, so what those rows show is
    a global parameter change and not a lesion. Written into the file's header
    as a known gap rather than papered over.

And one finding worth keeping: vein COUNT is unmoved at 260 across every agent
while traffic redistributes hugely (top strand 7.1% -> 0.8%, n50 10 -> 112).
Drawn width is a relative quantity, so a uniform change to a whole blade is
invisible to it. That is crown.mjs's normalised-fill lesson arriving again
through the renderer instead of through a metric.

smoke 73/73, views 23/23, pathogen and infected green.

Co-Authored-By: Claude Fable 5 <[email protected]>
The previous commit's section 2 reported `L.veins.length` as a vein count and
read a flat 260 across control, saturated and everything between as "the agent
does not change the network". 260 is `veinMax`, a hard cap in Leaf.bake(), and
every configuration tried produces more candidates than it. The column was
arithmetic, not a measurement — the same trap as the unreachable-maximum metric
test/venation.mjs documents, and it survived a full round of this harness.

Recounted with the cap taken off:

  agent       canals    d%     top strand   n50   burden
  (control)     410     --        7.1%       10     --
  gall          628  +53.2%       0.8%      112    1.00
  chlorosis     621  +51.5%       2.6%       69    1.00
  blind         410  + 0.0%       7.1%       10    1.00
  invert        410  + 0.0%       7.1%       10    1.00
  systemic      589  +43.7%       3.2%       40    0.30

So the agent changes the blade's network substantially — half again as many
canals, with traffic spread over 112 strands instead of 10 — and the earlier
"no effect" was entirely the cap. blind and invert sitting at exactly +0.0% is
now a second, independent confirmation that dComp cannot act in flux mode,
rather than the same non-result as everything else.

Both counts are printed. `drawn` stays in the table labelled as the cap, so
nobody re-derives the wrong conclusion from it.

Also derived and swept the localisation regime, since the honest gap is that
every agent saturates the blade. A blade matures at 96.7 time units with the
lattice appearing at 1401 steps, leaving 37.8tu of spread across a graph 44
hops wide — so a one-third lesion needs a front at ~0.39 cells/tu, i.e.
Dv*rho ~ 3.8e-2. Measured: Dv 0.004 / r 0.85 gives burden 0.32, and Dv 0.03 /
r 0.25 gives 0.10. The derivation lands where the sweep does.

Co-Authored-By: Claude Fable 5 <[email protected]>
…aim is false

Adds the literature sweep (docs/research_8_02_26_pathogen.md) and the two
mechanisms it says are actually demonstrated, plus the falsification of the one
it flagged as untested.

WHAT THE BRIEF CHANGED

1. `chi` — advecting the front along J — is NOT literature-supported. Nothing
   pathogenic moves in the polar auxin transport stream: phytoplasmas are
   sieve-element only, wilt fungi xylem-lumen only, viruses go plasmodesmata
   then phloem source-to-sink, galls do not move at all. It was the design
   decision I liked most. It stays, because it is measurable and the systemic
   *pattern* does track a source-sink topology our vein hierarchy approximates,
   but it is now labelled [OURS] in the table rather than implied physics.

2. What IS demonstrated: auxin raises plasmodesmal callose and closes
   plasmodesmata (Han 2014, Dev Cell; Sager 2020, Nat Commun). So the agent's
   own product gates the road it spreads on. `pdGate` / `pdN`, off by default.

3. A pure `rho` bump over-predicts by two to three orders of magnitude, because
   the host conjugates (GH3) and oxidises (DAO) the excess: in iaaM plants the
   precursor moves 945-2014x while free IAA moves 2.5x. `clampMu` / `clampK` is
   that response. Measured here: auxin behind the front 30.76 without it, 6.83
   with the gate, 2.60 with both.

THE FALSIFICATION

The brief's [OURS] paragraph predicted the gated front would DECELERATE "in
exactly the tissue it has already deformed", making the lesion self-limiting
with an emergent boundary. Section 6 was written to check it. It is wrong:

  agent                 c @25%   c @55%   c @90%   final front   a behind
  no gate             1.0212   1.0280   1.0303      419.0      30.76
  gate 1.2            0.5698   0.7420   0.7938      295.1       6.83
  gate 1.2 + clamp    0.6515   0.8140   0.9552      333.7       2.60
  gate, no dRho       1.0132   0.9627   1.0257      419.0       0.02

Speed RISES toward an asymptote and holds. The medium is homogeneous and every
coupling is local, so the system admits a travelling wave, and a travelling wave
has one speed; the slow early window is just the transient at the inoculation
site. Bounding a lesion needs something that breaks translation invariance.
That is asserted now, so it cannot quietly stop being true.

What survives is worth having: the gate cuts front speed ~23% and the auxin
doing the cutting is the agent's own, confirmed by the dRho = 0 control which
is unchanged at 1.026 against an ungated 1.030.

A BOUNDED LESION, BY GEOMETRY

A blade is finite, which is the translation invariance the chain lacked. Sizing
`Dv` from the blade rather than by eye — 37.8 time units of development, ~22
hops from the middle, so a third-of-a-blade lesion wants a front near 0.19
cells/tu, Dv*rho ~ 9e-3 — gives burden 0.36 and a lesion with an edge:

  agent       canals    d%     top strand   n50   burden
  (control)     410     --        7.1%       10     --
  gall          628  +53.2%       0.8%      112    1.00
  lesion        600  +46.3%       1.8%       65    0.36

It is drawn in section 3 and the boundary is visible. Nothing bounds it; it
stops where a slow front gets to before the blade stops developing.

Two more from the brief, not built, recorded for later: cyst nematodes
relocating PIN3 basal-to-lateral is the one documented pathogen polarity
rewrite; and phyllody is NOT auxin (SAP54 degrades ABCE MADS-box via RAD23), so
it belongs on `q` and must not be routed through this.

smoke 73/73, views 23/23, pathogen and infected green.

Co-Authored-By: Claude Fable 5 <[email protected]>
…instruments

SCIENCE.md — an injected agent is NOT on the numbered list, and the argument is
the falling blade's: a thing the plant is subject to, not a shape anybody drew.
No entry grows. What it buys free is the lesion's internal vein pattern, whether
its strands join the host's, which organs are hit and in what order, and the
severity gradient from the arrival point. What is STATED and now booked: arrival
position and time (an environmental event, same defence as the wind); which
variable the agent touches and its sign (the agent's genome — not derivable from
transport); magnitude and diffusivity (lookups from a table, the standard
39_fall.js set for leaf mass per area); and ⚠ `clampK`, a genuinely new constant
whose shape the biology gives and whose half-saturation nothing we could find
does.

It also records the correction: the brief argued the lesion's BOUNDARY came free
from the plasmodesmal gate. It does not. An organ being finite is what bounds a
lesion, and calling that emergent-from-the-mechanism would be a lie.

JOURNAL.md — the full entry, including the three pre-flight errors, the meristem
table, and the falsification of the brief's own [OURS] claim.

PITFALLS.md — three new entries, all of which read as "no effect" and none of
which were:
  - `L.veins.length` is a CAP (veinMax 260), so a flat column there is arithmetic.
    The mirror image of test/venation.mjs's unreachable-maximum trap.
  - `sum(P)/p` is one step stale, because stepAuxin allocates P in section 1 and
    updates p in section 4 of the same call. A conservation check on a grown field
    measures two simulations drifting apart.
  - `comp` is multiplied by zero in flux mode, so a leaf cannot test a competence
    agent at all.

TUNING.md — a section written in the fall's register rather than this file's
usual one, because most of these are not sweepable: a table of which knobs may be
swept and which are the disease's species definition, the endemic titre
`v* = 1 - clr/r` and why a fixed 0.5 threshold hides an invasion, and the
derivation of `Dv` from the organ's own clock (37.8 time units, ~22 hops, so
`Dv*rho ~ 9e-3`) with the sweep that confirms it.

CLAUDE.md — both harnesses in the list, the asserting count ten -> twelve,
15_pathogen.js in the architecture block, the research doc in the reading table,
and a four-point section at the top of the honest state.

smoke 73/73, pathogen and infected green, build clean.

Co-Authored-By: Claude Fable 5 <[email protected]>
…cement A/B

Plant.inoculate(name) puts an agent in one growing point. Cross-axis spread is
INHERITANCE AT BUD FORMATION — a bud is made of its parent's tissue, so it starts
with whatever that tissue carried — and deliberately not transport, because there
is no whole-plant auxin stream in the running piece. That gives a severity
gradient down the specimen out of a rule that mentions neither severity nor
position: axes founded early start light, ones founded late start heavy, because
the parent's titre rose in between.

  Abyssal Frond, seed 21, 2600 steps, inoculated at 300

  agent        axes   organs    d organs
  (control)       6      117         --
  gall            6      125      +6.8%
  leafygall       6      107      -8.5%
  chlorosis       6       63     -46.2%
  blind           3       22     -81.2%
  invert          3       10     -91.5%
  lesion          6      115      -1.7%

THE FALSIFICATION

The brief's finding 4 was one of its best: Agrobacterium installs an auxin SOURCE
and gets an undifferentiated blob, Rhodococcus changes WHERE maxima form and gets
organised iterated shoots, and the difference is exactly our rho-versus-comp
axis. `fas` creates new competent domains, so dComp > 0 and the prediction was
MORE organs.

The rho half works (+6.8%). The comp half does not: -8.5%, the wrong sign, and
sweeping it is non-monotonic and seed-fragile (+10.3% at dComp 0.40/Dv 0.012,
0.0% at 0.85/0.004, -15.4% at 0.85/0.05) — noise, not a mechanism.

Why, and it names the right variable: `comp` gates how sharply a cell may
polarise, not whether a region IS an organ-founding domain. Raising it uniformly
over-sharpens the maxima that already exist, making them stronger sinks, which
suppresses their neighbours harder. It sharpens domains; it does not create them.
Rhodococcus creates them. The variable that sets where a competent region is, is
`rCZ` — the central-zone radius, and the one spatial prior SCIENCE.md books.
Untried, and it is the obvious next experiment.

`leafygall` stays in the table labelled falsified, on the `rhoI: 0` /
`fluxPartition` principle: a negative result you cannot re-measure is a story.

And a caution written into the JOURNAL beside that table: the +6.8% to -91.5%
span is six different mechanisms on one seed and one species, not a calibrated
dial. The only two claims predicted in advance and surviving are structural —
comp agents act on meristems and not blades, and inverting polarity stops organ
founding because founding an organ IS the operation comp gates.

NOT YET WATCHED IN A BROWSER. Every number here is headless, and this project's
record is that three of its worst modelling errors were caught by a person
looking. `app.plant.inoculate('lesion')` is reachable from the console.

smoke 73/73, views 23/23, pathogen and infected green.

Co-Authored-By: Claude Fable 5 <[email protected]>
The agent is built and has never been looked at — every number about it is
headless, and this project has been wrong about exactly that three times. On a
whole specimen the agents span +6.8% organs to -91.5%, so there is something to
see; whether it reads as disease or as a broken renderer is the question no
harness here can answer.

Then the experiment the falsification named. dComp > 0 was meant to give
Rhodococcus's leafy gall and gives -8.5% organs, because `comp` gates how sharply
a cell polarises and not whether a region IS an organ-founding domain. `rCZ` is
the variable that decides where a competent region is. Untried and cheap.

Plus the two better-evidenced mechanisms the sweep found and this pass did not
build (nematode PIN3 relocation; HopM1/AtMIN7), and the warning not to route
phyllody through auxin.

Co-Authored-By: Claude Fable 5 <[email protected]>
…, not a coordinate

A second research pass on clubroot and nematode feeding sites (appended to
research_8_02_26_pathogen.md) retracted one thing and paid for another.

THE RETRACTION: clubroot is not an auxin disease and Part 0's row for it should
not be built. Free-IAA measurements disagree across four studies in the same
genus and the 2025 review's own words are "fluctuating IAA levels rather than
consistently higher levels". Both de novo routes are dispensable — `cyp79b2
cyp79b3` doubles make no detectable IAOx and show NO difference in
susceptibility, and PbGH3 overexpression changes neither hormone levels nor
susceptibility. What survives is cytokinin, which we do not have.

THE PAYMENT: SCIENCE.md booked "where the agent arrives" as a stated position.
It does not have to be. Every infection structure in that literature initiates
in provascular, undifferentiated tissue and the criterion is a DEVELOPMENTAL
STATE rather than a position — cyst syncytia take undifferentiated xylem
precursors and never differentiated ones (Liu & Mitchum 2024), root-knot giant
cells come from the xylem-pole pericycle with XPP-defective lines significantly
less infected (Cabrera 2014), a clubroot gall is amplification of pre-existing
cambial activity so disrupting that activity shrinks it (Malinowski 2012).

The parasite does not choose a coordinate. It chooses a state, and the host's
own geometry does the rest. `comp` IS our tissue-identity variable, so
Infection.inoculateByState samples by it and it is now the default. The entry
shrinks from "a position and a time" to "a time". Second time this project has
found a stated constant was derivable once somebody read the literature
properly; the first was the gravitropic set point on 2026-07-30.

AND A CORRECTION TO MY OWN WRITE-UP. The siting rule moves the numbers, so the
leafygall falsification needed restating: under state-based siting the same run
gives gall +7.7%, leafygall -0.9%, invert -87.2%. leafygall is NULL rather than
clearly negative under the shipped rule. The falsification is that it does not
produce the predicted INCREASE — that holds under both rules — and "-8.5%" is a
centre-inoculated number that should not be quoted as the effect size.

One more constraint recorded before anyone extends this: NPA does not abolish a
nematode feeding site, it removes "radial expansion toward the vascular bundle",
and the same inhibitor moves clubroot gall position. DIRECTION, NOT EXISTENCE,
is what auxin transport supplies. Also the real auxin maximum is transient and
moves outward, which our static dRho does not do.

smoke 73/73, views 23/23, pathogen and infected green. Ashfall Spire unchanged
at 3002 organs / 240 axes.

Co-Authored-By: Claude Fable 5 <[email protected]>
The clubroot/nematode sweep died mid-run and was re-run after the search
budget was gone, so its output landed as an appendix with author-year
citations and no DOIs. Two of its results are load-bearing and both
contradicted what the body of the document said:

- clubroot is not an auxin disease (the free-IAA reports disagree with
  each other; both de novo routes are dispensable), so section 1.4 no
  longer reads as an open gap
- site selection is a developmental-state criterion, not a coordinate, so
  "where the agent arrives" comes off the parameter list in 7.1.1 --
  which is the one change here that alters what gets built

Both places now name the appendix and its lower standard, the appendix
carries a header saying which citations to resolve before anyone acts on
it, and 3.4 records the thing that undercuts the whole Q3 case: every
"auxin accumulation" result in this literature is a transcriptional
output reporter. Nobody has measured a flux at an infection site.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NAt6Y9btm8gpGnioQo7uZz
tools/infect_shot.mjs drives a real browser, grows control and infected from one
seed in one session, and the answer to "does an infected plant look infected" is
NO — it looks stunted.

  agent       leaves  stage       divergence   reads as
  (control)      35   flowering   111 +/-57    a healthy plant
  lesion         36   flowering    66 +/-90    a healthy plant
  chlorosis      26   flowering    86 +/-94    thinner, sparser
  invert          5   leafing      40 +/-82    a bare whip, basal leaves

`invert` is unmistakable but reads as a plant that FAILED, not one with a
disease. And `lesion` — built specifically to be a bounded visible lesion, and
measured at burden 0.36 with a drawn edge in ASCII — is indistinguishable from
the control at specimen framing.

The cause is not the mechanism. `F.vir` is a per-cell scalar the renderer never
reads. The only channels an agent can reach a viewer through today are organ
count, placement and axis length, all of which say "less vigorous" and none of
which say "infected". This lands on ROADMAP 0z's own argument: a
computed-but-undrawn channel is a view waiting to happen. Until titre is drawn,
every number in test/infected.mjs describes something nobody can see.

THREE MISTAKES THE TOOL MADE FIRST, all already documented in this repo, all
caught by its own assertions rather than by me:

  - photographed a SENESCING plant three times, which is tree_shot.mjs's first
    run exactly (it photographed a dead tree)
  - framed each variant on its OWN bounds, so the camera flew in on the sick
    plant and 91% fewer organs would have read as "about the same". Every shot
    is now pinned to the control's distance, which is tree_shot's FIXDIST
  - called inoculate() after the leader had converted to a flower, so the agent
    landed nowhere four times and reported four identical plants in silence

Plant.inoculate now falls back to any axis that still has a growing point and
returns null when none does, so a caller can tell.

Also recorded: stage polling cannot be made fine in headless. The app takes up
to 12 sim steps per frame and software rendering makes frames long, so a
specimen crosses fruiting, ripe and senescing inside one 200ms poll whatever
speedMul says.

smoke 73/73.

Co-Authored-By: Claude Fable 5 <[email protected]>
An agent is the one thing in the piece with no UI at all — it is an event in the
environment, so nothing on the page implies one exists. Without __AGENTS a person
looking at an infected plant has no way to find out what to type, and has to read
the source for the names.

Same reasoning as __VIEWS and __SPECIES already carry.

  Object.keys(window.__AGENTS)
  -> gall, chlorosis, blind, invert, leafygall, lesion, systemic

Verified in a real page rather than assumed. smoke 73/73.

Co-Authored-By: Claude Fable 5 <[email protected]>
…y exposed

UI: an agent picker beside "cut the apex" — the same kind of control, a one-shot
perturbation of a standing plant rather than a parameter. Two buttons:

  inject         put the selected agent into this specimen as it stands
  fresh + inject a new specimen, grown to the middle of its susceptible window,
                 then injected. The reliable demonstration.

The specimen card grows two rows once something has been injected, and only
then — a healthy plant should say nothing about a disease it does not have, so
the row appearing IS the notification. `burden` is the one that matters:
inoculate() can succeed and the agent still fail to establish, because invasion
has a real threshold at R0 = r/clr with nothing scheduling it. Verified against
a raw scan of F.vir: agentBurden() agrees to every printed digit.

Each agent carries its own tip explaining the mechanism and what to expect,
including that `leafygall` is falsified and `systemic` is unsupported.

THE FINDING, which no headless harness could have produced because they all
inoculate at one fixed step: WHETHER AN INOCULATION TAKES DEPENDS ON WHEN.
`invert` on Abyssal Frond, eight seeds each:

  inoculate at   took   organs (median)   healthy
      200        8/8          16            105
      400        7/8          20            105
      600        4/8          75            105
      800        1/8         105            105
     1000        0/8         105            105

Past ~800 steps the specimen finishes with EXACTLY the organ count it would have
had untouched. A meristem is growing, advecting tissue — every cell it makes
starts clean — so early the agent can take the field before dilution wins, and
late the plant outruns it and the leader converts to a flower, taking the
growing point and the infection with it. Two independent clocks, and the disease
only exists where they overlap. Nothing in 15_pathogen.js knows about plant age.

This cost an hour of chasing a phantom first. The browser reported the agent
dying out while headless showed it establishing — which reads exactly like a
browser-only wiring bug, the class CLAUDE.md says only a browser can find. It
was neither: the click had landed late in the window, and the plant's step count
at the moment of clicking varies with frame pacing. A RUN-TO-RUN DIFFERENCE IS
NOT A PLATFORM DIFFERENCE. Sampling finely instead of once at the end showed it
establishing normally.

Also exposes window.__AGENTS, and Plant.inoculate falls back to any axis that
still has a growing point rather than failing silently on axis 0.

smoke 73/73, pathogen and infected green.

Co-Authored-By: Claude Fable 5 <[email protected]>
Watched in a real browser, and the honest headline for this whole feature is
"not seeing much here". That is the fourth time on this project that a person
looking has been the deciding instrument, and it is the only verdict that
counts, so it goes at the top of the section rather than buried under the
numbers that all say the mechanism is fine.

The mechanism IS fine — derived, measured against closed forms, drawn, wired to
a UI, spanning +6.8% organs to -91.5% on a whole specimen. It reads as STUNTING,
not as disease, and `lesion` (built specifically to be a visible bounded lesion,
burden 0.36 with an edge in the ASCII) is indistinguishable from a healthy plant
on screen.

The diagnosis is specific and it is not the mechanism: F.vir is a per-cell
scalar the renderer never reads. The only channels an agent can reach a viewer
through are organ count, placement and axis length, all of which say "less
vigorous" and none of which say "infected". So the one thing worth doing before
anything else here is drawing the titre — ROADMAP 0z's own argument, a
computed-but-undrawn channel is a view waiting to happen.

CLAUDE.md and ROADMAP both now lead with that and say plainly: do not reopen
this expecting a quick visual win.

Also recorded so nobody re-diagnoses it: `burden` reading "cleared" is usually
the window of susceptibility rather than a bug.

smoke 73/73.

Co-Authored-By: Claude Fable 5 <[email protected]>
@aj-dev-smith
aj-dev-smith merged commit cea273a into main Aug 3, 2026
4 checks passed
@aj-dev-smith
aj-dev-smith deleted the agent-in-the-tissue branch August 3, 2026 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant