Live probing against Penpot 2.16.2 (repeated, on a dedicated scratch page,
cleaned up afterwards) shows board.flex.appendChild(shape) is a real
function, distinct from board.appendChild, arity 1, throws nothing, and
preserves visual insertion order -- unlike board.appendChild, which still
inserts at the front as documented. The prior "broken" claim traces to the
MCP server's own high_level_overview() text (CRITICAL / BROKEN / NEVER use),
never verified with a live throw, and possibly confused with a real doc bug
that mislabels the grid 3-arg example as board.flex.
Removes flex.appendChild from the lint's FORBIDDEN patterns and from the
gate 5 forbidden-behavior veto, updates PENPOT_API_VERIFIED.md with the
verified facts, and drops the now-obsolete pattern from the replay filters
in 05_build_dataset.py and 07_build_lora2_mix.py.
07_build_lora2_mix.py assembles train_lora2.jsonl (900), eval_lora2.jsonl
and calibration_v2.jsonl from the new Penpot seeds plus a filtered replay
sample of data/train.jsonl. It never writes data/train.jsonl or
data/eval.jsonl: those are the provenance of the model in production and
the gate 1 baseline, and regenerating them is not idempotent anyway, since
stratified_split shuffles one RNG over the concatenated list, so touching
the penpot bucket reshuffles every other bucket's split too.
Two things worth flagging in the mix:
The 45 "corrected penpot basics" the plan lists inside the replay portion
do not come from data/train.jsonl. 21 of its 41 penpot seeds teach
findShapeById(page, id) and 5 use shape.layout, so sampling that bucket
would re-teach the exact bug this phase removes; the forbidden-pattern
filter would drop them anyway. They come from the new corpus instead. This
is a conscious deviation from the plan text and is recorded in the
docstring.
Variation comes only from hand-written meta.paraphrases, never from
automatic value substitution. That is the v1 lesson: perturb_value rewrote
only tool_calls.arguments and left the tool results and the final answer
saying something else, producing 30 self-contradictory examples. A
perturbed Penpot code payload is just broken code.
Linter fixes, both false positives found by running it against the real
corpus:
- flex evidence for a bare appendChild is now scoped to the whole seed
rather than the single payload. A multi-call seed builds the flex board
in call one and stashes helpers in storage, so by the time call two does
main.appendChild(...) neither addFlexLayout( nor .flex appears in that
payload. The old scope flagged exactly the storage-persistence pattern
that execute_code's own description asks for.
- a grey hex is a problem when it is applied, not when it is searched for.
The repair seeds have to name the greys they are about to replace, so
greys are allowed in that group inside a comparison context.
The training container from phases 3-5 no longer exists and nothing in the
repo pinned its versions, so a rebuild could silently change either the
checkpoint key conversion (breaking adapter naming) or the assistant-mask
behaviour (training on system/user/tool tokens). requirements.train.txt
pins what matters and documents the two-phase install: llmcompressor
declares torch>=2.10.0 and the NGC image ships the 2.10.0a0 pre-release,
which pip's resolver reads as older, so it goes in with --no-deps.
Pre-flight verified against the merged bf16 checkpoint on spark:
01_inspect_modules.py prints model.layers.0.linear_attn.*, config.json is
sha256-identical to the base (93a4693f...), and the index keysets match
exactly (1045 tensors, 690 under model.language_model.layers.*, 0 under
model.layers.*). So PEFT will name adapter #2 the same way it named #1 and
ADAPTER_TO_CHECKPOINT_PREFIX in 20_merge_lora.py applies unchanged.
10_train.py: every path and hyperparameter moves to an env var, with the
phase 3 values as defaults so a bare run still reproduces phase 3 exactly.
Adds three guards that each cover a specific silent failure:
- abort if OUTPUT_DIR already holds an adapter, unless ALLOW_OVERWRITE=1.
OUTPUT_DIR was hardcoded to out/lora-adapter, which is the provenance of
the model currently in production.
- MAX_TOKENS aborts rather than truncates. There was no length filter at
all, so one long design trajectory would blow the memory budget hours
into a run; truncating would be worse, since it would silently cut
assistant targets.
- assert use_rslora/use_dora/bias/modules_to_save. rsLoRA scales by
alpha/sqrt(r), so an adapter trained with it would merge at 2.0 where
11.3 belongs and pass every assertion in the merge script.
Also adds a config banner, a token-length histogram, and a per-bucket
assistant-mask ratio report.
New 07_lint_penpot_code.py hard-fails on the forbidden API patterns,
placeholder greys, fabricated penpot_api_info results, toy-shaped ids and
per-category coverage shortfalls. Error-recovery seeds legitimately need
the wrong pattern, so the exemption is derived mechanically rather than
declared by hand: a payload may contain a forbidden pattern only if its
tool result is a real error string from the allow-list and a later payload
in the same seed does the same thing without it.
Run against the 41 existing seeds it reproduces the diagnosis exactly:
110 problems, 36 unique payloads, 0% system messages, zero coverage of
addGridLayout/shadows/uploadMediaUrl/layoutChild, fabricated docs and
toy ids.