Phase 6.3: add the LoRA #2 mix builder and refine the linter
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.
This commit is contained in:
@@ -155,12 +155,22 @@ FORBIDDEN = [
|
||||
|
||||
# `appendChild` solo es correcto sobre un board con flex (o `grid.appendChild(s, r, c)`). Sobre un
|
||||
# padre sin layout hay que usar insertChild. No se puede resolver estaticamente en general, asi que
|
||||
# la heuristica es: si el payload usa `X.appendChild(` con un solo argumento, tiene que haber
|
||||
# evidencia de flex en el mismo payload.
|
||||
# la heuristica es exigir evidencia de flex.
|
||||
#
|
||||
# El alcance de esa evidencia es EL SEED ENTERO, no el payload. Un seed multi-llamada arma el board
|
||||
# con flex en la primera llamada y guarda los helpers en `storage`; para cuando la segunda llamada
|
||||
# hace `main.appendChild(...)`, ni `addFlexLayout(` ni `.flex` aparecen en ese payload. Buscar solo
|
||||
# dentro del payload marcaba como error justo el patron de persistencia en `storage` que la
|
||||
# descripcion de `execute_code` pide explicitamente.
|
||||
APPEND_ONE_ARG_RE = re.compile(r"(\w+)\s*\.\s*appendChild\s*\(\s*[^,()]+\s*\)")
|
||||
FLEX_EVIDENCE_RE = re.compile(r"addFlexLayout\s*\(|\.flex\b")
|
||||
GRID_APPEND_RE = re.compile(r"\.grid\s*\.\s*appendChild\s*\(")
|
||||
|
||||
# Un gris en el `code` es un problema cuando se APLICA, no cuando se BUSCA. Los seeds de reparacion
|
||||
# del grupo B8 tienen que poder detectar los grises que van a reemplazar, y para eso necesitan
|
||||
# nombrarlos. Se permite unicamente en ese grupo y solo en contexto de comparacion/pertenencia.
|
||||
GREY_COMPARISON_RE = re.compile(r"===|==|!==|!=|\.includes\s*\(|\.indexOf\s*\(|\.some\s*\(|\.has\s*\(")
|
||||
|
||||
|
||||
def is_placeholder_grey(hex_str):
|
||||
"""R2: saturacion <= 10 Y 100 <= max <= 220. Los casi-negros y casi-blancos pasan."""
|
||||
@@ -420,6 +430,9 @@ def main():
|
||||
seen = True
|
||||
return False
|
||||
|
||||
seed_code_blob = "\n".join(code for _, code in ordered_calls)
|
||||
seed_has_flex = bool(FLEX_EVIDENCE_RE.search(seed_code_blob))
|
||||
|
||||
code_by_call = {}
|
||||
for m in msgs:
|
||||
for tc in m.get("tool_calls") or []:
|
||||
@@ -460,7 +473,7 @@ def main():
|
||||
problems.append(f"{tag}: [{pname}] ...{frag}...\n -> {why}")
|
||||
|
||||
# appendChild sobre receptor sin evidencia de flex
|
||||
if (APPEND_ONE_ARG_RE.search(code) and not FLEX_EVIDENCE_RE.search(code)
|
||||
if (APPEND_ONE_ARG_RE.search(code) and not seed_has_flex
|
||||
and not GRID_APPEND_RE.search(code)):
|
||||
problems.append(
|
||||
f"{tag}: usa `X.appendChild(shape)` sin ninguna evidencia de flex en el "
|
||||
@@ -478,13 +491,20 @@ def main():
|
||||
f"-- el servidor lo prohibe explicitamente (llega duplicado)"
|
||||
)
|
||||
|
||||
# R2: grises de placeholder en el codigo (nunca permitidos, ni en B8)
|
||||
for hx in HEX_RE.findall(code):
|
||||
if is_placeholder_grey(hx):
|
||||
# R2: grises de placeholder en el codigo. Un gris que se APLICA es siempre un
|
||||
# error; uno que se BUSCA es legitimo, y solo en el grupo de reparacion, que
|
||||
# necesita nombrar los grises que va a reemplazar.
|
||||
for code_line in code.split("\n"):
|
||||
for hx in HEX_RE.findall(code_line):
|
||||
if not is_placeholder_grey(hx):
|
||||
continue
|
||||
if grupo == GREY_INPUT_GROUP and GREY_COMPARISON_RE.search(code_line):
|
||||
continue
|
||||
problems.append(
|
||||
f"{tag}: gris de placeholder {hx} en el `code` (invariante R2). "
|
||||
f"El gris solo puede aparecer como INPUT en los tool results del "
|
||||
f"grupo {GREY_INPUT_GROUP}."
|
||||
f"{tag}: gris de placeholder {hx} aplicado en el `code` "
|
||||
f"(invariante R2). El gris solo puede aparecer como INPUT en los "
|
||||
f"tool results del grupo {GREY_INPUT_GROUP}, o nombrado en una "
|
||||
f"comparacion dentro de ese mismo grupo para detectarlo."
|
||||
)
|
||||
|
||||
# cobertura
|
||||
|
||||
Reference in New Issue
Block a user