Skip to content

[runtime pending] feat(dnn): #649 RulesExplorer i18n — mirror loc() culture cascade - #674

Open
jsboige wants to merge 1 commit into
masterfrom
fix/649-rules-explorer-i18n-loc
Open

[runtime pending] feat(dnn): #649 RulesExplorer i18n — mirror loc() culture cascade#674
jsboige wants to merge 1 commit into
masterfrom
fix/649-rules-explorer-i18n-loc

Conversation

@jsboige

@jsboige jsboige commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

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. Base a41cbda6. [runtime pending] — gated like #596 (not for merge until sandbox runtime validation).

Changes (source-level)

File Change
_RulesExplorer_RuleList.cshtml Add lang detection (CmsContext.Culture) + Loc() helper; route EntityTitle + Summary through Loc().
_RulesExplorer_RuleDetail.cshtml Same helper; route EntityTitle (×3), Summary, Material, Installation, Content, Variants (cond + raw), Memo (cond + raw) through Loc().

Diff: 2 files, +89/−36.

The Loc() cascade — key design decision

<field>_<lang>  →  <field>_en  →  <field>_fr  →  <field>   (generic)

The 4th fallback on the generic (unsuffixed) field is the critical difference from FallacyExplorer:

  • FallacyExplorer reads App.Query["FallaciesFromCSV"] (CSV-backed) → suffixed fields text_fr/text_en exist in the source.
  • RulesExplorer reads the Rule content-type in DB → fields are generic (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 .cshtml templates are interpreted by 2sxc at runtime in DNN — they are not compiled by dotnet build, so they cannot be validated locally by the .NET pipeline. Runtime validation requires:

  1. The 2sxc sandbox (source-level ≠ runtime-validated — same garde-fou as chore(dnn): #131 migrate 12 RazorComponent templates to Razor14 (source-level, runtime pending) #596).
  2. The Rule content-type to gain the suffixed fields (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)

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)

…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>
jsboige added a commit that referenced this pull request Jul 4, 2026
…-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 clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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-probe class n/a, mais même discipline défensive).
  • if (lang != "en") = micro-optimization correcte (évite de re-tester _en quand 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).
  • lang source = CmsContext.Culture.CurrentCode (contexte serveur 2sxc, pas Request/QueryString user-input) → pas d'injection. Split('-')[0] + supported HashSet (8 langues) + fallback lang="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) :

  1. [runtime pending] = author-acknowledged : ces .cshtml sont interprétés par 2sxc à runtime DNN, non compilés par dotnet 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.
  2. 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.
  3. de X à Y joueurs hardcoded FR (RuleList L + RuleDetail) laissé au rail UI-strings séparé (#457/#487) — scoping propre, documenté in-body.
  4. 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).

jsboige added a commit that referenced this pull request Jul 4, 2026
…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>
jsboige added a commit that referenced this pull request Jul 5, 2026
…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>
jsboige added a commit that referenced this pull request Jul 5, 2026
…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>
This was referenced Jul 5, 2026
jsboige added a commit that referenced this pull request Jul 8, 2026
…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>
jsboige added a commit that referenced this pull request Jul 8, 2026
…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>
jsboige added a commit that referenced this pull request Jul 9, 2026
…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>
jsboige added a commit that referenced this pull request Jul 11, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants