Skip to content

feat(data): TRAIN + register the DataClass classifiers — specified→trained→reproduced (task #14) - #264

Merged
mdheller merged 1 commit into
mainfrom
feat/train-dataclass-classifiers
Aug 3, 2026
Merged

feat(data): TRAIN + register the DataClass classifiers — specified→trained→reproduced (task #14)#264
mdheller merged 1 commit into
mainfrom
feat/train-dataclass-classifiers

Conversation

@mdheller

@mdheller mdheller commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Close the last owed thread: the classifier was specified, not trained

The DataClass contract (#251) specifies a TF-Lattice classifier and references a ModelManifest + train/eval RunRecord by URN — but those URNs pointed at nothing. This trains real classifiers and emits the artifacts they resolve to.

  • tools/train_dataclass_classifiers.py trains, on a labelled fixture, per-class LOGISTIC heads (one-vs-rest, individually testable per glossary-term) + a per-table SOFTMAX (n-ary head), both MONOTONE-constrained in the declared monotonicFeatures — the TF-Lattice essence: the weight on a monotone feature is projected >= 0 each GD step, so raising it never lowers the class score. Deterministic → weights + ModelManifest.modelDigest reproducible.
  • Emits trained_model.json + a conformant ModelManifest (id = DataClass modelRef) + train/eval RunRecords (ids = runRef/evalRunRef) — the previously-dangling references now resolve.

Teeth (make validate-trained-classifiers, recompute-don't-trust)

  • ModelManifest digest matches the weight bytes (tampered weights → refused)
  • eval accuracy reproduced from weights + fixture — an inflated claim fails (verified the check discriminates)
  • per-class logistic accuracy reproduced too (individually testable per class)
  • model verified genuinely monotone: perturb a monotone feature up → revenue score never drops
  • DataClass modelRef/runRef/evalRunRef resolve to the emitted artifacts
  • all emitted artifacts schema-conform (ModelManifest + both RunRecords)

Closes the vocabulary/data-governance program's last owed follow-up (specified → trained → registered → reproduced). numpy installed in the make recipe.

…ained→reproduced (task #14)

The DataClass contract specified a TF-Lattice classifier + referenced a ModelManifest/RunRecord by
URN, but those URNs pointed at nothing (specified, not trained). This closes it:

- tools/train_dataclass_classifiers.py trains real per-class LOGISTIC (one-vs-rest) + per-table
  SOFTMAX heads, MONOTONE-constrained in monotonicFeatures (weight >=0 projection each GD step =
  the TF-Lattice essence — raising a monotone feature never lowers the class score). Deterministic
  (seeded, full-batch) so weights + ModelManifest.modelDigest are reproducible.
- Emits trained_model.json + a conformant ModelManifest (id=DataClass modelRef) + train/eval
  RunRecords (ids=runRef/evalRunRef), so the previously-dangling DataClass classifier refs RESOLVE.
- validate-trained-classifiers (recompute-don't-trust, in make validate): manifest digest matches
  the weight bytes; eval accuracy REPRODUCED from weights+fixture (an inflated claim fails); model
  verified genuinely monotone (perturb up -> score never drops); refs resolve; artifacts conform.
  Teeth-verified (tampered weights refused). numpy in the make recipe.
@mdheller
mdheller merged commit ace6c3f into main Aug 3, 2026
7 checks passed
mdheller added a commit that referenced this pull request Aug 3, 2026
…lf-review, no Copilot on #264) (#265)

Adversarial + functional review of #264 (which Copilot didn't review) found:
- the monotone constraint was VACUOUS — the fixture held decimal_places constant, so its weight
  trained to 0 and the monotone teeth passed trivially. New fixture makes BOTH monotone features
  (token_magnitude, decimal_places) the primary label signal → both weights bind strongly positive,
  accuracy realistic (<1.0), monotonicity genuinely exercised.
- standardiser was fit on FULL data (mild train/test leakage) → now fit on the TRAIN split only.
- ModelManifest overclaimed 'wide-and-deep'/lattice → renamed 'monotone-logistic' (an honest
  realisation of the declared tf-lattice-wide-and-deep contract, not a full calibrated lattice).
- validator now checks BOTH per-class heads monotone + asserts the constraint BINDS (non-vacuous),
  and — a latent bug the trivial 1.0-accuracy fixture had masked — compares reproduced accuracy at
  the stored 4-decimal precision instead of full-precision vs 1e-6 (per-class 0.7857 now reproduces).
Tamper teeth still refuses corrupted weights. 8 checks green.
mdheller pushed a commit that referenced this pull request Aug 4, 2026
…SP-FPRINT-STACK-001)

Lands the spine of the six-layer metadata fingerprint classifier: the stance algebra,
per-layer witnesses, the admissibility gate, the L5 drift detector, and DR-4 enforcement.

The one genuinely new thing is that ZERO (ignorance) and INADMISSIBLE (conflict) are
different values. The 2018 source design had the three-channel matcher, OntoDT, the
mereotopological framing and DQ-into-access-enforcement already; what it could not say was
"I don't know" distinctly from "my evidence contradicts itself". A gap wants more data; a
glut wants a human. Everything else here follows from separating them.

Structural properties, all enforced rather than documented:
- pool -> guard -> quantize, in that order; guards only lower stance in the knowledge
  order, so gated stances retain conformal coverage (policy costs efficiency, not validity)
- every stored verdict is recomputed from what produced it
- quorum is n_eff (covariance participation ratio), not Herfindahl concentration
- coverage does not compose: depth and the loosened epsilon ride on the stance
- guards are measurable w.r.t. evidence and witness only — reading the outcome breaks
  exchangeability, so `value.*` cannot be named in a guard input path
- an inadmissible layer contributes ZERO, never a guess

Axiom X1 (parthood and subtyping are disjoint arrows) is enforced as a CI invariant. It is
the precondition for the source deck's own two questions — a count over the subtype closure
and an enumeration of parts — being simultaneously answerable from one graph.

L5's drift detector is blocking, not advisory: under silent repurpose the schema does not
change, so L5's evidence is precisely the unchanged schema and it asserts stale semantics
with maximum confidence exactly when it is wrong.

Retires the `tf-lattice-wide-and-deep` const in DataClass — that repository was archived by
its owner in April 2026. The pin now enumerates structurally-monotone constructions, with
the dead name deprecated-but-accepted so existing documents stay valid.

Teeth: 81 checks, 9 schema negative vectors, and 25 semantic gates proven to bite by a
mutation harness that requires each rejection for its stated reason. The M5 monotonicity
property test is run against a deliberately non-monotone aggregator and fails if it does not
catch it — because the closest precedent in this repo (#264) shipped a monotone constraint
whose fixture held the monotone feature constant, so it bound nothing and passed vacuously.
mdheller pushed a commit that referenced this pull request Aug 4, 2026
…SP-FPRINT-STACK-001)

Lands the spine of the six-layer metadata fingerprint classifier: the stance algebra,
per-layer witnesses, the admissibility gate, the L5 drift detector, and DR-4 enforcement.

The one genuinely new thing is that ZERO (ignorance) and INADMISSIBLE (conflict) are
different values. The 2018 source design had the three-channel matcher, OntoDT, the
mereotopological framing and DQ-into-access-enforcement already; what it could not say was
"I don't know" distinctly from "my evidence contradicts itself". A gap wants more data; a
glut wants a human. Everything else here follows from separating them.

Structural properties, all enforced rather than documented:
- pool -> guard -> quantize, in that order; guards only lower stance in the knowledge
  order, so gated stances retain conformal coverage (policy costs efficiency, not validity)
- every stored verdict is recomputed from what produced it
- quorum is n_eff (covariance participation ratio), not Herfindahl concentration
- coverage does not compose: depth and the loosened epsilon ride on the stance
- guards are measurable w.r.t. evidence and witness only — reading the outcome breaks
  exchangeability, so `value.*` cannot be named in a guard input path
- an inadmissible layer contributes ZERO, never a guess

Axiom X1 (parthood and subtyping are disjoint arrows) is enforced as a CI invariant. It is
the precondition for the source deck's own two questions — a count over the subtype closure
and an enumeration of parts — being simultaneously answerable from one graph.

L5's drift detector is blocking, not advisory: under silent repurpose the schema does not
change, so L5's evidence is precisely the unchanged schema and it asserts stale semantics
with maximum confidence exactly when it is wrong.

Retires the `tf-lattice-wide-and-deep` const in DataClass — that repository was archived by
its owner in April 2026. The pin now enumerates structurally-monotone constructions, with
the dead name deprecated-but-accepted so existing documents stay valid.

Teeth: 81 checks, 9 schema negative vectors, and 25 semantic gates proven to bite by a
mutation harness that requires each rejection for its stated reason. The M5 monotonicity
property test is run against a deliberately non-monotone aggregator and fails if it does not
catch it — because the closest precedent in this repo (#264) shipped a monotone constraint
whose fixture held the monotone feature constant, so it bound nothing and passed vacuously.
mdheller added a commit that referenced this pull request Aug 4, 2026
…SP-FPRINT-STACK-001) (#284)

Lands the spine of the six-layer metadata fingerprint classifier: the stance algebra,
per-layer witnesses, the admissibility gate, the L5 drift detector, and DR-4 enforcement.

The one genuinely new thing is that ZERO (ignorance) and INADMISSIBLE (conflict) are
different values. The 2018 source design had the three-channel matcher, OntoDT, the
mereotopological framing and DQ-into-access-enforcement already; what it could not say was
"I don't know" distinctly from "my evidence contradicts itself". A gap wants more data; a
glut wants a human. Everything else here follows from separating them.

Structural properties, all enforced rather than documented:
- pool -> guard -> quantize, in that order; guards only lower stance in the knowledge
  order, so gated stances retain conformal coverage (policy costs efficiency, not validity)
- every stored verdict is recomputed from what produced it
- quorum is n_eff (covariance participation ratio), not Herfindahl concentration
- coverage does not compose: depth and the loosened epsilon ride on the stance
- guards are measurable w.r.t. evidence and witness only — reading the outcome breaks
  exchangeability, so `value.*` cannot be named in a guard input path
- an inadmissible layer contributes ZERO, never a guess

Axiom X1 (parthood and subtyping are disjoint arrows) is enforced as a CI invariant. It is
the precondition for the source deck's own two questions — a count over the subtype closure
and an enumeration of parts — being simultaneously answerable from one graph.

L5's drift detector is blocking, not advisory: under silent repurpose the schema does not
change, so L5's evidence is precisely the unchanged schema and it asserts stale semantics
with maximum confidence exactly when it is wrong.

Retires the `tf-lattice-wide-and-deep` const in DataClass — that repository was archived by
its owner in April 2026. The pin now enumerates structurally-monotone constructions, with
the dead name deprecated-but-accepted so existing documents stay valid.

Teeth: 81 checks, 9 schema negative vectors, and 25 semantic gates proven to bite by a
mutation harness that requires each rejection for its stated reason. The M5 monotonicity
property test is run against a deliberately non-monotone aggregator and fails if it does not
catch it — because the closest precedent in this repo (#264) shipped a monotone constraint
whose fixture held the monotone feature constant, so it bound nothing and passed vacuously.

Co-authored-by: mdheller <charles.peterson@socioprophet.ai>
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.

1 participant