Skip to content

feat(csv): #499 Phase 1 — add 12 relational/AIF columns to Virtues prod CSV - #590

Merged
jsboige merged 1 commit into
masterfrom
chore/499-virtues-prod-write-relational-columns
Jun 23, 2026
Merged

feat(csv): #499 Phase 1 — add 12 relational/AIF columns to Virtues prod CSV#590
jsboige merged 1 commit into
masterfrom
chore/499-virtues-prod-write-relational-columns

Conversation

@jsboige

@jsboige jsboige commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What

#499 Phase 1 — prod-write: append 12 relational/AIF columns to the Virtues prod CSV (Cards/Fallacies/Argumentum Virtues - Taxonomy.csv, 66 → 78 cols), populated from the 12 validated annotation CSVs in docs/taxonomy/ (joined by virtue_pk == pk).

De-gated by jsboige (interactive 2026-06-23, "GO" on docs/taxonomy/499-virtues-parity-closure.md); dispatched by ai-01 as Phase 1 of the #499 deep-queue. Data-only (the OWL code propagation = Phase 2, separate PR).

Ground-truth re-verified on current master f432455e (spec #580 claims held exactly)

Measure Value
Prod Virtues rows / distinct pk / cols 223 / 223 / 66
Annotation CSVs / total rows / distinct virtue_pk / dups 12 / 222 / 222 / 0
In-annotations not in prod 0
In-prod not annotated 1 (pk=0 taxonomy root — correctly left out)
Target cols populated (crossLink_Opposes / skosDirectRef / skosMappingType) 222 / 222 / 222

The 12 columns (verbatim Fallacies names, appended after link_fa col 66)

crossLink_PredatesOn, crossLink_Denounces, crossLink_Leverages, crossLink_Allows,
crossLink_Opposes, crossLink_Inverts, crossLink_Mirrors, crossLink_IsRelatedTo,
AIF_skosDirectRef, AIF_skosExceptionRef, AIF_skosOther, AIF_skosMappingType
  • 3 populated for 222 rows: crossLink_Opposes (Fallacy PK semicolon-list), AIF_skosDirectRef (Walton scheme label), AIF_skosMappingType (FR prose critical question).
  • 9 structurally empty by design: a Virtue's only cross-taxon link is "Opposes" its prevented Fallacy family; Exceptions/Other are Fallacy attributes.
  • pk=0 (root) left empty.

Additive proof (the critical invariant)

Existing 66 columns are byte-identical before/after0 value drift across 223 × 66 = 14 518 cells. The write is a line-append (12 new cells per line); existing content untouched. Guard: the script aborts if raw CRLF-line count ≠ 223 (embedded-newline safety).

Verification

  • Additive: 0 existing-cell drift ✓ | first-66 col names unchanged ✓
  • 66 → 78 cols, 12 new cols in spec order at positions 67–78 ✓
  • 3 target cols filled 222/222; 9 structural empties = 0 non-empty; pk=0 empty ✓
  • Cross-link integrity (tools(taxonomy): reusable anti-fab validator for scale-up CSVs #518-style): 60 distinct Fallacy PKs in crossLink_Opposes0 dangling (all resolve in the Fallacies corpus PK column) ✓
  • UTF-8 no-BOM, CRLF preserved, 998061 bytes (+30898) ✓
  • Spot-check pk=71 (the "Valid results" Virtue from fix(csv): #202 translate 9 RU Virtues cells 'Valid results' → Корректные результаты #583): crossLink_Opposes=[681;690], AIF_skosDirectRef=[Argument from Sign], AIF_skosMappingType=[Les résultats sont-ils correctement calculés, reproductibles et cohérents… ?] (FR prose comma correctly quoted) ✓
  • git diff --stat: 224 ins / 224 del (every line gets 12 cols appended; existing cells unchanged within each line) ✓
  • Unit tests: 533 passed / 0 failed / 5 skipped (baseline unchanged — unmapped cols ignored by CsvHelper on read) ✓

Phase 2 (separate code PR — NOT this one)

ArgumentVirtueClassMap mapping of the 12 new columns + OwlAdapter Virtues pass (currently Fallacies-only). No code changed in Phase 1.

🤖 Worker po-2024 · #499 Phase 1 prod-write · 12 additive cols (3 populated ×222) · additive-proof 0 drift · crossLink 0 dangling · 533 tests pass · Phase 2 (OWL code) next

…od CSV

Append-only prod-write: 12 new columns to Cards/Fallacies/Argumentum Virtues -
Taxonomy.csv (66 -> 78 cols), populated from the 12 validated annotation CSVs
in docs/taxonomy/ (joined by virtue_pk == pk). De-gated by jsboige (interactive
2026-06-23, "GO" on docs/taxonomy/499-virtues-parity-closure.md); dispatched by
ai-01 as Phase 1 of the #499 deep-queue (data write; OWL code = Phase 2 PR).

Columns added (verbatim Fallacies names, after link_fa col 66):
  crossLink_PredatesOn, crossLink_Denounces, crossLink_Leverages,
  crossLink_Allows, crossLink_Opposes, crossLink_Inverts, crossLink_Mirrors,
  crossLink_IsRelatedTo, AIF_skosDirectRef, AIF_skosExceptionRef,
  AIF_skosOther, AIF_skosMappingType

3 of 12 populated for 222 rows (the real Virtue nodes):
  crossLink_Opposes    (semicolon-list of opposed Fallacy PKs)
  AIF_skosDirectRef    (Walton scheme label)
  AIF_skosMappingType  (FR prose critical question, comma-quoted)
9 cols structurally empty by design (Virtue->Fallacy is solely "Opposes";
Exceptions/Other are Fallacy attributes). pk=0 (taxonomy root) left empty.

ADDITIVE PROOF: existing 66 cols byte-identical before/after (0 value drift
across 223x66 = 14518 cells). Line-append only; existing content untouched.
Guard: aborted if raw CRLF-line count != 223 (embedded-newline safety).

Verified: 223 rows x 78 cols, 3 target cols filled 222/222, 9 structural empties
0 non-empty, crossLink_Opposes 60 distinct Fallacy PKs all resolve in Fallacies
corpus (0 dangling). UTF-8 no-BOM, CRLF preserved, 998061 bytes (+30898).

Phase 2 (separate code PR): ArgumentVirtueClassMap mapping + OwlAdapter Virtues
pass. No code changed in Phase 1.

Co-Authored-By: Claude Opus 4.6 <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] — data-integrity review (RFC4180 parse of all 223 rows, old-master vs head blob)

✅ PASS — no data loss.

  • Schema 66 → 78 cols; every one of the 223 data rows parses to exactly 78 fields (no ragged rows).
  • The existing 66 columns are byte-identical across all rows (verified field-by-field). No unintended edits, no truncation.
  • Header prefix preserved; 12 new cols: crossLink_{PredatesOn,Denounces,Leverages,Allows,Opposes,Inverts,Mirrors,IsRelatedTo}, AIF_skos{DirectRef,ExceptionRef,Other}, AIF_skosMappingType.
  • Join integrity matches the PR's ground-truth table: populated cols show 222/223 non-empty = the 222 annotation rows; the single orphan prod row (no matching virtue_pk) is correctly left empty.
  • No secrets / no control-char corruption in the new blob.

⚠️ One to confirm (non-blocking): only 3/12 new cols carry data — crossLink_Opposes (222, 147 distinct), AIF_skosDirectRef (222, 14 distinct), AIF_skosMappingType (222, 222 distinct). The other 9 are empty across all rows. Given Phase 2 = OWL/crossLink propagation (separate PR) the empty crossLink_* set is plausibly expected — worth an explicit 👀 that these are "source annotations had no data for those relations," not a silent join gap. If only those 3 had source data today, the change is internally consistent.

No merge-blocking issues. Phase-1 prod-write looks structurally sound.

@jsboige
jsboige merged commit 6e029a6 into master Jun 23, 2026
3 checks passed
@jsboige
jsboige deleted the chore/499-virtues-prod-write-relational-columns branch June 23, 2026 15:13
jsboige added a commit that referenced this pull request Jun 24, 2026
…corpus, aif:goodTenorOf) (#592)

* feat(ontology): #499 Phase 2 — map 12 relational/AIF columns on Virtue entity

Phase 2 ClassMap: map the 12 columns appended to the Virtues prod CSV in #590
(66->78) onto Virtue / VirtueClassMap -- the entity that loads the prod CSV
(CsvType = typeof(Virtue), AssetConverterConfig.cs) and feeds the OWL/mindmap
pipeline (ParallelVirtueDocumentCreatorConfigBase<Virtue>).

Entity-target correction: ai-01's dispatch named ArgumentVirtueClassMap, but
ArgumentVirtue is only the AutoMapper ArgumentVirtue->Fallacy projection target
(MappingProfile.cs) -- it is NOT a CSV loader. The prod Virtues CSV + downstream
pipeline operate on Virtue. Flagged to ai-01 (the two entities are near-identical;
the naming conflates them in the dispatch and in CLAUDE.md).

All 12 columns .Optional() -- 9 structurally empty by design, 3 populated for the
222 real Virtue nodes (pk=0 root left empty):
  crossLink_PredatesOn..IsRelatedTo (x8) -- only crossLink_Opposes populated
    (semicolon list of prevented Fallacy PKs)
  AIF_skosDirectRef/ExceptionRef/Other/MappingType (x4) -- DirectRef + MappingType
    populated (Walton scheme label + FR-prose critical question)

Tests (+2 in VirtueClassMapRegressionTests):
  - Maps499RelationalAndAifColumns: populated row mirrors prod pk=71
    (crossLink_Opposes=681;690, AIF_skosDirectRef=Argument from Sign,
    AIF_skosMappingType=FR prose RFC4180-quoted for its internal comma);
    9 structural empties map to "" (CsvHelper empty-cell behavior, not null).
  - 499ColumnsAbsent_DoesNotThrow: Optional contract -- a header missing the
    relational block loads without throwing (property null, not throw).
  Suite: 533 -> 535 passed, 0 failed, 5 skipped.

Phase 2 part 2 (OwlAdapter Virtues OWL pass) follows in this branch once the 2
flagged design adaptations are confirmed with ai-01:
  1. AIF: Virtue AIF_skosMappingType is FR prose (not a skos:*Match enum token),
     so the Fallacies switch cannot fire -> adapt to skos:relatedMatch + annotation.
  2. crossLink_Opposes is cross-corpus (Virtue->Fallacy PKs); Fallacy concept URIs
     derive from TextEn, not PK -> the Virtues pass must load the Fallacies corpus
     to resolve PK->URI for the 0-dangling DoD.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ontology): #499 Phase 2 Part 2 — VirtueOwlGeneratorConfig (mono-corpus OWL pass)

Create the Virtues OWL/SKOS generator from scratch (mirroring OwlGeneratorConfig /
OwlDocumentConfig for Fallacies) and wire it into the pipeline alongside the Fallacies
pass. Emits argumentum_virtues.owl: a concept scheme with the 7-family hierarchy
(skos:narrower via Path/Depth, same GetParent shape as Fallacies), one concept per
Virtue node (prefLabel/description FR+EN on Title*/Description*), and a custom AIF
object property aif:goodTenorOf linking each Virtue to its Walton argumentation scheme.

Design decisions (jsboige interactive 2026-06-24):
- AIF predicate = custom `aif:goodTenorOf` (DeclareObjectProperty), NOT a skos:*Match
  token. The Virtue AIF_skosMappingType column holds the FR-prose critical question,
  not a skos enum token, so the Fallacies switch over AIFSkosMappingType cannot fire
  (design adaptation 1). The prose is carried as an rdfs:comment annotation.
- The AIF namespace already exists in the Fallacies pass
  (ExternalReferenceOntologyNamespaceURI = http://www.arg.dundee.ac.uk/aif#) — no new
  namespace introduced; aif:goodTenorOf resolves in the Dundee AIF ontology.
- Cross-corpus Virtue↔Fallacy links (crossLink_Opposes PK→URI resolution) DEFERRED to
  Phase 3. Resolving Fallacy PKs requires loading the Fallacies corpus (concept URIs
  derive from GetId(TextEn), not PK) — new architecture beyond this single-dataset
  pass. This pass is strictly mono-corpus Virtues.

Wiring (AssetConverterConfig.cs):
- new VirtueOwlGeneratorConfig property (instantiated, default Enabled=true).
- invoked in the OwlGenerator mode block (both sync + async branches), parallel to
  OwlGeneratorConfig.Apply.

Tests (+5 in VirtueOwlGenerationContractTests): GetId mirrors the Fallacies IRI transform
(byte-identical, asserted on the same proven inputs A-B/A, B/Appel à l'autorité);
generator defaults lock the ontology identity (document name / VirtuesTaxonomy dataset /
namespace / AIF namespace); aif:goodTenorOf URI construction pinned for Phase 3 reuse.

Verified: build 0 errors, 0 new warnings; suite 535 → 540 passed / 0 failed / 5 skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jun 28, 2026
…ster, #601 guardrail) (#604)

Add VirtueMindMapFRFrozenCharacterizationTests — a Golden-Master suite pinning
the CURRENT behaviour that Virtues mind map node text renders French for all 8
release languages (Fallacies are localized 8-lang, pinned by
MindMapLocalizationRegressionTests).

This is an executable snapshot of the 2-layer gap traced in investigation #601
(merged 71033f8), NOT a regression test of desired behaviour:

- Layer B (config): the single Virtue-targeting MindMapLocalization entry is a
  stale stub that only rewrites the tree-root literal "Vertus", for 4 of 7
  target languages (en/ru/pt/es; ar/fa/zh absent).
- Layer A (entity): Virtue exposes no TitleAr/TitleFa/TitleZh (CSV columns from
  #590/#595 exist but are invisible to the mind map), and Virtue.Text hard-
  delegates to TitleFr.

Three guardrail sections, each GREEN today and expected to FAIL when the 3-step
fix documented in #601 lands (entity ar/fa/zh props + {item.TitleFr} expression
+ mirrored config table). At that point flip the assertions to the localized
expectation rather than deleting them — the failure is the signal the gap closed.

The test-writing process surfaced a precision my investigation missed:
nameof(VirtueMindMapDocumentConfig.TitleExpression) returns the bare "TitleExpression",
which is SHARED with the Fallacy text entry — so the entry must be located by its
unique "Vertus" conversion, not by nameof (documented in the helper XML doc).

Additive only: no production code or existing test modified. 0 write under Cards/
(release freeze). Dispatch ai-01 2026-06-27 primary (characterization test, GO).

Co-authored-by: Your <your.email@example.com>
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