An agent in the tissue: a spreading perturbation of the auxin machinery - #39
Merged
Conversation
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]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.jsand39_fall.js— a thing the plant is subject to, not chemistry the plant does and not a shape anybody drew. Nothing in15_pathogen.jssays what an infected plant looks like. It says what the agent does to three numbers per cell, andstepAuxindecides the rest.The spread term is the point
Jis 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.mjsAn 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.
sqrt(2)scalingc*adv*(e^L - 1), notadv*L1 - clr/rFront 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/clrwith nothing scheduling it, same category asPlant.spent().The result:
test/infected.mjssection 5Inoculate a meristem at its centre and count what it founds.
dComp -1)dComp -2)dRho +2.4)dMu +1.6)Organ count is the column to read — founding an organ is sharpening an auxin maximum, exactly the operation
compgates. Inverted polarity takes it 167 → 56 with no live primordia left. Divergence is not evidence of anything here; it wanders 90–160° uninfected.comp < 0is the interesting oneBelow 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. Forcompin [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
veinMaxis a cap, not a count. The first pass reportedL.veins.lengthand read a flat 260 across control, saturated and everything between as "no effect". Recounted uncapped: 410 → 628 canals (+53%) for the gall. Same trap astest/venation.mjs's unreachable maximum.pupdates at the end of the same call that allocatedP, sosum(P)/pis 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.85gives burden 0.32. Not shipped in a preset yet.Checks
smoke73/73 ·views23/23 ·pathogengreen ·infectedgreen · 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, since38_shoot.jsships disabled. That gives a severity gradient down the specimen out of a rule that mentions neither severity nor position.dRho +2.4)dComp +0.85)dMu +1.6)dComp −1)dComp −2)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-compaxis, so the prediction wasdComp > 0→ more organs.The
rhohalf works (+6.8%). Thecomphalf 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:
compgates 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, isrCZ— the central-zone radius, and the one spatial prior SCIENCE.md books. Untried, and the obvious next experiment.leafygallstays in the table labelled falsified, on therhoI: 0/fluxPartitionprinciple.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:
compagents act on meristems and not blades, and inverting polarity stops organ founding because founding an organ is the operationcompgates.⚠ 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;
clampKbooked 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.viris 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 seventest/pathogen.mjs— the derivation, against exact discrete closed formstest/infected.mjs— real tissue, measured and drawntools/infect_shot.mjs— the browser capture that produced the verdictinject/fresh + inject, plusagentandburdenrows on the specimen carddocs/research_8_02_26_pathogen.md— the literature sweep, flagged[D]/[I]/[OURS]/⚠Results worth keeping regardless
comp < 0is inverted polarity, and it costs one clamp that is provably a no-op for every shipped species. 167 → 56 organs on a meristem.sqrt(2)scaling assumption.veinMaxis a cap not a count;sum(P)/pis one step stale;compis 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.