Skip to content

Commit 3f2d182

Browse files
Alexboiboiclaude
andcommitted
Restructure the documents around the work, not around FEM
TASKS.md was scoped to preparing for the FEM work, because that is what it was asked for, but the foundation is upstream of FEM rather than downstream: FEM is an application of studio, and integration built on a foundation that is about to move is rework. So the tracks invert. Foundation is primary, magpylib core runs in parallel because its lead time is upstream review rather than ours, and validation follows except for the physics layer, which is orthogonal -- a different repo, magpylib objects only, no solver and no open decision. The two foundation items were buried. Killing the round trip sat under "drag reduction" as though it were tidying, when it is the whole of the diagnosis; parameterised instancing sat in "not started" despite being the missing reuse feature and the answer to all four units of reuse. Instancing now has a design document rather than a line in a list, because it is the highest-design-risk item and the most tempting to start coding. The prior art settles the two questions that looked hardest: Godot, Onshape and Jsonnet all put a definition in its own file, and Godot's recurring instance-value bugs come from allowing arbitrary overrides, so an instance supplies declared arguments and nothing else. The useful observation is that the parameter declaration already exists -- variables plus variable_bounds are a signature in everything but name, so instancing is mostly letting another document supply them. Six questions stay open, version pinning and whether a document's variables really are its public signature being the ones that would hurt to get wrong. Layout follows the same idea: root holds the front door, the state and the work, docs/ holds the long-form thinking, and both README and TASKS carry a map so neither a person nor an agent has to guess which file answers which question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 985bb20 commit 3f2d182

5 files changed

Lines changed: 259 additions & 164 deletions

File tree

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,18 @@ fails at first contact, before anyone sees a feature — and it is what lets
237237
`.vsix` attached to each release is the same build, for anyone who would rather
238238
install it by hand.
239239

240-
See [CONTINUE.md](CONTINUE.md) for the current state and what is next.
240+
## Project documents
241+
242+
| Question | Document |
243+
| --------------------------- | ---------------------------------------- |
244+
| What is built? | [CONTINUE.md](CONTINUE.md) |
245+
| What is next? | [TASKS.md](TASKS.md) |
246+
| Why is it going that way? | [docs/direction.md](docs/direction.md) |
247+
| How does instancing work? | [docs/instancing.md](docs/instancing.md) |
248+
| How does FEM validation go? | [docs/fem.md](docs/fem.md) |
249+
250+
The three at the root are the state, the work and the front door; `docs/` holds
251+
the long-form thinking behind them.
241252

242253
## License
243254

TASKS.md

Lines changed: 108 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,158 @@
1-
# Tasks — preparing for the FEM work
2-
3-
The executable breakdown sitting between `FEM.md` (the plan) and doing it.
4-
`DIRECTION.md` holds the architectural position; the review that produced this
5-
list found the two do not collide.
6-
7-
## The review finding that matters
8-
9-
The direction research landed on **keep the document as the artifact**, and
10-
`FEM.md` §12.1 already assumes exactly that: a cache key over source-affecting
11-
events, `sources_all` / `sensors_all` as the partition, `set_rollback` and
12-
`undo` as free FEM navigation. Had the answer been code-as-truth, all of §12.1
13-
would need rewriting to a trace hash. It does not.
14-
15-
**So the open direction question blocks nothing below.** One positive
16-
interaction rather than a dependency: parameterised instancing would give FEM a
17-
library of validated assemblies and sweeps over instance parameters.
18-
19-
## Ordering
20-
21-
- **C starts now** — no blockers at all; needs no solver and no studio change.
22-
- **B starts now, in parallel** — the longest lead, and it is upstream review
23-
time rather than ours.
24-
- **A1 and A2 before any studio-side FEM UI.** A2's cost rises the longer it
25-
waits.
26-
- **D is independent**, and shrinks the maintenance surface before FEM adds to
27-
it.
1+
# Tasks
2+
3+
What to do next, ordered by what gates what. The reasoning behind each item
4+
lives in the plan it came from — this file stays thin enough to work from.
5+
6+
| Question | Document |
7+
| --------------------------- | ---------------------------------------- |
8+
| What is this? | [README.md](README.md) |
9+
| What is built? | [CONTINUE.md](CONTINUE.md) |
10+
| **What do I do next?** | **this file** |
11+
| Why is it going that way? | [docs/direction.md](docs/direction.md) |
12+
| How does instancing work? | [docs/instancing.md](docs/instancing.md) |
13+
| How does FEM validation go? | [docs/fem.md](docs/fem.md) |
14+
15+
**Track F (foundation) is primary.** It is what studio _is_ under the
16+
positioning in `docs/direction.md` §1, and everything else is an application of
17+
it. M runs in parallel because its lead time is upstream review rather than
18+
ours. V is downstream of F except for V1, which is orthogonal — a different repo
19+
that touches none of this.
2820

2921
---
3022

31-
## Group A — studio work the FEM plan needs
32-
33-
### A1 — Jobs in the RPC protocol
23+
## Track F — Foundation
3424

35-
**What.** Worker subprocess, server-initiated notifications, a job id space,
36-
cancellation.
25+
### F1 — Kill the round trip
3726

38-
**Why.** `serve()` is a strictly serial blocking loop: one line in, one line
39-
out, in order; every write echoes a request `id`, so there are no
40-
server-initiated messages; and there is no `threading`, `asyncio` or
41-
`subprocess` anywhere in the engine. A solve through it freezes the scene tree,
42-
the inspector, the sliders and the 3D view for minutes. It also pays for
43-
something already wanted — mesh reorientation blocks the same loop at 16 s for
44-
20k faces.
45-
46-
**Done when.** A long call runs without blocking any other request, reports
47-
progress, and can be cancelled; the existing test suite still passes.
27+
**What.** `parse_script` goes, with the matched emitter/parser idiom pairs
28+
(`_mirror`, `_superquadric`, the `for i in range(1, n)` loop shape, the
29+
`_name_copy` line emitted and then skipped). `to_script` becomes export and
30+
recording only.
4831

49-
**Blocks.** `FEM.md` M7, and all three workflows in §12.
32+
**Why.** `docs/direction.md` §2 and §5.1. Every project in its §4 table
33+
generates one-way. This is a deletion with a known shape and it shrinks the
34+
surface F2 has to work in.
5035

51-
### A2 — Refusal-first job API
36+
**The decision inside it.** The script tab stops being applied on save. That is
37+
a UI change, not only a deletion, and it should be decided rather than
38+
discovered. `load_script` and `apply_script` keep working by execution, which is
39+
already their fallback.
5240

53-
**What.** The job API **errors**, rather than warns, on: a residual without
54-
convergence metadata, a solve past budget, and a comparison mixing `ideal` and
55-
`physical` mode.
41+
**Done when.** The round trip is gone, import still works by execution, and the
42+
two-tier cliff is unreachable.
5643

57-
**Why.** §13.5. A human glances at a "provisional" badge; an agent ignores a
58-
warning field and hill-climbs on mesh noise, confidently, for hours. And the
59-
guardrail cannot live in a skill file — the README's _"there is no second
60-
validation layer"_ settles where it goes. Cheap now, expensive once callers
61-
depend on permissive behaviour.
44+
### F2 — Parameterised instancing
6245

63-
**Done when.** Each of the three refusals has a test asserting an error rather
64-
than a warning.
46+
**What.** A definition document with declared parameters, and an `instance`
47+
event that references it with arguments. Design in
48+
[docs/instancing.md](docs/instancing.md).
6549

66-
### A3 — Source/probe hash partition
50+
**Why.** The missing reuse feature. All four wanted units of reuse are
51+
composition units, and a structured format composes if it has instancing plus
52+
exported parameters.
6753

68-
**What.** A content hash over field-affecting events only — magnets, currents,
69-
anything with μr ≠ 1, and the transforms carrying them — excluding sensors and
70-
pixel grids.
54+
**Design first.** Six open questions in `docs/instancing.md` §5 — version
55+
pinning, nested instances, composition with patterns, editing through an
56+
instance, whether a document's variables are its signature, and whether
57+
same-document definitions are worth having. This is the highest-design-risk item
58+
in the plan and the most tempting to start coding.
7159

