feat(owm): deterministic rich capture — headers, evolve, inertia, pipeline, method, custom phases (1/3) - #45
Conversation
formicoidea
left a comment
There was a problem hiding this comment.
Recette adversariale — verdict : changements demandés
Trois revues indépendantes (contrat/architecture, LLM/prompts/recettes, tests/régressions), chaque finding reproduit par exécution, puis passe de réfutation avec ré-exécution indépendante des sondes décisives. Aucun bloquant n'a pu être réfuté.
Bloquants
B1 — Corruption silencieuse : evolve attribué au mauvais composant quand un nom contient un nombre.
owm/emit/dsl.mts:137 + owm/parse/dsl.mts:179-187. Sonde : HTTP (0.2) + HTTP 1.1 Gateway (0.4, evolvesTo 0.8) → emit écrit evolve HTTP 1.1 Gateway 0.8 ; au parse, le vendored découpe sur la première décimale → HTTP hérite d'un evolvesTo scalar 1 (le « 1.1 » du nom, clampé), le Gateway perd le sien, warnings ne contient rien d'utile. Donnée fausse et indétectable — rédhibitoire pour un socle de dataset.
B2 — Invariant round-trip byte-exact violé (ast-schema § 2.3.1 : bug bloquant).
evolvesToscalar 0 ou 1 :emitécritevolve X 1(round2 sans point décimal), la regex du parseur (/\s[0-9]?\.[0-9]+/) ne matche pas → « dropped: not a declared component » →emit(parse(emit(m))) ≠ emit(m).owm-dsl.mts:218-225+parse/dsl.mts:281-283.context: ''(légal au schéma) :// context:émis,HEADER_COMMENTexige(.+)→ perdu au retour,losses: [].owm-dsl.mts:128-134+emit/dsl.mts:87-92.evolveType≠natural: perdu sans note de perte, réinjecténatural.emit/dsl.mts:129-141.
B3 — Crash map: null sur une nomenclature d'axe ≠ 4 phases.
owm-dsl.mts:257-262 (signature relâchée en readonly string[]) + parse/dsl.mts:147-153 (.trim() sur undefined). Sonde : renderConfig à 2 labels — forme V3 valide, explicitement autorisée par le prompt du lint et utilisée par la propre fixture de text/lint/default.test.mts — → parsed: false, insight Cannot read properties of undefined (reading 'trim'), carte entièrement perdue. À 5 labels : troncature silencieuse à 4.
B4 — Le contrat de perte du dataset v2 casse au merge : 82 % au lieu de 100 %.
La branche est 2 commits derrière staging et ne voit pas e835393 (harnais v2). scripts/build-dataset.mts:442,601 (staging) exige l'insight evolvesTo targets are not projected que cette PR supprime en projetant evolve. Sur l'arbre mergé : --count 100 --seed 42 → 82,0 % (18 échecs, signature unique) et build-dataset.test.mts 19/21. La table LOSS_CONTRACT doit évoluer dans le même changement que la projection.
B5 — La recette text-to-canonical échoue sur son chemin par défaut, et le chemin structured du lint ne peut pas refuser.
recipes/render/map/text-to-canonical.recipe.json:15-19: avectarget: 'json'(défaut), le lint sortdsl: null; l'étapeowm:parse:dslreçoit un input invalide →$.output.result = { map: null, parsed: false }+ insight mensonger « upstream step not yet promoted », alors que la carte est dans$.linted.result.map.text/lint/default.mts:205-224: le chemin structuré passeoutputFormat: json_schema— le modèle est contraint de produire une carte, il ne peut ni émettreNOT_A_VALUE_CHAINni refuser.{text:"écris-moi un poème"}→ carte inventée,linted: true. L'invariant annoncé en tête de fichier (« the deterministic layer stays the only producer ») est faux sur ce chemin.
Majeurs
- Décorateurs projetés mais morts au rendu :
pipelineGeometryposé sanstype: 'pipeline'(invalide auvalidateMapdu renderer, bande jamais dessinée) —parse/dsl.mts:244,267-276etpng.mts:243-250;component.inertiajamais lu par le renderer (la barre vient deevolvesTo[].inertia) ;methodsansrenderConfig.style.decorators→ marqueur jamais dessiné. - Couleur → noir : les prompts demandent un nom CSS (
red),resolveColorne connaît que hex + palette Tailwind →#000000sans warning.prompts/render-image-parse-png.system.md:89-91,render-text-lint.system.md:47. - Décorateurs inventés par détection en sous-chaîne :
component inertia dampenerre-parse avecinertia: trueet re-émet… inertia;(build tools) X→method: build.emit/dsl.mts:160-176+ vendoredsetInertia/methodDecorator. - Alias français accentués = code mort :
parseHeaderComments(owm-dsl.mts:129) n'accepte pas les accents (\w), donctemporalité/granularité/portée/périmètrene matchent jamais ; le test passe en assertant la valeur par défaut.parse/dsl.mts:52-61,parse/dsl.test.mts:190-204. - Assertion supprimée :
assert.ok(WardleyMapSchema.safeParse(map).success)retirée deowm/parse/dsl.test.mtsau commit qui élargit la sortie du parseur. À restaurer. // context:perdu sievolutionprécède les en-têtes — ordre que le prompt du lint encourage.owm-dsl.mts:153.- Hors catalogue AST : le sous-domaine
textet la commandelintn'existent pas dans la spec normative (ast-schema.md:242-257, domaine déclaré exhaustif) ; la recette inventerender:mapcomme namespace (clé de feature-flag PostHog incluse). - JSON Schema envoyé au provider non contraignant :
z.toJSONSchema(WardleyMapSchema, {unrepresentable:'any'})→scalar: {}(tout accepté),evolveTyperequis en contradiction avec le prompt ; échec avalé parcatch {}+ dégradation.default.mts:60-68,199. - Injection via le document linté : source interpolée brute,
parseDelimitedBlockprend le premier blocLINT_START(un bloc injecté gagne),payload.includes('NOT_A_VALUE_CHAIN')matche n'importe où.default.mts:255-268. docs/functional/tools-reference.md:259: compteurs faux (70/24) non corrigés par le commit de sync.
À porter au crédit
Aucune fixture trafiquée ; compteurs de boot exacts (25 réelles / 86) ; règle prompts system/user respectée (0 {{ dans les system) ; tests sans appel LLM réel ; typecheck vert.
Recommandation
Scinder en trois : (1) l'élargissement OWM déterministe, mergeable après correction des round-trips B1-B3 + mise à jour du LOSS_CONTRACT dans le même changement (rebase sur staging obligatoire) ; (2) le linter + recette, après re-conception du chemin par défaut et de la capacité de refus ; (3) les décorateurs vision, après que la projection produise des cartes valides au rendu (validateMap en garde de sortie serait le bon oracle).
🤖 Generated with Claude Code
…eline, method, custom phases Review-hardened deterministic enlargement of the OWM dialect (PR 1/3 of the lint/deterministic split): - parse:dsl captures `// key: value` headers (unicode keys, French aliases folded onto canonical English ones, headers still read after an evolution directive) into result.header + PurposeContext + map.context; emit:dsl re-emits `// context:` (never when empty — the header grammar requires a value). - parse:dsl projects evolve->evolvesTo (component inertia mirrored onto the target, where the renderer reads it), inline inertia, pipeline-> type 'pipeline' + pipelineGeometry (validateComponent refuses the geometry elsewhere), (build|buy|outsource)->method, and a 4-label evolution directive->input-shape V3 renderConfig phases. - emit:dsl is the exact inverse, with grammar-hazard guards: evolve refused for names embedding a decimal (vendored pattern rebinds the directive) or quote-wrapped names; maturities always carry a decimal point; decorator-keyword substrings flagged; non-4 phase arities and non-natural evolveTypes dropped with notes. Byte-exact round-trip holds on the emitted dialect. - dataset v2 LOSS_CONTRACT updated in the same change: evolvesTo is NOT LOST on owm; the lossy injection only targets OWM-referenceable names. --count 100 --seed 42 = 100%. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3569ed8 to
b5006a4
Compare
|
Revue traitée — tous les bloquants et majeurs adressés, et la recommandation de scission appliquée :
Détail des sondes de la revue rejouées en tests unitaires : 🤖 Generated with Claude Code |
PR 1/3 of the lint/deterministic split requested by the adversarial review (restructured branch — the lint and vision parts moved to stacked PRs).
Scope (deterministic OWM only)
// key: valueheader capture (unicode keys, French aliases, headers read past anevolutiondirective) →result.header+PurposeContext+map.context;emit:dslre-emits// context:(never when empty).evolve→evolvesTo(component inertia mirrored onto the target, where the renderer reads it), inlineinertia,pipeline→typepipeline+pipelineGeometry(render-valid pervalidateComponent),(build|buy|outsource)→method, 4-labelevolutiondirective→input-shape V3 renderConfig phases.LOSS_CONTRACTupdated in the same change;--count 100 --seed 42= 100% on the rebased tree.Review blockers addressed here
1→1.0); emptycontextnever emitted; non-naturalevolveTypenoted.evolutiondirective ≠ 4 labels no longer crashes the parse; emit restricted to exactly 4 (other arities stay in the JSON path, noted).LOSS_CONTRACTevolves with the projection (evolvesTo NOT LOST on owm; lossy injection targets only OWM-referenceable names).future, not the default); schema-validity assertion restored; decorator-keyword substring hazard flagged.Stacked: split/text-lint (2/3) → split/vision-decorators (3/3).
🤖 Generated with Claude Code