feat(deploy): ship the canon classification vocabulary with the release
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:
parent
05dda8d276
commit
ac21accd7a
6 changed files with 270 additions and 2 deletions
|
|
@ -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]+)*$"
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue