[runtime pending] feat(dnn): #649 RulesExplorer i18n — mirror loc() culture cascade - #674
[runtime pending] feat(dnn): #649 RulesExplorer i18n — mirror loc() culture cascade#674jsboige wants to merge 1 commit into
Conversation
…ntime pending) Refactor the 2 RulesExplorer views to be culture-aware, mirroring the FallacyExplorer pattern established in #490. This is the concrete worker-able step (reco from investigation #669) to take the rules page from FR-only (1/8 langs, audit #662) toward 8/8. Changes (source-level, .cshtml interpreted by 2sxc at runtime): - _RulesExplorer_RuleList.cshtml: add lang detection (CmsContext.Culture) + Loc() helper; route EntityTitle + Summary through Loc(). - _RulesExplorer_RuleDetail.cshtml: same helper; route EntityTitle (x3), Summary, Material, Installation, Content, Variants (cond + raw), Memo (cond + raw) through Loc(). Loc() cascade: <field>_<lang> -> <field>_en -> <field>_fr -> <field> (generic). The 4th fallback is the key difference from FallacyExplorer: the Rule content-type currently exposes generic fields (Summary, Material, ...) holding the FR canonical value, with NO language dimensions wired. Without the generic fallback, Loc() would return '' until the DB gains suffixed fields — breaking the current FR rendering. The generic fallback preserves FR during the DB-migration transition. Runtime validation PENDING (gated like #596): these templates are not compiled by dotnet build; they are interpreted by 2sxc in DNN. Validation requires (a) the 2sxc sandbox, AND (b) the Rule content-type to gain the suffixed fields (DB change, gated on the 2sxc portal export — jsboige). Not for merge until sandbox runtime validation (same garde-fou as #596). Numeric fields (MinNbPlayers/MaxNbPlayers) and UrlKey left intact (non-localizable). 'de X a Y joueurs' hardcoded FR left for the UI-strings rail (#457 L1 / #487) — separate lane, out of this refactor scope. Relates: #669 #490 #662 #596 #458. ai-01 dispatch tbxfhu (primary). Co-Authored-By: Claude-Code <noreply@anthropic.com>
…-only binary scan) (#687) Dispatch lev5ct primary (2sxc export unblocked, voie #2 = local backup scan). Read-only extraction from the production DNN backup to unblock the i18n portage field-model decision (#682). Findings (signal, not PASS): - Rule content-type uses GENERIC single-value fields (EntityTitle, Summary, Material, MinNbPlayers, ...) — confirmed at DB level. ZERO language-suffixed fields (Summary_en/Summary_fr absent). Confirms #669 §2c from the view audit. - Production EAV is dimensioned FR+EN only. ru/ar/fa/zh = trace noise; pt/es absent. The 6 release target languages are NOT usable EAV dimensions today. - => #682 field-model decision effectively FORCED toward Path A (lang-suffixed + loc(), mirror #490 / PR #674): Path B (EAV dimensions) would require enabling 5 new DNN content cultures before any translation can attach. Blocker (honest): clean SQL restore denied — jsboi Windows login is not sysadmin on MSSQL$SQLEXPRESS, sa password not in keychain (not guessed, out of read-only scope). Two clean unblockers documented (sysadmin grant OR sa pwd OR voie-1 Playwright export). The binary-scan findings are already conclusive for the portage-direction decision; full per-attribute enumeration gated on the unblocker. Gate: read-only. No prod write, no DB mutation, no credential in the diff. Refs #681, #682, #674, #669. Dispatch msg-20260704T130644-lev5ct. Co-authored-by: Claude-Code <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] — LGTM at source level (cascade logic byte-vérifié firsthand, runtime validation = merge-gate séparé)
Vérification du Loc() cascade (2 fichiers .cshtml au head e83f26d9a6ec, base master) :
Les 2 fichiers portent un helper Loc(entity, field) identique (RuleDetail L + RuleList L14-27), implémentant la cascade documentée :
<field>_<lang> → (si lang≠en) <field>_en → <field>_fr → <field> (generic)
- Null-safety :
dict.ContainsKey(key) ? entity[key]?.ToString() ?? "" : ""— robuste aux champs absents ET aux valeurs null (feedback-notebook-fake-output-probeclass n/a, mais même discipline défensive). if (lang != "en")= micro-optimization correcte (évite de re-tester_enquand lang est déjà en).- Generic fallback
pick(field)= la claim critique du body est VRAIE et correctement implémentée : sans elle,Loc()retournerait""tant que la DB n'a pas les champs suffixés → casserait le rendu FR actuel. Le fallback préserve FR durant la transition DB-migration. C'est la différence saine vs FallacyExplorer #490 (CSV-backed, champs suffixés pré-existants) vs RulesExplorer (DB content-type, champs génériques FR canonique). langsource =CmsContext.Culture.CurrentCode(contexte serveur 2sxc, pasRequest/QueryStringuser-input) → pas d'injection.Split('-')[0]+supportedHashSet (8 langues) + fallbacklang="fr"si unsupported/empty → borné, safe.
Call sites migrés correctement (RuleDetail : EntityTitle ×3, Summary, Material, Installation, Content, Variants cond+raw, Memo cond+raw — tous @Html.Raw(Loc(...)) préservant le rendu HTML ; RuleList : EntityTitle, Summary). Le @if (!string.IsNullOrEmpty(Loc(ruleEntity, "Variants"))) améliore l'ancien @if (ruleEntity.Variants != "") qui aurait NullRef sur variante null → null-safe (improvement, pas juste migration).
Sécurité : @Html.Raw appliqué aux mêmes champs DB-entity qu'avant (Summary/Material/Content/etc.) → 0 nouvelle attack surface, pas de régression XSS. Secret scan (sk-/AKIA/ghp_/password/conn/token) = 0 hit.
Caveats (non bloquants, honnêtes) :
[runtime pending]= author-acknowledged : ces.cshtmlsont interprétés par 2sxc à runtime DNN, non compilés pardotnet build→ validation runtime réelle exige le sandbox 2sxc + DB-migration (champs suffixés), gated comme #596. Mon LGTM est source-level (cascade logique + null-safety + security + claim generic-fallback vérifiés firsthand) ; le merge-gate runtime reste l'action séparée (jsboige sandbox). Cadre honnête, pas un rubber-stamp.- Diff RuleList : le fichier entier apparaît comme -25/+53 (full rewrite) à cause d'une normalisation line-ending (CRLF↔LF) superposée à l'ajout réel du helper Loc (~28 lignes logiques). Cosmétique, non trompeur — le body claim +53/-25 est exact, le delta logique est l'ajout Loc + 2 call sites.
de X à Y joueurshardcoded FR (RuleList L + RuleDetail) laissé au rail UI-strings séparé (#457/#487) — scoping propre, documenté in-body.- DB-migration suffixed fields = action jsboige séparée (portal export 2sxc) — le generic fallback ponte correctement le gap jusqu'alors.
Mirrors #490 pattern prouvé en production. Code-side prerequisite pour L3 (#669) livré. Décision merge Emerjesse (post-sandbox-validation).
…ing + switcher) (#693) Design sketch (no prod execution) for step 4 of the #669 DNN i18n portage plan. Triggered by ai-01 dispatch sde6s0 (tertiary). jsboige-owned, parallel/non-blocked. Covers the 3 DNN-config layers to serve 8 cultures: - DNN content languages: enable 7 cultures (en/ru/pt/es/ar/fa/zh), FR canonical. - URL routing: path-based vs query-string (decision = jsboige, current state both broken: /en-US/ 404, ?language= ignored, <html lang> stuck fr-FR). - Language switcher: populate the <div class="language"> shell shipped by #490 (currently unpopulated). - <html lang>/<dir> reflection (RTL for ar/fa; #685 visual QA entry condition). Key insight: 2sxc loc() cascade (FallacyExplorer post-#490, RulesExplorer post-#674) already reads CmsContext.Culture.CurrentCode. #683 is purely the upstream DNN-config layer that makes CmsContext.Culture return the right value. Path A (lang-suffixed fields + loc()) does NOT need 2sxc EAV dimensioning. Design only: no prod write, no admin change, no web.config mutation. Decision points surfaced for jsboige (routing mode, culture codes, switcher UX, timing). Co-authored-by: Claude-Code <noreply@anthropic.com>
…ng list (49 fields) (#694) Decision-input for #682 (step 2 of the #669 portage plan). Triggered by ai-01 dispatch 0hbg9t (primary). Analysis only — no DB write. Recommendation: Path A (lang-suffixed fields + loc() cascade, mirror #490/#674). - Provision 7 translatable fields x 7 non-FR langs = 49 new suffixed fields on the Rule content-type (EntityTitle/Summary/Material/Installation/Content/ Variants/Memo x en/ru/pt/es/ar/fa/zh). - FR stays canonical (unsuffixed) — no data migration. - Path B (EAV dimensions) rejected: prod EAV is FR+EN only (#687), Path B would block on enabling 5 cultures + their dimensions; Path A needs none of that. Exact field-list derived from #669 Sec 2c (view-template audit) + #687 (binary scan) + PR #674 Loc() cascade (7 fields routed). The 4th-fallback design (generic = FR canonical) preserves current FR rendering during transition. Open (gated): exact 2sxc attribute types (String/Hyperlink/...) remain gated on the sysadmin unblocker (#687 Sec 3 — jsboi has no server role, not mapped in ArgumentumGames DB). Best-effort inference provided; jsboige confirms via live 2sxc editor. Re-verified the sysadmin blocker this tick (still active). Co-authored-by: Claude-Code <noreply@anthropic.com>
…gs, prep) (#696) Manifest/prep for #684 (step 5 of the #669 portage plan, the dominant cost). Triggered by ai-01 dispatch 0hbg9t (secondary). Prolongs #692. Enabled=false, no execution. Covers: - Entity inventory: ~30 rich-HTML prose entities (6 fields x ~5 games) x 7 target langs = ~210 translation units (exact count gated on #681 export). - Source = 2sxc Rule content-type FR entities (#681 export, jsboige), NOT the Rules CSV (#692 = secondary reference; site content richer HTML DB-only). - Sink = 49 suffixed fields provisioned by #682. - Chunking: Scenarii-style SequentialChunks ChunkSize>=12, OpenRouter gpt-5.5, HTML-preservation prompt, max_completion_tokens ~7000. - DatasetUpdater task config sketch (Enabled=false), fields = raw export headers. - Re-import plan: entity count parity, 0 FR-contaminated cell guard (#216-style, model = Rules CSV scan #692), harvest invariance check. - Per-lang effort: one DatasetUpdater pass comparable to Scenarii 167x8. Manifest only: no translation, no API calls, no DB write. Gated on #681/#682/#674/#683. Co-authored-by: Claude-Code <noreply@anthropic.com>
…columns AIF_attackType + AIF_attackedNode (#753) Reassigned by ai-01 (po-2023 dead-channel ~2j, dispatch 1pgunw). First prod CSV write of chantier #498. Adds 2 trailing-AIF-block columns to Cards/Fallacies/Argumentum Fallacies - Taxonomy.csv, inserted AFTER AIF_skosMappingType (col idx 94), before `shape` (idx 95) — new cols join the existing AIF block (cols 91-96). Derivation: §7 of the 12 docs/taxonomy/498-aif-*-cluster.md cluster docs. attackType = bolded token in each `### pk NNN -> **type**` header. attackedNode DETERMINISTIC per ratified contract #707§4 Option (a): undercut->RA-node, undermine->I-node, rebut->CA-node. Distribution (46 filled of 1408): 44 undercut/RA-node + 2 undermine/I-node (838, 843). 0 rebut (consistent with rebut-rarity). 17 FAIL-LOUD/gap leaves left EMPTY (0 fabrication #677): CA-token-missing {829,840,847,848,853} + RA-node/gap {832,834,835,837,861,868-874}. Remaining 1345 leaves (not decomposed in §7) empty. SURGICAL byte-exact: rewrites NO existing field. Byte-preservation VERIFIED independently — 0 mismatches: every existing field (idx 0-94 and 95-101) is byte-identical between backup and result; only 2 new fields inserted per row. CRLF (1409) + 144 embedded-LF + BOM preserved. CsvHelper maps by header name, so insertion breaks no existing mapping. Tests: dotnet test = 594 pass / 1 fail (#133 OWL pre-existing, unrelated) / 5 skip — 0 new failures; Fallacies-loading tests all pass. Audit: tools/498-fallacies-aif-columns-apply.py (re-runnable derivation + surgical apply + verify). Gated: review ai-01 + veto jsboige (structural reassignment). Does NOT self-merge. Does NOT touch #674/#666/#596 (HOLD). Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude-Code <noreply@anthropic.com>
…v2) into 2 new columns (#755) Executes the [primaire] mirror per ai-01 dispatch tw8www (post #753 merge). Applies the ratified deterministic back-fill (plan #750 v2) to Cards/Fallacies/Argumentum Virtues - Taxonomy.csv as the same 2 columns (AIF_attackType + AIF_attackedNode) — anti-drift with the Fallacies contract (#753). Both taxonomies now share an identical AIF column contract. Derivation (plan #750 v2, re-confirmed programmatically on master): default undercut/RA-node override undermine/I-node if opposes {889 Mensonge, 804 Acception arbitraire} override rebut/CA-node if opposes {340 Appel aux conséquences} Distribution EXACT: 206 undercut + 13 undermine {34,36,49,55,56,73,137,139,140, 153,154,155,158} + 3 rebut {53,54,165} = 222 nodes with scheme; 1 root node (pk 0, no scheme) = empty. SURGICAL byte-exact (same technique as #753, tools/499-...-apply.py on master via #754): rewrites NO existing field. Independently VERIFIED post-write backup-vs-result: 0 byte-preservation mismatch across all 223 data rows (fields idx 0-77 and 78 original -> 80 after byte-identical); CRLF (224) + BOM preserved; re-parse clean 81 cols (was 79). CsvHelper maps by header name -> 0 ClassMap break. Placement: after AIF_skosMappingType (idx 77), before print_and_play (->80). Same convention as #753 (per ai-01 verdict: keep after-AIF-block). Tests: dotnet test = 594 pass / 1 fail (#133 OWL pre-existing, unrelated) / 5 skip -- 0 new failures; Virtues-loading tests (VirtueClassMapRegressionTests, EntityClassMapRegressionTests) all pass. 0 entity/test references new cols. Gated: review ai-01 (independent byte-check, same as #753). Does NOT self-merge. Does NOT touch #674/#666/#596 (HOLD). Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude-Code <noreply@anthropic.com>
…nfirms Path A, 3 deltas corrected (#759) Revision of #682 (PR #694, 2026-07-05) in light of the live 2sxc v21 export (#681, delivered by web1, read-only SQL Method B). Addendum/supersede-note, NOT a rewrite — the original Path A decision (49 suffixed fields, FR canonical) is confirmed correct. 3 deltas corrected (the binary scan #687 could not see these): 1. Schema family: ToSIC_SexyContent_* (2sxc 15.02) -> ToSIC_EAV_* (2sxc v21). Spec §4 outdated. 2. app-id: app=31 (debate/fallacies) was wrong-app probed -> Argumentum game = app=60 (ZoneID=3). 3. Attribute types: previously gated on sysadmin unblocker -> RESOLVED. All 7 translatable fields are Type:String (Wysiwyg/rich-HTML). Full 15-attribute schema enumerated (AttributeSet 377). Key clarifications: - "7->15 attrs" dispatch wording = 15 = TOTAL attrs on content-type, not 15 translatable. Translatable count stays 7 (Title/Summary/Material/Installation/Content/Variants/Memo). The 8 extra attrs (Parent/Date/Author/Licence/Original + Min/MaxNbPlayers + UrlKey) are all non-localizable. Provisioning list (49 suffixed fields) is UNCHANGED. - "49 suffixes" = content-type attributes (this scope), NOT res.* label resources. The res.* rail (9 res.Rule* keys, eid=10340) is the separate #490/#457 localization lane. - #684 translation scope corrected: export confirms 5 published rules (not 24-30) — DB in seed/pre-migration state. - res.Rule* live FR values captured for #490 (next): 5 confirmed + 4 divergent/gap to reconcile. Added banner to the original 682-field-model-decision-input.md pointing to this revision. Analysis-only. No DB write. No prod interaction (export = read-only SELECT). No #674 merge. Co-authored-by: Claude <noreply@anthropic.com>
…v21 correction (#777) Dispatch bzkvnh (ai-01) — primaire #682 + secondaire spec staleness fix. Base master 7406bb8. Theme: "2sxc v21 ground truth" (now that live export #774 is committed, docs can lean on real data). 1. NEW docs/dnn-localization/682-path-a-provisioning-manifest.md — the concrete ops-gated artifact: the 49 lang-suffixed attributes (7 translatable String fields x 7 non-FR langs) to provision on AttributeSet 377 (Game Rule, app=60) so PR #674 Loc() cascade resolves per request culture. Ground truth sourced from live export 11-game-rule-schema.json. 2 apply paths (2sxc admin UI preferred / Method B SQL draft skeleton). HARD gate: zero DB write from repo. 2. 2sxc-export-spec.md — correct 15.02 staleness vs live v21 (#774): - SUPERSEDED banner (objectives P0/P1 accomplished: res.* FR VERIFIED via #772, HOLD #490 lifted, export delivered #774). - S2 scope table: + Statut column reflecting export findings (Glossary3/Faq4 NOT FOUND; Game Rule = 5 entities not 24-30). - S3 Method A UI: app=31 -> app=60, Rules -> Game Rule (id 377), res.* on App-Resource eid=10340. - S4 Method B SQL: ToSIC_SexyContent_* -> ToSIC_EAV_* (v21 family), FR dimensionless (DimensionID IS NULL) not v.Language='fr-FR', eid=10340. - S7: version 15.02 -> v21. No DB write. No Cards/ CSV. No runtime change. Doc-only. Co-authored-by: Claude-Code <noreply@anthropic.com>
What
Refactor the 2 RulesExplorer views to be culture-aware, mirroring the
loc()pattern established in FallacyExplorer (PR #490). This is the concrete worker-able step — reco from investigation #669 — to take the rules page from FR-only (1/8 langs, audit #662) toward 8/8.Primary task from ai-01 dispatch
tbxfhu. Basea41cbda6.[runtime pending]— gated like #596 (not for merge until sandbox runtime validation).Changes (source-level)
_RulesExplorer_RuleList.cshtmllangdetection (CmsContext.Culture) +Loc()helper; routeEntityTitle+SummarythroughLoc()._RulesExplorer_RuleDetail.cshtmlEntityTitle(×3),Summary,Material,Installation,Content,Variants(cond + raw),Memo(cond + raw) throughLoc().Diff: 2 files, +89/−36.
The
Loc()cascade — key design decisionThe 4th fallback on the generic (unsuffixed) field is the critical difference from FallacyExplorer:
App.Query["FallaciesFromCSV"](CSV-backed) → suffixed fieldstext_fr/text_enexist in the source.Summary,Material, …) holding the FR canonical value, with no language dimensions wired.Without the generic fallback,
Loc()would return""until the DB gains suffixed fields — breaking the current FR rendering. The generic fallback preserves FR during the DB-migration transition: today (FR-only DB) the cascade falls through to the generic field and renders exactly as before; once suffixed fields are added per language, the cascade picks them up automatically.Runtime validation PENDING (gated like #596)
These
.cshtmltemplates are interpreted by 2sxc at runtime in DNN — they are not compiled bydotnet build, so they cannot be validated locally by the .NET pipeline. Runtime validation requires:Summary_en,Summary_ru, …) — a DB change, gated on the 2sxc portal export (jsboige, per docs(dnn): i18n porting mechanism investigation (read-only) — coupled-release critical path #669 §3). Until then, the generic fallback keeps FR rendering intact.Not for merge until sandbox runtime validation.
Out of scope (noted, not done here)
de X à Y joueurshardcoded FR (RuleList l.15, RuleDetail l.61) — left for the UI-strings rail (feat(dnn): localize site content — CSV extraction, DatasetUpdater translation, re-import workflow #457 L1 / feat(dataset-updater): #457 DNN UI strings localization infra (Enabled=false) #487,Enabled=false), separate lane.@Resources.*labels (RuleSummary, RuleMaterial, …) — already culture-aware via 2sxc App Resources; untouched.MinNbPlayers/MaxNbPlayers) +UrlKey— non-localizable; untouched.Why this is the critical-path step
Per #669: the multilingual porting has 3 layers (L1 UI strings rail built / L2 HTML pages not wired / L3 content DB-only bulk). The RulesExplorer refactor is the code-side prerequisite for L3 — without
Loc()in the view, no amount of DB translation will surface on the rules page. It mirrors a pattern proven in production by #490, so the risk is contained and the runtime behavior is well-understood.Relates: #669 #490 #662 #596 #458. ai-01 dispatch
tbxfhu(primary).🤖 Worker po-2023 (GLM)