feat(deploy): ship the canon classification vocabulary with the release
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s

The API validates repo classification against the-custodian canon allowed
values. A container has no such checkout, so every classification write failed
with a 500 and classification could only ever be written from a workstation.

Mounts the vocabulary as a ConfigMap and points
REPO_CLASSIFICATION_ALLOWED_PATH at it.

The copy is the risk, so it is owned rather than trusted:
scripts/sync_classification_allowed.py regenerates it from canon and --check
fails on drift. make check-classification-allowed and
make railiance-state-hub-render both refuse to proceed when the copy diverges,
so a release cannot silently validate against a stale vocabulary.

The container volumeMounts and env blocks are merged rather than appended —
a second pair would have produced duplicate YAML keys as soon as sweep was
re-enabled.

Refs CUST-WP-0067-T09

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
tegwick 2026-08-24 23:46:05 +02:00
parent 05dda8d276
commit ac21accd7a
6 changed files with 270 additions and 2 deletions

View file

@ -0,0 +1,124 @@
# GENERATED — do not edit.
# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml
# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).
# Canon is authoritative; this copy exists only so the container has one.
# Machine-readable allowed-values for the Repo Classification Standard.
#
# Single source of truth for the standard's controlled vocabularies, derived
# from canon/standards/repo-classification-standard_v1.0.md. Consumed by:
# - the per-repo .repo-classification.yaml linter (tools/validate_repo_classification.py)
# - the State Hub registration validator (CUST-WP-0050 T04)
#
# When the standard's vocabularies change, update this file and bump `version`
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
version: "1.0"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
categories:
- experimental
- research
- project
- tooling
- product
- business
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
- infotech
- financials
- communication
- consumer
- health
- industrials
- energy
- utilities
- materials
- realestate
- crypto
- agents
- space
- government
# business_stake — 0..n; 2..6 recommended (§8)
business_stake:
- execution
- intelligence
- finance
- legal
- sales
- experience
- technology
- operations
- product
- people
- procurement
- sustainability
- automation
# business_mechanics — 0..n, optional (§9)
business_mechanics:
- intention
- control
- coordination
- operation
- adaptation
# capability_tags are intentionally OPEN-ENDED (§7): lowercase kebab-case, not
# restricted to this set. The families below are the standard's recommended
# canonical tags — used to warn on likely synonyms/typos, never to reject.
capability_families:
identity_and_access:
- identity
- authentication
- authorization
- access-control
- user-management
- tenancy
knowledge_and_evidence:
- knowledge
- citations
- evidence
- source-management
- traceability
- documentation
- decision-support
platform_and_operations:
- platform
- deployment
- operations
- observability
- feature-control
- configuration
- orchestration
market_and_coordination:
- marketplace
- pricing
- monetization
- reputation
- challenges
- bounties
- collaboration
- coordination
product_and_lifecycle:
- product-development
- lifecycle
governance_and_control:
- governance
- policy
- compliance
- risk
- audit
- control
games_and_entertainment:
- game
- entertainment
# Validation guidance (advisory bounds the linter applies as warnings)
guidance:
secondary_domains_max: 3
business_stake_recommended_min: 2
business_stake_recommended_max: 6
capability_tag_pattern: "^[a-z0-9]+(-[a-z0-9]+)*$"

View file

@ -0,0 +1,12 @@
{{- if .Values.classificationAllowed.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.classificationAllowed.name | quote }}
labels: {{- include "statehub.labels" . | nindent 4 }}
data:
# Generated copy of the-custodian canon allowed-values. Kept in sync by
# scripts/sync_classification_allowed.py; `--check` fails the release on drift.
repo-classification.allowed.yaml: |
{{ .Files.Get "files/repo-classification.allowed.yaml" | indent 4 }}
{{- end }}

View file

@ -22,8 +22,9 @@ spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets: {{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.sweep.enabled }}
{{- if or .Values.sweep.enabled .Values.classificationAllowed.enabled }}
volumes:
{{- if .Values.sweep.enabled }}
- name: sweep-repos
hostPath:
path: {{ .Values.sweep.hostPath | quote }}
@ -32,6 +33,12 @@ spec:
hostPath:
path: {{ .Values.sweep.sshHostPath | quote }}
type: DirectoryOrCreate
{{- end }}
{{- if .Values.classificationAllowed.enabled }}
- name: classification-allowed
configMap:
name: {{ .Values.classificationAllowed.name | quote }}
{{- end }}
{{- end }}
containers:
- name: state-hub
@ -50,17 +57,32 @@ spec:
- /bin/sh
- -c
- git config --global --add safe.directory '*'
{{- end }}
{{- if or .Values.sweep.enabled .Values.classificationAllowed.enabled }}
volumeMounts:
{{- if .Values.sweep.enabled }}
- name: sweep-repos
mountPath: {{ .Values.sweep.hostPath | quote }}
- name: sweep-ssh
mountPath: /root/.ssh
readOnly: true
{{- end }}
{{- if .Values.classificationAllowed.enabled }}
- name: classification-allowed
mountPath: {{ .Values.classificationAllowed.mountPath | quote }}
readOnly: true
{{- end }}
env:
{{- if .Values.sweep.enabled }}
- name: STATE_HUB_SWEEP_HOSTNAME
value: {{ .Values.sweep.hostname | quote }}
- name: GIT_SSH_COMMAND
value: "ssh -o StrictHostKeyChecking=accept-new -F /root/.ssh/config"
{{- end }}
{{- if .Values.classificationAllowed.enabled }}
- name: REPO_CLASSIFICATION_ALLOWED_PATH
value: {{ printf "%s/repo-classification.allowed.yaml" .Values.classificationAllowed.mountPath | quote }}
{{- end }}
{{- end }}
envFrom:
{{- if .Values.config.enabled }}

View file

@ -47,6 +47,15 @@ ingress:
traefik.ingress.kubernetes.io/router.tls: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
# Classification allowed-values (CUST-WP-0067-T09). The API validates repo
# classification against the-custodian canon; a container has no such checkout,
# so the file travels with the release as a ConfigMap. Without it every
# classification write fails with a 500.
classificationAllowed:
enabled: true
name: state-hub-classification-allowed
mountPath: /etc/state-hub/classification
# MCP layer (CUST-WP-0067-T08). Runs from the same image with a different
# command, as a stateless HTTP client over the API service. ClusterIP only —
# it proxies an unauthenticated API, so it must never gain an Ingress.