feat(iace): norm cross-reference matrix, batch 1 (ISO/DIN/ANSI/GB/JIS — 100 entries)
Adds a jurisdiction-cross-reference layer to the norms library. Each entry
maps an ISO/IEC/EN norm to its identifier in DIN (DE), ANSI/NFPA/UL/OSHA (US),
GB (CN), and JIS (JP), with explicit Relation (identical/equivalent/partial/
superseded_by/supersedes) and Confidence (verified/high/medium/low) fields.
Batch 1 covers IDs 1-100 in load order:
- 1a (50): A-norms + B1-norms + early B2-norms (ergonomics, vibration, noise)
- 1b (50): remaining B2 (ATEX, EMC, cybersec) + first C-norms (presses,
robots, conveyors, plastics, woodworking)
These are the foundational, internationally harmonized standards with the
strongest verified mappings (ISO 12100 ~> GB 15706 ~> JIS B 9700, EN 60204-1
~> NFPA 79 ~> GB 5226.1 ~> JIS B 9960-1, etc.).
API:
- GET /iace/norms-library?include_crossref=true → inline crossref
- GET /iace/norms-library/:id/crossref → single norm lookup
- GET /iace/norms-library/crossref → bulk dump
Strategic context: enables dual-use CE/US/CN/JP tech files without
re-authoring, and addresses the "Norm Translation Matrix" gap that the
US-export strategy memory entry calls out. 6 batches remaining (~571 norms)
to reach full library coverage.
Tests: 6 new tests; all pass via `go test -vet=off ./internal/iace/`.
(vet=off needed only to bypass an unrelated pre-existing typo in
document_export_sources.go.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package iace
|
||||
|
||||
// Norm cross-reference matrix: maps a core ISO/IEC/EN standard to the
|
||||
// jurisdiction-specific identifiers used in DIN (DE), ANSI / NFPA / UL (US),
|
||||
// GB (China), and JIS (Japan). This is an identifier-only mapping — no
|
||||
// copyrighted norm text is included. The matrix is used to render a
|
||||
// "this requirement also satisfies X in market Y" hint in tech files,
|
||||
// enabling dual-use compliance documents for CE + US/CN/JP export.
|
||||
//
|
||||
// IMPORTANT: each NormMapping carries an explicit Confidence and Relation.
|
||||
// Do NOT treat "partial" or "medium" entries as 1:1 substitutes. They
|
||||
// indicate scope overlap that must be verified by a competent person for
|
||||
// the concrete machine before relying on the foreign standard.
|
||||
|
||||
// NormMapping is one entry in the cross-reference table.
|
||||
type NormMapping struct {
|
||||
Region string `json:"region"` // "EU-DIN", "US-ANSI", "US-NFPA", "US-UL", "US-OSHA", "CN-GB", "JP-JIS", "INTL-ISO"
|
||||
Identifier string `json:"identifier"` // e.g. "DIN EN ISO 12100:2011"
|
||||
Relation string `json:"relation"` // "identical", "equivalent", "partial", "supersedes", "superseded_by"
|
||||
Confidence string `json:"confidence"` // "verified", "high", "medium", "low"
|
||||
Notes string `json:"notes,omitempty"` // Optional scope clarification (e.g. "only chapters 4-6")
|
||||
SourceURL string `json:"source_url,omitempty"` // Optional pointer to a public catalog entry
|
||||
}
|
||||
|
||||
// NormCrossRef is the cross-reference entry for one NormReference.ID.
|
||||
type NormCrossRef struct {
|
||||
NormID string `json:"norm_id"` // Matches NormReference.ID (e.g. "ISO-12100")
|
||||
Mappings []NormMapping `json:"mappings"` // International equivalents
|
||||
Notes string `json:"notes,omitempty"` // General notes about the cross-walk
|
||||
BatchID string `json:"batch_id"` // Tracking which batch added this entry
|
||||
}
|
||||
|
||||
// crossRefRegistry is the in-memory registry, populated by init() in each batch file.
|
||||
var crossRefRegistry = map[string]NormCrossRef{}
|
||||
|
||||
// registerCrossRefs is called by each batch file's init() to append entries.
|
||||
func registerCrossRefs(entries []NormCrossRef) {
|
||||
for _, e := range entries {
|
||||
crossRefRegistry[e.NormID] = e
|
||||
}
|
||||
}
|
||||
|
||||
// GetNormCrossRef returns the cross-reference entry for a given NormReference.ID,
|
||||
// or a zero value with NormID set if no mapping exists yet.
|
||||
func GetNormCrossRef(normID string) NormCrossRef {
|
||||
if entry, ok := crossRefRegistry[normID]; ok {
|
||||
return entry
|
||||
}
|
||||
return NormCrossRef{NormID: normID, Mappings: []NormMapping{}}
|
||||
}
|
||||
|
||||
// ListNormCrossRefs returns every entry in the registry. Used by the
|
||||
// /norms-library/crossref bulk endpoint and for tech-file batch rendering.
|
||||
func ListNormCrossRefs() []NormCrossRef {
|
||||
out := make([]NormCrossRef, 0, len(crossRefRegistry))
|
||||
for _, v := range crossRefRegistry {
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CrossRefCoverage returns counters that let the UI render a progress bar
|
||||
// ("X of Y norms have a cross-reference"). The "total" comes from the
|
||||
// caller (norms library size) since the cross-ref package does not depend
|
||||
// on the norms library to avoid a cyclic import.
|
||||
func CrossRefCoverage(totalNorms int) (covered, total int) {
|
||||
return len(crossRefRegistry), totalNorms
|
||||
}
|
||||
Reference in New Issue
Block a user