72-
**Why.** Solve once, probe forever. Moving a sensor must not invalidate a solve.
60+
**Done when.** Design questions closed, then: an instance resolves in `_build`,
61+
`to_script` emits a call, a definition round-trips by hash, and a pattern of
62+
instances works.
7363

74-
**Done when.** Moving a sensor leaves the hash unchanged; changing a magnet
75-
changes it; and undoing back to a previous state reproduces that state's earlier
76-
hash exactly. The last property is what turns history navigation into free FEM
77-
navigation.
64+
### F3 — GUI as parameter binder
7865

79-
### A4 — Units
66+
**What.** Introspect a definition's parameters, render widgets, rebuild, draw.
8067

81-
**What.** A unit _kind_ on a variable, beside `integer`; one model unit per
82-
document; emitters convert at the boundary; the UI formats.
68+
**Depends on** F2 — there are no parameters to bind until definitions exist.
8369

84-
**Why.** AEDT wants `"5mm"`, solvers want consistent SI, and `FEM.md` §6 chose
85-
metadata over unit-carrying values so no document migrates. Cheaper than that
86-
section implies: `_PARAM_UNITS` already sits beside `_PARAM_ATTRS`.
70+
**Why.** `docs/direction.md` §5.3. The sync surface shrinks to "parameters →
71+
widgets", which cannot drift. Proven shape: OpenSCAD Customizer, Storybook
72+
controls, Streamlit.
8773

88-
**Done when.** Every existing document loads unchanged and the script round-trip
89-
is stable.
74+
### F4 — Grow `expressions.py` toward Starlark
9075

91-
**Blocks.** M6.
76+
**Deliberately not scheduled.** `docs/direction.md` §5.4 — the right answer and
77+
the expensive one. Starlark took Google eight years. Nothing forces the choice
78+
yet.
9279

9380
---
9481

95-
## Group B — magpylib core (start now; upstream lead time is the constraint)
82+
## Track M — magpylib core (start now; the constraint is upstream review)
9683

97-
### B1`susceptibility` / μr as a documented property
84+
### M1`susceptibility` / μr as a documented property
9885

99-
**What.** A real property on magnets, rather than the attribute
100-
`magpylib-material-response` monkey-attaches and searches parents for.
101-
102-
**Why.** It is the `physical`-mode input. Without a shared convention the
103-
exporter invents its own home for μr and it differs from material-response's —
104-
at which point tier 1's three-way comparison silently compares magnets that are
105-
**not the same magnet**, and the discrepancy looks like physics.
86+
**Why.** It is the `physical`-mode input (`docs/fem.md` §3.2). Without a shared
87+
convention the exporter invents its own home for μr and it differs from
88+
`magpylib-material-response`'s — at which point tier 1's three-way comparison
89+
silently compares magnets that are **not the same magnet**, and the discrepancy
90+
looks like physics.
10691

10792
**Done when.** Merged upstream, or a documented studio-side convention exists
10893
that material-response also reads.
10994

110-
### B2 — Public constructor-parameter introspection
111-
112-
**What.** One accessor upstream replacing studio's hardcoded `_PARAM_ATTRS`.
95+
### M2 — Public constructor-parameter introspection
11396

114-
**Why.** The physics layer needs the identical table, so a new magnet class in
115-
core would fall silently through **both** copies.
97+
**Why.** Studio hardcodes `_PARAM_ATTRS`; the physics layer (V1) needs the
98+
identical table, so a new magnet class in core would fall silently through
99+
**both** copies.
116100

117101
**Done when.** `get_params` reads it instead of the literal tuple.
118102

119103
---
120104

121-
## Group C — the physics layer (unblocked, start today)
122-
123-
### C1 — Package skeleton and the home decision
105+
## Track V — Validation
124106

125-
**What.** Settle `FEM.md` §4's split — physics on magpylib objects in a
126-
standalone package, parametrics on the document in studio — and create it.
107+
Full plan and its gates in [docs/fem.md](docs/fem.md). Only V1 is orthogonal to
108+
Track F; the rest build on the foundation and should follow it.
127109

128-
**Done when.** It imports, has CI, and depends only on magpylib.
110+
### V1 — The physics layer (orthogonal — start any time)
129111

130-
### C2 — Conventions module
112+
Package skeleton and the §4 home decision · conventions module (§3.1's verified
113+
table, including the two off-by-two traps) · the required `ideal`/`physical`
114+
mode switch (§3.2) · solver-free conformance: the `getJ` occupancy test plus the
115+
volume/centroid check.
131116

132-
**What.** Pose → euler, J → (Hc, μr, world direction), geometry-parameter
133-
normalisation. Pure functions, no solver, no vendor, no document.
117+
**Why it can run alongside F.** Different repo, magpylib objects only, no
118+
solver, no studio change, no open decision. Reaches `docs/fem.md`'s **G1** with
119+
nothing installed.
134120

135-
**Why.** `FEM.md` §3.1's table, verified against a live checkout — including the
136-
two off-by-two traps: `Cylinder` and `Sphere` take **diameter**, and `Cuboid`
137-
takes **full** edge lengths while `create_box` takes a corner.
121+
### V2 — Units
138122

139-
**Done when.** Every row of that table is pinned by a test.
123+
Unit _kind_ on a variable beside `integer`; one model unit per document;
124+
emitters convert at the boundary. Cheaper than `docs/fem.md` §6 implies —
125+
`_PARAM_UNITS` already sits beside `_PARAM_ATTRS`. **Interacts with F2**: a
126+
definition's parameters want units for the same reason its variables do.
140127

141-
### C3The ideal / physical switch
128+
### V3Jobs in the RPC protocol
142129

143-
**What.** A required mode argument with no default.
130+
Worker subprocess, server-initiated notifications, a job id space, cancellation.
131+
`serve()` is a strictly serial blocking loop with no threading, asyncio or
132+
subprocess anywhere in the engine, so a solve through it freezes the whole UI
133+
for minutes. Also pays for mesh reorientation blocking the same loop at 16 s.
144134

145-
**Why.** §3.2. A μr = 1 magnet and a μr = 1.05 magnet with the same Br are
146-
different magnets. `ideal` validates our translation; `physical` measures the
147-
modelling error. Conflating them yields a comparison that means nothing while
148-
looking rigorous.
135+
### V4 — Refusal-first job API
149136

150-
**Done when.** No call site can omit it.
137+
Errors, not warnings, on: a residual without convergence metadata, a solve past
138+
budget, a comparison mixing `ideal` and `physical`. `docs/fem.md` §13.5 — an
139+
agent ignores a warning field and hill-climbs on mesh noise. **Cost rises the
140+
longer callers depend on permissive behaviour**, so decide the shape early even
141+
if the work lands late.
151142

152-
### C4Solver-free conformance tests
143+
### V5Source/probe hash partition
153144

154-
**What.** The `getJ` occupancy test, plus the volume/centroid check beside it.
155-
156-
**Why.** Catches centre-vs-corner, diameter-vs-radius, degrees-vs-radians,
157-
local-vs-world polarisation and a wrong Euler convention — with **nothing
158-
installed**.
159-
160-
**Done when.** Green for `Cuboid`, `Cylinder`, `Sphere` and `CylinderSegment`
161-
against a pure-Python membership evaluator. This is `FEM.md`'s **G1**.
145+
A content hash over field-affecting events only, excluding sensors and pixel
146+
grids. Solve once, probe forever. Undo back to a previous state must reproduce
147+
that state's earlier hash — that property is what turns history navigation into
148+
free FEM navigation.
162149

163150
---
164151

165-
## Group D — drag reduction
166-
167-
### D1 — Kill `parse_script`; `to_script` becomes export only
168-
169-
**What.** Remove the read-back path and the matched emitter/parser idiom pairs
170-
(`_mirror`, `_superquadric`, the `for i in range(1, n)` loop shape, the
171-
`_name_copy` line that is emitted and then skipped).
172-
173-
**Why.** `DIRECTION.md` §2's cliff and §5.1, and every project in its §4 table
174-
generating one-way. Shrink the maintenance surface before FEM adds to it.
175-
176-
**Careful.** `load_script`, `apply_script` and the script tab depend on it. The
177-
script tab stops being applied on save — that is a UI change, not only a
178-
deletion, and it needs deciding rather than discovering.
179-
180-
**Done when.** The round trip is gone, import still works by execution, and the
181-
two-tier cliff is no longer reachable.
182-
183-
### D2 — ~~Rewrite `DIRECTION.md`~~ **done**
184-
185-
Rewritten around _data is the artifact / one-way generation / parameterised
186-
instancing_, with code-as-truth kept as §7's recorded-and-rejected alternative
187-
and §4 carrying the Bazel, Jsonnet, Onshape, Godot and OpenSCAD evidence.
188-
189-
---
152+
## Not scheduled, and why
190153

191-
## Not started, and why
192-
193-
- **Anything downstream of `FEM.md`'s G3 spike** — the solver is deliberately
194-
undecided until it is measured.
195-
- **Parameterised instancing** (`DIRECTION.md` §5.2) — the missing reuse
196-
feature, and the answer to all four units of reuse. Agreed in principle, not
197-
scoped, and not a FEM dependency, so it is deliberately not in the groups
198-
above.
199-
- **Growing `expressions.py` toward Starlark** (`DIRECTION.md` §5.4) — the right
200-
answer and the expensive one. Nothing forces the choice yet.
201-
- **The agent skill (M9)** — a skill may only describe an API that exists.
154+
- **Anything downstream of `docs/fem.md`'s G3 spike** — the solver is
155+
deliberately undecided until it is measured.
156+
- **F4** — see above.
157+
- **The agent skill** (`docs/fem.md` M9) — a skill may only describe an API that
158+
exists.

DIRECTION.md renamed to docs/direction.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from the evidence in §4 and is not implemented. §7 records the alternative that
55
was argued first and rejected — kept because the reasoning is worth having, and
66
because it is how this file's conclusion arrived. Companion to `CONTINUE.md`
7-
(what is built), `FEM.md` (where validation goes) and `TASKS.md` (what to do).
7+
(what is built), `docs/fem.md` (where validation goes) and `TASKS.md` (what to
8+
do).
89

910
---
1011

@@ -60,7 +61,7 @@ already visible:
6061
forever.
6162

6263
And the failure is not graceful.
63-
[`importer.py:861`](magpylib_studio/importer.py):
64+
[`importer.py:861`](../magpylib_studio/importer.py):
6465

6566
> _"A script in the shape `to_script` emits -> (document, None), or (None,
6667
> reason) when it is anything else and **has to be executed**."_
@@ -353,11 +354,11 @@ added.
353354
| `to_script`, as export and recording | the matched emitter/parser pairs |
354355
| field, figures, field maps, sweeps | the two-tier cliff |
355356
| `expressions.py` — and §5.4 grows it | the script tab applying on save |
356-
| the whole of `FEM.md` | |
357+
| the whole of `docs/fem.md` | |
357358

358-
`FEM.md` is unaffected in particular: §12.1 already assumes the document stays
359-
the artifact, keys its cache on source-affecting events, and treats `undo` and
360-
`set_rollback` as free navigation over the same fold.
359+
`docs/fem.md` is unaffected in particular: §12.1 already assumes the document
360+
stays the artifact, keys its cache on source-affecting events, and treats `undo`
361+
and `set_rollback` as free navigation over the same fold.
361362

362363
---
363364

FEM.md renamed to docs/fem.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -785,12 +785,12 @@ and runs an analysis unattended. It is a good fit, with two hard constraints.
785785

786786
### 13.1 This document is not that file, and must not become it
787787

788-
`FEM.md` is a **plan**: it argues, records the alternatives it rejected, carries
789-
open questions, and above all **describes a system that does not exist yet**.
790-
Hand it to an autonomous agent today and the agent will call `validate()` and
791-
`read_result()` in good faith, with a citation. Plausible hallucination backed
792-
by an authoritative-looking document is the worst failure mode an agent artifact
793-
has.
788+
`docs/fem.md` is a **plan**: it argues, records the alternatives it rejected,
789+
carries open questions, and above all **describes a system that does not exist
790+
yet**. Hand it to an autonomous agent today and the agent will call `validate()`
791+
and `read_result()` in good faith, with a citation. Plausible hallucination
792+
backed by an authoritative-looking document is the worst failure mode an agent
793+
artifact has.
794794

795795
A skill is the opposite artifact: imperative, present tense, current state only,
796796
no history and no roads not taken. Two files, the skill derived from the plan,

0 commit comments

Comments
 (0)