Gaps fill up, fixes, ui restructuring
This commit is contained in:
125
.gitea/workflows/templates/dsse-attest-verify-check.yml
Normal file
125
.gitea/workflows/templates/dsse-attest-verify-check.yml
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# dsse-attest-verify-check.yml
|
||||||
|
# Sprint: SPRINT_20260219_011 (CIAP-02)
|
||||||
|
# Description: Signs SBOM with DSSE, verifies attestation, validates Rekor proof
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# This workflow creates a DSSE attestation for an SBOM, verifies it, and
|
||||||
|
# optionally validates the Rekor transparency log inclusion proof.
|
||||||
|
#
|
||||||
|
# Supports both keyless (Fulcio/OIDC) and keyed (cosign key) signing modes.
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
name: DSSE Attest + Verify + Rekor Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
subject_ref:
|
||||||
|
description: 'OCI image reference (registry/repo@sha256:...)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
predicate_path:
|
||||||
|
description: 'Path to the DSSE predicate JSON file'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
signing_mode:
|
||||||
|
description: 'Signing mode: keyless (Fulcio/OIDC) or key (cosign key)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'keyless'
|
||||||
|
public_key_path:
|
||||||
|
description: 'Path to cosign public key PEM (required for key mode)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
predicate_type:
|
||||||
|
description: 'Predicate type URI for the attestation'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'https://cyclonedx.org/bom'
|
||||||
|
skip_rekor:
|
||||||
|
description: 'Skip Rekor transparency log (for air-gapped environments)'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
attest-and-verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
id-token: write # For OIDC-based keyless signing
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install cosign
|
||||||
|
uses: sigstore/cosign-installer@v3
|
||||||
|
|
||||||
|
- name: Sign attestation
|
||||||
|
id: sign
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: '1'
|
||||||
|
run: |
|
||||||
|
SIGN_FLAGS="--predicate ${{ inputs.predicate_path }}"
|
||||||
|
SIGN_FLAGS="${SIGN_FLAGS} --type ${{ inputs.predicate_type }}"
|
||||||
|
|
||||||
|
if [ "${{ inputs.signing_mode }}" = "key" ]; then
|
||||||
|
# Keyed signing
|
||||||
|
SIGN_FLAGS="${SIGN_FLAGS} --key ${{ inputs.public_key_path }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ inputs.skip_rekor }}" = "true" ]; then
|
||||||
|
SIGN_FLAGS="${SIGN_FLAGS} --tlog-upload=false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cosign attest ${SIGN_FLAGS} "${{ inputs.subject_ref }}"
|
||||||
|
|
||||||
|
echo "### Attestation Signed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- Subject: \`${{ inputs.subject_ref }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- Mode: ${{ inputs.signing_mode }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- Predicate type: \`${{ inputs.predicate_type }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Verify attestation
|
||||||
|
id: verify
|
||||||
|
run: |
|
||||||
|
VERIFY_FLAGS="--type ${{ inputs.predicate_type }}"
|
||||||
|
|
||||||
|
if [ "${{ inputs.signing_mode }}" = "key" ]; then
|
||||||
|
VERIFY_FLAGS="${VERIFY_FLAGS} --key ${{ inputs.public_key_path }}"
|
||||||
|
else
|
||||||
|
# Keyless: verify against Sigstore trust root
|
||||||
|
VERIFY_FLAGS="${VERIFY_FLAGS} --certificate-identity-regexp '.*'"
|
||||||
|
VERIFY_FLAGS="${VERIFY_FLAGS} --certificate-oidc-issuer-regexp '.*'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cosign verify-attestation ${VERIFY_FLAGS} "${{ inputs.subject_ref }}"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Attestation verification: PASS" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "Attestation verification: FAIL" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Validate Rekor inclusion proof
|
||||||
|
if: inputs.skip_rekor != true
|
||||||
|
run: |
|
||||||
|
# Fetch the Rekor entry for our attestation
|
||||||
|
DIGEST=$(sha256sum "${{ inputs.predicate_path }}" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Use rekor-cli to search and verify
|
||||||
|
if command -v rekor-cli &> /dev/null; then
|
||||||
|
ENTRY=$(rekor-cli search --sha "sha256:${DIGEST}" 2>/dev/null | head -1)
|
||||||
|
if [ -n "${ENTRY}" ]; then
|
||||||
|
rekor-cli verify --artifact "${{ inputs.predicate_path }}" --entry "${ENTRY}"
|
||||||
|
echo "Rekor inclusion proof: PASS (entry: ${ENTRY})" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "Rekor entry not found (may be pending)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "rekor-cli not available, skipping Rekor verification" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
135
.gitea/workflows/templates/sbom-canonicalization-check.yml
Normal file
135
.gitea/workflows/templates/sbom-canonicalization-check.yml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# sbom-canonicalization-check.yml
|
||||||
|
# Sprint: SPRINT_20260219_011 (CIAP-01)
|
||||||
|
# Description: Validates CycloneDX SBOM and verifies canonical_id determinism
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# This workflow validates an SBOM against the CycloneDX schema, computes
|
||||||
|
# the canonical_id (sha256 of JCS-canonicalized JSON), and verifies
|
||||||
|
# that canonicalization is deterministic across runs.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# 1. Copy to your project's .gitea/workflows/ directory
|
||||||
|
# 2. Set BOM_PATH to your SBOM output location
|
||||||
|
# 3. Optionally set EXPECTED_CANONICAL_ID for regression testing
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
name: SBOM Canonicalization Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
bom_path:
|
||||||
|
description: 'Path to CycloneDX SBOM JSON file'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
expected_canonical_id:
|
||||||
|
description: 'Expected canonical_id for regression testing (optional)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
outputs:
|
||||||
|
canonical_id:
|
||||||
|
description: 'Computed canonical_id (sha256:<hex>)'
|
||||||
|
value: ${{ jobs.canonicalize.outputs.canonical_id }}
|
||||||
|
validation_result:
|
||||||
|
description: 'Schema validation result (pass/fail)'
|
||||||
|
value: ${{ jobs.canonicalize.outputs.validation_result }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
canonicalize:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
canonical_id: ${{ steps.compute.outputs.canonical_id }}
|
||||||
|
validation_result: ${{ steps.validate.outputs.result }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Validate CycloneDX schema
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
# Validate SBOM against CycloneDX 1.7 schema
|
||||||
|
if command -v sbom-utility &> /dev/null; then
|
||||||
|
sbom-utility validate -i "${{ inputs.bom_path }}" --force
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "result=pass" >> $GITHUB_OUTPUT
|
||||||
|
echo "Schema validation: PASS" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "result=fail" >> $GITHUB_OUTPUT
|
||||||
|
echo "Schema validation: FAIL" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Fallback: basic JSON validation with ajv
|
||||||
|
npx ajv-cli validate -s docs/schemas/cyclonedx-bom-1.7.schema.json -d "${{ inputs.bom_path }}" || {
|
||||||
|
echo "result=fail" >> $GITHUB_OUTPUT
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
echo "result=pass" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compute canonical_id
|
||||||
|
id: compute
|
||||||
|
run: |
|
||||||
|
# JCS canonicalize and compute SHA-256
|
||||||
|
# Uses Python for RFC 8785 compliance (json.loads + sorted keys + separators)
|
||||||
|
CANONICAL_ID=$(python3 -c "
|
||||||
|
import json, hashlib, sys
|
||||||
|
with open('${{ inputs.bom_path }}', 'rb') as f:
|
||||||
|
obj = json.load(f)
|
||||||
|
canonical = json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')
|
||||||
|
digest = hashlib.sha256(canonical).hexdigest()
|
||||||
|
print(f'sha256:{digest}')
|
||||||
|
")
|
||||||
|
|
||||||
|
echo "canonical_id=${CANONICAL_ID}" >> $GITHUB_OUTPUT
|
||||||
|
echo "### Canonical SBOM ID" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "\`${CANONICAL_ID}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Verify determinism (double-compute)
|
||||||
|
run: |
|
||||||
|
# Canonicalize twice, verify identical output
|
||||||
|
FIRST=$(python3 -c "
|
||||||
|
import json, hashlib
|
||||||
|
with open('${{ inputs.bom_path }}', 'rb') as f:
|
||||||
|
obj = json.load(f)
|
||||||
|
canonical = json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')
|
||||||
|
print(hashlib.sha256(canonical).hexdigest())
|
||||||
|
")
|
||||||
|
|
||||||
|
SECOND=$(python3 -c "
|
||||||
|
import json, hashlib
|
||||||
|
with open('${{ inputs.bom_path }}', 'rb') as f:
|
||||||
|
obj = json.load(f)
|
||||||
|
canonical = json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')
|
||||||
|
print(hashlib.sha256(canonical).hexdigest())
|
||||||
|
")
|
||||||
|
|
||||||
|
if [ "${FIRST}" != "${SECOND}" ]; then
|
||||||
|
echo "FATAL: Canonicalization is non-deterministic!" >&2
|
||||||
|
echo " Run 1: ${FIRST}" >&2
|
||||||
|
echo " Run 2: ${SECOND}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Determinism check: PASS (hash=${FIRST})" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Regression check (if expected_canonical_id provided)
|
||||||
|
if: inputs.expected_canonical_id != ''
|
||||||
|
run: |
|
||||||
|
ACTUAL="${{ steps.compute.outputs.canonical_id }}"
|
||||||
|
EXPECTED="${{ inputs.expected_canonical_id }}"
|
||||||
|
|
||||||
|
if [ "${ACTUAL}" != "${EXPECTED}" ]; then
|
||||||
|
echo "REGRESSION: canonical_id changed!" >&2
|
||||||
|
echo " Expected: ${EXPECTED}" >&2
|
||||||
|
echo " Actual: ${ACTUAL}" >&2
|
||||||
|
echo "### Regression Detected" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Expected: \`${EXPECTED}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Actual: \`${ACTUAL}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Regression check: PASS" >> $GITHUB_STEP_SUMMARY
|
||||||
167
.gitea/workflows/templates/vex-mapping-check.yml
Normal file
167
.gitea/workflows/templates/vex-mapping-check.yml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# vex-mapping-check.yml
|
||||||
|
# Sprint: SPRINT_20260219_011 (CIAP-03)
|
||||||
|
# Description: Validates VEX documents and verifies target artifact matching
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# This workflow validates OpenVEX or CycloneDX VEX documents against their
|
||||||
|
# schemas, asserts required fields are present and valid, and optionally
|
||||||
|
# verifies target artifact matches a known canonical_id.
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
name: VEX Mapping Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
vex_path:
|
||||||
|
description: 'Path to VEX document (JSON)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
vex_format:
|
||||||
|
description: 'VEX format: openvex or cyclonedx'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'openvex'
|
||||||
|
canonical_id:
|
||||||
|
description: 'Expected canonical_id of the target artifact (optional)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
schema_path:
|
||||||
|
description: 'Path to VEX JSON schema (optional, uses bundled schemas by default)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-vex:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Validate VEX schema
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
VEX_FILE="${{ inputs.vex_path }}"
|
||||||
|
FORMAT="${{ inputs.vex_format }}"
|
||||||
|
|
||||||
|
# Select schema
|
||||||
|
if [ -n "${{ inputs.schema_path }}" ]; then
|
||||||
|
SCHEMA="${{ inputs.schema_path }}"
|
||||||
|
elif [ "${FORMAT}" = "openvex" ]; then
|
||||||
|
SCHEMA="docs/schemas/openvex-0.2.0.schema.json"
|
||||||
|
else
|
||||||
|
SCHEMA="docs/schemas/cyclonedx-bom-1.7.schema.json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if [ -f "${SCHEMA}" ]; then
|
||||||
|
npx ajv-cli validate -s "${SCHEMA}" -d "${VEX_FILE}" && {
|
||||||
|
echo "Schema validation: PASS" >> $GITHUB_STEP_SUMMARY
|
||||||
|
} || {
|
||||||
|
echo "Schema validation: FAIL" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Schema file not found: ${SCHEMA}, skipping schema validation" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Assert required VEX fields
|
||||||
|
run: |
|
||||||
|
FORMAT="${{ inputs.vex_format }}"
|
||||||
|
VEX_FILE="${{ inputs.vex_path }}"
|
||||||
|
|
||||||
|
python3 -c "
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
with open('${VEX_FILE}') as f:
|
||||||
|
vex = json.load(f)
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
format_name = '${FORMAT}'
|
||||||
|
|
||||||
|
if format_name == 'openvex':
|
||||||
|
# OpenVEX validation
|
||||||
|
if 'statements' not in vex:
|
||||||
|
errors.append('Missing required field: statements')
|
||||||
|
else:
|
||||||
|
for i, stmt in enumerate(vex['statements']):
|
||||||
|
if 'status' not in stmt:
|
||||||
|
errors.append(f'Statement [{i}]: missing status')
|
||||||
|
elif stmt['status'] not in ('affected', 'not_affected', 'fixed', 'under_investigation'):
|
||||||
|
errors.append(f'Statement [{i}]: invalid status: {stmt[\"status\"]}')
|
||||||
|
if 'vulnerability' not in stmt:
|
||||||
|
errors.append(f'Statement [{i}]: missing vulnerability')
|
||||||
|
if 'product' not in stmt and 'products' not in stmt:
|
||||||
|
errors.append(f'Statement [{i}]: missing product or products')
|
||||||
|
else:
|
||||||
|
# CycloneDX VEX (embedded in SBOM vulnerabilities)
|
||||||
|
vulns = vex.get('vulnerabilities', [])
|
||||||
|
if not vulns:
|
||||||
|
errors.append('No vulnerabilities found in CycloneDX VEX')
|
||||||
|
for i, vuln in enumerate(vulns):
|
||||||
|
analysis = vuln.get('analysis', {})
|
||||||
|
state = analysis.get('state')
|
||||||
|
if not state:
|
||||||
|
errors.append(f'Vulnerability [{i}] ({vuln.get(\"id\",\"?\")}): missing analysis.state')
|
||||||
|
elif state not in ('resolved', 'resolved_with_pedigree', 'exploitable', 'in_triage', 'false_positive', 'not_affected'):
|
||||||
|
errors.append(f'Vulnerability [{i}]: invalid analysis.state: {state}')
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print('VEX field validation FAILED:', file=sys.stderr)
|
||||||
|
for e in errors:
|
||||||
|
print(f' - {e}', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print(f'VEX field validation: PASS ({format_name})')
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "VEX field assertions: PASS" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Verify target canonical_id match
|
||||||
|
if: inputs.canonical_id != ''
|
||||||
|
run: |
|
||||||
|
FORMAT="${{ inputs.vex_format }}"
|
||||||
|
VEX_FILE="${{ inputs.vex_path }}"
|
||||||
|
EXPECTED_ID="${{ inputs.canonical_id }}"
|
||||||
|
|
||||||
|
python3 -c "
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
with open('${VEX_FILE}') as f:
|
||||||
|
vex = json.load(f)
|
||||||
|
|
||||||
|
expected = '${EXPECTED_ID}'
|
||||||
|
format_name = '${FORMAT}'
|
||||||
|
found_match = False
|
||||||
|
|
||||||
|
if format_name == 'openvex':
|
||||||
|
for stmt in vex.get('statements', []):
|
||||||
|
product = stmt.get('product', '')
|
||||||
|
products = stmt.get('products', [])
|
||||||
|
targets = [product] if product else products
|
||||||
|
for t in targets:
|
||||||
|
pid = t if isinstance(t, str) else t.get('@id', '')
|
||||||
|
if expected.replace('sha256:', '') in pid or pid == expected:
|
||||||
|
found_match = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# CycloneDX: check affects refs
|
||||||
|
for vuln in vex.get('vulnerabilities', []):
|
||||||
|
for affects in vuln.get('affects', []):
|
||||||
|
ref = affects.get('ref', '')
|
||||||
|
if expected.replace('sha256:', '') in ref:
|
||||||
|
found_match = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found_match:
|
||||||
|
print(f'WARNING: canonical_id {expected} not found in VEX targets', file=sys.stderr)
|
||||||
|
print('This may indicate the VEX document does not apply to the expected artifact')
|
||||||
|
# Warning only, not a hard failure
|
||||||
|
else:
|
||||||
|
print(f'Target canonical_id match: PASS')
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "Target artifact check: completed" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
# Sprint 20260218_005 - UI V2 Rewire Spec Freeze
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Freeze all unresolved IA decisions before implementation sprints begin so downstream work cannot diverge.
|
|
||||||
- Produce complete, self-contained specs for Advisory Sources, Release Control capability rendering, trust ownership transition, and route deprecation.
|
|
||||||
- Bootstrap a root-domain endpoint contract ledger that classifies all screens as `EXISTS_COMPAT`, `EXISTS_ADAPT`, or `MISSING_NEW`.
|
|
||||||
- Working directory: `docs/modules/ui/v2-rewire`.
|
|
||||||
- Expected evidence: finalized specification docs, route mapping matrix, contract ledger v1, and signed handoff packet.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: none.
|
|
||||||
- Downstream dependencies: this sprint must be DONE before sprints `20260218_006`, `20260218_007`, and `20260218_008` can move to DOING.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `R0-01` and `R0-02` can run in parallel.
|
|
||||||
- `R0-03` can run in parallel with `R0-01` and `R0-02`.
|
|
||||||
- `R0-04` depends on `R0-01`, `R0-02`, and `R0-03`.
|
|
||||||
- `R0-05` depends on `R0-03` and `R0-04`.
|
|
||||||
- `R0-06` depends on `R0-01` through `R0-05`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/sprint-planning-guide.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/multi-sprint-plan.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-19.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-20.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-21.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/app.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### R0-01 - Freeze canonical IA taxonomy and ownership model
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Product Manager, Documentation author
|
|
||||||
Task description:
|
|
||||||
- Finalize the top-level IA taxonomy as the only allowed root-domain structure for implementation work. The taxonomy must explicitly define the seven roots: Dashboard, Release Control, Security and Risk, Evidence and Audit, Integrations, Platform Ops, and Administration.
|
|
||||||
- Freeze ownership boundaries that resolve known conflicts from earlier packs. The self-contained spec must explicitly define: Policy Governance owner, Trust and Signing owner, System owner, and the split of legacy Security Data between Integrations, Platform Ops, and Security and Risk.
|
|
||||||
- Define forbidden alternatives so downstream sprints cannot re-open superseded placements.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Canonical root-domain taxonomy is documented with final naming and order.
|
|
||||||
- [ ] Ownership boundaries for Policy, Trust, System, and Security Data split are explicit and conflict-free.
|
|
||||||
- [ ] Superseded alternatives are listed as non-allowed implementations.
|
|
||||||
- [ ] Decision record includes rationale and impacted downstream sprints.
|
|
||||||
|
|
||||||
### R0-02 - Produce full Advisory Sources screen specification
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Product Manager, Security lead, Documentation author
|
|
||||||
Task description:
|
|
||||||
- Create a complete screen spec for `Security and Risk -> Advisory Sources` with no reliance on pack text at implementation time. The spec must include exact screen sections, filters, columns, actions, detail drawer/page behavior, empty states, stale data states, and hard-fail states.
|
|
||||||
- The spec must define field-level ownership and the link contract between Advisory Sources and the two adjacent surfaces: `Integrations` (connectivity and source config) and `Platform Ops` (mirror and freshness operations).
|
|
||||||
- Include explicit behavior for conflicting advisory signals, unsigned advisories, and stale source freshness relative to policy gate decisions.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Screen layout and interaction model are fully specified.
|
|
||||||
- [ ] Field-level ownership matrix is present for Security and Risk vs Integrations vs Platform Ops.
|
|
||||||
- [ ] Data-state behavior is defined for healthy, stale, unavailable, and conflict conditions.
|
|
||||||
- [ ] API dependency list for this screen is present with initial status class per dependency.
|
|
||||||
|
|
||||||
### R0-03 - Freeze Release Control capability rendering policy
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Project Manager, UX lead, Frontend lead
|
|
||||||
Task description:
|
|
||||||
- Freeze one nav rendering policy for Release Control-owned capabilities. The policy must explicitly answer whether Releases and Approvals appear as direct shortcuts, nested-only entries, or hybrid shortcuts with strict ownership labeling.
|
|
||||||
- The policy must include desktop and mobile nav behavior, breadcrumbs, route naming, and legacy-label transition text. It must explicitly prevent mixed implementations across teams.
|
|
||||||
- Include route alias requirements needed to support staged migration from current navigation.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] One rendering policy is selected and documented for desktop and mobile.
|
|
||||||
- [ ] Breadcrumb and route naming rules are specified with concrete examples.
|
|
||||||
- [ ] Legacy label behavior is specified for migration period.
|
|
||||||
- [ ] Explicit do and do-not list prevents mixed rendering variants.
|
|
||||||
|
|
||||||
### R0-04 - Freeze Trust and Signing ownership transition policy
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R0-01
|
|
||||||
Owners: Product Manager, Security architect, Documentation author
|
|
||||||
Task description:
|
|
||||||
- Finalize transition where `Administration` is owner of Trust and Signing and `Evidence and Audit` plus `Security and Risk` consume trust state via deep links and context panels.
|
|
||||||
- Define canonical link paths, allowed embedding patterns, and non-allowed ownership regressions.
|
|
||||||
- Define temporary aliasing behavior for legacy trust routes and timeline for removal.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Ownership and consumption model is explicit and final.
|
|
||||||
- [ ] Cross-link contract is defined for all consuming screens.
|
|
||||||
- [ ] Alias and deprecation behavior is defined by route family.
|
|
||||||
- [ ] Auth scope and role implications are documented.
|
|
||||||
|
|
||||||
### R0-05 - Produce route deprecation and migration baseline
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R0-03
|
|
||||||
Owners: Project Manager, Frontend lead
|
|
||||||
Task description:
|
|
||||||
- Create a complete route baseline mapping current paths to target IA paths. Include routes from root navigation, settings-derived paths, and legacy redirects.
|
|
||||||
- Each route must be assigned one explicit action: keep, redirect, alias, or remove-later. Include rationale and migration risk per high-traffic route.
|
|
||||||
- Include sequence guidance for when redirects can be activated relative to implementation sprints.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Baseline map covers all root domains and major child route families.
|
|
||||||
- [ ] Every mapped route has exactly one migration action.
|
|
||||||
- [ ] High-risk deep-link routes have mitigation notes.
|
|
||||||
- [ ] Activation sequence aligns with downstream sprint dependency plan.
|
|
||||||
|
|
||||||
### R0-06 - Bootstrap endpoint contract ledger v1
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R0-02
|
|
||||||
Owners: Project Manager, API architect, Module leads
|
|
||||||
Task description:
|
|
||||||
- Produce v1 endpoint contract ledger for all active-authority screens defined in `docs/modules/ui/v2-rewire/authority-matrix.md`. The ledger must be self-contained and include candidate endpoints, status class, owner module, auth scope impact, schema delta, and ticket linkage.
|
|
||||||
- No screen may remain unclassified. `MISSING_NEW` entries must include proposed endpoint contracts and owning module.
|
|
||||||
- Ledger must identify cross-module dependencies that require explicit allowance in implementation sprint files.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] All active-authority screens are present in ledger v1.
|
|
||||||
- [ ] All rows have non-empty status class and owner module.
|
|
||||||
- [ ] All `MISSING_NEW` rows include concrete proposed endpoint contracts.
|
|
||||||
- [ ] Ledger review sign-off captured from frontend and backend leads.
|
|
||||||
|
|
||||||
### R0-07 - Publish S00 handoff packet
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R0-06
|
|
||||||
Owners: Project Manager
|
|
||||||
Task description:
|
|
||||||
- Publish a handoff packet for sprints `20260218_006` through `20260218_008` containing frozen decisions, unresolved risks, route migration baseline, and contract ledger references.
|
|
||||||
- The handoff packet must explicitly list blocked topics (if any) and mitigation actions with owners.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Handoff packet is published and linked from the sprint file.
|
|
||||||
- [ ] Downstream sprint owners and dependencies are explicit.
|
|
||||||
- [ ] Remaining risks have owners and checkpoint dates.
|
|
||||||
- [ ] All non-shipped exploratory work is reset to TODO with notes.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for v2 rewire spec freeze and contract-ledger bootstrap. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision pending: final rendering policy for Release Control-owned capabilities (`Releases`, `Approvals`, `Deployments`, `Regions and Environments`, `Bundles`) must be frozen before nav implementation.
|
|
||||||
- Risk: unresolved Advisory Sources boundary can duplicate logic across Security and Risk, Integrations, and Platform Ops; mitigation is field-level ownership matrix in `R0-02`.
|
|
||||||
- Risk: trust ownership transition can break historical deep links and user expectations; mitigation is explicit alias/deprecation policy in `R0-04` and `R0-05`.
|
|
||||||
- Risk: hidden backend gaps may stall frontend sprints; mitigation is complete classification in `R0-06` with `MISSING_NEW` proposals.
|
|
||||||
- Existing code references for migration analysis: `src/Web/StellaOps.Web/src/app/app.routes.ts`, `src/Web/StellaOps.Web/src/app/features/settings/settings.routes.ts`, `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-19: Review drafts for `R0-01`, `R0-02`, and `R0-03`.
|
|
||||||
- 2026-02-20: Review and sign off `R0-04` and `R0-05`.
|
|
||||||
- 2026-02-21: Review `R0-06` ledger and publish `R0-07` handoff packet.
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
# Sprint 20260218_006 - UI V2 Rewire Navigation Shell and Route Migration
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement the canonical navigation shell and route framework for the new IA, using frozen outputs from sprint `20260218_005`.
|
|
||||||
- Deliver a single rendering model for root domains and Release Control-owned capabilities with deterministic route behavior.
|
|
||||||
- Include migration-safe aliases, breadcrumbs, and transition labels that preserve usability during staged cutover.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: route tree changes, nav model updates, redirect behavior tests, breadcrumb/label verification artifacts.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md` must be DONE.
|
|
||||||
- Downstream dependencies: required before `20260218_007`, `20260218_008`, `20260218_009`, and `20260218_010` can finalize routes.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `N1-01` and `N1-02` can run in parallel after dependency resolution.
|
|
||||||
- `N1-03` depends on `N1-01`.
|
|
||||||
- `N1-04` depends on `N1-02`.
|
|
||||||
- `N1-05` depends on `N1-01` through `N1-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/sprint-planning-guide.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_sprint_spec_package.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/app.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/layout/breadcrumb/breadcrumb.component.ts`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### N1-01 - Implement canonical root-domain navigation model
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer, UX developer
|
|
||||||
Task description:
|
|
||||||
- Replace current sidebar root model with canonical domains in this order: Dashboard, Release Control, Security and Risk, Evidence and Audit, Integrations, Platform Ops, Administration.
|
|
||||||
- Implement the frozen Release Control capability rendering policy from sprint `20260218_005` with explicit prevention of mixed variants.
|
|
||||||
- Preserve scope-based visibility behavior and ensure hidden groups do not break active-route resolution.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Root nav displays canonical domains and labels exactly.
|
|
||||||
- [ ] Release Control capability rendering matches frozen policy for desktop and mobile.
|
|
||||||
- [ ] Scope gating behavior remains deterministic and tested.
|
|
||||||
- [ ] No orphaned nav items link to removed or undefined routes.
|
|
||||||
|
|
||||||
### N1-02 - Build canonical route tree scaffolding for IA v2
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Refactor root route declarations to align with canonical IA while keeping runtime compatibility via staged aliases.
|
|
||||||
- Introduce target route families for each domain and define placeholder-compatible child trees for sprints `007` to `015`.
|
|
||||||
- Ensure route titles and metadata match canonical naming.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Root route tree includes all canonical domains.
|
|
||||||
- [ ] Target child route families exist for all planned capability areas.
|
|
||||||
- [ ] Route metadata uses canonical names and ownership.
|
|
||||||
- [ ] Existing deep links continue to resolve via aliases or redirects.
|
|
||||||
|
|
||||||
### N1-03 - Implement breadcrumb and transition-label policy
|
|
||||||
Status: TODO
|
|
||||||
Dependency: N1-01
|
|
||||||
Owners: Frontend developer, UX developer
|
|
||||||
Task description:
|
|
||||||
- Apply one breadcrumb convention across canonical domains and child routes.
|
|
||||||
- Add transition labels (formerly called) where migration policy requires them.
|
|
||||||
- Ensure transition labels are contextual, temporary, and do not alter canonical route names.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Breadcrumb behavior is consistent across root and child routes.
|
|
||||||
- [ ] Transition labels appear only where specified by migration policy.
|
|
||||||
- [ ] Canonical labels remain primary in all navigational surfaces.
|
|
||||||
- [ ] Unit tests cover breadcrumb generation and transition-label conditions.
|
|
||||||
|
|
||||||
### N1-04 - Implement migration alias and redirect framework
|
|
||||||
Status: TODO
|
|
||||||
Dependency: N1-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Update redirect and alias rules to map legacy paths to canonical IA routes according to the baseline deprecation map from sprint `20260218_005`.
|
|
||||||
- Ensure query parameters and fragments remain preserved through redirects.
|
|
||||||
- Add guard-safe behavior for authenticated and unauthenticated redirect paths.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Legacy settings and historical routes map to canonical targets per approved policy.
|
|
||||||
- [ ] Query and fragment preservation is verified for redirect families.
|
|
||||||
- [ ] No redirect loops are present in route tests.
|
|
||||||
- [ ] Redirect behavior is documented in sprint execution evidence.
|
|
||||||
|
|
||||||
### N1-05 - Navigation shell verification and regression tests
|
|
||||||
Status: TODO
|
|
||||||
Dependency: N1-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add targeted unit and E2E checks for sidebar groups, root routes, breadcrumbs, and key redirects.
|
|
||||||
- Verify behavior for desktop collapsed sidebar, desktop expanded, and mobile navigation drawer.
|
|
||||||
- Verify scope-filtered user profiles do not produce dead-end navigation.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit tests cover nav model, visibility filtering, and breadcrumb rules.
|
|
||||||
- [ ] E2E checks cover canonical root traversal and critical redirects.
|
|
||||||
- [ ] Mobile and desktop nav behavior passes targeted checks.
|
|
||||||
- [ ] No runtime nav errors in console/network during traversal.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for v2 navigation shell and route migration foundation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision dependency: Release Control capability rendering policy from sprint `20260218_005` is binding.
|
|
||||||
- Risk: redirect misconfiguration can silently break deep links; mitigate with explicit alias tests and no-loop checks.
|
|
||||||
- Risk: scope-filtered visibility can hide required parent groups; mitigate with permission-profile test matrix.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts`, `src/Web/StellaOps.Web/src/app/app.routes.ts`, `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-20: Confirm canonical root nav and route tree (`N1-01`, `N1-02`).
|
|
||||||
- 2026-02-21: Validate breadcrumb, transition labels, and redirect framework (`N1-03`, `N1-04`).
|
|
||||||
- 2026-02-22: Complete verification and publish regression evidence (`N1-05`).
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
# Sprint 20260218_007 - UI V2 Rewire Administration Foundation
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement the Administration domain as the owner surface for IAM, tenants/branding, notifications, usage/limits, policy governance, trust/signing, and system admin controls.
|
|
||||||
- Deliver fully specified Admin A0 through A7 screens with explicit cross-links to Release Control, Security and Risk, Evidence and Audit, and Platform Ops.
|
|
||||||
- Preserve migration compatibility from legacy settings paths while converging on canonical IA ownership.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: implemented admin routes/screens, cross-link behavior proofs, access-control validation, and migration checks.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md`, `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`.
|
|
||||||
- Downstream dependencies: informs `20260218_014` and `20260218_015` trust and policy consumption surfaces.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `A2-01` and `A2-02` can run in parallel.
|
|
||||||
- `A2-03` depends on `A2-01`.
|
|
||||||
- `A2-04` depends on `A2-01` and `A2-02`.
|
|
||||||
- `A2-05` depends on `A2-04`.
|
|
||||||
- `A2-06` depends on `A2-01` through `A2-05`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-21.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_sprint_spec_package.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/settings/settings.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/app.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/core/auth/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### A2-01 - Build Administration shell and A0 overview
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer, UX developer
|
|
||||||
Task description:
|
|
||||||
- Implement `Administration` root shell and overview (`A0`) with summary cards for Identity and Access, Tenant and Branding, Notifications, Usage and Limits, Policy Governance, Trust and Signing, and System.
|
|
||||||
- Overview must include operational drilldown links for quotas and system health while maintaining Administration ownership.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Administration shell route and overview screen are implemented.
|
|
||||||
- [ ] A0 contains all canonical cards and link targets.
|
|
||||||
- [ ] Ownership labels are explicit and match canonical IA.
|
|
||||||
- [ ] Legacy settings links route correctly into Administration surfaces.
|
|
||||||
|
|
||||||
### A2-02 - Implement A1 through A4 operational admin pages
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement `A1 Identity and Access`, `A2 Tenant and Branding`, `A3 Notifications`, and `A4 Usage and Limits` with complete page-level structure.
|
|
||||||
- Each page must include explicit action surfaces and linkouts: identity scope diagnostics, tenant branding application state, notification rule/channel/template management, and usage policy controls.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] A1 through A4 routes and page components are implemented.
|
|
||||||
- [ ] Each page includes defined sections, actions, and state handling.
|
|
||||||
- [ ] Cross-links to dependent domains are present where required.
|
|
||||||
- [ ] Access-controlled actions respect existing scopes and permissions.
|
|
||||||
|
|
||||||
### A2-03 - Implement A5 Policy Governance under Administration ownership
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A2-01
|
|
||||||
Owners: Frontend developer, Policy UX owner
|
|
||||||
Task description:
|
|
||||||
- Implement `A5 Policy Governance` as Administration-owned surface with deep links to Release Control gate usage contexts.
|
|
||||||
- Include policy baseline, rule, simulation, and exception-workflow views or links according to frozen ownership policy.
|
|
||||||
- Explicitly prevent regression to Release Control ownership labeling.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] A5 surface is present under Administration and labeled as owner.
|
|
||||||
- [ ] Policy sub-areas are reachable and internally consistent.
|
|
||||||
- [ ] Release Control linkage exists as consumer context, not owner.
|
|
||||||
- [ ] Breadcrumbs and labels reflect final ownership policy.
|
|
||||||
|
|
||||||
### A2-04 - Implement A6 Trust and Signing plus A7 System
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A2-02
|
|
||||||
Owners: Frontend developer, Security UX owner
|
|
||||||
Task description:
|
|
||||||
- Implement `A6 Trust and Signing` and `A7 System` as Administration-owned surfaces.
|
|
||||||
- A6 must expose keys, issuers, certificates, transparency log, trust scoring, and audit references, with explicit consumer links to Evidence and Security pages.
|
|
||||||
- A7 must expose system admin controls and diagnostics with drilldowns into Platform Ops operational pages.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] A6 and A7 routes and UI surfaces are implemented.
|
|
||||||
- [ ] A6 shows all trust primitives and allowed consumer links.
|
|
||||||
- [ ] A7 includes diagnostics/admin controls and ops drilldowns.
|
|
||||||
- [ ] Ownership labels prevent fallback to Evidence/System-root legacy models.
|
|
||||||
|
|
||||||
### A2-05 - Migrate legacy settings routes to Administration targets
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A2-04
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Update route mappings for legacy settings items that now belong to Administration.
|
|
||||||
- Ensure each legacy path resolves to one canonical Administration destination with preserved query and fragment where applicable.
|
|
||||||
- Keep transitional compatibility labels where migration policy requires them.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] All Administration-owned legacy settings routes have explicit canonical targets.
|
|
||||||
- [ ] Redirect/alias behavior matches deprecation baseline.
|
|
||||||
- [ ] Query and fragment preservation verified.
|
|
||||||
- [ ] No duplicate ownership routes remain active.
|
|
||||||
|
|
||||||
### A2-06 - Administration verification and access-control coverage
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A2-05
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add targeted tests for Administration route coverage, ownership labeling, deep links, and permission behavior.
|
|
||||||
- Validate no regressions for authenticated admin and non-admin users.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit/E2E tests cover A0 through A7 primary paths.
|
|
||||||
- [ ] Permission matrix checks validate visibility and action gating.
|
|
||||||
- [ ] Migration routes are validated via automated or scripted checks.
|
|
||||||
- [ ] Execution evidence captures passing behavior and residual risks.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for Administration ownership implementation in IA v2. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: Administration owns Policy Governance, Trust and Signing, and System per sprint `20260218_005` freeze.
|
|
||||||
- Risk: trust ownership migration can create duplicate entry points; mitigate by canonical route enforcement and alias pruning.
|
|
||||||
- Risk: policy governance users may expect Release Control ownership; mitigate with explicit context links and transition labels.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/settings/settings.routes.ts`, `src/Web/StellaOps.Web/src/app/core/auth/`, `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-21: A0-A4 implementation review (`A2-01`, `A2-02`).
|
|
||||||
- 2026-02-22: A5-A7 ownership and linkage review (`A2-03`, `A2-04`).
|
|
||||||
- 2026-02-23: Migration and verification closure (`A2-05`, `A2-06`).
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
# Sprint 20260218_008 - UI V2 Rewire Integrations and Platform Ops Data Integrity
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement canonical Integrations taxonomy and Platform Ops Data Integrity model with explicit separation of connectivity ownership and decision-impact consumption.
|
|
||||||
- Deliver screen-level behavior for integrations hub/detail and platform data-integrity operations including feeds, mirrors, airgap, locks, and health confidence.
|
|
||||||
- Ensure Security Data split is implemented exactly: Integrations and Platform Ops own source health operations, Security and Risk consumes gating impact.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: implemented integrations/ops routes and pages, cross-domain links, freshness-state behavior tests, and contract classification updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md`, `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`.
|
|
||||||
- Downstream dependencies: feeds into dashboard, approvals, environment detail, and advisory sources in sprints `012`, `011`, `013`, and `014`.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `I3-01` and `I3-02` can run in parallel.
|
|
||||||
- `I3-03` depends on `I3-01`.
|
|
||||||
- `I3-04` depends on `I3-02`.
|
|
||||||
- `I3-05` depends on `I3-03` and `I3-04`.
|
|
||||||
- `I3-06` depends on `I3-01` through `I3-05`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-10.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-15.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-21.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/operations/operations.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/integration-hub/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/feed-mirror/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### I3-01 - Implement Integrations domain taxonomy and overview
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement Integrations root with canonical categories: SCM, CI/CD, Registries, Secrets, Targets/Runtimes, Feeds, Notification Providers.
|
|
||||||
- Implement Integrations overview with operational health summary, degradation indicators, and direct jump actions to connector detail pages.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Integrations taxonomy and root pages match canonical category model.
|
|
||||||
- [ ] Overview includes status/freshness/impact summary cards.
|
|
||||||
- [ ] Category filtering and search behavior is deterministic.
|
|
||||||
- [ ] Broken/degraded connectors expose actionable drilldowns.
|
|
||||||
|
|
||||||
### I3-02 - Implement Platform Ops Data Integrity overview and subpages
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement Data Integrity as Platform Ops source of truth with subpages: overview, nightly report, feeds freshness, scan pipeline health, reachability ingest health, integration connectivity, DLQ and replays, data quality SLOs.
|
|
||||||
- Ensure pages summarize and link to specialized operational pages without duplicating ownership responsibilities.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Data Integrity overview and all required subpages are implemented.
|
|
||||||
- [ ] Subpage models include state for healthy, degraded, stale, and failed conditions.
|
|
||||||
- [ ] Deep links route to owning operational screens.
|
|
||||||
- [ ] No duplicated conflicting health source-of-truth is introduced.
|
|
||||||
|
|
||||||
### I3-03 - Implement Integration detail standard contract view
|
|
||||||
Status: TODO
|
|
||||||
Dependency: I3-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement standardized Integration detail template with required sections: config, status and health, errors/logs, test connection, impact map, permissions/scopes.
|
|
||||||
- Impact map must explicitly list affected release, approvals, SBOM ingest, and evidence workflows.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Detail template is consistent across connector categories.
|
|
||||||
- [ ] Impact map fields and links are present and actionable.
|
|
||||||
- [ ] Scope/permission diagnostics are visible and accurate.
|
|
||||||
- [ ] Error and recovery actions are available for degraded connectors.
|
|
||||||
|
|
||||||
### I3-04 - Implement Feeds and AirGap ops surfaces under Platform Ops
|
|
||||||
Status: TODO
|
|
||||||
Dependency: I3-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement or align pages for feed sources/freshness, mirrors, airgap bundles, and version locks under Platform Ops.
|
|
||||||
- Ensure Integrations and Data Integrity pages link to these screens as operational drilldowns.
|
|
||||||
- Enforce canonical ownership: these are operations controls, not settings controls.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Feed source, mirror, airgap, and lock pages are accessible under Platform Ops.
|
|
||||||
- [ ] Cross-links from Integrations and Data Integrity are implemented.
|
|
||||||
- [ ] Ownership labels and breadcrumbs are canonical.
|
|
||||||
- [ ] No stale settings-era route remains primary for these capabilities.
|
|
||||||
|
|
||||||
### I3-05 - Implement Security Data split wiring and impact propagation
|
|
||||||
Status: TODO
|
|
||||||
Dependency: I3-04
|
|
||||||
Owners: Frontend developer, Product engineer
|
|
||||||
Task description:
|
|
||||||
- Wire split contract for legacy Security Data:
|
|
||||||
- Connectivity and freshness managed in Integrations and Platform Ops.
|
|
||||||
- Decision impact consumed in Security and Risk Advisory Sources surface.
|
|
||||||
- Implement explicit context payload transfer for source freshness and impact severity used by downstream domains.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Split ownership is visible and consistent in UI flows.
|
|
||||||
- [ ] Context payloads for impact and freshness are available to downstream surfaces.
|
|
||||||
- [ ] No single page incorrectly combines both ownership responsibilities.
|
|
||||||
- [ ] Contract-ledger status is updated for affected rows.
|
|
||||||
|
|
||||||
### I3-06 - Verification, contract classification, and regression checks
|
|
||||||
Status: TODO
|
|
||||||
Dependency: I3-05
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Execute targeted tests for Integrations and Data Integrity route families, including degraded/stale states.
|
|
||||||
- Update endpoint contract ledger with observed status class for all screens in this sprint scope.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit/E2E checks cover Integrations and Data Integrity critical paths.
|
|
||||||
- [ ] Stale and degraded state behavior is validated.
|
|
||||||
- [ ] Contract ledger rows for this sprint are updated and reviewed.
|
|
||||||
- [ ] Residual risks are documented with concrete follow-up actions.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for Integrations and Platform Ops Data Integrity implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: Security Data ownership split from sprint `20260218_005` is mandatory.
|
|
||||||
- Risk: same data shown by multiple domains may drift; mitigate with single-source ownership and linked drilldowns only.
|
|
||||||
- Risk: degraded-state UX can become inconsistent across connectors; mitigate with one detail template and shared status conventions.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/integration-hub/`, `src/Web/StellaOps.Web/src/app/features/feed-mirror/`, `src/Web/StellaOps.Web/src/app/features/operations/operations.routes.ts`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-22: Integrations taxonomy and Data Integrity overview review (`I3-01`, `I3-02`).
|
|
||||||
- 2026-02-23: Detail and Feeds/AirGap ops review (`I3-03`, `I3-04`).
|
|
||||||
- 2026-02-24: Split wiring and verification closure (`I3-05`, `I3-06`).
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# Sprint 20260218_009 - UI V2 Rewire Bundle Organizer and Lifecycle
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement the Release Control bundle lifecycle as the immutable release input model.
|
|
||||||
- Deliver complete bundle catalog, bundle detail, bundle builder, bundle version detail, and materialize-to-environment flow.
|
|
||||||
- Ensure bundle identity is digest-first and supports per-repository changelog and config contract materialization.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: bundle route implementation, builder workflow behavior, validation states, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md`, `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`.
|
|
||||||
- Downstream dependencies: required for promotions (`010`), approvals (`011`), environment detail (`013`), and dashboard risk summaries (`012`).
|
|
||||||
- Safe parallelism:
|
|
||||||
- `B4-01` and `B4-02` can run in parallel.
|
|
||||||
- `B4-03` depends on `B4-01`.
|
|
||||||
- `B4-04` depends on `B4-02`.
|
|
||||||
- `B4-05` depends on `B4-03` and `B4-04`.
|
|
||||||
- `B4-06` depends on `B4-01` through `B4-05`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-12.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/release-orchestrator/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/core/api/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### B4-01 - Implement bundle catalog and bundle detail
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement bundle catalog view with canonical columns: bundle name, latest bundle version, component count, regions/environments impact, readiness indicators.
|
|
||||||
- Implement bundle detail with overview, versions, deployments usage, and evidence pointers.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Bundle catalog route and page are implemented with required fields.
|
|
||||||
- [ ] Bundle detail route and sections are implemented.
|
|
||||||
- [ ] Catalog-to-detail navigation and filtering work deterministically.
|
|
||||||
- [ ] Empty and error states are implemented with actionable guidance.
|
|
||||||
|
|
||||||
### B4-02 - Implement bundle builder workflow shell
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer, UX developer
|
|
||||||
Task description:
|
|
||||||
- Implement builder workflow with steps: select component versions, config contracts, changelog preview, validation.
|
|
||||||
- Builder must preserve a clear draft state model and prevent invalid step advancement.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Builder route and step navigation are implemented.
|
|
||||||
- [ ] Step progression is gated by required validations.
|
|
||||||
- [ ] Draft state is deterministic and recoverable on refresh/re-entry.
|
|
||||||
- [ ] Step error messaging is explicit and actionable.
|
|
||||||
|
|
||||||
### B4-03 - Implement component selection and digest-first identity view
|
|
||||||
Status: TODO
|
|
||||||
Dependency: B4-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement component version selection table with digest-first identity and display version labels.
|
|
||||||
- Show required metadata: SBOM presence/freshness, finding counters, reachability coverage, provenance pointers.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Component selector exposes digest-first identity and display version correctly.
|
|
||||||
- [ ] Required metadata fields are visible and sortable/filterable.
|
|
||||||
- [ ] Selection constraints prevent incompatible component combinations.
|
|
||||||
- [ ] Selection state is carried forward to later builder steps.
|
|
||||||
|
|
||||||
### B4-04 - Implement config-contract and changelog steps
|
|
||||||
Status: TODO
|
|
||||||
Dependency: B4-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement config-contract step including required inputs, source bindings (Vault/Consul style), and readiness checks.
|
|
||||||
- Implement changelog preview grouped by repository with clear commit/range summaries.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Config-contract step validates required inputs and binding readiness.
|
|
||||||
- [ ] Changelog step presents per-repo diffs with deterministic ordering.
|
|
||||||
- [ ] Validation failures are surfaced with clear remediation guidance.
|
|
||||||
- [ ] Output from both steps is preserved in draft bundle state.
|
|
||||||
|
|
||||||
### B4-05 - Implement bundle validation and immutable version detail
|
|
||||||
Status: TODO
|
|
||||||
Dependency: B4-04
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement final validation step covering policy readiness, SBOM readiness, reachability evidence availability, and data-freshness preconditions.
|
|
||||||
- Implement bundle version detail as immutable snapshot containing manifest digest, component map, config snapshot, and evidence links.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Validation summary includes all required readiness checks.
|
|
||||||
- [ ] Version publish action creates immutable snapshot representation in UI state.
|
|
||||||
- [ ] Bundle version detail displays manifest and linked evidence context.
|
|
||||||
- [ ] Failure states block publish with explicit blocking reasons.
|
|
||||||
|
|
||||||
### B4-06 - Implement materialize-to-environment flow and verification
|
|
||||||
Status: TODO
|
|
||||||
Dependency: B4-05
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Implement materialize-to-environment flow from bundle version detail, including environment selection and input readiness summary.
|
|
||||||
- Add targeted tests for bundle lifecycle routes and state transitions.
|
|
||||||
- Update contract ledger rows for bundle pages and builder dependencies.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Materialization flow is available from bundle version detail.
|
|
||||||
- [ ] Environment readiness summary includes required preconditions.
|
|
||||||
- [ ] Bundle lifecycle unit/E2E checks pass.
|
|
||||||
- [ ] Contract ledger updates are complete and reviewed.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for bundle organizer and lifecycle implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: bundle lifecycle semantics are digest-first and immutable.
|
|
||||||
- Risk: builder complexity can create fragile client state; mitigate with explicit step state machine and test coverage.
|
|
||||||
- Risk: missing backend composition endpoints may block progress; mitigate via early contract classification and `MISSING_NEW` proposals.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/`, `src/Web/StellaOps.Web/src/app/core/api/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-23: Catalog/detail and builder shell review (`B4-01`, `B4-02`).
|
|
||||||
- 2026-02-24: Selection/config/changelog review (`B4-03`, `B4-04`).
|
|
||||||
- 2026-02-25: Validation/materialization and test closure (`B4-05`, `B4-06`).
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
# Sprint 20260218_010 - UI V2 Rewire Releases Promotions and Run Timeline
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement bundle-version anchored promotions and release detail flow in Release Control.
|
|
||||||
- Deliver release creation, release list, release detail, and run timeline integration with rollback and replay-context entry points.
|
|
||||||
- Ensure promotion decisions and execution states are traceable and linked to approvals, evidence, and environment context.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: release routes/screens, promotion wizard behavior, timeline behavior, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_009_FE_ui_v2_rewire_bundle_organizer_lifecycle.md`, `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`.
|
|
||||||
- Downstream dependencies: required before approvals enhancements (`011`) and final dashboard/environment risk surfacing (`012`, `013`).
|
|
||||||
- Safe parallelism:
|
|
||||||
- `R5-01` and `R5-02` can run in parallel.
|
|
||||||
- `R5-03` depends on `R5-01`.
|
|
||||||
- `R5-04` depends on `R5-02`.
|
|
||||||
- `R5-05` depends on `R5-03` and `R5-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-13.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-14.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/release-orchestrator/releases/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/deployments/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### R5-01 - Implement promotions list with bundle-version identity
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement release promotions list with canonical columns: promotion id, bundle version identity, source and target env path, gate status, decision status, run status, and freshness indicators.
|
|
||||||
- Include list-level filters for environment, status, risk summary, and freshness state.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Promotions list route and page are implemented.
|
|
||||||
- [ ] Required columns and filters are present.
|
|
||||||
- [ ] Row navigation to release detail is deterministic.
|
|
||||||
- [ ] Empty, loading, and error states are complete.
|
|
||||||
|
|
||||||
### R5-02 - Implement create promotion wizard
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement wizard for selecting bundle version, promotion path, target environments, and input materialization readiness.
|
|
||||||
- Wizard must surface preflight checks and block submission when gating prerequisites are unresolved.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Create promotion wizard route and steps are implemented.
|
|
||||||
- [ ] Preflight checks and blocking states are visible and enforceable.
|
|
||||||
- [ ] Submission payload includes bundle-version identity and target path context.
|
|
||||||
- [ ] Validation and failure messages are actionable.
|
|
||||||
|
|
||||||
### R5-03 - Implement release detail with gate and evidence summary
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R5-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement release detail as canonical promotion case file with overview, gates summary, security snapshot, ops/data summary, and evidence pointers.
|
|
||||||
- Ensure detail page deep-links into approvals, environment detail, and run timeline.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Release detail sections and summaries are implemented.
|
|
||||||
- [ ] Deep links to approvals/env/run/evidence targets function correctly.
|
|
||||||
- [ ] Gate status is human-readable and includes blocking rationale.
|
|
||||||
- [ ] Page supports stale/partial data states gracefully.
|
|
||||||
|
|
||||||
### R5-04 - Implement run timeline shell and step detail integration
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R5-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement run timeline entry from release detail showing stage checkpoints, step statuses, and evidence capture moments.
|
|
||||||
- Implement step-detail navigation with logs/artifacts/evidence pointers and rollback trigger visibility.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Timeline view and step navigation are implemented.
|
|
||||||
- [ ] Step detail includes logs, artifacts, and evidence pointers.
|
|
||||||
- [ ] Rollback/retry control visibility follows status and role conditions.
|
|
||||||
- [ ] Timeline state handling is deterministic for partial runs.
|
|
||||||
|
|
||||||
### R5-05 - Verify promotions and timeline flows and update contract ledger
|
|
||||||
Status: TODO
|
|
||||||
Dependency: R5-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add targeted unit and E2E checks for release list, create wizard, release detail, and timeline paths.
|
|
||||||
- Update endpoint contract ledger for promotions and run timeline APIs, including any `MISSING_NEW` proposals.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Tests cover create -> detail -> timeline flow.
|
|
||||||
- [ ] Blocking and degraded states are tested.
|
|
||||||
- [ ] Contract-ledger rows are updated and reviewed.
|
|
||||||
- [ ] Residual risks are documented with owner and follow-up sprint.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for promotions and run timeline implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: promotions are anchored to immutable bundle versions.
|
|
||||||
- Risk: run timeline may depend on backend step granularity not currently exposed; mitigate with early contract classification.
|
|
||||||
- Risk: promotion preflight checks can diverge from approval checks; mitigate by reusing shared gate summary model.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/releases/`, `src/Web/StellaOps.Web/src/app/features/deployments/`, `src/Web/StellaOps.Web/src/app/core/api/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-24: Promotions list and creation wizard review (`R5-01`, `R5-02`).
|
|
||||||
- 2026-02-25: Release detail and timeline integration review (`R5-03`, `R5-04`).
|
|
||||||
- 2026-02-26: Verification and contract-ledger closure (`R5-05`).
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# Sprint 20260218_011 - UI V2 Rewire Approvals Decision Cockpit
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement approvals v2 as a self-sufficient decision cockpit with full operational/security/evidence context.
|
|
||||||
- Deliver approvals queue and approval detail tabs: overview, gates, security, reachability, ops/data health, evidence, replay/verify, and history.
|
|
||||||
- Ensure decision actions are auditable and clearly linked to policy, evidence, and environment impact.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: approvals UI implementation, decision-flow tests, audit-linked actions, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_010_FE_ui_v2_rewire_releases_promotions_run_timeline.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`, `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`.
|
|
||||||
- Downstream dependencies: dashboard and environment standardization sprints consume approval summaries.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `A6-01` and `A6-02` can run in parallel.
|
|
||||||
- `A6-03` depends on `A6-01`.
|
|
||||||
- `A6-04` depends on `A6-02`.
|
|
||||||
- `A6-05` depends on `A6-03` and `A6-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-17.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-13.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/approvals/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/core/api/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### A6-01 - Implement approvals queue v2
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement approvals queue with decision-critical columns: request id, bundle version, source and target env, gate status, risk summary (`CritR`, SBOM freshness), data confidence, pending approvers, and age.
|
|
||||||
- Add filter and search model for environment, status, risk type, and data confidence severity.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Queue route and page are implemented with required columns.
|
|
||||||
- [ ] Filters and search support decision-critical triage.
|
|
||||||
- [ ] Queue rows link to approval detail reliably.
|
|
||||||
- [ ] Empty/error/loading states are complete.
|
|
||||||
|
|
||||||
### A6-02 - Implement approval detail overview and gates tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement overview tab with promotion summary, target risk, data confidence summary, and quick decision context.
|
|
||||||
- Implement gates tab with full gate trace including policy decisions, timestamps, and blocking rationale.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Overview and gates tabs are implemented.
|
|
||||||
- [ ] Gate trace contains explicit pass/fail/waived states.
|
|
||||||
- [ ] Blocking reasons are human-readable and actionable.
|
|
||||||
- [ ] Tabs handle partial data and stale snapshots gracefully.
|
|
||||||
|
|
||||||
### A6-03 - Implement security, reachability, and ops/data tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A6-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement security tab showing SBOM findings by environment and delta context.
|
|
||||||
- Implement reachability tab with hybrid B/I/R summary and evidence age.
|
|
||||||
- Implement ops/data health tab with data integrity confidence and failing-source links.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Security tab includes env-scoped findings and summary metrics.
|
|
||||||
- [ ] Reachability tab includes B/I/R coverage and evidence-age indicators.
|
|
||||||
- [ ] Ops/data tab links to Data Integrity and source details.
|
|
||||||
- [ ] All three tabs use consistent severity and freshness semantics.
|
|
||||||
|
|
||||||
### A6-04 - Implement evidence, replay/verify, and history tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A6-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement evidence tab with decision packet contents and export hooks.
|
|
||||||
- Implement replay/verify tab for contextual verification path entry.
|
|
||||||
- Implement history tab with decision lifecycle timeline and actor trail.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Evidence tab includes packet references and actions.
|
|
||||||
- [ ] Replay/verify tab links to correct verification contexts.
|
|
||||||
- [ ] History tab captures lifecycle chronology and actors.
|
|
||||||
- [ ] Tabs preserve context identifiers across navigation.
|
|
||||||
|
|
||||||
### A6-05 - Implement decision actions and verify approvals flow
|
|
||||||
Status: TODO
|
|
||||||
Dependency: A6-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Implement approve/reject/defer/escalate actions with confirmation, reason capture, and auditable submission.
|
|
||||||
- Add targeted tests for queue to detail traversal and decision action outcomes.
|
|
||||||
- Update endpoint contract ledger entries for approvals and linked evidence/gate APIs.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Decision actions enforce reason capture and confirmation rules.
|
|
||||||
- [ ] Action outcomes are reflected in queue/detail/history states.
|
|
||||||
- [ ] Approvals flow tests pass for happy path and blocking path.
|
|
||||||
- [ ] Contract-ledger rows are updated and reviewed.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for approvals decision cockpit implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: approvals must be decision-complete without forcing operators to hunt across domains.
|
|
||||||
- Risk: tab payload fan-out may cause latency and partial data; mitigate with clear stale/partial states and lazy loading strategy.
|
|
||||||
- Risk: decision actions without audit context can fail compliance; mitigate with enforced reason capture and history linkage.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/approvals/`, `src/Web/StellaOps.Web/src/app/core/api/`, `src/Web/StellaOps.Web/tests/e2e/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-25: Queue and core tab review (`A6-01`, `A6-02`).
|
|
||||||
- 2026-02-26: Advanced tabs review (`A6-03`, `A6-04`).
|
|
||||||
- 2026-02-27: Decision action verification and ledger update (`A6-05`).
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
# Sprint 20260218_012 - UI V2 Rewire Dashboard V3 Mission Board
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement Dashboard v3 as the release mission board with environment risk, SBOM state, hybrid reachability, and data-integrity signals.
|
|
||||||
- Deliver mission-critical drilldowns to releases, approvals, environment detail, security findings, and ops data integrity.
|
|
||||||
- Ensure dashboard aggregates are accurate, freshness-aware, and non-duplicative of owning domains.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: dashboard UI implementation, card/drawer flows, cross-link tests, and aggregate contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`, `SPRINT_20260218_010_FE_ui_v2_rewire_releases_promotions_run_timeline.md`, `SPRINT_20260218_011_FE_ui_v2_rewire_approvals_decision_cockpit.md`.
|
|
||||||
- Downstream dependencies: consumed by environment detail and security/evidence consolidation sprints.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `D7-01` and `D7-02` can run in parallel.
|
|
||||||
- `D7-03` depends on `D7-01`.
|
|
||||||
- `D7-04` depends on `D7-02`.
|
|
||||||
- `D7-05` depends on `D7-03` and `D7-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-16.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/control-plane/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/layout/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### D7-01 - Implement dashboard header, filters, and mission summary
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement dashboard header with global filters (region, environment, time window) and mission status summary.
|
|
||||||
- Include canonical mission summary indicators for active promotions, blocked promotions, highest risk environment, and data integrity status.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Header and global filters are implemented and functional.
|
|
||||||
- [ ] Mission summary indicators are visible and context-aware.
|
|
||||||
- [ ] Filter state is deterministic and URL-safe where required.
|
|
||||||
- [ ] Empty and loading states are defined.
|
|
||||||
|
|
||||||
### D7-02 - Implement regional pipeline status board
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement regional pipeline board showing per-environment deploy status, SBOM freshness, CritR summary, and B/I/R coverage.
|
|
||||||
- Provide direct links to environment detail and release detail from pipeline nodes.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Regional pipeline board includes required env-level status fields.
|
|
||||||
- [ ] Node links route to environment and release context correctly.
|
|
||||||
- [ ] Severity and freshness indicators follow shared conventions.
|
|
||||||
- [ ] Board handles incomplete regional data without breaking layout.
|
|
||||||
|
|
||||||
### D7-03 - Implement SBOM findings snapshot and drawer flow
|
|
||||||
Status: TODO
|
|
||||||
Dependency: D7-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement SBOM findings snapshot card and expandable drawer with environment-level breakdown.
|
|
||||||
- Include filters for critical reachable focus, prod-only scope, and stale/missing SBOM states.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Snapshot card is present with actionable summary values.
|
|
||||||
- [ ] Drawer flow includes env breakdown and quick filters.
|
|
||||||
- [ ] Drawer links to security findings with preserved context.
|
|
||||||
- [ ] Error/stale indicators are explicit.
|
|
||||||
|
|
||||||
### D7-04 - Implement hybrid reachability and nightly/data-integrity cards
|
|
||||||
Status: TODO
|
|
||||||
Dependency: D7-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement hybrid reachability summary card (B/I/R coverage) and nightly/data-integrity signals card.
|
|
||||||
- Cards must link to owning domains (Security and Risk, Platform Ops Data Integrity) and avoid duplicating deep operational controls.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Hybrid reachability and nightly/data cards are implemented.
|
|
||||||
- [ ] Cards link to owning pages with context filters.
|
|
||||||
- [ ] Card semantics and thresholds align with shared severity model.
|
|
||||||
- [ ] No duplicated ownership behavior is introduced.
|
|
||||||
|
|
||||||
### D7-05 - Dashboard verification and aggregate contract updates
|
|
||||||
Status: TODO
|
|
||||||
Dependency: D7-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add tests covering dashboard mission board interactions, drawer behavior, and drilldown links.
|
|
||||||
- Update endpoint contract ledger rows for dashboard aggregate dependencies and freshness model.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit/E2E checks pass for dashboard critical flows.
|
|
||||||
- [ ] Drilldown navigation is validated across key cards.
|
|
||||||
- [ ] Aggregate contract rows updated in ledger.
|
|
||||||
- [ ] Residual dashboard risks documented with follow-up owners.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for dashboard v3 mission-board implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: dashboard is a mission board and must summarize, not re-own, downstream domain controls.
|
|
||||||
- Risk: aggregate cards may mask stale source data; mitigate with explicit freshness badges and stale state UX.
|
|
||||||
- Risk: too many cards can fragment operator attention; mitigate by preserving critical path ordering and contextual drilldowns.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/control-plane/`, `src/Web/StellaOps.Web/src/app/layout/`, `src/Web/StellaOps.Web/src/app/core/api/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-26: Header, filters, and pipeline board review (`D7-01`, `D7-02`).
|
|
||||||
- 2026-02-27: Snapshot and integrity card review (`D7-03`, `D7-04`).
|
|
||||||
- 2026-02-28: Verification and ledger update closure (`D7-05`).
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
# Sprint 20260218_013 - UI V2 Rewire Environment Detail Standardization
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement standardized Environment Detail as the single environment decision view.
|
|
||||||
- Deliver canonical environment header and tabs: overview, deploy status, SBOM/findings, reachability, inputs, promotions/approvals, data confidence, evidence/audit.
|
|
||||||
- Ensure environment page links to bundle, release run, security, ops, and evidence contexts without ownership duplication.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: environment detail implementation, tab behavior checks, cross-link validation, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_009_FE_ui_v2_rewire_bundle_organizer_lifecycle.md`, `SPRINT_20260218_010_FE_ui_v2_rewire_releases_promotions_run_timeline.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`, `SPRINT_20260218_012_FE_ui_v2_rewire_dashboard_v3_mission_board.md`.
|
|
||||||
- Downstream dependencies: provides core context for approvals, security, and evidence deep-link consistency.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `E8-01` and `E8-02` can run in parallel.
|
|
||||||
- `E8-03` depends on `E8-01`.
|
|
||||||
- `E8-04` depends on `E8-02`.
|
|
||||||
- `E8-05` depends on `E8-03` and `E8-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-18.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/release-orchestrator/environments/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### E8-01 - Implement standardized environment header shell
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement environment detail shell with canonical header summary: deploy status, SBOM freshness, CritR, hybrid B/I/R coverage, and data confidence.
|
|
||||||
- Header must include explicit identity for region, environment, and active bundle/release context.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Header shows all required summary fields.
|
|
||||||
- [ ] Region/environment identity and context links are explicit.
|
|
||||||
- [ ] Severity and freshness semantics align with shared model.
|
|
||||||
- [ ] Header supports partial data with deterministic fallback states.
|
|
||||||
|
|
||||||
### E8-02 - Implement overview and deploy-status tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement overview tab with top risks, pending actions, and quick links.
|
|
||||||
- Implement deploy-status tab with runtime targets, service states, and recent deployment checkpoints.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Overview tab includes risk and action summaries.
|
|
||||||
- [ ] Deploy tab includes target/service status detail.
|
|
||||||
- [ ] Links route to release run and deployment details correctly.
|
|
||||||
- [ ] Tab state remains stable across filter changes.
|
|
||||||
|
|
||||||
### E8-03 - Implement SBOM/findings and reachability tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: E8-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement SBOM/findings tab with deployed inventory, SBOM state, CritR/HighR/HighNR summaries, and finding links.
|
|
||||||
- Implement reachability tab with hybrid B/I/R matrix, source presence, and evidence age indicators.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] SBOM/findings tab includes env-scoped inventory and finding summary.
|
|
||||||
- [ ] Reachability tab includes B/I/R matrix and evidence-age fields.
|
|
||||||
- [ ] Links to Security and Risk findings preserve environment context.
|
|
||||||
- [ ] Non-available data states are represented clearly.
|
|
||||||
|
|
||||||
### E8-04 - Implement inputs, promotions/approvals, data confidence, and evidence tabs
|
|
||||||
Status: TODO
|
|
||||||
Dependency: E8-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement inputs tab with configuration materialization readiness and binding diagnostics.
|
|
||||||
- Implement promotions/approvals tab with env-scoped timeline and pending decisions.
|
|
||||||
- Implement data confidence tab as env-scoped Data Integrity summary.
|
|
||||||
- Implement evidence/audit tab with env snapshot and proof references.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Inputs tab includes readiness and binding visibility.
|
|
||||||
- [ ] Promotions/approvals tab includes pending and historical context.
|
|
||||||
- [ ] Data confidence tab links to owning Ops Data Integrity pages.
|
|
||||||
- [ ] Evidence tab links to Evidence and Audit exports/references.
|
|
||||||
|
|
||||||
### E8-05 - Verify environment detail and update contract ledger
|
|
||||||
Status: TODO
|
|
||||||
Dependency: E8-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add tests for environment detail shell and all canonical tabs.
|
|
||||||
- Verify deep links to bundles, release runs, approvals, security, ops, and evidence.
|
|
||||||
- Update endpoint contract ledger rows for environment-scoped data dependencies.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Tab-level tests cover major happy path and degraded path scenarios.
|
|
||||||
- [ ] Cross-domain deep links are validated end-to-end.
|
|
||||||
- [ ] Ledger rows for environment detail dependencies are updated.
|
|
||||||
- [ ] Residual risks are recorded with owner and mitigation path.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for standardized environment detail implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: environment detail is the single environment decision page and must aggregate, not duplicate domain ownership.
|
|
||||||
- Risk: tab fan-out can create latency or stale states; mitigate with explicit loading/staleness semantics.
|
|
||||||
- Risk: inconsistent context propagation can break drilldowns; mitigate with canonical environment context payload across links.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/environments/`, `src/Web/StellaOps.Web/src/app/core/api/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-27: Header and first tab-set review (`E8-01`, `E8-02`).
|
|
||||||
- 2026-02-28: Remaining tab-set review (`E8-03`, `E8-04`).
|
|
||||||
- 2026-03-01: Verification and ledger update closure (`E8-05`).
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
# Sprint 20260218_014 - UI V2 Rewire Security and Risk Consolidation
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement the consolidated Security and Risk domain with decision-first ordering.
|
|
||||||
- Deliver risk overview, findings explorer/detail, vulnerabilities explorer/detail, SBOM lake/graph placement, VEX, exceptions, and Advisory Sources.
|
|
||||||
- Preserve second-class visibility of reachability and enforce ownership split for advisory source operations vs decision impact.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: security route implementation, cross-link behavior, advisory-source behavior, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`, `SPRINT_20260218_012_FE_ui_v2_rewire_dashboard_v3_mission_board.md`, `SPRINT_20260218_013_FE_ui_v2_rewire_environment_detail_standardization.md`.
|
|
||||||
- Downstream dependencies: evidence and audit integration checks in sprint `015` and cutover in sprint `016`.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `S9-01` and `S9-02` can run in parallel.
|
|
||||||
- `S9-03` depends on `S9-01`.
|
|
||||||
- `S9-04` depends on `S9-02`.
|
|
||||||
- `S9-05` depends on `S9-03` and `S9-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-19.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_advisory_sources_spec.md` (or equivalent signed output from sprint `20260218_005`)
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/security/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### S9-01 - Implement risk overview and findings explorer
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement risk overview as the entry page emphasizing what blocks releases and where critical reachable risk exists.
|
|
||||||
- Implement findings explorer with filters for severity, reachability, environment, bundle, freshness, and exception status.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Risk overview and findings explorer routes are implemented.
|
|
||||||
- [ ] Findings filters support decision-critical triage dimensions.
|
|
||||||
- [ ] Risk-to-release and risk-to-environment links are functional.
|
|
||||||
- [ ] Data-state behavior for stale/partial feeds is clear.
|
|
||||||
|
|
||||||
### S9-02 - Implement vulnerabilities explorer and detail surfaces
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement vulnerabilities explorer for CVE-centric analysis with impacted environment and disposition context.
|
|
||||||
- Implement vulnerability detail with affected components, evidence pointers, exception linkage, and issuer/VEX context where applicable.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Vulnerabilities list and detail routes are implemented.
|
|
||||||
- [ ] Required impacted-context fields are visible.
|
|
||||||
- [ ] Links to findings, exceptions, and evidence are functional.
|
|
||||||
- [ ] Placeholder-only legacy behavior is removed.
|
|
||||||
|
|
||||||
### S9-03 - Implement SBOM data surfaces and VEX/Exceptions integration
|
|
||||||
Status: TODO
|
|
||||||
Dependency: S9-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement SBOM lake and SBOM graph placement within Security and Risk domain with decision-context links.
|
|
||||||
- Implement VEX and exceptions pages with explicit links to trust, policy, and approval implications.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] SBOM lake/graph pages are reachable within canonical security structure.
|
|
||||||
- [ ] VEX and exceptions pages include decision-context links.
|
|
||||||
- [ ] Trust consumer links align with Administration ownership policy.
|
|
||||||
- [ ] Reachability remains second-class but visible in relevant views.
|
|
||||||
|
|
||||||
### S9-04 - Implement Advisory Sources screen and split ownership behavior
|
|
||||||
Status: TODO
|
|
||||||
Dependency: S9-02
|
|
||||||
Owners: Frontend developer, Product engineer
|
|
||||||
Task description:
|
|
||||||
- Implement Advisory Sources according to signed S00 spec, including source health, decision-impact indicators, and drilldowns.
|
|
||||||
- Enforce split behavior:
|
|
||||||
- Integrations and Platform Ops own connectivity and mirror operations.
|
|
||||||
- Security and Risk owns gating impact representation and policy relevance.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Advisory Sources route and screen are implemented per signed S00 spec.
|
|
||||||
- [ ] Source health and decision-impact fields follow ownership matrix.
|
|
||||||
- [ ] Drilldowns to Integrations and Platform Ops preserve context.
|
|
||||||
- [ ] Conflict/stale/unavailable advisory states are explicitly handled.
|
|
||||||
|
|
||||||
### S9-05 - Security consolidation verification and ledger updates
|
|
||||||
Status: TODO
|
|
||||||
Dependency: S9-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add targeted tests for all consolidated security routes and cross-domain links.
|
|
||||||
- Update endpoint contract ledger rows for security and advisory-source dependencies.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit/E2E checks pass for risk/findings/vuln/SBOM/VEX/exceptions/advisory routes.
|
|
||||||
- [ ] Cross-links to approvals/releases/env/evidence/ops are validated.
|
|
||||||
- [ ] Contract-ledger rows updated with final status class and deltas.
|
|
||||||
- [ ] Residual issues are recorded with owner and mitigation plan.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for Security and Risk consolidation implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: Security and Risk is decision-first; reachability remains visible as second-class context.
|
|
||||||
- Risk: Advisory Sources can regress into mixed ownership; mitigate with strict field-level ownership contract from S00.
|
|
||||||
- Risk: SBOM and vulnerability models may diverge in filters/labels; mitigate with shared taxonomy and filter-strip components.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/security/`, `src/Web/StellaOps.Web/src/app/core/api/`, `src/Web/StellaOps.Web/tests/e2e/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-02-28: Core risk/findings/vuln review (`S9-01`, `S9-02`).
|
|
||||||
- 2026-03-01: SBOM/VEX/Exceptions and Advisory Sources review (`S9-03`, `S9-04`).
|
|
||||||
- 2026-03-02: Verification and ledger closure (`S9-05`).
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
# Sprint 20260218_015 - UI V2 Rewire Evidence and Audit Consolidation
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Implement consolidated Evidence and Audit domain centered on release, bundle, environment, and approval evidence retrieval.
|
|
||||||
- Deliver evidence home router, evidence packs, bundles, export center, proof chains, replay/verify, and audit log surfaces.
|
|
||||||
- Enforce trust ownership transition: Trust and Signing remains Administration-owned with Evidence as consumer.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: evidence route implementation, retrieval/export flows, audit navigation proofs, and contract-ledger updates.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_005_DOCS_ui_v2_rewire_spec_freeze.md`, `SPRINT_20260218_007_FE_ui_v2_rewire_administration_foundation.md`, `SPRINT_20260218_010_FE_ui_v2_rewire_releases_promotions_run_timeline.md`, `SPRINT_20260218_011_FE_ui_v2_rewire_approvals_decision_cockpit.md`.
|
|
||||||
- Downstream dependencies: cutover and QA sprint `016`.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `V10-01` and `V10-02` can run in parallel.
|
|
||||||
- `V10-03` depends on `V10-01`.
|
|
||||||
- `V10-04` depends on `V10-02`.
|
|
||||||
- `V10-05` depends on `V10-03` and `V10-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/pack-20.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_trust_ownership_transition.md` (or equivalent signed output from sprint `20260218_005`)
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/evidence/`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/features/evidence-export/`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### V10-01 - Implement evidence home router and navigation model
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement Evidence home as router page that directs users by evidence need: promotion decision, bundle evidence, environment snapshot, proof verification, and audit trail.
|
|
||||||
- Include clear entry actions to packs, bundles, exports, proof chains, replay, and audit log.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Evidence home route and page are implemented.
|
|
||||||
- [ ] Router shortcuts map to canonical evidence surfaces.
|
|
||||||
- [ ] Context keys for release/bundle/env/approval are preserved.
|
|
||||||
- [ ] Empty/error states provide actionable next steps.
|
|
||||||
|
|
||||||
### V10-02 - Implement evidence packs, bundles, and detail pages
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement evidence packs list/detail and evidence bundles list/detail pages.
|
|
||||||
- Detail pages must include payload summary, evidence inventory, related decisions, and export actions.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Packs and bundles list/detail routes are implemented.
|
|
||||||
- [ ] Detail pages show inventory and relation context.
|
|
||||||
- [ ] Export and cross-link actions are functional.
|
|
||||||
- [ ] Search/filter behavior is deterministic.
|
|
||||||
|
|
||||||
### V10-03 - Implement export center, proof chains, and replay/verify
|
|
||||||
Status: TODO
|
|
||||||
Dependency: V10-01
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement export center with export jobs and scope templates.
|
|
||||||
- Implement proof chains view and detail traversal.
|
|
||||||
- Implement replay/verify surface with context-preserving entry from approvals and releases.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Export center, proof chains, and replay routes are implemented.
|
|
||||||
- [ ] Context-preserving links from approvals/releases are functioning.
|
|
||||||
- [ ] Job/status handling includes queued/running/failed/succeeded states.
|
|
||||||
- [ ] Replay entry paths retain decision context identifiers.
|
|
||||||
|
|
||||||
### V10-04 - Implement consolidated audit log and trust consumer links
|
|
||||||
Status: TODO
|
|
||||||
Dependency: V10-02
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Implement consolidated audit log view with filters for actor, action, resource type, and domain context.
|
|
||||||
- Implement consumer links from evidence pages to Administration-owned Trust and Signing pages per transition policy.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Audit log route and filter model are implemented.
|
|
||||||
- [ ] Audit entries deep-link to related evidence/release/approval entities.
|
|
||||||
- [ ] Trust links follow Administration ownership without duplicate owner pages.
|
|
||||||
- [ ] Legacy trust-in-evidence ownership labels are removed.
|
|
||||||
|
|
||||||
### V10-05 - Evidence consolidation verification and ledger updates
|
|
||||||
Status: TODO
|
|
||||||
Dependency: V10-04
|
|
||||||
Owners: Frontend developer, QA
|
|
||||||
Task description:
|
|
||||||
- Add targeted tests for end-to-end evidence retrieval/export/replay/audit navigation.
|
|
||||||
- Update endpoint contract ledger rows for evidence APIs and ownership boundaries.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Unit/E2E checks pass for evidence critical workflows.
|
|
||||||
- [ ] Cross-domain links from approvals/releases/env remain intact.
|
|
||||||
- [ ] Contract-ledger rows are updated and reviewed.
|
|
||||||
- [ ] Residual risks and blockers are captured with mitigation owners.
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for Evidence and Audit consolidation implementation. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Decision binding: Trust and Signing ownership remains in Administration; Evidence consumes trust state.
|
|
||||||
- Risk: evidence context keys can drift between pages; mitigate with unified context identifier model.
|
|
||||||
- Risk: export and replay status handling may diverge by endpoint; mitigate with shared status component and contract alignment.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/features/evidence/`, `src/Web/StellaOps.Web/src/app/features/evidence-export/`, `src/Web/StellaOps.Web/src/app/core/api/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-03-01: Home router and packs/bundles review (`V10-01`, `V10-02`).
|
|
||||||
- 2026-03-02: Export/proof/replay and audit/trust-link review (`V10-03`, `V10-04`).
|
|
||||||
- 2026-03-03: Verification and ledger closure (`V10-05`).
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
# Sprint 20260218_016 - UI V2 Rewire Cutover Redirects and QA Readiness
|
|
||||||
|
|
||||||
## Topic & Scope
|
|
||||||
- Execute final IA cutover for canonical routes and labels with migration-safe redirects and compatibility behavior.
|
|
||||||
- Complete end-to-end QA coverage for all root domains and critical workflows under the finalized IA.
|
|
||||||
- Deliver release-readiness evidence package with residual risk register and go/no-go checklist.
|
|
||||||
- Working directory: `src/Web/StellaOps.Web`.
|
|
||||||
- Expected evidence: final redirect map implementation, E2E verification artifacts, accessibility checks, readiness report.
|
|
||||||
|
|
||||||
## Dependencies & Concurrency
|
|
||||||
- Upstream dependencies: `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`, `SPRINT_20260218_007_FE_ui_v2_rewire_administration_foundation.md`, `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`, `SPRINT_20260218_009_FE_ui_v2_rewire_bundle_organizer_lifecycle.md`, `SPRINT_20260218_010_FE_ui_v2_rewire_releases_promotions_run_timeline.md`, `SPRINT_20260218_011_FE_ui_v2_rewire_approvals_decision_cockpit.md`, `SPRINT_20260218_012_FE_ui_v2_rewire_dashboard_v3_mission_board.md`, `SPRINT_20260218_013_FE_ui_v2_rewire_environment_detail_standardization.md`, `SPRINT_20260218_014_FE_ui_v2_rewire_security_risk_consolidation.md`, `SPRINT_20260218_015_FE_ui_v2_rewire_evidence_audit_consolidation.md`.
|
|
||||||
- Safe parallelism:
|
|
||||||
- `C11-01` and `C11-02` can run in parallel.
|
|
||||||
- `C11-03` depends on `C11-01`.
|
|
||||||
- `C11-04` depends on `C11-02`.
|
|
||||||
- `C11-05` depends on `C11-03` and `C11-04`.
|
|
||||||
|
|
||||||
## Documentation Prerequisites
|
|
||||||
- `docs/modules/ui/v2-rewire/source-of-truth.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/authority-matrix.md`
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md` (or equivalent signed output from sprint `20260218_005`)
|
|
||||||
- `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
|
||||||
- `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`
|
|
||||||
- `src/Web/StellaOps.Web/tests/e2e/`
|
|
||||||
- `docs/qa/feature-checks/FLOW.md`
|
|
||||||
|
|
||||||
## Delivery Tracker
|
|
||||||
|
|
||||||
### C11-01 - Finalize redirect and alias cutover behavior
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Apply final redirect/alias behavior from approved deprecation map for all legacy route families.
|
|
||||||
- Remove temporary aliases marked remove-later where safe and maintain required compatibility aliases.
|
|
||||||
- Verify query and fragment preservation for all retained redirects.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Redirect map implementation matches approved deprecation baseline.
|
|
||||||
- [ ] Removed aliases are explicitly listed with rationale.
|
|
||||||
- [ ] Preserved aliases are tested for query/fragment behavior.
|
|
||||||
- [ ] No redirect loops remain.
|
|
||||||
|
|
||||||
### C11-02 - Apply final canonical labeling and breadcrumb cleanup
|
|
||||||
Status: TODO
|
|
||||||
Dependency: none
|
|
||||||
Owners: Frontend developer, UX developer
|
|
||||||
Task description:
|
|
||||||
- Remove stale transition labels where migration window is complete and keep only required compatibility labels.
|
|
||||||
- Ensure all root and child routes use canonical names in nav, headers, breadcrumbs, and search entries.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Canonical naming is consistent across all navigation surfaces.
|
|
||||||
- [ ] Deprecated labels are removed or marked with explicit sunset policy.
|
|
||||||
- [ ] Breadcrumb chains are accurate across all root domains.
|
|
||||||
- [ ] Search and quick-nav entries align with canonical names.
|
|
||||||
|
|
||||||
### C11-03 - Execute critical-path E2E verification suite
|
|
||||||
Status: TODO
|
|
||||||
Dependency: C11-01
|
|
||||||
Owners: QA, Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Execute E2E verification for critical workflows:
|
|
||||||
- dashboard to release to approval decision flow,
|
|
||||||
- bundle to promotion to run timeline,
|
|
||||||
- environment detail to security and evidence,
|
|
||||||
- admin trust/policy/system cross-domain flows,
|
|
||||||
- integrations and data-integrity stale/failure handling.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Critical-path E2E scenarios pass with evidence artifacts.
|
|
||||||
- [ ] Failures are triaged with root cause and owner assignment.
|
|
||||||
- [ ] No unresolved critical severity failures remain.
|
|
||||||
- [ ] Verification records include command output and artifact paths.
|
|
||||||
|
|
||||||
### C11-04 - Execute accessibility and regression hardening checks
|
|
||||||
Status: TODO
|
|
||||||
Dependency: C11-02
|
|
||||||
Owners: QA, Frontend developer
|
|
||||||
Task description:
|
|
||||||
- Run focused accessibility checks on nav, complex tables, tab systems, and action dialogs.
|
|
||||||
- Run regression checks for permissions, mobile navigation, and high-latency/stale-data UI behavior.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Accessibility checks are run and documented for critical surfaces.
|
|
||||||
- [ ] Keyboard and focus behavior is verified for nav and dialogs.
|
|
||||||
- [ ] Mobile navigation and responsive behavior pass checks.
|
|
||||||
- [ ] Regression issues are triaged with owner and priority.
|
|
||||||
|
|
||||||
### C11-05 - Publish release readiness package and go/no-go decision
|
|
||||||
Status: TODO
|
|
||||||
Dependency: C11-04
|
|
||||||
Owners: Project Manager, QA lead
|
|
||||||
Task description:
|
|
||||||
- Publish final readiness package including route cutover summary, QA outcomes, contract-ledger status, and residual risks.
|
|
||||||
- Produce go/no-go recommendation with explicit blocking conditions if unresolved.
|
|
||||||
|
|
||||||
Completion criteria:
|
|
||||||
- [ ] Readiness package includes route, QA, and contract-ledger summaries.
|
|
||||||
- [ ] Residual risks are clearly prioritized with mitigation owner.
|
|
||||||
- [ ] Go/no-go recommendation is explicit and justified.
|
|
||||||
- [ ] Sprint closure status reflects real outcome (`DONE` or `BLOCKED`).
|
|
||||||
|
|
||||||
## Execution Log
|
|
||||||
| Date (UTC) | Update | Owner |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 2026-02-18 | Sprint created for final IA cutover, redirects, and QA readiness. | Planning |
|
|
||||||
|
|
||||||
## Decisions & Risks
|
|
||||||
- Risk: removing aliases too early can break saved deep links; mitigate with explicit traffic/usage checks before removal.
|
|
||||||
- Risk: final label cleanup can confuse users during transition; mitigate with controlled compatibility labels and communication.
|
|
||||||
- Risk: cross-domain E2E suite may reveal latent contract gaps; mitigate with strict triage and no silent acceptance.
|
|
||||||
- Existing code references: `src/Web/StellaOps.Web/src/app/routes/legacy-redirects.routes.ts`, `src/Web/StellaOps.Web/tests/e2e/`, `src/Web/StellaOps.Web/src/app/layout/`.
|
|
||||||
|
|
||||||
## Next Checkpoints
|
|
||||||
- 2026-03-03: Redirect and labeling cutover review (`C11-01`, `C11-02`).
|
|
||||||
- 2026-03-04: E2E and accessibility/regression review (`C11-03`, `C11-04`).
|
|
||||||
- 2026-03-05: Readiness package and go/no-go decision (`C11-05`).
|
|
||||||
454
docs/implplan/SPRINT_20260219_017_FE_qa_live_run_bug_fixes.md
Normal file
454
docs/implplan/SPRINT_20260219_017_FE_qa_live_run_bug_fixes.md
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
# Sprint 20260219-017 — QA Live-Run Bug Triage & Fixes
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- Post-QA bug triage sprint from the 2026-02-19 live Playwright walkthrough (see `docs/qa/issues-report-2026-02-19.md`).
|
||||||
|
- 18 issues found; root causes investigated for all high/critical items.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/` (primary); cross-module changes allowed for ISSUE-002 (`src/Integrations/`) and ISSUE-004 (`src/Authority/`).
|
||||||
|
- Expected evidence: passing tests, fixed routes, no console errors on key pages, NaN resolved.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Depends on: SPRINT_20260219_016 (Orchestrator pack backend) for route context.
|
||||||
|
- The v2 routes issue (TASK-01) may partially overlap with SPRINT_20260219_002/003 (navigation shell work); check before touching `app.routes.ts`.
|
||||||
|
- TASK-04 (Authority user endpoints) is a backend sprint extension — safe to parallelize with FE tasks.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/qa/issues-report-2026-02-19.md` — full issue list with screenshots.
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_nav_rendering_policy.md` — v2 IA route policy.
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md` — old → new route map.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/app.routes.ts` — root route definitions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Investigate and fix v2 route guards redirecting to `/`
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Every v2 route (`/release-control/*`, `/security-risk/*`, `/evidence-audit/*`, `/platform-ops/*`,
|
||||||
|
`/administration/*`, `/dashboard`) redirects silently to `/`. Root-cause investigation confirmed
|
||||||
|
all route definitions and components exist. The redirects are caused by one or more of the three
|
||||||
|
`canMatch` guards failing:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
canMatch: [requireConfigGuard, requireBackendsReachableGuard, requireAuthGuard]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause confirmed (2026-02-19):**
|
||||||
|
The issue is NOT a code bug in the guards. Investigation of all three guards found:
|
||||||
|
- `requireAuthGuard` — returns `UrlTree` to `/welcome` on failure; cannot cause catch-all fallthrough
|
||||||
|
- `requireBackendsReachableGuard` — returns `UrlTree` to `/setup?reason=unreachable` on failure; cannot cause catch-all fallthrough
|
||||||
|
- `requireConfigGuard` — returns `UrlTree` to `/setup` or `/setup/wizard` on failure; cannot cause catch-all fallthrough
|
||||||
|
|
||||||
|
None of the guards return `false` — all return `UrlTree` on failure. A `UrlTree` redirect cannot
|
||||||
|
trigger the catch-all `{ path: '**', redirectTo: '' }` because it's an immediate redirect, not
|
||||||
|
route fall-through.
|
||||||
|
|
||||||
|
**Actual root cause:** `app.routes.ts` is listed as `M` (modified) in `git status`. The deployed
|
||||||
|
Docker stack runs the last committed version of the Angular bundle, which does NOT contain the
|
||||||
|
v2 canonical route definitions. The v2 routes (`/release-control`, `/security-risk`, etc.) and
|
||||||
|
their child route modules (`release-control.routes.ts`, `dashboard.routes.ts`, etc.) exist only
|
||||||
|
in the current working tree as untracked files (`??`). The Docker container was built before these
|
||||||
|
files were committed.
|
||||||
|
|
||||||
|
**Fix:** Build and redeploy the Angular app with the current working tree code.
|
||||||
|
1. `ng build` in `src/Web/StellaOps.Web/`
|
||||||
|
2. Rebuild/restart the console Docker container with the new dist output
|
||||||
|
|
||||||
|
The source code is correct. No code change needed.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [x] Root cause guard identified and documented in Decisions & Risks.
|
||||||
|
- [x] Root cause confirmed: deployment gap, not a code bug.
|
||||||
|
- [ ] All 22 v2 routes tested (via Playwright) render their designated component, not home. *(pending rebuild)*
|
||||||
|
- [ ] No regression on v1 routes. *(pending rebuild)*
|
||||||
|
- [x] `config.json` investigation finding recorded.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix Integration Hub enum mismatch (FE `type=0` vs BE `Registry=1`)
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer + Integrations BE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
`/integrations` hub fires 10 console errors on load because the frontend `IntegrationType` enum
|
||||||
|
starts at 0, but the backend `IntegrationType` enum starts at 1:
|
||||||
|
|
||||||
|
| Value | Frontend (FE) | Backend (BE) |
|
||||||
|
|-------|---------------|--------------|
|
||||||
|
| 0 | `Registry` | *(invalid)* |
|
||||||
|
| 1 | `Scm` | `Registry` |
|
||||||
|
| 2 | `Ci` | `Scm` |
|
||||||
|
| 3 | `Host` | `CiCd` |
|
||||||
|
| 4 | `Feed` | `RepoSource` |
|
||||||
|
| 5 | `Artifact` | `RuntimeHost`|
|
||||||
|
| 6 | *(none)* | `FeedMirror` |
|
||||||
|
| 7 | *(none)* | `SymbolSource`|
|
||||||
|
| 8 | *(none)* | `Marketplace`|
|
||||||
|
|
||||||
|
Files:
|
||||||
|
- FE: `src/Web/StellaOps.Web/src/app/features/integration-hub/integration.models.ts` (lines 6–13)
|
||||||
|
- BE: `src/Integrations/__Libraries/StellaOps.Integrations.Core/IntegrationEnums.cs` (lines 6–31)
|
||||||
|
|
||||||
|
Fix options (choose one and document):
|
||||||
|
**Option A (Preferred):** Update FE enum to match BE values exactly (1-based, add missing types).
|
||||||
|
**Option B:** Add a mapping adapter in the integration service to translate before sending.
|
||||||
|
|
||||||
|
The fix must also add `SymbolSource` and `Marketplace` types to the FE enum since the BE exposes them.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] FE and BE enum values are aligned.
|
||||||
|
- [ ] `/integrations` page loads with zero console errors.
|
||||||
|
- [ ] All 5 summary cards (Registries, SCM, CI/CD, Hosts, Feeds) display correct counts.
|
||||||
|
- [ ] Unit test added/updated for the integration type mapping.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Fix post-create-release navigation: `/release-orchestrator/releases` → `/releases`
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
After submitting the Create Release wizard, the app navigates to the stale path
|
||||||
|
`/release-orchestrator/releases`. The cause is a hardcoded legacy path in the component.
|
||||||
|
|
||||||
|
File: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/releases/create-release/create-release.component.ts`
|
||||||
|
|
||||||
|
Relevant code (line ~671):
|
||||||
|
```typescript
|
||||||
|
// TODO comment in source: "In a real app, we'd wait for the release to be created first"
|
||||||
|
this.router.navigate(['/release-orchestrator/releases']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Two problems:
|
||||||
|
1. Navigates to stale route; should be `/releases`.
|
||||||
|
2. Navigates synchronously before the async `createRelease()` operation completes — the new
|
||||||
|
release ID is never captured, so it cannot navigate to the detail page.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
```typescript
|
||||||
|
this.store.createRelease({ ... }).subscribe({
|
||||||
|
next: (newRelease) => {
|
||||||
|
this.router.navigate(['/releases', newRelease.id]);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.error.set('Failed to create release');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
(Adjust to match how `store.createRelease()` exposes the result — Observable, Promise, or signal.)
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] After creating a release, browser navigates to `/releases/{newId}` (detail page).
|
||||||
|
- [ ] If navigation to detail is not yet possible, falls back to `/releases` (list) — NOT old path.
|
||||||
|
- [ ] `router.navigate` call happens inside the success callback, not synchronously before it.
|
||||||
|
- [ ] No regression on the Cancel button.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Implement Authority user-management API endpoints (Identity & Access page empty)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Authority BE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
`/settings/admin` shows "No users found" because the Authority service does not expose the admin
|
||||||
|
user-management API endpoints that the frontend calls.
|
||||||
|
|
||||||
|
The frontend component (`admin-settings-page.component.ts`) calls:
|
||||||
|
- `GET /api/admin/users` → `AdminUser[]`
|
||||||
|
- `GET /api/admin/roles` → `AdminRole[]`
|
||||||
|
- `GET /api/admin/clients` → `AdminClient[]`
|
||||||
|
- `GET /api/admin/tokens` → `AdminToken[]`
|
||||||
|
- `GET /api/admin/tenants` → `AdminTenant[]`
|
||||||
|
- `POST /api/admin/users` → create user
|
||||||
|
- `DELETE /api/admin/users/{id}` → delete user
|
||||||
|
|
||||||
|
None of these exist in `src/Authority/StellaOps.Authority/`. The Authority service does have
|
||||||
|
console admin extensions (`ConsoleAdminEndpointExtensions.cs`) but only for branding/console config.
|
||||||
|
|
||||||
|
The standard identity plugin config lives in `etc/authority/plugins/standard.yaml` — this is the
|
||||||
|
data source. The Authority service must expose a read/write API over this data, scoped to the
|
||||||
|
`authority:users.read` and `authority:users.write` scopes (already requested by the UI's OIDC
|
||||||
|
client in the `connect/authorize` scope list).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] `GET /api/admin/users` returns the list of users from the standard identity provider.
|
||||||
|
- [ ] The `admin` bootstrap user appears in the list.
|
||||||
|
- [ ] `POST /api/admin/users` creates a new user.
|
||||||
|
- [ ] Endpoints require `authority:users.read` / `authority:users.write` scope.
|
||||||
|
- [ ] Integration test added covering list + create user.
|
||||||
|
- [ ] `/settings/admin` Users tab shows at minimum the `admin` user without errors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Fix Platform Health "NaNms" latency and "/" services count
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
`/operations/health` displays "NaNms" for P95 avg latency and a bare "/" for the services count.
|
||||||
|
|
||||||
|
**Root cause 1 — NaNms:**
|
||||||
|
File: `src/Web/StellaOps.Web/src/app/features/platform-health/platform-health-dashboard.component.ts` (line ~80)
|
||||||
|
calls `formatLatency(summary()!.averageLatencyMs)`.
|
||||||
|
|
||||||
|
The `formatLatency()` function in `platform-health.models.ts` passes `null` to `Math.round()`:
|
||||||
|
```typescript
|
||||||
|
export function formatLatency(ms: number): string {
|
||||||
|
if (ms < 1) return '<1ms'; // null < 1 is false
|
||||||
|
if (ms >= 1000) return ...; // null >= 1000 is false
|
||||||
|
return `${Math.round(ms)}ms`; // Math.round(null) = 0? No — returns NaN in context
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Fix: add a null/undefined guard at the top:
|
||||||
|
```typescript
|
||||||
|
if (ms == null || isNaN(ms)) return '—';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause 2 — "/" services count:**
|
||||||
|
The services summary stat card is rendering a fraction like "healthy/total" where both values are
|
||||||
|
0 or undefined when no snapshot is available, producing a bare "/". The template needs a
|
||||||
|
zero-state guard:
|
||||||
|
```html
|
||||||
|
<!-- Current (broken): -->
|
||||||
|
{{ summary()?.healthyServices }}/{{ summary()?.totalServices }}
|
||||||
|
|
||||||
|
<!-- Fixed: -->
|
||||||
|
@if (summary()?.totalServices) {
|
||||||
|
{{ summary()!.healthyServices }}/{{ summary()!.totalServices }}
|
||||||
|
} @else {
|
||||||
|
—
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both issues are display-only and do not indicate a backend problem; the backend simply has no
|
||||||
|
service snapshot on a fresh install with unhealthy backend containers.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] `formatLatency(null)` returns `'—'` not `'NaNms'`.
|
||||||
|
- [ ] Services count shows `'—'` or `'0/0'` (not bare `/`) when no snapshot.
|
||||||
|
- [ ] Both fixes covered by unit tests in `platform-health.models.spec.ts`.
|
||||||
|
- [ ] No regression when real service data is present.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Add confirmation dialog to Approve action on Approvals inbox
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
On the Approvals list page (`/approvals`), clicking "Approve" fires the action immediately with
|
||||||
|
no confirmation. The approval detail page (`/approvals/:id`) has a proper Decision sidebar with
|
||||||
|
Approve/Reject buttons, a reason field, and an exception checkbox — the inbox list skips all of
|
||||||
|
this, firing the API call directly in `approveRequest()`.
|
||||||
|
|
||||||
|
File: `src/Web/StellaOps.Web/src/app/features/approvals/approvals-inbox.component.ts`
|
||||||
|
|
||||||
|
Current handler (line ~491):
|
||||||
|
```typescript
|
||||||
|
approveRequest(id: string): void {
|
||||||
|
this.api.approve(id, '').pipe(...).subscribe(() => this.loadApprovals());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The second arg `''` is the reason — it's hardcoded as empty string.
|
||||||
|
|
||||||
|
Fix options:
|
||||||
|
**Option A (preferred):** Route the user to the approval detail page when they click Approve from
|
||||||
|
the list: `this.router.navigate(['/approvals', id])`. This reuses the existing detailed decision
|
||||||
|
flow.
|
||||||
|
**Option B:** Show an inline confirmation snackbar/dialog with a reason input before calling
|
||||||
|
`api.approve()`.
|
||||||
|
|
||||||
|
Either option must ensure the decision reason is captured before the API call fires.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Clicking "Approve" from the inbox list does not fire the API immediately.
|
||||||
|
- [ ] User is prompted for a reason before the action completes.
|
||||||
|
- [ ] Reject action has the same protection.
|
||||||
|
- [ ] Existing approval detail page decision flow unaffected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Fix Promote button on release detail (Angular signal/change-detection bug)
|
||||||
|
|
||||||
|
Status: DONE
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The "Promote" button on `/releases/:id` does nothing when clicked. The template uses
|
||||||
|
`@if (showPromoteDialog)` but `showPromoteDialog` is a plain class property, not an Angular
|
||||||
|
`signal`. In the zoneless/signal-based change detection used by this app, mutating a plain
|
||||||
|
property does not trigger re-render.
|
||||||
|
|
||||||
|
File: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/releases/release-detail/release-detail.component.ts`
|
||||||
|
|
||||||
|
Problem (line ~817):
|
||||||
|
```typescript
|
||||||
|
showPromoteDialog = false; // plain property — NOT a signal
|
||||||
|
```
|
||||||
|
|
||||||
|
Button (line ~59):
|
||||||
|
```typescript
|
||||||
|
(click)="showPromoteDialog = true" // mutation not detected
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
```typescript
|
||||||
|
showPromoteDialog = signal(false);
|
||||||
|
|
||||||
|
// In template:
|
||||||
|
(click)="showPromoteDialog.set(true)"
|
||||||
|
@if (showPromoteDialog()) { ... }
|
||||||
|
(click)="showPromoteDialog.set(false)"
|
||||||
|
```
|
||||||
|
Apply the same fix to any other plain-property `@if` guards in this component
|
||||||
|
(e.g., `showRollbackDialog` if present).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Promote button opens the promotion environment selection dialog.
|
||||||
|
- [ ] Dialog closes on Cancel and on confirm.
|
||||||
|
- [ ] After confirming, `store.requestPromotion()` is called with the correct release ID and target.
|
||||||
|
- [ ] Component test updated to cover dialog open/close behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-08 — Fix incorrect `<title>` tags across Security, Evidence, and Operations pages
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
Note: Settings section page titles are tracked separately in SPRINT_20260219_021 TASK-01.
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Multiple pages either share a wrong title or show the generic "StellaOps" title. Angular's
|
||||||
|
`Title` service must be called with the page-specific string in each route component's `ngOnInit`
|
||||||
|
or via the route `title` property in the route definition.
|
||||||
|
|
||||||
|
Pages with wrong/generic title:
|
||||||
|
|
||||||
|
| Route | Current Title | Expected Title |
|
||||||
|
|-------|--------------|----------------|
|
||||||
|
| `/security/findings` | Security Overview - StellaOps | Security Findings - StellaOps |
|
||||||
|
| `/security/vex` | Security Overview - StellaOps | VEX Hub - StellaOps |
|
||||||
|
| `/security/sbom` | Security Overview - StellaOps | SBOM Graph - StellaOps |
|
||||||
|
| `/security/vulnerabilities` | StellaOps | Vulnerabilities - StellaOps |
|
||||||
|
| `/evidence/proof-chains` | StellaOps | Proof Chains - StellaOps |
|
||||||
|
| `/evidence/replay` | StellaOps | Verdict Replay - StellaOps |
|
||||||
|
| `/evidence/export` | StellaOps | Export Center - StellaOps |
|
||||||
|
| `/operations/orchestrator` | StellaOps | Orchestrator - StellaOps |
|
||||||
|
| `/settings/integrations` | StellaOps | Integrations - StellaOps |
|
||||||
|
| `/settings/admin` | StellaOps | Identity & Access - StellaOps |
|
||||||
|
| `/settings/system` | StellaOps | System - StellaOps |
|
||||||
|
|
||||||
|
Preferred fix: add the `title` property to each route definition in the relevant `*.routes.ts`
|
||||||
|
file (Angular uses this automatically with `TitleStrategy`). This is a one-liner per route, no
|
||||||
|
component changes needed if a `TitleStrategy` is already wired.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Each listed route has a page-specific `<title>`.
|
||||||
|
- [ ] Titles follow the pattern `<Page Name> - StellaOps`.
|
||||||
|
- [ ] No `<title>` regressions on pages that already have correct titles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-09 — Fix Evidence Proof Chains empty-state: show input prompt instead of error
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
`/evidence/proof-chains` shows "Subject digest is required — Retry" immediately on page load,
|
||||||
|
before the user has had a chance to enter a digest. The error state is being triggered on
|
||||||
|
component init with an empty/null input rather than being deferred until a search is attempted.
|
||||||
|
|
||||||
|
Fix: On initial load (when no digest is in URL params or user has not submitted), render the
|
||||||
|
input form in a neutral "search" state, not an error state. Only show "Subject digest is required"
|
||||||
|
after the user submits the form with an empty field.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page loads showing a search input form, not an error message.
|
||||||
|
- [ ] Submitting an empty digest shows the validation error.
|
||||||
|
- [ ] Entering a valid digest and submitting shows the proof chain result (or "not found").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-10 — Document placeholder pages and create tracking items
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer / Product Manager
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Three pages render permanent placeholder messages that indicate unimplemented features:
|
||||||
|
|
||||||
|
1. **SBOM Graph** (`/security/sbom`): "SBOM graph visualization is not yet available in this build."
|
||||||
|
2. **Vulnerabilities** (`/security/vulnerabilities`): "Vulnerability list is pending data integration."
|
||||||
|
3. **Integration Hub Recent Activity** (`/integrations`): "Integration activity timeline coming soon…"
|
||||||
|
|
||||||
|
These are not bugs — they are known gaps — but they should be:
|
||||||
|
a) Given proper empty-state styling (not plain italic text) with a "Coming soon" badge or
|
||||||
|
"Request access" CTA so users understand it's intentional.
|
||||||
|
b) Linked to existing sprint tasks that implement them (if sprints exist) or new sprint tasks
|
||||||
|
created to track implementation.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Each placeholder has a styled empty state (icon + heading + description) rather than raw italic text.
|
||||||
|
- [ ] Sprint tasks exist for implementing each feature; issue IDs linked in the empty-state tooltip or docs.
|
||||||
|
- [ ] No false "error" impression for users — clearly communicates "coming soon" vs "broken".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
|------------|--------|-------|
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA live-run. Root causes confirmed for TASK-01 through TASK-07 via source investigation. See `docs/qa/issues-report-2026-02-19.md`. | QA/Planning |
|
||||||
|
| 2026-02-19 | TASK-01 root cause confirmed: deployment gap (app.routes.ts and v2 child route modules exist in working tree but were not in deployed Docker image). No code change needed — rebuild required. | FE Developer |
|
||||||
|
| 2026-02-19 | TASK-02 DONE: FE IntegrationType enum rewritten to match BE (1-based, all 8 members). integration.models.ts, integration-list.component.ts, integration-hub.component.ts updated. All 4 integration spec files updated to use correct enum member names. | FE Developer |
|
||||||
|
| 2026-02-19 | TASK-03 DONE: create-release.component.ts and release-detail.component.ts updated — all hardcoded `/release-orchestrator/releases` paths changed to `/releases`. Breadcrumb simplified to 2 levels. | FE Developer |
|
||||||
|
| 2026-02-19 | TASK-05 DONE: formatLatency() null/undefined guard added (platform-health.models.ts). Services count display guarded with @if totalServices != null (platform-health-dashboard.component.ts). | FE Developer |
|
||||||
|
| 2026-02-19 | TASK-06 DONE: approvals-inbox.component.ts — approveRequest() and rejectRequest() now route to /approvals/:id detail page instead of firing API with empty reason string. | FE Developer |
|
||||||
|
| 2026-02-19 | TASK-07 DONE: release-detail.component.ts — showPromoteDialog, showDeployDialog, showRollbackDialog, showEditDialog, showAddComponent all converted from plain boolean properties to WritableSignal<boolean>. Template and method bindings updated throughout. | FE Developer |
|
||||||
|
| 2026-02-19 | Second QA Playwright sweep completed (all nav sections: Operations, Analytics, Evidence, Settings, user menu, status bar links). 18 additional issues found and grouped into sprints 018–021. TASK-08 scope cross-referenced with SPRINT_20260219_021 (Settings titles). | QA |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **TASK-01 guard investigation**: If `requireBackendsReachableGuard` is the culprit, the fix must
|
||||||
|
not weaken security — consider adding a grace period or retry rather than disabling the guard.
|
||||||
|
- **TASK-02 enum fix**: Changing the FE enum is a breaking change if any other component
|
||||||
|
serializes/deserializes by integer value. Audit all usages of `IntegrationType` in the FE before
|
||||||
|
changing values. The BE enum must remain the source of truth.
|
||||||
|
- **TASK-04 Authority endpoints**: Scope to read-only first (`listUsers`) to unblock the UI;
|
||||||
|
create/delete can follow in a separate sprint once audit logging is confirmed.
|
||||||
|
- **TASK-07 signal fix**: Review the entire `release-detail.component.ts` for other plain-property
|
||||||
|
`@if` guards — there may be a `showRollbackDialog` with the same issue.
|
||||||
|
- Docs to update after fixes land:
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_nav_rendering_policy.md` (TASK-01 outcome)
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md` (TASK-03 old route removal)
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01, TASK-03, TASK-05 are small/isolated — good for a single developer pass.
|
||||||
|
- TASK-02 requires coordination between FE and Integrations BE teams — schedule before end of sprint.
|
||||||
|
- TASK-04 (Authority) is a backend sprint; estimate separately before committing deadline.
|
||||||
|
- TASK-06 and TASK-07 are UX-critical blockers for the approval and promotion flows — prioritize above TASK-08/09/10.
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
# Sprint 20260219-018 — QA UX Polish: VEX Hub, Approvals, Naming Consistency
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA live-run follow-up for issues observed on 2026-02-19 Playwright sweep.
|
||||||
|
- Covers: VEX Hub dark-theme mismatch, duplicate breadcrumb, approval detail missing reason
|
||||||
|
field, dead Docs link on approvals, evidence nav naming mismatch, proof-chain heading
|
||||||
|
mismatch, approvals badge count vs list count mismatch.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/`.
|
||||||
|
- Expected evidence: visual regression tests pass, no dark-theme inconsistencies, all
|
||||||
|
breadcrumbs and nav labels aligned, approvals badge count matches list count.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Complements SPRINT_20260219_017 (bug fixes round 1); tasks here are independent.
|
||||||
|
- No backend changes required for any task in this sprint.
|
||||||
|
- TASK-05 (badge count mismatch) may share code with approvals inbox component touched in
|
||||||
|
Sprint 017 TASK-06; coordinate to avoid conflicts.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_nav_rendering_policy.md` — nav label rules.
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md` — route naming.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/approvals/` — approvals components.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/` — VEX Hub component.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/evidence-audit/` — evidence components.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Fix VEX Hub dark-theme inconsistency
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The VEX Hub page (`/security/vex`) uses dark background CSS variables (dark sidebar, dark cards)
|
||||||
|
while the rest of the application uses a light cream/warm white theme. The component loads its
|
||||||
|
own dark-mode styles unconditionally, making it visually jarring and out of place.
|
||||||
|
|
||||||
|
Locate the VEX Hub component stylesheet
|
||||||
|
(`src/Web/StellaOps.Web/src/app/features/security-risk/vex-hub/` or equivalent path).
|
||||||
|
Remove or reclassify any hardcoded dark-mode CSS variables so the component inherits the
|
||||||
|
application's global light theme tokens.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] VEX Hub page visually matches the light theme of all other pages (no dark backgrounds)
|
||||||
|
- [ ] No CSS variables from a dark theme palette referenced unconditionally in the component
|
||||||
|
- [ ] Unit test or visual spot-check screenshot confirms consistency
|
||||||
|
- [ ] No regressions to other security-risk sub-pages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix VEX Hub duplicate breadcrumb ("VEX Hub > VEX Hub")
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The VEX Hub page breadcrumb displays "VEX Hub > VEX Hub" — the section name and the page name
|
||||||
|
are identical. The breadcrumb should show "Security > VEX Hub" (or just omit the parent
|
||||||
|
segment if the nav group name suffices).
|
||||||
|
|
||||||
|
Locate the breadcrumb configuration in the VEX Hub component or its route definition and fix
|
||||||
|
the parent label so it correctly reflects the Security section.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Breadcrumb on `/security/vex` reads "Security > VEX Hub" (or equivalent correct hierarchy)
|
||||||
|
- [ ] No other security sub-pages affected
|
||||||
|
- [ ] Existing breadcrumb tests pass or are updated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Add reason/comment field to Approval Detail decision panel
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Approval Detail page (`/approvals/:id`) has an Approve and Reject button in the Decision
|
||||||
|
panel, but there is no input for a reason or comment. Approving or rejecting without a reason
|
||||||
|
is poor UX and may break audit requirements.
|
||||||
|
|
||||||
|
Add a required `reason` textarea to the Decision panel:
|
||||||
|
- Placed above the Approve/Reject buttons
|
||||||
|
- Label: "Decision Reason"
|
||||||
|
- Placeholder: "Enter your reason for this decision..."
|
||||||
|
- Required validation: both Approve and Reject must be disabled until reason has at least
|
||||||
|
10 characters
|
||||||
|
- Pass the reason value to the approval/rejection API call
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Decision panel has a labeled reason textarea
|
||||||
|
- [ ] Approve and Reject buttons disabled until reason is >= 10 chars
|
||||||
|
- [ ] Reason is passed to `api.approve(id, reason)` and `api.reject(id, reason)`
|
||||||
|
- [ ] Unit test covers both enabled and disabled button states based on reason length
|
||||||
|
- [ ] No regression to approval list page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Fix dead "Docs →" link on Approvals page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Approvals inbox page (`/approvals`) has a "Docs →" link that navigates to `/docs`, which
|
||||||
|
is a non-existent route. In the deployed app this silently redirects to `/` or shows a blank
|
||||||
|
page.
|
||||||
|
|
||||||
|
Options (in order of preference):
|
||||||
|
1. Remove the Docs link if no documentation route is planned.
|
||||||
|
2. Point to a valid internal or external documentation anchor if one exists.
|
||||||
|
3. If a `/docs` route is planned but not yet implemented, disable the link with a tooltip
|
||||||
|
"Documentation coming soon".
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "Docs →" link does not navigate to a 404/blank route
|
||||||
|
- [ ] If removed, no visual gap in the approvals page layout
|
||||||
|
- [ ] Unit test confirms the link is either absent or has a valid href
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Fix Approvals inbox badge count vs list count mismatch
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The sidebar nav badge on Approvals shows "3" (pending), but the Approvals list page shows
|
||||||
|
"Results (2)". The two counts come from different sources and are out of sync.
|
||||||
|
|
||||||
|
Investigate whether:
|
||||||
|
a) The badge fetches from a different API endpoint than the list, or
|
||||||
|
b) The list applies a filter that excludes one item (e.g. status filter excludes "In Review"),
|
||||||
|
or
|
||||||
|
c) One of the counts includes/excludes the current user's own approvals.
|
||||||
|
|
||||||
|
Fix so both counts reflect the same logical set of pending approvals visible to the user.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Nav badge count matches the "Results (N)" count on the approvals list page
|
||||||
|
- [ ] Root cause documented in the sprint Decisions & Risks section
|
||||||
|
- [ ] Unit test covers badge count derivation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Fix Evidence nav "Packets" vs page heading "Bundles" naming mismatch
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Evidence sub-navigation label reads "Packets" (route: `/evidence`), but the page heading
|
||||||
|
says "Evidence Bundles". The inconsistency confuses users navigating to the evidence section.
|
||||||
|
|
||||||
|
Decide on canonical name: the sprint documentation uses "Packets" (see
|
||||||
|
`docs/modules/ui/v2-rewire/S00_nav_rendering_policy.md`). If "Packets" is canonical:
|
||||||
|
- Update the page heading from "Evidence Bundles" to "Evidence Packets" (or "Packets")
|
||||||
|
- Update the `<title>` to "Evidence Packets - StellaOps"
|
||||||
|
If "Bundles" is canonical, update the nav label instead.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Nav label and page heading use the same term
|
||||||
|
- [ ] `<title>` reflects the canonical name
|
||||||
|
- [ ] Any internal links or breadcrumbs updated consistently
|
||||||
|
- [ ] Unit test updated to match new heading text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Fix Proof Chains page heading "Evidence Chain" vs nav "Proof Chains"
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Proof Chains nav item (`/evidence/proof-chains`) reads "Proof Chains" in the sidebar, but
|
||||||
|
the page heading says "Evidence Chain". Standardise to "Proof Chains".
|
||||||
|
|
||||||
|
Update the component heading from "Evidence Chain" to "Proof Chains" and ensure the
|
||||||
|
`<title>` reads "Proof Chains - StellaOps".
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page heading reads "Proof Chains"
|
||||||
|
- [ ] `<title>` reads "Proof Chains - StellaOps"
|
||||||
|
- [ ] Unit test updated for heading text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA sweep (session 2). Issues observed live on deployed instance. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **VEX Hub dark theme**: Root cause is likely a stray `[data-theme="dark"]` or hardcoded CSS
|
||||||
|
custom properties in the component. Check for `var(--color-bg-dark)` or similar before
|
||||||
|
assuming a global theming bug.
|
||||||
|
- **Reason field**: API contracts for `/approvals/:id/approve` and `/reject` should already
|
||||||
|
accept a reason body; confirm with backend spec before adding the field.
|
||||||
|
- **Badge vs list count**: Most likely explanation is the badge queries total pending approvals
|
||||||
|
in the system while the list is filtered to "assigned to me". Both behaviours may be
|
||||||
|
intentional — decision needed on which scope to use.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- FE dev to complete TASK-01 through TASK-07 before next QA verification session.
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
# Sprint 20260219-019 — QA: Operations Section — Icon Rendering, Route Prefix, Permissions
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA live-run follow-up for issues observed in the Operations section on 2026-02-19.
|
||||||
|
- Covers: icon names rendering as literal text (Quotas, Dead Letter), Scheduler sub-pages
|
||||||
|
breaking out of `/operations/` route tree, and admin user incorrectly denied Orchestrator
|
||||||
|
permissions.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/`.
|
||||||
|
- Expected evidence: buttons show icons, Scheduler detail routes stay within `/operations/`,
|
||||||
|
admin user sees full Orchestrator access.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- TASK-01 (icon rendering) and TASK-02 (route prefix) are independent and can run in parallel.
|
||||||
|
- TASK-03 (orchestrator permissions) requires reading the permission/role resolution service.
|
||||||
|
- No backend changes expected — all three issues are FE-side logic.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/platform-ops/` — Operations feature components.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/platform-ops.routes.ts` — Operations route config.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/platform-ops/orchestrator/` — Orchestrator component.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Fix icon names rendering as literal text in Operations buttons
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Multiple buttons in the Operations section render icon names as visible text instead of
|
||||||
|
rendering the icon glyphs:
|
||||||
|
|
||||||
|
- **Quotas page** (`/operations/quotas`):
|
||||||
|
- "bell Configure Alerts" (icon name "bell" visible)
|
||||||
|
- "download Export Report" (icon name "download" visible)
|
||||||
|
- standalone "refresh" button (icon name "refresh" visible)
|
||||||
|
|
||||||
|
- **Dead Letter page** (`/operations/dead-letter`):
|
||||||
|
- "download Export CSV" (icon name "download" visible)
|
||||||
|
- "refresh Replay All Retryable (0)" (icon name "refresh" visible)
|
||||||
|
- standalone "refresh" button (icon name "refresh" visible)
|
||||||
|
|
||||||
|
Root cause: The icon component likely renders the icon name as a `<span>` or text node inside
|
||||||
|
the button template, possibly because the icon library is not initialised or the icon names
|
||||||
|
passed are string literals rather than template expressions.
|
||||||
|
|
||||||
|
Locate the Quotas and Dead Letter components and their icon usage. Fix the icon rendering so
|
||||||
|
button labels show only the icon glyph + text label (e.g. "🔔 Configure Alerts", not
|
||||||
|
"bell Configure Alerts"). If using a Lucide/Hero/Material icon component, ensure it is
|
||||||
|
properly imported and the icon name is resolved as a component input, not raw text.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] No button in Quotas or Dead Letter renders a visible icon name string
|
||||||
|
- [ ] All affected buttons show the correct icon glyph
|
||||||
|
- [ ] Unit tests confirm button accessible names match expected text (without icon name prefix)
|
||||||
|
- [ ] No other Operations pages regress
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix Scheduler sub-page route prefix inconsistency
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
From the Scheduler Runs page (`/operations/scheduler/runs`), two action buttons navigate
|
||||||
|
outside the `/operations/` route subtree:
|
||||||
|
|
||||||
|
- "Manage Schedules" → `/scheduler/schedules` (should be `/operations/scheduler/schedules`)
|
||||||
|
- "Worker Fleet" → `/scheduler/workers` (should be `/operations/scheduler/workers`)
|
||||||
|
|
||||||
|
The "Back to Runs" links on those pages also point to `/scheduler/runs` instead of
|
||||||
|
`/operations/scheduler/runs`.
|
||||||
|
|
||||||
|
This breaks the sidebar highlight (Scheduler item loses active state) and the breadcrumb
|
||||||
|
hierarchy.
|
||||||
|
|
||||||
|
Fix the navigation targets in the Scheduler Runs component so detail views stay under
|
||||||
|
`/operations/scheduler/`. Update the route definitions in
|
||||||
|
`src/Web/StellaOps.Web/src/app/routes/platform-ops.routes.ts` (or the operations routes file)
|
||||||
|
to include child routes for `schedules` and `workers` under the `operations/scheduler` prefix.
|
||||||
|
Update back-navigation links in the Schedule Management and Worker Fleet components.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "Manage Schedules" navigates to `/operations/scheduler/schedules`
|
||||||
|
- [ ] "Worker Fleet" navigates to `/operations/scheduler/workers`
|
||||||
|
- [ ] "Back to Runs" on both pages links to `/operations/scheduler/runs`
|
||||||
|
- [ ] Sidebar Scheduler item remains active/highlighted while on those sub-pages
|
||||||
|
- [ ] Breadcrumb shows correct hierarchy (Operations > Scheduler > Schedule Management, etc.)
|
||||||
|
- [ ] Unit tests updated for navigation targets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Fix Orchestrator permissions: admin user denied Operate/Quotas/Backfill
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Orchestrator Dashboard (`/operations/orchestrator`) displays permission checks for the
|
||||||
|
current user:
|
||||||
|
|
||||||
|
```
|
||||||
|
View Jobs: Granted ✓
|
||||||
|
Operate: Denied ✗ ← BUG for admin
|
||||||
|
Manage Quotas: Denied ✗ ← BUG for admin
|
||||||
|
Initiate Backfill: Denied ✗ ← BUG for admin
|
||||||
|
```
|
||||||
|
|
||||||
|
The admin user should have all permissions. This suggests the permission check is evaluating
|
||||||
|
specific scopes (e.g. `orchestrator:operate`, `orchestrator:manage-quotas`) rather than
|
||||||
|
checking the `admin` role which should implicitly grant all scopes.
|
||||||
|
|
||||||
|
Locate the Orchestrator Dashboard component's permission resolution logic. It likely calls
|
||||||
|
`authService.hasScope('orchestrator:operate')` or similar. Fix it so that:
|
||||||
|
1. `admin` role (or equivalent `admin` scope) grants all permissions, OR
|
||||||
|
2. The admin's token includes the required scopes and the check reads them correctly.
|
||||||
|
|
||||||
|
If the issue is the token lacks the scopes for the test admin user (Authority config), note
|
||||||
|
it in Decisions & Risks as a config gap rather than a code bug and add a fallback that
|
||||||
|
checks for the admin role.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Admin user sees all four permissions as "Granted" on Orchestrator Dashboard
|
||||||
|
- [ ] Non-admin user (Viewer role) still sees correct restrictions
|
||||||
|
- [ ] Unit test for the permission check covers admin role case
|
||||||
|
- [ ] Root cause (scope vs role check) documented in Decisions & Risks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA sweep (session 2), Operations section walkthrough. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Icon rendering**: Check if icon library (e.g. `ng-lucide`, `@angular/material/icon`,
|
||||||
|
or a custom icon component) requires `forRoot()` registration or module import in the
|
||||||
|
Operations feature module. The bug may affect more pages than Quotas/Dead Letter.
|
||||||
|
- **Route prefix**: Adding child routes under `operations/scheduler` may require lazy-loaded
|
||||||
|
module refactoring if Scheduler is currently a flat route. Check `platform-ops.routes.ts`
|
||||||
|
before creating new nested route configs.
|
||||||
|
- **Orchestrator permissions**: If admin token doesn't include `orchestrator:*` scopes, this
|
||||||
|
is partly an Authority config issue. FE fix should be to treat `admin` role as having all
|
||||||
|
scopes as a fallback. Backend Authority config fix may be in a separate sprint.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- FE dev to complete all three tasks before next Operations QA pass.
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# Sprint 20260219-020 — QA: Profile Page Dev Content Exposure & Identity/Access Bugs
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA live-run follow-up for critical UX/security issues in user-facing identity flows.
|
||||||
|
- Covers: Profile page exposing developer debug content, UUID email display, and
|
||||||
|
Settings > Identity & Access showing no users despite admin being logged in.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/` (FE) and potentially `src/Authority/` for TASK-03.
|
||||||
|
- Expected evidence: Profile shows real user info, no developer content visible to end users,
|
||||||
|
Identity & Access loads the admin user in its list.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- TASK-01 and TASK-02 are FE-only and independent.
|
||||||
|
- TASK-03 (users list) overlaps with SPRINT_20260219_017 TASK-04 (Authority user endpoints);
|
||||||
|
coordinate before starting.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/administration/` — Settings admin components.
|
||||||
|
- Authority service API contracts for `/api/v1/users` (or equivalent).
|
||||||
|
- `docs/modules/platform/architecture-overview.md` — Auth/Authority module overview.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Replace dev-debug Profile page with real user profile
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The user Profile page (`/console/profile`, linked from the user menu "Profile" option) is a
|
||||||
|
developer debug panel, not a real profile page. It currently shows:
|
||||||
|
|
||||||
|
1. **"Console Session" heading** (not "Profile")
|
||||||
|
2. **Policy Studio roles & scopes documentation** — a reference list of roles and their
|
||||||
|
scopes, presumably for developers to understand token coverage
|
||||||
|
3. **References to test fixtures**: "For e2e, load stub sessions from
|
||||||
|
`testing/auth-fixtures.ts` ... and seed `AuthSessionStore` before navigating."
|
||||||
|
4. **"No console session data available for the current identity."** — no actual user data
|
||||||
|
|
||||||
|
This page must NOT be shown to end users. It exposes internal development guidance and
|
||||||
|
test infrastructure details.
|
||||||
|
|
||||||
|
Fix by replacing the page content with a real user profile view:
|
||||||
|
- Display: username, display name, email (if available), role
|
||||||
|
- Show account settings (change password link, preferences)
|
||||||
|
- Remove all developer documentation content
|
||||||
|
- If the debug session viewer is needed for development, gate it behind a dev-mode flag or
|
||||||
|
move it to a `/dev/console-session` route that is only registered in development builds
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] `/console/profile` shows the logged-in user's name, role, and basic profile info
|
||||||
|
- [ ] No developer documentation, test fixture references, or internal code references shown
|
||||||
|
- [ ] Page heading reads "Profile" (matching the menu item label)
|
||||||
|
- [ ] Title reads "Profile - StellaOps"
|
||||||
|
- [ ] Debug/console session content moved to a dev-only route or removed
|
||||||
|
- [ ] Unit test covers that profile fields are rendered from user session data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix admin user email displayed as UUID hash in user menu
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The user menu dropdown (top-right of header) shows the admin user's email as:
|
||||||
|
`847f3baef22342d292fa369840e5975e@unknown.local`
|
||||||
|
|
||||||
|
This is a UUID-derived fallback email, not a real address. It appears because the admin
|
||||||
|
user (configured in `standard.yaml` Authority plugin) does not have a real email set.
|
||||||
|
|
||||||
|
Fix options (in order of preference):
|
||||||
|
1. In the user menu component, if the email matches the `@unknown.local` domain pattern or
|
||||||
|
is a generated UUID email, show "No email configured" or omit the email line entirely.
|
||||||
|
2. Alternatively, update the Authority admin user seed to include a real email
|
||||||
|
(`admin@stella-ops.local`), which would also fix the Identity & Access page.
|
||||||
|
|
||||||
|
If option 2 is chosen, update `/app/etc/authority/plugins/standard.yaml` or equivalent
|
||||||
|
Authority config file, and document the change.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] User menu does not display a UUID hash as the email address
|
||||||
|
- [ ] Fallback display is either "No email configured" or a sensible placeholder
|
||||||
|
- [ ] Unit test for the user menu email display covers the UUID email edge case
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Fix Identity & Access users list showing "No users found"
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: SPRINT_20260219_017 TASK-04 (Authority user endpoints — may provide the API)
|
||||||
|
Owners: FE Developer / Backend Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Settings > Identity & Access (`/settings/admin`) shows an empty Users table with
|
||||||
|
"No users found" even though the admin user is actively logged in.
|
||||||
|
|
||||||
|
The page fetches users from an API endpoint (likely `GET /api/v1/users` on the Authority
|
||||||
|
service). The failure is either:
|
||||||
|
|
||||||
|
a) **Backend not implemented**: The Authority service user management endpoints were flagged
|
||||||
|
as missing in SPRINT_20260219_017 TASK-04. If the endpoint doesn't exist, the FE gets
|
||||||
|
a 404 which results in an empty list.
|
||||||
|
b) **FE silently swallows errors**: The component's error handler may display "No users found"
|
||||||
|
for both an empty list AND a failed API call.
|
||||||
|
c) **URL misconfigured**: The FE may be calling a wrong base URL or path.
|
||||||
|
|
||||||
|
Fix both layers:
|
||||||
|
- FE: Distinguish "empty list" from "load error" — show a specific error state when the API
|
||||||
|
call fails (e.g. "Unable to load users. Check Authority service connectivity.")
|
||||||
|
- Backend (if TASK-04 not yet complete): Implement the `GET /api/v1/users` endpoint that
|
||||||
|
returns the list of configured users from the Authority plugin
|
||||||
|
- Ensure at minimum the admin user appears in the returned list
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Users list loads and shows at minimum the admin user
|
||||||
|
- [ ] Error state is shown if the API call fails (not silently shown as "No users found")
|
||||||
|
- [ ] Unit test distinguishes empty list from error state
|
||||||
|
- [ ] Backend endpoint returns user list (or TASK-04 tracks this if it's the blocking item)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA sweep (session 2). Profile page dev exposure is high priority. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Profile dev content**: The console session debug panel should be gated at build time
|
||||||
|
(Angular environment flag) rather than at runtime to prevent accidental exposure in
|
||||||
|
production. Check if `environment.production` is already used elsewhere.
|
||||||
|
- **UUID email**: The Authority `standard.yaml` creates the admin user without a real email.
|
||||||
|
If we update the config, existing deployments may need to reseed. Treat as a one-way
|
||||||
|
migration or add a migration note in the runbook.
|
||||||
|
- **Users list dependency on TASK-04**: If Sprint 017 TASK-04 is blocked, mark this TASK-03
|
||||||
|
as BLOCKED and coordinate with the Authority backend sprint.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 is critical — dev content exposure should be fixed in the next development cycle.
|
||||||
|
- TASK-03 depends on TASK-04 progress in Sprint 017.
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
# Sprint 20260219-021 — QA: Settings Section — Page Titles, Offline Nav Entry, Stub Pages
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA live-run follow-up for issues in the Settings section (2026-02-19 sweep).
|
||||||
|
- Covers: all Settings sub-pages showing generic "Settings - StellaOps" title, the
|
||||||
|
`/settings/offline` page being absent from the Settings sidebar nav, and the
|
||||||
|
Integration Detail page being a stub with no data.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/`.
|
||||||
|
- Expected evidence: every Settings page has a specific `<title>`, Offline Settings appears
|
||||||
|
in the sidebar, Integration Detail renders integration name and tab content.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Extends SPRINT_20260219_017 TASK-08 (page title fixes); that task covers Security,
|
||||||
|
Operations, and Evidence sections — this sprint covers the Settings section.
|
||||||
|
- TASK-01 (page titles) and TASK-02 (offline nav) can run in parallel.
|
||||||
|
- TASK-03 (integration detail stub) may depend on having a real API for integration data;
|
||||||
|
if backend is not ready, the stub can be improved with better empty state messaging.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/` — route files to find where `title` is set.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/layout/app-sidebar/` — sidebar nav component.
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/administration/` — Settings feature area.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Fix all Settings sub-pages to use specific page titles
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Every Settings sub-page currently shows "Settings - StellaOps" as the browser tab title.
|
||||||
|
Each page needs a specific title matching its heading.
|
||||||
|
|
||||||
|
Affected pages and required titles:
|
||||||
|
|
||||||
|
| Route | Current Title | Required Title |
|
||||||
|
|---|---|---|
|
||||||
|
| `/settings/integrations` | Settings - StellaOps | Integrations - StellaOps |
|
||||||
|
| `/settings/release-control` | Settings - StellaOps | Release Control - StellaOps |
|
||||||
|
| `/settings/trust` | Settings - StellaOps | Trust & Signing - StellaOps |
|
||||||
|
| `/settings/security-data` | Settings - StellaOps | Security Data - StellaOps |
|
||||||
|
| `/settings/admin` | Settings - StellaOps | Identity & Access - StellaOps |
|
||||||
|
| `/settings/branding` | Settings - StellaOps | Tenant / Branding - StellaOps |
|
||||||
|
| `/settings/usage` | Settings - StellaOps | Usage & Limits - StellaOps |
|
||||||
|
| `/settings/notifications` | Settings - StellaOps | Notifications - StellaOps |
|
||||||
|
| `/settings/policy` | Settings - StellaOps | Policy Governance - StellaOps |
|
||||||
|
| `/settings/system` | Settings - StellaOps | System - StellaOps |
|
||||||
|
| `/settings/offline` | Settings - StellaOps | Offline Settings - StellaOps |
|
||||||
|
| `/settings/integrations/:id` | Settings - StellaOps | Integration Detail - StellaOps (or `{name} - StellaOps` once data loads) |
|
||||||
|
|
||||||
|
Add `title` properties to each route definition in the Settings route configuration file.
|
||||||
|
Angular router's `title` strategy should be used consistently (same pattern as existing
|
||||||
|
routes that already have titles like `/operations/feeds`).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 12 routes listed above have specific `<title>` values
|
||||||
|
- [ ] Titles follow the "{Page Name} - StellaOps" pattern
|
||||||
|
- [ ] Unit test for the router confirms title is set per route (or smoke test via Playwright)
|
||||||
|
- [ ] No other route titles regressed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Add Offline Settings to the Settings sidebar navigation
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Offline Settings page (`/settings/offline`) exists and works but is NOT listed in the
|
||||||
|
Settings sidebar submenu. It is only reachable by clicking the "Offline: OK" status bar
|
||||||
|
indicator in the header — users who haven't noticed that indicator won't find the page.
|
||||||
|
|
||||||
|
Add "Offline Settings" (or "Offline") as an entry to the Settings submenu in the sidebar
|
||||||
|
navigation component, between "System" and the end of the list (or in a logical position
|
||||||
|
consistent with other settings entries).
|
||||||
|
|
||||||
|
Use the same icon style as other Settings items (e.g. a wifi-off or download-cloud icon).
|
||||||
|
Route: `/settings/offline`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "Offline" (or "Offline Settings") appears in the Settings sidebar submenu
|
||||||
|
- [ ] Clicking it navigates to `/settings/offline`
|
||||||
|
- [ ] The nav item is highlighted when on `/settings/offline`
|
||||||
|
- [ ] Sidebar nav unit test updated to include the new item
|
||||||
|
- [ ] The "Offline: OK" status bar link still works as a secondary entry point
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Fix Integration Detail page: show integration name and populate tabs
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none (but depends on Settings > Integrations API returning integration data)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Navigating to a specific integration detail (`/settings/integrations/:id`, e.g.
|
||||||
|
`/settings/integrations/jenkins-1`) shows a completely stub page:
|
||||||
|
|
||||||
|
- Heading: "Integration Detail" (generic, not the integration name e.g. "Jenkins")
|
||||||
|
- Subtitle: "Integration ID: jenkins-1" (raw ID shown)
|
||||||
|
- Breadcrumb: "Settings > Integration Detail" (should be "Settings > Integrations > Jenkins")
|
||||||
|
- All tabs (Overview, Health, Activity, Permissions, Secrets, Webhooks) show no content
|
||||||
|
|
||||||
|
Fix the Integration Detail component to:
|
||||||
|
1. Load the integration record by ID from the API (the list page already fetches
|
||||||
|
`/settings/integrations` and returns integration objects with name, type, status)
|
||||||
|
2. Display the integration name as the page heading once loaded
|
||||||
|
3. Show a loading spinner while data is fetched, and an error state if fetch fails
|
||||||
|
4. Breadcrumb: "Settings > Integrations > {Integration Name}"
|
||||||
|
5. Title: "{Integration Name} — StellaOps"
|
||||||
|
6. Populate at minimum the Overview tab with: name, type, provider, status, last sync time,
|
||||||
|
description
|
||||||
|
|
||||||
|
For tabs with no backend data yet (Health, Activity, Secrets, Webhooks, Permissions),
|
||||||
|
render a proper "Not yet available" empty state instead of a blank tab body.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Integration name displayed in heading and breadcrumb
|
||||||
|
- [ ] Overview tab shows integration name, type, status, last sync time
|
||||||
|
- [ ] Tabs without data show a "Not yet available" placeholder (not a blank white area)
|
||||||
|
- [ ] Loading and error states implemented
|
||||||
|
- [ ] Unit test for the component covers data-loading and name display
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Fix blank Settings pages (integrations, policy, system, usage, offline)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
QA sweep (2026-02-19) confirmed that the following five Settings sub-pages render a completely
|
||||||
|
empty `<main>` — no heading, no content, no error state. They are not just missing titles
|
||||||
|
(TASK-01), they are entirely blank:
|
||||||
|
|
||||||
|
| Route | Observed title | Blank? |
|
||||||
|
|---|---|---|
|
||||||
|
| `/settings/integrations` | "Stella Ops" | YES — main is empty |
|
||||||
|
| `/settings/policy` | "Settings - StellaOps" | YES — main is empty |
|
||||||
|
| `/settings/system` | "Settings - StellaOps" | YES — main is empty |
|
||||||
|
| `/settings/usage` | "Settings - StellaOps" | YES — main is empty |
|
||||||
|
| `/settings/offline` | "Settings - StellaOps" | YES — main is empty |
|
||||||
|
|
||||||
|
Contrast: `/settings/release-control`, `/settings/trust`, `/settings/security-data`,
|
||||||
|
`/settings/admin`, `/settings/branding`, `/settings/notifications` all render with content.
|
||||||
|
|
||||||
|
Root cause investigation required for each blank page:
|
||||||
|
1. Check if the component is registered and the route is correctly mapped to the component
|
||||||
|
2. Check for lazy-loading chunk failures (browser console for import errors)
|
||||||
|
3. Check if the component requires an auth/permission guard that is blocking the render
|
||||||
|
4. Check if the component constructor or ngOnInit has an unhandled error preventing render
|
||||||
|
|
||||||
|
Note: `/settings/integrations` showing title "Stella Ops" (raw, no suffix) vs other blank
|
||||||
|
pages showing "Settings - StellaOps" suggests `/settings/integrations` may have a completely
|
||||||
|
different (missing) route registration.
|
||||||
|
|
||||||
|
Note: `/settings/offline` is reachable via the "Offline: OK" status bar link — users who
|
||||||
|
click that indicator land on a blank page. This is a critical UX regression.
|
||||||
|
|
||||||
|
Also note: `/settings/policy` is reachable via the "Policy:" status bar link — same issue.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 5 pages render content (at minimum a heading and description, even if feature
|
||||||
|
content is stub/empty state)
|
||||||
|
- [ ] `/settings/integrations` shows the integrations list (or a meaningful empty state)
|
||||||
|
- [ ] `/settings/policy` shows Policy Governance content
|
||||||
|
- [ ] `/settings/system` shows System settings content
|
||||||
|
- [ ] `/settings/usage` shows Usage & Limits content
|
||||||
|
- [ ] `/settings/offline` shows Offline Settings content
|
||||||
|
- [ ] "Offline: OK" and "Policy:" status bar links lead to non-blank pages
|
||||||
|
- [ ] No console errors on load for any of the 5 pages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Fix Settings > Branding breadcrumb / heading label mismatch
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
On `/settings/branding`, the breadcrumb shows "Branding" but the page heading reads
|
||||||
|
"Tenant / Branding". These should match. Per pack-21 the canonical name is "Tenant & Branding"
|
||||||
|
(or "Tenant / Branding").
|
||||||
|
|
||||||
|
Fix: Update the breadcrumb `data.breadcrumb` in the route definition to match the heading.
|
||||||
|
Also ensure the nav sidebar item label matches — nav currently says "Tenant / Branding".
|
||||||
|
Target consistent label: "Tenant & Branding" (use & not /).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Breadcrumb shows the same label as the page heading
|
||||||
|
- [ ] Nav item, breadcrumb, and heading all use the same label
|
||||||
|
- [ ] Title also updated (cross-reference TASK-01)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Fix Settings > Release Control sub-action buttons (non-functional)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
On `/settings/release-control`, the four action buttons ("Manage Environments", "Manage
|
||||||
|
Targets", "Manage Agents", "Edit Workflows") do not navigate anywhere — clicking them has
|
||||||
|
no effect (URL stays at `/settings/release-control`, no modal opens, no route change).
|
||||||
|
|
||||||
|
Per pack-21, Release Control Setup should have sub-pages for each of these areas. For this
|
||||||
|
sprint: at minimum each button should navigate to a sub-route or open a meaningful modal/
|
||||||
|
panel. Stub sub-routes are acceptable.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
1. Create sub-routes: `/settings/release-control/environments`, `/settings/release-control/
|
||||||
|
targets`, `/settings/release-control/agents`, `/settings/release-control/workflows`
|
||||||
|
2. Or convert to anchor links that expand inline sections on the same page
|
||||||
|
|
||||||
|
Per SPRINT_20260219_028 TASK-01 and SPRINT_20260219_029 TASK-03, these will eventually
|
||||||
|
migrate to `/release-control/setup/environments` etc. For now, stubs under the current
|
||||||
|
path are sufficient so buttons are not dead.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Each button either navigates to a sub-route or opens a functional inline section
|
||||||
|
- [ ] No button click produces no visible response
|
||||||
|
- [ ] If sub-routes are used, breadcrumbs are correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Fix Offline Settings Bundle Freshness dark card theme inconsistency
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01 (lower priority, can wait for the title sprint to land)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Offline Settings page (`/settings/offline`) has a "Bundle Freshness" visualisation card
|
||||||
|
that uses a dark grey background (`#3a3a3a` or similar) inconsistent with the rest of the
|
||||||
|
light-themed page. This is the same class of issue as the VEX Hub dark theme (Sprint 018
|
||||||
|
TASK-01).
|
||||||
|
|
||||||
|
Locate the Bundle Freshness component/widget and restyle it to use the application's
|
||||||
|
light theme tokens. The card should use a bordered white or off-white card style consistent
|
||||||
|
with other data panels on the page.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Bundle Freshness card uses the application's light theme palette
|
||||||
|
- [ ] No standalone dark-mode CSS variables used unconditionally
|
||||||
|
- [ ] Visual spot-check confirms consistency with surrounding content
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA sweep (session 2), Settings section walkthrough. | QA |
|
||||||
|
| 2026-02-19 | Full Settings section re-sweep. Added TASK-05 (5 blank pages: integrations, policy, system, usage, offline), TASK-06 (branding label mismatch), TASK-07 (release-control sub-action buttons non-functional). Confirmed offline + policy status bar links lead to blank pages. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Page titles**: The Angular router `title` property is likely already used for some routes
|
||||||
|
(e.g. Feeds shows "Feed Mirror & AirGap Operations - StellaOps"). Check the implementation
|
||||||
|
pattern before adding a custom title strategy.
|
||||||
|
- **Integration Detail tabs**: Health, Activity, Permissions, Secrets, Webhooks tabs may
|
||||||
|
require new backend endpoints that don't exist yet. Scope this task to UI empty states
|
||||||
|
only if backend is not ready; do not block on backend.
|
||||||
|
- **Offline Settings nav entry**: Position in the sidebar can be debated. Suggested: after
|
||||||
|
"System" since both are admin-level operational pages. Confirm with product if a different
|
||||||
|
grouping is preferred.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 and TASK-02 are quick wins — target for immediate implementation.
|
||||||
|
- TASK-03 is more involved; may need to be split if the Overview tab + empty-state tabs
|
||||||
|
scope is too large for one task.
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
# Sprint 20260219-022 — QA Gap: Dashboard v3 — SBOM/Reachability/Data-Integrity Signals Missing
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-16 (Dashboard v3 spec).
|
||||||
|
- The current "Control Plane" page is a v1 dashboard that lacks all v2 signal upgrades.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/dashboard-v3/`
|
||||||
|
- Expected evidence: Dashboard shows regional pipeline nodes with SBOM+CritR+B/I/R status,
|
||||||
|
Environments at Risk table, SBOM Findings Snapshot card, Nightly Ops Signals card.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Depends on SPRINT_20260219_023 (Operations: Data Integrity) for the Nightly Ops Signals card
|
||||||
|
data source.
|
||||||
|
- TASK-01 (rename) is independent. TASK-02 through TASK-05 can run in parallel once data
|
||||||
|
contracts are agreed.
|
||||||
|
- Pack-16 spec is the authoritative wireframe reference.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-16.md` — Dashboard v3 full spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_handoff_packet.md` — canonical IA decisions
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/dashboard-v3/` — dashboard feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Rename "Control Plane" to "Dashboard" everywhere
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The live app root page is titled "Control Plane" and the nav item reads "Control Plane". Per the
|
||||||
|
v2 IA spec (S00 handoff packet, pack-16), the root domain is "Dashboard" (formerly Control
|
||||||
|
Plane). Update all references:
|
||||||
|
|
||||||
|
- Nav sidebar label: "Control Plane" → "Dashboard"
|
||||||
|
- Page `<h1>` heading on the root page
|
||||||
|
- Browser `<title>`: "Control Plane - StellaOps" → "Dashboard - StellaOps"
|
||||||
|
- Breadcrumb root label where "Control Plane" appears
|
||||||
|
- Route title in the Angular router config
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Nav item reads "Dashboard"
|
||||||
|
- [ ] Page heading reads "Dashboard"
|
||||||
|
- [ ] Browser tab shows "Dashboard - StellaOps"
|
||||||
|
- [ ] Legacy alias `/control-plane` still redirects to `/` (do not remove redirect)
|
||||||
|
- [ ] Unit test for the nav item label updated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Upgrade Regional Promotion Pipeline nodes to show SBOM + CritR + B/I/R status
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The current "Environment Pipeline" section on the dashboard shows four flat nodes (Development,
|
||||||
|
Staging, UAT, Production) with only deploy health (HEALTHY / DEGRADED / UNKNOWN). Per pack-16,
|
||||||
|
each pipeline node must show:
|
||||||
|
|
||||||
|
1. Deploy status
|
||||||
|
2. SBOM status (OK / STALE / MISSING / PENDING)
|
||||||
|
3. Critical Reachable count (CritR)
|
||||||
|
4. Hybrid Reachability coverage shorthand (B/I/R — Build/Image/Runtime, shown as e.g. "2/3")
|
||||||
|
|
||||||
|
The pipeline must also be region-aware: show a region selector or grouped by region. Clicking
|
||||||
|
a node navigates to the Environment Detail page.
|
||||||
|
|
||||||
|
Minimum viable: Show SBOM status and CritR count per environment node as badges under the
|
||||||
|
environment name. Add "Open Env Detail" link per node.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Each pipeline node shows SBOM freshness badge (OK/STALE/MISSING/PENDING)
|
||||||
|
- [ ] Each node shows Critical Reachable count (0 = clean, >0 = highlighted)
|
||||||
|
- [ ] Hybrid B/I/R coverage shorthand visible (e.g. "2/3") or "N/A" if data absent
|
||||||
|
- [ ] Clicking a node opens Environment Detail (existing or stub)
|
||||||
|
- [ ] Data uses API or well-typed stubs; no hardcoded strings in production path
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Add "Environments at Risk" table to Dashboard
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-02 (shares data model)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Pack-16 specifies an "Environments at Risk" table below the regional pipeline, showing the top
|
||||||
|
N environments with issues. Columns per the spec:
|
||||||
|
|
||||||
|
| Region/Env | Deploy Health | SBOM Status | Crit Reach | Hybrid B/I/R | Last SBOM | Action |
|
||||||
|
|
||||||
|
This is a focused decision-support table — it surfaces only environments that have a problem
|
||||||
|
(not all envs). Empty state: "All environments are healthy."
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Table renders with the 7 specified columns
|
||||||
|
- [ ] Only environments with SBOM stale, CritR > 0, or deploy degraded appear
|
||||||
|
- [ ] "Open" action link navigates to Environment Detail
|
||||||
|
- [ ] Empty state shows "All environments are healthy" message
|
||||||
|
- [ ] Loading state is handled gracefully
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Add SBOM Findings Snapshot card to Dashboard
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Pack-16 specifies a "SBOM Findings Snapshot" card in the dashboard snapshot row. The card shows:
|
||||||
|
|
||||||
|
- Critical reachable environments count and total CritR count
|
||||||
|
- Environments with no findings count
|
||||||
|
- Top affected envs (linked)
|
||||||
|
- [Open Findings] action link to Security Findings filtered to CritR
|
||||||
|
|
||||||
|
This is a summary card, not a full page. Clicking "Open Findings" navigates to
|
||||||
|
`/security/findings?reachability=critical`.
|
||||||
|
|
||||||
|
If no findings data is available from API, show a "Data unavailable" state with a link to
|
||||||
|
Security Findings.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Card shows CritR env count and total CritR count
|
||||||
|
- [ ] "No issues" state displays correctly when CritR = 0
|
||||||
|
- [ ] [Open Findings] link correctly filters Security Findings
|
||||||
|
- [ ] Card is responsive and fits dashboard layout
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Add Nightly Ops Signals card to Dashboard (links to Data Integrity)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: SPRINT_20260219_023 TASK-01 (Data Integrity Overview must exist for deep link)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Pack-16 specifies a "Nightly Ops Signals (Data Integrity)" card on the dashboard. The card
|
||||||
|
shows a summary of the most critical nightly job / feed / integration issues:
|
||||||
|
|
||||||
|
- SBOM rescan: OK / FAIL / WARN
|
||||||
|
- CVE feed (NVD): OK / STALE (Xh) / WARN
|
||||||
|
- Key integration: OK / DEGRADED
|
||||||
|
- DLQ: OK / N items
|
||||||
|
|
||||||
|
The card links to `/operations/data-integrity` for the full view. Until SPRINT_20260219_023
|
||||||
|
lands, the card can be stubbed with static "Not yet available" content and a link placeholder.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Card shows at minimum 4 signal rows (SBOM rescan, NVD feed, integration status, DLQ)
|
||||||
|
- [ ] [Open Data Integrity] link navigates to `/operations/data-integrity` (or shows a coming-soon
|
||||||
|
state if the route does not exist)
|
||||||
|
- [ ] Card status indicators use consistent OK/WARN/FAIL visual language
|
||||||
|
- [ ] No blank card body — always shows either data or a defined empty state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Fix Releases list "Loading releases..." stuck state
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer / Backend Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Releases page (`/releases`) shows "Loading releases..." indefinitely and displays "(0)"
|
||||||
|
next to the heading, even though the Control Plane / Dashboard page clearly shows releases
|
||||||
|
(Hotfix 1.2.4, Platform Release 1.3.0-rc1, etc.).
|
||||||
|
|
||||||
|
Root cause: Either the API call is failing silently, the component is not receiving the
|
||||||
|
response, or there is a loading state bug that never resolves.
|
||||||
|
|
||||||
|
Fix both layers:
|
||||||
|
- Diagnose root cause (inspect network request in test)
|
||||||
|
- Ensure the Releases list correctly fetches from the API endpoint and renders loaded data
|
||||||
|
- Distinguish "empty list" from "load error" — show a specific error message if the API call fails
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Releases list shows the known releases (Hotfix 1.2.4, Platform Release 1.3.0-rc1, etc.)
|
||||||
|
- [ ] Status filter counts reflect real data
|
||||||
|
- [ ] Error state shown if API call fails (not stuck spinner)
|
||||||
|
- [ ] Unit test confirms the list renders when data is returned
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-16 cross-reference. All tasks confirmed absent from live app. TASK-06 found live via Playwright observation (Releases page stuck at "Loading..."). | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Pack-16 is the authoritative reference** for dashboard layout and signal ordering.
|
||||||
|
- **Data Integrity dependency (TASK-05)**: The Nightly Ops Signals card references a section
|
||||||
|
(Operations → Data Integrity) that does not yet exist. TASK-05 can stub this with a static
|
||||||
|
card body until SPRINT_20260219_023 lands.
|
||||||
|
- **Regional pipeline nodes (TASK-02)**: The current Control Plane pipeline uses a flat 4-env
|
||||||
|
model. Pack-16 specifies a region-first model. The minimum viable implementation adds SBOM and
|
||||||
|
CritR badges to the existing flat model; region grouping is a follow-on.
|
||||||
|
- **Releases list (TASK-06)**: The data visible on the Control Plane (Recent Releases table)
|
||||||
|
uses different data sourcing than the `/releases` route — investigate whether they share a
|
||||||
|
service or whether the Releases route calls a different endpoint.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (rename) and TASK-06 (releases loading bug) are quick wins.
|
||||||
|
- TASK-02 through TASK-05 require UI data contracts to be established first.
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
# Sprint 20260219-023 — QA Gap: Operations — Data Integrity Section Entirely Missing
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-15 (Operations: Data Integrity).
|
||||||
|
- The entire "Data Integrity" sub-section under Operations is absent from the live app.
|
||||||
|
Navigating to `/operations/data-integrity` redirects to Control Plane (no route registered).
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/platform-ops/`
|
||||||
|
- Expected evidence: All 7 Data Integrity sub-pages exist, render, and link correctly to
|
||||||
|
their canonical source pages (Scheduler, Orchestrator, DLQ, Integrations).
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Pack-15 is the authoritative spec for this entire section.
|
||||||
|
- SPRINT_20260219_022 TASK-05 (Dashboard Nightly Ops Signals card) depends on this sprint's
|
||||||
|
TASK-01 (Data Integrity Overview) being landed first.
|
||||||
|
- All tasks in this sprint are independent of each other and can run in parallel once the
|
||||||
|
route shell and nav entry are created.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-15.md` — Data Integrity full spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-06.md` — Platform Ops menu graph (Operations root structure)
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/platform-ops.routes.ts` — route file to extend
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Create Operations → Data Integrity route shell + nav entry
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Create the route shell for the Data Integrity section under Operations. This includes:
|
||||||
|
|
||||||
|
1. Register child routes under `/operations/data-integrity/`:
|
||||||
|
- `/operations/data-integrity` → Data Integrity Overview (TASK-02)
|
||||||
|
- `/operations/data-integrity/nightly-ops` → Nightly Ops Report (TASK-03)
|
||||||
|
- `/operations/data-integrity/feeds-freshness` → Feeds Freshness (TASK-04)
|
||||||
|
- `/operations/data-integrity/scan-pipeline` → Scan Pipeline Health (TASK-05)
|
||||||
|
- `/operations/data-integrity/reachability-ingest` → Reachability Ingest Health (TASK-06)
|
||||||
|
- `/operations/data-integrity/integration-connectivity` → Integration Connectivity (TASK-07)
|
||||||
|
- `/operations/data-integrity/dlq` → DLQ & Replays (TASK-08)
|
||||||
|
- `/operations/data-integrity/slos` → Data Quality SLOs (TASK-09)
|
||||||
|
|
||||||
|
2. Add "Data Integrity" to the Operations sidebar submenu between "Platform Health" and
|
||||||
|
"Orchestrator" (or as first item — per pack-15 design intent).
|
||||||
|
|
||||||
|
3. Set `title` on each route: "{Page Name} - StellaOps" format.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 8 routes registered and navigable without 404
|
||||||
|
- [ ] "Data Integrity" appears in Operations sidebar submenu
|
||||||
|
- [ ] Each route shows at minimum a heading (stub pages acceptable)
|
||||||
|
- [ ] Sidebar highlights correctly when on any data-integrity sub-page
|
||||||
|
- [ ] Breadcrumb shows: Operations > Data Integrity > {Page}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Implement Data Integrity Overview page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Data Integrity Overview is the operator console for data trustworthiness. Per pack-15.3:
|
||||||
|
|
||||||
|
Sections:
|
||||||
|
1. **Data Trust Score** summary row: Feeds Freshness status, SBOM Pipeline status,
|
||||||
|
Reachability Ingest status, Integrations status, DLQ status — each as a badge with a
|
||||||
|
deep link to the relevant sub-page.
|
||||||
|
2. **Impacted Decisions** panel: count of approvals blocked due to data issues, list of
|
||||||
|
affected promotion names with links to Approvals.
|
||||||
|
3. **Top Failures** list: top 3 items to fix (failed job, stale feed, ingest lag) with
|
||||||
|
links to sub-pages.
|
||||||
|
|
||||||
|
The page must have:
|
||||||
|
- Region and environment type scope filters
|
||||||
|
- Time window filter (24h default)
|
||||||
|
- All badges link to the relevant data integrity sub-page
|
||||||
|
|
||||||
|
For the initial implementation, the page can render stub data if the backend data contract
|
||||||
|
is not yet defined. Define a stub contract matching the pack-15 ASCII mock fields.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Data Trust Score section renders with 5 signal badges
|
||||||
|
- [ ] Impacted Decisions panel renders (0 decisions if no data)
|
||||||
|
- [ ] Top Failures list renders (empty state if no failures)
|
||||||
|
- [ ] All deep links navigate to the correct sub-pages
|
||||||
|
- [ ] Region + time window filters are present (functional filter not required in v1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Implement Nightly Ops Report page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Nightly Ops Report is the release-impact view of nightly jobs. Per pack-15.4, it shows a
|
||||||
|
table of jobs with columns:
|
||||||
|
|
||||||
|
| Job | Schedule | Last Run | Status | Why it matters (release impact) |
|
||||||
|
|
||||||
|
Standard jobs to show (with stub data):
|
||||||
|
- cve-sync-osv, cve-sync-nvd, sbom-ingest-registry, sbom-nightly-rescan,
|
||||||
|
reachability-ingest-image, reachability-ingest-runtime, evidence-seal-bundles
|
||||||
|
|
||||||
|
Each row has actions: [View Run] [Open Scheduler] [Open Orchestrator] [Open Integration]
|
||||||
|
[Open DLQ]
|
||||||
|
|
||||||
|
The "Status" column must use OK / WARN / FAIL badges consistent with the rest of the app.
|
||||||
|
The "Why it matters" column shows a plain-language description of the release governance
|
||||||
|
impact (e.g., "stale SBOM → approvals may block").
|
||||||
|
|
||||||
|
Page scope filter: Window ▾ (24h default), Region ▾.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Table renders with 5 required columns
|
||||||
|
- [ ] At least 7 stub job rows visible
|
||||||
|
- [ ] Status badges are visually distinct (OK green, WARN amber, FAIL red)
|
||||||
|
- [ ] Row action buttons are present (links can be stub for now)
|
||||||
|
- [ ] Job Run Detail link (from [View Run]) navigates to job run detail (TASK-10 or stub)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Implement Feeds Freshness page (Data Integrity sub-page)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.6, Feeds Freshness is a data integrity sub-page that shows:
|
||||||
|
|
||||||
|
| Source | Status | Last Sync | SLA | Resulting gate impact |
|
||||||
|
|
||||||
|
For each advisory source (OSV, NVD, CISA KEV, etc.). This is a different lens from the
|
||||||
|
existing Operations → Feeds page — this page focuses on "can we trust today's CVE data for
|
||||||
|
approvals?" with gate impact stated explicitly.
|
||||||
|
|
||||||
|
Links at bottom: [Open Feeds & AirGap Ops] [Apply Version Lock] [Retry source sync]
|
||||||
|
|
||||||
|
Scope filter: Region ▾, SLA profile ▾.
|
||||||
|
|
||||||
|
Note: Do NOT duplicate the Feeds mirror/lock configuration — link to Operations → Feeds for
|
||||||
|
those operational controls. This page is read-only freshness status.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Table renders with 5 required columns
|
||||||
|
- [ ] At least 3 advisory source rows (OSV, NVD, CISA KEV)
|
||||||
|
- [ ] Gate impact column shows meaningful text (not blank)
|
||||||
|
- [ ] [Open Feeds & AirGap Ops] link navigates to `/operations/feeds`
|
||||||
|
- [ ] No mirror/lock configuration UI on this page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Implement Scan Pipeline Health page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.7, Scan Pipeline Health shows the SBOM scan pipeline end-to-end with stage status:
|
||||||
|
|
||||||
|
1. Image discovery (registry) — OK / WARN / FAIL
|
||||||
|
2. SBOM generation/ingest — count of produced vs pending
|
||||||
|
3. Nightly SBOM rescan — how many stale
|
||||||
|
4. CVE feeds sync — freshness status
|
||||||
|
5. CVE ↔ SBOM match/update — completeness
|
||||||
|
|
||||||
|
Below: impact summary showing environments with "unknown SBOM freshness" and approvals blocked.
|
||||||
|
|
||||||
|
Links: [Nightly Ops Report] [Feeds Freshness] [Integrations] [Security Findings]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] 5 pipeline stages render with status indicators
|
||||||
|
- [ ] Impact summary section shows affected env count and approval block count
|
||||||
|
- [ ] All 4 footer links present and correct
|
||||||
|
- [ ] Stage statuses use consistent OK/WARN/FAIL visual language
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Implement Reachability Ingest Health page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.8, Reachability Ingest Health shows coverage and pipeline status for the three
|
||||||
|
hybrid reachability sources: Image/Dover, Build, Runtime.
|
||||||
|
|
||||||
|
Coverage summary: Build X% | Image X% | Runtime X%
|
||||||
|
Pipeline table showing each source: last batch time, backlog count, status.
|
||||||
|
|
||||||
|
Links: [Open Agents] [Open DLQ bucket] [Open impacted approvals]
|
||||||
|
|
||||||
|
This page surfaces when one ingest source is lagging so reachability confidence is downgraded
|
||||||
|
for approvals.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Coverage summary shows B/I/R as percentages or "N/A"
|
||||||
|
- [ ] Pipeline table shows 3 rows (Image/Dover, Build, Runtime)
|
||||||
|
- [ ] Backlog count shown per source
|
||||||
|
- [ ] All 3 footer links present and correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Implement Integration Connectivity page (Data Integrity lens)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.9, Integration Connectivity shows connectors with a pipeline-impact focus:
|
||||||
|
|
||||||
|
| Connector | Status | Dependent pipelines | Impact |
|
||||||
|
|
||||||
|
Example connectors: Harbor Registry, Jenkins, Vault, Consul, NVD Source.
|
||||||
|
Row actions: [Open Detail] [Test] [View dependent jobs] [View impacted approvals]
|
||||||
|
|
||||||
|
This is a DATA INTEGRITY lens on integrations — it shows "which pipeline is broken because
|
||||||
|
which connector is down?" Do NOT duplicate Integrations Hub configuration here; link to it.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Table renders with 4 required columns
|
||||||
|
- [ ] At least 5 stub connector rows
|
||||||
|
- [ ] Row actions present (links can be stub)
|
||||||
|
- [ ] [Open Integrations Hub] footer link navigates to `/settings/integrations` (or future
|
||||||
|
canonical Integrations root when that sprint lands)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-08 — Implement DLQ & Replays page (Data Integrity lens)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.10, DLQ & Replays is a data integrity view of the Dead Letter Queue showing
|
||||||
|
buckets by pipeline with release impact context.
|
||||||
|
|
||||||
|
Bucket list: reachability-runtime-ingest, sbom-nightly-rescan, evidence-seal-bundles (with
|
||||||
|
item counts and agent/cause notes).
|
||||||
|
|
||||||
|
Selecting a bucket shows items with: payload description, age, [Replay] [View] [Link job]
|
||||||
|
actions.
|
||||||
|
|
||||||
|
Note: this is NOT a duplicate of Operations → Dead Letter. This is a data integrity lens
|
||||||
|
that shows "which approvals are unsafe because DLQ items are queued." Link to the existing
|
||||||
|
Dead Letter page for operational replay management.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Bucket list renders with item counts
|
||||||
|
- [ ] Selecting a bucket shows item rows
|
||||||
|
- [ ] Item rows show payload, age, and action buttons
|
||||||
|
- [ ] [Open Dead Letter] link to `/operations/dead-letter`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-09 — Implement Data Quality SLOs page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.11, Data Quality SLOs is an env-scoped slice of SLO monitoring focused on
|
||||||
|
data-integrity metrics that affect approvals:
|
||||||
|
|
||||||
|
| SLO | Target | Current | Status | Approval impact |
|
||||||
|
|
||||||
|
Standard SLOs:
|
||||||
|
- CVE feed freshness (NVD/OSV) — target <2h
|
||||||
|
- SBOM staleness (prod envs) — target <24h
|
||||||
|
- Runtime reach coverage (prod) — target >50%
|
||||||
|
|
||||||
|
Links: [Open System SLO Monitoring] [Open impacted approvals]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Table renders with 5 required columns
|
||||||
|
- [ ] At least 3 SLO rows with stub data
|
||||||
|
- [ ] Approval impact column is not blank
|
||||||
|
- [ ] [Open System SLO Monitoring] link navigates to Settings > System (or future canonical)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-10 — Implement Job Run Detail page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-03 (Nightly Ops Report links to it)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15.5, the Job Run Detail page is the investigation page bridging Ops mechanics to
|
||||||
|
release decisions. Accessible from Nightly Ops Report [View Run] action.
|
||||||
|
|
||||||
|
Required sections:
|
||||||
|
- Status header: job name, run ID, status badge, start/end times, error message
|
||||||
|
- Integration reference: which integration caused the failure, with link to Integration Detail
|
||||||
|
- Affected items list: which images/components/envs were not processed
|
||||||
|
- Links: [Open impacted approvals] [Open bundles] [Open DLQ bucket] [Open logs]
|
||||||
|
|
||||||
|
Route: `/operations/data-integrity/nightly-ops/{runId}` or
|
||||||
|
`/operations/scheduler/runs/{runId}` (whichever is canonical).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Status header renders with all fields
|
||||||
|
- [ ] Affected items list renders (empty state if none)
|
||||||
|
- [ ] Integration link present
|
||||||
|
- [ ] All action links present
|
||||||
|
- [ ] Breadcrumb: Operations > Data Integrity > Nightly Ops Report > Run #{id}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-15 cross-reference. All routes confirmed absent — `/operations/data-integrity` redirects to root. Entire section unimplemented. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Scope**: This sprint covers 10 tasks (route shell + 9 pages). A single developer should
|
||||||
|
tackle TASK-01 first (route shell + nav) then distribute remaining tasks in parallel.
|
||||||
|
- **Backend data contracts**: The Data Integrity pages aggregate from Scheduler, Orchestrator,
|
||||||
|
Integrations, and DLQ backends. No dedicated `/api/v1/data-integrity` endpoint exists. FE
|
||||||
|
can start with stub data while backend composition endpoint is planned.
|
||||||
|
- **No duplication policy**: All 7 sub-pages must link to the canonical source pages (Scheduler,
|
||||||
|
Dead Letter, Integrations, Feeds) rather than duplicating their UI. This is a summary/lens
|
||||||
|
layer only.
|
||||||
|
- **Relationship to existing pages**: Operations → Feeds (`/operations/feeds`) continues to
|
||||||
|
exist for mirror/lock configuration. Operations → Dead Letter continues to exist for
|
||||||
|
operational replay. Data Integrity sub-pages are READ-ONLY summaries.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (route shell + nav) must land before any other task starts.
|
||||||
|
- TASK-02 (Overview) and TASK-03 (Nightly Ops Report) are highest priority — these are
|
||||||
|
referenced by other sprints (Dashboard TASK-05, Approval Detail TASK-04).
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
# Sprint 20260219-024 — QA Gap: Approval Detail v2 — Tabs, Reachability, Ops/Data, Evidence
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-17 (Approvals v2 spec).
|
||||||
|
- The current Approval Detail page (`/approvals/:id`) is a flat single-panel layout.
|
||||||
|
The v2 spec requires a tabbed detail page with 8 tabs plus a standardized decision header.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/approvals/`
|
||||||
|
- Expected evidence: Approval Detail shows tabs [Overview][Gates][Security][Reachability]
|
||||||
|
[Ops/Data][Evidence][Replay/Verify][History] with correct content in each.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Pack-17 is the authoritative spec for this sprint.
|
||||||
|
- TASK-01 (tab shell) must land first; all tab tasks are blocked on it.
|
||||||
|
- TASK-04 (Ops/Data tab) requires SPRINT_20260219_023 (Data Integrity) for its deep links to
|
||||||
|
work; the tab can stub the content until Data Integrity is implemented.
|
||||||
|
- Approval Queue banner (TASK-08) is independent of the Detail page changes.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-17.md` — Approvals v2 full spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-13.md` — Releases + Approvals redesign (bundle-version driven)
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/approvals/` — approvals feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Refactor Approval Detail to tabbed layout with standardized decision header
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The current Approval Detail page has a two-column flat layout (Security Diff + Gate Results
|
||||||
|
on the left, Decision panel on the right). Per pack-17, it must become a full-width tabbed
|
||||||
|
page with:
|
||||||
|
|
||||||
|
**Standardized decision readiness header** (always visible above tabs):
|
||||||
|
- Bundle Version name + manifest digest (sha256:...)
|
||||||
|
- Target region + env path (e.g. EU-West eu-stage → eu-prod)
|
||||||
|
- Workflow name
|
||||||
|
- Requested by + time ago
|
||||||
|
- Decision readiness panel: Gates summary (PASS/BLOCK counts), Approvals count, CritR in
|
||||||
|
target env, SBOM freshness badge, Hybrid B/I/R coverage, Data Integrity confidence badge
|
||||||
|
- Action buttons: [Approve] [Reject] [Request Exception] [Export Decision Packet]
|
||||||
|
[Replay/Verify]
|
||||||
|
Note: [Approve] must be disabled if blocking gates are present (not just visually, but
|
||||||
|
functionally — the button must not submit if gates are BLOCK)
|
||||||
|
|
||||||
|
**Tabs** (below the header):
|
||||||
|
[Overview] [Gates] [Security] [Reachability] [Ops/Data] [Evidence] [Replay/Verify] [History]
|
||||||
|
|
||||||
|
Keep the existing approve/reject functionality; just restructure around the new layout.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Standardized decision header renders above tabs
|
||||||
|
- [ ] Bundle manifest digest shown in header
|
||||||
|
- [ ] Gates summary (PASS/BLOCK count) shown in header
|
||||||
|
- [ ] 8 tabs render and are navigable
|
||||||
|
- [ ] Approve button is disabled when blocking gates exist
|
||||||
|
- [ ] Existing approve/reject/exception workflow preserved in new layout
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Implement Gates tab (trace with inputs + timestamps + fix links)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The current page shows "Gate Results" as a simple list (PASS/BLOCK/WARN with a label and
|
||||||
|
[Explain] button). Per pack-17.4, the Gates tab must show a full gate trace table:
|
||||||
|
|
||||||
|
| Gate | Result | Why |
|
||||||
|
|
||||||
|
With a "Data snapshot" line at the top showing: feed freshness ages, rescan status. And a
|
||||||
|
"Decision digest" (sha256 of the gate trace record).
|
||||||
|
|
||||||
|
Each row must have:
|
||||||
|
- A [Gate detail trace] expandable section showing: inputs used, timestamps, hashes, evidence
|
||||||
|
age
|
||||||
|
- Fix links for BLOCK gates: [Trigger SBOM Scan] [Open Finding] [Request Exception]
|
||||||
|
[Open Data Integrity]
|
||||||
|
- Forensics links: [Replay Gate Eval] [Open Governance Rules]
|
||||||
|
|
||||||
|
The current [Explain] button can become the gate detail expand trigger.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Gates table shows columns: Gate, Result, Why
|
||||||
|
- [ ] Data snapshot line at top of tab
|
||||||
|
- [ ] Decision digest shown
|
||||||
|
- [ ] Each BLOCK gate shows at least one fix link
|
||||||
|
- [ ] Expandable trace section per row (can be accordion)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Implement Security tab (SBOM + Findings by env with delta)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.5, the Security tab shows:
|
||||||
|
|
||||||
|
1. Target env + summary: CritR, HighR, HighNR, VEX coverage, SBOM freshness
|
||||||
|
2. By-environment breakdown (stage vs prod CritR counts)
|
||||||
|
3. Delta vs currently deployed in target env: "+N Critical reachable introduced by this bundle"
|
||||||
|
4. Top CVE/package list with reachability label and VEX status
|
||||||
|
5. Links: [Open Findings (filtered)] [Open VEX Hub] [Open Exceptions]
|
||||||
|
|
||||||
|
The existing "Security Diff" panel content can be migrated here as the starting point.
|
||||||
|
Enhance it with the environment breakdown and delta section.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Summary line shows CritR + VEX coverage + SBOM freshness
|
||||||
|
- [ ] By-environment breakdown shows at least target env CritR
|
||||||
|
- [ ] Delta section shows +/- introduced vs resolved
|
||||||
|
- [ ] Top CVEs table shows CVE, package, component, reachability, VEX status
|
||||||
|
- [ ] All 3 footer links present
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Implement Reachability tab (Hybrid B/I/R matrix)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.6, the Reachability tab shows:
|
||||||
|
|
||||||
|
1. Coverage summary: Build X% | Image X% | Runtime X%
|
||||||
|
2. Evidence age per source: Build Xh | Image Xh | Runtime Xh
|
||||||
|
3. Policy interpretation: what coverage means (WARN/BLOCK threshold)
|
||||||
|
4. Per-component B/I/R matrix table:
|
||||||
|
| Component | Digest | Build ✓/✗ | Image ✓/✗ | Runtime ✓/✗ |
|
||||||
|
5. Links: [Open Reachability Ingest Health] [Open Env Detail]
|
||||||
|
|
||||||
|
The existing "Reachable (82%)" button in the Security Diff can be removed or repurposed here.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Coverage summary row shows B/I/R percentages
|
||||||
|
- [ ] Evidence age shown per source
|
||||||
|
- [ ] Policy interpretation text present
|
||||||
|
- [ ] Per-component matrix table renders
|
||||||
|
- [ ] Links present and correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Implement Ops/Data Health tab (Data Integrity confidence panel)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01; deep links require SPRINT_20260219_023
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.7, the Ops/Data tab is a summary of data integrity confidence for this approval.
|
||||||
|
It shows snapshots from 4 categories:
|
||||||
|
|
||||||
|
1. Feeds: OSV freshness, NVD freshness (with WARN if stale), KEV freshness
|
||||||
|
2. Nightly jobs: sbom-nightly-rescan status, reachability-runtime-ingest status
|
||||||
|
3. Integrations: Harbor, Jenkins, Vault, Consul connectivity status
|
||||||
|
4. DLQ: runtime-ingest bucket count
|
||||||
|
|
||||||
|
Bottom: [Open Data Integrity] [Open Integrations] [Open Scheduler Runs] [Open DLQ]
|
||||||
|
|
||||||
|
Until SPRINT_20260219_023 lands, the tab can render stub data with "Live data pending
|
||||||
|
Operations → Data Integrity implementation" notice.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] 4 data sections render (Feeds, Jobs, Integrations, DLQ)
|
||||||
|
- [ ] Status indicators consistent with rest of app (OK/WARN/FAIL)
|
||||||
|
- [ ] [Open Data Integrity] link to `/operations/data-integrity`
|
||||||
|
- [ ] Tab is not blank — always shows either live data or a defined stub state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Implement Evidence tab (Decision Packet)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.8, the Evidence tab shows the decision packet composition:
|
||||||
|
|
||||||
|
- List of evidence artifacts: policy-decision.dsse ✓, gate-trace.json ✓, data-snapshot.json ✓,
|
||||||
|
proof-chain.json ○ (sealed on completion)
|
||||||
|
- Signature status + transparency log presence
|
||||||
|
- Actions: [Export Packet] [Open Export Center] [Open Proof Chain]
|
||||||
|
|
||||||
|
The existing "Open Evidence Packet" link in the current decision panel can be migrated here.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Evidence artifact list renders (can be stub artifacts)
|
||||||
|
- [ ] Signature status line present
|
||||||
|
- [ ] [Export Packet] button present (action can be stub for now)
|
||||||
|
- [ ] [Open Export Center] links to `/evidence/export`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Implement Replay/Verify tab and History tab (stubs)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.9 and 17.10, two additional tabs:
|
||||||
|
|
||||||
|
**Replay/Verify tab**:
|
||||||
|
- Pre-filled replay request form (Verdict ID, Bundle manifest, Baseline, Data snapshot)
|
||||||
|
- [Request Replay] button
|
||||||
|
- Recent replays list (empty state if none)
|
||||||
|
- Link: [Open canonical Replay/Verify] → `/evidence/replay`
|
||||||
|
|
||||||
|
**History tab**:
|
||||||
|
- Event timeline: gate eval timestamps, data health changes, exceptions requested, evidence
|
||||||
|
exports
|
||||||
|
- Comments/Rationales from approvers
|
||||||
|
- Links to related release/promotion run
|
||||||
|
|
||||||
|
Both tabs can show stub data initially with well-defined empty states.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Replay/Verify tab renders with pre-filled form
|
||||||
|
- [ ] History tab renders with event timeline (stub events OK)
|
||||||
|
- [ ] Neither tab is blank
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-08 — Add Data Integrity warning banner to Approvals Queue
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: SPRINT_20260219_023 TASK-02 (Data Integrity Overview for deep link)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-17.2, the Approvals Queue page must show a banner at the top when data integrity
|
||||||
|
issues are present:
|
||||||
|
|
||||||
|
"Data Integrity WARN — NVD stale 3h | SBOM rescan FAILED | Runtime ingest lagging [Open
|
||||||
|
Data Integrity]"
|
||||||
|
|
||||||
|
The banner should be:
|
||||||
|
- Dismissible per session
|
||||||
|
- Color-coded: WARN = amber banner, FAIL = red banner, OK = hidden
|
||||||
|
- The data source is the same Data Integrity Overview endpoint (SPRINT_20260219_023)
|
||||||
|
|
||||||
|
Until the Data Integrity section is implemented, this banner can be hidden or show a static
|
||||||
|
"Data integrity monitoring not yet configured" state.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Banner renders on Approvals Queue when data issues present
|
||||||
|
- [ ] Banner is hidden when all data is OK
|
||||||
|
- [ ] [Open Data Integrity] link navigates to `/operations/data-integrity`
|
||||||
|
- [ ] Banner is dismissible for the session
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-17 cross-reference. Live Approval Detail at `/approvals/apr-001` confirmed as flat two-panel layout missing all v2 tabs. Gate trace missing inputs/timestamps. No Reachability, Ops/Data, or History tabs. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Preserve existing functionality**: The existing approve/reject/exception workflow must be
|
||||||
|
preserved exactly. The refactor changes layout only, not decision logic.
|
||||||
|
- **Gate trace data**: The [Explain] button currently exists but the content of the expandable
|
||||||
|
trace is not specified in the current codebase. Define a stub contract for gate trace
|
||||||
|
inputs/timestamps/hashes.
|
||||||
|
- **Bundle manifest digest**: The current approval detail shows a `code` element with a digest
|
||||||
|
(confirmed in live observation: `sha256:7aa1b2c3d4e5f6...`). This is good — it can be kept
|
||||||
|
and promoted to the standardized header.
|
||||||
|
- **Approve button disability**: Functional disable (not submittable) when blocking gates exist
|
||||||
|
is important for correctness — confirm the current implementation actually blocks the API call
|
||||||
|
or only disables the button visually.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (tab shell + header) is the gate for all other tasks.
|
||||||
|
- TASK-03 (Security tab) can reuse existing Security Diff data as a starting point.
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
# Sprint 20260219-025 — QA Gap: Environment Detail — Standardized Header + Tabs Missing
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-18 (Environment Detail spec).
|
||||||
|
- There is no dedicated Environment Detail page. The dashboard pipeline nodes link nowhere;
|
||||||
|
environments are only visible as flat nodes on the Control Plane without a detail view.
|
||||||
|
Settings → Release Control → Environments exists but is a flat config list, not a
|
||||||
|
runtime status page.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/release-orchestrator/environments/`
|
||||||
|
- Expected evidence: `/environments/:region/:env` (or equivalent) renders a full environment
|
||||||
|
detail page with standardized status header and 8 tabs.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Pack-18 is the authoritative spec.
|
||||||
|
- Depends on understanding from `S00_handoff_packet.md` (region-first model).
|
||||||
|
- TASK-01 (route + header) must land before tab tasks.
|
||||||
|
- Tab tasks (TASK-02 through TASK-08) are independent of each other once TASK-01 is done.
|
||||||
|
- SPRINT_20260219_022 TASK-02 (pipeline nodes) links to this page — coordinate route.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-18.md` — Environment Detail full spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-11.md` — Regions & Environments as first-class structure
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/release-orchestrator/environments/` — env feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Create Environment Detail route and standardized status header
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Create or extend the Environment Detail route at:
|
||||||
|
`/release-orchestrator/environments/:region/:env` (or the v2 canonical route once agreed).
|
||||||
|
|
||||||
|
The page must have a **standardized status header** that is always visible regardless of which
|
||||||
|
tab is active. Per pack-18 section 18.2, the header contains:
|
||||||
|
|
||||||
|
```
|
||||||
|
Environment: {env-name} Region: {region} Type: {Production|Staging|...}
|
||||||
|
Deploy: {status} targets {N}/{M} healthy | SBOM: {status} scanned {N}/{M} pending {P}
|
||||||
|
Findings (target env): CritR={n} HighR={n} HighNR={n} VEX={n}%
|
||||||
|
Hybrid reach coverage: Build {n}% | Image {n}% | Runtime {n}% (evidence age: B {h} / I {h} / R {h})
|
||||||
|
Data Confidence: {status} ({issues list})
|
||||||
|
Policy baseline: {baseline name} Version lock: {lock name}
|
||||||
|
Deployed bundle: {bundle name} (manifest sha256:...)
|
||||||
|
Quick links: [Open Deployed Bundle] [Open Findings] [Open Data Integrity] [Open Promotion Run]
|
||||||
|
```
|
||||||
|
|
||||||
|
Tabs below the header:
|
||||||
|
[Overview] [Deploy Status] [SBOM & Findings] [Reachability] [Inputs] [Promotions] [Data
|
||||||
|
Confidence] [Evidence & Audit]
|
||||||
|
|
||||||
|
Route must have `title`: "{Region}/{Env} Environment - StellaOps"
|
||||||
|
Breadcrumb: Release Control > Regions & Environments > {Region} > {Env}
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route is registered and navigable
|
||||||
|
- [ ] Standardized header renders with all 7 sections
|
||||||
|
- [ ] Manifest digest shown in header
|
||||||
|
- [ ] 8 tabs render
|
||||||
|
- [ ] Breadcrumb correct
|
||||||
|
- [ ] Page title correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Implement Overview tab (env situation report)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.3, the Overview tab is the decision brief:
|
||||||
|
|
||||||
|
Left column:
|
||||||
|
- Current deployment: bundle name + manifest + last promoted by + components summary
|
||||||
|
- Promotion posture: pending approvals count, active runs count, next scheduled
|
||||||
|
|
||||||
|
Right column (action panel):
|
||||||
|
- [Trigger SBOM rescan] [Retry NVD sync] [Open Inputs] [Open Run] [Export Env Snapshot]
|
||||||
|
|
||||||
|
Below: Top risks list (top 3 issues) with links to [Open Findings] [Open Data Integrity]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Current deployment panel shows bundle and manifest digest
|
||||||
|
- [ ] Pending approvals count shown
|
||||||
|
- [ ] Top risks list renders (empty state: "No current risks")
|
||||||
|
- [ ] Action buttons present (actions can be stub)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Implement Deploy Status tab (targets + services)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.4, Deploy Status shows:
|
||||||
|
|
||||||
|
1. **Targets table**: target name, agent, health, last heartbeat, notes
|
||||||
|
2. **Services/Workloads table**: service name, status, digest, replica count / error rate
|
||||||
|
|
||||||
|
Links: [Open last Promotion Run] [Open agent logs]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Targets table renders with 4 columns (name, agent, health, heartbeat)
|
||||||
|
- [ ] Services table renders with 4 columns (name, status, digest, replicas)
|
||||||
|
- [ ] Health badges are visually distinct (healthy/degraded/unknown)
|
||||||
|
- [ ] [Open last Promotion Run] link present
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Implement SBOM & Findings tab (deployed inventory + scan status)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.5, SBOM & Findings shows:
|
||||||
|
|
||||||
|
1. Findings summary: CritR, HighR, HighNR, VEX%, SBOM freshness, missing SBOM count
|
||||||
|
2. Deployed inventory table (digest-first):
|
||||||
|
| Component | Version label | Digest | SBOM status | Findings (CritR) |
|
||||||
|
3. Top CVE issues list
|
||||||
|
4. Actions: [Trigger SBOM scan/rescan] [Open Findings] [Open VEX/Exceptions]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Summary line renders with all 6 metrics
|
||||||
|
- [ ] Deployed inventory table renders with 5 columns
|
||||||
|
- [ ] SBOM status column shows OK/PENDING/STALE badges
|
||||||
|
- [ ] Top CVE issues list renders (empty state if none)
|
||||||
|
- [ ] All 3 action links present
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Implement Reachability tab (Hybrid B/I/R matrix per env)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.6, same structure as the Approval Detail Reachability tab but scoped to this
|
||||||
|
specific environment's deployed bundle:
|
||||||
|
|
||||||
|
1. Coverage: Build X% | Image X% | Runtime X%
|
||||||
|
2. Evidence age: Build Xh | Image Xh | Runtime Xh
|
||||||
|
3. Policy interpretation text
|
||||||
|
4. Per-component B/I/R matrix: | Component | Digest | Build | Image | Runtime |
|
||||||
|
5. Links: [Open Reachability Ingest Health] [Open component version details]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Coverage + evidence age row present
|
||||||
|
- [ ] Policy interpretation text present
|
||||||
|
- [ ] Per-component matrix table renders
|
||||||
|
- [ ] Links correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Implement Inputs tab (Vault/Consul bindings + materialization readiness)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.7, the Inputs tab shows Vault/Consul bindings for each required variable from
|
||||||
|
the deployed bundle's config contracts:
|
||||||
|
|
||||||
|
Per service:
|
||||||
|
- Variable name
|
||||||
|
- Source (consul key / vault path)
|
||||||
|
- Binding status (✓ bound / ✗ MISSING)
|
||||||
|
|
||||||
|
If missing bindings exist: "Impact: promotions using this env will BLOCK at Materialize
|
||||||
|
Inputs" warning banner + [Bind missing var] action.
|
||||||
|
|
||||||
|
Links: [Open Vault integration] [Open Consul integration]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Binding table renders per-service with variable/source/status columns
|
||||||
|
- [ ] Missing binding highlighted in red with impact message
|
||||||
|
- [ ] [Bind missing var] action present when missing bindings exist
|
||||||
|
- [ ] Footer integration links present
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Implement Promotions & Approvals tab (env-centric history)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-18.8, Promotions & Approvals is an env-centric view showing:
|
||||||
|
|
||||||
|
1. Pending approvals targeting this env (with [Open Approval] links)
|
||||||
|
2. Recent promotions table: date, bundle, status, [Open Run] [Evidence] links
|
||||||
|
3. Diff section: proposed vs deployed bundle comparison with [Open Diff]
|
||||||
|
|
||||||
|
Links: [Open Releases filtered to this env] [Open Approvals filtered to this env]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Pending approvals section renders (empty state if none)
|
||||||
|
- [ ] Recent promotions table renders with date/bundle/status columns
|
||||||
|
- [ ] [Open Run] and [Evidence] links per row
|
||||||
|
- [ ] Diff section shows "proposed vs deployed" summary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-08 — Implement Data Confidence tab and Evidence & Audit tab (stubs)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01; SPRINT_20260219_023 for Data Confidence deep links
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Two remaining tabs:
|
||||||
|
|
||||||
|
**Data Confidence tab** (pack-18.9):
|
||||||
|
- Feeds section: OSV/NVD/KEV freshness for this env's region
|
||||||
|
- Jobs impacting this env: sbom-nightly-rescan, reachability-runtime-ingest
|
||||||
|
- Integrations relevant to this env
|
||||||
|
- DLQ counts
|
||||||
|
- Link: [Open Ops → Data Integrity (region + env filter)]
|
||||||
|
|
||||||
|
**Evidence & Audit tab** (pack-18.10):
|
||||||
|
- [Export Env Snapshot] button with description of what it includes
|
||||||
|
- Latest promotion evidence pack link + download
|
||||||
|
- Proof chain refs
|
||||||
|
- Audit trail of env config changes (who changed inputs/bindings/policy, with timestamps)
|
||||||
|
- Link: [Open Evidence Export Center]
|
||||||
|
|
||||||
|
Both tabs can stub data pending backend contracts. They must not be blank.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Data Confidence tab renders with 4 sections
|
||||||
|
- [ ] [Open Data Integrity] link present with region+env filter intent noted
|
||||||
|
- [ ] Evidence & Audit tab renders with export option and audit trail
|
||||||
|
- [ ] Neither tab is blank
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-18 cross-reference. Live app has no dedicated Environment Detail page. Dashboard pipeline nodes do not link anywhere. Settings > Release Control > Environments is a config-only list without runtime status. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Route choice**: Current environments live under `/release-orchestrator/environments/`.
|
||||||
|
The v2 canonical route is Release Control → Regions & Environments. Coordinate with the
|
||||||
|
nav restructure sprint (SPRINT_20260219_029) before finalizing the route.
|
||||||
|
- **Standard header is critical**: The standardized status header (TASK-01) is the defining
|
||||||
|
pattern for the v2 environment model. All other environment-context pages (Approvals,
|
||||||
|
Releases, Dashboard) link to this page expecting the standard header.
|
||||||
|
- **Region model**: The v2 spec is region-first. The current app does not distinguish regions.
|
||||||
|
Initial implementation can use a single default region with environment path. Region support
|
||||||
|
is an enhancement.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (route + header) gates all other tasks.
|
||||||
|
- TASK-04 (SBOM & Findings) is highest-priority tab as it directly affects the
|
||||||
|
"environments at risk" use case from the Dashboard spec.
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
# Sprint 20260219-026 — QA Gap: Evidence & Audit — Home Router, Audit Log, Export Blank, Trust Migration
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-20 (Evidence & Audit spec).
|
||||||
|
- Multiple issues found via Playwright observation:
|
||||||
|
1. Evidence Export page (`/evidence/export`) renders a completely blank `<main>` — page title
|
||||||
|
shows "Export - Stella Ops Dashboard" (wrong format) and no content loads.
|
||||||
|
2. Nav item "Packets" label mismatches page heading "Evidence Bundles" (wrong label).
|
||||||
|
3. Evidence Home (router/search page) is absent from nav and routes.
|
||||||
|
4. Audit Log is absent from nav and routes.
|
||||||
|
5. Trust & Signing is in Settings, not yet under Evidence & Audit.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/evidence-audit/`
|
||||||
|
- Expected evidence: all Evidence sub-pages render content, nav labels match spec, Audit Log
|
||||||
|
page exists, Export page is not blank.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Pack-20 is the authoritative spec.
|
||||||
|
- TASK-01 (Export blank bug) and TASK-02 (nav label fix) are independent quick fixes.
|
||||||
|
- TASK-03 (Evidence Home) and TASK-04 (Audit Log) can run in parallel.
|
||||||
|
- TASK-05 (Trust & Signing migration) is a cross-module change requiring coordination with
|
||||||
|
the Settings decomposition sprint (SPRINT_20260219_029).
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-20.md` — Evidence & Audit full spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_trust_ownership_transition.md` — Trust & Signing ownership
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/evidence-audit/` — evidence feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Fix Evidence Export page rendering blank content (CRITICAL BUG)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Navigating to `/evidence/export` loads a blank page. Observed via Playwright:
|
||||||
|
- Page title: "Export - Stella Ops Dashboard" (format inconsistent with other pages)
|
||||||
|
- The `<main>` element is completely empty — no content rendered
|
||||||
|
- The sidebar no longer shows Evidence expanded
|
||||||
|
|
||||||
|
Root cause investigation required:
|
||||||
|
1. Check the route definition for `/evidence/export` — is it registered correctly?
|
||||||
|
2. Check if the component is lazy-loaded and failing to load (check browser console for errors)
|
||||||
|
3. Check if the page requires authentication/authorization data that is unavailable
|
||||||
|
4. The title format "Export - Stella Ops Dashboard" vs "Export - StellaOps" suggests the
|
||||||
|
component may be using a different title strategy
|
||||||
|
|
||||||
|
Fix the root cause so the Export page renders its content. Per pack-20.7, the Export Center
|
||||||
|
should show:
|
||||||
|
- Standard profiles list (Approval Decision Pack, Env Snapshot Export, Audit Bundle, Daily
|
||||||
|
Compliance Export)
|
||||||
|
- Export Runs table
|
||||||
|
- [Create Profile] action
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] `/evidence/export` renders page content (not blank)
|
||||||
|
- [ ] Page title: "Export Center - StellaOps"
|
||||||
|
- [ ] Export profiles list renders (empty state OK if no profiles configured)
|
||||||
|
- [ ] Export Runs table renders (empty state OK)
|
||||||
|
- [ ] No console errors on load
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix Evidence nav label: "Packets" → "Evidence Packs"
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Evidence sidebar nav item "Packets" (at /evidence) navigates to `/evidence/bundles` and
|
||||||
|
the page heading reads "Evidence Bundles". There are two issues:
|
||||||
|
|
||||||
|
1. **Nav label mismatch**: Nav says "Packets" but the page is "Evidence Bundles". Per
|
||||||
|
pack-20 spec, the renamed terms are: "Packets" → "Evidence Packs" (the nav item for the
|
||||||
|
packs list) and "Evidence Bundles" is a separate concept (compiled exports for auditors).
|
||||||
|
|
||||||
|
2. **Route mismatch**: The nav item "Packets" links to `/evidence` but navigates to
|
||||||
|
`/evidence/bundles`. The canonical route for Evidence Packs should be `/evidence/packs`
|
||||||
|
(or keep `/evidence` as the root and redirect to the correct default sub-page).
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Rename nav item "Packets" → "Evidence Packs"
|
||||||
|
- Ensure the nav item links to the Evidence Packs list page (not bundles)
|
||||||
|
- Add a separate "Evidence Bundles" nav item at `/evidence/bundles`
|
||||||
|
- Update the Evidence Packs page heading to "Evidence Packs"
|
||||||
|
- Keep "Evidence Bundles" page heading as "Evidence Bundles"
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Nav shows "Evidence Packs" and "Evidence Bundles" as distinct items
|
||||||
|
- [ ] "Evidence Packs" navigates to and shows the packs list
|
||||||
|
- [ ] "Evidence Bundles" navigates to and shows the bundles list
|
||||||
|
- [ ] Page headings match nav labels
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Add Evidence Home (router/search page)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-20.2, Evidence Home is the entry router for the Evidence section. It provides a
|
||||||
|
search interface for finding evidence by:
|
||||||
|
|
||||||
|
- Release, Bundle Version, Environment, Approval
|
||||||
|
- Or by pasting a digest / verdict-id / bundle-id
|
||||||
|
|
||||||
|
Quick views:
|
||||||
|
- Latest promotion evidence packs (24h)
|
||||||
|
- Latest sealed bundles (7d)
|
||||||
|
- Failed verification / replay (7d)
|
||||||
|
- Expiring trust/certs (30d)
|
||||||
|
|
||||||
|
Shortcuts: [Export Center] [Evidence Bundles] [Replay & Verify] [Proof Chains]
|
||||||
|
[Trust & Signing]
|
||||||
|
|
||||||
|
This page becomes the default landing for the "Evidence" nav section (replaces the current
|
||||||
|
direct navigation to Packets/Bundles). Add it as the first item in Evidence nav or as the
|
||||||
|
section header link.
|
||||||
|
|
||||||
|
Route: `/evidence` (or `/evidence/home`)
|
||||||
|
Title: "Evidence & Audit - StellaOps"
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with search form and quick view tiles
|
||||||
|
- [ ] Search form has 4 context selectors (Release, Bundle, Env, Approval)
|
||||||
|
- [ ] Quick view tiles render (empty states OK)
|
||||||
|
- [ ] All 5 shortcut links present and correct
|
||||||
|
- [ ] Accessible from Evidence nav section
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Add Audit Log page to Evidence section
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-20.11, the Evidence section needs an Audit Log page — a unified event log with
|
||||||
|
filters and artifact links. This is missing entirely from the current nav.
|
||||||
|
|
||||||
|
Route: `/evidence/audit-log`
|
||||||
|
Title: "Audit Log - StellaOps"
|
||||||
|
Nav: Add "Audit Log" as last item in Evidence submenu.
|
||||||
|
|
||||||
|
The page shows a time-ordered event list with columns:
|
||||||
|
- Timestamp
|
||||||
|
- Event type (ExportRun, PromotionDecision, TrustCertRotated, etc.)
|
||||||
|
- Context (Release/Env/Approval/User reference)
|
||||||
|
- [Open] link to the referenced artifact
|
||||||
|
|
||||||
|
Filters: Event type ▾, Release ▾, Env ▾, Approval ▾, User ▾, Time window ▾
|
||||||
|
|
||||||
|
Actions: [Export audit log slice → Evidence export]
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/evidence/audit-log` exists and renders
|
||||||
|
- [ ] "Audit Log" appears in Evidence sidebar submenu
|
||||||
|
- [ ] Event list renders with 5 columns
|
||||||
|
- [ ] Time window filter is present
|
||||||
|
- [ ] [Export audit log slice] action present
|
||||||
|
- [ ] Empty state when no events: "No audit events in selected time window"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Plan Trust & Signing migration: Settings → Evidence & Audit
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: SPRINT_20260219_029 (root nav IA restructure) for execution
|
||||||
|
Owners: FE Developer / Project Manager
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-20.10 and S00_trust_ownership_transition.md, Trust & Signing must move from
|
||||||
|
Settings → Trust & Signing to Evidence & Audit → Trust & Signing.
|
||||||
|
|
||||||
|
Current location: `/settings/trust`
|
||||||
|
Target canonical location: `/evidence/trust-signing`
|
||||||
|
|
||||||
|
The Settings header shows "Evidence: OFF" with a link to `/settings/trust` — this link
|
||||||
|
must be updated to `/evidence/trust-signing` once migrated.
|
||||||
|
|
||||||
|
This task is a **planning task** — document the migration plan and required steps. Execution
|
||||||
|
is blocked on SPRINT_20260219_029 deciding the final Settings decomposition approach.
|
||||||
|
|
||||||
|
Migration plan to document:
|
||||||
|
1. Create new route `/evidence/trust-signing` that renders the Trust & Signing component
|
||||||
|
2. Add "Trust & Signing" nav item to Evidence sidebar submenu
|
||||||
|
3. Add redirect from `/settings/trust` → `/evidence/trust-signing`
|
||||||
|
4. Update "Evidence: OFF" status bar link to new route
|
||||||
|
5. Remove Trust & Signing from Settings sidebar once redirect is in place
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Migration plan is documented in sprint Decisions & Risks
|
||||||
|
- [ ] Route and redirect plan specified (no code changes in this task)
|
||||||
|
- [ ] Status bar link update is noted
|
||||||
|
- [ ] Dependency on SPRINT_20260219_029 recorded
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-20 cross-reference. Evidence Export blank page confirmed via Playwright. Nav label "Packets" mismatch confirmed. Evidence Home and Audit Log absent from nav and routes. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Export blank page (TASK-01)**: The blank page is a blocking bug for export functionality.
|
||||||
|
The title format mismatch ("Stella Ops Dashboard" vs "StellaOps") suggests the component may
|
||||||
|
be from a different template or generation cycle. Check git blame to understand origin.
|
||||||
|
- **Nav label "Packets" vs "Evidence Packs"**: The v2 spec uses "Evidence Packs" as the term
|
||||||
|
for atomic evidence artifacts (previously "Packets"). "Evidence Bundles" are compiled exports
|
||||||
|
for auditors. These are distinct. The current nav conflates them by linking "Packets" to
|
||||||
|
`/evidence/bundles`.
|
||||||
|
- **Trust & Signing migration timing**: Do not execute TASK-05 until SPRINT_20260219_029
|
||||||
|
(Settings decomposition) is underway. Premature migration will leave a dangling redirect
|
||||||
|
if Settings is restructured at the same time.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (export blank bug) and TASK-02 (nav label fix) are quick wins — implement first.
|
||||||
|
- TASK-03 (Evidence Home) and TASK-04 (Audit Log) are medium effort.
|
||||||
|
- TASK-05 (Trust migration plan) can be done in parallel as a planning exercise.
|
||||||
@@ -0,0 +1,376 @@
|
|||||||
|
# Sprint 20260219-027 — QA Gap: Security & Risk — Nav Rename, SBOM Data Grouping, Risk Overview
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs pack-19 (Security consolidated spec).
|
||||||
|
- The current Security nav uses v1 labels and flat structure. Pack-19 specifies:
|
||||||
|
1. Root rename: "Security" → "Security & Risk"
|
||||||
|
2. Sub-grouping: "SBOM Data" group (SBOM Lake + SBOM Graph) — SBOM Lake is currently under
|
||||||
|
Analytics, not Security
|
||||||
|
3. Sub-grouping: "VEX & Exceptions" group (VEX Hub + Exceptions)
|
||||||
|
4. Risk Overview (renamed from Overview) with data confidence banner
|
||||||
|
5. Finding Detail page (no explicit detail page currently exists)
|
||||||
|
6. Vulnerability Detail page (no explicit detail page currently exists)
|
||||||
|
7. Advisory Sources page (missing from Security nav)
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/security-risk/`
|
||||||
|
- Expected evidence: Security nav shows all 7+ items with correct groupings and labels.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Pack-19 is the authoritative spec.
|
||||||
|
- TASK-01 (nav rename) is independent and a quick win.
|
||||||
|
- TASK-02 (SBOM Lake migration from Analytics) requires Analytics route to add a redirect.
|
||||||
|
- TASK-03 through TASK-06 are independent of each other.
|
||||||
|
- TASK-07 (Advisory Sources) overlaps with Concelier/Policy backend contracts
|
||||||
|
(S00_advisory_sources_spec.md) — coordinate with backend team.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-19.md` — Security consolidated spec (CRITICAL, read first)
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_advisory_sources_spec.md` — Advisory Sources screen ownership
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/` — security feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Rename "Security" to "Security & Risk" in nav and all page titles
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The v2 IA canonical name for the security root domain is "Security & Risk". Update:
|
||||||
|
|
||||||
|
- Sidebar nav section button: "Security" → "Security & Risk"
|
||||||
|
- Sub-page breadcrumbs: "Security" → "Security & Risk" as root crumb
|
||||||
|
- All page `<title>` values: "Overview - StellaOps" → "Risk Overview - StellaOps" (see TASK-02
|
||||||
|
for the rename of the overview page itself)
|
||||||
|
- Route config `title` properties where "Security" prefix is used
|
||||||
|
|
||||||
|
Also rename the Overview sub-page:
|
||||||
|
- Nav item: "Overview" → "Risk Overview"
|
||||||
|
- Page heading: "Security Overview" → "Risk Overview"
|
||||||
|
- Route title: "Risk Overview - StellaOps"
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Sidebar shows "Security & Risk" as the section label
|
||||||
|
- [ ] All security sub-page breadcrumbs use "Security & Risk" as root
|
||||||
|
- [ ] Nav sub-item "Overview" renamed to "Risk Overview"
|
||||||
|
- [ ] Page heading and title updated for the overview page
|
||||||
|
- [ ] No references to old label remain in visible UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Move SBOM Lake from Analytics to Security & Risk → SBOM Data sub-group
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Currently SBOM Lake is under Analytics (`/analytics/sbom-lake`). Per pack-19.7, SBOM Lake
|
||||||
|
belongs under Security → SBOM Data as a backend exploration tool.
|
||||||
|
|
||||||
|
Changes required:
|
||||||
|
1. Add "SBOM Data" sub-group to Security & Risk nav (with a collapsible group or flat listing)
|
||||||
|
2. Add "SBOM Lake" nav item under SBOM Data → `/security/sbom-lake` (new canonical route)
|
||||||
|
3. Register route `/security/sbom-lake` rendering the SBOM Lake component
|
||||||
|
4. Add redirect from `/analytics/sbom-lake` → `/security/sbom-lake`
|
||||||
|
5. Move "SBOM Graph" (already at `/security/sbom`) under the SBOM Data group
|
||||||
|
6. Add redirect from `/security/sbom` → `/security/sbom-data/graph` (or keep current path
|
||||||
|
and just group it visually in the nav)
|
||||||
|
|
||||||
|
Analytics section: After SBOM Lake is moved, "Analytics" may become empty. Check if there
|
||||||
|
are other Analytics sub-pages. If empty, either remove the Analytics nav section or add a
|
||||||
|
redirect for the Analytics root.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "SBOM Lake" appears under Security & Risk in the sidebar
|
||||||
|
- [ ] `/security/sbom-lake` route renders the SBOM Lake page
|
||||||
|
- [ ] `/analytics/sbom-lake` redirects to `/security/sbom-lake`
|
||||||
|
- [ ] "SBOM Graph" and "SBOM Lake" are visually grouped (either as a sub-group or consecutive
|
||||||
|
items with a divider label)
|
||||||
|
- [ ] Analytics section handles its now-empty state gracefully
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Add "VEX & Exceptions" grouping in Security & Risk nav
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-19 menu graph, VEX Hub and Exceptions should be grouped under a "VEX & Exceptions"
|
||||||
|
sub-group in the nav (similar to how they are grouped in packs). Currently they appear as flat
|
||||||
|
items: "VEX Hub" and "Exceptions".
|
||||||
|
|
||||||
|
Options:
|
||||||
|
1. Add a collapsible sub-group "VEX & Exceptions" containing both items
|
||||||
|
2. Add a divider label "VEX & Exceptions" above the two items (no collapse)
|
||||||
|
|
||||||
|
Either approach is acceptable. Visually they should be distinct from Findings/Vulnerabilities/
|
||||||
|
SBOM Data as a governance/disposition layer.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] VEX Hub and Exceptions are visually grouped in the sidebar
|
||||||
|
- [ ] Group label reads "VEX & Exceptions" (or similar)
|
||||||
|
- [ ] Navigation behavior is unchanged (both still navigate to the same routes)
|
||||||
|
- [ ] Sidebar active state highlights correctly for both items
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Add Finding Detail page (explicit decision case-file)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-19.4, there is no explicit "Finding Detail" page — users navigate from the Findings
|
||||||
|
list to a row without a dedicated URL. Add a Finding Detail page at:
|
||||||
|
`/security/findings/:findingId`
|
||||||
|
|
||||||
|
Required sections per pack-19.4:
|
||||||
|
1. Header: CVE, Package, Severity, Component, Digest, Environment
|
||||||
|
2. Reachability: REACHABLE/NOT REACHABLE, confidence, Hybrid B/I/R evidence (age)
|
||||||
|
3. Impact: affected environments, affected bundle versions, blocked approvals count
|
||||||
|
4. Disposition: VEX statements present (none/linked), Exceptions active (none/linked)
|
||||||
|
5. Actions: [Create Exception Request] [Search/Import VEX] [Export as Evidence]
|
||||||
|
|
||||||
|
Route title: "Finding Detail - StellaOps" (or "{CVE-ID} - StellaOps" once data loads)
|
||||||
|
Breadcrumb: Security & Risk > Findings Explorer > {CVE-ID}
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/security/findings/:findingId` exists and renders
|
||||||
|
- [ ] All 5 sections present
|
||||||
|
- [ ] B/I/R evidence age shown per source (with ✓/✗ indicators)
|
||||||
|
- [ ] Blocked approvals count links to Approvals filtered to this finding
|
||||||
|
- [ ] All 3 action buttons present (actions can be stub)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Add Vulnerability Detail page (CVE dossier)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-19.6, there is no explicit Vulnerability Detail page — CVEs are only shown in a
|
||||||
|
list view. Add a Vulnerability Detail page at:
|
||||||
|
`/security/vulnerabilities/:cveId`
|
||||||
|
|
||||||
|
Required sections per pack-19.6:
|
||||||
|
1. Header: CVE ID, Package, Severity, EPSS/KEV (if feeds provide it)
|
||||||
|
2. Data confidence banner (if NVD stale)
|
||||||
|
3. Impact summary: impacted envs count, finding counts by reachability class, affected
|
||||||
|
components + bundle versions
|
||||||
|
4. Disposition: VEX (none/linked), Exceptions (none/linked)
|
||||||
|
5. Actions: [Open Findings] [Open SBOM Graph] [Create Exception] [Export Report]
|
||||||
|
|
||||||
|
Route title: "{CVE-ID} - StellaOps"
|
||||||
|
Breadcrumb: Security & Risk > Vulnerabilities Explorer > {CVE-ID}
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/security/vulnerabilities/:cveId` exists and renders
|
||||||
|
- [ ] All 5 sections present
|
||||||
|
- [ ] Impact summary shows finding counts by reachability class (reachable/not/unknown)
|
||||||
|
- [ ] All 4 action buttons present
|
||||||
|
- [ ] Data confidence banner shown when feeds are stale
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Upgrade Risk Overview with Data Confidence banner
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-19.2, the Security Risk Overview must have a Data Confidence banner at the top:
|
||||||
|
"Data Confidence: WARN (NVD stale 3h; SBOM rescan FAIL; Jenkins DEGRADED; DLQ runtime 1,230)
|
||||||
|
[Open Ops → Data Integrity]"
|
||||||
|
|
||||||
|
The current overview page lacks this banner. The banner must appear before the snapshot
|
||||||
|
metrics. Until SPRINT_20260219_023 (Data Integrity) is implemented, the banner can be stubbed
|
||||||
|
as "Data confidence monitoring not yet available. [Open Operations]".
|
||||||
|
|
||||||
|
Also add to the overview:
|
||||||
|
- "Critical Reachable by Environment" breakdown (per-env CritR count row)
|
||||||
|
- "SBOM posture" summary card (coverage %, freshness, pending scans count)
|
||||||
|
- "VEX & Exceptions" summary card (statement count, expiring exceptions count)
|
||||||
|
|
||||||
|
These sections may already partially exist — enhance them per the pack-19.2 ASCII spec.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Data Confidence banner renders (stub state acceptable until Data Integrity lands)
|
||||||
|
- [ ] "Critical Reachable by Environment" section renders
|
||||||
|
- [ ] SBOM posture card renders
|
||||||
|
- [ ] VEX & Exceptions card renders
|
||||||
|
- [ ] [Open Data Integrity] link in banner navigates correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Add Advisory Sources page to Security & Risk (placeholder)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: S00_advisory_sources_spec.md + backend contracts from Concelier/Policy
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per S00_advisory_sources_spec.md and pack-19 menu graph, "Advisory Sources" belongs under
|
||||||
|
Security & Risk (decision-impact view). The ownership split:
|
||||||
|
- Security & Risk: decision-impact view (how sources affect gate verdicts)
|
||||||
|
- Integrations: connector config (how sources are connected and synced)
|
||||||
|
- Platform Ops: freshness ops (Data Integrity → Feeds Freshness page)
|
||||||
|
|
||||||
|
Add a placeholder "Advisory Sources" nav item and page:
|
||||||
|
Route: `/security/advisory-sources`
|
||||||
|
Title: "Advisory Sources - StellaOps"
|
||||||
|
Nav: Add after "Risk Overview" in Security & Risk sidebar
|
||||||
|
|
||||||
|
The page should show at minimum:
|
||||||
|
- Advisory sources list (OSV, NVD, CISA KEV, etc.)
|
||||||
|
- For each: how it affects gate verdicts (which gates use it, what threshold)
|
||||||
|
- Freshness status badge with link to Platform Ops → Feeds Freshness
|
||||||
|
- Connector config link to Integrations
|
||||||
|
|
||||||
|
If the backend endpoint is not ready, render a "Not yet configured" empty state with
|
||||||
|
ownership explanation.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/security/advisory-sources` exists and renders
|
||||||
|
- [ ] "Advisory Sources" appears in Security & Risk nav
|
||||||
|
- [ ] Page explains the ownership split (decision-impact here, config in Integrations)
|
||||||
|
- [ ] [Open Integrations] and [Open Feeds Freshness] links present
|
||||||
|
- [ ] Empty state is meaningful (not blank)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-08 — Fix blank Security sub-pages (Findings, VEX Hub)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
QA sweep (2026-02-19 session 3) confirmed two Security sub-pages render a completely empty
|
||||||
|
`<main>`:
|
||||||
|
|
||||||
|
| Route | Observed title | Blank? |
|
||||||
|
|---|---|---|
|
||||||
|
| `/security/findings` | "Security Overview - StellaOps" (wrong) | YES — main is empty |
|
||||||
|
| `/security/vex` | "Stella Ops" (no title) | YES — main is empty |
|
||||||
|
|
||||||
|
Contrast: `/security/overview` and `/security/exceptions` render content; `/security/
|
||||||
|
vulnerabilities` renders a minimal stub; `/security/sbom` renders a stub with message.
|
||||||
|
|
||||||
|
Root cause investigation per page:
|
||||||
|
- Check route → component mapping for findings and vex routes
|
||||||
|
- Check for lazy-loading failures (browser console)
|
||||||
|
- The title "Security Overview - StellaOps" on the Findings page suggests the Findings
|
||||||
|
route may be falling back to the parent route's component
|
||||||
|
- The title "Stella Ops" on VEX Hub (no suffix) suggests VEX route has no component mapped
|
||||||
|
|
||||||
|
Fix: Implement or stub the missing components so pages render a heading + description at
|
||||||
|
minimum.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] `/security/findings` renders content (Findings Explorer list with empty state)
|
||||||
|
- [ ] `/security/vex` renders content (VEX Hub list with empty state)
|
||||||
|
- [ ] Neither page shows blank `<main>`
|
||||||
|
- [ ] Titles follow "Findings - StellaOps" and "VEX Hub - StellaOps" pattern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-09 — Fix Security sub-page title strategy (all sub-pages show wrong title)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
QA sweep confirmed that ALL Security sub-pages (Findings, Vulnerabilities, SBOM Graph,
|
||||||
|
VEX Hub, Exceptions) show incorrect titles:
|
||||||
|
- Findings: "Security Overview - StellaOps" (uses parent page title)
|
||||||
|
- Vulnerabilities: "Security Overview - StellaOps"
|
||||||
|
- SBOM Graph: "StellaOps" (no page title at all)
|
||||||
|
- VEX Hub: "Stella Ops" (no title)
|
||||||
|
|
||||||
|
Only the Overview page has a correct title ("Security Overview - StellaOps").
|
||||||
|
|
||||||
|
Root cause: The Angular route `title` property is not set on Security child routes. The
|
||||||
|
parent route title propagates to children.
|
||||||
|
|
||||||
|
Fix: Add `title` to each Security child route definition.
|
||||||
|
|
||||||
|
Required titles per page:
|
||||||
|
| Route | Required Title |
|
||||||
|
|---|---|
|
||||||
|
| `/security` (overview) | Risk Overview - StellaOps |
|
||||||
|
| `/security/findings` | Findings Explorer - StellaOps |
|
||||||
|
| `/security/vulnerabilities` | Vulnerabilities Explorer - StellaOps |
|
||||||
|
| `/security/sbom` | SBOM Graph - StellaOps |
|
||||||
|
| `/security/vex` | VEX Hub - StellaOps |
|
||||||
|
| `/security/exceptions` | Exceptions - StellaOps |
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Each Security sub-page has its own specific title
|
||||||
|
- [ ] No Security page shows "Security Overview - StellaOps" except the Overview page itself
|
||||||
|
- [ ] Title follows "{Page Name} - StellaOps" pattern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-10 — Fix Security sub-pages missing breadcrumb root crumb
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01 (rename "Security" → "Security & Risk" first, then use that label)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
QA sweep confirmed that ALL Security sub-pages have broken breadcrumbs — the root "Security"
|
||||||
|
crumb is missing. Each page shows only its own name:
|
||||||
|
|
||||||
|
- Findings: breadcrumb shows just "Findings" (no "Security" parent)
|
||||||
|
- Vulnerabilities: breadcrumb shows just "Vulnerabilities"
|
||||||
|
- SBOM Graph: breadcrumb shows just "SBOM Graph"
|
||||||
|
- Exceptions: breadcrumb shows just "Exceptions"
|
||||||
|
|
||||||
|
The breadcrumb should show: Security & Risk > {Page Name}
|
||||||
|
|
||||||
|
Fix: Add breadcrumb data to each Security child route (or ensure the parent route's
|
||||||
|
breadcrumb data propagates correctly).
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All Security sub-pages show "Security & Risk > {Page Name}" breadcrumb
|
||||||
|
- [ ] Breadcrumb root "Security & Risk" links to `/security`
|
||||||
|
- [ ] No Security sub-page shows a single-item breadcrumb
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack-19 cross-reference. Security nav is flat with old labels. SBOM Lake confirmed under Analytics (wrong domain). No Finding Detail or Vulnerability Detail pages. Advisory Sources absent. | QA |
|
||||||
|
| 2026-02-19 | Full Security section re-sweep. Added TASK-08 (Findings + VEX blank pages), TASK-09 (all Security sub-pages have wrong/missing title — parent title propagating to all children), TASK-10 (all Security sub-pages missing root breadcrumb). Also confirmed Analytics > SBOM Lake is blank (only Analytics page, confirms TASK-02 priority). | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Analytics section**: Moving SBOM Lake to Security & Risk may leave Analytics empty.
|
||||||
|
Check if there are other Analytics sub-pages beyond SBOM Lake. The "Analytics" nav section
|
||||||
|
may need to be removed or converted to a redirect.
|
||||||
|
- **Advisory Sources ownership**: Per S00_advisory_sources_spec.md, Advisory Sources is owned
|
||||||
|
by three domains (Security, Integrations, Platform Ops) with different views. The Security
|
||||||
|
view is decision-impact only. Do not show connector config on this page.
|
||||||
|
- **Data confidence banner source**: Until SPRINT_20260219_023 (Data Integrity) is implemented,
|
||||||
|
the banner on Risk Overview will use stub data. Design the banner component to accept a
|
||||||
|
nullable data contract so it gracefully degrades.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (rename) and TASK-03 (VEX grouping) are quick wins — implement first.
|
||||||
|
- TASK-02 (SBOM Lake migration) requires coordination with any Analytics-using code.
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
# Sprint 20260219-028 — QA Gap: Release Control — Bundle Organizer (New Feature, Not Implemented)
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs packs 04, 08, 12, 13, 21
|
||||||
|
(Release Control + Bundle Organizer spec).
|
||||||
|
- The Bundle Organizer is the most critical new capability in the v2 IA. It is entirely
|
||||||
|
absent from the live app. Navigating to `/release-control/bundles` redirects to Control
|
||||||
|
Plane (no route registered).
|
||||||
|
- Additional missing Release Control features:
|
||||||
|
- Bundle Catalog (list of bundles with security posture)
|
||||||
|
- Bundle Version Detail (with Manifest, Security, Reachability, Changelog, Evidence tabs)
|
||||||
|
- Regions & Environments as first-class Release Control section
|
||||||
|
- Hotfixes as a dedicated queue
|
||||||
|
- Governance & Policy section (moved from Settings Policy Governance)
|
||||||
|
- Release Control Setup (Targets, Agents, Workflows as Release Control sub-pages)
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/release-control/`
|
||||||
|
- Expected evidence: Bundle Catalog, Bundle Organizer wizard, Bundle Version Detail, and
|
||||||
|
Regions & Environments first-class section all exist and render.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Packs 04, 08, 12, 13, 21 are authoritative specs.
|
||||||
|
- This sprint is a **scoping and architecture sprint** — the feature is too large for a single
|
||||||
|
sprint. TASK-01 through TASK-04 define the architecture and route structure. Subsequent
|
||||||
|
implementation tasks should be created as child sprints.
|
||||||
|
- SPRINT_20260219_029 (root nav IA restructure) is a dependency for the Release Control root
|
||||||
|
menu item to appear in the sidebar.
|
||||||
|
- TASK-01 (route shell) is a prerequisite for all other tasks.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-12.md` — Bundle Organizer wizard spec (CRITICAL)
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-13.md` — Releases + Approvals bundle-version driven spec
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-08.md` — Release Control screen details
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-21.md` — Bundle Organizer ASCII mock + integrations menu
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/release-control/` — release control feature dir
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Define Release Control route structure and create route shell
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: SPRINT_20260219_029 TASK-01 (Release Control root nav entry)
|
||||||
|
Owners: FE Developer / Architect
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Define the complete route structure for the Release Control root menu area. Per packs 08, 11,
|
||||||
|
21, Release Control becomes a root menu with the following top-level sub-sections:
|
||||||
|
|
||||||
|
```
|
||||||
|
/release-control → Control Plane (same as current /)
|
||||||
|
/release-control/bundles → Bundle Catalog
|
||||||
|
/release-control/bundles/:bundleId → Bundle Detail
|
||||||
|
/release-control/bundles/:bundleId/organizer → Bundle Organizer
|
||||||
|
/release-control/bundles/:bundleId/versions/:versionId → Bundle Version Detail
|
||||||
|
/release-control/releases → Releases list (same as /releases)
|
||||||
|
/release-control/approvals → Approvals queue (same as /approvals)
|
||||||
|
/release-control/regions → Regions & Environments root
|
||||||
|
/release-control/regions/:region → Region Detail
|
||||||
|
/release-control/regions/:region/environments/:env → Environment Detail
|
||||||
|
/release-control/governance → Governance & Policy hub
|
||||||
|
/release-control/hotfixes → Hotfixes queue
|
||||||
|
/release-control/setup → Setup hub (Targets, Agents, Workflows)
|
||||||
|
/release-control/setup/environments → Environments & Promotion Paths
|
||||||
|
/release-control/setup/targets → Targets & Agents
|
||||||
|
/release-control/setup/workflows → Workflows
|
||||||
|
```
|
||||||
|
|
||||||
|
For each route: register the route with a title, create a stub component with a heading,
|
||||||
|
add the route to the breadcrumb strategy.
|
||||||
|
|
||||||
|
Note: Legacy routes (`/releases`, `/approvals`) must continue to work via redirects.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All routes registered without 404
|
||||||
|
- [ ] Each route shows at minimum a page heading (stub)
|
||||||
|
- [ ] `/release-control/bundles` renders (not redirect to root)
|
||||||
|
- [ ] Legacy `/releases` and `/approvals` redirect to canonical routes
|
||||||
|
- [ ] Breadcrumbs correct for all new routes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Implement Bundle Catalog page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-08 and pack-12, the Bundle Catalog is the list of all bundles (by product/team/
|
||||||
|
repo-set) with security posture per bundle.
|
||||||
|
|
||||||
|
Page layout:
|
||||||
|
- Filter bar: team ▾, status ▾, security posture ▾
|
||||||
|
- Bundle cards or table rows:
|
||||||
|
| Bundle Name | Latest Version | Status | SBOM Coverage | CritR Findings | Evidence |
|
||||||
|
- Per bundle: [Open Bundle] [Create New Version] actions
|
||||||
|
|
||||||
|
The Bundle is an organizational concept — it groups component versions and defines the unit
|
||||||
|
of promotion. Bundles are NOT releases; they are the source material for creating releases.
|
||||||
|
|
||||||
|
Route: `/release-control/bundles`
|
||||||
|
Title: "Bundle Catalog - StellaOps"
|
||||||
|
Nav: "Bundles" item under Release Control
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders at `/release-control/bundles`
|
||||||
|
- [ ] Bundle list table/cards render (empty state: "No bundles yet. [+ Create Bundle]")
|
||||||
|
- [ ] [+ Create Bundle] action present
|
||||||
|
- [ ] Security posture column visible
|
||||||
|
- [ ] "Bundles" appears in Release Control sidebar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Implement Bundle Organizer multi-step wizard (core feature)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01, TASK-02
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-12, the Bundle Organizer is a 6-step wizard for creating a new Bundle Version:
|
||||||
|
|
||||||
|
Step 1: Base version (choose existing version to fork or create new)
|
||||||
|
Step 2: Select component versions (digest-first table with Hybrid Reachability columns)
|
||||||
|
- Columns: Service, Image Digest, Service Version, SBOM status, Reachability, Gate status
|
||||||
|
Step 3: Config contracts (Vault/Consul variable requirements per service, per region/env)
|
||||||
|
Step 4: Changelog preview per repository (pull from SCM integration)
|
||||||
|
Step 5: Validate (policy, SBOM, feeds, reachability coverage) — run gate pre-check
|
||||||
|
Step 6: Finalize → immutable Bundle Version (compute manifest digest)
|
||||||
|
|
||||||
|
The wizard must:
|
||||||
|
- Allow saving progress as draft between steps
|
||||||
|
- Show validation errors inline at each step
|
||||||
|
- Show a summary sidebar of current selections throughout
|
||||||
|
- On Step 5 failure: show which gates block and allow "continue with exceptions" option
|
||||||
|
|
||||||
|
Route: `/release-control/bundles/:bundleId/organizer` (or `/organizer/new`)
|
||||||
|
Title: "Bundle Organizer - StellaOps"
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] 6-step wizard renders and allows forward/back navigation
|
||||||
|
- [ ] Step 2 shows component digest table with SBOM and reachability columns
|
||||||
|
- [ ] Step 3 shows Vault/Consul binding requirements per service
|
||||||
|
- [ ] Step 4 shows per-repo changelog (stub data acceptable)
|
||||||
|
- [ ] Step 5 shows validation results with gate breakdown
|
||||||
|
- [ ] Step 6 completes and creates an immutable Bundle Version with computed digest
|
||||||
|
- [ ] Draft save/restore works between sessions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Implement Bundle Version Detail page (tabbed)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-12, Bundle Version Detail has tabs:
|
||||||
|
[Manifest] [Security] [Reachability] [Changelog] [Evidence] [Promotions] [Diff]
|
||||||
|
|
||||||
|
Tab content per pack-12:
|
||||||
|
- **Manifest**: component list with digest + version label, computed bundle manifest digest,
|
||||||
|
config contract references per service
|
||||||
|
- **Security**: findings summary by reachability class, SBOM coverage, top CVEs
|
||||||
|
- **Reachability**: B/I/R matrix for all components
|
||||||
|
- **Changelog**: per-repo PR/commit summary
|
||||||
|
- **Evidence**: DSSE envelope status, Rekor receipt, proof chain ref
|
||||||
|
- **Promotions**: history of this version's promotion runs + pending approvals
|
||||||
|
- **Diff**: compare to another bundle version (previous version selector)
|
||||||
|
|
||||||
|
Route: `/release-control/bundles/:bundleId/versions/:versionId`
|
||||||
|
Title: "{Bundle} v{version} - StellaOps"
|
||||||
|
Breadcrumb: Release Control > Bundles > {Bundle} > Version {version}
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 7 tabs render (stub content acceptable)
|
||||||
|
- [ ] Manifest tab shows component list with digests
|
||||||
|
- [ ] Bundle manifest digest displayed prominently in the header
|
||||||
|
- [ ] Security tab shows CritR summary
|
||||||
|
- [ ] Promotions tab shows promotion history for this version
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Implement Regions & Environments as first-class Release Control section
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-11 and pack-18, Regions & Environments is now a first-class section under Release
|
||||||
|
Control. The current app has environments as flat nodes on the Control Plane without their
|
||||||
|
own section.
|
||||||
|
|
||||||
|
Required pages:
|
||||||
|
1. **Regions & Environments root** (`/release-control/regions`): list of regions with health
|
||||||
|
summary per region. Each region shows env count, overall health, SBOM posture.
|
||||||
|
2. **Region Detail** (`/release-control/regions/:region`): NEW — environments grouped under
|
||||||
|
this region with pipeline view (Dev→Stage→Prod), region health summary.
|
||||||
|
3. **Environment Detail** (`/release-control/regions/:region/environments/:env`):
|
||||||
|
Standardized header + 8 tabs as per SPRINT_20260219_025.
|
||||||
|
|
||||||
|
Nav: Add "Regions & Environments" under Release Control sidebar.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Regions list page renders at `/release-control/regions`
|
||||||
|
- [ ] Region Detail page renders with environment pipeline view
|
||||||
|
- [ ] Environment Detail links from Region Detail
|
||||||
|
- [ ] "Regions & Environments" appears in Release Control sidebar
|
||||||
|
- [ ] Breadcrumb: Release Control > Regions & Environments > {Region} > {Env}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Implement Hotfixes dedicated queue page (stub)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-04 and pack-08, Hotfixes are a dedicated queue (first-class, not just a release
|
||||||
|
type). The current app has hotfixes as a release type (seen in the dashboard as "Hotfix
|
||||||
|
1.2.4") but no dedicated Hotfixes queue page.
|
||||||
|
|
||||||
|
Route: `/release-control/hotfixes`
|
||||||
|
Title: "Hotfixes - StellaOps"
|
||||||
|
Nav: "Hotfixes" item under Release Control
|
||||||
|
|
||||||
|
Page layout:
|
||||||
|
- Active hotfixes list: bundle name, target env, urgency, gates status, [Review] action
|
||||||
|
- "No active hotfixes" empty state
|
||||||
|
- [+ Create Hotfix] action
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/release-control/hotfixes` exists and renders
|
||||||
|
- [ ] "Hotfixes" appears in Release Control sidebar
|
||||||
|
- [ ] List renders with empty state
|
||||||
|
- [ ] [+ Create Hotfix] action present
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Create Governance & Policy hub under Release Control
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01; coordinate with SPRINT_20260219_029 for Settings > Policy migration
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-09 and pack-21, Governance & Policy moves from Settings → Administration to Release
|
||||||
|
Control → Governance. The canonical location for policy baseline configuration, governance
|
||||||
|
rules, simulation, and exception workflow is Release Control.
|
||||||
|
|
||||||
|
Current location: `/settings/policy`
|
||||||
|
Target location: `/release-control/governance`
|
||||||
|
|
||||||
|
For this sprint: Create the Governance hub at `/release-control/governance` with sub-pages:
|
||||||
|
- Policy Baselines (per region/env scoped)
|
||||||
|
- Governance Rules
|
||||||
|
- Policy Simulation
|
||||||
|
- Exception Workflow
|
||||||
|
|
||||||
|
These are the same pages as the current Policy Governance — move them and add redirects.
|
||||||
|
Execution is blocked on SPRINT_20260219_029 deciding the Settings decomposition approach.
|
||||||
|
|
||||||
|
This task is a **planning + stub task**: create the route shell and document the migration
|
||||||
|
plan. Do not remove from Settings until SPRINT_20260219_029 landing is confirmed.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Route `/release-control/governance` exists with sub-routes registered
|
||||||
|
- [ ] "Governance" appears in Release Control sidebar
|
||||||
|
- [ ] Stubs render for Baselines, Rules, Simulation, Exception Workflow
|
||||||
|
- [ ] Migration plan from `/settings/policy` documented in Decisions & Risks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Pack 04/08/12/13/21 cross-reference. Bundle Organizer entirely absent — `/release-control/bundles` redirects to root. Regions & Environments, Hotfixes, and Governance sections also absent. This is the largest feature gap in the v2 IA. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Bundle Organizer is the most critical missing feature**. It is the core workflow for
|
||||||
|
composing immutable, security-postured release bundles. Without it, the bundle-version-driven
|
||||||
|
Approvals and Promotions spec cannot be fully realized.
|
||||||
|
- **Bundle digest is authoritative**: Per the spec, "Bundle Version" is a human-friendly
|
||||||
|
label; the authoritative identity is the content-addressed bundle manifest digest. This must
|
||||||
|
be enforced in the Bundle Organizer finalize step.
|
||||||
|
- **Vault/Consul config in Bundle Organizer (Step 3)**: The config snapshot requires
|
||||||
|
integration with the live Vault/Consul integration. If those integrations are not configured
|
||||||
|
in the test environment, Step 3 must show a graceful "No config sources connected" state.
|
||||||
|
- **Scope management**: This sprint documents 7 tasks across a very large feature surface.
|
||||||
|
Each task should spawn its own follow-on implementation sprint. This sprint is the scoping
|
||||||
|
exercise only — do not attempt all 7 tasks in one sprint cycle.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (route shell) must land first.
|
||||||
|
- TASK-02 (Bundle Catalog) and TASK-05 (Regions & Environments) are prerequisites for the
|
||||||
|
Dashboard pipeline node links (SPRINT_20260219_022 TASK-02).
|
||||||
|
- TASK-03 (Bundle Organizer wizard) is the most complex task — plan as a dedicated sprint.
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
# Sprint 20260219-029 — QA Gap: Root Nav IA Restructure — Settings Decomposition + Root Renames
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Cross-referenced live app vs packs 05, 09, 21 and
|
||||||
|
S00_handoff_packet.md (frozen v2 IA decisions).
|
||||||
|
- The live app has a v1 root nav: Control Plane, Releases, Approvals, Security, Analytics,
|
||||||
|
Evidence, Operations, Settings.
|
||||||
|
- The v2 canonical root nav (frozen in S00 handoff): Dashboard, Release Control, Security &
|
||||||
|
Risk, Evidence & Audit, Integrations, Platform Ops, Administration.
|
||||||
|
- The "Settings" section must be decomposed — its items migrate to:
|
||||||
|
- Release Control Setup (Environments, Targets, Agents, Workflows)
|
||||||
|
- Release Control Governance (Policy Governance)
|
||||||
|
- Evidence & Audit (Trust & Signing)
|
||||||
|
- Integrations root (Integrations connector hub)
|
||||||
|
- Platform Ops (System/Platform Admin)
|
||||||
|
- Administration (Identity & Access, Tenant & Branding, Notifications, Usage & Limits)
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/layout/app-sidebar/` and
|
||||||
|
`src/Web/StellaOps.Web/src/app/routes/`
|
||||||
|
- Expected evidence: Root nav shows all 7 v2 canonical roots in correct order. Settings
|
||||||
|
items are accessible from their new canonical locations with redirects from old paths.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- This is the **highest-impact structural sprint** — it changes the root nav for every user.
|
||||||
|
- Must coordinate with ALL other v2 IA sprints (022-028) to avoid conflicting route changes.
|
||||||
|
- TASK-01 (nav audit) must precede all other tasks.
|
||||||
|
- TASK-02 (Integrations as root) and TASK-03 (Release Control as root) are the highest
|
||||||
|
priority roots to add — other sprints depend on them.
|
||||||
|
- TASK-07 (remove Settings items) must be LAST — only after redirects are confirmed.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_handoff_packet.md` — frozen IA decisions (CRITICAL)
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md` — v1→v2 route mapping
|
||||||
|
- `docs/modules/ui/v2-rewire/S00_nav_rendering_policy.md` — rendering rules (do-not list)
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-21.md` — Administration + Integrations screen specs
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-05.md` — Integrations root + Administration spec
|
||||||
|
- `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts` — sidebar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Audit current sidebar and create v1→v2 nav item mapping
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer / Project Manager
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Read `app-sidebar.component.ts` and the route files to produce a complete current-state nav
|
||||||
|
audit. For each current nav item document:
|
||||||
|
- Current label, route, icon
|
||||||
|
- Target v2 label, route, icon (per spec)
|
||||||
|
- Migration action: RENAME / MOVE / ADD / KEEP / REMOVE-AFTER-REDIRECT
|
||||||
|
|
||||||
|
Required mapping (per S00_handoff_packet.md frozen decisions):
|
||||||
|
|
||||||
|
| Current | Action | v2 Target |
|
||||||
|
|---|---|---|
|
||||||
|
| Control Plane (/) | RENAME | Dashboard (/) |
|
||||||
|
| Releases (/releases) | MOVE | Release Control > Releases |
|
||||||
|
| Approvals (/approvals) | MOVE | Release Control > Approvals (shortcut) |
|
||||||
|
| Security > Overview | RENAME + MOVE | Security & Risk > Risk Overview |
|
||||||
|
| Security > Findings | RENAME | Security & Risk > Findings Explorer |
|
||||||
|
| Security > Vulnerabilities | RENAME | Security & Risk > Vulnerabilities Explorer |
|
||||||
|
| Security > SBOM Graph | MOVE | Security & Risk > SBOM Data > SBOM Graph |
|
||||||
|
| Security > VEX Hub | MOVE | Security & Risk > VEX & Exceptions > VEX Hub |
|
||||||
|
| Security > Exceptions | MOVE | Security & Risk > VEX & Exceptions > Exceptions |
|
||||||
|
| Analytics > SBOM Lake | MOVE | Security & Risk > SBOM Data > SBOM Lake |
|
||||||
|
| Analytics (root) | REMOVE | (empty after SBOM Lake moves) |
|
||||||
|
| Evidence > Packets | RENAME | Evidence & Audit > Evidence Packs |
|
||||||
|
| Evidence > Proof Chains | KEEP | Evidence & Audit > Proof Chains |
|
||||||
|
| Evidence > Replay/Verify | KEEP | Evidence & Audit > Replay & Verify |
|
||||||
|
| Evidence > Export | RENAME | Evidence & Audit > Export Center |
|
||||||
|
| Operations > Orchestrator | KEEP | Platform Ops > Orchestrator |
|
||||||
|
| Operations > Scheduler | KEEP | Platform Ops > Scheduler |
|
||||||
|
| Operations > Quotas | KEEP | Platform Ops > Quotas |
|
||||||
|
| Operations > Dead Letter | KEEP | Platform Ops > Dead Letter |
|
||||||
|
| Operations > Platform Health | KEEP | Platform Ops > Platform Health |
|
||||||
|
| Operations > Feeds | KEEP | Platform Ops > Feeds & AirGap Ops |
|
||||||
|
| Operations (root) | RENAME | Platform Ops |
|
||||||
|
| Settings > Integrations | MOVE | Integrations (ROOT) |
|
||||||
|
| Settings > Release Control | MOVE | Release Control > Setup |
|
||||||
|
| Settings > Trust & Signing | MOVE | Evidence & Audit > Trust & Signing |
|
||||||
|
| Settings > Security Data | MOVE | Integrations > Feeds + Security & Risk > Advisory Sources |
|
||||||
|
| Settings > Admin (IAM) | MOVE | Administration > Identity & Access |
|
||||||
|
| Settings > Branding | MOVE | Administration > Tenant & Branding |
|
||||||
|
| Settings > Usage & Limits | MOVE | Administration > Usage & Limits |
|
||||||
|
| Settings > Notifications | MOVE | Administration > Notifications |
|
||||||
|
| Settings > Policy | MOVE | Release Control > Governance (or Administration > Policy) |
|
||||||
|
| Settings > System | MOVE | Administration > System |
|
||||||
|
| Settings > Offline | KEEP | Administration > Offline Settings |
|
||||||
|
| Settings (root) | RENAME | Administration |
|
||||||
|
|
||||||
|
New roots to ADD:
|
||||||
|
- Release Control (new root, promoted from Settings > Release Control)
|
||||||
|
- Integrations (new root, promoted from Settings > Integrations)
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Full current nav item inventory documented in this sprint's Decisions & Risks
|
||||||
|
- [ ] v1→v2 mapping confirmed against S00_route_deprecation_map.md
|
||||||
|
- [ ] Any discrepancies between S00 map and current live app noted as gaps
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Add "Integrations" as a root nav section
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-05 and pack-21, "Integrations" becomes a root menu with sub-items:
|
||||||
|
- Overview (connector hub) → `/integrations`
|
||||||
|
- SCM → `/integrations/scm`
|
||||||
|
- CI/CD → `/integrations/ci-cd`
|
||||||
|
- Registries → `/integrations/registries`
|
||||||
|
- Secrets → `/integrations/secrets`
|
||||||
|
- Targets / Runtimes → `/integrations/targets`
|
||||||
|
- Feeds → `/integrations/feeds`
|
||||||
|
- Notification Providers → `/integrations/notifications`
|
||||||
|
|
||||||
|
The current Settings > Integrations page at `/settings/integrations` must:
|
||||||
|
1. Be moved to `/integrations` (new canonical route)
|
||||||
|
2. Have a redirect `/settings/integrations` → `/integrations`
|
||||||
|
3. The Settings > Integration Detail `/settings/integrations/:id` → `/integrations/:id`
|
||||||
|
|
||||||
|
Add "Integrations" to the root nav between "Evidence & Audit" and "Platform Ops".
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "Integrations" appears in root nav
|
||||||
|
- [ ] `/integrations` renders the integrations hub (or the existing Settings Integrations page)
|
||||||
|
- [ ] `/settings/integrations` redirects to `/integrations`
|
||||||
|
- [ ] `/settings/integrations/:id` redirects to `/integrations/:id`
|
||||||
|
- [ ] Sub-section stubs registered (SCM, CI/CD, etc.) — empty states OK
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Add "Release Control" as a root nav section
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01; coordinate with SPRINT_20260219_028 TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per S00_handoff_packet.md, "Release Control" is a frozen root domain. Add it to the root nav
|
||||||
|
between "Dashboard" and "Security & Risk".
|
||||||
|
|
||||||
|
Release Control sub-items per nav rendering policy (S00_nav_rendering_policy.md):
|
||||||
|
- Control Plane (the dashboard / control center — same as root Dashboard)
|
||||||
|
- Releases (shortcut)
|
||||||
|
- Approvals (shortcut, with pending count badge)
|
||||||
|
- Bundles (new) → `/release-control/bundles`
|
||||||
|
- Regions & Environments → `/release-control/regions`
|
||||||
|
- Governance → `/release-control/governance`
|
||||||
|
- Hotfixes → `/release-control/hotfixes`
|
||||||
|
- Setup → `/release-control/setup`
|
||||||
|
|
||||||
|
The existing top-level "Releases" and "Approvals" nav items may remain as shortcuts or be
|
||||||
|
moved under Release Control depending on the nav rendering policy decision. Per
|
||||||
|
S00_nav_rendering_policy.md: "Releases and Approvals may be direct nav shortcuts under
|
||||||
|
Release Control group" — implement them as expanded sub-items of Release Control.
|
||||||
|
|
||||||
|
Legacy routes `/releases` and `/approvals` must remain as redirects.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] "Release Control" appears in root nav between Dashboard and Security & Risk
|
||||||
|
- [ ] Release Control expands to show sub-items (minimum: Releases, Approvals, Bundles,
|
||||||
|
Regions & Environments)
|
||||||
|
- [ ] Top-level "Releases" and "Approvals" items removed from root (kept as shortcuts in
|
||||||
|
Release Control group)
|
||||||
|
- [ ] `/releases` → `/release-control/releases` redirect in place
|
||||||
|
- [ ] `/approvals` → `/release-control/approvals` redirect in place
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Rename "Operations" to "Platform Ops"
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per S00_handoff_packet.md, the canonical root domain is "Platform Ops" (formerly Operations).
|
||||||
|
Update:
|
||||||
|
- Root nav button: "Operations" → "Platform Ops"
|
||||||
|
- All breadcrumbs using "Operations" → "Platform Ops"
|
||||||
|
- All page titles: "... - StellaOps" operations-prefixed pages → use "Platform Ops" prefix
|
||||||
|
- Route prefix: `/operations/...` — KEEP as-is (do not change URLs, only labels)
|
||||||
|
Legacy URLs under `/operations/` should NOT be renamed — only the nav label changes
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Root nav shows "Platform Ops"
|
||||||
|
- [ ] All breadcrumbs use "Platform Ops"
|
||||||
|
- [ ] `/operations/...` routes still work (unchanged)
|
||||||
|
- [ ] No visible "Operations" label remains in the nav
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Rename "Evidence" to "Evidence & Audit" in root nav
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per S00_handoff_packet.md, the canonical root domain is "Evidence & Audit". Update:
|
||||||
|
- Root nav button: "Evidence" → "Evidence & Audit"
|
||||||
|
- All breadcrumbs using "Evidence" → "Evidence & Audit"
|
||||||
|
- Route prefix: `/evidence/...` — KEEP (no URL changes)
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Root nav shows "Evidence & Audit"
|
||||||
|
- [ ] Breadcrumbs updated
|
||||||
|
- [ ] Existing routes unaffected
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Rename "Settings" to "Administration" and add Administration hub
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01; coordinate with SPRINT_20260219_026 (Trust migration) and SPRINT_20260219_028 (Policy migration)
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-21, the "Settings" root becomes "Administration". Administration contains ONLY:
|
||||||
|
- Administration Overview (new hub page) — `/administration`
|
||||||
|
- Identity & Access (formerly `/settings/admin`) — `/administration/identity`
|
||||||
|
- Tenant & Branding (formerly `/settings/branding`) — `/administration/branding`
|
||||||
|
- Notifications (formerly `/settings/notifications`) — `/administration/notifications`
|
||||||
|
- Usage & Limits (formerly `/settings/usage`) — `/administration/usage`
|
||||||
|
- System (formerly `/settings/system`) — `/administration/system`
|
||||||
|
- Offline Settings (formerly `/settings/offline`) — `/administration/offline` or keep
|
||||||
|
`/settings/offline` with a redirect
|
||||||
|
|
||||||
|
Items that MOVE OUT of Administration (to be removed from Settings sidebar AFTER their new
|
||||||
|
canonical routes land):
|
||||||
|
- Policy Governance → Release Control > Governance (SPRINT_20260219_028 TASK-07)
|
||||||
|
- Trust & Signing → Evidence & Audit > Trust & Signing (SPRINT_20260219_026 TASK-05)
|
||||||
|
- Release Control setup → Release Control > Setup (SPRINT_20260219_028 TASK-01)
|
||||||
|
- Integrations → Integrations root (TASK-02 of this sprint)
|
||||||
|
- Security Data → Integrations > Feeds + Security & Risk > Advisory Sources
|
||||||
|
|
||||||
|
For this sprint: rename the Settings root to "Administration" and add the Administration
|
||||||
|
Overview hub page. The old Settings sub-items remain in place (visible from Administration)
|
||||||
|
while their migration to new homes is executed in parallel sprints.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Root nav shows "Administration" (not "Settings")
|
||||||
|
- [ ] `/administration` route renders Administration Overview hub
|
||||||
|
- [ ] Administration Overview shows cards for all sub-areas
|
||||||
|
- [ ] `/settings` redirects to `/administration`
|
||||||
|
- [ ] All existing `/settings/...` routes continue to work (no broken links during migration)
|
||||||
|
- [ ] "Offline Settings" added to sidebar if not already present (per SPRINT_20260219_021)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Establish v1→v2 redirects for all deprecated Settings routes
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: All migration tasks in SPRINT_20260219_026, SPRINT_20260219_028, TASK-02
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per S00_route_deprecation_map.md, all v1 routes must redirect to their v2 canonical targets.
|
||||||
|
Once each Settings sub-item has been migrated to its canonical location, add the redirect:
|
||||||
|
|
||||||
|
| Old Route | New Canonical Route |
|
||||||
|
|---|---|
|
||||||
|
| `/settings/integrations` | `/integrations` |
|
||||||
|
| `/settings/integrations/:id` | `/integrations/:id` |
|
||||||
|
| `/settings/release-control` | `/release-control/setup` |
|
||||||
|
| `/settings/trust` | `/evidence/trust-signing` |
|
||||||
|
| `/settings/policy` | `/release-control/governance` |
|
||||||
|
| `/settings/admin` | `/administration/identity` |
|
||||||
|
| `/settings/branding` | `/administration/branding` |
|
||||||
|
| `/settings/notifications` | `/administration/notifications` |
|
||||||
|
| `/settings/usage` | `/administration/usage` |
|
||||||
|
| `/settings/system` | `/administration/system` |
|
||||||
|
| `/settings/offline` | `/administration/offline` |
|
||||||
|
| `/settings/security-data` | `/integrations/feeds` |
|
||||||
|
|
||||||
|
Each redirect must be registered in `legacy-redirects.routes.ts` (or equivalent). Do NOT
|
||||||
|
remove these redirects — keep them permanently for existing bookmarks and external links.
|
||||||
|
|
||||||
|
This task is LAST — only add a redirect AFTER the target route exists and renders.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 12 redirects registered in the route file
|
||||||
|
- [ ] Each redirect tested: source URL → correct destination
|
||||||
|
- [ ] No 404 for any deprecated route
|
||||||
|
- [ ] Redirects documented in S00_route_deprecation_map.md update
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from QA sweep. Full nav audit from live app cross-referenced with S00 frozen IA. Live nav confirmed as v1 structure. Integrations, Release Control not root menus. Settings not yet renamed to Administration. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Breaking risk**: Root nav changes affect every user and every page. Roll out in phases:
|
||||||
|
1. Phase 1: Add new roots (Release Control, Integrations) alongside existing nav
|
||||||
|
2. Phase 2: Rename Operations → Platform Ops, Evidence → Evidence & Audit, Security →
|
||||||
|
Security & Risk, Settings → Administration
|
||||||
|
3. Phase 3: Move Settings items to canonical locations + add redirects
|
||||||
|
4. Phase 4: Remove old nav items once redirects are confirmed stable
|
||||||
|
- **Releases and Approvals shortcut policy**: Per S00_nav_rendering_policy.md, Releases and
|
||||||
|
Approvals "may be direct nav shortcuts under Release Control group." Do NOT make them
|
||||||
|
top-level roots — they are shortcuts within Release Control.
|
||||||
|
- **FORBIDDEN placements** (per S00 authority-matrix.md): Trust in Evidence is OK (allowed);
|
||||||
|
Policy in Release Control is OK (allowed); System as top-level root is FORBIDDEN.
|
||||||
|
- **Settings→Administration timeline**: Do not rush the rename — Settings decomposition is
|
||||||
|
complex. The rename (TASK-06) can happen before all items are migrated, as long as all
|
||||||
|
`/settings/...` routes continue to work.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (nav audit) is a prerequisite — complete this before writing any code.
|
||||||
|
- TASK-02 (Integrations root) and TASK-03 (Release Control root) are highest priority.
|
||||||
|
- TASK-04 and TASK-05 (renames) are quick and can be done in parallel with TASK-02/03.
|
||||||
|
- TASK-07 (redirects) is final — only after all migrations land.
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
# Sprint 20260219-030 — QA Gap: Operations Section — Blank Pages (Scheduler, Quotas, Health, Dead Letter, Feeds)
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
- QA pack-tracking gap sprint. Full Operations section sweep via Playwright (2026-02-19).
|
||||||
|
- 5 out of 6 Operations sub-pages render a completely blank `<main>`. Only the Orchestrator
|
||||||
|
page renders content — but it contains an internal link with the wrong route prefix.
|
||||||
|
- Critical UX regressions:
|
||||||
|
- Status bar "Feed: Live" links to `/operations/feeds` which is blank.
|
||||||
|
- Status bar "Offline: OK" links to `/settings/offline` which is also blank (see SPRINT_021).
|
||||||
|
- The Orchestrator "Jobs" link goes to `/orchestrator/jobs` (legacy prefix) instead of
|
||||||
|
`/operations/orchestrator/jobs`.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/features/platform-ops/` (or wherever
|
||||||
|
the Operations feature components live under the current routing).
|
||||||
|
- Expected evidence: All 6 Operations sub-pages render content; internal Orchestrator links
|
||||||
|
use correct route prefixes; Feeds page shows feed status.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- SPRINT_20260219_023 (Data Integrity new section) depends on Operations being functional —
|
||||||
|
Data Integrity is a new Operations sub-section; the existing sub-pages must work first.
|
||||||
|
- SPRINT_20260219_029 TASK-04 (rename "Operations" → "Platform Ops") depends on these pages
|
||||||
|
being stable — fix blank pages before renaming.
|
||||||
|
- TASK-01 (blank page investigation) is the root cause prerequisite for all other tasks.
|
||||||
|
- TASK-02 through TASK-06 are independent of each other once the root cause is understood.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/platform-ops/` — Operations feature directory
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/` — route definitions for Operations
|
||||||
|
- `docs/modules/ui/v2-rewire/pack-15.md` — Data Integrity spec (shows what Operations should
|
||||||
|
eventually contain)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### TASK-01 — Investigate root cause of blank Operations pages
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
QA sweep confirmed the following Operations sub-pages render a completely empty `<main>`:
|
||||||
|
|
||||||
|
| Route | Observed title | Nav label | Blank? |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `/operations/scheduler` | "StellaOps" | Scheduler | YES |
|
||||||
|
| `/operations/quotas` | "Stella Ops" | Quotas | YES |
|
||||||
|
| `/operations/health` | "Stella Ops" | Platform Health | YES |
|
||||||
|
| `/operations/dead-letter` | "StellaOps" | Dead Letter | YES |
|
||||||
|
| `/operations/feeds` | "StellaOps" | Feeds | YES |
|
||||||
|
|
||||||
|
Working page:
|
||||||
|
| `/operations/orchestrator` | "Operations - StellaOps" | Orchestrator | Renders (with bugs) |
|
||||||
|
|
||||||
|
Title inconsistencies in blank pages: some show "StellaOps", some show "Stella Ops" (with
|
||||||
|
space) — suggesting some routes have no title defined at all while others partially resolve.
|
||||||
|
|
||||||
|
Investigation checklist:
|
||||||
|
1. Open browser console on each blank page — check for JS errors (module load failures,
|
||||||
|
injection errors, or unhandled exceptions)
|
||||||
|
2. Examine the Operations route file — verify each sub-route maps to a component (not just
|
||||||
|
a path with no component)
|
||||||
|
3. Check if components exist on disk or if they're placeholder/empty files
|
||||||
|
4. Verify lazy-loading chunk registration (are component files part of the bundle?)
|
||||||
|
5. Check for guard or resolver that silently blocks rendering without error
|
||||||
|
|
||||||
|
Root cause hypothesis: The Operations feature area likely has component placeholders or
|
||||||
|
route stubs registered but the actual component implementations are empty or missing.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Root cause documented in Decisions & Risks
|
||||||
|
- [ ] Whether pages need component implementation vs route fix vs lazy-load fix is determined
|
||||||
|
- [ ] Each blank page's component file located (or confirmed missing) on disk
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-02 — Fix Operations > Scheduler page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Per pack-15 and S00_handoff_packet.md, the Scheduler page shows the execution schedule for
|
||||||
|
all recurring ops jobs (sbom-nightly-rescan, reachability-runtime-ingest, feed-sync, etc.).
|
||||||
|
|
||||||
|
At minimum the page must render:
|
||||||
|
- Page heading: "Scheduler"
|
||||||
|
- List or table of scheduled jobs: job name, schedule (cron), next run, last run, status
|
||||||
|
- [Trigger Now] and [Pause] actions per job
|
||||||
|
- Empty state if no jobs configured: "No scheduled jobs configured."
|
||||||
|
|
||||||
|
Route: `/operations/scheduler`
|
||||||
|
Title: "Scheduler - StellaOps"
|
||||||
|
Breadcrumb: Operations > Scheduler
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with heading and job list (empty state acceptable)
|
||||||
|
- [ ] Title: "Scheduler - StellaOps"
|
||||||
|
- [ ] Breadcrumb: Operations > Scheduler
|
||||||
|
- [ ] "Scheduler" nav item active when on this page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-03 — Fix Operations > Quotas page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Quotas page shows resource consumption against configured limits. At minimum:
|
||||||
|
- Page heading: "Quotas"
|
||||||
|
- Table: resource name, current usage, limit, % used, status
|
||||||
|
- Resources: scan jobs, replay requests, export runs, API calls
|
||||||
|
- [Edit Limits] action (admin only)
|
||||||
|
|
||||||
|
Route: `/operations/quotas`
|
||||||
|
Title: "Quotas - StellaOps"
|
||||||
|
Breadcrumb: Operations > Quotas
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with heading and quota table (empty/zero values acceptable)
|
||||||
|
- [ ] Title: "Quotas - StellaOps"
|
||||||
|
- [ ] Breadcrumb: Operations > Quotas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-04 — Fix Operations > Platform Health page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
Platform Health shows the health status of all internal services. At minimum:
|
||||||
|
- Page heading: "Platform Health"
|
||||||
|
- Service health table: service name, status (healthy/degraded/down), last check, uptime
|
||||||
|
- Services: Concelier, Scanner, Attestor, Policy, Evidence Locker, Orchestrator, Signals
|
||||||
|
- Color-coded status indicators
|
||||||
|
|
||||||
|
Route: `/operations/health`
|
||||||
|
Title: "Platform Health - StellaOps"
|
||||||
|
Breadcrumb: Operations > Platform Health
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with heading and service health table
|
||||||
|
- [ ] Title: "Platform Health - StellaOps"
|
||||||
|
- [ ] Breadcrumb: Operations > Platform Health
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-05 — Fix Operations > Dead Letter page
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Dead Letter page shows failed/unprocessable messages. At minimum:
|
||||||
|
- Page heading: "Dead Letter Queue"
|
||||||
|
- Table: message ID, type, error, timestamp, [Retry] action
|
||||||
|
- Filter: by type, time window
|
||||||
|
- Empty state: "No dead letter messages."
|
||||||
|
- [Retry All] bulk action
|
||||||
|
|
||||||
|
Route: `/operations/dead-letter`
|
||||||
|
Title: "Dead Letter Queue - StellaOps"
|
||||||
|
Breadcrumb: Operations > Dead Letter
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with heading and DLQ table (empty state acceptable)
|
||||||
|
- [ ] Title: "Dead Letter Queue - StellaOps"
|
||||||
|
- [ ] Breadcrumb: Operations > Dead Letter
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-06 — Fix Operations > Feeds page (status bar "Feed: Live" link target)
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: TASK-01
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Feeds page is the target of the status bar "Feed: Live" indicator link. This is a
|
||||||
|
high-visibility entry point that currently leads to a blank page.
|
||||||
|
|
||||||
|
Per pack-15 and SPRINT_023 context, the Feeds page in Operations shows the air-gap/feed
|
||||||
|
mirror configuration and sync status. At minimum:
|
||||||
|
- Page heading: "Feeds & AirGap Operations"
|
||||||
|
- Feed sources list: name, type (OSV/NVD/KEV/etc.), last sync, status badge
|
||||||
|
- Each feed: last updated, next scheduled sync, [Force Sync] action
|
||||||
|
- Air-gap mode toggle or status indicator
|
||||||
|
|
||||||
|
Route: `/operations/feeds`
|
||||||
|
Title: "Feeds & AirGap Operations - StellaOps"
|
||||||
|
Breadcrumb: Operations > Feeds
|
||||||
|
|
||||||
|
Critical: The status bar "Feed: Live" indicator links here — this page MUST render content
|
||||||
|
so users who click the status bar find useful information.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Page renders with heading and feeds list (empty state acceptable)
|
||||||
|
- [ ] Title: "Feeds & AirGap Operations - StellaOps"
|
||||||
|
- [ ] Breadcrumb: Operations > Feeds
|
||||||
|
- [ ] "Feed: Live" status bar link no longer leads to a blank page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TASK-07 — Fix Orchestrator internal link wrong route prefix
|
||||||
|
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
The Orchestrator page (`/operations/orchestrator`) renders correctly but contains an internal
|
||||||
|
navigation link with the wrong route prefix:
|
||||||
|
|
||||||
|
Current (broken): "Jobs" → `/orchestrator/jobs`
|
||||||
|
Correct: "Jobs" → `/operations/orchestrator/jobs` (or `/operations/jobs`)
|
||||||
|
|
||||||
|
The link `/orchestrator/jobs` is a legacy route that no longer exists. Clicking it redirects
|
||||||
|
to the root (or produces a 404). This is the same legacy route prefix issue identified in
|
||||||
|
prior sprints.
|
||||||
|
|
||||||
|
Fix: Update the link in the Orchestrator Dashboard component to use the correct
|
||||||
|
`/operations/orchestrator/jobs` route (or whichever is the canonical path in the current
|
||||||
|
route config). Also verify the `jobs` sub-route exists under Operations.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Orchestrator "Jobs" link uses the correct route prefix
|
||||||
|
- [ ] Clicking "Jobs" navigates to a valid route (not redirected to root)
|
||||||
|
- [ ] If `/operations/orchestrator/jobs` does not exist as a route, register it as a stub
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-19 | Sprint created from Playwright QA sweep (session 3). Full Operations section sweep. 5/6 pages blank. Orchestrator renders but has wrong internal route `/orchestrator/jobs`. Status bar "Feed: Live" links to blank /operations/feeds page — critical UX failure. | QA |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
- **Blast radius**: The Operations section is used by admins. All 5 blank pages represent
|
||||||
|
complete feature unavailability. This is a higher-severity issue than missing features —
|
||||||
|
features that exist in the nav and are inaccessible are worse than missing nav items.
|
||||||
|
- **Status bar links to blank pages**: Both "Feed: Live" (→ `/operations/feeds`) and
|
||||||
|
"Offline: OK" (→ `/settings/offline`, see SPRINT_021 TASK-05) are prominent status
|
||||||
|
indicators that link to blank pages. These are among the first things admins click
|
||||||
|
when troubleshooting. Fix TASK-06 and SPRINT_021 TASK-05 before any other polish work.
|
||||||
|
- **Root cause likely same for all 5 blank pages**: Given all 5 Operations sub-pages are
|
||||||
|
blank while the 6th (Orchestrator) works, the root cause is likely a component
|
||||||
|
registration pattern where Orchestrator has a complete component but the others have
|
||||||
|
empty/stub implementations that fail silently.
|
||||||
|
- **Title "Stella Ops" vs "StellaOps" vs nothing**: The inconsistent title formats across
|
||||||
|
blank pages (some "StellaOps", some "Stella Ops" with space) indicate different
|
||||||
|
generations or templates used to create the route stubs.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
|
||||||
|
- TASK-01 (root cause) must be done first — results determine implementation strategy.
|
||||||
|
- TASK-06 (Feeds page) is highest priority due to the status bar regression.
|
||||||
|
- TASK-07 (Orchestrator route fix) is a quick fix that should be done immediately.
|
||||||
|
- TASK-02 through TASK-05 can be done in parallel once TASK-01 is complete.
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# Sprint 20260220_001 - Symbol Marketplace: Contracts and Persistence
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Establish the domain model and persistence layer for the Symbol/Debug Pack Marketplace.
|
||||||
|
- Create `StellaOps.Symbols.Marketplace` project with source registry, catalog, freshness, and trust scoring models.
|
||||||
|
- Add repository interfaces and in-memory implementations for marketplace data access.
|
||||||
|
- Add `SymbolSource = 7` integration type to the integration enum.
|
||||||
|
- Working directory: `src/Symbols/StellaOps.Symbols.Marketplace/` and `src/Integrations/__Libraries/StellaOps.Integrations.Core/`.
|
||||||
|
- Expected evidence: unit tests for trust scoring and model construction, compilable project.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- No upstream sprint dependencies.
|
||||||
|
- Safe to parallelize with Sprint 002 (API) once models are stable.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `docs/modules/platform/moat-gap-analysis.md` (symbol proof score context).
|
||||||
|
- `src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Postgres/Repositories/AdvisorySourceReadRepository.cs` (pattern reference for freshness repository).
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### MKT-01 - Domain models for Symbol Marketplace
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `Models/SymbolPackSource.cs` — registry of symbol providers (vendor/distro/community/partner).
|
||||||
|
- Create `Models/SymbolPackCatalogEntry.cs` — catalog entry for installable packs.
|
||||||
|
- Create `Models/SymbolSourceFreshnessRecord.cs` — freshness projection mirroring advisory source pattern.
|
||||||
|
- Create `Models/SymbolSourceTrustScore.cs` — four-dimension trust scoring record.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All four model files compile under `StellaOps.Symbols.Marketplace` namespace
|
||||||
|
- [ ] Models follow record pattern consistent with existing codebase
|
||||||
|
|
||||||
|
### MKT-02 - Create Marketplace project file
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `StellaOps.Symbols.Marketplace.csproj` targeting net10.0.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Project file exists and builds
|
||||||
|
|
||||||
|
### MKT-03 - Repository interfaces and implementations
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-01
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `ISymbolSourceReadRepository.cs` with source listing and freshness retrieval.
|
||||||
|
- Create `IMarketplaceCatalogRepository.cs` with catalog listing, search, install/uninstall.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Interfaces are defined with async methods
|
||||||
|
- [ ] Methods mirror AdvisorySourceReadRepository pattern
|
||||||
|
|
||||||
|
### MKT-04 - Trust scorer interface and implementation
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-01
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `ISymbolSourceTrustScorer` interface.
|
||||||
|
- Implement `DefaultSymbolSourceTrustScorer` with weighted scoring: Freshness=0.3, Signature=0.3, Coverage=0.2, SLA=0.2.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Scorer produces correct weighted averages
|
||||||
|
- [ ] Unit tests verify four-dimension scoring
|
||||||
|
|
||||||
|
### MKT-05 - Add IntegrationType.SymbolSource
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Add `SymbolSource = 7` to `IntegrationType` enum.
|
||||||
|
- Add provider values: `MicrosoftSymbols = 700, UbuntuDebuginfod = 701, FedoraDebuginfod = 702, DebianDebuginfod = 703, PartnerSymbols = 704`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Enum values added without breaking existing assignments
|
||||||
|
- [ ] Project compiles
|
||||||
|
|
||||||
|
### MKT-06 - Unit tests for marketplace models and scorer
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-01, MKT-04
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `SymbolSourceTrustScorerTests.cs` — test four-dimension scoring logic.
|
||||||
|
- Create `SymbolSourceFreshnessRecordTests.cs` — test model construction.
|
||||||
|
- Create `SymbolPackCatalogEntryTests.cs` — test model construction.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All tests pass
|
||||||
|
- [ ] Scorer tests verify boundary values and weighted averages
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Trust score weights (0.3/0.3/0.2/0.2) are initial values; may need tuning based on production feedback.
|
||||||
|
- Freshness pattern mirrors advisory sources to maintain architectural consistency.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Models and tests complete before API sprint (002) begins endpoint wiring.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Sprint 20260220_002 - Symbol Marketplace: API and CLI
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Expose Symbol Marketplace functionality via HTTP API endpoints.
|
||||||
|
- Create SymbolSourceEndpoints extension method following the ReleaseControlEndpoints pattern.
|
||||||
|
- Wire endpoints into the Symbols Server Program.cs.
|
||||||
|
- Working directory: `src/Symbols/StellaOps.Symbols.Server/`.
|
||||||
|
- Expected evidence: endpoints compile and are wired into the application.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 001 (MKT-01 through MKT-04) for domain models and repository interfaces.
|
||||||
|
- Safe to parallelize with Sprint 003 (UI) once endpoint contracts are stable.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `src/Platform/StellaOps.Platform.WebService/Endpoints/ReleaseControlEndpoints.cs` (pattern reference).
|
||||||
|
- Sprint 001 models and interfaces.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### MKT-07 - Symbol Source endpoints
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-03
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `Endpoints/SymbolSourceEndpoints.cs` with IEndpointRouteBuilder extension.
|
||||||
|
- Implement source CRUD: list, get by ID, create, update, disable.
|
||||||
|
- Implement summary and freshness detail endpoints.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All source endpoints defined under `/api/v1/symbols/sources`
|
||||||
|
- [ ] Follows MapGroup + WithTags pattern
|
||||||
|
|
||||||
|
### MKT-08 - Marketplace catalog endpoints
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-03
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Add marketplace catalog endpoints: list, search, get detail, install, uninstall, list installed, trigger sync.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All catalog endpoints defined under `/api/v1/symbols/marketplace`
|
||||||
|
- [ ] Install/uninstall return appropriate status codes
|
||||||
|
|
||||||
|
### MKT-09 - Wire endpoints into Program.cs
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-07, MKT-08
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Add `app.MapSymbolSourceEndpoints()` call to `src/Symbols/StellaOps.Symbols.Server/Program.cs`.
|
||||||
|
- Add project reference to Marketplace project in Server csproj.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Endpoints are reachable when server starts
|
||||||
|
- [ ] Server project compiles with new reference
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Endpoints follow the existing Symbols Server inline pattern but use extension method for new marketplace surface.
|
||||||
|
- Authentication follows existing RequireAuthorization pattern.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- API surface stable before UI sprint (003) begins binding.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Sprint 20260220_003 - FE: Symbol Sources Marketplace UI
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Build Angular UI components for the Symbol Sources and Marketplace features.
|
||||||
|
- Create API service, list/detail/catalog components, routes, and sidebar entries.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/`.
|
||||||
|
- Expected evidence: components render, routes navigate correctly.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 002 (MKT-07 through MKT-09) for API endpoint contracts.
|
||||||
|
- Safe to parallelize documentation (Sprint 004).
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/advisory-sources.component.ts` (pattern reference).
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts` (route structure).
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### MKT-14 - Symbol Sources API service
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-07
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/symbol-sources/symbol-sources.api.ts`.
|
||||||
|
- Define TypeScript interfaces mirroring backend models.
|
||||||
|
- Implement service methods: listSources, getSourceSummary, listCatalog, installPack, uninstallPack.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Service injectable and compilable
|
||||||
|
- [ ] All endpoint paths match backend API surface
|
||||||
|
|
||||||
|
### MKT-15 - Symbol Sources list component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-14
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/symbol-sources/symbol-sources-list.component.ts`.
|
||||||
|
- Standalone Angular component with freshness summary cards and source table.
|
||||||
|
- Follow advisory-sources component pattern.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component renders summary cards and table
|
||||||
|
- [ ] Freshness status badges use state machine colors
|
||||||
|
|
||||||
|
### MKT-16 - Symbol Source detail component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-14
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/symbol-sources/symbol-source-detail.component.ts`.
|
||||||
|
- Show status timeline, pack coverage, trust breakdown for a single source.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component loads source by ID from route parameter
|
||||||
|
- [ ] Trust score dimensions displayed
|
||||||
|
|
||||||
|
### MKT-17 - Symbol Marketplace catalog component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-14
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/symbol-sources/symbol-marketplace-catalog.component.ts`.
|
||||||
|
- Search/filter catalog entries with install/uninstall buttons.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component renders catalog grid with search
|
||||||
|
- [ ] Install/uninstall actions trigger API calls
|
||||||
|
|
||||||
|
### MKT-18 - Routes and sidebar integration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-15, MKT-16, MKT-17
|
||||||
|
Owners: Developer
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Add symbol-sources and symbol-marketplace routes to `routes/security-risk.routes.ts`.
|
||||||
|
- Add sidebar items under security-risk section in `app-sidebar.component.ts`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Routes navigate to correct components
|
||||||
|
- [ ] Sidebar shows Symbol Sources and Symbol Marketplace items
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Components follow advisory-sources pattern for consistency.
|
||||||
|
- Standalone components with inject() for DI, signals where appropriate.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- UI functional before documentation sprint (004) finalizes architecture docs.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Sprint 20260220_004 - DOCS: Symbol Marketplace Architecture and Moat
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Document the Symbol Marketplace architecture, primitives, DB schema, API surface, and integration points.
|
||||||
|
- Update moat gap analysis to reflect improved symbol proof score.
|
||||||
|
- Update moat strategy summary with Symbol Marketplace thesis.
|
||||||
|
- Working directory: `docs/`.
|
||||||
|
- Expected evidence: architecture doc, updated moat docs.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprints 001-003 for implementation details.
|
||||||
|
- Safe to start architecture doc skeleton early.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `docs/modules/platform/moat-gap-analysis.md` (current symbol proof score).
|
||||||
|
- `docs/product/moat-strategy-summary.md` (moat enhancement roadmap).
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### MKT-20 - Create marketplace architecture doc
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-01, MKT-07
|
||||||
|
Owners: Documentation Author
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Create `docs/modules/symbols/marketplace-architecture.md`.
|
||||||
|
- Document architecture overview, domain primitives, DB schema, API surface, integration points, trust scoring model.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Architecture doc covers all marketplace components
|
||||||
|
- [ ] API surface matches implemented endpoints
|
||||||
|
|
||||||
|
### MKT-21 - Update moat gap analysis
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-20
|
||||||
|
Owners: Documentation Author
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Update `docs/modules/platform/moat-gap-analysis.md` — update symbolized call-stack proofs score from 85% to 95%.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Score updated with rationale
|
||||||
|
|
||||||
|
### MKT-22 - Update moat strategy summary
|
||||||
|
Status: TODO
|
||||||
|
Dependency: MKT-20
|
||||||
|
Owners: Documentation Author
|
||||||
|
|
||||||
|
Task description:
|
||||||
|
- Update `docs/product/moat-strategy-summary.md` — add Symbol Marketplace thesis under moat enhancement roadmap.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Symbol Marketplace referenced in strategy document
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Architecture doc structure follows existing module dossier pattern.
|
||||||
|
- Moat score increase from 85% to 95% reflects marketplace + trust scoring additions.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- All docs reviewed and consistent with implementation.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Sprint 20260220-005 -- Telemetry: Federated Privacy Primitives
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Build the core privacy-preserving primitives for federated runtime telemetry.
|
||||||
|
- Differential privacy budget tracking, k-anonymity aggregation, consent management, and bundle building.
|
||||||
|
- Working directory: `src/Telemetry/StellaOps.Telemetry.Federation/`
|
||||||
|
- Expected evidence: unit tests for all primitives, deterministic aggregation with seeded RNG.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- No upstream sprint dependencies; this is the foundation for Sprints 006-009.
|
||||||
|
- Safe to implement in parallel with non-Telemetry sprints.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `docs/modules/telemetry/federation-architecture.md` (created in Sprint 009)
|
||||||
|
- Existing `src/Telemetry/StellaOps.Telemetry.Core/` DI patterns
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### FPT-01 - Project skeleton
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `StellaOps.Telemetry.Federation.csproj` targeting net10.0.
|
||||||
|
- Create `FederationServiceCollectionExtensions.cs` with DI registrations for all federation services.
|
||||||
|
- Create `FederatedTelemetryOptions.cs` with configurable k-anonymity threshold, epsilon budget, reset period, aggregation interval, sealed mode flag, and predicate types.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Project builds successfully
|
||||||
|
- [ ] DI extension registers all five services
|
||||||
|
- [ ] Options class has all required properties with defaults
|
||||||
|
|
||||||
|
### FPT-02 - Privacy budget tracker
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Privacy/IPrivacyBudgetTracker.cs` interface with RemainingEpsilon, TotalBudget, IsBudgetExhausted, TrySpend, Reset, GetSnapshot.
|
||||||
|
- Create `Privacy/PrivacyBudgetTracker.cs` implementation using thread-safe Interlocked operations.
|
||||||
|
- Include Laplacian noise helper method.
|
||||||
|
- Create `Privacy/PrivacyBudgetSnapshot.cs` record type.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Thread-safe budget tracking with atomic operations
|
||||||
|
- [ ] Laplacian noise helper produces correct distribution
|
||||||
|
- [ ] Budget exhaustion prevents further spending
|
||||||
|
|
||||||
|
### FPT-03 - Telemetry aggregator
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Aggregation/ITelemetryAggregator.cs` interface.
|
||||||
|
- Create `Aggregation/TelemetryAggregator.cs` implementation applying k-anonymity suppression and Laplacian noise.
|
||||||
|
- Create record types: TelemetryFact, AggregationBucket, AggregationResult.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] K-anonymity suppresses buckets below threshold
|
||||||
|
- [ ] Laplacian noise added to surviving bucket counts
|
||||||
|
- [ ] Epsilon spending tracked via IPrivacyBudgetTracker
|
||||||
|
|
||||||
|
### FPT-04 - Consent manager
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Consent/IConsentManager.cs` interface with GetConsentState, GrantConsent, RevokeConsent.
|
||||||
|
- Create `Consent/ConsentManager.cs` in-memory implementation with TTL support.
|
||||||
|
- Create record types: ConsentState, ConsentProof.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Grant/revoke lifecycle works correctly
|
||||||
|
- [ ] TTL expiry transitions consent to revoked
|
||||||
|
- [ ] DSSE digest placeholder generated for proof
|
||||||
|
|
||||||
|
### FPT-05 - Federated bundle builder
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Bundles/IFederatedTelemetryBundleBuilder.cs` interface with Build and Verify methods.
|
||||||
|
- Create `Bundles/FederatedTelemetryBundleBuilder.cs` implementation.
|
||||||
|
- Create FederatedBundle record type.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Build produces a bundle from aggregation + consent proof
|
||||||
|
- [ ] Verify round-trips successfully
|
||||||
|
- [ ] Bundle includes DSSE digest placeholders
|
||||||
|
|
||||||
|
### FPT-06 - Register predicates
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Document predicate types `stella.ops/federatedConsent@v1` and `stella.ops/federatedTelemetry@v1` in sprint decisions.
|
||||||
|
- Actual registration deferred to Attestor migration pattern.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Predicate types documented in Decisions & Risks section
|
||||||
|
|
||||||
|
### FPT-07 - Unit tests
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FPT-02, FPT-03, FPT-04, FPT-05
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `StellaOps.Telemetry.Federation.Tests/` test project.
|
||||||
|
- `PrivacyBudgetTrackerTests.cs` -- budget exhaustion, reset, spend tracking.
|
||||||
|
- `TelemetryAggregatorTests.cs` -- k-suppression, noise addition, deterministic with fixed seed.
|
||||||
|
- `ConsentManagerTests.cs` -- grant/revoke lifecycle, TTL expiry.
|
||||||
|
- `FederatedTelemetryBundleBuilderTests.cs` -- build + verify round-trip.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All tests pass
|
||||||
|
- [ ] Deterministic aggregation tests use fixed seed
|
||||||
|
- [ ] Budget exhaustion scenario covered
|
||||||
|
- [ ] Consent TTL expiry scenario covered
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Predicate types: `stella.ops/federatedConsent@v1` and `stella.ops/federatedTelemetry@v1` will be registered via the existing Attestor predicate schema registry migration pattern (FPT-06).
|
||||||
|
- Laplacian noise uses standard double-precision arithmetic; acceptable for telemetry privacy guarantees.
|
||||||
|
- In-memory consent manager is sufficient for MVP; persistent store deferred to follow-up sprint.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Sprint 006 depends on all primitives being available.
|
||||||
|
- Sprint 009 (docs) should reference the final API surface.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Sprint 20260220-006 -- Telemetry: Federation Sync and Intelligence
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Implement background sync service and exploit intelligence merging for federated telemetry.
|
||||||
|
- Working directory: `src/Telemetry/StellaOps.Telemetry.Federation/`
|
||||||
|
- Expected evidence: sync service lifecycle, intelligence normalization, egress policy integration.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Upstream: Sprint 005 (privacy primitives must be available).
|
||||||
|
- Safe to implement in parallel with Sprints 007-009 once primitives exist.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Sprint 005 completion (IPrivacyBudgetTracker, ITelemetryAggregator, IConsentManager, IFederatedTelemetryBundleBuilder)
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### FTS-01 - Federated sync service
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Sync/FederatedTelemetrySyncService.cs` as a BackgroundService.
|
||||||
|
- Periodically aggregates telemetry facts, checks consent, builds bundles, and syncs to federation peers.
|
||||||
|
- Respects privacy budget exhaustion and sealed mode.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] BackgroundService lifecycle (start/stop/cancellation)
|
||||||
|
- [ ] Aggregation triggered on configurable interval
|
||||||
|
- [ ] Consent check before bundle creation
|
||||||
|
- [ ] Budget exhaustion halts sync cycle
|
||||||
|
|
||||||
|
### FTS-02 - Exploit intelligence merger interface
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Intelligence/IExploitIntelligenceMerger.cs` interface for merging shared exploit corpus from federation peers.
|
||||||
|
- Create `Intelligence/ExploitIntelligenceMerger.cs` implementation.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Merge produces deduplicated exploit intelligence
|
||||||
|
- [ ] Conflict resolution by latest observation timestamp
|
||||||
|
|
||||||
|
### FTS-03 - Intelligence normalizer
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FTS-02
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Intelligence/FederatedIntelligenceNormalizer.cs` to normalize incoming exploit data from heterogeneous federation peers.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] CVE ID normalization
|
||||||
|
- [ ] Artifact digest format normalization
|
||||||
|
- [ ] Timestamp UTC normalization
|
||||||
|
|
||||||
|
### FTS-04 - Egress policy integration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FTS-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Sync/EgressPolicyIntegration.cs` to validate outbound federation traffic against the platform egress policy.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Egress check before outbound bundle transmission
|
||||||
|
- [ ] Blocked egress logged and bundle marked as pending
|
||||||
|
|
||||||
|
### FTS-05 - Sync service DI registration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FTS-01, FTS-02, FTS-03, FTS-04
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Extend `FederationServiceCollectionExtensions.cs` to register sync and intelligence services.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All sync/intelligence services registered in DI
|
||||||
|
|
||||||
|
### FTS-06 - Unit tests for sync and intelligence
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FTS-01, FTS-02, FTS-03, FTS-04
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add tests for sync service lifecycle, intelligence merging, normalization, and egress policy.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Sync service start/stop tests
|
||||||
|
- [ ] Intelligence merge deduplication test
|
||||||
|
- [ ] Normalizer format tests
|
||||||
|
- [ ] Egress blocked scenario test
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Sync service uses configurable interval from FederatedTelemetryOptions.AggregationInterval.
|
||||||
|
- Egress policy integration reuses existing IEgressPolicy from AirGap module.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Sprint 007 API endpoints depend on sync service availability.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# Sprint 20260220-007 -- Telemetry: Federation API, CLI, Doctor
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Expose federated telemetry capabilities via Platform WebService REST endpoints.
|
||||||
|
- Add authorization scopes and policies for federation management.
|
||||||
|
- Working directory: `src/Platform/StellaOps.Platform.WebService/`
|
||||||
|
- Expected evidence: endpoint tests, scope/policy constants, endpoint registration in Program.cs.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Upstream: Sprint 005 (privacy primitives), Sprint 006 (sync/intelligence).
|
||||||
|
- Can scaffold endpoints while primitives are being built.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Existing endpoint patterns in `src/Platform/StellaOps.Platform.WebService/Endpoints/ReleaseControlEndpoints.cs`
|
||||||
|
- Existing scope/policy patterns in `Constants/PlatformScopes.cs` and `Constants/PlatformPolicies.cs`
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### FAC-01 - Federation telemetry endpoints
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `Endpoints/FederationTelemetryEndpoints.cs` with endpoints:
|
||||||
|
- GET /api/v1/telemetry/federation/consent -- get consent state
|
||||||
|
- POST /api/v1/telemetry/federation/consent/grant -- grant consent
|
||||||
|
- POST /api/v1/telemetry/federation/consent/revoke -- revoke consent
|
||||||
|
- GET /api/v1/telemetry/federation/status -- federation status
|
||||||
|
- GET /api/v1/telemetry/federation/bundles -- list bundles
|
||||||
|
- GET /api/v1/telemetry/federation/bundles/{id} -- bundle detail
|
||||||
|
- GET /api/v1/telemetry/federation/intelligence -- exploit corpus
|
||||||
|
- GET /api/v1/telemetry/federation/privacy-budget -- budget snapshot
|
||||||
|
- POST /api/v1/telemetry/federation/trigger -- trigger aggregation
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 9 endpoints implemented
|
||||||
|
- [ ] Proper authorization policies applied
|
||||||
|
- [ ] Error handling follows existing patterns
|
||||||
|
|
||||||
|
### FAC-02 - Authorization scopes
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add `FederationRead` and `FederationManage` scopes to `PlatformScopes.cs`.
|
||||||
|
- Add `FederationRead` and `FederationManage` policies to `PlatformPolicies.cs`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Scopes added to PlatformScopes
|
||||||
|
- [ ] Policies added to PlatformPolicies
|
||||||
|
- [ ] Read endpoints use FederationRead
|
||||||
|
- [ ] Write endpoints use FederationManage
|
||||||
|
|
||||||
|
### FAC-03 - Endpoint registration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FAC-01, FAC-02
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Register `MapFederationTelemetryEndpoints()` in Platform WebService Program.cs.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Endpoints registered in app pipeline
|
||||||
|
|
||||||
|
### FAC-04 - Endpoint contract models
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FAC-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create request/response models for federation endpoints in `Contracts/FederationTelemetryModels.cs`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All request/response DTOs defined
|
||||||
|
- [ ] Models match federation primitive types
|
||||||
|
|
||||||
|
### FAC-05 - Endpoint tests
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FAC-01, FAC-02, FAC-03, FAC-04
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `FederationTelemetryEndpointsTests.cs` in Platform test project.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Tests for consent grant/revoke lifecycle
|
||||||
|
- [ ] Tests for bundle listing
|
||||||
|
- [ ] Tests for privacy budget snapshot
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Endpoint pattern follows ReleaseControlEndpoints.cs conventions.
|
||||||
|
- PlatformRequestContextResolver reused for tenant resolution.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Sprint 008 UI depends on these endpoints being available.
|
||||||
119
docs/implplan/SPRINT_20260220_008_FE_telemetry_federation_ui.md
Normal file
119
docs/implplan/SPRINT_20260220_008_FE_telemetry_federation_ui.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# Sprint 20260220-008 -- FE: Telemetry Federation UI
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Build Angular UI for federated telemetry management under Platform Ops.
|
||||||
|
- Overview dashboard, consent management, bundle explorer, intelligence viewer, privacy budget monitor.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/`
|
||||||
|
- Expected evidence: components render, routes navigate, API service calls backend.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Upstream: Sprint 007 (API endpoints must be available).
|
||||||
|
- Safe to scaffold components before API is complete.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Existing component patterns in `src/Web/StellaOps.Web/src/app/features/platform-ops/`
|
||||||
|
- Route patterns in `src/Web/StellaOps.Web/src/app/routes/platform-ops.routes.ts`
|
||||||
|
- Sidebar patterns in `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts`
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### FUI-01 - Federation routes
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add P10 federation telemetry routes to `routes/platform-ops.routes.ts`.
|
||||||
|
- Five routes: overview, consent, bundles, intelligence, privacy.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All 5 routes added under P10 section
|
||||||
|
- [ ] Lazy-loaded components
|
||||||
|
- [ ] Breadcrumb data set
|
||||||
|
|
||||||
|
### FUI-02 - Sidebar navigation item
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add Federation sidebar item under platform-ops children in `app-sidebar.component.ts`.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Federation item visible under Platform Ops group
|
||||||
|
- [ ] Route points to /platform-ops/federation-telemetry
|
||||||
|
|
||||||
|
### FUI-03 - API service
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/platform-ops/federation-telemetry/federation-telemetry.api.ts`.
|
||||||
|
- Service calls /api/v1/telemetry/federation/* endpoints.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All endpoint methods defined
|
||||||
|
- [ ] Typed request/response interfaces
|
||||||
|
- [ ] HttpClient injection
|
||||||
|
|
||||||
|
### FUI-04 - Federation overview component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FUI-03
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/platform-ops/federation-telemetry/federation-overview.component.ts`.
|
||||||
|
- Dashboard with status cards, consent state, budget gauge, bundle history.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Standalone component with OnPush strategy
|
||||||
|
- [ ] Status cards for consent, budget, bundle count
|
||||||
|
- [ ] Navigation links to sub-pages
|
||||||
|
|
||||||
|
### FUI-05 - Consent management component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FUI-03
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/platform-ops/federation-telemetry/consent-management.component.ts`.
|
||||||
|
- Grant/revoke UI with DSSE proof display.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Grant button triggers API call
|
||||||
|
- [ ] Revoke button triggers API call
|
||||||
|
- [ ] Current consent state displayed
|
||||||
|
- [ ] DSSE digest shown when granted
|
||||||
|
|
||||||
|
### FUI-06 - Bundle explorer component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FUI-03
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/platform-ops/federation-telemetry/bundle-explorer.component.ts`.
|
||||||
|
- Table of bundles with verification status.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Bundle list table with columns: ID, site, created, verified
|
||||||
|
- [ ] Click navigates to detail view
|
||||||
|
|
||||||
|
### FUI-07 - Intelligence viewer and privacy monitor components
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FUI-03
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/platform-ops/federation-telemetry/intelligence-viewer.component.ts` -- table of shared exploit corpus.
|
||||||
|
- Create `features/platform-ops/federation-telemetry/privacy-budget-monitor.component.ts` -- epsilon gauge, suppression stats, k-anonymity history.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Intelligence viewer displays CVE table
|
||||||
|
- [ ] Privacy monitor shows epsilon remaining gauge
|
||||||
|
- [ ] Suppression stats displayed
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Angular standalone components with signals and OnPush change detection per codebase convention.
|
||||||
|
- Lazy loading for all federation sub-routes.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Sprint 009 documentation references UI component paths.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Sprint 20260220-009 -- DOCS: Telemetry Federation Architecture
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Create architecture documentation, predicate schemas, consent proof schema, and operational runbook for federated telemetry.
|
||||||
|
- Working directory: `docs/`
|
||||||
|
- Expected evidence: complete architecture doc, contract schemas, operational runbook.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Upstream: Sprints 005-008 (implementation must be substantially complete for accurate docs).
|
||||||
|
- Can scaffold documentation structure in parallel with implementation.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Implementation details from Sprints 005-008.
|
||||||
|
- Existing docs patterns in `docs/modules/`, `docs/contracts/`, `docs/runbooks/`.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### FDC-01 - Federation architecture document
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation
|
||||||
|
Task description:
|
||||||
|
- Create `docs/modules/telemetry/federation-architecture.md`.
|
||||||
|
- Cover: privacy model, k-anonymity, differential privacy, consent flow, sync lifecycle, intelligence merging, bundle format, sealed mode behavior.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Architecture overview with data flow diagram
|
||||||
|
- [ ] Privacy guarantees section
|
||||||
|
- [ ] Consent lifecycle section
|
||||||
|
- [ ] Sync service behavior section
|
||||||
|
- [ ] Intelligence merging section
|
||||||
|
|
||||||
|
### FDC-02 - Federated telemetry predicate schema
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation
|
||||||
|
Task description:
|
||||||
|
- Create `docs/contracts/federated-telemetry-v1.md`.
|
||||||
|
- Define `stella.ops/federatedTelemetry@v1` predicate schema.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Schema definition with all fields
|
||||||
|
- [ ] Validation rules
|
||||||
|
- [ ] Example payload
|
||||||
|
|
||||||
|
### FDC-03 - Federated consent predicate schema
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation
|
||||||
|
Task description:
|
||||||
|
- Create `docs/contracts/federated-consent-v1.md`.
|
||||||
|
- Define `stella.ops/federatedConsent@v1` predicate schema.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Schema definition with all fields
|
||||||
|
- [ ] Consent lifecycle states
|
||||||
|
- [ ] Example payload
|
||||||
|
|
||||||
|
### FDC-04 - Operational runbook
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation
|
||||||
|
Task description:
|
||||||
|
- Create `docs/runbooks/federated-telemetry-operations.md`.
|
||||||
|
- Cover: enabling federation, consent management, budget monitoring, troubleshooting sync failures, sealed mode operations.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Enable/disable federation procedure
|
||||||
|
- [ ] Consent management procedures
|
||||||
|
- [ ] Budget monitoring and reset procedures
|
||||||
|
- [ ] Sync failure troubleshooting
|
||||||
|
|
||||||
|
### FDC-05 - Cross-reference updates
|
||||||
|
Status: TODO
|
||||||
|
Dependency: FDC-01, FDC-02, FDC-03, FDC-04
|
||||||
|
Owners: Documentation
|
||||||
|
Task description:
|
||||||
|
- Update `docs/README.md` to reference new federation docs.
|
||||||
|
- Ensure federation architecture is linked from telemetry module index.
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] README updated with federation section
|
||||||
|
- [ ] Cross-references validated
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Documentation created based on implementation; any implementation changes require doc updates.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- All docs complete before feature is considered shipped.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# Sprint 20260220-010 — Remediation Registry and Persistence
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Create the `src/Remediation/` module skeleton with Core, WebService, Persistence, and Tests projects.
|
||||||
|
- Define domain models: FixTemplate, PrSubmission, Contributor, MarketplaceSource.
|
||||||
|
- Create SQL migration for remediation schema.
|
||||||
|
- Implement IRemediationRegistry, IContributorTrustScorer, and repository interfaces.
|
||||||
|
- Working directory: `src/Remediation/`.
|
||||||
|
- Expected evidence: compilable projects, passing unit tests, SQL migration.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- No upstream sprint dependencies.
|
||||||
|
- Can run in parallel with Sprints 011-015.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- `docs/modules/remediation/architecture.md` (created in Sprint 015).
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-01 - Module skeleton and .csproj files
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `src/Remediation/StellaOps.Remediation.Core/StellaOps.Remediation.Core.csproj` (net10.0, classlib)
|
||||||
|
- Create `src/Remediation/StellaOps.Remediation.WebService/StellaOps.Remediation.WebService.csproj` (net10.0, web)
|
||||||
|
- Create `src/Remediation/StellaOps.Remediation.Persistence/StellaOps.Remediation.Persistence.csproj` (net10.0, classlib)
|
||||||
|
- Create `src/Remediation/__Tests/StellaOps.Remediation.Tests/StellaOps.Remediation.Tests.csproj` (net10.0, test)
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All four .csproj files exist and target net10.0
|
||||||
|
- [ ] `dotnet build` succeeds for each project
|
||||||
|
|
||||||
|
### REM-02 - Domain models
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-01
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create FixTemplate.cs, PrSubmission.cs, Contributor.cs, MarketplaceSource.cs in Core/Models/
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All four model records exist with documented properties
|
||||||
|
- [ ] Models compile without warnings
|
||||||
|
|
||||||
|
### REM-03 - SQL migration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `001_remediation_registry_schema.sql` with tables: fix_templates, pr_submissions, contributors, marketplace_sources
|
||||||
|
- Include indexes on cve_id, purl, status
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Migration file exists with all four tables
|
||||||
|
- [ ] Indexes created for query-hot columns
|
||||||
|
|
||||||
|
### REM-04 - IRemediationRegistry interface and repository implementations
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-02
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Define IRemediationRegistry in Core/Abstractions/
|
||||||
|
- Create IFixTemplateRepository and PostgresFixTemplateRepository in Persistence/
|
||||||
|
- Create IPrSubmissionRepository and PostgresPrSubmissionRepository in Persistence/
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Interface defines CRUD for templates and submissions
|
||||||
|
- [ ] Repository interfaces and Postgres stubs exist
|
||||||
|
|
||||||
|
### REM-05 - IContributorTrustScorer
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-02
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Define IContributorTrustScorer in Core/Abstractions/
|
||||||
|
- Implement ContributorTrustScorer in Core/Services/
|
||||||
|
- Score formula: (verified * 1.0 - rejected * 0.5) / max(total, 1) clamped to [0, 1]
|
||||||
|
- Trust tiers: trusted (>0.8), established (>0.5), new (>0.2), untrusted
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Interface and implementation exist
|
||||||
|
- [ ] Unit tests validate score calculation and tier assignment
|
||||||
|
|
||||||
|
### REM-06 - WebService endpoints
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-04
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create RemediationRegistryEndpoints.cs with template and submission CRUD
|
||||||
|
- Create RemediationMatchEndpoints.cs for CVE/PURL matching
|
||||||
|
- Create RemediationSourceEndpoints.cs for marketplace source management
|
||||||
|
- Create RemediationContractModels.cs for API DTOs
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] All endpoint classes compile
|
||||||
|
- [ ] Routes follow /api/v1/remediation/* pattern
|
||||||
|
|
||||||
|
### REM-07 - Auth policies
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-06
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add remediation.read, remediation.submit, remediation.manage authorization policies
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Policies registered in Program.cs
|
||||||
|
- [ ] Endpoints use RequireAuthorization
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- New top-level module under src/Remediation/ — follows existing module patterns.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Module compiles and tests pass.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Sprint 20260220-011 — Signals Remediation Webhook Handler
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Add remediation PR detection to the Signals webhook pipeline.
|
||||||
|
- Detect PRs by title convention `fix(CVE-XXXX):` or label `stella-ops/remediation`.
|
||||||
|
- Extract CVE IDs and create PrSubmission records.
|
||||||
|
- Working directory: `src/Signals/StellaOps.Signals/`.
|
||||||
|
- Expected evidence: webhook handler service, unit tests.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 010 (Core models for PrSubmission).
|
||||||
|
- Can run in parallel with Sprints 012-015.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Sprint 010 domain models.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-08 - RemediationPrWebhookHandler service
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-02 (Sprint 010)
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Create `src/Signals/StellaOps.Signals/Services/RemediationPrWebhookHandler.cs`
|
||||||
|
- Implement IsRemediationPr() detection by title prefix and label
|
||||||
|
- Implement ExtractCveId() with regex extraction
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Handler detects remediation PRs by title and label
|
||||||
|
- [ ] CVE ID extraction works for standard CVE format
|
||||||
|
|
||||||
|
### REM-09 - Webhook handler unit tests
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-08
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add tests for IsRemediationPr and ExtractCveId in Signals test project
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Tests cover title-based detection, label-based detection, and CVE extraction
|
||||||
|
- [ ] Tests pass
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Title convention `fix(CVE-XXXX):` aligns with conventional commit standard.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Webhook handler tests pass.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Sprint 20260220-012 — Remediation Verification Pipeline
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Implement the verification pipeline that validates remediation PRs.
|
||||||
|
- Compare pre/post scan digests, reachability deltas, and produce fix-chain DSSE envelopes.
|
||||||
|
- Working directory: `src/Remediation/StellaOps.Remediation.Core/`.
|
||||||
|
- Expected evidence: IRemediationVerifier interface and implementation.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 010 (Core models).
|
||||||
|
- Can run in parallel with Sprints 011, 013-015.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Sprint 010 domain models and registry interface.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-13 - ReachGraph delta endpoint concept
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Document the conceptual POST /v1/reachability/delta endpoint for two-digest reachability diff
|
||||||
|
- This is a contract stub for future implementation
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Concept documented in sprint decisions
|
||||||
|
|
||||||
|
### REM-14 - IRemediationVerifier interface
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-02 (Sprint 010)
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Define IRemediationVerifier in Core/Services/
|
||||||
|
- Define VerificationResult record with verdict, digests, affected paths
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Interface defined with VerifyAsync method
|
||||||
|
- [ ] VerificationResult record defined
|
||||||
|
|
||||||
|
### REM-15 - RemediationVerifier implementation
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-14
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Implement verification logic in Core/Services/RemediationVerifier.cs
|
||||||
|
- Stub external dependencies (scan service, reachability service)
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Implementation compiles
|
||||||
|
- [ ] Verification produces deterministic results for test inputs
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- REM-13: ReachGraph delta is a conceptual contract; actual implementation deferred.
|
||||||
|
- Verification pipeline stubs external scan/reachability calls for initial implementation.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Verification pipeline compiles and stubs are testable.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Sprint 20260220-013 — Remediation Matching, Sources, and Policy
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Implement IRemediationMatcher for CVE/PURL-based fix template matching.
|
||||||
|
- Add IntegrationType.Marketplace to integration enums.
|
||||||
|
- Working directory: `src/Remediation/`, `src/Integrations/`.
|
||||||
|
- Expected evidence: matcher interface, integration enum update.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 010 (Core models and registry).
|
||||||
|
- Cross-module edit: `src/Integrations/__Libraries/StellaOps.Integrations.Core/IntegrationEnums.cs`.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Sprint 010 domain models.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-18 - IRemediationMatcher interface and implementation
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-04 (Sprint 010)
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Define IRemediationMatcher in Core/Abstractions/
|
||||||
|
- Implement matching logic that queries templates by CVE, PURL, and version
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Interface and implementation exist
|
||||||
|
- [ ] FindMatchesAsync filters by CVE, PURL, and version
|
||||||
|
|
||||||
|
### REM-20 - IntegrationType.Marketplace enum
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Developer
|
||||||
|
Task description:
|
||||||
|
- Add `Marketplace = 8` to IntegrationType enum
|
||||||
|
- Add providers: `CommunityFixes = 800, PartnerFixes = 801, VendorFixes = 802`
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Enum values added to IntegrationEnums.cs
|
||||||
|
- [ ] No compilation errors in Integrations module
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Cross-module edit to IntegrationEnums.cs is minimal and additive only.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Matcher compiles, enum values added.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# Sprint 20260220-014 — FE Remediation Marketplace UI
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Create Angular UI components for the remediation marketplace.
|
||||||
|
- Add API service, browse/detail/submit components, badge component.
|
||||||
|
- Add routes under /security-risk/remediation.
|
||||||
|
- Add sidebar navigation entry.
|
||||||
|
- Working directory: `src/Web/StellaOps.Web/src/app/`.
|
||||||
|
- Expected evidence: components compile, routes registered, sidebar entry visible.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprint 010 (backend API contracts for type alignment).
|
||||||
|
- Can run in parallel with backend sprints.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- Sprint 010 API contract models.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-21 - Remediation API service
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/remediation/remediation.api.ts`
|
||||||
|
- Implement RemediationApiService with HttpClient methods for templates, submissions, contributors, matching
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Service injectable with all API methods defined
|
||||||
|
- [ ] Uses /api/v1/remediation/* endpoints
|
||||||
|
|
||||||
|
### REM-22 - Remediation browse component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-21
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/remediation/remediation-browse.component.ts`
|
||||||
|
- Search by CVE/PURL, filter by trust/status, display fix cards
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component renders marketplace browse view
|
||||||
|
- [ ] OnPush change detection, standalone
|
||||||
|
|
||||||
|
### REM-23 - Remediation fix detail component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-21
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/remediation/remediation-fix-detail.component.ts`
|
||||||
|
- Show attestation chain, patch content, contributor trust, reachability delta
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component renders fix detail with attestation chain
|
||||||
|
- [ ] OnPush change detection, standalone
|
||||||
|
|
||||||
|
### REM-24 - Remediation submit component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-21
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/remediation/remediation-submit.component.ts`
|
||||||
|
- PR submit form with verification status pipeline timeline
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Component renders submit form and status timeline
|
||||||
|
- [ ] OnPush change detection, standalone
|
||||||
|
|
||||||
|
### REM-25 - Remediation fixes badge component
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-21
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Create `features/security-risk/remediation/remediation-fixes-badge.component.ts`
|
||||||
|
- Contextual "N Available Fixes" badge for vulnerability detail page
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Badge component renders fix count
|
||||||
|
- [ ] OnPush change detection, standalone
|
||||||
|
|
||||||
|
### REM-26 - Routes and sidebar registration
|
||||||
|
Status: TODO
|
||||||
|
Dependency: REM-22, REM-23, REM-24
|
||||||
|
Owners: FE Developer
|
||||||
|
Task description:
|
||||||
|
- Add remediation routes to security-risk.routes.ts
|
||||||
|
- Add sidebar entry under security-risk children in app-sidebar.component.ts
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Routes registered for /security-risk/remediation/*
|
||||||
|
- [ ] Sidebar shows Remediation entry under Security and Risk
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- UI follows existing security-risk feature patterns (standalone, OnPush, signals).
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- All components compile, routes work in dev.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Sprint 20260220-015 — Remediation Offline, CLI, and Documentation
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
- Create architecture documentation for the Remediation module.
|
||||||
|
- Create PR predicate schema contract documentation.
|
||||||
|
- Working directory: `docs/`.
|
||||||
|
- Expected evidence: architecture doc, contract doc.
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
- Depends on Sprints 010-014 for implementation details.
|
||||||
|
- Can be drafted in parallel.
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
- All prior Remediation sprints for implementation context.
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
### REM-27 - Remediation architecture documentation
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation author
|
||||||
|
Task description:
|
||||||
|
- Create `docs/modules/remediation/architecture.md`
|
||||||
|
- Document module overview, domain model, API surface, verification pipeline, trust scoring
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Architecture doc covers all key aspects of the module
|
||||||
|
- [ ] Links to relevant sprint tasks and contracts
|
||||||
|
|
||||||
|
### REM-28 - Remediation PR predicate schema contract
|
||||||
|
Status: TODO
|
||||||
|
Dependency: none
|
||||||
|
Owners: Documentation author
|
||||||
|
Task description:
|
||||||
|
- Create `docs/contracts/remediation-pr-v1.md`
|
||||||
|
- Document the fix-chain DSSE predicate schema for remediation PRs
|
||||||
|
|
||||||
|
Completion criteria:
|
||||||
|
- [ ] Contract doc defines predicate type, subject, fields
|
||||||
|
- [ ] Consistent with existing predicate schemas in docs/contracts/
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
| Date (UTC) | Update | Owner |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 2026-02-20 | Sprint created. | Planning |
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
- Documentation drafted from implementation; will be refined as features mature.
|
||||||
|
|
||||||
|
## Next Checkpoints
|
||||||
|
- Docs reviewed and linked from module README.
|
||||||
94
docs/modules/attestor/predicate-schema-registry.md
Normal file
94
docs/modules/attestor/predicate-schema-registry.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Predicate Schema Registry
|
||||||
|
|
||||||
|
## Status
|
||||||
|
- Status: DRAFT (2026-02-19)
|
||||||
|
- Owner: Attestor Guild
|
||||||
|
- Sprint: SPRINT_20260219_010
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Replace hardcoded predicate type URIs scattered across the codebase with a discoverable, versioned, PostgreSQL-backed registry. External tooling (cosign, policy-as-code engines, audit exporters) can query the registry to discover and validate predicate schemas.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
- Schema: `proofchain` (alongside existing proof chain tables)
|
||||||
|
- Table: `proofchain.predicate_type_registry`
|
||||||
|
|
||||||
|
### Data Model
|
||||||
|
Each registry entry:
|
||||||
|
| Column | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| `registry_id` | UUID | Primary key |
|
||||||
|
| `predicate_type_uri` | TEXT UNIQUE | The canonical predicate type URI |
|
||||||
|
| `display_name` | TEXT | Human-readable name |
|
||||||
|
| `version` | TEXT | Semver string (e.g., "1.0.0") |
|
||||||
|
| `category` | TEXT | Category: stella-core, stella-proof, ecosystem, intoto |
|
||||||
|
| `json_schema` | JSONB | JSON Schema document for payload validation (nullable) |
|
||||||
|
| `description` | TEXT | Purpose description |
|
||||||
|
| `is_active` | BOOLEAN | Whether this type accepts new submissions |
|
||||||
|
| `validation_mode` | TEXT | log-only / warn / reject (default: log-only) |
|
||||||
|
| `created_at` | TIMESTAMPTZ | Created timestamp |
|
||||||
|
| `updated_at` | TIMESTAMPTZ | Last update timestamp |
|
||||||
|
|
||||||
|
### Immutability Rule
|
||||||
|
Once a `(predicate_type_uri, version)` pair is published, its `json_schema` MUST NOT change. New versions get new semver.
|
||||||
|
|
||||||
|
### API Endpoints
|
||||||
|
- `GET /api/v1/attestor/predicates` — List all registered predicate types (paged, filterable by category and is_active)
|
||||||
|
- `GET /api/v1/attestor/predicates/{uri}` — Get schema and metadata for a specific predicate type URI (URI is URL-encoded)
|
||||||
|
- `POST /api/v1/attestor/predicates` — Register a new predicate type (admin-only, OpTok-gated with `attestor:admin` scope)
|
||||||
|
|
||||||
|
### Submission Validation
|
||||||
|
When a DSSE envelope is submitted via `POST /api/v1/rekor/entries`:
|
||||||
|
1. Look up `predicate_type` in registry
|
||||||
|
2. If found and `validation_mode = "log-only"`: validate payload against `json_schema`, log result (pass/mismatch), proceed
|
||||||
|
3. If found and `validation_mode = "warn"`: validate, emit warning metric, proceed
|
||||||
|
4. If found and `validation_mode = "reject"`: validate, reject on mismatch (400 Bad Request)
|
||||||
|
5. If not found: log unknown predicate type, proceed (don't block unregistered types during rollout)
|
||||||
|
|
||||||
|
### Seeded Predicate Types (from codebase analysis)
|
||||||
|
|
||||||
|
**stella-core (Attestor native):**
|
||||||
|
1. `https://stella-ops.org/predicates/sbom-linkage/v1`
|
||||||
|
2. `https://stella-ops.org/predicates/vex-verdict/v1`
|
||||||
|
3. `https://stella-ops.org/predicates/evidence/v1`
|
||||||
|
4. `https://stella-ops.org/predicates/reasoning/v1`
|
||||||
|
5. `https://stella-ops.org/predicates/proof-spine/v1`
|
||||||
|
6. `https://stella-ops.org/predicates/reachability-drift/v1`
|
||||||
|
7. `https://stella-ops.org/predicates/reachability-subgraph/v1`
|
||||||
|
8. `https://stella-ops.org/predicates/delta-verdict/v1`
|
||||||
|
9. `https://stella-ops.org/predicates/policy-decision/v1`
|
||||||
|
10. `https://stella-ops.org/predicates/unknowns-budget/v1`
|
||||||
|
11. `https://stella-ops.org/predicates/ai-code-guard/v1`
|
||||||
|
12. `https://stella-ops.org/predicates/fix-chain/v1`
|
||||||
|
13. `https://stella-ops.org/attestation/graph-root/v1`
|
||||||
|
|
||||||
|
**stella-proof (ProofChain predicates):**
|
||||||
|
14. `https://stella.ops/predicates/path-witness/v1`
|
||||||
|
15. `https://stella.ops/predicates/runtime-witness/v1`
|
||||||
|
16. `https://stella.ops/predicates/policy-decision@v2`
|
||||||
|
17. `https://stellaops.dev/predicates/binary-micro-witness@v1`
|
||||||
|
18. `https://stellaops.dev/predicates/binary-fingerprint-evidence@v1`
|
||||||
|
19. `https://stellaops.io/attestation/budget-check/v1`
|
||||||
|
20. `https://stellaops.dev/attestation/vex/v1`
|
||||||
|
21. `https://stellaops.dev/attestations/vex-override/v1`
|
||||||
|
22. `https://stellaops.dev/predicates/trust-verdict@v1`
|
||||||
|
23. `https://stellaops.io/attestation/v1/signed-exception`
|
||||||
|
24. `https://stellaops.dev/attestation/verification-report/v1`
|
||||||
|
|
||||||
|
**stella-delta (Delta predicates):**
|
||||||
|
25. `stella.ops/changetrace@v1`
|
||||||
|
26. `stella.ops/vex-delta@v1`
|
||||||
|
27. `stella.ops/sbom-delta@v1`
|
||||||
|
28. `stella.ops/verdict-delta@v1`
|
||||||
|
29. `stellaops.binarydiff.v1`
|
||||||
|
|
||||||
|
**ecosystem (Standard predicates):**
|
||||||
|
30. `https://spdx.dev/Document`
|
||||||
|
31. `https://cyclonedx.org/bom`
|
||||||
|
32. `https://slsa.dev/provenance`
|
||||||
|
|
||||||
|
**intoto (In-Toto standard):**
|
||||||
|
33. `https://in-toto.io/Statement/v1`
|
||||||
|
34. `https://in-toto.io/Link/v1`
|
||||||
|
35. `https://in-toto.io/Layout/v1`
|
||||||
@@ -23,6 +23,7 @@ The OCI Distribution Spec v1.1 introduced the native referrers API (), which ena
|
|||||||
| **Harbor 1.x** | No | Yes | N/A | Fallback only |
|
| **Harbor 1.x** | No | Yes | N/A | Fallback only |
|
||||||
| **Quay.io** | Partial | Yes | Limited | Support varies by version and configuration |
|
| **Quay.io** | Partial | Yes | Limited | Support varies by version and configuration |
|
||||||
| **JFrog Artifactory** | Partial | Yes | Limited | Requires OCI layout repository type |
|
| **JFrog Artifactory** | Partial | Yes | Limited | Requires OCI layout repository type |
|
||||||
|
| **GitLab Container Registry** | No | Yes | N/A | Stores OCI artifacts with `subject` field but does not expose referrers endpoint; use tag-based fallback or GitLab-specific APIs |
|
||||||
| **Zot** | Yes | Yes | Yes | Full OCI 1.1 support |
|
| **Zot** | Yes | Yes | Yes | Full OCI 1.1 support |
|
||||||
| **Distribution (registry:2)** | No | Yes | N/A | Reference implementation without referrers API |
|
| **Distribution (registry:2)** | No | Yes | N/A | Reference implementation without referrers API |
|
||||||
|
|
||||||
@@ -60,7 +61,9 @@ The OCI Distribution Spec v1.1 introduced the native referrers API (), which ena
|
|||||||
- **Fallback**: Yes, as backup
|
- **Fallback**: Yes, as backup
|
||||||
- **Authentication**: Google Cloud service account or gcloud auth
|
- **Authentication**: Google Cloud service account or gcloud auth
|
||||||
- **Rate Limits**: Generous; project quotas apply
|
- **Rate Limits**: Generous; project quotas apply
|
||||||
- **Known Issues**: None significant
|
- **Known Issues**:
|
||||||
|
- Google Artifact Registry also exposes an **attachments model** (`gcloud artifacts attachments list`) as an alternative metadata UX alongside the standard OCI referrers endpoint. StellaOps uses the standard OCI API; the Google-specific attachments API is not required.
|
||||||
|
- Some non-Docker format features may be in public preview; Docker/OCI artifact discovery is stable.
|
||||||
|
|
||||||
### Amazon Elastic Container Registry (ECR)
|
### Amazon Elastic Container Registry (ECR)
|
||||||
|
|
||||||
@@ -89,16 +92,18 @@ The OCI Distribution Spec v1.1 introduced the native referrers API (), which ena
|
|||||||
- **Known Issues**:
|
- **Known Issues**:
|
||||||
- Harbor 1.x does not support referrers API
|
- Harbor 1.x does not support referrers API
|
||||||
- Project-level permissions required
|
- Project-level permissions required
|
||||||
|
- Harbor UI may display cosign signatures or SBOM referrers as **"UNKNOWN"** artifact type in versions around v2.15+; this is a Harbor UI classification issue and does not affect API-level discovery or StellaOps functionality
|
||||||
|
|
||||||
### Quay.io / Red Hat Quay
|
### Quay.io / Red Hat Quay
|
||||||
|
|
||||||
- **API Support**: Partial (version-dependent)
|
- **API Support**: Partial (version-dependent); Red Hat has announced full OCI Referrers API support on Quay.io
|
||||||
- **Fallback**: Yes
|
- **Fallback**: Yes
|
||||||
- **Authentication**: Robot account or OAuth token
|
- **Authentication**: Robot account or OAuth token
|
||||||
- **Rate Limits**: Account tier dependent
|
- **Rate Limits**: Account tier dependent
|
||||||
- **Known Issues**:
|
- **Known Issues**:
|
||||||
- Support varies significantly by version
|
- Support varies significantly by version
|
||||||
- Some deployments may have referrers API disabled
|
- Some deployments may have referrers API disabled
|
||||||
|
- Self-hosted Quay deployments may require **admin toggles or deployment flags** to enable the referrers API; if referrer discovery is inconsistent, verify the feature is enabled in the Quay configuration
|
||||||
|
|
||||||
### JFrog Artifactory
|
### JFrog Artifactory
|
||||||
|
|
||||||
@@ -110,6 +115,17 @@ The OCI Distribution Spec v1.1 introduced the native referrers API (), which ena
|
|||||||
- Repository must be configured as Docker with OCI layout
|
- Repository must be configured as Docker with OCI layout
|
||||||
- Referrers API requires Artifactory 7.x+
|
- Referrers API requires Artifactory 7.x+
|
||||||
|
|
||||||
|
### GitLab Container Registry
|
||||||
|
|
||||||
|
- **API Support**: No native referrers API
|
||||||
|
- **Fallback**: Yes, required for all referrer discovery
|
||||||
|
- **Authentication**: GitLab deploy token, personal access token, or CI job token with `read_registry` scope
|
||||||
|
- **Rate Limits**: Instance-dependent
|
||||||
|
- **Known Issues**:
|
||||||
|
- Stores OCI artifacts with `subject` field but does not expose a referrers endpoint
|
||||||
|
- Referrer discovery must use tag-schema fallback or GitLab-specific APIs
|
||||||
|
- Discovery behavior mirrors GHCR: push referrers with tag-schema pattern and enumerate via tag listing
|
||||||
|
|
||||||
## Discovery Methods
|
## Discovery Methods
|
||||||
|
|
||||||
### Native Referrers API (OCI 1.1)
|
### Native Referrers API (OCI 1.1)
|
||||||
|
|||||||
@@ -26,15 +26,22 @@ This document captures the gap analysis between the competitive moat advisory an
|
|||||||
|
|
||||||
| Feature | Moat | Current % | Key Gaps | Sprint Coverage |
|
| Feature | Moat | Current % | Key Gaps | Sprint Coverage |
|
||||||
|---------|------|-----------|----------|-----------------|
|
|---------|------|-----------|----------|-----------------|
|
||||||
| Signed, replayable risk verdicts | 5 | 70% | OCI push, one-command replay | 4300_0001_* |
|
| Signed, replayable risk verdicts | 5 | 85% | OCI push polish | 4300_0001_* |
|
||||||
| VEX decisioning engine | 4 | 85% | Evidence hooks | Minimal |
|
| VEX decisioning engine | 4 | 90% | Evidence hooks polish | Minimal |
|
||||||
| Reachability with proof | 4 | 75% | Standalone artifact | 4400_0001_0002 |
|
| Reachability with proof | 4 | 85% | Standalone artifact polish | 4400_0001_0002 |
|
||||||
| Smart-Diff semantic delta | 4 | 80% | Signed delta verdict | 4400_0001_0001 |
|
| Smart-Diff semantic delta | 4 | 85% | Signed delta verdict | 4400_0001_0001 |
|
||||||
| Unknowns as first-class state | 4 | 75% | Policy budgets, attestations | 4300_0002_* |
|
| Unknowns as first-class state | 4 | 80% | Policy budgets, attestations | 4300_0002_* |
|
||||||
| Air-gapped epistemic mode | 4 | 70% | Sealed snapshot workflow | 4300_0003_0001 |
|
| Air-gapped epistemic mode | 4 | 80% | Sealed snapshot workflow | 4300_0003_0001 |
|
||||||
| SBOM ledger + lineage | 3 | 60% | Historical tracking, BYOS | 4600_0001_* |
|
| SBOM ledger + lineage | 3 | 70% | Historical tracking, BYOS | 4600_0001_* |
|
||||||
| Policy engine with proofs | 3 | 85% | Compilation to artifact | Minimal |
|
| Policy engine with proofs | 3 | 90% | Compilation to artifact | Minimal |
|
||||||
| VEX distribution network | 3-4 | 30% | Hub layer entirely | 4500_0001_* |
|
| VEX distribution network | 3-4 | 50% | Hub layer refinement | 4500_0001_* |
|
||||||
|
| Symbolized call-stack proofs | 4 | 95% | Rust/Ruby/PHP language support | Sprint 0401+, 20260220_001-002 (marketplace) |
|
||||||
|
| Deterministic signed scoring | 5 | 85% | SLO formalization | Existing |
|
||||||
|
| Rekor size-aware pointer strategy | 4 | 90% | Documentation polish | Existing |
|
||||||
|
| Signed execution evidence | 3-4 | 40% | Trace-to-DSSE pipeline, policy gate | 20260219_013 |
|
||||||
|
| Runtime beacon attestations | 3 | 20% | Beacon fact type, attestation pipeline | 20260219_014 |
|
||||||
|
| Privacy-preserving federated telemetry | 5 | 0% | Full stack: privacy primitives, sync, API, UI | 20260220_005-009 |
|
||||||
|
| Remediation marketplace (signed-PR fixes) | 4 | 0% | Full stack: registry, webhook, verification, UI | 20260220_010-015 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -209,6 +216,106 @@ This document captures the gap analysis between the competitive moat advisory an
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 10. Signed Execution Evidence (Moat 3-4)
|
||||||
|
|
||||||
|
> *Added 2026-02-19 from advisory review (rescoped from external "sandbox traces" proposal).*
|
||||||
|
|
||||||
|
**What exists:**
|
||||||
|
- `RuntimeTracesEndpoints` — runtime trace ingestion in Findings module
|
||||||
|
- `RuntimeSignalIngester` — containment/blast-radius signal ingestion in Unknowns
|
||||||
|
- `SignalSnapshotBuilder` — signal snapshot composition for replay/audit
|
||||||
|
- Signals `POST /signals/runtime-facts` — runtime fact ingestion (eBPF/ETW)
|
||||||
|
- `InMemoryRuntimeInstrumentationServices` — address canonicalization, hot-symbol aggregation
|
||||||
|
|
||||||
|
**Gaps:**
|
||||||
|
| Gap | Sprint |
|
||||||
|
|-----|--------|
|
||||||
|
| `executionEvidence@v1` predicate type | 20260219_013 (SEE-01) |
|
||||||
|
| Trace-to-DSSE pipeline (canonicalize → aggregate → sign) | 20260219_013 (SEE-02) |
|
||||||
|
| Policy gate: require execution evidence before promotion | 20260219_013 (SEE-03) |
|
||||||
|
| Execution evidence in audit packs | 20260219_013 (SEE-04) |
|
||||||
|
|
||||||
|
**Moat Thesis**: "We don't just claim it ran — we provide signed, replayable proof of execution with deterministic trace summarization."
|
||||||
|
|
||||||
|
**Moat Strategy**: Elevates from Level 3 (runtime instrumentation exists elsewhere) to Level 4 when combined with existing proof chain (signed execution evidence + verdict + reachability = attestable decision lifecycle).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. Runtime Beacon Attestations (Moat 3)
|
||||||
|
|
||||||
|
> *Added 2026-02-19 from advisory review (rescoped from external "canary beacons" proposal).*
|
||||||
|
|
||||||
|
**What exists:**
|
||||||
|
- Signals runtime-facts ingestion pipeline
|
||||||
|
- Zastava module (planned runtime protection/admission controller)
|
||||||
|
- Doctor module runtime host capabilities (eBPF, ETW, dyld agents)
|
||||||
|
|
||||||
|
**Gaps:**
|
||||||
|
| Gap | Sprint |
|
||||||
|
|-----|--------|
|
||||||
|
| `beacon` fact type in Signals | 20260219_014 (BEA-01) |
|
||||||
|
| `beaconAttestation@v1` predicate type | 20260219_014 (BEA-01) |
|
||||||
|
| Beacon ingestion + batched attestation pipeline | 20260219_014 (BEA-02) |
|
||||||
|
| Beacon verification rate as policy input | 20260219_014 (BEA-03) |
|
||||||
|
| Beacon attestations in audit packs | 20260219_014 (BEA-04) |
|
||||||
|
|
||||||
|
**Moat Thesis**: "Low-volume signed proof that this artifact actually ran in this environment — verifiable offline, no image modification required."
|
||||||
|
|
||||||
|
**Moat Strategy**: Level 3 standalone; combined with execution evidence and proof chain, contributes to the "attestable decision lifecycle" story for compliance-oriented customers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 12. Privacy-Preserving Federated Runtime Telemetry (New L5 — Structural)
|
||||||
|
|
||||||
|
> *Added 2026-02-19 from moat-gap advisory.*
|
||||||
|
|
||||||
|
**What exists:**
|
||||||
|
- Signals runtime-facts ingestion pipeline (eBPF/ETW/dyld)
|
||||||
|
- FederationHub / CrossRegionSync for bundle transport
|
||||||
|
- DsseEnvelope signing infrastructure
|
||||||
|
- AirGap egress policy enforcement
|
||||||
|
|
||||||
|
**Implementation (Sprints 20260220_005-009):**
|
||||||
|
| Component | Sprint |
|
||||||
|
|-----------|--------|
|
||||||
|
| Privacy primitives (k-anonymity, DP, epsilon budget) | 20260220_005 (FPT-01 → FPT-07) |
|
||||||
|
| Federation sync + intelligence merger | 20260220_006 (FTS-01 → FTS-06) |
|
||||||
|
| API endpoints + CLI + Doctor plugin | 20260220_007 (FAC-01 → FAC-05) |
|
||||||
|
| UI (5 pages under Platform Ops) | 20260220_008 (FUI-01 → FUI-07) |
|
||||||
|
| Documentation + contracts | 20260220_009 (FDC-01 → FDC-05) |
|
||||||
|
|
||||||
|
**Moat Thesis**: "We share exploit intelligence across sites without sharing raw code — privacy-preserving, consent-proven, offline-compatible."
|
||||||
|
|
||||||
|
**Moat Strategy**: No competitor has DP + k-anonymity over federated runtime signals with DSSE consent. Network-effect moat: each new participant enriches the shared corpus. Combined with existing proof chain, creates attestable federated intelligence lifecycle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 13. Developer-Facing Signed-PR Remediation Marketplace (New L4 — Strong)
|
||||||
|
|
||||||
|
> *Added 2026-02-19 from moat-gap advisory.*
|
||||||
|
|
||||||
|
**What exists:**
|
||||||
|
- FixChainAttestationService (DSSE-signed fix chain proofs)
|
||||||
|
- SCM webhook pipeline in Signals
|
||||||
|
- ReachGraph for reachability delta computation
|
||||||
|
- Integration Hub plugin framework
|
||||||
|
|
||||||
|
**Implementation (Sprints 20260220_010-015):**
|
||||||
|
| Component | Sprint |
|
||||||
|
|-----------|--------|
|
||||||
|
| Registry + persistence + domain models | 20260220_010 (REM-01 → REM-07) |
|
||||||
|
| Signals webhook handler | 20260220_011 (REM-08 → REM-12) |
|
||||||
|
| Verification pipeline (scan → delta → attest) | 20260220_012 (REM-13 → REM-17) |
|
||||||
|
| Matching + marketplace sources + policy | 20260220_013 (REM-18 → REM-22) |
|
||||||
|
| UI (3 pages + contextual badge) | 20260220_014 (REM-23 → REM-27) |
|
||||||
|
| Offline bundles + CLI + docs | 20260220_015 (REM-28 → REM-32) |
|
||||||
|
|
||||||
|
**Moat Thesis**: "Every remediation PR is verified against reachability proof deltas and cryptographically attested — not just a patch, but proof the fix actually reduces exploitable surface."
|
||||||
|
|
||||||
|
**Moat Strategy**: No competitor has PR-level fix attestations verified against reachability proof deltas. Six-module integration depth (Attestor + ReachGraph + Signals + Scanner + Policy + EvidenceLocker) creates deep switching cost.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Sprint Roadmap
|
## Sprint Roadmap
|
||||||
|
|
||||||
### Phase 1: Moat 5 Anchor (P0)
|
### Phase 1: Moat 5 Anchor (P0)
|
||||||
@@ -246,15 +353,46 @@ This document captures the gap analysis between the competitive moat advisory an
|
|||||||
└── SBOM becomes historical
|
└── SBOM becomes historical
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Phase 5: Runtime Evidence (P2-P3)
|
||||||
|
```
|
||||||
|
20260219_013 (SEE-01 → SEE-04)
|
||||||
|
│
|
||||||
|
└── Execution becomes attestable
|
||||||
|
|
||||||
|
20260219_014 (BEA-01 → BEA-04)
|
||||||
|
│
|
||||||
|
└── Presence becomes provable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 6: Moat Expansion — Three New Capabilities (P1)
|
||||||
|
```
|
||||||
|
20260220_001 → 20260220_002 → 20260220_003
|
||||||
|
│
|
||||||
|
└── Symbol Marketplace (L4 @ 95%)
|
||||||
|
|
||||||
|
20260220_005 → 20260220_006 → 20260220_007 → 20260220_008
|
||||||
|
│
|
||||||
|
└── Federated Telemetry (New L5)
|
||||||
|
|
||||||
|
20260220_010 → 20260220_011 → 20260220_012 → 20260220_013 → 20260220_014
|
||||||
|
│
|
||||||
|
└── Remediation Marketplace (New L4)
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Competitive Positioning Summary
|
## Competitive Positioning Summary
|
||||||
|
|
||||||
### Where StellaOps Is Strong
|
### Where StellaOps Is Strong
|
||||||
1. **VEX decisioning** — Multi-mode consensus engine is ahead of competitors
|
1. **VEX decisioning** — Multi-mode consensus engine is ahead of all competitors (including Docker Scout, JFrog)
|
||||||
2. **Smart-Diff** — R1-R4 rules with priority scoring is unique
|
2. **Smart-Diff** — R1-R4 rules with priority scoring is unique
|
||||||
3. **Policy engine** — OPA/Rego with proof output is mature
|
3. **Policy engine** — OPA/Rego with proof output is mature
|
||||||
4. **Attestor** — in-toto/DSSE infrastructure is complete
|
4. **Attestor** — in-toto/DSSE infrastructure is complete
|
||||||
|
5. **Symbolized call-stack proofs** — No competitor (Docker Scout, Trivy, JFrog) delivers function-level symbol evidence with demangled names and build-ID binding
|
||||||
|
6. **Deterministic signed scoring** — JFrog centralizes evidence but can't replay; Stella produces seeded, verifiable scoring envelopes
|
||||||
|
7. **Rekor size-aware strategy** — Hash pointer in Rekor + full payload in Evidence Locker solves real ~100KB upload constraints
|
||||||
|
8. **Federated telemetry** — Privacy-preserving cross-site exploit intelligence with DP + k-anonymity + DSSE consent proofs
|
||||||
|
9. **Remediation marketplace** — Signed-PR fix attestations verified against reachability proof deltas with contributor trust scoring
|
||||||
|
|
||||||
### Where StellaOps Must Improve
|
### Where StellaOps Must Improve
|
||||||
1. **Verdict portability** — OCI push makes verdicts first-class artifacts
|
1. **Verdict portability** — OCI push makes verdicts first-class artifacts
|
||||||
@@ -266,6 +404,8 @@ This document captures the gap analysis between the competitive moat advisory an
|
|||||||
- **Snyk**: Don't compete on developer UX; compete on proof-carrying reachability
|
- **Snyk**: Don't compete on developer UX; compete on proof-carrying reachability
|
||||||
- **Prisma**: Don't compete on CNAPP breadth; compete on decision integrity
|
- **Prisma**: Don't compete on CNAPP breadth; compete on decision integrity
|
||||||
- **Anchore**: Don't compete on SBOM storage; compete on semantic diff + VEX reasoning
|
- **Anchore**: Don't compete on SBOM storage; compete on semantic diff + VEX reasoning
|
||||||
|
- **Docker Scout**: Don't compete on registry-native DHI integration; compete on call-stack symbolization, replay, and lattice VEX
|
||||||
|
- **JFrog**: Don't compete on artifact management breadth; compete on deterministic scoring, replayable verdicts, and function-level proofs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
104
docs/modules/policy/gates/beacon-rate-gate.md
Normal file
104
docs/modules/policy/gates/beacon-rate-gate.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# Beacon Verification Rate Gate
|
||||||
|
|
||||||
|
**Gate ID:** `beacon-rate`
|
||||||
|
|
||||||
|
Enforces minimum beacon verification rate for runtime canary coverage. When enabled, blocks or warns for releases where beacon coverage is insufficient in a required environment.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. Checks if the target environment requires beacon coverage (configurable per environment)
|
||||||
|
2. Reads beacon telemetry data from the policy context
|
||||||
|
3. If no beacon data exists, applies the configured missing-beacon action (warn or block)
|
||||||
|
4. If beacon count is below the minimum, defers rate enforcement (insufficient sample size)
|
||||||
|
5. Compares verification rate against threshold, returns pass, warn, or block
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PolicyGates": {
|
||||||
|
"BeaconRate": {
|
||||||
|
"Enabled": false,
|
||||||
|
"BelowThresholdAction": "Warn",
|
||||||
|
"MissingBeaconAction": "Warn",
|
||||||
|
"MinVerificationRate": 0.8,
|
||||||
|
"RequiredEnvironments": ["production"],
|
||||||
|
"MinBeaconCount": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|--------|------|---------|-------------|
|
||||||
|
| `Enabled` | bool | `false` | Whether the gate is active (opt-in) |
|
||||||
|
| `BelowThresholdAction` | enum | `Warn` | Action when rate is below threshold: `Warn` or `Block` |
|
||||||
|
| `MissingBeaconAction` | enum | `Warn` | Action when no beacon data exists: `Warn` or `Block` |
|
||||||
|
| `MinVerificationRate` | double | `0.8` | Minimum acceptable verification rate (0.0–1.0) |
|
||||||
|
| `RequiredEnvironments` | string[] | `["production"]` | Environments requiring beacon coverage |
|
||||||
|
| `MinBeaconCount` | int | `10` | Minimum beacons before rate enforcement applies |
|
||||||
|
|
||||||
|
## Context Metadata Keys
|
||||||
|
|
||||||
|
The gate reads the following keys from `PolicyGateContext.Metadata`:
|
||||||
|
|
||||||
|
| Key | Type | Description |
|
||||||
|
|-----|------|-------------|
|
||||||
|
| `beacon_verification_rate` | double string | Verification rate (0.0–1.0) |
|
||||||
|
| `beacon_verified_count` | int string | Number of verified beacon events |
|
||||||
|
|
||||||
|
## Beacon Verification States
|
||||||
|
|
||||||
|
| State | Description | Default Behavior |
|
||||||
|
|-------|-------------|------------------|
|
||||||
|
| No data | No beacon telemetry available | Depends on `MissingBeaconAction` |
|
||||||
|
| Insufficient count | Fewer beacons than `MinBeaconCount` | Rate enforcement deferred (pass with warning) |
|
||||||
|
| Below threshold | Rate < `MinVerificationRate` | Depends on `BelowThresholdAction` |
|
||||||
|
| Above threshold | Rate >= `MinVerificationRate` | Pass |
|
||||||
|
|
||||||
|
## Example Gate Results
|
||||||
|
|
||||||
|
**Pass:**
|
||||||
|
```
|
||||||
|
Beacon verification rate (95.0%) meets threshold (80.0%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pass (environment not required):**
|
||||||
|
```
|
||||||
|
Beacon rate not required for environment 'dev'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pass (insufficient sample):**
|
||||||
|
```
|
||||||
|
Beacon count (3) below minimum (10); rate enforcement deferred
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warn (below threshold):**
|
||||||
|
```
|
||||||
|
Beacon verification rate (60.0%) is below threshold (warn mode)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fail (no data, block mode):**
|
||||||
|
```
|
||||||
|
No beacon telemetry data available for this artifact
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fail (below threshold, block mode):**
|
||||||
|
```
|
||||||
|
Beacon verification rate (60.0%) is below threshold (80.0%)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
This gate consumes beacon verification rate data derived from `stella.ops/beaconAttestation@v1` predicates. The rate is computed by the Signals beacon pipeline as `verified_beacons / expected_beacons` over a configurable lookback window.
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `docs/contracts/beacon-attestation-v1.md` — Predicate contract
|
||||||
|
- `docs/modules/policy/gates/execution-evidence-gate.md` — Companion execution evidence gate
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last updated: 2026-02-19.*
|
||||||
96
docs/modules/policy/gates/execution-evidence-gate.md
Normal file
96
docs/modules/policy/gates/execution-evidence-gate.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Execution Evidence Gate
|
||||||
|
|
||||||
|
**Gate ID:** `execution-evidence`
|
||||||
|
|
||||||
|
Enforces that an artifact has signed execution evidence from a specific environment before promotion. Ensures artifacts are observed running (with sufficient trace quality) before advancing through the release pipeline.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. Checks if the target environment requires execution evidence (configurable per environment)
|
||||||
|
2. Reads execution evidence metadata from the policy context
|
||||||
|
3. If no evidence exists, applies the configured action (warn or block)
|
||||||
|
4. If evidence exists, validates trace quality (minimum hot symbols and unique call paths)
|
||||||
|
5. Returns pass, warn, or block result
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PolicyGates": {
|
||||||
|
"ExecutionEvidence": {
|
||||||
|
"Enabled": false,
|
||||||
|
"MissingEvidenceAction": "Warn",
|
||||||
|
"RequiredEnvironments": ["production"],
|
||||||
|
"MinHotSymbolCount": 3,
|
||||||
|
"MinUniqueCallPaths": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|--------|------|---------|-------------|
|
||||||
|
| `Enabled` | bool | `false` | Whether the gate is active (opt-in) |
|
||||||
|
| `MissingEvidenceAction` | enum | `Warn` | Action when evidence is missing: `Warn` or `Block` |
|
||||||
|
| `RequiredEnvironments` | string[] | `["production"]` | Environments that require execution evidence |
|
||||||
|
| `MinHotSymbolCount` | int | `3` | Minimum hot symbols for sufficient trace quality |
|
||||||
|
| `MinUniqueCallPaths` | int | `1` | Minimum unique call paths for sufficient trace quality |
|
||||||
|
|
||||||
|
## Context Metadata Keys
|
||||||
|
|
||||||
|
The gate reads the following keys from `PolicyGateContext.Metadata`:
|
||||||
|
|
||||||
|
| Key | Type | Description |
|
||||||
|
|-----|------|-------------|
|
||||||
|
| `has_execution_evidence` | `"true"/"false"` | Whether execution evidence exists |
|
||||||
|
| `execution_evidence_hot_symbol_count` | int string | Number of hot symbols in the evidence |
|
||||||
|
| `execution_evidence_unique_call_paths` | int string | Number of unique call paths |
|
||||||
|
|
||||||
|
## Example Gate Results
|
||||||
|
|
||||||
|
**Pass (evidence meets quality):**
|
||||||
|
```
|
||||||
|
Execution evidence meets quality thresholds (hot symbols: 42, call paths: 17)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pass (environment not required):**
|
||||||
|
```
|
||||||
|
Execution evidence not required for environment 'staging'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warn (no evidence, warn mode):**
|
||||||
|
```
|
||||||
|
No execution evidence found for this artifact (warn mode)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fail (no evidence, block mode):**
|
||||||
|
```
|
||||||
|
No execution evidence found for this artifact in required environment
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fail (insufficient quality):**
|
||||||
|
```
|
||||||
|
Execution evidence trace quality is insufficient: hot symbols 1 < 3 or call paths 0 < 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
This gate consumes `stella.ops/executionEvidence@v1` predicates generated by the Signals execution evidence pipeline. Evidence is populated in the policy context during release evaluation.
|
||||||
|
|
||||||
|
Typical flow:
|
||||||
|
1. Artifact runs in staging environment
|
||||||
|
2. Signals captures runtime trace via eBPF/ETW
|
||||||
|
3. `ExecutionEvidenceBuilder` generates signed predicate
|
||||||
|
4. Release promotion to production triggers policy evaluation
|
||||||
|
5. This gate verifies execution evidence exists from staging
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- `docs/contracts/execution-evidence-v1.md` — Predicate contract
|
||||||
|
- `docs/modules/policy/gates/beacon-rate-gate.md` — Companion beacon rate gate
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last updated: 2026-02-19.*
|
||||||
137
docs/modules/remediation/architecture.md
Normal file
137
docs/modules/remediation/architecture.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Remediation Module Architecture
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Remediation module provides a developer-facing signed-PR remediation marketplace for the Stella Ops platform. It enables developers to discover, apply, and verify community-contributed or vendor-supplied fix templates for known vulnerabilities (CVEs).
|
||||||
|
|
||||||
|
## Key Concepts
|
||||||
|
|
||||||
|
### Fix Templates
|
||||||
|
Structured remediation patches tied to specific CVE + PURL combinations. Templates include unified diff content, version range applicability, and trust scores from contributor history.
|
||||||
|
|
||||||
|
### PR Submissions
|
||||||
|
Tracks the lifecycle of a remediation pull request from submission through scanning, merging, and post-merge verification. Each submission produces attestation evidence including reachability deltas and fix-chain DSSE envelopes.
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
Community members or vendors who submit fix templates. Each contributor has a trust score computed from their verification history (verified fixes, rejections).
|
||||||
|
|
||||||
|
### Marketplace Sources
|
||||||
|
Curated collections of fix templates from community, partner, or vendor origins. Sources are rated independently and can be enabled/disabled per tenant.
|
||||||
|
|
||||||
|
## Domain Model
|
||||||
|
|
||||||
|
```
|
||||||
|
FixTemplate (remediation.fix_templates)
|
||||||
|
├── CveId (text, indexed)
|
||||||
|
├── Purl (text, indexed — pkg:type/name)
|
||||||
|
├── VersionRange (semver range)
|
||||||
|
├── PatchContent (unified diff)
|
||||||
|
├── Status (pending/verified/rejected)
|
||||||
|
├── TrustScore (0.0–1.0)
|
||||||
|
├── DsseDigest (nullable — signed envelope hash)
|
||||||
|
└── ContributorId / SourceId (foreign keys)
|
||||||
|
|
||||||
|
PrSubmission (remediation.pr_submissions)
|
||||||
|
├── FixTemplateId (nullable FK)
|
||||||
|
├── PrUrl, RepositoryUrl, SourceBranch, TargetBranch
|
||||||
|
├── CveId (text, indexed)
|
||||||
|
├── Status (opened/scanning/merged/verified/failed/inconclusive)
|
||||||
|
├── PreScanDigest, PostScanDigest
|
||||||
|
├── ReachabilityDeltaDigest, FixChainDsseDigest
|
||||||
|
├── Verdict (fixed/partial/not_fixed/inconclusive)
|
||||||
|
└── ContributorId
|
||||||
|
|
||||||
|
Contributor (remediation.contributors)
|
||||||
|
├── Username (unique)
|
||||||
|
├── VerifiedFixes, TotalSubmissions, RejectedSubmissions
|
||||||
|
└── TrustScore (computed)
|
||||||
|
|
||||||
|
MarketplaceSource (remediation.marketplace_sources)
|
||||||
|
├── Key (unique)
|
||||||
|
├── SourceType (community/partner/vendor)
|
||||||
|
├── Enabled, TrustScore
|
||||||
|
└── LastSyncAt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Trust Scoring
|
||||||
|
|
||||||
|
Contributor trust score formula:
|
||||||
|
```
|
||||||
|
score = clamp((verified * 1.0 - rejected * 0.5) / max(total, 1), 0, 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Trust tiers:
|
||||||
|
- **trusted** (> 0.8): Verified track record
|
||||||
|
- **established** (> 0.5): Growing history
|
||||||
|
- **new** (> 0.2): Recently joined
|
||||||
|
- **untrusted** (<= 0.2): Insufficient or negative history
|
||||||
|
|
||||||
|
## API Surface
|
||||||
|
|
||||||
|
All endpoints under `/api/v1/remediation/`.
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
- `GET /templates` — List fix templates (filter by CVE, PURL)
|
||||||
|
- `GET /templates/{id}` — Get template detail
|
||||||
|
- `POST /templates` — Create template (requires `remediation.submit`)
|
||||||
|
|
||||||
|
### Submissions
|
||||||
|
- `GET /submissions` — List PR submissions
|
||||||
|
- `GET /submissions/{id}` — Get submission with attestation chain
|
||||||
|
- `POST /submissions` — Submit PR for verification
|
||||||
|
- `GET /submissions/{id}/status` — Pipeline status
|
||||||
|
|
||||||
|
### Matching
|
||||||
|
- `GET /match?cve=...&purl=...&version=...` — Find applicable fix templates
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
- `GET /contributors` — List contributors
|
||||||
|
- `GET /contributors/{username}` — Profile with trust score
|
||||||
|
|
||||||
|
### Sources
|
||||||
|
- `GET /sources` — List marketplace sources
|
||||||
|
- `GET /sources/{key}` — Source detail
|
||||||
|
- `POST /sources` — Create/update source (requires `remediation.manage`)
|
||||||
|
|
||||||
|
## Authorization Policies
|
||||||
|
|
||||||
|
| Policy | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `remediation.read` | Read templates, submissions, contributors, sources |
|
||||||
|
| `remediation.submit` | Create templates and submit PRs |
|
||||||
|
| `remediation.manage` | Manage marketplace sources, verify/reject templates |
|
||||||
|
|
||||||
|
## Verification Pipeline
|
||||||
|
|
||||||
|
1. PR submitted (status: `opened`)
|
||||||
|
2. Pre-merge scan captures baseline SBOM digest
|
||||||
|
3. PR merged (status: `merged`)
|
||||||
|
4. Post-merge scan captures updated SBOM digest
|
||||||
|
5. Reachability delta computed between pre/post digests
|
||||||
|
6. Fix-chain DSSE envelope signed
|
||||||
|
7. Verdict determined: `fixed`, `partial`, `not_fixed`, or `inconclusive`
|
||||||
|
|
||||||
|
## Webhook Integration
|
||||||
|
|
||||||
|
The `RemediationPrWebhookHandler` in the Signals module detects remediation PRs by:
|
||||||
|
- Title convention: `fix(CVE-XXXX-NNNNN): description`
|
||||||
|
- Label: `stella-ops/remediation`
|
||||||
|
|
||||||
|
## Module Location
|
||||||
|
|
||||||
|
```
|
||||||
|
src/Remediation/
|
||||||
|
├── StellaOps.Remediation.Core/ — Domain models, interfaces, services
|
||||||
|
├── StellaOps.Remediation.WebService/ — API endpoints, Program.cs
|
||||||
|
├── StellaOps.Remediation.Persistence/ — SQL migrations, repositories
|
||||||
|
└── __Tests/StellaOps.Remediation.Tests/ — Unit tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Sprints
|
||||||
|
|
||||||
|
- SPRINT_20260220_010: Registry and persistence
|
||||||
|
- SPRINT_20260220_011: Signals webhook handler
|
||||||
|
- SPRINT_20260220_012: Verification pipeline
|
||||||
|
- SPRINT_20260220_013: Matching, sources, policy
|
||||||
|
- SPRINT_20260220_014: UI components
|
||||||
|
- SPRINT_20260220_015: Documentation
|
||||||
129
docs/modules/symbols/marketplace-architecture.md
Normal file
129
docs/modules/symbols/marketplace-architecture.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Symbol Marketplace Architecture
|
||||||
|
|
||||||
|
**Module**: `src/Symbols/StellaOps.Symbols.Marketplace/`
|
||||||
|
**Server**: `src/Symbols/StellaOps.Symbols.Server/`
|
||||||
|
**Sprint**: SPRINT_20260220_001, SPRINT_20260220_002
|
||||||
|
**Status**: Implemented
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Symbol Marketplace extends the existing Symbols module with a registry of symbol/debug pack sources, a browsable catalog, and a four-dimension trust scoring model. It provides the infrastructure needed to discover, evaluate, and install debug symbol packs from vendor, distro, community, and partner providers.
|
||||||
|
|
||||||
|
This directly strengthens the "Symbolized call-stack proofs" moat by ensuring Stella Ops can source verified debug symbols for any artifact in the reachability graph, enabling DSSE-signed call-stack resolution across platforms.
|
||||||
|
|
||||||
|
## Domain Primitives
|
||||||
|
|
||||||
|
### SymbolPackSource
|
||||||
|
Registry entry for a symbol provider. Each source has:
|
||||||
|
- **Key/Name**: Human-readable identifier (e.g., `microsoft-symbols`, `ubuntu-debuginfod`).
|
||||||
|
- **SourceType**: `vendor` | `distro` | `community` | `partner`.
|
||||||
|
- **Priority**: Integer ordering for resolution precedence.
|
||||||
|
- **FreshnessSLA**: Target sync interval in seconds (default: 6 hours).
|
||||||
|
- **WarningRatio**: Threshold (0-1) for warning state transition.
|
||||||
|
|
||||||
|
### SymbolPackCatalogEntry
|
||||||
|
Represents an installable symbol/debug pack:
|
||||||
|
- **PackId**: PURL-formatted package identifier.
|
||||||
|
- **Platform**: Target platform (e.g., `linux/amd64`, `any`).
|
||||||
|
- **Components**: Array of debug components included.
|
||||||
|
- **DsseDigest**: DSSE signature digest for integrity verification.
|
||||||
|
- **Installed**: Whether the pack is active for the tenant.
|
||||||
|
|
||||||
|
### SymbolSourceFreshnessRecord
|
||||||
|
Materialized freshness projection following the advisory source pattern:
|
||||||
|
- Tracks sync cadence, error rates, and SLA compliance.
|
||||||
|
- Freshness state machine: `healthy` -> `warning` -> `stale` -> `unavailable`.
|
||||||
|
- Includes signature coverage metrics (signed/unsigned/failure counts).
|
||||||
|
|
||||||
|
### SymbolSourceTrustScore
|
||||||
|
Four-dimension trust scoring:
|
||||||
|
| Dimension | Weight | Description |
|
||||||
|
|-----------|--------|-------------|
|
||||||
|
| Freshness | 0.30 | How up-to-date the source is relative to SLA |
|
||||||
|
| Signature | 0.30 | DSSE signature coverage (signed packs / total packs) |
|
||||||
|
| Coverage | 0.20 | Artifact coverage derived from sync success rate |
|
||||||
|
| SLA Compliance | 0.20 | Whether source stays within freshness window |
|
||||||
|
|
||||||
|
Overall score = weighted average, clamped to [0, 1].
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### symbol_pack_sources
|
||||||
|
| Column | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| id | uuid PK | Source identifier |
|
||||||
|
| key | text UNIQUE | Machine-readable key |
|
||||||
|
| name | text | Display name |
|
||||||
|
| source_type | text | vendor/distro/community/partner |
|
||||||
|
| url | text NULL | Source endpoint URL |
|
||||||
|
| priority | int | Resolution priority |
|
||||||
|
| enabled | boolean | Active flag |
|
||||||
|
| freshness_sla_seconds | int | Target sync interval |
|
||||||
|
| warning_ratio | decimal | Warning threshold |
|
||||||
|
| created_at | timestamptz | Creation timestamp |
|
||||||
|
| updated_at | timestamptz NULL | Last update |
|
||||||
|
|
||||||
|
### symbol_pack_catalog
|
||||||
|
| Column | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| id | uuid PK | Entry identifier |
|
||||||
|
| source_id | uuid FK | References symbol_pack_sources |
|
||||||
|
| pack_id | text | PURL identifier |
|
||||||
|
| platform | text | Target platform |
|
||||||
|
| components | text[] | Component list |
|
||||||
|
| dsse_digest | text | Signature digest |
|
||||||
|
| version | text | Pack version |
|
||||||
|
| size_bytes | bigint | Pack size |
|
||||||
|
| published_at | timestamptz | Publish date |
|
||||||
|
|
||||||
|
## API Surface
|
||||||
|
|
||||||
|
### Symbol Sources (`/api/v1/symbols/sources`)
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/` | List sources with freshness projections |
|
||||||
|
| GET | `/summary` | Summary cards (healthy/stale/unavailable counts + avg trust) |
|
||||||
|
| GET | `/{id}` | Source detail with trust score |
|
||||||
|
| GET | `/{id}/freshness` | Freshness detail |
|
||||||
|
| POST | `/` | Create source |
|
||||||
|
| PUT | `/{id}` | Update source |
|
||||||
|
| DELETE | `/{id}` | Disable source |
|
||||||
|
|
||||||
|
### Marketplace Catalog (`/api/v1/symbols/marketplace`)
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/` | List catalog entries |
|
||||||
|
| GET | `/search` | Search by PURL/platform |
|
||||||
|
| GET | `/{entryId}` | Catalog entry detail |
|
||||||
|
| POST | `/{entryId}/install` | Install pack for tenant |
|
||||||
|
| POST | `/{entryId}/uninstall` | Uninstall pack |
|
||||||
|
| GET | `/installed` | List installed packs |
|
||||||
|
| POST | `/sync` | Trigger sync from sources |
|
||||||
|
|
||||||
|
All responses include `dataAsOf` timestamp for staleness detection.
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### IntegrationType.SymbolSource (= 7)
|
||||||
|
New integration type added to `StellaOps.Integrations.Core`:
|
||||||
|
- `MicrosoftSymbols = 700`
|
||||||
|
- `UbuntuDebuginfod = 701`
|
||||||
|
- `FedoraDebuginfod = 702`
|
||||||
|
- `DebianDebuginfod = 703`
|
||||||
|
- `PartnerSymbols = 704`
|
||||||
|
|
||||||
|
### UI Integration
|
||||||
|
- **Symbol Sources list**: `/security-risk/symbol-sources` — freshness summary + source table.
|
||||||
|
- **Symbol Source detail**: `/security-risk/symbol-sources/:sourceId` — trust breakdown, sync timeline.
|
||||||
|
- **Symbol Marketplace**: `/security-risk/symbol-marketplace` — catalog browse/search with install/uninstall.
|
||||||
|
- Sidebar entries under "Security and Risk" section.
|
||||||
|
|
||||||
|
### Existing Module Touchpoints
|
||||||
|
- **Scanner**: Symbol resolution uses marketplace-installed packs for call-stack symbolication.
|
||||||
|
- **ReachGraph**: Coverage dimension reflects artifact matching from reachability analysis.
|
||||||
|
- **Attestor**: DSSE signatures on packs are verified through the existing proof chain infrastructure.
|
||||||
|
- **Policy**: Trust scores feed into policy gate decisions for symbol-dependent verdicts.
|
||||||
118
docs/modules/telemetry/federation-architecture.md
Normal file
118
docs/modules/telemetry/federation-architecture.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Federated Telemetry Architecture
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Federated Telemetry subsystem enables privacy-preserving sharing of runtime exploit intelligence across Stella Ops instances in a federation mesh. It uses differential privacy (Laplacian noise) and k-anonymity to ensure that individual tenant data cannot be reconstructed from shared aggregates.
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Tenant Runtime -> TelemetryFact Buffer -> TelemetryAggregator
|
||||||
|
-> k-Anonymity Filter -> Laplacian Noise -> AggregationResult
|
||||||
|
-> ConsentManager Check -> BundleBuilder -> DSSE-Signed Bundle
|
||||||
|
-> EgressPolicy Check -> Federation Mesh Sync
|
||||||
|
-> ExploitIntelligenceMerger <- Incoming Bundles from Peers
|
||||||
|
```
|
||||||
|
|
||||||
|
## Privacy Model
|
||||||
|
|
||||||
|
### Differential Privacy (Epsilon Budget)
|
||||||
|
|
||||||
|
Each aggregation cycle consumes a portion of the total epsilon budget. The budget resets on a configurable period (default: 24 hours).
|
||||||
|
|
||||||
|
- **Epsilon per bucket**: `total_budget / number_of_buckets`
|
||||||
|
- **Laplacian noise**: `-(sensitivity / epsilon) * sign(u) * ln(1 - 2|u|)` where u ~ Uniform(-0.5, 0.5)
|
||||||
|
- **Budget exhaustion**: When remaining epsilon reaches zero, all further aggregation is suppressed until the next reset period.
|
||||||
|
|
||||||
|
### K-Anonymity
|
||||||
|
|
||||||
|
Buckets (grouped by CVE ID) with fewer than `k` distinct artifact digests are suppressed entirely. The default threshold is k=5, configurable via `FederatedTelemetryOptions.KAnonymityThreshold`.
|
||||||
|
|
||||||
|
## Consent Lifecycle
|
||||||
|
|
||||||
|
1. **Not Granted** (default) -- no federation data leaves the instance.
|
||||||
|
2. **Granted** -- admin explicitly grants consent with optional TTL. A DSSE-signed consent proof is created.
|
||||||
|
3. **Expired** -- consent with a TTL automatically reverts to Not Granted after expiry.
|
||||||
|
4. **Revoked** -- admin explicitly revokes consent.
|
||||||
|
|
||||||
|
Consent state is checked at the start of each sync cycle. No bundles are built or transmitted without active consent.
|
||||||
|
|
||||||
|
## Sync Service
|
||||||
|
|
||||||
|
`FederatedTelemetrySyncService` is a `BackgroundService` that runs on a configurable interval (default: 15 minutes).
|
||||||
|
|
||||||
|
Each cycle:
|
||||||
|
1. Check sealed mode -- skip if active.
|
||||||
|
2. Check privacy budget -- skip if exhausted.
|
||||||
|
3. Check consent -- skip if not granted.
|
||||||
|
4. Drain fact buffer.
|
||||||
|
5. Aggregate facts with k-anonymity and Laplacian noise.
|
||||||
|
6. Build DSSE-signed bundle.
|
||||||
|
7. Check egress policy.
|
||||||
|
8. Transmit to federation mesh.
|
||||||
|
|
||||||
|
## Intelligence Merging
|
||||||
|
|
||||||
|
Incoming bundles from federation peers are processed by `ExploitIntelligenceMerger`:
|
||||||
|
- Entries are normalized (CVE ID uppercase, timestamps UTC, site IDs lowercase).
|
||||||
|
- Deduplication by `(CveId, SourceSiteId)` composite key.
|
||||||
|
- Conflict resolution: latest `ObservedAt` wins.
|
||||||
|
|
||||||
|
## Bundle Format
|
||||||
|
|
||||||
|
A `FederatedBundle` contains:
|
||||||
|
- Unique ID (GUID)
|
||||||
|
- Source site identifier
|
||||||
|
- Aggregation result (buckets with noisy counts, suppression flags)
|
||||||
|
- Consent DSSE digest (proof that consent was active)
|
||||||
|
- Bundle DSSE digest (integrity verification)
|
||||||
|
- DSSE envelope (signed payload)
|
||||||
|
- Creation timestamp
|
||||||
|
|
||||||
|
## Sealed Mode
|
||||||
|
|
||||||
|
When `FederatedTelemetryOptions.SealedModeEnabled` is true:
|
||||||
|
- Sync service skips all cycles.
|
||||||
|
- No outbound traffic is generated.
|
||||||
|
- Local aggregation still functions for internal analytics.
|
||||||
|
- Intelligence merging is paused.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"FederatedTelemetry": {
|
||||||
|
"KAnonymityThreshold": 5,
|
||||||
|
"EpsilonBudget": 1.0,
|
||||||
|
"BudgetResetPeriod": "24:00:00",
|
||||||
|
"AggregationInterval": "00:15:00",
|
||||||
|
"SealedModeEnabled": false,
|
||||||
|
"ConsentPredicateType": "stella.ops/federatedConsent@v1",
|
||||||
|
"BundlePredicateType": "stella.ops/federatedTelemetry@v1",
|
||||||
|
"SiteId": "site-001"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Surface
|
||||||
|
|
||||||
|
See `src/Platform/StellaOps.Platform.WebService/Endpoints/FederationTelemetryEndpoints.cs` for the full REST API.
|
||||||
|
|
||||||
|
| Method | Path | Auth Policy | Description |
|
||||||
|
|--------|------|-------------|-------------|
|
||||||
|
| GET | /api/v1/telemetry/federation/consent | FederationRead | Get consent state |
|
||||||
|
| POST | /api/v1/telemetry/federation/consent/grant | FederationManage | Grant consent |
|
||||||
|
| POST | /api/v1/telemetry/federation/consent/revoke | FederationManage | Revoke consent |
|
||||||
|
| GET | /api/v1/telemetry/federation/status | FederationRead | Federation status |
|
||||||
|
| GET | /api/v1/telemetry/federation/bundles | FederationRead | List bundles |
|
||||||
|
| GET | /api/v1/telemetry/federation/bundles/{id} | FederationRead | Bundle detail |
|
||||||
|
| GET | /api/v1/telemetry/federation/intelligence | FederationRead | Exploit corpus |
|
||||||
|
| GET | /api/v1/telemetry/federation/privacy-budget | FederationRead | Budget snapshot |
|
||||||
|
| POST | /api/v1/telemetry/federation/trigger | FederationManage | Trigger aggregation |
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
|
||||||
|
- Project: `src/Telemetry/StellaOps.Telemetry.Federation/`
|
||||||
|
- Tests: `src/Telemetry/StellaOps.Telemetry.Federation.Tests/`
|
||||||
|
- API: `src/Platform/StellaOps.Platform.WebService/Endpoints/FederationTelemetryEndpoints.cs`
|
||||||
|
- UI: `src/Web/StellaOps.Web/src/app/features/platform-ops/federation-telemetry/`
|
||||||
@@ -1,45 +1,132 @@
|
|||||||
# S00 Advisory Sources Specification
|
# S00 Advisory Sources Specification
|
||||||
|
|
||||||
Status: Draft (created for sprint planning pointer integrity)
|
Status: Frozen (implemented backend contracts reconciled)
|
||||||
Date: 2026-02-18
|
Date: 2026-02-19
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
|
Sprint: `20260218_005`, task `R0-02`
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
Define `Security and Risk -> Advisory Sources` as the decision-impact view of advisory-source health.
|
|
||||||
|
|
||||||
## Ownership split
|
Define `Security and Risk -> Advisory Sources` as the decision-impact view of advisory-source health.
|
||||||
- `Integrations` owns source connector configuration, credentials, and connectivity checks.
|
This is the security gating interpretation surface; operations on connectors/mirrors belong to other domains.
|
||||||
- `Platform Ops` owns mirror/freshness operation workflows.
|
|
||||||
- `Security and Risk` owns advisory decision impact (gate relevance, risk confidence impact).
|
## Implementation reconciliation (2026-02-19)
|
||||||
|
|
||||||
|
- Freshness routes are implemented in Concelier:
|
||||||
|
- `GET /api/v1/advisory-sources`
|
||||||
|
- `GET /api/v1/advisory-sources/summary`
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/freshness`
|
||||||
|
- Policy impact/conflict routes are implemented in Policy Gateway:
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/impact`
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/conflicts`
|
||||||
|
- Persistence backing is implemented via:
|
||||||
|
- `src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/004_add_advisory_source_freshness_projection.sql`
|
||||||
|
- `src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/005_add_advisory_source_signature_projection.sql`
|
||||||
|
- `src/Policy/__Libraries/StellaOps.Policy.Persistence/Migrations/005_advisory_source_projection.sql`
|
||||||
|
- Frontend Security & Risk consumption is implemented via:
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/advisory-sources.api.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/advisory-sources.component.ts`
|
||||||
|
- Endpoint-driven table/summary/detail state rendering (hard fail, degraded, conflict, and empty behaviors).
|
||||||
|
- Detail-panel advisory statistics now bind to backend contract fields (`totalAdvisories`, `signedAdvisories`, `unsignedAdvisories`, `signatureFailureCount`) instead of placeholders.
|
||||||
|
|
||||||
|
## Ownership split (field-level)
|
||||||
|
|
||||||
|
| Field / Action | Owner domain | Rationale |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Source name, family, description | Integrations | Connector catalog owner |
|
||||||
|
| Credential / connectivity status | Integrations | Connector health owner |
|
||||||
|
| Test connection action | Integrations | Links to Integrations connector detail |
|
||||||
|
| Mirror and freshness operation actions | Platform Ops | Mirror workflow owner |
|
||||||
|
| Last successful ingest, freshness age, freshness SLA | Platform Ops (source), Security & Risk (display) | Platform Ops publishes freshness facts; this screen reads and interprets for gating impact |
|
||||||
|
| Signature / trust status | Administration (Trust & Signing, source), Security & Risk (display) | Administration manages trust keys; this screen shows effect on advisory verification |
|
||||||
|
| Impacted decisions count | Security & Risk | Gate evaluation owner |
|
||||||
|
| Impact severity | Security & Risk | Risk scoring owner |
|
||||||
|
| Conflict detection / conflict diagnostics | Security & Risk | Conflict resolution belongs to security decision model |
|
||||||
|
| Unsigned advisory ratio | Security & Risk | Advisory interpretation owner |
|
||||||
|
|
||||||
## Screen structure
|
## Screen structure
|
||||||
- Header: scope filters (region, env, source family, freshness severity).
|
|
||||||
- Summary cards: healthy sources, stale sources, unavailable sources, conflicting-source warnings.
|
### Header
|
||||||
- Source table columns:
|
- Page title: `Advisory Sources`
|
||||||
- Source name
|
- Scope filters: region, environment, source family (feed type), freshness severity.
|
||||||
- Last successful ingest
|
- Quick stats bar: total sources, healthy count, stale count, unavailable count.
|
||||||
- Freshness SLA
|
|
||||||
- Current freshness age
|
### Summary cards (4 cards)
|
||||||
- Signature/trust status
|
- Healthy sources — count with trend.
|
||||||
- Impacted decisions count
|
- Stale sources — count with worst freshness age and SLA breach delta.
|
||||||
- Impact severity
|
- Unavailable sources — count; includes sources with connectivity failure or mirror lag > threshold.
|
||||||
- Actions: open connector config, open mirror ops, open impacted findings/gates
|
- Conflicting-source warnings — count of active advisory conflicts with unresolved triage status.
|
||||||
- Detail panel:
|
|
||||||
- Source status timeline
|
### Source table
|
||||||
- Conflict diagnostics
|
Required columns:
|
||||||
- Signed/unsigned advisory ratio
|
|
||||||
- Impacted release/approval/environment references
|
| Column | Source | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Source name | Integrations | Link to Integrations connector detail with preserved source id |
|
||||||
|
| Source family | Integrations | Feed type (NVD, OSV, GHSA, vendor, custom) |
|
||||||
|
| Last successful ingest | Platform Ops | Timestamp |
|
||||||
|
| Freshness age | Platform Ops | Age since last successful ingest |
|
||||||
|
| Freshness SLA | Platform Ops | Configured SLA threshold |
|
||||||
|
| Freshness status | Platform Ops | Healthy / Warning / Stale / Unavailable badge |
|
||||||
|
| Signature / trust status | Administration | Signed / Unsigned / Untrusted |
|
||||||
|
| Impacted decisions count | Security & Risk | Count of release/approval decisions gated by this source |
|
||||||
|
| Impact severity | Security & Risk | Highest severity of active advisory in this source affecting decisions |
|
||||||
|
|
||||||
|
### Table actions per row
|
||||||
|
- Open connector config → navigates to Integrations connector detail (preserved source id).
|
||||||
|
- Open mirror ops → navigates to Platform Ops feeds/freshness page (preserved source id).
|
||||||
|
- View impacted findings/gates → navigates to Security & Risk findings filtered by source.
|
||||||
|
|
||||||
|
### Detail panel (slide-in)
|
||||||
|
Opened from row click. Sections:
|
||||||
|
- Source status timeline — ingest events, gaps, and failure events.
|
||||||
|
- Conflict diagnostics — conflicting statement list with source pair, advisory id, conflict type (severity mismatch, remediation mismatch, existence conflict).
|
||||||
|
- Advisory statistics — total advisories, signed count, unsigned count, signature failure count.
|
||||||
|
- Impacted release/approval/environment references — linked list of active decisions impacted by this source.
|
||||||
|
|
||||||
## State behavior
|
## State behavior
|
||||||
- Healthy: all freshness and signature checks pass.
|
|
||||||
- Stale: freshness age exceeds SLA; show gating confidence warning.
|
|
||||||
- Unavailable: source unreachable; mark impacted decisions as degraded confidence.
|
|
||||||
- Conflict: source statements disagree; show conflict badge and triage action.
|
|
||||||
|
|
||||||
## Required links
|
### Per-source states
|
||||||
- To `Integrations` connector detail with preserved source id.
|
|
||||||
- To `Platform Ops` feeds/mirror page with preserved source id.
|
|
||||||
- To `Security and Risk` findings filtered by source impact.
|
|
||||||
|
|
||||||
## Contract notes
|
| State | Trigger | UI treatment |
|
||||||
- This screen likely requires an aggregate endpoint composed from integrations + ops + security data.
|
| --- | --- | --- |
|
||||||
- Initial classification expected: `MISSING_NEW` pending contract definition.
|
| Healthy | Freshness within SLA, signature valid or source is unsigned-accepted | Green badge; no action surfaced |
|
||||||
|
| Warning | Freshness age approaching SLA (configurable threshold, default 80%) | Yellow badge; show time-to-breach |
|
||||||
|
| Stale | Freshness age exceeds SLA | Red badge; show gating confidence degraded warning; show Open mirror ops action |
|
||||||
|
| Unavailable | No ingest activity in critical window or mirror failure | Critical badge; show Open connector config action; impacted decisions show degraded confidence |
|
||||||
|
| Conflicting | Active unresolved advisory conflict involving this source | Conflict badge; show conflict count; triage link |
|
||||||
|
|
||||||
|
### Page-level states
|
||||||
|
|
||||||
|
| State | Trigger | UI treatment |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| All healthy | All sources healthy or warning | No banner; summary cards show normal |
|
||||||
|
| Degraded sources present | One or more stale or unavailable | Warning banner with count and quick action links |
|
||||||
|
| Conflict active | One or more unresolved conflicts | Security banner with conflict count; link to filtered view |
|
||||||
|
| Stale data | Advisory source API returns cached or stale data (> configured page-stale threshold) | Stale-data banner with last-refreshed timestamp; disable gating-critical actions |
|
||||||
|
| Hard fail | Advisory source API unavailable | Error banner; page content unavailable; link to Platform Ops data-integrity page |
|
||||||
|
| Empty | No advisory sources configured | Empty state with link to Integrations to configure first source |
|
||||||
|
|
||||||
|
## Forbidden behaviors
|
||||||
|
|
||||||
|
- This page must not expose connector credential editing (Integrations owns this).
|
||||||
|
- This page must not expose freshness operation controls such as trigger sync, clear cache (Platform Ops owns this).
|
||||||
|
- This page must not host trust key or issuer management (Administration owns this).
|
||||||
|
- Conflict diagnostics is a read-only view; resolution actions are surfaced as links to owning triage surfaces.
|
||||||
|
|
||||||
|
## API dependency list
|
||||||
|
|
||||||
|
| API | Proposed endpoint | Owner module | Status class | Auth scope | Notes |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| Advisory source list with freshness | `GET /api/v1/advisory-sources` | `Concelier` | `EXISTS_COMPAT` | `advisory:read` | Implemented; requires tenant via `X-Stella-Tenant` or `tenant_id` claim |
|
||||||
|
| Advisory source freshness detail | `GET /api/v1/advisory-sources/{id}/freshness` | `Concelier` | `EXISTS_COMPAT` | `advisory:read` | Implemented; supports source UUID/key lookup and includes advisory stats fields for detail diagnostics |
|
||||||
|
| Advisory source gating impact | `GET /api/v1/advisory-sources/{id}/impact` | `Policy` | `EXISTS_COMPAT` | `findings:read` | Implemented; supports `region`, `environment`, and `sourceFamily` filters |
|
||||||
|
| Advisory source conflict report | `GET /api/v1/advisory-sources/{id}/conflicts` | `Policy` | `EXISTS_COMPAT` | `findings:read` | Implemented; supports `status` plus deterministic `limit`/`offset` pagination |
|
||||||
|
| Advisory source summary aggregate | `GET /api/v1/advisory-sources/summary` | `Concelier` | `EXISTS_COMPAT` | `advisory:read` | Implemented card aggregate (healthy/warning/stale/unavailable/disabled/conflicts placeholder) |
|
||||||
|
| Security source freshness (existing) | `GET /api/v1/security/sources/freshness` (check Concelier) | `Concelier` | `EXISTS_ADAPT` | existing | May need freshness-SLA delta and impact-count additions |
|
||||||
|
|
||||||
|
## Non-allowed implementations
|
||||||
|
|
||||||
|
- A single combined API that merges connector config and freshness without a clear split contract.
|
||||||
|
- Advisory Sources rendered as a sub-tab of Integrations or Platform Ops (Security & Risk is owner).
|
||||||
|
- Freshness operation controls embedded in this page (must be deep-link to Platform Ops only).
|
||||||
|
|||||||
@@ -1,27 +1,50 @@
|
|||||||
# S00 Endpoint Contract Ledger v1 (Starter)
|
# S00 Endpoint Contract Ledger v1
|
||||||
|
|
||||||
Status: Starter sheet
|
Status: Frozen baseline (reconciled with backend implementation)
|
||||||
Instructions: replace placeholder values with discovered implementation reality.
|
Date: 2026-02-19
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
Template source: `S00_contract_ledger_template.md`
|
Template source: `S00_contract_ledger_template.md`
|
||||||
|
Sprint: `20260218_005`, task `R0-06`
|
||||||
|
|
||||||
|
## Reconciliation note (2026-02-19)
|
||||||
|
|
||||||
|
- Frontend shell structure was reverified in `SPRINT_20260219_002` to `SPRINT_20260219_007`.
|
||||||
|
- Backend dependency rows `S00-T05-RC-01` and `S00-T05-SEC-02` are shipped and reclassified to `EXISTS_COMPAT`; frontend endpoint consumption for both rows is now implemented in UI surfaces.
|
||||||
|
- Backend contract-enrichment adapters were implemented in `SPRINT_20260219_016` for `S00-T05-DASH-01`, `S00-T05-RC-02`, `S00-T05-RUN-01`, `S00-T05-APR-01`, `S00-T05-ENV-01`, `S00-T05-SEC-01`, `S00-T05-EVID-01`, `S00-T05-INT-01`, and `S00-T05-OPS-01`; these rows are now reclassified to `EXISTS_COMPAT`.
|
||||||
|
- Backend administration adapters now cover Pack-21 A0-A7 (`/api/v1/administration/{summary,identity-access,tenant-branding,notifications,usage-limits,policy-governance,trust-signing,system}`), so `S00-T05-ADM-01` is reclassified to `EXISTS_COMPAT`.
|
||||||
|
- Trust owner mutation routes for keys/issuers/certificates/transparency log are implemented under `/api/v1/administration/trust-signing/*` with `platform.trust.write` / `platform.trust.admin`, backed by Platform DB migration `046_TrustSigningAdministration.sql`.
|
||||||
|
- Readiness reconciliation is recorded in `S16_release_readiness_package.md`.
|
||||||
|
|
||||||
|
## Status class definitions
|
||||||
|
|
||||||
|
| Status class | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `EXISTS_COMPAT` | Endpoint exists and is compatible with v2 screen needs without schema change. |
|
||||||
|
| `EXISTS_ADAPT` | Endpoint exists but requires schema additions, filter/sort extensions, or composition changes for v2. |
|
||||||
|
| `MISSING_NEW` | No endpoint exists; must be designed and implemented before the consuming sprint can complete. |
|
||||||
|
|
||||||
|
## Ledger
|
||||||
|
|
||||||
| Domain | Screen/Page | Canonical source refs | Current route/page | Current endpoint candidate(s) | Status | Owner module | Auth scope impact | Schema delta summary | Decision/risk notes | Action ticket |
|
| Domain | Screen/Page | Canonical source refs | Current route/page | Current endpoint candidate(s) | Status | Owner module | Auth scope impact | Schema delta summary | Decision/risk notes | Action ticket |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| Dashboard | Dashboard v3 mission board | `source-of-truth.md 3.2`, `authority-matrix.md A: Dashboard`, `pack-16.md` | `/` (control-plane/dashboard variants) | `TBD` | `EXISTS_ADAPT` | `Web` | `TBD` | aggregate model for CritR, SBOM freshness, B/I/R, data integrity likely needs composition changes | route naming and model aggregation not finalized | `S00-T05-DASH-01` |
|
| Dashboard | Dashboard v3 mission board | `source-of-truth.md 3.2`, `authority-matrix.md A: Dashboard`, `pack-16.md` | `/` (control-plane/dashboard variants) | `GET /api/v1/dashboard/summary`; existing promotion, approval, and scan summary endpoints | `EXISTS_COMPAT` | `Web` (composition) + `ReleaseOrchestrator`, `Policy`, `Scanner` | No new scopes; requires existing viewer scopes | Implemented in Platform pack adapters with deterministic data-confidence, CritR env breakdown, B/I/R coverage, and top-driver fields consumed by dashboard v3 cards | Route finalized to `/api/v1/dashboard/summary`; validated by `PackAdapterEndpointsTests` | `S00-T05-DASH-01` |
|
||||||
| Release Control | Bundle catalog/detail/builder | `source-of-truth.md 3.1`, `authority-matrix.md A: bundles`, `pack-12.md` | `/releases/*` and related bundle placeholders | `TBD` | `MISSING_NEW` | `ReleaseOrchestrator` | `TBD` | bundle-version lifecycle and materialization contracts likely incomplete | high risk for schema spread across modules | `S00-T05-RC-01` |
|
| Release Control | Bundle catalog/detail/builder | `source-of-truth.md 3.1`, `authority-matrix.md A: bundles`, `pack-12.md` | `/release-control/bundles/*` | `GET /api/v1/release-control/bundles`; `GET /api/v1/release-control/bundles/{bundleId}`; `GET /api/v1/release-control/bundles/{bundleId}/versions`; `GET /api/v1/release-control/bundles/{bundleId}/versions/{versionId}`; `POST /api/v1/release-control/bundles`; `POST /api/v1/release-control/bundles/{bundleId}/versions`; `POST /api/v1/release-control/bundles/{bundleId}/versions/{versionId}/materialize` | `EXISTS_COMPAT` | `Platform` (`StellaOps.Platform.WebService`) | `orch:read` (read routes), `orch:operate` (create/publish/materialize) | Implemented with Postgres-backed lifecycle tables (`release.control_bundles*`) plus deterministic list ordering and idempotent materialization key handling | Collision with Evidence bundle export routes resolved by dedicated `/api/v1/release-control/*` namespace; frontend bundle surfaces are now API-bound (see sprint `20260219_003` RC3-06) | `S00-T05-RC-01` |
|
||||||
| Release Control | Promotions list/create/detail | `source-of-truth.md 3.1`, `authority-matrix.md A: releases`, `pack-13.md` | `/releases/*` | `TBD` | `EXISTS_ADAPT` | `ReleaseOrchestrator` | `TBD` | bundle-version anchoring required in promotion contracts | depends on bundle contract finalization | `S00-T05-RC-02` |
|
| Release Control | Promotions list/create/detail | `source-of-truth.md 3.1`, `authority-matrix.md A: releases`, `pack-13.md` | `/release-control/promotions/*` | `GET /api/release-orchestrator/approvals` (list); `GET /api/release-orchestrator/approvals/{id}` (detail); `GET /api/release-orchestrator/releases/{releaseId}/available-environments` (target preflight); `GET /api/release-orchestrator/releases/{releaseId}/promotion-preview` (gate preflight); `POST /api/release-orchestrator/releases/{releaseId}/promote` (create); `POST /api/release-orchestrator/approvals/{id}/approve`; `POST /api/release-orchestrator/approvals/{id}/reject` | `EXISTS_COMPAT` | `ReleaseOrchestrator` | Existing `orch:read` / `orch:operate` | Legacy promotion/approval payloads are enriched with manifest digest, risk snapshot, hybrid reachability coverage, ops confidence, and decision digest via `ApprovalEndpoints.WithDerivedSignals` | Contract fields verified by `ReleaseControlV2EndpointsTests`; Pack 13 digest-first promotion cards no longer depend on frontend-only gap placeholders | `S00-T05-RC-02` |
|
||||||
| Approvals | Approvals v2 tabs and decision packet | `source-of-truth.md 3.3`, `authority-matrix.md A: approvals`, `pack-17.md` | `/approvals/*` | `TBD` | `EXISTS_ADAPT` | `Policy` | `TBD` | richer gate trace and ops/data context payloads expected | cross-service joins may be needed | `S00-T05-APR-01` |
|
| Release Control | Run timeline, checkpoints, rollback | `source-of-truth.md 3.1`, `authority-matrix.md A: run timeline`, `pack-14.md` | `/deployments/*` and run views | `GET /api/v1/runs/{id}` (run detail); `GET /api/v1/runs/{id}/steps` (step list); `GET /api/v1/runs/{id}/steps/{stepId}` (step detail + logs); `POST /api/v1/runs/{id}/rollback` (trigger rollback) | `EXISTS_COMPAT` | `ReleaseOrchestrator` | Existing `orch:read` / `orch:operate` | Implemented v2 run contracts include ordered checkpoints plus explicit evidence-thread and log-artifact links; rollback returns deterministic accepted payload with guard state | `/api/v1/runs/*` and `/v1/runs/*` compatibility routes are live and test-backed; policy-coupled rollback guard hardening remains future work | `S00-T05-RUN-01` |
|
||||||
| Release Runs | Run timeline and rollback | `source-of-truth.md 3.1`, `authority-matrix.md A: run timeline`, `pack-14.md` | `/deployments/*` and run views | `TBD` | `EXISTS_ADAPT` | `ReleaseOrchestrator` | `TBD` | checkpoint-level evidence/log linkage may be partial | rollback guard semantics must be explicit | `S00-T05-RUN-01` |
|
| Approvals | Approvals v2 tabs and decision packet | `source-of-truth.md 3.3`, `authority-matrix.md A: approvals`, `pack-17.md` | `/approvals/*` | `GET /api/v1/approvals` (queue); `GET /api/v1/approvals/{id}` (detail); `GET /api/v1/approvals/{id}/gates` (gate trace); `GET /api/v1/approvals/{id}/evidence` (evidence packet); `GET /api/v1/approvals/{id}/security-snapshot` (security tab data); `GET /api/v1/approvals/{id}/ops-health` (ops/data tab); `POST /api/v1/approvals/{id}/decision` (approve/reject/defer/escalate) | `EXISTS_COMPAT` | `Policy` + `ReleaseOrchestrator` | Existing policy reviewer / approver scopes | v2 approvals adapter routes now return deterministic decision-packet shapes containing digest, gate trace, security snapshot (risk + B/I/R), and ops/data confidence payloads | Deterministic ordering and contract fields are verified in `ReleaseControlV2EndpointsTests` (queue determinism, gate ordering, decision mutation, not-found behavior) | `S00-T05-APR-01` |
|
||||||
| Environment | Environment detail standard tabs | `source-of-truth.md 3.1 and 3.6`, `authority-matrix.md A: env detail`, `pack-18.md` | `/environments/*` | `TBD` | `EXISTS_ADAPT` | `ReleaseOrchestrator` | `TBD` | env summary requires deploy+security+ops evidence merge | risk of expensive fan-out queries | `S00-T05-ENV-01` |
|
| Environment | Environment detail standard tabs | `source-of-truth.md 3.1 and 3.6`, `authority-matrix.md A: env detail`, `pack-18.md` | `/environments/*` | `GET /api/v1/environments/{id}` (detail); `GET /api/v1/environments/{id}/deployments` (deployment history); `GET /api/v1/environments/{id}/security-snapshot` (security state); `GET /api/v1/environments/{id}/evidence` (evidence summary); `GET /api/v1/environments/{id}/ops-health` (data confidence) | `EXISTS_COMPAT` | `ReleaseOrchestrator` | Existing `orch:read` | Pack-18 environment tab contracts are implemented with standardized header fields (manifest digest, risk snapshot, B/I/R coverage, ops confidence) and deterministic deployment ordering | Environment adapters are live under `/api/v1/environments/*` and validated in `ReleaseControlV2EndpointsTests` | `S00-T05-ENV-01` |
|
||||||
| Security and Risk | Risk overview/findings/vuln/vex/exceptions | `source-of-truth.md 3.4`, `authority-matrix.md A: security`, `pack-19.md` | `/security/*` | `TBD` | `EXISTS_ADAPT` | `Scanner` | `TBD` | decision-first grouping and filters may require endpoint normalization | mapping from existing pages may be non-trivial | `S00-T05-SEC-01` |
|
| Security and Risk | Risk overview, findings, vulns, vex, exceptions, reachability | `source-of-truth.md 3.4`, `authority-matrix.md A: security`, `pack-19.md` | `/security/*` | `GET /api/v1/security/findings` (decision-first grouped); `GET /api/v1/security/vulnerabilities`; `GET /api/v1/security/vex`; `GET /api/v1/security/reachability`; existing risk/scanner endpoints | `EXISTS_COMPAT` | `Scanner` | Existing security viewer scopes | Security adapter routes now normalize findings/vulnerability/VEX/reachability payloads with deterministic filters and B/I/R confidence fields expected by Pack 19 decision-centric screens | Scanner routes are validated in `SecurityAdapterEndpointsTests`; exception lifecycle remains served by Policy endpoints (`/api/policy/exceptions`) and linked from security flows | `S00-T05-SEC-01` |
|
||||||
| Security and Risk | Advisory Sources | `source-of-truth.md 3.4 and 5`, `authority-matrix.md B: legacy security data split`, `pack-21.md` | `TBD` | `TBD` | `MISSING_NEW` | `Integrations` | `TBD` | final screen spec pending S00-T01, likely needs new aggregate endpoint | ownership boundary unresolved until S00 freeze | `S00-T05-SEC-02` |
|
| Security and Risk | Advisory Sources | `source-of-truth.md 3.4 and 5`, `authority-matrix.md B: legacy security data split`, `pack-21.md`, `S00_advisory_sources_spec.md` | `/security-risk/advisory-sources` | `GET /api/v1/advisory-sources`; `GET /api/v1/advisory-sources/summary`; `GET /api/v1/advisory-sources/{id}/freshness` (Concelier); `GET /api/v1/advisory-sources/{id}/impact`; `GET /api/v1/advisory-sources/{id}/conflicts` (Policy) | `EXISTS_COMPAT` | `Concelier` (freshness) + `Policy` (impact/conflicts) | `advisory:read` (Concelier freshness routes), `findings:read` (Policy impact/conflicts routes); tenant header required | Implemented with Concelier freshness + signature-stat projections (`vuln.source_freshness_sla`, `vuln.advisory_source_signature_projection`) and Policy impact/conflict projections (`policy.advisory_source_impacts`, `policy.advisory_source_conflicts`) | Ownership split implemented at endpoint boundary; UI composes read-only facts from Concelier + Policy without write side-effects, including backend advisory stats in detail diagnostics (see sprint `20260219_004` SR4-07) | `S00-T05-SEC-02` |
|
||||||
| Evidence and Audit | Evidence home/packs/bundles/export/proof/replay/audit | `source-of-truth.md 3.5`, `authority-matrix.md A: evidence`, `pack-20.md` | `/evidence/*` | `TBD` | `EXISTS_ADAPT` | `EvidenceLocker` | `TBD` | requires consolidated navigation model and consistent search keys | trust links must follow administration ownership override | `S00-T05-EVID-01` |
|
| Evidence and Audit | Evidence home, packs, bundles, export, proof, replay, audit | `source-of-truth.md 3.5`, `authority-matrix.md A: evidence`, `pack-20.md` | `/evidence/*` | `GET /api/v1/evidence` (home); `GET /api/v1/evidence/packs` (pack list); `GET /api/v1/evidence/packs/{id}` (pack detail); `GET /api/v1/evidence/proofs/{subjectDigest}` (proof chain); `GET /api/v1/evidence/thread/{id}` (evidence thread); `GET /api/v1/evidence/audit` (unified audit log); `GET /api/v1/evidence/receipts/cvss/{id}` | `EXISTS_COMPAT` | `EvidenceLocker` + `Attestor` | Existing evidence viewer scopes | Evidence adapter family is implemented for home/packs/proofs/audit/receipts plus thread lookup with deterministic ordering and explicit not-found contracts | Routes are validated by `EvidenceAuditEndpointsTests`; trust management remains an Administration owner workflow while evidence APIs stay read-only consumer surfaces | `S00-T05-EVID-01` |
|
||||||
| Administration | A0-A7 admin surfaces (IAM, policy, trust, system) | `source-of-truth.md 2.2 and 3.8`, `authority-matrix.md A: administration`, `pack-21.md` | `/settings/*` migration targets `TBD` | `TBD` | `EXISTS_ADAPT` | `Authority` | `TBD` | ownership shift from settings to administration needs route/permissions cleanup | high migration surface area | `S00-T05-ADM-01` |
|
| Administration | A0 overview + A1 Identity and Access + A2 Tenant and Branding + A3 Notifications + A4 Usage and Limits + A5 Policy Governance + A6 Trust and Signing + A7 System | `source-of-truth.md 2.2 and 3.8`, `authority-matrix.md A: administration`, `pack-21.md` | `/settings/*` migration targets and new `/administration/*` routes | `GET /api/v1/administration/summary`; `GET /api/v1/administration/identity-access`; `GET /api/v1/administration/tenant-branding`; `GET /api/v1/administration/notifications`; `GET /api/v1/administration/usage-limits`; `GET /api/v1/administration/policy-governance`; `GET /api/v1/administration/trust-signing`; `GET /api/v1/administration/system`; `GET /api/v1/administration/trust-signing/{keys,issuers,certificates,transparency-log}`; `POST /api/v1/administration/trust-signing/keys`; `POST /api/v1/administration/trust-signing/keys/{keyId}/rotate`; `POST /api/v1/administration/trust-signing/keys/{keyId}/revoke`; `POST /api/v1/administration/trust-signing/issuers`; `POST /api/v1/administration/trust-signing/certificates`; `POST /api/v1/administration/trust-signing/certificates/{certificateId}/revoke`; `PUT /api/v1/administration/trust-signing/transparency-log` | `EXISTS_COMPAT` | `Platform` (composition) + `Authority` + `Policy` | `platform.setup.read` for A0/A1/A2/A3/A4/A5/A7 adapters; A6 read routes use `platform.trust.read` (`trust:read`), owner mutations use `platform.trust.write` (`trust:write`) and `platform.trust.admin` (`trust:admin`) | Pack adapters now return deterministic A1-A7 payloads plus `legacyAliases` route-migration metadata for `/settings/*`, `/policy/*`, and `/admin/*`; trust-owner mutation routes persist deterministic state via Platform stores | Adapter surface decouples frontend from legacy prefixes while preserving explicit trust-owner boundaries and admin-grade mutation authorization for keys/issuers/certificates/transparency configuration | `S00-T05-ADM-01` |
|
||||||
| Integrations | Integrations taxonomy and detail + feeds tie-in | `source-of-truth.md 3.7`, `authority-matrix.md A: integrations`, `pack-21.md`, `pack-10.md` | `/settings/integrations/*` and related | `TBD` | `EXISTS_ADAPT` | `Integrations` | `TBD` | advisory connectivity and impact mapping may require model split | coordinate with Advisory Sources spec | `S00-T05-INT-01` |
|
| Integrations | Integrations taxonomy, hub overview, connector detail, feeds tie-in | `source-of-truth.md 3.7`, `authority-matrix.md A: integrations`, `pack-21.md`, `pack-10.md` | `/settings/integrations/*` and `/integrations/*` (partially) | `GET /api/v1/integrations` (hub list); `GET /api/v1/integrations/{id}` (connector detail); `GET /api/v1/integrations/{id}/health` (health check); `GET /api/v1/integrations/{id}/impact` (impact map); `POST /api/v1/integrations/{id}/test` (test connection) | `EXISTS_COMPAT` | `Integrations` | Existing integration admin scopes | Impact map contract is implemented at `/api/v1/integrations/{id}/impact` with deterministic workflow ordering; list/detail/health/test routes remain compatible for pack-21 integration detail tabs | Endpoint behavior is validated in `IntegrationImpactEndpointsTests`; advisory source ownership split remains handled by `S00-T05-SEC-02` | `S00-T05-INT-01` |
|
||||||
| Platform Ops | Data Integrity and Feeds/AirGap ops | `source-of-truth.md 3.6`, `authority-matrix.md A: ops`, `pack-15.md`, `pack-10.md` | `/operations/*` | `TBD` | `EXISTS_ADAPT` | `Orchestrator` | `TBD` | data-integrity aggregate likely spans scheduler/orchestrator/integrations | ensure no duplicated source-of-truth cards | `S00-T05-OPS-01` |
|
| Platform Ops | Data Integrity overview + nightly report + feeds freshness + scan pipeline health + reachability ingest + DLQ + data quality SLOs | `source-of-truth.md 3.6`, `authority-matrix.md A: ops`, `pack-15.md`, `pack-10.md`, `pack-21.md` | `/operations/*` (current) | `GET /api/v1/platform/data-integrity/summary` (overview cards); `GET /api/v1/platform/data-integrity/report` (nightly report); `GET /api/v1/platform/feeds/freshness` (feeds health); `GET /api/v1/platform/scan-pipeline/health`; `GET /api/v1/platform/reachability/ingest-health`; existing DLQ and SLO endpoints | `EXISTS_COMPAT` | `Orchestrator` + `Concelier` + `Scanner` | Existing ops viewer scopes | Platform pack adapters now expose the data-integrity aggregate routes required by Packs 15/21 with deterministic card/report ordering and feed/pipeline/reachability drilldown links | Endpoints and tenant-header validation are covered in `PackAdapterEndpointsTests`; ownership split with Integrations remains explicit per `S00_advisory_sources_spec.md` | `S00-T05-OPS-01` |
|
||||||
|
|
||||||
## Completion checklist
|
## Sign-off requirement
|
||||||
|
|
||||||
- [ ] Replace all `TBD` values with concrete route and endpoint references.
|
Before readiness closure, frontend and backend leads must confirm:
|
||||||
- [ ] Verify one status class per row.
|
- All previously `MISSING_NEW` rows are either shipped or formally deferred with owner/date.
|
||||||
- [ ] Add rows for additional active-authority screens discovered during route audit.
|
- Any `EXISTS_ADAPT` rows (none at this revision) have backend team acknowledgment of planned schema delta.
|
||||||
- [ ] Link each `Action ticket` to a concrete sprint task.
|
- No active-authority screen remains unclassified.
|
||||||
|
|
||||||
|
Sign-off is captured in `S00_handoff_packet.md`.
|
||||||
|
|||||||
@@ -1,19 +1,64 @@
|
|||||||
# S00 Handoff Packet
|
# S00 Handoff Packet
|
||||||
|
|
||||||
Status: Placeholder (created for sprint planning pointer integrity)
|
Status: Published (reconciled to reopened 20260219 sprint wave)
|
||||||
Date: 2026-02-18
|
Date: 2026-02-19
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
|
Sprint: `20260218_005`, task `R0-07`
|
||||||
|
|
||||||
## Upstream artifacts
|
## Purpose
|
||||||
- `S00_advisory_sources_spec.md`
|
|
||||||
- `S00_nav_rendering_policy.md`
|
|
||||||
- `S00_trust_ownership_transition.md`
|
|
||||||
- `S00_route_deprecation_map.md`
|
|
||||||
- `S00_endpoint_contract_ledger_v1.md`
|
|
||||||
|
|
||||||
## Downstream target sprints
|
This packet is the authoritative handoff from sprint `20260218_005` (Spec Freeze) to implementation sprints.
|
||||||
- `SPRINT_20260218_006_FE_ui_v2_rewire_navigation_shell_route_migration.md`
|
All frozen decisions are referenced here.
|
||||||
- `SPRINT_20260218_007_FE_ui_v2_rewire_administration_foundation.md`
|
|
||||||
- `SPRINT_20260218_008_FE_ui_v2_rewire_integrations_platform_ops_data_integrity.md`
|
|
||||||
|
|
||||||
## Current status
|
Implementation execution for this handoff was the reopened sprint set:
|
||||||
- This packet is a planning placeholder and will be expanded when sprint `20260218_005` reaches DONE.
|
|
||||||
|
- `SPRINT_20260219_002` through `SPRINT_20260219_007`
|
||||||
|
- `SPRINT_20260219_008` (backend endpoint + migration dependency closure)
|
||||||
|
- `SPRINT_20260219_015` (Pack-13 promotions contract binding follow-on)
|
||||||
|
|
||||||
|
All completed sprint files from this set are now archived under `docs-archived/implplan/`.
|
||||||
|
|
||||||
|
## Frozen decisions
|
||||||
|
|
||||||
|
| Decision | Document | Key ruling |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Canonical IA taxonomy and root domain ordering | `source-of-truth.md` sections 2.1 and 2.2 | Seven roots: Dashboard, Release Control, Security and Risk, Evidence and Audit, Integrations, Platform Ops, Administration. Order is fixed. |
|
||||||
|
| Ownership boundaries (Policy, Trust, System, Security Data split) | `source-of-truth.md` section 2.2, `authority-matrix.md` section B | Policy Governance -> Administration. Trust and Signing -> Administration. System -> Administration. Legacy Security Data -> split: connectivity in Integrations/Platform Ops, gating impact in Security and Risk. |
|
||||||
|
| Superseded alternatives (forbidden placements) | `authority-matrix.md` section B; `S00_nav_rendering_policy.md` do-not list | Trust in Evidence, Policy in Release Control, System as top-level root are forbidden. |
|
||||||
|
| Release Control capability rendering policy | `S00_nav_rendering_policy.md` | Releases and Approvals may be direct nav shortcuts under Release Control group; Bundles, Deployments, and Environments stay grouped under Release Control ownership. |
|
||||||
|
| Advisory Sources screen ownership and spec | `S00_advisory_sources_spec.md` | Security and Risk owns decision-impact view. Integrations owns connector config. Platform Ops owns freshness ops. |
|
||||||
|
| Trust and Signing ownership transition and consumer model | `S00_trust_ownership_transition.md` | Administration is sole owner. Evidence and Audit and Security and Risk are consumers with read-only links only. |
|
||||||
|
| Route deprecation map and activation sequence | `S00_route_deprecation_map.md` | Complete v1 -> v2 mapping with per-sprint activation sequence. |
|
||||||
|
| Endpoint contract ledger v1 | `S00_endpoint_contract_ledger_v1.md` | 12 screen domains classified; previously missing rows `S00-T05-RC-01` and `S00-T05-SEC-02` are now reconciled to shipped backend contracts (`EXISTS_COMPAT`). |
|
||||||
|
|
||||||
|
## Downstream target sprints (executed and archived)
|
||||||
|
|
||||||
|
| Sprint | Dependency on S00 decisions | Unblocked after |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SPRINT_20260219_002_FE_ui_v2_shell_navigation_and_route_truth` | Nav rendering policy, route deprecation map | `SPRINT_20260219_001` DONE |
|
||||||
|
| `SPRINT_20260219_003_FE_ui_v2_shell_release_control_structure` | Release Control ownership policy, Pack 12/13/14 structure, contract ledger RC rows | `SPRINT_20260219_002` |
|
||||||
|
| `SPRINT_20260219_004_FE_ui_v2_shell_security_and_advisory_sources` | Advisory Sources spec, ownership split, contract ledger SEC rows | `SPRINT_20260219_002` |
|
||||||
|
| `SPRINT_20260219_005_FE_ui_v2_shell_evidence_audit_structure` | Trust transition doc, evidence ownership policy, contract ledger EVID row | `SPRINT_20260219_002` |
|
||||||
|
| `SPRINT_20260219_006_FE_ui_v2_shell_integrations_platform_ops_alignment` | Integrations/Platform Ops taxonomy, security-data split policy | `SPRINT_20260219_002` |
|
||||||
|
| `SPRINT_20260219_007_FE_ui_v2_shell_qa_and_readiness_reverification` | Strict closure gate, ledger reconciliation, readiness publication | `SPRINT_20260219_003` to `SPRINT_20260219_006` |
|
||||||
|
|
||||||
|
## Unresolved risks (carry into implementation)
|
||||||
|
|
||||||
|
| Risk | Severity | Mitigation | Owner sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Bundle API (`S00-T05-RC-01`) contract drift after implementation | Medium | Keep ledger pinned to implemented `/api/v1/release-control/bundles*` routes and reject path regressions that collide with evidence bundle export namespace. | `SPRINT_20260219_008` + downstream QA |
|
||||||
|
| Advisory Sources cross-service composition drift (`S00-T05-SEC-02`) | Medium | Keep Concelier freshness and Policy impact/conflicts ownership split explicit; verify tenant/scope behavior in readiness reruns. | `SPRINT_20260219_008` + downstream QA |
|
||||||
|
| Trust scope model (`trust:read`, `trust:write`, `trust:admin`) requires Authority alignment | Closed (2026-02-19) | Authority canonical scopes and Platform trust policies are wired; A6 now includes owner mutation routes (`/api/v1/administration/trust-signing/{keys,issuers,certificates,transparency-log}`) with DB backing via migration `046_TrustSigningAdministration.sql`. | `SPRINT_20260219_016` |
|
||||||
|
| Approvals multi-tab fan-out latency (`S00-T05-APR-01`) | Medium | Preserve lazy loading and stale-data behavior in shell and add backend performance verification in follow-on integration work. | `SPRINT_20260219_003` |
|
||||||
|
| Data Integrity aggregate endpoint (`S00-T05-OPS-01`) spans modules | Medium | Keep ownership split explicit in shell and assign backend composition owner before full readiness GO. | `SPRINT_20260219_006` / `SPRINT_20260219_007` |
|
||||||
|
| Legacy alias removal can miss long-tail deep links | Low | Keep redirect map under strict tests and remove aliases only after measured traffic evidence. | `SPRINT_20260219_002` / `SPRINT_20260219_007` |
|
||||||
|
|
||||||
|
## Contract ledger sign-off status
|
||||||
|
|
||||||
|
- Frontend shell sign-off is complete through `SPRINT_20260219_006`.
|
||||||
|
- Backend dependency sign-off for previously unresolved rows (`S00-T05-RC-01`, `S00-T05-SEC-02`) is now complete via `SPRINT_20260219_008` evidence and ledger reconciliation.
|
||||||
|
- Promotions row `S00-T05-RC-02` and Administration row `S00-T05-ADM-01` are fully reconciled to `EXISTS_COMPAT` via `SPRINT_20260219_016` backend contract enrichment evidence.
|
||||||
|
|
||||||
|
## Non-shipped exploratory work
|
||||||
|
|
||||||
|
None.
|
||||||
|
|||||||
@@ -1,25 +1,116 @@
|
|||||||
# S00 Nav Rendering Policy
|
# S00 Nav Rendering Policy
|
||||||
|
|
||||||
Status: Draft (created for sprint planning pointer integrity)
|
Status: Frozen
|
||||||
Date: 2026-02-18
|
Date: 2026-02-18
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
|
Sprint: `20260218_005`, task `R0-03`
|
||||||
|
|
||||||
## Policy statement
|
## Policy statement
|
||||||
Release Control-owned capabilities may be rendered as direct shortcuts if and only if ownership remains labeled as Release Control in breadcrumbs and headers.
|
|
||||||
|
|
||||||
## Allowed model
|
Release Control-owned capabilities may be rendered as direct shortcuts in the sidebar if and only if:
|
||||||
- Root domains remain canonical.
|
1. Ownership is labeled as **Release Control** in breadcrumbs and page headers.
|
||||||
- Shortcuts allowed for `Releases` and `Approvals` when they route to Release Control-owned routes.
|
2. The canonical routes for those capabilities live under `/release-control/*`.
|
||||||
- `Bundles`, `Deployments`, and `Regions and Environments` remain under Release Control navigation hierarchy.
|
3. The sidebar shortcut links to the canonical route, not an alias.
|
||||||
|
|
||||||
|
This policy prevents mixed rendering where the same screen appears to be owned by two domains.
|
||||||
|
|
||||||
|
## Allowed rendering model
|
||||||
|
|
||||||
|
### Desktop (expanded sidebar)
|
||||||
|
|
||||||
|
```
|
||||||
|
Dashboard
|
||||||
|
Release Control
|
||||||
|
├── Releases [shortcut direct nav allowed]
|
||||||
|
├── Approvals [shortcut direct nav allowed]
|
||||||
|
├── Bundles [nested only — no direct shortcut]
|
||||||
|
├── Deployments [nested only — no direct shortcut]
|
||||||
|
└── Regions & Environments [nested only — no direct shortcut]
|
||||||
|
Security & Risk
|
||||||
|
Evidence & Audit
|
||||||
|
Integrations
|
||||||
|
Platform Ops
|
||||||
|
Administration
|
||||||
|
```
|
||||||
|
|
||||||
|
`Releases` and `Approvals` may appear as direct children under `Release Control` in the sidebar
|
||||||
|
(rather than requiring expand → click).
|
||||||
|
`Bundles`, `Deployments`, and `Regions & Environments` remain nested and require expand.
|
||||||
|
|
||||||
|
### Desktop (collapsed sidebar — icons only)
|
||||||
|
|
||||||
|
- Show icon for Release Control root only.
|
||||||
|
- Tooltip on hover shows "Release Control".
|
||||||
|
- Click navigates to Release Control overview or last active child.
|
||||||
|
- No separate Releases / Approvals icons in collapsed mode.
|
||||||
|
|
||||||
|
### Mobile (navigation drawer)
|
||||||
|
|
||||||
|
- All root domains appear as top-level items in the drawer.
|
||||||
|
- Release Control expands in-place to show child nav items.
|
||||||
|
- `Releases` and `Approvals` may appear as drawer children with Release Control as visible parent.
|
||||||
|
- No Release Control capabilities may appear as top-level drawer items separate from the Release Control group.
|
||||||
|
|
||||||
## Breadcrumb rules
|
## Breadcrumb rules
|
||||||
- Any shortcut route must render breadcrumb prefix `Release Control`.
|
|
||||||
- Header titles use canonical naming; optional compatibility labels may be temporary.
|
|
||||||
|
|
||||||
## Non-allowed model
|
Canonical format: `Root Domain > Capability > [Sub-page]`
|
||||||
- Dual ownership labels for same screen.
|
|
||||||
- Divergent mobile vs desktop ownership paths.
|
|
||||||
- Legacy settings-first entry as primary owner path.
|
|
||||||
|
|
||||||
## Route guidance
|
| Scenario | Breadcrumb | Notes |
|
||||||
- Use alias redirects for historical direct paths.
|
| --- | --- | --- |
|
||||||
- Canonical targets must live under final IA route families.
|
| Releases list | `Release Control > Releases` | No shortcut bypasses ownership label |
|
||||||
|
| Release detail | `Release Control > Releases > RCB-1234` | ID or name appended |
|
||||||
|
| Approvals queue | `Release Control > Approvals` | |
|
||||||
|
| Approval detail | `Release Control > Approvals > APR-5678` | |
|
||||||
|
| Bundle catalog | `Release Control > Bundles` | |
|
||||||
|
| Bundle detail | `Release Control > Bundles > my-bundle` | |
|
||||||
|
| Bundle version detail | `Release Control > Bundles > my-bundle > v1.3.0` | |
|
||||||
|
| Deployments | `Release Control > Deployments` | |
|
||||||
|
| Environments list | `Release Control > Regions & Environments` | |
|
||||||
|
| Environment detail | `Release Control > Regions & Environments > staging-eu` | |
|
||||||
|
|
||||||
|
### Concrete counter-examples (forbidden)
|
||||||
|
|
||||||
|
| Forbidden breadcrumb | Reason |
|
||||||
|
| --- | --- |
|
||||||
|
| `Approvals > APR-5678` | Missing Release Control ownership prefix |
|
||||||
|
| `Releases` (no parent) | Same — no domain context |
|
||||||
|
| `Settings > Policy Governance` | Policy Governance owner is Administration, not Settings |
|
||||||
|
| `Evidence & Audit > Trust & Signing` | Trust & Signing owner is Administration; Evidence may only show a consumer link |
|
||||||
|
|
||||||
|
## Legacy label transition behavior
|
||||||
|
|
||||||
|
Where users know a surface by an old label, show a compact transition label during the migration window defined in `S00_route_deprecation_map.md`.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Transition labels appear only in page headers and sidebar items, not in breadcrumbs.
|
||||||
|
- Format: canonical label is primary; old label appears parenthetically — e.g., `Policy Governance (formerly Policy Studio)`.
|
||||||
|
- Transition labels are removed at sprint 016 cutover unless traffic evidence requires extension.
|
||||||
|
- Canonical labels are always primary; old labels never replace canonical ones.
|
||||||
|
|
||||||
|
Planned transition labels:
|
||||||
|
|
||||||
|
| Canonical label | Transition label (migration window only) | Remove at |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Security & Risk` | `Security & Risk (formerly Security)` | Sprint 016 |
|
||||||
|
| `Platform Ops` | `Platform Ops (formerly Operations)` | Sprint 016 |
|
||||||
|
| `Evidence & Audit` | `Evidence & Audit (formerly Evidence)` | Sprint 016 |
|
||||||
|
| `Policy Governance` | `Policy Governance (formerly Policy Studio / Policy)` | Sprint 016 |
|
||||||
|
|
||||||
|
## Explicit do-not list
|
||||||
|
|
||||||
|
The following rendering patterns are forbidden in any sprint implementation:
|
||||||
|
|
||||||
|
1. **Do not** place Release Control capability screens (`Releases`, `Approvals`, `Bundles`, `Deployments`, `Environments`) as root-level sidebar items independent from the `Release Control` group.
|
||||||
|
2. **Do not** display a breadcrumb that omits the canonical root domain prefix.
|
||||||
|
3. **Do not** show different ownership labels on desktop vs. mobile for the same screen.
|
||||||
|
4. **Do not** use legacy root-level nav paths (e.g., `/approvals`, `/releases`) as the canonical nav target — they must redirect to `/release-control/*` canonical targets.
|
||||||
|
5. **Do not** label `Trust & Signing` as owned by Evidence & Audit or Security in any nav or header.
|
||||||
|
6. **Do not** label `Policy Governance` as owned by Release Control in any nav or header.
|
||||||
|
7. **Do not** introduce a new root domain that is not in the canonical 7: Dashboard, Release Control, Security & Risk, Evidence & Audit, Integrations, Platform Ops, Administration.
|
||||||
|
|
||||||
|
## Route alias requirements for migration
|
||||||
|
|
||||||
|
During the alias window, current root-level paths (`/releases`, `/approvals`) must:
|
||||||
|
- Resolve to the canonical `/release-control/releases` and `/release-control/approvals` routes.
|
||||||
|
- Render the canonical breadcrumb (e.g., `Release Control > Releases`) — not an alias-derived breadcrumb.
|
||||||
|
- Not appear as primary nav items in the sidebar; the sidebar must link to canonical paths only.
|
||||||
|
|||||||
@@ -1,26 +1,183 @@
|
|||||||
# S00 Route Deprecation Map
|
# S00 Route Deprecation Map
|
||||||
|
|
||||||
Status: Draft baseline (created for sprint planning pointer integrity)
|
Status: Frozen baseline
|
||||||
Date: 2026-02-18
|
Date: 2026-02-18
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
|
Canonical source: `source-of-truth.md`, `authority-matrix.md`
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
Baseline mapping for legacy route families to canonical IA targets.
|
|
||||||
|
|
||||||
## Route action values
|
Complete route baseline mapping current v1 canonical paths to v2 target IA families.
|
||||||
- `keep`
|
Every major route family must have exactly one migration action.
|
||||||
- `redirect`
|
This map governs all implementation in sprints 006 through 016.
|
||||||
- `alias`
|
|
||||||
- `remove-later`
|
|
||||||
|
|
||||||
## Baseline mapping examples
|
## Route action definitions
|
||||||
| Legacy family | Canonical target family | Action |
|
|
||||||
|
| Action | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `keep` | Path and semantics are unchanged; no migration work required. |
|
||||||
|
| `redirect` | Current path redirects to v2 canonical target; old path is no longer authoritative. |
|
||||||
|
| `alias` | Current path remains active and resolves to the same content as canonical; both paths are valid during the migration window. Planned for removal after cutover. |
|
||||||
|
| `remove-later` | Path is superseded; leave as redirect stub until traffic confirms safety, then remove in sprint 016. |
|
||||||
|
|
||||||
|
## Section 1 — Root domain family migrations
|
||||||
|
|
||||||
|
These are the highest-priority mappings because they affect top-level navigation and all deep links.
|
||||||
|
|
||||||
|
| Current v1 path family | v2 canonical target family | Action | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/` (control-plane landing) | `/dashboard` | `redirect` | Current Control Plane becomes Dashboard v3 landing. Sprint 012 implements target. |
|
||||||
|
| `/security/*` | `/security-risk/*` | `redirect` + temporary `alias` | High-traffic. Alias `/security/*` during sprint 014 window; remove in sprint 016. |
|
||||||
|
| `/operations/*` | `/platform-ops/*` | `redirect` + temporary `alias` | Ops team bookmarks. Alias during sprint 008 window; remove in sprint 016. |
|
||||||
|
| `/evidence/*` | `/evidence-audit/*` | `redirect` + temporary `alias` | Alias during sprint 015 window; remove in sprint 016. |
|
||||||
|
| `/policy/*` | `/administration/policy-governance/*` | `redirect` | Ownership change. High risk; enforce breadcrumb and ownership labels per nav policy. |
|
||||||
|
| `/settings/*` (admin subset) | `/administration/*` | `redirect` | Split: admin sub-paths go to `/administration/*`; integration sub-paths go to `/integrations/*`. |
|
||||||
|
| `/settings/integrations/*` | `/integrations/*` | `redirect` | Integrations becomes a canonical root domain. |
|
||||||
|
| `/integrations/*` (current shallow root) | `/integrations/*` (v2 canonical root) | `keep` | Route family stays. Sprint 008 expands content and taxonomy. |
|
||||||
|
| `/approvals/*` | `/release-control/approvals/*` | `redirect` + temporary `alias` | Alias `/approvals/*` for operator convenience during cutover; remove in sprint 016. |
|
||||||
|
| `/releases/*` | `/release-control/releases/*` | `redirect` + temporary `alias` | High-traffic operator route. Alias during sprints 010-016 window. |
|
||||||
|
| `/environments/*` | `/release-control/environments/*` | `redirect` | Medium risk. |
|
||||||
|
| `/deployments/*` | `/release-control/deployments/*` | `redirect` | Medium risk. |
|
||||||
|
| `/analytics/*` | `/security-risk/analytics/*` | `redirect` | Analytics is consumed under Security & Risk. |
|
||||||
|
|
||||||
|
## Section 2 — Settings sub-family migrations
|
||||||
|
|
||||||
|
All settings sub-paths have a final canonical owner under Administration or Integrations.
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/settings/admin/users` | `/administration/identity-access/users` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/roles` | `/administration/identity-access/roles` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/tenants` | `/administration/identity-access/tenants` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/clients` | `/administration/identity-access/clients` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/tokens` | `/administration/identity-access/tokens` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/branding` | `/administration/tenant-branding` | `redirect` | 007 |
|
||||||
|
| `/settings/admin/:page` | `/administration/:page` | `redirect` (catch-all) | 007 |
|
||||||
|
| `/settings/trust/*` | `/administration/trust-signing/*` | `redirect` | 007 |
|
||||||
|
| `/settings/notifications/*` | `/administration/notifications/*` | `redirect` | 007 |
|
||||||
|
| `/settings/security-data/trivy` | `/integrations/feeds/trivy` | `redirect` | 008 |
|
||||||
|
| `/settings/sbom-sources/*` | `/integrations/sbom-sources/*` | `redirect` | 008 |
|
||||||
|
| `/settings/workflows/*` | `/administration/system/workflows` | `redirect` | 007 |
|
||||||
|
| `/settings/profile` | `/administration/profile` | `alias` | 007 (keep; `/administration/profile` is canonical) |
|
||||||
|
| `/settings/configuration-pane` | `/administration/system/configuration` | `redirect` | 007 |
|
||||||
|
|
||||||
|
## Section 3 — Evidence & Audit sub-family migrations
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/evidence` | `/evidence-audit` | `redirect` + alias | 015 |
|
||||||
|
| `/evidence/audit` | `/evidence-audit/audit` | `redirect` | 015 |
|
||||||
|
| `/evidence/packs/*` | `/evidence-audit/packs/*` | `redirect` | 015 |
|
||||||
|
| `/evidence/proofs/*` | `/evidence-audit/proofs/*` | `alias` | 015 (permanent convenience alias for external linking) |
|
||||||
|
| `/evidence/change-trace/*` | `/evidence-audit/change-trace/*` | `redirect` | 015 |
|
||||||
|
| `/evidence/receipts/cvss/*` | `/evidence-audit/receipts/cvss/*` | `redirect` | 015 |
|
||||||
|
| `/evidence-thread/*` | `/evidence-audit/thread/*` | `redirect` | 015 |
|
||||||
|
| `/timeline/*` | `/evidence-audit/timeline/*` | `redirect` | 015 |
|
||||||
|
|
||||||
|
## Section 4 — Platform Ops sub-family migrations
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/operations/feeds/*` | `/platform-ops/data-integrity/feeds/*` | `redirect` | 008 |
|
||||||
|
| `/operations/orchestrator/*` | `/platform-ops/orchestrator/*` | `redirect` | 008 |
|
||||||
|
| `/operations/health` | `/platform-ops/health` | `redirect` | 008 |
|
||||||
|
| `/operations/quotas/*` | `/platform-ops/quotas/*` | `redirect` | 008 |
|
||||||
|
| `/operations/slo` | `/platform-ops/data-integrity/slo` | `redirect` | 008 |
|
||||||
|
| `/operations/dead-letter` | `/platform-ops/orchestrator/dead-letter` | `redirect` | 008 |
|
||||||
|
| `/operations/aoc` | `/platform-ops/aoc` | `redirect` | 008 |
|
||||||
|
| `/operations/doctor` | `/platform-ops/doctor` | `redirect` | 008 |
|
||||||
|
| `/operations/offline-kit/*` | `/platform-ops/offline-kit/*` | `redirect` | 008 |
|
||||||
|
| `/operations/agents/*` | `/platform-ops/agents/*` | `redirect` | 008 |
|
||||||
|
| `/operations/scanner/*` | `/platform-ops/scanner/*` | `redirect` | 008 |
|
||||||
|
| `/operations/packs/*` | `/platform-ops/pack-registry/*` | `redirect` | 008 |
|
||||||
|
| `/operations/signals/*` | `/platform-ops/signals/*` | `redirect` | 008 |
|
||||||
|
| `/operations/ai-runs/*` | `/platform-ops/ai-runs/*` | `redirect` | 008 |
|
||||||
|
| `/operations/notifications` | `/administration/notifications` | `redirect` | 007 (ownership change) |
|
||||||
|
| `/operations/status` | `/administration/system/status` | `redirect` | 007 (ownership change) |
|
||||||
|
|
||||||
|
## Section 5 — Release Control sub-family migrations
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/releases` | `/release-control/releases` | `redirect` + alias | 010 |
|
||||||
|
| `/releases/:id` | `/release-control/releases/:id` | `redirect` | 010 |
|
||||||
|
| `/approvals` | `/release-control/approvals` | `redirect` + alias | 011 |
|
||||||
|
| `/approvals/:id` | `/release-control/approvals/:id` | `redirect` | 011 |
|
||||||
|
| `/environments` | `/release-control/environments` | `redirect` | 013 |
|
||||||
|
| `/environments/:id` | `/release-control/environments/:id` | `redirect` | 013 |
|
||||||
|
| `/deployments/*` | `/release-control/deployments/*` | `redirect` | 010 |
|
||||||
|
| (new) `/release-control/bundles/*` | `/release-control/bundles/*` | `new (implemented)` | 20260219_003 |
|
||||||
|
|
||||||
|
## Section 6 — Security & Risk sub-family migrations
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/security` | `/security-risk` | `redirect` + alias | 014 |
|
||||||
|
| `/security/findings/*` | `/security-risk/findings/*` | `redirect` | 014 |
|
||||||
|
| `/security/vulnerabilities/*` | `/security-risk/vulnerabilities/*` | `redirect` | 014 |
|
||||||
|
| `/security/sbom/graph` | `/security-risk/sbom/graph` | `redirect` | 014 |
|
||||||
|
| `/security/lineage/*` | `/security-risk/lineage/*` | `redirect` | 014 |
|
||||||
|
| `/security/reachability` | `/security-risk/reachability` | `redirect` | 014 |
|
||||||
|
| `/security/risk` | `/security-risk/risk` | `redirect` | 014 |
|
||||||
|
| `/security/artifacts/*` | `/security-risk/artifacts/*` | `redirect` | 014 |
|
||||||
|
| `/security/vex/*` | `/security-risk/vex/*` | `redirect` | 014 |
|
||||||
|
| `/security/unknowns` | `/security-risk/unknowns` | `redirect` | 014 |
|
||||||
|
| `/security/patch-map` | `/security-risk/patch-map` | `redirect` | 014 |
|
||||||
|
| `/security/scans/*` | `/security-risk/scans/*` | `redirect` | 014 |
|
||||||
|
| (new) `/security-risk/advisory-sources` | `/security-risk/advisory-sources` | `new (implemented)` | 20260219_004 |
|
||||||
|
|
||||||
|
## Section 7 — Administration sub-family migrations
|
||||||
|
|
||||||
|
| Current v1 path | v2 target | Action | Sprint |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/policy/governance` | `/administration/policy-governance` | `redirect` | 007 |
|
||||||
|
| `/policy/exceptions/*` | `/administration/policy-governance/exceptions/*` | `redirect` | 007 |
|
||||||
|
| `/policy/packs/*` | `/administration/policy-governance/packs/*` | `redirect` | 007 |
|
||||||
|
| `/admin/trust/*` | `/administration/trust-signing/*` | `redirect` | 007 |
|
||||||
|
| `/admin/audit` | `/evidence-audit/audit` | `redirect` | 015 |
|
||||||
|
| `/admin/notifications` | `/administration/notifications` | `redirect` | 007 |
|
||||||
|
| `/admin/policy/governance` | `/administration/policy-governance` | `redirect` | 007 |
|
||||||
|
| `/admin/policy/simulation` | `/administration/policy-governance/simulation` | `redirect` | 007 |
|
||||||
|
| `/admin/registries` | `/integrations/registries` | `redirect` | 008 |
|
||||||
|
| `/admin/issuers` | `/administration/trust-signing/issuers` | `redirect` | 007 |
|
||||||
|
| `/admin/vex-hub/*` | `/security-risk/vex/*` | `redirect` | 014 |
|
||||||
|
|
||||||
|
## Section 8 — Remove-later candidates
|
||||||
|
|
||||||
|
Paths that are stale and should be removed after traffic confirmation:
|
||||||
|
|
||||||
|
| Path | Current state | Proposed timeline |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `/settings/*` admin-owned surfaces | `/administration/*` | `redirect` |
|
| `/home` | Already redirects to `/` | Sprint 016: confirm and remove from app.routes |
|
||||||
| `/settings/security-data` | split to `/integrations/*` and `/security/*` contexts | `redirect` |
|
| `/orchestrator/*` | Already redirects to `/operations/*` → sprint 008 will update to `/platform-ops/*` | Sprint 016 |
|
||||||
| `/integrations/*` legacy settings paths | `/integrations/*` canonical root | `alias` |
|
| `/release-orchestrator/*` | Already redirects to root routes | Sprint 016 |
|
||||||
| historical trust routes | `/administration/trust*` | `redirect` |
|
| `/ops/*` | Already redirects to `/operations/*` → sprint 008 will update | Sprint 016 |
|
||||||
| historical ops aliases | `/operations/*` canonical root | `alias` |
|
| `/console/*` | Already redirects to `/settings/*` → sprint 007 will update to `/administration/*` | Sprint 016 |
|
||||||
|
| `/triage/*` | Already redirects to `/security/*` → sprint 014 will update | Sprint 016 |
|
||||||
|
| `/qa/*` (internal workbenches) | Internal tooling; keep as `alias` long-term | No sprint 016 removal |
|
||||||
|
|
||||||
## Notes
|
## Section 9 — High-risk deep-link mitigation
|
||||||
- Full detailed map is completed in sprint `20260218_005` task `R0-05`.
|
|
||||||
- Query and fragment preservation is required for redirect families.
|
| Risk | Mitigation |
|
||||||
|
| --- | --- |
|
||||||
|
| `/approvals/:id` bookmarks (operators) | Alias `/approvals/:id` until sprint 016 cutover confirmation. |
|
||||||
|
| `/releases/:id` links from CI/CD notifications | Alias `/releases/:id` until sprint 016. Log alias traffic before removal. |
|
||||||
|
| `/settings/trust/*` from admin-written runbooks | Update internal runbooks in sprint 007 alongside redirect implementation. |
|
||||||
|
| `/policy/*` ownership migration confuses policy authors | Apply transition labels in sprint 007 alongside redirect; breadcrumb shows `Administration > Policy Governance`. |
|
||||||
|
| `/operations/*` ops-team dashboards with hardcoded links | Announce alias window in release notes. Alias during sprint 008-016 window. |
|
||||||
|
|
||||||
|
## Section 10 — Activation sequence
|
||||||
|
|
||||||
|
| Sprint | Routes activated / aliases established |
|
||||||
|
| --- | --- |
|
||||||
|
| 006 | Root nav + canonical domain route trees; alias existing roots to new domains |
|
||||||
|
| 007 | Administration domain routes; redirect `/settings/admin/*`, `/policy/*`, `/admin/*` paths |
|
||||||
|
| 008 | Integrations and Platform Ops routes; redirect `/operations/*`, `/settings/integrations/*` paths |
|
||||||
|
| 009 | Bundle routes under `/release-control/bundles/*` (new) |
|
||||||
|
| 010 | Release and promotion routes; redirect `/releases/*`, `/deployments/*` |
|
||||||
|
| 011 | Approvals routes; alias `/approvals/*` to `/release-control/approvals/*` |
|
||||||
|
| 012 | Dashboard v3; redirect `/` and update home behavior |
|
||||||
|
| 013 | Environment detail routes; redirect `/environments/*` |
|
||||||
|
| 014 | Security & Risk routes; alias `/security/*` |
|
||||||
|
| 015 | Evidence & Audit routes; alias `/evidence/*` |
|
||||||
|
| 016 | Remove all `alias` and `remove-later` temporary paths; publish cutover confirmation |
|
||||||
|
|||||||
@@ -1,23 +1,96 @@
|
|||||||
# S00 Trust Ownership Transition
|
# S00 Trust Ownership Transition
|
||||||
|
|
||||||
Status: Draft (created for sprint planning pointer integrity)
|
Status: Frozen
|
||||||
Date: 2026-02-18
|
Date: 2026-02-18
|
||||||
|
Working directory: `docs/modules/ui/v2-rewire`
|
||||||
|
Sprint: `20260218_005`, task `R0-04`
|
||||||
|
|
||||||
## Ownership decision
|
## Ownership decision
|
||||||
`Administration` is the owner domain for Trust and Signing.
|
|
||||||
|
`Administration` is the sole owner domain for Trust and Signing.
|
||||||
|
This is a final decision (Pack 21 overrides Packs 9, 11, and 20 on ownership).
|
||||||
|
|
||||||
|
No other domain may host trust management screens. Trust management includes:
|
||||||
|
- Key lifecycle (rotate, revoke, generate).
|
||||||
|
- Issuer/CA registration and trust configuration.
|
||||||
|
- Certificate lifecycle and renewal.
|
||||||
|
- Transparency log configuration.
|
||||||
|
- Trust scoring policy.
|
||||||
|
|
||||||
## Consumer model
|
## Consumer model
|
||||||
- `Evidence and Audit` consumes trust state through deep links and contextual trust indicators.
|
|
||||||
- `Security and Risk` consumes issuer/signature confidence as decision context.
|
|
||||||
|
|
||||||
## Route policy
|
Two domains consume trust state without owning it:
|
||||||
- Legacy trust routes redirect or alias to Administration trust pages.
|
|
||||||
- Evidence and Security pages must not host owner-duplicate trust management screens.
|
|
||||||
|
|
||||||
## UX policy
|
### Evidence & Audit (consumer)
|
||||||
- Trust actions (rotate, issuer management, cert lifecycle) remain in Administration.
|
- Displays trust indicators on proof chain, attestation, and evidence node views.
|
||||||
- Consumer pages provide contextual links with preserved entity ids.
|
- Links to Administration > Trust & Signing > [entity] for management actions.
|
||||||
|
- Read-only trust status display only; no management surface.
|
||||||
|
- Preserved entity id must be included in all deep links to Administration trust pages.
|
||||||
|
|
||||||
## Risk controls
|
### Security & Risk (consumer)
|
||||||
- Prevent duplicate owner surfaces.
|
- Displays issuer/signature confidence as a decision context field in security findings, advisory sources, and approval tabs.
|
||||||
- Ensure breadcrumbs and page headers always indicate Administration ownership.
|
- Links to Administration > Trust & Signing > Issuers > [issuerId] when an issuer is referenced in a finding or advisory.
|
||||||
|
- Read-only trust confidence display only; no management surface.
|
||||||
|
|
||||||
|
## Cross-link contract
|
||||||
|
|
||||||
|
All trust management deep links from consumer domains must:
|
||||||
|
1. Navigate to the Administration trust screen that is the canonical owner of the referenced entity.
|
||||||
|
2. Preserve the entity identifier as a route parameter or query parameter.
|
||||||
|
3. Return-navigation must allow the user to return to the originating domain context.
|
||||||
|
|
||||||
|
| Consumer page | Link target | Preserved context |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Evidence proof chain node (issuer) | `/administration/trust-signing/issuers/:issuerId` | `issuerId` |
|
||||||
|
| Evidence attestation detail (signing key) | `/administration/trust-signing/keys/:keyId` | `keyId` |
|
||||||
|
| Security finding advisory (issuer trust) | `/administration/trust-signing/issuers/:issuerId` | `issuerId` |
|
||||||
|
| Approval detail — trust confidence indicator | `/administration/trust-signing` (overview) | none required |
|
||||||
|
| Security advisory source — signature status | `/administration/trust-signing/issuers` (filtered) | `sourceId` as query param |
|
||||||
|
|
||||||
|
## Alias and deprecation behavior by route family
|
||||||
|
|
||||||
|
| Legacy path | v2 canonical target | Action | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/admin/trust` | `/administration/trust-signing` | `redirect` | Sprint 007 |
|
||||||
|
| `/admin/trust/keys` | `/administration/trust-signing/keys` | `redirect` | Sprint 007 |
|
||||||
|
| `/admin/trust/issuers` | `/administration/trust-signing/issuers` | `redirect` | Sprint 007 |
|
||||||
|
| `/admin/trust/certs` | `/administration/trust-signing/certificates` | `redirect` | Sprint 007 |
|
||||||
|
| `/admin/trust/:page` | `/administration/trust-signing/:page` | `redirect` (catch-all) | Sprint 007 |
|
||||||
|
| `/admin/issuers` | `/administration/trust-signing/issuers` | `redirect` | Sprint 007 |
|
||||||
|
| `/settings/trust` | `/administration/trust-signing` | `redirect` | Sprint 007 |
|
||||||
|
| `/settings/trust/:page` | `/administration/trust-signing/:page` | `redirect` (catch-all) | Sprint 007 |
|
||||||
|
| `/evidence/trust` | `/administration/trust-signing` | `redirect` | Sprint 015 (if exists) |
|
||||||
|
|
||||||
|
Alias window: trust route aliases are removed at sprint 016 cutover.
|
||||||
|
Legacy `/admin/trust/*` and `/settings/trust/*` paths must not remain as primary navigation targets after sprint 007.
|
||||||
|
|
||||||
|
## Auth scope implications
|
||||||
|
|
||||||
|
| Action | Required scope | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| View trust overview and key list | `trust:read` | Read-only access; auditors and security reviewers |
|
||||||
|
| View issuer list and trust scoring | `trust:read` | Read access |
|
||||||
|
| Create or update key, rotate key | `trust:write` | Restricted to trust admins |
|
||||||
|
| Revoke key or certificate | `trust:admin` | Highest privilege; requires explicit MFA re-auth recommendation |
|
||||||
|
| Register issuer | `trust:write` | |
|
||||||
|
| Configure transparency log | `trust:admin` | |
|
||||||
|
| View trust state in consumer domains (Evidence, Security) | No additional scope; inherited from existing page access | Consumer pages do not require trust scope to display trust indicators |
|
||||||
|
|
||||||
|
Trust scope constants are now implemented in Authority (`StellaOpsScopes.TrustRead`, `StellaOpsScopes.TrustWrite`, `StellaOpsScopes.TrustAdmin`) and mapped in Platform policy wiring.
|
||||||
|
`/api/v1/administration/trust-signing` now enforces `platform.trust.read` (`trust:read`) and contract row `S00-T05-ADM-01` remains `EXISTS_COMPAT`.
|
||||||
|
|
||||||
|
Trust-owner backend mutation routes are now implemented under Platform Administration A6:
|
||||||
|
- `POST /api/v1/administration/trust-signing/keys` (`platform.trust.write`)
|
||||||
|
- `POST /api/v1/administration/trust-signing/keys/{keyId}/rotate` (`platform.trust.write`)
|
||||||
|
- `POST /api/v1/administration/trust-signing/keys/{keyId}/revoke` (`platform.trust.admin`)
|
||||||
|
- `POST /api/v1/administration/trust-signing/issuers` (`platform.trust.write`)
|
||||||
|
- `POST /api/v1/administration/trust-signing/certificates` (`platform.trust.write`)
|
||||||
|
- `POST /api/v1/administration/trust-signing/certificates/{certificateId}/revoke` (`platform.trust.admin`)
|
||||||
|
- `PUT /api/v1/administration/trust-signing/transparency-log` (`platform.trust.admin`)
|
||||||
|
|
||||||
|
## Non-allowed regressions
|
||||||
|
|
||||||
|
- Evidence & Audit may not host a `Trust Management` section or own a trust key/issuer editing surface.
|
||||||
|
- Security & Risk may not host issuer or key management; only trust confidence indicators are allowed.
|
||||||
|
- Legacy route paths (`/admin/trust/*`, `/settings/trust/*`) may not be kept as primary authoritative routes after sprint 007; they must redirect.
|
||||||
|
- Breadcrumbs on all trust pages must show `Administration > Trust & Signing > ...`, never `Evidence > Trust` or `Security > Trust`.
|
||||||
|
|||||||
189
docs/modules/ui/v2-rewire/S16_release_readiness_package.md
Normal file
189
docs/modules/ui/v2-rewire/S16_release_readiness_package.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# UI V2 Rewire - Release Readiness Package
|
||||||
|
|
||||||
|
**Sprint:** `SPRINT_20260219_007_FE_ui_v2_shell_qa_and_readiness_reverification`
|
||||||
|
**Date:** 2026-02-19
|
||||||
|
**Owner:** Project Manager, QA lead
|
||||||
|
**Status:** PASS (frontend shell structure + backend contract dependency closure + UI endpoint binding)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Scope Reverification Summary
|
||||||
|
|
||||||
|
Frontend shell restructuring is implemented for the canonical seven domains and verified against reopened sprint requirements:
|
||||||
|
|
||||||
|
- Dashboard
|
||||||
|
- Release Control
|
||||||
|
- Security and Risk
|
||||||
|
- Evidence and Audit
|
||||||
|
- Integrations
|
||||||
|
- Platform Ops
|
||||||
|
- Administration
|
||||||
|
|
||||||
|
Implemented shell evidence (non-exhaustive):
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/app.routes.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/release-control.routes.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/evidence-audit.routes.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/platform-ops.routes.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/routes/administration.routes.ts`
|
||||||
|
|
||||||
|
API binding evidence for previously blocked contract rows:
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/bundles/bundle-organizer.api.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/bundles/bundle-catalog.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/bundles/bundle-detail.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/bundles/bundle-builder.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/bundles/bundle-version-detail.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/advisory-sources.api.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/security-risk/advisory-sources.component.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. QA Evidence (Strict Suites)
|
||||||
|
|
||||||
|
### 2.1 Unit and Structural Route Coverage
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run test -- --watch=false --include src/tests/navigation/nav-route-integrity.spec.ts --include src/tests/navigation/nav-model.spec.ts --include src/tests/navigation/legacy-redirects.spec.ts --include src/tests/release-control/release-control-routes.spec.ts --include src/tests/release-control/release-control-setup.component.spec.ts --include src/tests/release-control/release-control-structure.component.spec.ts --include src/tests/security-risk/security-risk-routes.spec.ts --include src/tests/security-risk/advisory-sources.component.spec.ts --include src/tests/evidence-audit/evidence-audit-routes.spec.ts --include src/tests/evidence-audit/evidence-audit-overview.component.spec.ts --include src/tests/platform-ops/platform-ops-routes.spec.ts --include src/tests/administration/administration-routes.spec.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
- 12 files passed
|
||||||
|
- 167 tests passed
|
||||||
|
- 0 failed
|
||||||
|
|
||||||
|
### 2.2 E2E Shell Reverification
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test tests/e2e/nav-shell.spec.ts tests/e2e/critical-path.spec.ts tests/e2e/ia-v2-a11y-regression.spec.ts --workers=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
- 33 tests passed
|
||||||
|
- 0 failed
|
||||||
|
|
||||||
|
Suites covered:
|
||||||
|
|
||||||
|
- canonical nav shell and redirect behavior
|
||||||
|
- cross-domain critical flows
|
||||||
|
- IA v2 accessibility/regression checks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Contract Ledger Reconciliation (QA7-04 + BE8-06)
|
||||||
|
|
||||||
|
Source ledger: `docs/modules/ui/v2-rewire/S00_endpoint_contract_ledger_v1.md`
|
||||||
|
|
||||||
|
Previously blocked backend dependency rows are now implemented and reconciled:
|
||||||
|
|
||||||
|
1. `S00-T05-RC-01` (Bundle catalog/detail/builder endpoint family)
|
||||||
|
- Reclassified from `MISSING_NEW` -> `EXISTS_COMPAT`.
|
||||||
|
- Implemented route family:
|
||||||
|
- `GET /api/v1/release-control/bundles`
|
||||||
|
- `GET /api/v1/release-control/bundles/{bundleId}`
|
||||||
|
- `GET /api/v1/release-control/bundles/{bundleId}/versions`
|
||||||
|
- `GET /api/v1/release-control/bundles/{bundleId}/versions/{versionId}`
|
||||||
|
- `POST /api/v1/release-control/bundles`
|
||||||
|
- `POST /api/v1/release-control/bundles/{bundleId}/versions`
|
||||||
|
- `POST /api/v1/release-control/bundles/{bundleId}/versions/{versionId}/materialize`
|
||||||
|
- Persistence implemented by migration:
|
||||||
|
- `src/Platform/__Libraries/StellaOps.Platform.Database/Migrations/Release/045_ReleaseControlBundleLifecycle.sql`
|
||||||
|
|
||||||
|
2. `S00-T05-SEC-02` (Advisory Sources aggregate endpoint family)
|
||||||
|
- Reclassified from `MISSING_NEW` -> `EXISTS_COMPAT`.
|
||||||
|
- Implemented Concelier freshness routes:
|
||||||
|
- `GET /api/v1/advisory-sources`
|
||||||
|
- `GET /api/v1/advisory-sources/summary`
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/freshness`
|
||||||
|
- Implemented Policy impact/conflict routes:
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/impact`
|
||||||
|
- `GET /api/v1/advisory-sources/{id}/conflicts`
|
||||||
|
- Persistence implemented by migrations:
|
||||||
|
- `src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/004_add_advisory_source_freshness_projection.sql`
|
||||||
|
- `src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/005_add_advisory_source_signature_projection.sql`
|
||||||
|
- `src/Policy/__Libraries/StellaOps.Policy.Persistence/Migrations/005_advisory_source_projection.sql`
|
||||||
|
- Advisory detail diagnostics now include backend contract fields for total/signed/unsigned/signature-failure counts.
|
||||||
|
|
||||||
|
Reconciled truth:
|
||||||
|
|
||||||
|
- Frontend shell conformance: PASS.
|
||||||
|
- Backend dependency closure for UI shell contracts (`S00-T05-RC-01`, `S00-T05-SEC-02`): PASS.
|
||||||
|
- Frontend endpoint-consumption closure for `S00-T05-RC-01` and `S00-T05-SEC-02`: PASS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Decision
|
||||||
|
|
||||||
|
### Readiness outcome
|
||||||
|
|
||||||
|
- Frontend shell gate (sprints 002-006 scope): **PASS**.
|
||||||
|
- Backend dependency gate for full pack closure (`S00-T05-RC-01`, `S00-T05-SEC-02`): **PASS**.
|
||||||
|
|
||||||
|
### Verification evidence (backend dependency closure)
|
||||||
|
|
||||||
|
- `dotnet test src/Platform/__Tests/StellaOps.Platform.WebService.Tests/StellaOps.Platform.WebService.Tests.csproj -v minimal` -> Passed 115/115 (MTP full project run)
|
||||||
|
- `dotnet test src/Policy/__Tests/StellaOps.Policy.Gateway.Tests/StellaOps.Policy.Gateway.Tests.csproj -v minimal` -> Passed 131/131 (MTP full project run)
|
||||||
|
- `src/Platform/__Tests/StellaOps.Platform.WebService.Tests/bin/Debug/net10.0/StellaOps.Platform.WebService.Tests.exe -class "StellaOps.Platform.WebService.Tests.ReleaseControlEndpointsTests"` -> Passed 3/3
|
||||||
|
- `src/Policy/__Tests/StellaOps.Policy.Gateway.Tests/bin/Debug/net10.0/StellaOps.Policy.Gateway.Tests.exe -class "StellaOps.Policy.Gateway.Tests.AdvisorySourceEndpointsTests"` -> Passed 5/5
|
||||||
|
- `src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/bin/Debug/net10.0/StellaOps.Concelier.WebService.Tests.exe -class "StellaOps.Concelier.WebService.Tests.AdvisorySourceEndpointsTests"` -> Passed 5/5
|
||||||
|
- Note: `dotnet test --filter` remains non-deterministic in this repo under Microsoft Testing Platform (`MTP0001`), so targeted class evidence uses xUnit in-proc runner executables.
|
||||||
|
- `npm run test -- --watch=false --include src/tests/release-control/release-control-structure.component.spec.ts --include src/tests/security-risk/advisory-sources.component.spec.ts` -> Passed 11/11
|
||||||
|
- `npm run build` -> Passed (with existing bundle-size/commonjs warnings unrelated to these endpoint bindings)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Sprint Archival Decision
|
||||||
|
|
||||||
|
Backend dependency blockers tracked by this package are cleared.
|
||||||
|
|
||||||
|
Archival for reopened UI sprints can proceed once sprint owners confirm remaining non-endpoint risks (if any) are closed and statuses are updated in their sprint trackers.
|
||||||
|
|
||||||
|
- backend contract blockers are implemented (completed here),
|
||||||
|
- ledger reconciliation remains current with implementation state,
|
||||||
|
- sprint trackers carry explicit QA/closure evidence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Addendum - Promotions Contract Binding (Sprint 015)
|
||||||
|
|
||||||
|
Follow-on sprint `SPRINT_20260219_015_FE_ui_v2_shell_release_control_promotions_pack13_contract_binding` completed pack-13 promotions contract binding work that remained after structural closure.
|
||||||
|
|
||||||
|
Implemented frontend evidence:
|
||||||
|
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/promotions/promotions-list.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/promotions/promotion-detail.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/app/features/promotions/create-promotion.component.ts`
|
||||||
|
- `src/Web/StellaOps.Web/src/tests/release-control/release-control-structure.component.spec.ts`
|
||||||
|
|
||||||
|
Validation evidence:
|
||||||
|
|
||||||
|
- `npm run test -- --watch=false --include src/tests/release-control/release-control-structure.component.spec.ts --include src/tests/release-control/release-control-routes.spec.ts` -> Passed 33/33.
|
||||||
|
- `npm run build` -> Passed (existing bundle-size/commonjs warnings unchanged).
|
||||||
|
|
||||||
|
Ledger impact:
|
||||||
|
|
||||||
|
- `S00-T05-RC-02` and `S00-T05-ADM-01` are now `EXISTS_COMPAT` after backend contract enrichment in sprint `20260219_016` (release-control derived-signal contracts + administration A0-A7 adapter routes).
|
||||||
|
- Trust-owner mutation routes (`/api/v1/administration/trust-signing/{keys,issuers,certificates,transparency-log}`) are now shipped with `platform.trust.write` / `platform.trust.admin` mapping and DB backing via `046_TrustSigningAdministration.sql`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Post-Readiness Verification and Archival Update
|
||||||
|
|
||||||
|
Additional verification was executed after reading all `docs/modules/ui/v2-rewire/pack-01.md` through `pack-21.md` to account for higher-pack overrides.
|
||||||
|
|
||||||
|
Updated Playwright evidence:
|
||||||
|
|
||||||
|
- `npx playwright test tests/e2e/nav-shell.spec.ts tests/e2e/critical-path.spec.ts tests/e2e/ia-v2-a11y-regression.spec.ts --workers=1` -> Passed 33/33.
|
||||||
|
- Deterministic advisory-source API fixtures were added to `tests/e2e/critical-path.spec.ts` so ownership-split assertions are validated against stable data.
|
||||||
|
|
||||||
|
Archival update:
|
||||||
|
|
||||||
|
- Completed sprint files were moved from `docs/implplan/` to `docs-archived/implplan/`.
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
This document is the **authoritative source** for all competitive positioning claims made by StellaOps. All marketing materials, sales collateral, and documentation must reference claims from this index to ensure accuracy and consistency.
|
This document is the **authoritative source** for all competitive positioning claims made by StellaOps. All marketing materials, sales collateral, and documentation must reference claims from this index to ensure accuracy and consistency.
|
||||||
|
|
||||||
**Last Updated:** 2025-12-20
|
**Last Updated:** 2026-02-19
|
||||||
**Next Review:** 2026-03-20
|
**Next Review:** 2026-05-19
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -28,6 +28,8 @@ This document is the **authoritative source** for all competitive positioning cl
|
|||||||
| REACH-002 | "Signed reachability graphs with DSSE attestation" | `src/Attestor/` module; DSSE envelope implementation | High | 2025-12-14 | 2026-03-14 |
|
| REACH-002 | "Signed reachability graphs with DSSE attestation" | `src/Attestor/` module; DSSE envelope implementation | High | 2025-12-14 | 2026-03-14 |
|
||||||
| REACH-003 | "~85% of critical vulnerabilities in containers are in inactive code" | Sysdig 2024 Container Security Report (external) | Medium | 2025-11-01 | 2026-02-01 |
|
| REACH-003 | "~85% of critical vulnerabilities in containers are in inactive code" | Sysdig 2024 Container Security Report (external) | Medium | 2025-11-01 | 2026-02-01 |
|
||||||
| REACH-004 | "Multi-language support: Java, C#, Go, JavaScript, TypeScript, Python" | Language analyzer implementations in `src/Scanner/Analyzers/` | High | 2025-12-14 | 2026-03-14 |
|
| REACH-004 | "Multi-language support: Java, C#, Go, JavaScript, TypeScript, Python" | Language analyzer implementations in `src/Scanner/Analyzers/` | High | 2025-12-14 | 2026-03-14 |
|
||||||
|
| REACH-005 | "Symbolized call-stack proofs with demangled names, build-ID binding, and source file references" | `src/Symbols/` module; `src/Scanner/__Libraries/StellaOps.Scanner.Symbols.Native/`; Symbol Manifest v1 spec | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
| REACH-006 | "OCI-attached symbol packs as first-class referrer artifacts" | Symbol manifest OCI artifact type `application/vnd.stella.symbols.manifest.v1+json`; `src/Symbols/` server REST API | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
|
||||||
### 3. VEX & Lattice Claims
|
### 3. VEX & Lattice Claims
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ This document is the **authoritative source** for all competitive positioning cl
|
|||||||
| ATT-002 | "Optional Sigstore Rekor transparency logging" | `src/Attestor/StellaOps.Attestor.Rekor/` integration | High | 2025-12-14 | 2026-03-14 |
|
| ATT-002 | "Optional Sigstore Rekor transparency logging" | `src/Attestor/StellaOps.Attestor.Rekor/` integration | High | 2025-12-14 | 2026-03-14 |
|
||||||
| ATT-003 | "in-toto attestation format support" | in-toto predicates in attestation module | High | 2025-12-14 | 2026-03-14 |
|
| ATT-003 | "in-toto attestation format support" | in-toto predicates in attestation module | High | 2025-12-14 | 2026-03-14 |
|
||||||
| ATT-004 | "Regional crypto support: eIDAS, FIPS, GOST, SM" | `StellaOps.Cryptography` with plugin architecture | Medium | 2025-12-14 | 2026-03-14 |
|
| ATT-004 | "Regional crypto support: eIDAS, FIPS, GOST, SM" | `StellaOps.Cryptography` with plugin architecture | Medium | 2025-12-14 | 2026-03-14 |
|
||||||
|
| ATT-005 | "Size-aware Rekor pointer strategy: hash pointer in transparency log, full payload in Evidence Locker CAS" | `src/Attestor/` detached payload references; `src/EvidenceLocker/` CAS storage; Rekor v2 submission with hash pre-check | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
|
||||||
### 4a. Proof & Evidence Chain Claims
|
### 4a. Proof & Evidence Chain Claims
|
||||||
|
|
||||||
@@ -117,6 +120,26 @@ This document is the **authoritative source** for all competitive positioning cl
|
|||||||
| COMP-SNYK-002 | "Snyk's reachability is limited to specific languages" | Snyk documentation review | Medium | 2025-12-14 | 2026-03-14 |
|
| COMP-SNYK-002 | "Snyk's reachability is limited to specific languages" | Snyk documentation review | Medium | 2025-12-14 | 2026-03-14 |
|
||||||
| COMP-SNYK-003 | "Snyk lacks offline/air-gap capability" | Snyk architecture documentation | High | 2025-12-14 | 2026-03-14 |
|
| COMP-SNYK-003 | "Snyk lacks offline/air-gap capability" | Snyk architecture documentation | High | 2025-12-14 | 2026-03-14 |
|
||||||
|
|
||||||
|
### vs. Docker Scout
|
||||||
|
|
||||||
|
| ID | Claim | Evidence | Confidence | Verified | Next Review |
|
||||||
|
|----|-------|----------|------------|----------|-------------|
|
||||||
|
| COMP-SCOUT-001 | "Docker Scout produces SBOM/VEX/provenance attestations via cosign but lacks symbolized call-stack proofs, deterministic replay, and lattice VEX reasoning" | Docker Scout documentation (docs.docker.com/scout); DHI surface analysis | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
| COMP-SCOUT-002 | "Docker Scout does not address Rekor payload size constraints or provide size-aware pointer strategies" | Docker Scout attestation flow analysis; Rekor public instance constraints | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
|
||||||
|
### vs. JFrog (Xray + Evidence Collection)
|
||||||
|
|
||||||
|
| ID | Claim | Evidence | Confidence | Verified | Next Review |
|
||||||
|
|----|-------|----------|------------|----------|-------------|
|
||||||
|
| COMP-JFROG-001 | "JFrog Evidence Collection centralizes signed evidence across SDLC but lacks deterministic scoring envelopes, replayable verdicts, and formal VEX lattice reasoning" | JFrog Evidence documentation (jfrog.com/evidence); solution sheet analysis | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
| COMP-JFROG-002 | "JFrog lacks signed reachability graphs and call-stack symbolization; evidence is SBOM/provenance-level, not function-level" | JFrog Xray feature matrix; Evidence Collection solution sheet | High | 2026-02-19 | 2026-05-19 |
|
||||||
|
|
||||||
|
### vs. Oligo Security
|
||||||
|
|
||||||
|
| ID | Claim | Evidence | Confidence | Verified | Next Review |
|
||||||
|
|----|-------|----------|------------|----------|-------------|
|
||||||
|
| COMP-OLIGO-001 | "Oligo Security provides runtime call-stack exploitability evidence but lacks SBOM/VEX integration, deterministic replay, lattice VEX reasoning, signed reachability graphs, and offline/air-gap capability" | Oligo Security blog post on call-stack evidence; product positioning as runtime-only tool | Medium | 2026-02-19 | 2026-05-19 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Confidence Levels
|
## Confidence Levels
|
||||||
@@ -209,6 +232,10 @@ When a claim becomes false (e.g., competitor adds feature):
|
|||||||
| 2025-12-20 | Added DET-004 (content-addressed proof bundles) | Agent |
|
| 2025-12-20 | Added DET-004 (content-addressed proof bundles) | Agent |
|
||||||
| 2025-12-20 | Added PROOF-001/002/003 (deterministic proof ledgers, proof chains, score replay) | Agent |
|
| 2025-12-20 | Added PROOF-001/002/003 (deterministic proof ledgers, proof chains, score replay) | Agent |
|
||||||
| 2025-12-20 | Added UNKNOWNS-001/002/003 (two-factor ranking, band prioritization, competitor gap) | Agent |
|
| 2025-12-20 | Added UNKNOWNS-001/002/003 (two-factor ranking, band prioritization, competitor gap) | Agent |
|
||||||
|
| 2026-02-19 | Added REACH-005/006 (symbolized call-stacks, OCI symbol packs) from competitive advisory review | Product Manager |
|
||||||
|
| 2026-02-19 | Added ATT-005 (Rekor size-aware pointer strategy) from competitive advisory review | Product Manager |
|
||||||
|
| 2026-02-19 | Added COMP-SCOUT-001/002 (Docker Scout gaps) and COMP-JFROG-001/002 (JFrog gaps) from competitive advisory review | Product Manager |
|
||||||
|
| 2026-02-19 | Added COMP-OLIGO-001 (Oligo Security runtime-only gaps) from VEX/call-stack/determinism competitive advisory | Product Manager |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Source: internal advisories "23-Nov-2025 - Stella Ops vs Competitors" and "09-Ja
|
|||||||
| **CI/CD Tools** | GitHub Actions, Jenkins, GitLab CI | Running pipelines, build automation | No central release authority; no audit-grade evidence; deployment is afterthought |
|
| **CI/CD Tools** | GitHub Actions, Jenkins, GitLab CI | Running pipelines, build automation | No central release authority; no audit-grade evidence; deployment is afterthought |
|
||||||
| **CD Orchestrators** | Octopus, Harness, Spinnaker | Deployment automation, Kubernetes | Security is bolt-on; non-K8s is second-class; pricing punishes automation |
|
| **CD Orchestrators** | Octopus, Harness, Spinnaker | Deployment automation, Kubernetes | Security is bolt-on; non-K8s is second-class; pricing punishes automation |
|
||||||
| **Registries** | Harbor, JFrog Artifactory | Artifact storage, scanning | No release governance; no promotion workflows; no deployment execution |
|
| **Registries** | Harbor, JFrog Artifactory | Artifact storage, scanning | No release governance; no promotion workflows; no deployment execution |
|
||||||
| **Scanners/CNAPP** | Trivy, Snyk, Aqua | Vulnerability detection | No release orchestration; findings don't integrate with promotion gates |
|
| **Scanners/CNAPP** | Trivy, Snyk, Aqua (incl. VEX Hub) | Vulnerability detection; centralized VEX consumption (Aqua VEX Hub) | No release orchestration; findings don't integrate with promotion gates; VEX Hub reduces noise but lacks lattice logic and provenance |
|
||||||
|
|
||||||
### Stella Ops Suite Positioning
|
### Stella Ops Suite Positioning
|
||||||
|
|
||||||
@@ -100,9 +100,9 @@ These comparisons focus on where release governance, evidence export, and audit
|
|||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| **Last Updated** | 2026-01-03 |
|
| **Last Updated** | 2026-02-19 |
|
||||||
| **Last Verified** | 2025-12-14 |
|
| **Last Verified** | 2026-02-19 |
|
||||||
| **Next Review** | 2026-03-14 |
|
| **Next Review** | 2026-05-19 |
|
||||||
| **Claims Index** | [`docs/product/claims-citation-index.md`](claims-citation-index.md) |
|
| **Claims Index** | [`docs/product/claims-citation-index.md`](claims-citation-index.md) |
|
||||||
| **Verification Method** | Source code audit (OSS), documentation review, feature testing |
|
| **Verification Method** | Source code audit (OSS), documentation review, feature testing |
|
||||||
|
|
||||||
@@ -123,6 +123,8 @@ The scanner market evolved from three distinct origins. Each origin created arch
|
|||||||
| **Developer UX** | Snyk | IDE integration, fix PRs, onboarding | SaaS-only (offline impossible); no attestation infrastructure; reachability limited to specific languages |
|
| **Developer UX** | Snyk | IDE integration, fix PRs, onboarding | SaaS-only (offline impossible); no attestation infrastructure; reachability limited to specific languages |
|
||||||
| **Policy/Compliance** | Prisma Cloud, Aqua | Runtime protection, CNAPP breadth | No deterministic replay; no cryptographic provenance for verdicts; no semantic diff |
|
| **Policy/Compliance** | Prisma Cloud, Aqua | Runtime protection, CNAPP breadth | No deterministic replay; no cryptographic provenance for verdicts; no semantic diff |
|
||||||
| **SBOM Operations** | Anchore | SBOM storage, lifecycle | No lattice VEX reasoning; no signed reachability graphs; no regional crypto profiles |
|
| **SBOM Operations** | Anchore | SBOM storage, lifecycle | No lattice VEX reasoning; no signed reachability graphs; no regional crypto profiles |
|
||||||
|
| **Supply Chain Evidence** | Docker Scout, JFrog | SBOM/VEX/provenance attestations; signed evidence collection | Evidence is SBOM/VEX/provenance-level; no symbolized call-stack proofs; no deterministic signed scoring envelopes; no replayable micro-witnesses; no size-aware Rekor pointer strategy; no formal VEX lattice reasoning |
|
||||||
|
| **Runtime Exploitability** | Oligo Security | Runtime call-stack evidence showing where vulns actually execute | Runtime-only; not an SBOM/VEX integrator; no deterministic replay; no lattice VEX; no offline/air-gap; no signed reachability graphs; single signal source vs. Stella's three-layer fusion |
|
||||||
|
|
||||||
### The Core Problem
|
### The Core Problem
|
||||||
|
|
||||||
@@ -195,13 +197,17 @@ This isn't a feature gap—it's a category difference. Retrofitting it requires:
|
|||||||
| **No signed reachability** | Reachability claims are assertions, not proofs. There's no cryptographic binding between "this CVE is reachable" and the call path that proves it. | COMP-GRYPE-001, REACH-002 | 2025-12-14 |
|
| **No signed reachability** | Reachability claims are assertions, not proofs. There's no cryptographic binding between "this CVE is reachable" and the call path that proves it. | COMP-GRYPE-001, REACH-002 | 2025-12-14 |
|
||||||
| **No semantic diff** | Tools report "+3 CVEs" without context. They can't say "exploitable surface decreased despite new CVEs" because they don't track reachability deltas. | — | 2025-12-14 |
|
| **No semantic diff** | Tools report "+3 CVEs" without context. They can't say "exploitable surface decreased despite new CVEs" because they don't track reachability deltas. | — | 2025-12-14 |
|
||||||
| **Offline/sovereign gaps** | Snyk is SaaS-only. Others have partial offline support but no regional crypto (GOST, SM2, eIDAS) and no sealed knowledge snapshots for air-gapped reproducibility. | COMP-SNYK-003, ATT-004 | 2025-12-14 |
|
| **Offline/sovereign gaps** | Snyk is SaaS-only. Others have partial offline support but no regional crypto (GOST, SM2, eIDAS) and no sealed knowledge snapshots for air-gapped reproducibility. | COMP-SNYK-003, ATT-004 | 2025-12-14 |
|
||||||
|
| **No symbolized call-stack proofs** | Docker Scout, Trivy, and JFrog produce SBOM/VEX/provenance attestations but none deliver symbolized, replayable call-stack evidence tied to the shipping binary. Stella provides DSSE-signed reachability graphs with demangled symbols, build-ID binding, and edge-bundle attestations. | REACH-002, REACH-005, REACH-006 | 2026-02-19 |
|
||||||
|
| **No deterministic signed scoring** | JFrog centralizes signed evidence but doesn't produce deterministic scoring envelopes that can be re-computed. Stella's Policy Engine produces seeded, deterministic verdicts signed via DSSE with full intermediate state for byte-for-byte replay. | DET-001, DET-004, PROOF-003 | 2026-02-19 |
|
||||||
|
| **No Rekor size-aware strategy** | Public Rekor has ~100 KB upload limits. Docker Scout and Trivy submit attestations without addressing this. Stella uses hash-pointer-in-Rekor + full-payload-in-vault (Evidence Locker CAS) with detached payload references, solving the size/UX gap. | ATT-005 | 2026-02-19 |
|
||||||
|
|
||||||
## Snapshot table (condensed)
|
## Snapshot table (condensed)
|
||||||
|
|
||||||
| Vendor | SBOM Gen | SBOM Ingest | Attest (DSSE) | Rekor | Offline | Primary gaps vs Stella | Related Claims |
|
| Vendor | SBOM Gen | SBOM Ingest | Attest (DSSE) | Rekor | Offline | Primary gaps vs Stella | Related Claims |
|
||||||
|--------|----------|-------------|---------------|-------|---------|------------------------|----------------|
|
|--------|----------|-------------|---------------|-------|---------|------------------------|----------------|
|
||||||
| Trivy | Yes | Yes | Cosign | Query | Strong | No replay, no lattice | COMP-TRIVY-001, COMP-TRIVY-002, COMP-TRIVY-003 |
|
| Trivy | Yes | Yes | Cosign | Query | Strong | No replay, no lattice, no symbolized call-stacks | COMP-TRIVY-001, COMP-TRIVY-002, COMP-TRIVY-003 |
|
||||||
| Syft/Grype | Yes | Yes | Cosign-only | Indir | Medium | No replay, no lattice | COMP-GRYPE-001, COMP-GRYPE-002, COMP-GRYPE-003 |
|
| Syft/Grype | Yes | Yes | Cosign-only | Indir | Medium | No replay, no lattice | COMP-GRYPE-001, COMP-GRYPE-002, COMP-GRYPE-003 |
|
||||||
|
| Docker Scout | Yes | Yes | Cosign (DHI) | Query | Medium | SBOM/VEX/provenance attestations via cosign, but no signed reachability, no deterministic replay, no call-stack symbolization, no lattice VEX | COMP-SCOUT-001, COMP-SCOUT-002 |
|
||||||
| Snyk | Yes | Limited | No | No | Weak | No attest/VEX/replay | COMP-SNYK-001, COMP-SNYK-002, COMP-SNYK-003 |
|
| Snyk | Yes | Limited | No | No | Weak | No attest/VEX/replay | COMP-SNYK-001, COMP-SNYK-002, COMP-SNYK-003 |
|
||||||
| Prisma | Yes | Limited | No | No | Strong | No attest/replay | — |
|
| Prisma | Yes | Limited | No | No | Strong | No attest/replay | — |
|
||||||
| AWS (Inspector/Signer) | Partial | Partial | Notary v2 | No | Weak | Closed, no replay | — |
|
| AWS (Inspector/Signer) | Partial | Partial | Notary v2 | No | Weak | Closed, no replay | — |
|
||||||
@@ -210,10 +216,11 @@ This isn't a feature gap—it's a category difference. Retrofitting it requires:
|
|||||||
| GitLab | Yes | Limited | Partial | No | Medium | No replay/lattice | — |
|
| GitLab | Yes | Limited | Partial | No | Medium | No replay/lattice | — |
|
||||||
| Microsoft Defender | Partial | Partial | No | No | Weak | No attest/reachability | — |
|
| Microsoft Defender | Partial | Partial | No | No | Weak | No attest/reachability | — |
|
||||||
| Anchore Enterprise | Yes | Yes | Some | No | Good | No sovereign crypto | — |
|
| Anchore Enterprise | Yes | Yes | Some | No | Good | No sovereign crypto | — |
|
||||||
| JFrog Xray | Yes | Yes | No | No | Medium | No attest/lattice | — |
|
| JFrog (Xray + Evidence) | Yes | Yes | Signed evidence | No | Medium | Centralized signed evidence collection but no deterministic replay, no lattice VEX, no signed reachability graphs, no call-stack replay | COMP-JFROG-001, COMP-JFROG-002 |
|
||||||
| Tenable | Partial | Limited | No | No | Weak | Not SBOM/VEX-focused | — |
|
| Tenable | Partial | Limited | No | No | Weak | Not SBOM/VEX-focused | — |
|
||||||
| Qualys | Limited | Limited | No | No | Medium | No attest/lattice | — |
|
| Qualys | Limited | Limited | No | No | Medium | No attest/lattice | — |
|
||||||
| Rezilion | Yes | Yes | No | No | Medium | Runtime-only; no DSSE | — |
|
| Rezilion | Yes | Yes | No | No | Medium | Runtime-only; no DSSE | — |
|
||||||
|
| Oligo Security | No | No | No | No | No | Runtime call-stack evidence only; no SBOM/VEX/attestation integration; no replay | COMP-OLIGO-001 |
|
||||||
| Chainguard | Yes | Yes | Yes | Yes | Medium | No replay/lattice | — |
|
| Chainguard | Yes | Yes | Yes | Yes | Medium | No replay/lattice | — |
|
||||||
|
|
||||||
## How to use this doc
|
## How to use this doc
|
||||||
@@ -281,4 +288,5 @@ This isn't a feature gap—it's a category difference. Retrofitting it requires:
|
|||||||
|
|
||||||
## Sources
|
## Sources
|
||||||
- Full advisory: `docs/product/advisories/23-Nov-2025 - Stella Ops vs Competitors.md`
|
- Full advisory: `docs/product/advisories/23-Nov-2025 - Stella Ops vs Competitors.md`
|
||||||
|
- VEX/call-stack/determinism advisory (archived, no gaps): `docs-archived/product/advisories/2026-02-19-vex-callstack-determinism-competitive-landscape.md`
|
||||||
- Claims Citation Index: `docs/product/claims-citation-index.md`
|
- Claims Citation Index: `docs/product/claims-citation-index.md`
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ Use these in sales conversations, marketing materials, and internal alignment.
|
|||||||
| Trust scoring of VEX sources | P1 | 4500_0001_0002 |
|
| Trust scoring of VEX sources | P1 | 4500_0001_0002 |
|
||||||
| Tier 4 binary fingerprinting | P1 | 7204-7206 |
|
| Tier 4 binary fingerprinting | P1 | 7204-7206 |
|
||||||
| SBOM historical lineage | P2 | 4600_0001_* |
|
| SBOM historical lineage | P2 | 4600_0001_* |
|
||||||
|
| Signed execution evidence (trace-to-DSSE) | P2 | 20260219_013 |
|
||||||
|
| Runtime beacon attestations | P3 | 20260219_014 |
|
||||||
|
| Symbol/Debug Pack Marketplace | P1 | 20260220_001-003 |
|
||||||
|
| Privacy-Preserving Federated Telemetry | P1 | 20260220_005-009 |
|
||||||
|
| Developer-Facing Remediation Marketplace | P1 | 20260220_010-015 |
|
||||||
|
|
||||||
## Competitor Positioning
|
## Competitor Positioning
|
||||||
|
|
||||||
@@ -96,6 +101,9 @@ Use these in sales conversations, marketing materials, and internal alignment.
|
|||||||
| **Prisma Cloud** | CNAPP breadth, graph investigation | Platform completeness | Decision integrity, deterministic replay, semantic diff |
|
| **Prisma Cloud** | CNAPP breadth, graph investigation | Platform completeness | Decision integrity, deterministic replay, semantic diff |
|
||||||
| **Anchore** | SBOM operations maturity | SBOM storage | Lattice VEX, signed reachability, proof chains |
|
| **Anchore** | SBOM operations maturity | SBOM storage | Lattice VEX, signed reachability, proof chains |
|
||||||
| **Aqua/Trivy** | Runtime protection, broad coverage | Ecosystem breadth | Forensic reproducibility, K4 logic, regional crypto |
|
| **Aqua/Trivy** | Runtime protection, broad coverage | Ecosystem breadth | Forensic reproducibility, K4 logic, regional crypto |
|
||||||
|
| **Docker Scout** | DHI integration, SBOM/VEX/provenance attestations via cosign | Registry-native UX | Symbolized call-stack proofs, deterministic replay, lattice VEX, Rekor size-aware pointer strategy |
|
||||||
|
| **JFrog** | Evidence Collection centralizing signed SDLC evidence | Artifact management breadth | Deterministic scoring envelopes, function-level reachability proofs, replayable verdicts, formal VEX reasoning |
|
||||||
|
| **Oligo Security** | Runtime call-stack exploitability proofs | Runtime-only depth | Three-layer fusion (static+binary+runtime), SBOM/VEX integration, deterministic replay, offline/air-gap, signed graphs |
|
||||||
|
|
||||||
### Our Winning Positions
|
### Our Winning Positions
|
||||||
|
|
||||||
@@ -108,11 +116,16 @@ Use these in sales conversations, marketing materials, and internal alignment.
|
|||||||
|
|
||||||
### Where We're Ahead
|
### Where We're Ahead
|
||||||
|
|
||||||
1. **VEX decisioning** — K4 lattice with conflict detection; no competitor has this
|
1. **VEX decisioning** — K4 lattice with conflict detection; no competitor has this (including Docker Scout, JFrog)
|
||||||
2. **Smart-Diff** — Semantic risk deltas with priority scoring; unique
|
2. **Smart-Diff** — Semantic risk deltas with priority scoring; unique
|
||||||
3. **Signed reachability** — DSSE graphs + edge bundles; unique
|
3. **Signed reachability** — DSSE graphs + edge bundles; unique. Docker Scout/JFrog/Trivy stop at SBOM/VEX/provenance attestations
|
||||||
4. **Deterministic replay** — Bit-for-bit reproducibility; unique
|
4. **Deterministic replay** — Bit-for-bit reproducibility; unique. JFrog Evidence Collection centralizes evidence but can't replay verdicts
|
||||||
5. **Regional crypto** — FIPS/eIDAS/GOST/SM/PQC; unique
|
5. **Regional crypto** — FIPS/eIDAS/GOST/SM/PQC; unique
|
||||||
|
6. **Symbolized call-stack proofs + Symbol Marketplace** — Demangled symbols, build-ID binding, OCI symbol packs as first-class referrer artifacts; no competitor has function-level symbol evidence. The Symbol Marketplace adds source trust scoring (freshness/signature/coverage/SLA), browsable catalog with DSSE-verified install, and multi-provider federation (Microsoft Symbols, debuginfod distros, partner feeds)
|
||||||
|
7. **Privacy-Preserving Federated Telemetry** — Differential privacy (Laplacian noise, epsilon budget) + k-anonymity over federated runtime signals with DSSE-signed consent proofs; no competitor has privacy-safe cross-site exploit intelligence sharing. Network-effect moat.
|
||||||
|
8. **Developer-Facing Remediation Marketplace** — Signed-PR fix attestations verified against reachability proof deltas with contributor trust scoring; no competitor has PR-level fix verification tied to reachability evidence. Six-module integration depth.
|
||||||
|
9. **Rekor size-aware pointer strategy** — Hash pointer in transparency log + full payload in vault; addresses real Rekor ~100KB upload constraints that competitors ignore
|
||||||
|
10. **Deterministic signed scoring envelopes** — Seeded, replayable score computation with DSSE-signed intermediates; competitors sign evidence but not deterministic scoring traces
|
||||||
|
|
||||||
### Where Competitors Lead (For Now)
|
### Where Competitors Lead (For Now)
|
||||||
|
|
||||||
@@ -159,4 +172,4 @@ stella scan --offline --image <digest>
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-01-03
|
**Last Updated**: 2026-02-19
|
||||||
|
|||||||
306
docs/qa/issues-report-2026-02-19.md
Normal file
306
docs/qa/issues-report-2026-02-19.md
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
# Stella Ops — QA Issues Report
|
||||||
|
**Date:** 2026-02-19
|
||||||
|
**Tester:** Claude Code (Playwright automated walkthrough)
|
||||||
|
**Stack:** Fresh `docker compose up` from `devops/compose/docker-compose.stella-ops.yml`
|
||||||
|
**Auth:** `admin` / default credentials
|
||||||
|
**Base URL:** `https://stella-ops.local/`
|
||||||
|
**Build:** v1.0.0 (as shown in sidebar footer)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| Severity | Count |
|
||||||
|
|----------|-------|
|
||||||
|
| 🔴 Critical | 1 |
|
||||||
|
| 🟠 High | 4 |
|
||||||
|
| 🟡 Medium | 7 |
|
||||||
|
| 🔵 Low | 6 |
|
||||||
|
| **Total** | **18** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 Critical
|
||||||
|
|
||||||
|
### ISSUE-001 — All v2 navigation routes redirect to home (`/`)
|
||||||
|
**Pages:** `/release-control/*`, `/security-risk/*`, `/evidence-audit/*`, `/platform-ops/*`, `/administration/*`, `/dashboard`
|
||||||
|
**Reproduction:** Navigate to any of the 22+ new v2 IA routes introduced in SPRINT_20260218_006–016.
|
||||||
|
**Observed:** Every route silently redirects to `/` (Control Plane dashboard). No 404, no error — just home.
|
||||||
|
**Expected:** Each route renders its designated v2 component.
|
||||||
|
**Impact:** The entire v2 information architecture (Release Control, Security & Risk, Evidence & Audit, Platform Ops, Administration, Dashboard v3) is inaccessible. Only the old v1 routes work.
|
||||||
|
**Notes:** This is the primary blocker for SPRINT_20260218 sprint delivery. The new sidebar components exist in source but the routes are not wired to the deployed build. The `/integrations` route is the only v2-era route that partially works.
|
||||||
|
**Affected routes tested:**
|
||||||
|
```
|
||||||
|
/release-control → / (Control Plane)
|
||||||
|
/release-control/releases → /
|
||||||
|
/release-control/approvals → /
|
||||||
|
/release-control/environments→ /
|
||||||
|
/release-control/bundles → /
|
||||||
|
/release-control/promotions → /
|
||||||
|
/release-control/runs → /
|
||||||
|
/security-risk → /
|
||||||
|
/security-risk/findings → /
|
||||||
|
/security-risk/advisory-sources → /
|
||||||
|
/security-risk/vulnerabilities → /
|
||||||
|
/evidence-audit → /
|
||||||
|
/evidence-audit/packs → /
|
||||||
|
/evidence-audit/proofs → /
|
||||||
|
/evidence-audit/audit → /
|
||||||
|
/platform-ops → /
|
||||||
|
/platform-ops/health → /
|
||||||
|
/platform-ops/feeds → /
|
||||||
|
/administration → /
|
||||||
|
/administration/identity-access → /
|
||||||
|
/administration/policy-governance → /
|
||||||
|
/dashboard → /
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟠 High
|
||||||
|
|
||||||
|
### ISSUE-002 — Integration Hub (`/integrations`) fires 10 API errors on load
|
||||||
|
**Page:** `https://stella-ops.local/integrations`
|
||||||
|
**Reproduction:** Navigate to `/integrations`.
|
||||||
|
**Observed:** Page loads visually (shows Integration Hub with all category counts as 0) but generates 10 console errors:
|
||||||
|
```
|
||||||
|
Failed to load resource: server responded with an error
|
||||||
|
/api/v1/integrations?type=0&pageSize=1
|
||||||
|
/api/v1/integrations?type=1&pageSize=1
|
||||||
|
/api/v1/integrations?type=2&pageSize=1
|
||||||
|
/api/v1/integrations?type=3&pageSize=1
|
||||||
|
/api/v1/integrations?type=4&pageSize=1
|
||||||
|
(plus 5x "ERROR N @ chunk-2UEM7CYT.js:3")
|
||||||
|
```
|
||||||
|
**Expected:** API calls succeed; summary counts reflect actual integration state (the old `/settings/integrations` shows 8 integrations with seed data).
|
||||||
|
**Impact:** The v2 Integration Hub is broken — all counts show 0 and the "Recent Activity" section shows a placeholder ("Integration activity timeline coming soon…"). Users cannot use this page.
|
||||||
|
**Note:** `/settings/integrations` works correctly (8 integrations shown). The backend API endpoint `/api/v1/integrations` may not be connected to the integrations service.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-003 — After creating a release, redirects to orphaned route `/release-orchestrator/releases`
|
||||||
|
**Page:** `/releases/create`
|
||||||
|
**Reproduction:** Create a release through the 3-step wizard → click "Create Release" on step 3.
|
||||||
|
**Observed:** After submit, browser navigates to `/release-orchestrator/releases`.
|
||||||
|
**Expected:** Should navigate to `/releases` (the current releases list route).
|
||||||
|
**Impact:** The post-create redirect lands on an old route that no longer exists in the sidebar IA and was renamed. The URL works (Angular handles it), but it's a stale reference that will break when the old route aliases are removed during the v2 cutover (SPRINT_20260218_016).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-004 — Identity & Access (`/settings/admin`) shows "No users found" with admin logged in
|
||||||
|
**Page:** `https://stella-ops.local/settings/admin`
|
||||||
|
**Reproduction:** Navigate to Settings → Identity & Access → Users tab.
|
||||||
|
**Observed:** "No users found" message shown even though the `admin` user is currently authenticated.
|
||||||
|
**Expected:** At minimum the `admin` user should appear in the user list.
|
||||||
|
**Impact:** Administrators cannot view or manage users from this page. User management is effectively broken.
|
||||||
|
**Screenshot context:** Bootstrap admin email is `admin@unknown.local` (possibly indicating the user was seeded without persisting to the listing query).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-005 — Approvals badge count (3) does not match Pending filter results (2)
|
||||||
|
**Page:** `/approvals`
|
||||||
|
**Reproduction:** Observe sidebar badge → click through to Approvals page → filter defaults to "Pending" status.
|
||||||
|
**Observed:**
|
||||||
|
- Sidebar badge: **3 pending**
|
||||||
|
- Pending filter: **Results (2)**
|
||||||
|
- All filter: **Results (4)**
|
||||||
|
**Expected:** Badge should equal the "Pending" filtered count. The badge logic and the pending query are sourced differently.
|
||||||
|
**Impact:** Misleading count for approvers — could cause someone to think they've missed an item or search for a non-existent third pending approval.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 Medium
|
||||||
|
|
||||||
|
### ISSUE-006 — Platform Health shows "NaNms" P95 latency and "/" service count
|
||||||
|
**Page:** `https://stella-ops.local/operations/health`
|
||||||
|
**Reproduction:** Navigate to Operations → Platform Health.
|
||||||
|
**Observed:**
|
||||||
|
- "Avg Latency **NaNms** — P95 across services"
|
||||||
|
- "Services **/** Healthy" (shows a bare `/` instead of a number)
|
||||||
|
- "No services available in current snapshot"
|
||||||
|
- "Dependencies: 0 nodes · 0 connections"
|
||||||
|
**Expected:** Should show either real service health data or a meaningful empty state ("No health data available yet" with guidance).
|
||||||
|
**Impact:** The health dashboard is completely non-functional on a fresh install. The NaN renders because it divides by zero services. The "/" is a formatting bug where a fraction like "0/0" is rendered without the surrounding numbers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-007 — Approve button on Approvals list has no confirmation step
|
||||||
|
**Page:** `/approvals`
|
||||||
|
**Reproduction:** On the approvals list, click "Approve" directly on any approval card.
|
||||||
|
**Observed:** No confirmation dialog, modal, or reason input appears. The action fires silently (or may silently fail — no success/error toast was observed).
|
||||||
|
**Expected:** A confirmation dialog or inline form should appear asking for a decision reason, especially since approvals are policy-gated actions that must produce signed evidence.
|
||||||
|
**Impact:** Accidental approvals are possible with a single click. Audit trail for the decision reason is missing if no reason is captured.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-008 — SBOM Graph is a placeholder: "not yet available in this build"
|
||||||
|
**Page:** `https://stella-ops.local/security/sbom`
|
||||||
|
**Reproduction:** Navigate to Security → SBOM Graph.
|
||||||
|
**Observed:** Page renders with heading "SBOM Graph" and single message: "SBOM graph visualization is not yet available in this build."
|
||||||
|
**Expected:** SBOM dependency graph visualization.
|
||||||
|
**Impact:** Feature is advertised in navigation but completely unimplemented in the deployed build.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-009 — Vulnerabilities page is a placeholder: "pending data integration"
|
||||||
|
**Page:** `https://stella-ops.local/security/vulnerabilities`
|
||||||
|
**Reproduction:** Navigate to Security → Vulnerabilities.
|
||||||
|
**Observed:** Page renders with heading "Vulnerabilities" and message: "Vulnerability list is pending data integration."
|
||||||
|
**Expected:** Vulnerability explorer with CVE list, filters, and triage actions.
|
||||||
|
**Impact:** Feature is advertised in navigation but has no functional content.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-010 — Promote button on a deployed release does nothing
|
||||||
|
**Page:** `/releases/rel-001` (Platform Release 1.2.3 — DEPLOYED)
|
||||||
|
**Reproduction:** Click the "Promote" button on a deployed release detail page.
|
||||||
|
**Observed:** No navigation, no modal, no drawer — the page stays unchanged.
|
||||||
|
**Expected:** A promotion dialog or navigation to the promotion wizard.
|
||||||
|
**Impact:** Users cannot initiate a promotion from the release detail page — a core workflow action is broken.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-011 — Security sub-pages carry wrong `<title>`: "Security Overview - StellaOps"
|
||||||
|
**Pages affected:**
|
||||||
|
- `/security/findings` → title: "Security Overview - StellaOps"
|
||||||
|
- `/security/vex` → title: "Security Overview - StellaOps"
|
||||||
|
- `/security/sbom` → title: "Security Overview - StellaOps"
|
||||||
|
**Expected:** Each page should have its own title, e.g. "Security Findings - StellaOps", "VEX Hub - StellaOps".
|
||||||
|
**Impact:** Browser tabs, bookmarks, and screen-reader announcements all say "Security Overview" regardless of which security sub-page is open. Causes confusion and breaks accessibility.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-012 — Integration Hub "Recent Activity" is a permanent placeholder
|
||||||
|
**Page:** `https://stella-ops.local/integrations`
|
||||||
|
**Observed:** "Integration activity timeline coming soon…" italic placeholder text under Recent Activity heading.
|
||||||
|
**Expected:** Activity timeline showing integration sync events, errors, and status changes.
|
||||||
|
**Impact:** The activity view the section promises is not implemented.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔵 Low
|
||||||
|
|
||||||
|
### ISSUE-013 — Many pages have generic `<title>` "StellaOps" (no page context)
|
||||||
|
**Pages affected:**
|
||||||
|
| Route | Title |
|
||||||
|
|-------|-------|
|
||||||
|
| `/security/vulnerabilities` | StellaOps |
|
||||||
|
| `/evidence/proof-chains` | StellaOps |
|
||||||
|
| `/evidence/replay` | StellaOps |
|
||||||
|
| `/evidence/export` | StellaOps |
|
||||||
|
| `/operations/orchestrator` | StellaOps |
|
||||||
|
| `/settings/integrations` | StellaOps |
|
||||||
|
| `/settings/release-control` | StellaOps |
|
||||||
|
| `/settings/security-data` | StellaOps |
|
||||||
|
| `/settings/admin` | StellaOps |
|
||||||
|
| `/settings/system` | StellaOps |
|
||||||
|
|
||||||
|
**Expected:** `<Page Name> - StellaOps`
|
||||||
|
**Impact:** Browser tabs are undifferentiable, bookmarks are unlabelled, screen readers announce the wrong page context. This likely affects all pages whose route modules don't call Angular's `Title` service.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-014 — Release detail breadcrumb references old "Release Orchestrator" path
|
||||||
|
**Page:** `/releases/rel-001`
|
||||||
|
**Observed:** Breadcrumb reads: `Release Orchestrator / Releases / Platform Release 1.2.3`
|
||||||
|
**Links to:** `/release-orchestrator` and `/release-orchestrator/releases`
|
||||||
|
**Expected:** `Releases / Platform Release 1.2.3` (linking to `/releases`)
|
||||||
|
**Impact:** Clicking the breadcrumb links navigates to old route aliases that will be removed at v2 cutover. Low impact now; will become a broken link after SPRINT_20260218_016.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-015 — Evidence Proof Chains page shows error state on load with no input
|
||||||
|
**Page:** `https://stella-ops.local/evidence/proof-chains`
|
||||||
|
**Observed:** Page immediately shows "Subject digest is required — Retry" with no input field offered.
|
||||||
|
**Expected:** An empty state with a search or input field to enter a subject digest; error should only appear after a failed search.
|
||||||
|
**Impact:** Page is confusing on first load — appears broken but is just waiting for a digest input that it never prompts for.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-016 — `/evidence` redirects to `/evidence/bundles` (not to Packets)
|
||||||
|
**Page:** Navigate to `/evidence` (from Evidence nav button).
|
||||||
|
**Observed:** Redirects to `/evidence/bundles` — heading "Evidence Bundles".
|
||||||
|
**Expected per sidebar label:** "Packets" (sidebar link text) — `/evidence` should land on Evidence Packets, not Evidence Bundles. The sub-page URL `/evidence/bundles` is not in the sidebar nav.
|
||||||
|
**Impact:** Minor navigation inconsistency — sidebar says "Packets", page says "Bundles", route says "bundles". Naming is not aligned.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-017 — Scheduler nav link lands on `/operations/scheduler/runs` not `/operations/scheduler`
|
||||||
|
**Page:** Click Operations → Scheduler in the sidebar.
|
||||||
|
**Observed:** Navigates to `/operations/scheduler/runs`.
|
||||||
|
**Expected:** `/operations/scheduler` (the root scheduler page) with the runs as a sub-view.
|
||||||
|
**Impact:** Minor — the redirect is functional but means the scheduler root route appears to have no direct landing page.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ISSUE-018 — `/settings/admin` is labeled "Identity & Access" in sidebar but Settings section uses "Identity & Access" inconsistently
|
||||||
|
**Page:** Settings group in sidebar.
|
||||||
|
**Observed:** The Settings sidebar link for the admin page reads "Identity & Access", which is correct — but the page was also previously accessible at the legacy path `/settings/admin`. The link in the sidebar still uses `/settings/admin` (the implementation path) rather than a semantic path like `/settings/identity`.
|
||||||
|
**Impact:** Minor URL semantics issue; the path exposes an internal implementation name (`admin`) rather than the user-facing label (`identity-access`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pages Verified — No Issues
|
||||||
|
|
||||||
|
| Page | URL | Status |
|
||||||
|
|------|-----|--------|
|
||||||
|
| Welcome / Sign In | `/welcome` | ✅ |
|
||||||
|
| Control Plane Dashboard | `/` | ✅ |
|
||||||
|
| Releases List | `/releases` | ✅ |
|
||||||
|
| Release Detail | `/releases/rel-001` | ✅ (Promote broken, see ISSUE-010) |
|
||||||
|
| Approvals List | `/approvals` | ✅ (count mismatch, see ISSUE-005) |
|
||||||
|
| Approval Detail | `/approvals/apr-001` | ✅ |
|
||||||
|
| Security Overview | `/security/overview` | ✅ |
|
||||||
|
| Security Findings | `/security/findings` | ✅ |
|
||||||
|
| Security VEX Hub | `/security/vex` | ✅ |
|
||||||
|
| Security Exceptions | `/security/exceptions` | ✅ |
|
||||||
|
| SBOM Lake | `/analytics/sbom-lake` | ✅ |
|
||||||
|
| Evidence Bundles | `/evidence/bundles` | ✅ |
|
||||||
|
| Verdict Replay | `/evidence/replay` | ✅ |
|
||||||
|
| Export Center | `/evidence/export` | ✅ |
|
||||||
|
| Orchestrator Dashboard | `/operations/orchestrator` | ✅ |
|
||||||
|
| Scheduler Runs | `/operations/scheduler/runs` | ✅ |
|
||||||
|
| Quota Dashboard | `/operations/quotas` | ✅ |
|
||||||
|
| Dead-Letter Queue | `/operations/dead-letter` | ✅ |
|
||||||
|
| Feed Mirror & AirGap | `/operations/feeds` | ✅ |
|
||||||
|
| Integrations (legacy) | `/settings/integrations` | ✅ |
|
||||||
|
| Integrations SCM | `/integrations/scm` | ✅ |
|
||||||
|
| Integrations Registries | `/integrations/registries` | ✅ |
|
||||||
|
| Integration Detail | `/settings/integrations/jenkins-1` | ✅ |
|
||||||
|
| Integration Onboarding | `/integrations/onboarding/registry` | ✅ |
|
||||||
|
| Release Control Settings | `/settings/release-control` | ✅ |
|
||||||
|
| Trust & Signing | `/settings/trust` | ✅ |
|
||||||
|
| Security Data | `/settings/security-data` | ✅ |
|
||||||
|
| Tenant / Branding | `/settings/branding` | ✅ |
|
||||||
|
| Usage & Limits | `/settings/usage` | ✅ |
|
||||||
|
| Notifications | `/settings/notifications` | ✅ |
|
||||||
|
| Policy Governance | `/settings/policy` | ✅ |
|
||||||
|
| System | `/settings/system` | ✅ |
|
||||||
|
| Create Release Wizard (3 steps) | `/releases/create` | ✅ (redirect bug, see ISSUE-003) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Actions Verified
|
||||||
|
|
||||||
|
| Action | Result |
|
||||||
|
|--------|--------|
|
||||||
|
| Sign In (OAuth/OIDC) | ✅ Works |
|
||||||
|
| Global Search (type "hotfix") | ✅ Inline results shown |
|
||||||
|
| Sidebar expand/collapse all sections | ✅ Works |
|
||||||
|
| Release list filter by status/environment | ✅ Works |
|
||||||
|
| Release detail Timeline tab | ✅ Works |
|
||||||
|
| Approval list filter by Status/Environment | ✅ Works |
|
||||||
|
| Approval detail Explain gate | ✅ Opens explanation |
|
||||||
|
| Approval detail Add Comment | ✅ Comment saved |
|
||||||
|
| Create Release wizard (3 steps) | ✅ Completes (bad redirect after) |
|
||||||
|
| Export CSV (Findings) | ✅ Button present |
|
||||||
|
| Add Integration (opens onboarding) | ✅ Navigates to onboarding |
|
||||||
|
| User menu (Profile / Settings / Sign out) | ✅ All present |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Notes
|
||||||
|
|
||||||
|
- Fresh install with no scan data → all security counters (CVE counts, SBOM, reachability) are zero. Zero counts are **expected**, not bugs.
|
||||||
|
- Seed data is present for: Releases (5), Approvals (4), Integrations (8), and some environmental data.
|
||||||
|
- Several services reported `unhealthy` in Docker (`stellaops-signals`, `stellaops-smremote`, `stellaops-advisory-ai-worker`, etc.) — these backend health states may explain some of the data gaps (Platform Health no snapshot, Integration Hub API failures).
|
||||||
143
docs/runbooks/federated-telemetry-operations.md
Normal file
143
docs/runbooks/federated-telemetry-operations.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# Federated Telemetry Operations Runbook
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This runbook covers operational procedures for the Stella Ops Federated Telemetry subsystem, including enabling/disabling federation, managing consent, monitoring privacy budgets, and troubleshooting sync failures.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Platform admin access with `platform:federation:manage` scope.
|
||||||
|
- Access to the Stella Ops Platform Ops UI or API.
|
||||||
|
|
||||||
|
## Procedures
|
||||||
|
|
||||||
|
### 1. Enable Federation
|
||||||
|
|
||||||
|
Federation is enabled by default when `SealedModeEnabled` is `false` in configuration.
|
||||||
|
|
||||||
|
**Via configuration:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"FederatedTelemetry": {
|
||||||
|
"SealedModeEnabled": false,
|
||||||
|
"SiteId": "your-site-id",
|
||||||
|
"KAnonymityThreshold": 5,
|
||||||
|
"EpsilonBudget": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
```
|
||||||
|
GET /api/v1/telemetry/federation/status
|
||||||
|
```
|
||||||
|
Response should show `enabled: true`, `sealedMode: false`.
|
||||||
|
|
||||||
|
### 2. Disable Federation (Sealed Mode)
|
||||||
|
|
||||||
|
Set `SealedModeEnabled: true` in configuration and restart the service.
|
||||||
|
|
||||||
|
In sealed mode:
|
||||||
|
- No outbound federation traffic.
|
||||||
|
- Sync service skips all cycles.
|
||||||
|
- Local aggregation continues for internal use.
|
||||||
|
|
||||||
|
### 3. Grant Consent
|
||||||
|
|
||||||
|
Consent must be explicitly granted before any data is shared.
|
||||||
|
|
||||||
|
**Via API:**
|
||||||
|
```
|
||||||
|
POST /api/v1/telemetry/federation/consent/grant
|
||||||
|
{
|
||||||
|
"grantedBy": "admin@example.com",
|
||||||
|
"ttlHours": 720
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Via UI:**
|
||||||
|
Navigate to Platform Ops > Federation > Consent Management and click "Grant Consent".
|
||||||
|
|
||||||
|
### 4. Revoke Consent
|
||||||
|
|
||||||
|
**Via API:**
|
||||||
|
```
|
||||||
|
POST /api/v1/telemetry/federation/consent/revoke
|
||||||
|
{
|
||||||
|
"revokedBy": "admin@example.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Via UI:**
|
||||||
|
Navigate to Platform Ops > Federation > Consent Management and click "Revoke Consent".
|
||||||
|
|
||||||
|
After revocation:
|
||||||
|
- No new bundles will be created.
|
||||||
|
- Existing bundles remain in the store.
|
||||||
|
- Federation peers will stop receiving updates.
|
||||||
|
|
||||||
|
### 5. Monitor Privacy Budget
|
||||||
|
|
||||||
|
**Via API:**
|
||||||
|
```
|
||||||
|
GET /api/v1/telemetry/federation/privacy-budget
|
||||||
|
```
|
||||||
|
|
||||||
|
**Via UI:**
|
||||||
|
Navigate to Platform Ops > Federation > Privacy Budget.
|
||||||
|
|
||||||
|
Key metrics:
|
||||||
|
- `remaining` / `total`: Current epsilon consumption.
|
||||||
|
- `exhausted`: If true, no aggregation until next reset.
|
||||||
|
- `queriesThisPeriod`: Number of successful aggregations.
|
||||||
|
- `suppressedThisPeriod`: Number of rejected aggregations due to budget.
|
||||||
|
- `nextReset`: When the budget will be replenished.
|
||||||
|
|
||||||
|
### 6. Manual Aggregation Trigger
|
||||||
|
|
||||||
|
**Via API:**
|
||||||
|
```
|
||||||
|
POST /api/v1/telemetry/federation/trigger
|
||||||
|
```
|
||||||
|
|
||||||
|
Will fail if:
|
||||||
|
- Privacy budget is exhausted.
|
||||||
|
- Consent is not granted.
|
||||||
|
- Sealed mode is active.
|
||||||
|
|
||||||
|
### 7. Troubleshooting Sync Failures
|
||||||
|
|
||||||
|
**Symptom: No bundles being created**
|
||||||
|
|
||||||
|
Check in order:
|
||||||
|
1. Is federation enabled? (`GET /status` -> `enabled: true`)
|
||||||
|
2. Is consent granted? (`GET /consent` -> `granted: true`)
|
||||||
|
3. Is privacy budget available? (`GET /privacy-budget` -> `exhausted: false`)
|
||||||
|
4. Are there telemetry facts to aggregate? (Check service logs for "No telemetry facts to aggregate")
|
||||||
|
5. Is egress policy blocking? (Check service logs for "Egress blocked")
|
||||||
|
|
||||||
|
**Symptom: Budget exhausting too quickly**
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- Increase `EpsilonBudget` (less privacy, more queries).
|
||||||
|
- Decrease aggregation frequency (`AggregationInterval`).
|
||||||
|
- Increase `KAnonymityThreshold` (more suppression, fewer budget-consuming buckets).
|
||||||
|
|
||||||
|
**Symptom: All buckets suppressed**
|
||||||
|
|
||||||
|
The k-anonymity threshold is too high relative to the data volume. Either:
|
||||||
|
- Lower `KAnonymityThreshold`.
|
||||||
|
- Wait for more diverse telemetry data.
|
||||||
|
|
||||||
|
### 8. Configuration Reference
|
||||||
|
|
||||||
|
| Setting | Default | Description |
|
||||||
|
|---------|---------|-------------|
|
||||||
|
| `KAnonymityThreshold` | 5 | Minimum distinct artifacts per CVE bucket |
|
||||||
|
| `EpsilonBudget` | 1.0 | Total differential privacy budget per period |
|
||||||
|
| `BudgetResetPeriod` | 24h | Budget reset interval |
|
||||||
|
| `AggregationInterval` | 15m | Background sync cycle interval |
|
||||||
|
| `SealedModeEnabled` | false | Disable all outbound federation traffic |
|
||||||
|
| `SiteId` | "default" | This instance's federation mesh identifier |
|
||||||
|
| `ConsentPredicateType` | stella.ops/federatedConsent@v1 | DSSE predicate for consent proofs |
|
||||||
|
| `BundlePredicateType` | stella.ops/federatedTelemetry@v1 | DSSE predicate for telemetry bundles |
|
||||||
@@ -23,7 +23,7 @@ stella doctor --tag registry --format json --output registry-report.json
|
|||||||
| Registry | Referrers API | Recommendation |
|
| Registry | Referrers API | Recommendation |
|
||||||
|----------|---------------|----------------|
|
|----------|---------------|----------------|
|
||||||
| ACR, ECR, GCR, Harbor 2.6+, Quay 3.12+, JFrog 7.x+, Zot | Native | Full support |
|
| ACR, ECR, GCR, Harbor 2.6+, Quay 3.12+, JFrog 7.x+, Zot | Native | Full support |
|
||||||
| GHCR, Docker Hub, registry:2 | Fallback | Supported with automatic fallback |
|
| GHCR, GitLab, Docker Hub, registry:2 | Fallback | Supported with automatic fallback |
|
||||||
|
|
||||||
## Common Issues
|
## Common Issues
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ This runbook covers diagnosing and resolving OCI referrer discovery issues durin
|
|||||||
| ECR | Yes | Yes | Requires proper IAM permissions |
|
| ECR | Yes | Yes | Requires proper IAM permissions |
|
||||||
| ACR | Yes | Yes | Full OCI 1.1 support |
|
| ACR | Yes | Yes | Full OCI 1.1 support |
|
||||||
| Harbor 2.0+ | Yes | Yes | Full OCI 1.1 support |
|
| Harbor 2.0+ | Yes | Yes | Full OCI 1.1 support |
|
||||||
| Quay | Partial | Yes | Varies by version |
|
| Quay | Partial | Yes | Varies by version; admin toggles may control feature |
|
||||||
| JFrog Artifactory | Partial | Yes | Requires OCI layout repository |
|
| JFrog Artifactory | Partial | Yes | Requires OCI layout repository |
|
||||||
|
| GitLab | No | Yes | Stores `subject` field but no referrers endpoint |
|
||||||
|
|
||||||
See [Registry Compatibility Matrix](../modules/export-center/registry-compatibility.md) for detailed information.
|
See [Registry Compatibility Matrix](../modules/export-center/registry-compatibility.md) for detailed information.
|
||||||
|
|
||||||
@@ -169,6 +170,37 @@ curl "https://registry.example.com/v2/repo/referrers/sha256:abc123?artifactType=
|
|||||||
2. Verify bundle integrity: `sha256sum bundle.tgz`
|
2. Verify bundle integrity: `sha256sum bundle.tgz`
|
||||||
3. Check if referrer was intentionally updated upstream
|
3. Check if referrer was intentionally updated upstream
|
||||||
|
|
||||||
|
### Issue: Harbor UI shows referrers as "UNKNOWN" artifact type
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Referrer artifacts (cosign signatures, SBOMs) appear as "UNKNOWN" in Harbor UI
|
||||||
|
- API-level discovery works correctly
|
||||||
|
|
||||||
|
**Causes:**
|
||||||
|
1. Harbor UI mediaType classification lags API capabilities (especially around v2.15+)
|
||||||
|
2. Custom artifact types not recognized by Harbor's UI layer
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
- This is a Harbor-side UI classification issue; it does **not** affect StellaOps referrer discovery or functionality
|
||||||
|
- Verify API-level discovery works: `curl -H "Accept: application/vnd.oci.image.index.v1+json" "https://harbor.example.com/v2/repo/referrers/sha256:..."`
|
||||||
|
- If needed, check Harbor release notes for mediaType classification updates
|
||||||
|
|
||||||
|
### Issue: Quay referrers API returns inconsistent results
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Referrer discovery works on Quay.io but not on self-hosted Quay
|
||||||
|
- Intermittent 404 or empty results from referrers endpoint
|
||||||
|
|
||||||
|
**Causes:**
|
||||||
|
1. OCI Referrers API feature not enabled in self-hosted Quay deployment
|
||||||
|
2. Quay admin toggles or deployment flags controlling the feature
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
- Verify the OCI Referrers API feature is enabled in Quay's deployment configuration
|
||||||
|
- Check Quay admin console for referrers-related feature flags
|
||||||
|
- If feature is disabled, StellaOps automatically uses tag-based fallback; no action required
|
||||||
|
- Contact Quay administrator to enable the feature if native referrers discovery is preferred
|
||||||
|
|
||||||
### Issue: Slow referrer discovery
|
### Issue: Slow referrer discovery
|
||||||
|
|
||||||
**Symptoms:**
|
**Symptoms:**
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ using StellaOps.Attestor.Core.Storage;
|
|||||||
using StellaOps.Attestor.Core.Submission;
|
using StellaOps.Attestor.Core.Submission;
|
||||||
using StellaOps.Attestor.Core.Verification;
|
using StellaOps.Attestor.Core.Verification;
|
||||||
using StellaOps.Attestor.Infrastructure;
|
using StellaOps.Attestor.Infrastructure;
|
||||||
|
using StellaOps.Attestor.Persistence;
|
||||||
using StellaOps.Attestor.ProofChain;
|
using StellaOps.Attestor.ProofChain;
|
||||||
using StellaOps.Attestor.Spdx3;
|
using StellaOps.Attestor.Spdx3;
|
||||||
using StellaOps.Attestor.Watchlist;
|
using StellaOps.Attestor.Watchlist;
|
||||||
|
using StellaOps.Attestor.WebService.Endpoints;
|
||||||
using StellaOps.Attestor.WebService.Options;
|
using StellaOps.Attestor.WebService.Options;
|
||||||
using StellaOps.Auth.ServerIntegration;
|
using StellaOps.Auth.ServerIntegration;
|
||||||
using StellaOps.Configuration;
|
using StellaOps.Configuration;
|
||||||
@@ -141,6 +143,13 @@ internal static class AttestorWebServiceComposition
|
|||||||
builder.Services.AddAttestorInfrastructure();
|
builder.Services.AddAttestorInfrastructure();
|
||||||
builder.Services.AddProofChainServices();
|
builder.Services.AddProofChainServices();
|
||||||
|
|
||||||
|
// Predicate type registry (Sprint: SPRINT_20260219_010, PSR-02)
|
||||||
|
var postgresConnectionString = builder.Configuration["attestor:postgres:connectionString"];
|
||||||
|
if (!string.IsNullOrWhiteSpace(postgresConnectionString))
|
||||||
|
{
|
||||||
|
builder.Services.AddPredicateTypeRegistry(postgresConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.AddScoped<Services.IProofChainQueryService, Services.ProofChainQueryService>();
|
builder.Services.AddScoped<Services.IProofChainQueryService, Services.ProofChainQueryService>();
|
||||||
builder.Services.AddScoped<Services.IProofVerificationService, Services.ProofVerificationService>();
|
builder.Services.AddScoped<Services.IProofVerificationService, Services.ProofVerificationService>();
|
||||||
|
|
||||||
@@ -410,6 +419,7 @@ internal static class AttestorWebServiceComposition
|
|||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
app.MapAttestorEndpoints(attestorOptions);
|
app.MapAttestorEndpoints(attestorOptions);
|
||||||
app.MapWatchlistEndpoints();
|
app.MapWatchlistEndpoints();
|
||||||
|
app.MapPredicateRegistryEndpoints();
|
||||||
|
|
||||||
app.TryRefreshStellaRouterEndpoints(routerOptions);
|
app.TryRefreshStellaRouterEndpoints(routerOptions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// PredicateRegistryEndpoints.cs
|
||||||
|
// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
// Task: PSR-02 - Create Predicate Schema Registry endpoints and repository
|
||||||
|
// Description: REST API endpoints for the predicate type schema registry
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using StellaOps.Attestor.Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace StellaOps.Attestor.WebService.Endpoints;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Endpoints for the predicate type schema registry.
|
||||||
|
/// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
/// </summary>
|
||||||
|
public static class PredicateRegistryEndpoints
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maps predicate registry endpoints.
|
||||||
|
/// </summary>
|
||||||
|
public static void MapPredicateRegistryEndpoints(this IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
var group = app.MapGroup("/api/v1/attestor/predicates")
|
||||||
|
.WithTags("Predicate Registry")
|
||||||
|
.WithOpenApi();
|
||||||
|
|
||||||
|
group.MapGet("/", ListPredicateTypes)
|
||||||
|
.WithName("ListPredicateTypes")
|
||||||
|
.WithSummary("List all registered predicate types")
|
||||||
|
.Produces<PredicateTypeListResponse>(StatusCodes.Status200OK);
|
||||||
|
|
||||||
|
group.MapGet("/{uri}", GetPredicateType)
|
||||||
|
.WithName("GetPredicateType")
|
||||||
|
.WithSummary("Get predicate type schema by URI")
|
||||||
|
.Produces<PredicateTypeRegistryEntry>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> ListPredicateTypes(
|
||||||
|
IPredicateTypeRegistryRepository repository,
|
||||||
|
string? category = null,
|
||||||
|
bool? isActive = null,
|
||||||
|
int offset = 0,
|
||||||
|
int limit = 100,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var entries = await repository.ListAsync(category, isActive, offset, limit, ct);
|
||||||
|
return Results.Ok(new PredicateTypeListResponse
|
||||||
|
{
|
||||||
|
Items = entries,
|
||||||
|
Offset = offset,
|
||||||
|
Limit = limit,
|
||||||
|
Count = entries.Count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> GetPredicateType(
|
||||||
|
string uri,
|
||||||
|
IPredicateTypeRegistryRepository repository,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var decoded = Uri.UnescapeDataString(uri);
|
||||||
|
var entry = await repository.GetByUriAsync(decoded, ct);
|
||||||
|
if (entry is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound(new { error = "Predicate type not found", uri = decoded });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.Ok(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Response for listing predicate types.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record PredicateTypeListResponse
|
||||||
|
{
|
||||||
|
/// <summary>The predicate type entries.</summary>
|
||||||
|
public required IReadOnlyList<PredicateTypeRegistryEntry> Items { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Pagination offset.</summary>
|
||||||
|
public int Offset { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Pagination limit.</summary>
|
||||||
|
public int Limit { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Number of items returned.</summary>
|
||||||
|
public int Count { get; init; }
|
||||||
|
}
|
||||||
@@ -32,5 +32,6 @@
|
|||||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Bundling\StellaOps.Attestor.Bundling.csproj" />
|
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Bundling\StellaOps.Attestor.Bundling.csproj" />
|
||||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Spdx3\StellaOps.Attestor.Spdx3.csproj" />
|
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Spdx3\StellaOps.Attestor.Spdx3.csproj" />
|
||||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Watchlist\StellaOps.Attestor.Watchlist.csproj" />
|
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Watchlist\StellaOps.Attestor.Watchlist.csproj" />
|
||||||
|
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Persistence\StellaOps.Attestor.Persistence.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
-- Attestor Schema Migration 002: Predicate Type Registry
|
||||||
|
-- Sprint: SPRINT_20260219_010 (PSR-01)
|
||||||
|
-- Creates discoverable, versioned registry for all predicate types
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Predicate Type Registry Table
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS proofchain.predicate_type_registry (
|
||||||
|
registry_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
predicate_type_uri TEXT NOT NULL,
|
||||||
|
display_name TEXT NOT NULL,
|
||||||
|
version TEXT NOT NULL DEFAULT '1.0.0',
|
||||||
|
category TEXT NOT NULL DEFAULT 'stella-core'
|
||||||
|
CHECK (category IN ('stella-core', 'stella-proof', 'stella-delta', 'ecosystem', 'intoto', 'custom')),
|
||||||
|
json_schema JSONB,
|
||||||
|
description TEXT,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
validation_mode TEXT NOT NULL DEFAULT 'log-only'
|
||||||
|
CHECK (validation_mode IN ('log-only', 'warn', 'reject')),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT uq_predicate_type_version UNIQUE (predicate_type_uri, version)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_predicate_registry_uri
|
||||||
|
ON proofchain.predicate_type_registry(predicate_type_uri);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_predicate_registry_category
|
||||||
|
ON proofchain.predicate_type_registry(category);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_predicate_registry_active
|
||||||
|
ON proofchain.predicate_type_registry(is_active) WHERE is_active = TRUE;
|
||||||
|
|
||||||
|
-- Apply updated_at trigger
|
||||||
|
DROP TRIGGER IF EXISTS update_predicate_registry_updated_at ON proofchain.predicate_type_registry;
|
||||||
|
CREATE TRIGGER update_predicate_registry_updated_at
|
||||||
|
BEFORE UPDATE ON proofchain.predicate_type_registry
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION proofchain.update_updated_at_column();
|
||||||
|
|
||||||
|
COMMENT ON TABLE proofchain.predicate_type_registry IS 'Discoverable registry of all predicate types accepted by the Attestor';
|
||||||
|
COMMENT ON COLUMN proofchain.predicate_type_registry.predicate_type_uri IS 'Canonical URI for the predicate type (e.g., https://stella-ops.org/predicates/evidence/v1)';
|
||||||
|
COMMENT ON COLUMN proofchain.predicate_type_registry.validation_mode IS 'How mismatches are handled: log-only (default), warn, or reject';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Seed: stella-core predicates
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
INSERT INTO proofchain.predicate_type_registry (predicate_type_uri, display_name, version, category, description) VALUES
|
||||||
|
('https://stella-ops.org/predicates/sbom-linkage/v1', 'SBOM Linkage', '1.0.0', 'stella-core', 'Links SBOM components to evidence and proof spines'),
|
||||||
|
('https://stella-ops.org/predicates/vex-verdict/v1', 'VEX Verdict', '1.0.0', 'stella-core', 'VEX consensus verdict for an artifact+advisory tuple'),
|
||||||
|
('https://stella-ops.org/predicates/evidence/v1', 'Evidence', '1.0.0', 'stella-core', 'Generic evidence attestation linking scan results to artifacts'),
|
||||||
|
('https://stella-ops.org/predicates/reasoning/v1', 'Reasoning', '1.0.0', 'stella-core', 'Policy reasoning chain for a release decision'),
|
||||||
|
('https://stella-ops.org/predicates/proof-spine/v1', 'Proof Spine', '1.0.0', 'stella-core', 'Merkle-aggregated proof spine linking evidence to verdicts'),
|
||||||
|
('https://stella-ops.org/predicates/reachability-drift/v1', 'Reachability Drift', '1.0.0', 'stella-core', 'Reachability state changes between consecutive scans'),
|
||||||
|
('https://stella-ops.org/predicates/reachability-subgraph/v1', 'Reachability Subgraph', '1.0.0', 'stella-core', 'Call graph subgraph for a specific vulnerability path'),
|
||||||
|
('https://stella-ops.org/predicates/delta-verdict/v1', 'Delta Verdict', '1.0.0', 'stella-core', 'Verdict differences between two scan runs'),
|
||||||
|
('https://stella-ops.org/predicates/policy-decision/v1', 'Policy Decision', '1.0.0', 'stella-core', 'Policy engine evaluation result for a release gate'),
|
||||||
|
('https://stella-ops.org/predicates/unknowns-budget/v1', 'Unknowns Budget', '1.0.0', 'stella-core', 'Budget check for unknown reachability components'),
|
||||||
|
('https://stella-ops.org/predicates/ai-code-guard/v1', 'AI Code Guard', '1.0.0', 'stella-core', 'AI-assisted code security analysis results'),
|
||||||
|
('https://stella-ops.org/predicates/fix-chain/v1', 'Fix Chain', '1.0.0', 'stella-core', 'Linked chain of fix commits from vulnerability to resolution'),
|
||||||
|
('https://stella-ops.org/attestation/graph-root/v1', 'Graph Root', '1.0.0', 'stella-core', 'Root attestation for a complete call graph')
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Seed: stella-proof predicates (ProofChain)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
INSERT INTO proofchain.predicate_type_registry (predicate_type_uri, display_name, version, category, description) VALUES
|
||||||
|
('https://stella.ops/predicates/path-witness/v1', 'Path Witness', '1.0.0', 'stella-proof', 'Entrypoint-to-sink call path witness with gate detection'),
|
||||||
|
('https://stella.ops/predicates/runtime-witness/v1', 'Runtime Witness', '1.0.0', 'stella-proof', 'Runtime micro-witness from eBPF/ETW observations'),
|
||||||
|
('https://stella.ops/predicates/policy-decision@v2', 'Policy Decision v2', '2.0.0', 'stella-proof', 'Enhanced policy decision with reachability context'),
|
||||||
|
('https://stellaops.dev/predicates/binary-micro-witness@v1', 'Binary Micro-Witness', '1.0.0', 'stella-proof', 'Binary-level micro-witness with build ID correlation'),
|
||||||
|
('https://stellaops.dev/predicates/binary-fingerprint-evidence@v1', 'Binary Fingerprint', '1.0.0', 'stella-proof', 'Binary fingerprint evidence for patch detection'),
|
||||||
|
('https://stellaops.io/attestation/budget-check/v1', 'Budget Check', '1.0.0', 'stella-proof', 'Unknowns budget check attestation'),
|
||||||
|
('https://stellaops.dev/attestation/vex/v1', 'VEX Attestation', '1.0.0', 'stella-proof', 'DSSE-signed VEX statement attestation'),
|
||||||
|
('https://stellaops.dev/attestations/vex-override/v1', 'VEX Override', '1.0.0', 'stella-proof', 'Manual VEX override decision with justification'),
|
||||||
|
('https://stellaops.dev/predicates/trust-verdict@v1', 'Trust Verdict', '1.0.0', 'stella-proof', 'Trust lattice verdict combining P/C/R vectors'),
|
||||||
|
('https://stellaops.io/attestation/v1/signed-exception', 'Signed Exception', '1.0.0', 'stella-proof', 'Manually approved exception with expiry'),
|
||||||
|
('https://stellaops.dev/attestation/verification-report/v1', 'Verification Report', '1.0.0', 'stella-proof', 'QA verification report attestation')
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Seed: stella-delta predicates
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
INSERT INTO proofchain.predicate_type_registry (predicate_type_uri, display_name, version, category, description) VALUES
|
||||||
|
('stella.ops/changetrace@v1', 'Change Trace', '1.0.0', 'stella-delta', 'File-level change trace between SBOM versions'),
|
||||||
|
('stella.ops/vex-delta@v1', 'VEX Delta', '1.0.0', 'stella-delta', 'VEX statement differences between consecutive ingestions'),
|
||||||
|
('stella.ops/sbom-delta@v1', 'SBOM Delta', '1.0.0', 'stella-delta', 'Component differences between two SBOM versions'),
|
||||||
|
('stella.ops/verdict-delta@v1', 'Verdict Delta', '1.0.0', 'stella-delta', 'Verdict changes between policy evaluations'),
|
||||||
|
('stellaops.binarydiff.v1', 'Binary Diff', '1.0.0', 'stella-delta', 'Binary diff signatures for patch detection')
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Seed: ecosystem predicates
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
INSERT INTO proofchain.predicate_type_registry (predicate_type_uri, display_name, version, category, description) VALUES
|
||||||
|
('https://spdx.dev/Document', 'SPDX Document', '2.3.0', 'ecosystem', 'SPDX 2.x document attestation'),
|
||||||
|
('https://cyclonedx.org/bom', 'CycloneDX BOM', '1.7.0', 'ecosystem', 'CycloneDX BOM attestation'),
|
||||||
|
('https://slsa.dev/provenance', 'SLSA Provenance', '1.0.0', 'ecosystem', 'SLSA v1.0 build provenance')
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Seed: in-toto standard predicates
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
INSERT INTO proofchain.predicate_type_registry (predicate_type_uri, display_name, version, category, description) VALUES
|
||||||
|
('https://in-toto.io/Statement/v1', 'In-Toto Statement', '1.0.0', 'intoto', 'In-toto attestation statement wrapper'),
|
||||||
|
('https://in-toto.io/Link/v1', 'In-Toto Link', '1.0.0', 'intoto', 'In-toto supply chain link'),
|
||||||
|
('https://in-toto.io/Layout/v1', 'In-Toto Layout', '1.0.0', 'intoto', 'In-toto supply chain layout')
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
-- Migration 003: Artifact Canonical Record materialized view
|
||||||
|
-- Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
-- Purpose: Unified read projection joining sbom_entries + dsse_envelopes + rekor_entries
|
||||||
|
-- for the Evidence Thread API (GET /api/v1/evidence/thread/{canonical_id}).
|
||||||
|
|
||||||
|
-- Materialized view: one row per canonical_id with aggregated attestation evidence.
|
||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS proofchain.artifact_canonical_records AS
|
||||||
|
SELECT
|
||||||
|
se.bom_digest AS canonical_id,
|
||||||
|
'cyclonedx-jcs:1'::text AS format,
|
||||||
|
se.artifact_digest,
|
||||||
|
se.purl,
|
||||||
|
se.created_at,
|
||||||
|
COALESCE(
|
||||||
|
jsonb_agg(
|
||||||
|
DISTINCT jsonb_build_object(
|
||||||
|
'predicate_type', de.predicate_type,
|
||||||
|
'dsse_digest', de.body_hash,
|
||||||
|
'signer_keyid', de.signer_keyid,
|
||||||
|
'rekor_entry_id', re.uuid,
|
||||||
|
'rekor_tile', re.log_id,
|
||||||
|
'signed_at', de.signed_at
|
||||||
|
)
|
||||||
|
) FILTER (WHERE de.env_id IS NOT NULL),
|
||||||
|
'[]'::jsonb
|
||||||
|
) AS attestations
|
||||||
|
FROM proofchain.sbom_entries se
|
||||||
|
LEFT JOIN proofchain.dsse_envelopes de ON de.entry_id = se.entry_id
|
||||||
|
LEFT JOIN proofchain.rekor_entries re ON re.env_id = de.env_id
|
||||||
|
GROUP BY se.entry_id, se.bom_digest, se.artifact_digest, se.purl, se.created_at;
|
||||||
|
|
||||||
|
-- Unique index for CONCURRENTLY refresh and fast lookup.
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_acr_canonical_id
|
||||||
|
ON proofchain.artifact_canonical_records (canonical_id);
|
||||||
|
|
||||||
|
-- Index for PURL-based lookup (Evidence Thread by PURL).
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_acr_purl
|
||||||
|
ON proofchain.artifact_canonical_records (purl)
|
||||||
|
WHERE purl IS NOT NULL;
|
||||||
|
|
||||||
|
COMMENT ON MATERIALIZED VIEW proofchain.artifact_canonical_records IS
|
||||||
|
'Unified read projection for the Evidence Thread API. Joins SBOM entries, DSSE envelopes, and Rekor entries into one row per canonical_id. Refresh via REFRESH MATERIALIZED VIEW CONCURRENTLY.';
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using StellaOps.Attestor.Persistence.Repositories;
|
||||||
using System.Diagnostics.Metrics;
|
using System.Diagnostics.Metrics;
|
||||||
|
|
||||||
namespace StellaOps.Attestor.Persistence;
|
namespace StellaOps.Attestor.Persistence;
|
||||||
@@ -28,4 +29,18 @@ public static class PersistenceServiceCollectionExtensions
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the predicate type registry repository backed by PostgreSQL.
|
||||||
|
/// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
/// </summary>
|
||||||
|
public static IServiceCollection AddPredicateTypeRegistry(
|
||||||
|
this IServiceCollection services,
|
||||||
|
string connectionString)
|
||||||
|
{
|
||||||
|
services.TryAddSingleton<IPredicateTypeRegistryRepository>(
|
||||||
|
new PostgresPredicateTypeRegistryRepository(connectionString));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// IPredicateTypeRegistryRepository.cs
|
||||||
|
// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
// Task: PSR-02 - Create Predicate Schema Registry endpoints and repository
|
||||||
|
// Description: Repository interface for predicate type registry lookups and management
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace StellaOps.Attestor.Persistence.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repository for predicate type registry lookups and management.
|
||||||
|
/// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
/// </summary>
|
||||||
|
public interface IPredicateTypeRegistryRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists predicate type entries with optional filtering.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="category">Optional category filter.</param>
|
||||||
|
/// <param name="isActive">Optional active status filter.</param>
|
||||||
|
/// <param name="offset">Pagination offset.</param>
|
||||||
|
/// <param name="limit">Maximum entries to return.</param>
|
||||||
|
/// <param name="ct">Cancellation token.</param>
|
||||||
|
/// <returns>Matching entries ordered by category and URI.</returns>
|
||||||
|
Task<IReadOnlyList<PredicateTypeRegistryEntry>> ListAsync(
|
||||||
|
string? category = null,
|
||||||
|
bool? isActive = null,
|
||||||
|
int offset = 0,
|
||||||
|
int limit = 100,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a predicate type entry by its URI (latest version).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicateTypeUri">Canonical predicate type URI.</param>
|
||||||
|
/// <param name="ct">Cancellation token.</param>
|
||||||
|
/// <returns>The entry if found, null otherwise.</returns>
|
||||||
|
Task<PredicateTypeRegistryEntry?> GetByUriAsync(
|
||||||
|
string predicateTypeUri,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a new predicate type entry (upsert on URI+version conflict).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">The entry to register.</param>
|
||||||
|
/// <param name="ct">Cancellation token.</param>
|
||||||
|
/// <returns>The registered entry with generated fields populated.</returns>
|
||||||
|
Task<PredicateTypeRegistryEntry> RegisterAsync(
|
||||||
|
PredicateTypeRegistryEntry entry,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single entry in the predicate type registry.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record PredicateTypeRegistryEntry
|
||||||
|
{
|
||||||
|
/// <summary>Primary key (UUID).</summary>
|
||||||
|
public Guid RegistryId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Canonical URI for the predicate type.</summary>
|
||||||
|
public required string PredicateTypeUri { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Human-readable display name.</summary>
|
||||||
|
public required string DisplayName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Semver version string.</summary>
|
||||||
|
public string Version { get; init; } = "1.0.0";
|
||||||
|
|
||||||
|
/// <summary>Category: stella-core, stella-proof, stella-delta, ecosystem, intoto, custom.</summary>
|
||||||
|
public string Category { get; init; } = "stella-core";
|
||||||
|
|
||||||
|
/// <summary>Optional JSON Schema for payload validation.</summary>
|
||||||
|
public string? JsonSchema { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Optional human-readable description.</summary>
|
||||||
|
public string? Description { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Whether this predicate type is currently active.</summary>
|
||||||
|
public bool IsActive { get; init; } = true;
|
||||||
|
|
||||||
|
/// <summary>Validation mode: log-only, warn, or reject.</summary>
|
||||||
|
public string ValidationMode { get; init; } = "log-only";
|
||||||
|
|
||||||
|
/// <summary>Creation timestamp.</summary>
|
||||||
|
public DateTimeOffset CreatedAt { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Last update timestamp.</summary>
|
||||||
|
public DateTimeOffset UpdatedAt { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// PostgresPredicateTypeRegistryRepository.cs
|
||||||
|
// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
// Task: PSR-02 - Create Predicate Schema Registry endpoints and repository
|
||||||
|
// Description: PostgreSQL implementation of predicate type registry repository
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace StellaOps.Attestor.Persistence.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PostgreSQL-backed predicate type registry repository.
|
||||||
|
/// Sprint: SPRINT_20260219_010 (PSR-02)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PostgresPredicateTypeRegistryRepository : IPredicateTypeRegistryRepository
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new PostgreSQL predicate type registry repository.
|
||||||
|
/// </summary>
|
||||||
|
public PostgresPredicateTypeRegistryRepository(string connectionString)
|
||||||
|
{
|
||||||
|
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IReadOnlyList<PredicateTypeRegistryEntry>> ListAsync(
|
||||||
|
string? category = null,
|
||||||
|
bool? isActive = null,
|
||||||
|
int offset = 0,
|
||||||
|
int limit = 100,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
await using var conn = new NpgsqlConnection(_connectionString);
|
||||||
|
await conn.OpenAsync(ct);
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT registry_id, predicate_type_uri, display_name, version, category,
|
||||||
|
json_schema, description, is_active, validation_mode, created_at, updated_at
|
||||||
|
FROM proofchain.predicate_type_registry
|
||||||
|
WHERE (@category::text IS NULL OR category = @category)
|
||||||
|
AND (@is_active::boolean IS NULL OR is_active = @is_active)
|
||||||
|
ORDER BY category, predicate_type_uri
|
||||||
|
OFFSET @offset LIMIT @limit";
|
||||||
|
|
||||||
|
await using var cmd = new NpgsqlCommand(sql, conn);
|
||||||
|
cmd.Parameters.AddWithValue("category", (object?)category ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("is_active", isActive.HasValue ? isActive.Value : DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("offset", offset);
|
||||||
|
cmd.Parameters.AddWithValue("limit", limit);
|
||||||
|
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||||
|
var results = new List<PredicateTypeRegistryEntry>();
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(ct))
|
||||||
|
{
|
||||||
|
results.Add(MapEntry(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<PredicateTypeRegistryEntry?> GetByUriAsync(
|
||||||
|
string predicateTypeUri,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
await using var conn = new NpgsqlConnection(_connectionString);
|
||||||
|
await conn.OpenAsync(ct);
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT registry_id, predicate_type_uri, display_name, version, category,
|
||||||
|
json_schema, description, is_active, validation_mode, created_at, updated_at
|
||||||
|
FROM proofchain.predicate_type_registry
|
||||||
|
WHERE predicate_type_uri = @predicate_type_uri
|
||||||
|
ORDER BY version DESC
|
||||||
|
LIMIT 1";
|
||||||
|
|
||||||
|
await using var cmd = new NpgsqlCommand(sql, conn);
|
||||||
|
cmd.Parameters.AddWithValue("predicate_type_uri", predicateTypeUri);
|
||||||
|
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||||
|
if (await reader.ReadAsync(ct))
|
||||||
|
{
|
||||||
|
return MapEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<PredicateTypeRegistryEntry> RegisterAsync(
|
||||||
|
PredicateTypeRegistryEntry entry,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(entry);
|
||||||
|
|
||||||
|
await using var conn = new NpgsqlConnection(_connectionString);
|
||||||
|
await conn.OpenAsync(ct);
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
INSERT INTO proofchain.predicate_type_registry
|
||||||
|
(predicate_type_uri, display_name, version, category, json_schema, description, is_active, validation_mode)
|
||||||
|
VALUES (@predicate_type_uri, @display_name, @version, @category, @json_schema::jsonb, @description, @is_active, @validation_mode)
|
||||||
|
ON CONFLICT (predicate_type_uri, version) DO NOTHING
|
||||||
|
RETURNING registry_id, created_at, updated_at";
|
||||||
|
|
||||||
|
await using var cmd = new NpgsqlCommand(sql, conn);
|
||||||
|
cmd.Parameters.AddWithValue("predicate_type_uri", entry.PredicateTypeUri);
|
||||||
|
cmd.Parameters.AddWithValue("display_name", entry.DisplayName);
|
||||||
|
cmd.Parameters.AddWithValue("version", entry.Version);
|
||||||
|
cmd.Parameters.AddWithValue("category", entry.Category);
|
||||||
|
cmd.Parameters.AddWithValue("json_schema", (object?)entry.JsonSchema ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("description", (object?)entry.Description ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("is_active", entry.IsActive);
|
||||||
|
cmd.Parameters.AddWithValue("validation_mode", entry.ValidationMode);
|
||||||
|
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
||||||
|
if (await reader.ReadAsync(ct))
|
||||||
|
{
|
||||||
|
return entry with
|
||||||
|
{
|
||||||
|
RegistryId = reader.GetGuid(0),
|
||||||
|
CreatedAt = reader.GetDateTime(1),
|
||||||
|
UpdatedAt = reader.GetDateTime(2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict (already exists) - return existing
|
||||||
|
var existing = await GetByUriAsync(entry.PredicateTypeUri, ct);
|
||||||
|
return existing ?? entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PredicateTypeRegistryEntry MapEntry(NpgsqlDataReader reader)
|
||||||
|
{
|
||||||
|
return new PredicateTypeRegistryEntry
|
||||||
|
{
|
||||||
|
RegistryId = reader.GetGuid(0),
|
||||||
|
PredicateTypeUri = reader.GetString(1),
|
||||||
|
DisplayName = reader.GetString(2),
|
||||||
|
Version = reader.GetString(3),
|
||||||
|
Category = reader.GetString(4),
|
||||||
|
JsonSchema = reader.IsDBNull(5) ? null : reader.GetString(5),
|
||||||
|
Description = reader.IsDBNull(6) ? null : reader.GetString(6),
|
||||||
|
IsActive = reader.GetBoolean(7),
|
||||||
|
ValidationMode = reader.GetString(8),
|
||||||
|
CreatedAt = reader.GetDateTime(9),
|
||||||
|
UpdatedAt = reader.GetDateTime(10),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
namespace StellaOps.Attestor.ProofChain.Predicates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Predicate model for triage auto-suppress decisions.
|
||||||
|
/// Emitted when a runtime witness confirms a VEX not_affected consensus
|
||||||
|
/// with supporting unreachability evidence.
|
||||||
|
/// Sprint: SPRINT_20260219_012 (MWS-01)
|
||||||
|
/// </summary>
|
||||||
|
public sealed record TriageSuppressPredicate
|
||||||
|
{
|
||||||
|
public const string PredicateTypeUri = "stella.ops/triageSuppress@v1";
|
||||||
|
|
||||||
|
public required string CveId { get; init; }
|
||||||
|
|
||||||
|
public required string SuppressReason { get; init; }
|
||||||
|
|
||||||
|
public required VexConsensusRef VexConsensus { get; init; }
|
||||||
|
|
||||||
|
public required WitnessEvidenceRef WitnessEvidence { get; init; }
|
||||||
|
|
||||||
|
public required string ReachabilityState { get; init; }
|
||||||
|
|
||||||
|
public required DateTimeOffset Timestamp { get; init; }
|
||||||
|
|
||||||
|
public DeterministicReplayInputs? DeterministicReplayInputs { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record VexConsensusRef
|
||||||
|
{
|
||||||
|
public required string Status { get; init; }
|
||||||
|
|
||||||
|
public string? Justification { get; init; }
|
||||||
|
|
||||||
|
public required double ConfidenceScore { get; init; }
|
||||||
|
|
||||||
|
public required string ConsensusDigest { get; init; }
|
||||||
|
|
||||||
|
public int SourceCount { get; init; }
|
||||||
|
|
||||||
|
public required DateTimeOffset ComputedAt { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record WitnessEvidenceRef
|
||||||
|
{
|
||||||
|
public required string WitnessId { get; init; }
|
||||||
|
|
||||||
|
public required string DsseDigest { get; init; }
|
||||||
|
|
||||||
|
public required string ObservationType { get; init; }
|
||||||
|
|
||||||
|
public required string PredicateType { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record DeterministicReplayInputs
|
||||||
|
{
|
||||||
|
public required string CanonicalId { get; init; }
|
||||||
|
|
||||||
|
public required string VexConsensusDigest { get; init; }
|
||||||
|
|
||||||
|
public required string WitnessId { get; init; }
|
||||||
|
}
|
||||||
@@ -72,6 +72,9 @@ components:
|
|||||||
signals:read: Read Signals events and state.
|
signals:read: Read Signals events and state.
|
||||||
signals:write: Publish Signals events or mutate state.
|
signals:write: Publish Signals events or mutate state.
|
||||||
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
||||||
|
trust:admin: Administer trust and signing configuration.
|
||||||
|
trust:read: Read trust and signing state.
|
||||||
|
trust:write: Mutate trust and signing configuration.
|
||||||
ui.read: Read Console UX resources.
|
ui.read: Read Console UX resources.
|
||||||
vex:ingest: Submit VEX ingestion payloads.
|
vex:ingest: Submit VEX ingestion payloads.
|
||||||
vex:read: Read VEX ingestion data.
|
vex:read: Read VEX ingestion data.
|
||||||
@@ -127,6 +130,9 @@ components:
|
|||||||
signals:read: Read Signals events and state.
|
signals:read: Read Signals events and state.
|
||||||
signals:write: Publish Signals events or mutate state.
|
signals:write: Publish Signals events or mutate state.
|
||||||
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
||||||
|
trust:admin: Administer trust and signing configuration.
|
||||||
|
trust:read: Read trust and signing state.
|
||||||
|
trust:write: Mutate trust and signing configuration.
|
||||||
ui.read: Read Console UX resources.
|
ui.read: Read Console UX resources.
|
||||||
vex:ingest: Submit VEX ingestion payloads.
|
vex:ingest: Submit VEX ingestion payloads.
|
||||||
vex:read: Read VEX ingestion data.
|
vex:read: Read VEX ingestion data.
|
||||||
@@ -184,6 +190,9 @@ components:
|
|||||||
signals:read: Read Signals events and state.
|
signals:read: Read Signals events and state.
|
||||||
signals:write: Publish Signals events or mutate state.
|
signals:write: Publish Signals events or mutate state.
|
||||||
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
stellaops.bypass: Bypass trust boundary protections (restricted identities only).
|
||||||
|
trust:admin: Administer trust and signing configuration.
|
||||||
|
trust:read: Read trust and signing state.
|
||||||
|
trust:write: Mutate trust and signing configuration.
|
||||||
ui.read: Read Console UX resources.
|
ui.read: Read Console UX resources.
|
||||||
vex:ingest: Submit VEX ingestion payloads.
|
vex:ingest: Submit VEX ingestion payloads.
|
||||||
vex:read: Read VEX ingestion data.
|
vex:read: Read VEX ingestion data.
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ public class StellaOpsScopesTests
|
|||||||
[InlineData(StellaOpsScopes.EvidenceHold)]
|
[InlineData(StellaOpsScopes.EvidenceHold)]
|
||||||
[InlineData(StellaOpsScopes.AttestRead)]
|
[InlineData(StellaOpsScopes.AttestRead)]
|
||||||
[InlineData(StellaOpsScopes.ObservabilityIncident)]
|
[InlineData(StellaOpsScopes.ObservabilityIncident)]
|
||||||
|
[InlineData(StellaOpsScopes.TrustRead)]
|
||||||
|
[InlineData(StellaOpsScopes.TrustWrite)]
|
||||||
|
[InlineData(StellaOpsScopes.TrustAdmin)]
|
||||||
[InlineData(StellaOpsScopes.AuthorityTenantsRead)]
|
[InlineData(StellaOpsScopes.AuthorityTenantsRead)]
|
||||||
public void All_IncludesNewScopes(string scope)
|
public void All_IncludesNewScopes(string scope)
|
||||||
{
|
{
|
||||||
@@ -93,6 +96,7 @@ public class StellaOpsScopesTests
|
|||||||
[InlineData("Packs.Run", StellaOpsScopes.PacksRun)]
|
[InlineData("Packs.Run", StellaOpsScopes.PacksRun)]
|
||||||
[InlineData("Packs.Approve", StellaOpsScopes.PacksApprove)]
|
[InlineData("Packs.Approve", StellaOpsScopes.PacksApprove)]
|
||||||
[InlineData("Notify.Escalate", StellaOpsScopes.NotifyEscalate)]
|
[InlineData("Notify.Escalate", StellaOpsScopes.NotifyEscalate)]
|
||||||
|
[InlineData("TRUST:WRITE", StellaOpsScopes.TrustWrite)]
|
||||||
[InlineData("VULN:VIEW", StellaOpsScopes.VulnView)]
|
[InlineData("VULN:VIEW", StellaOpsScopes.VulnView)]
|
||||||
[InlineData("VULN:INVESTIGATE", StellaOpsScopes.VulnInvestigate)]
|
[InlineData("VULN:INVESTIGATE", StellaOpsScopes.VulnInvestigate)]
|
||||||
[InlineData("VULN:OPERATE", StellaOpsScopes.VulnOperate)]
|
[InlineData("VULN:OPERATE", StellaOpsScopes.VulnOperate)]
|
||||||
|
|||||||
@@ -442,6 +442,21 @@ public static class StellaOpsScopes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string UiAdmin = "ui.admin";
|
public const string UiAdmin = "ui.admin";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scope granting read-only access to trust and signing state.
|
||||||
|
/// </summary>
|
||||||
|
public const string TrustRead = "trust:read";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scope granting permission to mutate trust and signing configuration.
|
||||||
|
/// </summary>
|
||||||
|
public const string TrustWrite = "trust:write";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scope granting administrative control over trust and signing operations.
|
||||||
|
/// </summary>
|
||||||
|
public const string TrustAdmin = "trust:admin";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scope granting read-only access to Scanner scan results and metadata.
|
/// Scope granting read-only access to Scanner scan results and metadata.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
using HttpResults = Microsoft.AspNetCore.Http.Results;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||||
|
|
||||||
|
namespace StellaOps.Concelier.WebService.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advisory-source freshness endpoints used by UI v2 shell.
|
||||||
|
/// </summary>
|
||||||
|
internal static class AdvisorySourceEndpointExtensions
|
||||||
|
{
|
||||||
|
private const string AdvisoryReadPolicy = "Concelier.Advisories.Read";
|
||||||
|
|
||||||
|
public static void MapAdvisorySourceEndpoints(this WebApplication app)
|
||||||
|
{
|
||||||
|
var group = app.MapGroup("/api/v1/advisory-sources")
|
||||||
|
.WithTags("Advisory Sources");
|
||||||
|
|
||||||
|
group.MapGet(string.Empty, async (
|
||||||
|
HttpContext httpContext,
|
||||||
|
[FromQuery] bool includeDisabled,
|
||||||
|
[FromServices] IAdvisorySourceReadRepository readRepository,
|
||||||
|
TimeProvider timeProvider,
|
||||||
|
CancellationToken cancellationToken) =>
|
||||||
|
{
|
||||||
|
if (!TryGetTenant(httpContext, out _))
|
||||||
|
{
|
||||||
|
return HttpResults.BadRequest(new { error = "tenant_required", header = StellaOps.Concelier.WebService.Program.TenantHeaderName });
|
||||||
|
}
|
||||||
|
|
||||||
|
var records = await readRepository.ListAsync(includeDisabled, cancellationToken).ConfigureAwait(false);
|
||||||
|
var items = records.Select(MapListItem).ToList();
|
||||||
|
|
||||||
|
return HttpResults.Ok(new AdvisorySourceListResponse
|
||||||
|
{
|
||||||
|
Items = items,
|
||||||
|
TotalCount = items.Count,
|
||||||
|
DataAsOf = timeProvider.GetUtcNow()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.WithName("ListAdvisorySources")
|
||||||
|
.WithSummary("List advisory sources with freshness state")
|
||||||
|
.Produces<AdvisorySourceListResponse>(StatusCodes.Status200OK)
|
||||||
|
.RequireAuthorization(AdvisoryReadPolicy);
|
||||||
|
|
||||||
|
group.MapGet("/summary", async (
|
||||||
|
HttpContext httpContext,
|
||||||
|
[FromServices] IAdvisorySourceReadRepository readRepository,
|
||||||
|
TimeProvider timeProvider,
|
||||||
|
CancellationToken cancellationToken) =>
|
||||||
|
{
|
||||||
|
if (!TryGetTenant(httpContext, out _))
|
||||||
|
{
|
||||||
|
return HttpResults.BadRequest(new { error = "tenant_required", header = StellaOps.Concelier.WebService.Program.TenantHeaderName });
|
||||||
|
}
|
||||||
|
|
||||||
|
var records = await readRepository.ListAsync(includeDisabled: true, cancellationToken).ConfigureAwait(false);
|
||||||
|
var response = new AdvisorySourceSummaryResponse
|
||||||
|
{
|
||||||
|
TotalSources = records.Count,
|
||||||
|
HealthySources = records.Count(r => string.Equals(r.FreshnessStatus, "healthy", StringComparison.OrdinalIgnoreCase)),
|
||||||
|
WarningSources = records.Count(r => string.Equals(r.FreshnessStatus, "warning", StringComparison.OrdinalIgnoreCase)),
|
||||||
|
StaleSources = records.Count(r => string.Equals(r.FreshnessStatus, "stale", StringComparison.OrdinalIgnoreCase)),
|
||||||
|
UnavailableSources = records.Count(r => string.Equals(r.FreshnessStatus, "unavailable", StringComparison.OrdinalIgnoreCase)),
|
||||||
|
DisabledSources = records.Count(r => !r.Enabled),
|
||||||
|
ConflictingSources = 0,
|
||||||
|
DataAsOf = timeProvider.GetUtcNow()
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResults.Ok(response);
|
||||||
|
})
|
||||||
|
.WithName("GetAdvisorySourceSummary")
|
||||||
|
.WithSummary("Get advisory source summary cards")
|
||||||
|
.Produces<AdvisorySourceSummaryResponse>(StatusCodes.Status200OK)
|
||||||
|
.RequireAuthorization(AdvisoryReadPolicy);
|
||||||
|
|
||||||
|
group.MapGet("/{id}/freshness", async (
|
||||||
|
HttpContext httpContext,
|
||||||
|
string id,
|
||||||
|
[FromServices] IAdvisorySourceReadRepository readRepository,
|
||||||
|
[FromServices] ISourceRepository sourceRepository,
|
||||||
|
TimeProvider timeProvider,
|
||||||
|
CancellationToken cancellationToken) =>
|
||||||
|
{
|
||||||
|
if (!TryGetTenant(httpContext, out _))
|
||||||
|
{
|
||||||
|
return HttpResults.BadRequest(new { error = "tenant_required", header = StellaOps.Concelier.WebService.Program.TenantHeaderName });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
return HttpResults.BadRequest(new { error = "source_id_required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
id = id.Trim();
|
||||||
|
AdvisorySourceFreshnessRecord? record = null;
|
||||||
|
|
||||||
|
if (Guid.TryParse(id, out var sourceId))
|
||||||
|
{
|
||||||
|
record = await readRepository.GetBySourceIdAsync(sourceId, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var source = await sourceRepository.GetByKeyAsync(id, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (source is not null)
|
||||||
|
{
|
||||||
|
record = await readRepository.GetBySourceIdAsync(source.Id, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record is null)
|
||||||
|
{
|
||||||
|
return HttpResults.NotFound(new { error = "advisory_source_not_found", id });
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResults.Ok(new AdvisorySourceFreshnessResponse
|
||||||
|
{
|
||||||
|
Source = MapListItem(record),
|
||||||
|
LastSyncAt = record.LastSyncAt,
|
||||||
|
LastSuccessAt = record.LastSuccessAt,
|
||||||
|
LastError = record.LastError,
|
||||||
|
SyncCount = record.SyncCount,
|
||||||
|
ErrorCount = record.ErrorCount,
|
||||||
|
DataAsOf = timeProvider.GetUtcNow()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.WithName("GetAdvisorySourceFreshness")
|
||||||
|
.WithSummary("Get freshness details for one advisory source")
|
||||||
|
.Produces<AdvisorySourceFreshnessResponse>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.RequireAuthorization(AdvisoryReadPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetTenant(HttpContext httpContext, out string tenant)
|
||||||
|
{
|
||||||
|
tenant = string.Empty;
|
||||||
|
|
||||||
|
var claimTenant = httpContext.User?.FindFirst("tenant_id")?.Value;
|
||||||
|
if (!string.IsNullOrWhiteSpace(claimTenant))
|
||||||
|
{
|
||||||
|
tenant = claimTenant.Trim();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerTenant = httpContext.Request.Headers[StellaOps.Concelier.WebService.Program.TenantHeaderName].FirstOrDefault();
|
||||||
|
if (!string.IsNullOrWhiteSpace(headerTenant))
|
||||||
|
{
|
||||||
|
tenant = headerTenant.Trim();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AdvisorySourceListItem MapListItem(AdvisorySourceFreshnessRecord record)
|
||||||
|
{
|
||||||
|
return new AdvisorySourceListItem
|
||||||
|
{
|
||||||
|
SourceId = record.SourceId,
|
||||||
|
SourceKey = record.SourceKey,
|
||||||
|
SourceName = record.SourceName,
|
||||||
|
SourceFamily = record.SourceFamily,
|
||||||
|
SourceUrl = record.SourceUrl,
|
||||||
|
Priority = record.Priority,
|
||||||
|
Enabled = record.Enabled,
|
||||||
|
LastSyncAt = record.LastSyncAt,
|
||||||
|
LastSuccessAt = record.LastSuccessAt,
|
||||||
|
FreshnessAgeSeconds = record.FreshnessAgeSeconds,
|
||||||
|
FreshnessSlaSeconds = record.FreshnessSlaSeconds,
|
||||||
|
FreshnessStatus = record.FreshnessStatus,
|
||||||
|
SignatureStatus = record.SignatureStatus,
|
||||||
|
LastError = record.LastError,
|
||||||
|
SyncCount = record.SyncCount,
|
||||||
|
ErrorCount = record.ErrorCount,
|
||||||
|
TotalAdvisories = record.TotalAdvisories,
|
||||||
|
SignedAdvisories = record.SignedAdvisories,
|
||||||
|
UnsignedAdvisories = record.UnsignedAdvisories,
|
||||||
|
SignatureFailureCount = record.SignatureFailureCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AdvisorySourceListResponse
|
||||||
|
{
|
||||||
|
public IReadOnlyList<AdvisorySourceListItem> Items { get; init; } = [];
|
||||||
|
public int TotalCount { get; init; }
|
||||||
|
public DateTimeOffset DataAsOf { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AdvisorySourceListItem
|
||||||
|
{
|
||||||
|
public Guid SourceId { get; init; }
|
||||||
|
public string SourceKey { get; init; } = string.Empty;
|
||||||
|
public string SourceName { get; init; } = string.Empty;
|
||||||
|
public string SourceFamily { get; init; } = string.Empty;
|
||||||
|
public string? SourceUrl { get; init; }
|
||||||
|
public int Priority { get; init; }
|
||||||
|
public bool Enabled { get; init; }
|
||||||
|
public DateTimeOffset? LastSyncAt { get; init; }
|
||||||
|
public DateTimeOffset? LastSuccessAt { get; init; }
|
||||||
|
public long FreshnessAgeSeconds { get; init; }
|
||||||
|
public int FreshnessSlaSeconds { get; init; }
|
||||||
|
public string FreshnessStatus { get; init; } = "unknown";
|
||||||
|
public string SignatureStatus { get; init; } = "unsigned";
|
||||||
|
public string? LastError { get; init; }
|
||||||
|
public long SyncCount { get; init; }
|
||||||
|
public int ErrorCount { get; init; }
|
||||||
|
public long TotalAdvisories { get; init; }
|
||||||
|
public long SignedAdvisories { get; init; }
|
||||||
|
public long UnsignedAdvisories { get; init; }
|
||||||
|
public long SignatureFailureCount { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AdvisorySourceSummaryResponse
|
||||||
|
{
|
||||||
|
public int TotalSources { get; init; }
|
||||||
|
public int HealthySources { get; init; }
|
||||||
|
public int WarningSources { get; init; }
|
||||||
|
public int StaleSources { get; init; }
|
||||||
|
public int UnavailableSources { get; init; }
|
||||||
|
public int DisabledSources { get; init; }
|
||||||
|
public int ConflictingSources { get; init; }
|
||||||
|
public DateTimeOffset DataAsOf { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AdvisorySourceFreshnessResponse
|
||||||
|
{
|
||||||
|
public AdvisorySourceListItem Source { get; init; } = new();
|
||||||
|
public DateTimeOffset? LastSyncAt { get; init; }
|
||||||
|
public DateTimeOffset? LastSuccessAt { get; init; }
|
||||||
|
public string? LastError { get; init; }
|
||||||
|
public long SyncCount { get; init; }
|
||||||
|
public int ErrorCount { get; init; }
|
||||||
|
public DateTimeOffset DataAsOf { get; init; }
|
||||||
|
}
|
||||||
@@ -909,6 +909,7 @@ app.MapConcelierMirrorEndpoints(authorityConfigured, enforceAuthority);
|
|||||||
|
|
||||||
// Canonical advisory endpoints (Sprint 8200.0012.0003)
|
// Canonical advisory endpoints (Sprint 8200.0012.0003)
|
||||||
app.MapCanonicalAdvisoryEndpoints();
|
app.MapCanonicalAdvisoryEndpoints();
|
||||||
|
app.MapAdvisorySourceEndpoints();
|
||||||
app.MapInterestScoreEndpoints();
|
app.MapInterestScoreEndpoints();
|
||||||
|
|
||||||
// Federation endpoints for site-to-site bundle sync
|
// Federation endpoints for site-to-site bundle sync
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
|
|||||||
| AUDIT-0242-M | DONE | Revalidated 2026-01-07. |
|
| AUDIT-0242-M | DONE | Revalidated 2026-01-07. |
|
||||||
| AUDIT-0242-T | DONE | Revalidated 2026-01-07. |
|
| AUDIT-0242-T | DONE | Revalidated 2026-01-07. |
|
||||||
| AUDIT-0242-A | DONE | Applied 2026-01-13; TimeProvider defaults, ASCII cleanup, federation tests. |
|
| AUDIT-0242-A | DONE | Applied 2026-01-13; TimeProvider defaults, ASCII cleanup, federation tests. |
|
||||||
|
| BE8-07-API | DONE | Advisory-source freshness endpoint contract extended with advisory stats fields consumed by UI security diagnostics. |
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public static class ConcelierPersistenceExtensions
|
|||||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||||
|
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
|
||||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||||
@@ -87,6 +88,7 @@ public static class ConcelierPersistenceExtensions
|
|||||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||||
|
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
|
||||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
-- Concelier migration 004: advisory source freshness projection support
|
||||||
|
-- Sprint: SPRINT_20260219_008 (BE8-04)
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS vuln.source_freshness_sla (
|
||||||
|
source_id UUID PRIMARY KEY REFERENCES vuln.sources(id) ON DELETE CASCADE,
|
||||||
|
sla_seconds INT NOT NULL DEFAULT 21600 CHECK (sla_seconds > 0),
|
||||||
|
warning_ratio NUMERIC(4,2) NOT NULL DEFAULT 0.80 CHECK (warning_ratio > 0 AND warning_ratio < 1),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_by TEXT NOT NULL DEFAULT 'system'
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE vuln.source_freshness_sla IS
|
||||||
|
'Freshness SLA thresholds per advisory source for advisory-sources UI contracts.';
|
||||||
|
|
||||||
|
INSERT INTO vuln.source_freshness_sla (source_id)
|
||||||
|
SELECT s.id
|
||||||
|
FROM vuln.sources s
|
||||||
|
ON CONFLICT (source_id) DO NOTHING;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_source_states_last_success_at
|
||||||
|
ON vuln.source_states (last_success_at DESC)
|
||||||
|
WHERE last_success_at IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_source_states_last_sync_at
|
||||||
|
ON vuln.source_states (last_sync_at DESC)
|
||||||
|
WHERE last_sync_at IS NOT NULL;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS trg_source_freshness_sla_updated_at ON vuln.source_freshness_sla;
|
||||||
|
CREATE TRIGGER trg_source_freshness_sla_updated_at
|
||||||
|
BEFORE UPDATE ON vuln.source_freshness_sla
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION vuln.update_updated_at();
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
-- Concelier migration 005: advisory-source signature projection support
|
||||||
|
-- Sprint: SPRINT_20260219_008 (BE8-07)
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_advisories_source_key
|
||||||
|
ON vuln.advisories (source_id, advisory_key)
|
||||||
|
WHERE source_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_source_edge_source_advisory
|
||||||
|
ON vuln.advisory_source_edge (source_id, source_advisory_id);
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW vuln.advisory_source_signature_projection AS
|
||||||
|
WITH advisory_totals AS (
|
||||||
|
SELECT
|
||||||
|
a.source_id,
|
||||||
|
COUNT(*)::BIGINT AS total_advisories
|
||||||
|
FROM vuln.advisories a
|
||||||
|
WHERE a.source_id IS NOT NULL
|
||||||
|
GROUP BY a.source_id
|
||||||
|
),
|
||||||
|
signed_totals AS (
|
||||||
|
SELECT
|
||||||
|
a.source_id,
|
||||||
|
COUNT(*)::BIGINT AS signed_advisories
|
||||||
|
FROM vuln.advisories a
|
||||||
|
WHERE a.source_id IS NOT NULL
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM vuln.advisory_source_edge e
|
||||||
|
WHERE e.source_id = a.source_id
|
||||||
|
AND e.source_advisory_id = a.advisory_key
|
||||||
|
AND e.dsse_envelope IS NOT NULL
|
||||||
|
AND CASE
|
||||||
|
WHEN jsonb_typeof(e.dsse_envelope->'signatures') = 'array'
|
||||||
|
THEN jsonb_array_length(e.dsse_envelope->'signatures') > 0
|
||||||
|
ELSE FALSE
|
||||||
|
END
|
||||||
|
)
|
||||||
|
GROUP BY a.source_id
|
||||||
|
),
|
||||||
|
failure_totals AS (
|
||||||
|
SELECT
|
||||||
|
ss.source_id,
|
||||||
|
CASE
|
||||||
|
WHEN ss.metadata ? 'signature_failure_count'
|
||||||
|
AND (ss.metadata->>'signature_failure_count') ~ '^[0-9]+$'
|
||||||
|
THEN (ss.metadata->>'signature_failure_count')::BIGINT
|
||||||
|
ELSE 0::BIGINT
|
||||||
|
END AS signature_failure_count
|
||||||
|
FROM vuln.source_states ss
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
s.id AS source_id,
|
||||||
|
COALESCE(t.total_advisories, 0)::BIGINT AS total_advisories,
|
||||||
|
LEAST(
|
||||||
|
COALESCE(t.total_advisories, 0)::BIGINT,
|
||||||
|
COALESCE(st.signed_advisories, 0)::BIGINT
|
||||||
|
) AS signed_advisories,
|
||||||
|
GREATEST(
|
||||||
|
COALESCE(t.total_advisories, 0)::BIGINT
|
||||||
|
- LEAST(
|
||||||
|
COALESCE(t.total_advisories, 0)::BIGINT,
|
||||||
|
COALESCE(st.signed_advisories, 0)::BIGINT
|
||||||
|
),
|
||||||
|
0::BIGINT
|
||||||
|
) AS unsigned_advisories,
|
||||||
|
COALESCE(f.signature_failure_count, 0)::BIGINT AS signature_failure_count
|
||||||
|
FROM vuln.sources s
|
||||||
|
LEFT JOIN advisory_totals t ON t.source_id = s.id
|
||||||
|
LEFT JOIN signed_totals st ON st.source_id = s.id
|
||||||
|
LEFT JOIN failure_totals f ON f.source_id = s.id;
|
||||||
|
|
||||||
|
COMMENT ON VIEW vuln.advisory_source_signature_projection IS
|
||||||
|
'Per-source advisory totals and signature rollups for advisory-source detail diagnostics.';
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Npgsql;
|
||||||
|
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||||
|
|
||||||
|
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PostgreSQL-backed read model for advisory source freshness contracts.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AdvisorySourceReadRepository : RepositoryBase<ConcelierDataSource>, IAdvisorySourceReadRepository
|
||||||
|
{
|
||||||
|
private const string SystemTenantId = "_system";
|
||||||
|
|
||||||
|
public AdvisorySourceReadRepository(
|
||||||
|
ConcelierDataSource dataSource,
|
||||||
|
ILogger<AdvisorySourceReadRepository> logger)
|
||||||
|
: base(dataSource, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IReadOnlyList<AdvisorySourceFreshnessRecord>> ListAsync(
|
||||||
|
bool includeDisabled = false,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
const string sql = """
|
||||||
|
WITH source_projection AS (
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.key,
|
||||||
|
s.name,
|
||||||
|
s.source_type,
|
||||||
|
s.url,
|
||||||
|
s.priority,
|
||||||
|
s.enabled,
|
||||||
|
st.last_sync_at,
|
||||||
|
st.last_success_at,
|
||||||
|
st.last_error,
|
||||||
|
COALESCE(st.sync_count, 0) AS sync_count,
|
||||||
|
COALESCE(st.error_count, 0) AS error_count,
|
||||||
|
COALESCE(sla.sla_seconds, 21600) AS freshness_sla_seconds,
|
||||||
|
COALESCE(sla.warning_ratio, 0.80) AS warning_ratio,
|
||||||
|
COALESCE(s.metadata->>'signature_status', 'unsigned') AS signature_status,
|
||||||
|
COALESCE(sig.total_advisories, 0) AS total_advisories,
|
||||||
|
COALESCE(sig.signed_advisories, 0) AS signed_advisories,
|
||||||
|
COALESCE(sig.unsigned_advisories, 0) AS unsigned_advisories,
|
||||||
|
COALESCE(sig.signature_failure_count, 0) AS signature_failure_count
|
||||||
|
FROM vuln.sources s
|
||||||
|
LEFT JOIN vuln.source_states st ON st.source_id = s.id
|
||||||
|
LEFT JOIN vuln.source_freshness_sla sla ON sla.source_id = s.id
|
||||||
|
LEFT JOIN vuln.advisory_source_signature_projection sig ON sig.source_id = s.id
|
||||||
|
WHERE (@include_disabled OR s.enabled = TRUE)
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
source_type,
|
||||||
|
url,
|
||||||
|
priority,
|
||||||
|
enabled,
|
||||||
|
last_sync_at,
|
||||||
|
last_success_at,
|
||||||
|
last_error,
|
||||||
|
sync_count,
|
||||||
|
error_count,
|
||||||
|
freshness_sla_seconds,
|
||||||
|
warning_ratio,
|
||||||
|
CAST(
|
||||||
|
EXTRACT(EPOCH FROM (
|
||||||
|
NOW() - COALESCE(last_success_at, last_sync_at, NOW())
|
||||||
|
)) AS BIGINT) AS freshness_age_seconds,
|
||||||
|
CASE
|
||||||
|
WHEN last_success_at IS NULL THEN 'unavailable'
|
||||||
|
WHEN EXTRACT(EPOCH FROM (NOW() - last_success_at)) > freshness_sla_seconds THEN 'stale'
|
||||||
|
WHEN EXTRACT(EPOCH FROM (NOW() - last_success_at)) > (freshness_sla_seconds * warning_ratio) THEN 'warning'
|
||||||
|
ELSE 'healthy'
|
||||||
|
END AS freshness_status,
|
||||||
|
signature_status,
|
||||||
|
total_advisories,
|
||||||
|
signed_advisories,
|
||||||
|
unsigned_advisories,
|
||||||
|
signature_failure_count
|
||||||
|
FROM source_projection
|
||||||
|
ORDER BY enabled DESC, priority DESC, key
|
||||||
|
""";
|
||||||
|
|
||||||
|
return QueryAsync(
|
||||||
|
SystemTenantId,
|
||||||
|
sql,
|
||||||
|
cmd => AddParameter(cmd, "include_disabled", includeDisabled),
|
||||||
|
MapRecord,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<AdvisorySourceFreshnessRecord?> GetBySourceIdAsync(
|
||||||
|
Guid sourceId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
const string sql = """
|
||||||
|
WITH source_projection AS (
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.key,
|
||||||
|
s.name,
|
||||||
|
s.source_type,
|
||||||
|
s.url,
|
||||||
|
s.priority,
|
||||||
|
s.enabled,
|
||||||
|
st.last_sync_at,
|
||||||
|
st.last_success_at,
|
||||||
|
st.last_error,
|
||||||
|
COALESCE(st.sync_count, 0) AS sync_count,
|
||||||
|
COALESCE(st.error_count, 0) AS error_count,
|
||||||
|
COALESCE(sla.sla_seconds, 21600) AS freshness_sla_seconds,
|
||||||
|
COALESCE(sla.warning_ratio, 0.80) AS warning_ratio,
|
||||||
|
COALESCE(s.metadata->>'signature_status', 'unsigned') AS signature_status,
|
||||||
|
COALESCE(sig.total_advisories, 0) AS total_advisories,
|
||||||
|
COALESCE(sig.signed_advisories, 0) AS signed_advisories,
|
||||||
|
COALESCE(sig.unsigned_advisories, 0) AS unsigned_advisories,
|
||||||
|
COALESCE(sig.signature_failure_count, 0) AS signature_failure_count
|
||||||
|
FROM vuln.sources s
|
||||||
|
LEFT JOIN vuln.source_states st ON st.source_id = s.id
|
||||||
|
LEFT JOIN vuln.source_freshness_sla sla ON sla.source_id = s.id
|
||||||
|
LEFT JOIN vuln.advisory_source_signature_projection sig ON sig.source_id = s.id
|
||||||
|
WHERE s.id = @source_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
source_type,
|
||||||
|
url,
|
||||||
|
priority,
|
||||||
|
enabled,
|
||||||
|
last_sync_at,
|
||||||
|
last_success_at,
|
||||||
|
last_error,
|
||||||
|
sync_count,
|
||||||
|
error_count,
|
||||||
|
freshness_sla_seconds,
|
||||||
|
warning_ratio,
|
||||||
|
CAST(
|
||||||
|
EXTRACT(EPOCH FROM (
|
||||||
|
NOW() - COALESCE(last_success_at, last_sync_at, NOW())
|
||||||
|
)) AS BIGINT) AS freshness_age_seconds,
|
||||||
|
CASE
|
||||||
|
WHEN last_success_at IS NULL THEN 'unavailable'
|
||||||
|
WHEN EXTRACT(EPOCH FROM (NOW() - last_success_at)) > freshness_sla_seconds THEN 'stale'
|
||||||
|
WHEN EXTRACT(EPOCH FROM (NOW() - last_success_at)) > (freshness_sla_seconds * warning_ratio) THEN 'warning'
|
||||||
|
ELSE 'healthy'
|
||||||
|
END AS freshness_status,
|
||||||
|
signature_status,
|
||||||
|
total_advisories,
|
||||||
|
signed_advisories,
|
||||||
|
unsigned_advisories,
|
||||||
|
signature_failure_count
|
||||||
|
FROM source_projection
|
||||||
|
""";
|
||||||
|
|
||||||
|
return QuerySingleOrDefaultAsync(
|
||||||
|
SystemTenantId,
|
||||||
|
sql,
|
||||||
|
cmd => AddParameter(cmd, "source_id", sourceId),
|
||||||
|
MapRecord,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AdvisorySourceFreshnessRecord MapRecord(NpgsqlDataReader reader)
|
||||||
|
{
|
||||||
|
return new AdvisorySourceFreshnessRecord(
|
||||||
|
SourceId: reader.GetGuid(0),
|
||||||
|
SourceKey: reader.GetString(1),
|
||||||
|
SourceName: reader.GetString(2),
|
||||||
|
SourceFamily: reader.GetString(3),
|
||||||
|
SourceUrl: GetNullableString(reader, 4),
|
||||||
|
Priority: reader.GetInt32(5),
|
||||||
|
Enabled: reader.GetBoolean(6),
|
||||||
|
LastSyncAt: GetNullableDateTimeOffset(reader, 7),
|
||||||
|
LastSuccessAt: GetNullableDateTimeOffset(reader, 8),
|
||||||
|
LastError: GetNullableString(reader, 9),
|
||||||
|
SyncCount: reader.GetInt64(10),
|
||||||
|
ErrorCount: reader.GetInt32(11),
|
||||||
|
FreshnessSlaSeconds: reader.GetInt32(12),
|
||||||
|
WarningRatio: reader.GetDecimal(13),
|
||||||
|
FreshnessAgeSeconds: reader.GetInt64(14),
|
||||||
|
FreshnessStatus: reader.GetString(15),
|
||||||
|
SignatureStatus: reader.GetString(16),
|
||||||
|
TotalAdvisories: reader.GetInt64(17),
|
||||||
|
SignedAdvisories: reader.GetInt64(18),
|
||||||
|
UnsignedAdvisories: reader.GetInt64(19),
|
||||||
|
SignatureFailureCount: reader.GetInt64(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read-model repository for advisory source freshness surfaces.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAdvisorySourceReadRepository
|
||||||
|
{
|
||||||
|
Task<IReadOnlyList<AdvisorySourceFreshnessRecord>> ListAsync(
|
||||||
|
bool includeDisabled = false,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<AdvisorySourceFreshnessRecord?> GetBySourceIdAsync(
|
||||||
|
Guid sourceId,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AdvisorySourceFreshnessRecord(
|
||||||
|
Guid SourceId,
|
||||||
|
string SourceKey,
|
||||||
|
string SourceName,
|
||||||
|
string SourceFamily,
|
||||||
|
string? SourceUrl,
|
||||||
|
int Priority,
|
||||||
|
bool Enabled,
|
||||||
|
DateTimeOffset? LastSyncAt,
|
||||||
|
DateTimeOffset? LastSuccessAt,
|
||||||
|
string? LastError,
|
||||||
|
long SyncCount,
|
||||||
|
int ErrorCount,
|
||||||
|
int FreshnessSlaSeconds,
|
||||||
|
decimal WarningRatio,
|
||||||
|
long FreshnessAgeSeconds,
|
||||||
|
string FreshnessStatus,
|
||||||
|
string SignatureStatus,
|
||||||
|
long TotalAdvisories,
|
||||||
|
long SignedAdvisories,
|
||||||
|
long UnsignedAdvisories,
|
||||||
|
long SignatureFailureCount);
|
||||||
@@ -41,6 +41,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||||
|
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
|
||||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||||
@@ -90,6 +91,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||||
|
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
|
||||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
|
|||||||
| CICD-VAL-SMOKE-001 | DONE | Smoke validation: restore reference summaries from raw payload. |
|
| CICD-VAL-SMOKE-001 | DONE | Smoke validation: restore reference summaries from raw payload. |
|
||||||
| TASK-015-011 | DONE | Added enriched SBOM storage table + Postgres repository. |
|
| TASK-015-011 | DONE | Added enriched SBOM storage table + Postgres repository. |
|
||||||
| TASK-015-007d | DONE | Added license indexes and repository queries for license inventory. |
|
| TASK-015-007d | DONE | Added license indexes and repository queries for license inventory. |
|
||||||
|
| BE8-07 | DONE | Added migration `005_add_advisory_source_signature_projection.sql` and advisory-source signature stats projection fields for UI detail diagnostics. |
|
||||||
|
|||||||
@@ -0,0 +1,324 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using StellaOps.Concelier.Core.Jobs;
|
||||||
|
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||||
|
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||||
|
using StellaOps.Concelier.WebService.Extensions;
|
||||||
|
using StellaOps.Concelier.WebService.Options;
|
||||||
|
using StellaOps.TestKit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace StellaOps.Concelier.WebService.Tests;
|
||||||
|
|
||||||
|
public sealed class AdvisorySourceWebAppFactory : WebApplicationFactory<Program>
|
||||||
|
{
|
||||||
|
public AdvisorySourceWebAppFactory()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("CONCELIER__POSTGRESSTORAGE__CONNECTIONSTRING", "Host=localhost;Port=5432;Database=test-advisory-sources");
|
||||||
|
Environment.SetEnvironmentVariable("CONCELIER__POSTGRESSTORAGE__COMMANDTIMEOUTSECONDS", "30");
|
||||||
|
Environment.SetEnvironmentVariable("CONCELIER__TELEMETRY__ENABLED", "false");
|
||||||
|
Environment.SetEnvironmentVariable("CONCELIER_SKIP_OPTIONS_VALIDATION", "1");
|
||||||
|
Environment.SetEnvironmentVariable("CONCELIER_TEST_STORAGE_DSN", "Host=localhost;Port=5432;Database=test-advisory-sources");
|
||||||
|
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Testing");
|
||||||
|
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Testing");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||||
|
{
|
||||||
|
builder.ConfigureAppConfiguration((_, config) =>
|
||||||
|
{
|
||||||
|
var overrides = new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "PostgresStorage:ConnectionString", "Host=localhost;Port=5432;Database=test-advisory-sources" },
|
||||||
|
{ "PostgresStorage:CommandTimeoutSeconds", "30" },
|
||||||
|
{ "Telemetry:Enabled", "false" }
|
||||||
|
};
|
||||||
|
|
||||||
|
config.AddInMemoryCollection(overrides);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.UseEnvironment("Testing");
|
||||||
|
|
||||||
|
builder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = TestAuthHandler.SchemeName;
|
||||||
|
options.DefaultChallengeScheme = TestAuthHandler.SchemeName;
|
||||||
|
})
|
||||||
|
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(TestAuthHandler.SchemeName, static _ => { });
|
||||||
|
|
||||||
|
services.AddAuthorization(options =>
|
||||||
|
{
|
||||||
|
// Endpoint behavior in this test suite focuses on tenant/header/repository behavior.
|
||||||
|
// Authorization policy is exercised in dedicated auth coverage tests.
|
||||||
|
options.AddPolicy("Concelier.Advisories.Read", policy => policy.RequireAssertion(static _ => true));
|
||||||
|
});
|
||||||
|
|
||||||
|
services.RemoveAll<ILeaseStore>();
|
||||||
|
services.AddSingleton<ILeaseStore, Fixtures.TestLeaseStore>();
|
||||||
|
|
||||||
|
services.RemoveAll<IAdvisorySourceReadRepository>();
|
||||||
|
services.AddSingleton<IAdvisorySourceReadRepository, StubAdvisorySourceReadRepository>();
|
||||||
|
|
||||||
|
services.RemoveAll<ISourceRepository>();
|
||||||
|
services.AddSingleton<ISourceRepository, StubSourceRepository>();
|
||||||
|
|
||||||
|
services.AddSingleton<ConcelierOptions>(new ConcelierOptions
|
||||||
|
{
|
||||||
|
PostgresStorage = new ConcelierOptions.PostgresStorageOptions
|
||||||
|
{
|
||||||
|
ConnectionString = "Host=localhost;Port=5432;Database=test-advisory-sources",
|
||||||
|
CommandTimeoutSeconds = 30
|
||||||
|
},
|
||||||
|
Telemetry = new ConcelierOptions.TelemetryOptions
|
||||||
|
{
|
||||||
|
Enabled = false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<IConfigureOptions<ConcelierOptions>>(_ => new ConfigureOptions<ConcelierOptions>(opts =>
|
||||||
|
{
|
||||||
|
opts.PostgresStorage ??= new ConcelierOptions.PostgresStorageOptions();
|
||||||
|
opts.PostgresStorage.ConnectionString = "Host=localhost;Port=5432;Database=test-advisory-sources";
|
||||||
|
opts.PostgresStorage.CommandTimeoutSeconds = 30;
|
||||||
|
opts.Telemetry ??= new ConcelierOptions.TelemetryOptions();
|
||||||
|
opts.Telemetry.Enabled = false;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||||
|
{
|
||||||
|
public const string SchemeName = "AdvisorySourceTests";
|
||||||
|
|
||||||
|
public TestAuthHandler(
|
||||||
|
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||||
|
ILoggerFactory logger,
|
||||||
|
UrlEncoder encoder)
|
||||||
|
: base(options, logger, encoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||||
|
{
|
||||||
|
var claims = new[]
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, "advisory-source-tests")
|
||||||
|
};
|
||||||
|
|
||||||
|
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, SchemeName));
|
||||||
|
var ticket = new AuthenticationTicket(principal, SchemeName);
|
||||||
|
return Task.FromResult(AuthenticateResult.Success(ticket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class StubAdvisorySourceReadRepository : IAdvisorySourceReadRepository
|
||||||
|
{
|
||||||
|
private static readonly AdvisorySourceFreshnessRecord[] Records =
|
||||||
|
[
|
||||||
|
new(
|
||||||
|
SourceId: Guid.Parse("0e94e643-4045-4af8-b0c4-f4f04f769279"),
|
||||||
|
SourceKey: "nvd",
|
||||||
|
SourceName: "NVD",
|
||||||
|
SourceFamily: "nvd",
|
||||||
|
SourceUrl: "https://nvd.nist.gov",
|
||||||
|
Priority: 100,
|
||||||
|
Enabled: true,
|
||||||
|
LastSyncAt: DateTimeOffset.Parse("2026-02-19T08:11:00Z"),
|
||||||
|
LastSuccessAt: DateTimeOffset.Parse("2026-02-19T08:10:00Z"),
|
||||||
|
LastError: null,
|
||||||
|
SyncCount: 220,
|
||||||
|
ErrorCount: 1,
|
||||||
|
FreshnessSlaSeconds: 14400,
|
||||||
|
WarningRatio: 0.8m,
|
||||||
|
FreshnessAgeSeconds: 3600,
|
||||||
|
FreshnessStatus: "healthy",
|
||||||
|
SignatureStatus: "signed",
|
||||||
|
TotalAdvisories: 220,
|
||||||
|
SignedAdvisories: 215,
|
||||||
|
UnsignedAdvisories: 5,
|
||||||
|
SignatureFailureCount: 1),
|
||||||
|
new(
|
||||||
|
SourceId: Guid.Parse("fc9d6356-01d8-4012-8ce7-31e0f983f8c3"),
|
||||||
|
SourceKey: "ghsa",
|
||||||
|
SourceName: "GHSA",
|
||||||
|
SourceFamily: "ghsa",
|
||||||
|
SourceUrl: "https://github.com/advisories",
|
||||||
|
Priority: 80,
|
||||||
|
Enabled: false,
|
||||||
|
LastSyncAt: DateTimeOffset.Parse("2026-02-19T01:00:00Z"),
|
||||||
|
LastSuccessAt: DateTimeOffset.Parse("2026-02-18T20:30:00Z"),
|
||||||
|
LastError: "timeout",
|
||||||
|
SyncCount: 200,
|
||||||
|
ErrorCount: 8,
|
||||||
|
FreshnessSlaSeconds: 14400,
|
||||||
|
WarningRatio: 0.8m,
|
||||||
|
FreshnessAgeSeconds: 43200,
|
||||||
|
FreshnessStatus: "stale",
|
||||||
|
SignatureStatus: "unsigned",
|
||||||
|
TotalAdvisories: 200,
|
||||||
|
SignedAdvisories: 0,
|
||||||
|
UnsignedAdvisories: 200,
|
||||||
|
SignatureFailureCount: 0)
|
||||||
|
];
|
||||||
|
|
||||||
|
public Task<IReadOnlyList<AdvisorySourceFreshnessRecord>> ListAsync(
|
||||||
|
bool includeDisabled = false,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
IReadOnlyList<AdvisorySourceFreshnessRecord> items = includeDisabled
|
||||||
|
? Records
|
||||||
|
: Records.Where(static record => record.Enabled).ToList();
|
||||||
|
return Task.FromResult(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<AdvisorySourceFreshnessRecord?> GetBySourceIdAsync(
|
||||||
|
Guid sourceId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Records.FirstOrDefault(record => record.SourceId == sourceId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class StubSourceRepository : ISourceRepository
|
||||||
|
{
|
||||||
|
private static readonly IReadOnlyList<SourceEntity> Sources =
|
||||||
|
[
|
||||||
|
new SourceEntity
|
||||||
|
{
|
||||||
|
Id = Guid.Parse("0e94e643-4045-4af8-b0c4-f4f04f769279"),
|
||||||
|
Key = "nvd",
|
||||||
|
Name = "NVD",
|
||||||
|
SourceType = "nvd",
|
||||||
|
Url = "https://nvd.nist.gov",
|
||||||
|
Priority = 100,
|
||||||
|
Enabled = true,
|
||||||
|
CreatedAt = DateTimeOffset.Parse("2026-02-18T00:00:00Z"),
|
||||||
|
UpdatedAt = DateTimeOffset.Parse("2026-02-19T00:00:00Z")
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
public Task<SourceEntity> UpsertAsync(SourceEntity source, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(source);
|
||||||
|
|
||||||
|
public Task<SourceEntity?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(Sources.FirstOrDefault(source => source.Id == id));
|
||||||
|
|
||||||
|
public Task<SourceEntity?> GetByKeyAsync(string key, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(Sources.FirstOrDefault(source => string.Equals(source.Key, key, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
|
||||||
|
public Task<IReadOnlyList<SourceEntity>> ListAsync(bool? enabled = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var items = Sources
|
||||||
|
.Where(source => enabled is null || source.Enabled == enabled.Value)
|
||||||
|
.ToList();
|
||||||
|
return Task.FromResult<IReadOnlyList<SourceEntity>>(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class AdvisorySourceEndpointsTests : IClassFixture<AdvisorySourceWebAppFactory>
|
||||||
|
{
|
||||||
|
private readonly AdvisorySourceWebAppFactory _factory;
|
||||||
|
|
||||||
|
public AdvisorySourceEndpointsTests(AdvisorySourceWebAppFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task ListEndpoints_WithoutTenantHeader_ReturnsBadRequest()
|
||||||
|
{
|
||||||
|
using var client = _factory.CreateClient();
|
||||||
|
|
||||||
|
var response = await client.GetAsync("/api/v1/advisory-sources", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task ListEndpoints_WithTenantHeader_ReturnsRecords()
|
||||||
|
{
|
||||||
|
using var client = CreateTenantClient();
|
||||||
|
|
||||||
|
var response = await client.GetAsync("/api/v1/advisory-sources?includeDisabled=true", CancellationToken.None);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadFromJsonAsync<AdvisorySourceListResponse>(cancellationToken: CancellationToken.None);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.Equal(2, payload!.TotalCount);
|
||||||
|
Assert.Contains(payload.Items, static item => item.SourceKey == "nvd");
|
||||||
|
Assert.Contains(payload.Items, static item => item.SourceKey == "ghsa");
|
||||||
|
var nvd = payload.Items.Single(static item => item.SourceKey == "nvd");
|
||||||
|
Assert.Equal(220, nvd.TotalAdvisories);
|
||||||
|
Assert.Equal(215, nvd.SignedAdvisories);
|
||||||
|
Assert.Equal(5, nvd.UnsignedAdvisories);
|
||||||
|
Assert.Equal(1, nvd.SignatureFailureCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task SummaryEndpoint_ReturnsExpectedCounts()
|
||||||
|
{
|
||||||
|
using var client = CreateTenantClient();
|
||||||
|
|
||||||
|
var response = await client.GetAsync("/api/v1/advisory-sources/summary", CancellationToken.None);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadFromJsonAsync<AdvisorySourceSummaryResponse>(cancellationToken: CancellationToken.None);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.Equal(2, payload!.TotalSources);
|
||||||
|
Assert.Equal(1, payload.HealthySources);
|
||||||
|
Assert.Equal(1, payload.StaleSources);
|
||||||
|
Assert.Equal(1, payload.DisabledSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task FreshnessEndpoint_ByKey_ReturnsRecord()
|
||||||
|
{
|
||||||
|
using var client = CreateTenantClient();
|
||||||
|
|
||||||
|
var response = await client.GetAsync("/api/v1/advisory-sources/nvd/freshness", CancellationToken.None);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadFromJsonAsync<AdvisorySourceFreshnessResponse>(cancellationToken: CancellationToken.None);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.Equal("nvd", payload!.Source.SourceKey);
|
||||||
|
Assert.Equal("healthy", payload.Source.FreshnessStatus);
|
||||||
|
Assert.Equal(220, payload.Source.TotalAdvisories);
|
||||||
|
Assert.Equal(215, payload.Source.SignedAdvisories);
|
||||||
|
Assert.Equal(5, payload.Source.UnsignedAdvisories);
|
||||||
|
Assert.Equal(1, payload.Source.SignatureFailureCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task FreshnessEndpoint_UnknownSource_ReturnsNotFound()
|
||||||
|
{
|
||||||
|
using var client = CreateTenantClient();
|
||||||
|
|
||||||
|
var response = await client.GetAsync("/api/v1/advisory-sources/unknown-source/freshness", CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient CreateTenantClient()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.Add("X-Stella-Tenant", "tenant-a");
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,3 +8,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
|
|||||||
| AUDIT-0243-M | DONE | Revalidated 2026-01-07. |
|
| AUDIT-0243-M | DONE | Revalidated 2026-01-07. |
|
||||||
| AUDIT-0243-T | DONE | Revalidated 2026-01-07. |
|
| AUDIT-0243-T | DONE | Revalidated 2026-01-07. |
|
||||||
| AUDIT-0243-A | DONE | Waived (test project; revalidated 2026-01-07). |
|
| AUDIT-0243-A | DONE | Waived (test project; revalidated 2026-01-07). |
|
||||||
|
| BE8-07-TEST | DONE | Extended advisory-source endpoint coverage for total/signed/unsigned/signature-failure contract fields. |
|
||||||
|
|||||||
@@ -0,0 +1,234 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace StellaOps.EvidenceLocker.Api;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pack-driven Evidence & Audit adapter routes.
|
||||||
|
/// </summary>
|
||||||
|
public static class EvidenceAuditEndpoints
|
||||||
|
{
|
||||||
|
private static readonly DateTimeOffset SnapshotAt = DateTimeOffset.Parse("2026-02-19T03:15:00Z");
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<EvidencePackSummaryDto> Packs =
|
||||||
|
[
|
||||||
|
new EvidencePackSummaryDto("pack-9001", "rel-003", "us-prod", "1.2.4", "sealed", "2026-02-18T08:33:00Z"),
|
||||||
|
new EvidencePackSummaryDto("pack-9002", "rel-002", "us-uat", "1.3.0-rc1", "sealed", "2026-02-18T07:30:00Z"),
|
||||||
|
new EvidencePackSummaryDto("pack-9003", "rel-001", "eu-prod", "1.2.3", "sealed", "2026-02-17T08:30:00Z"),
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, EvidencePackDetailDto> PackDetails =
|
||||||
|
new Dictionary<string, EvidencePackDetailDto>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["pack-9001"] = new(
|
||||||
|
PackId: "pack-9001",
|
||||||
|
ReleaseId: "rel-003",
|
||||||
|
Environment: "us-prod",
|
||||||
|
BundleVersion: "1.2.4",
|
||||||
|
ManifestDigest: "sha256:beef000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
Decision: "pass_with_ack",
|
||||||
|
PromotionRunId: "run-7712",
|
||||||
|
Artifacts:
|
||||||
|
[
|
||||||
|
new EvidencePackArtifactDto("sbom", "spdx", "sha256:sbom-9001"),
|
||||||
|
new EvidencePackArtifactDto("findings", "json", "sha256:findings-9001"),
|
||||||
|
new EvidencePackArtifactDto("policy-decision", "dsse", "sha256:policy-9001"),
|
||||||
|
new EvidencePackArtifactDto("vex", "openvex", "sha256:vex-9001"),
|
||||||
|
],
|
||||||
|
ProofChainId: "chain-9912")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, ProofChainDetailDto> ProofsByDigest =
|
||||||
|
new Dictionary<string, ProofChainDetailDto>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["sha256:beef000000000000000000000000000000000000000000000000000000000003"] = new(
|
||||||
|
ChainId: "chain-9912",
|
||||||
|
SubjectDigest: "sha256:beef000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
Status: "valid",
|
||||||
|
DsseEnvelope: "dsse://pack-9001",
|
||||||
|
RekorEntry: "rekor://entry/9912",
|
||||||
|
VerifiedAt: "2026-02-19T03:10:00Z")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, CvssReceiptDto> CvssReceipts =
|
||||||
|
new Dictionary<string, CvssReceiptDto>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["CVE-2026-1234"] = new(
|
||||||
|
VulnerabilityId: "CVE-2026-1234",
|
||||||
|
CvssVector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||||
|
BaseScore: 9.8m,
|
||||||
|
ScoredAt: "2026-02-18T08:21:00Z",
|
||||||
|
Source: "nvd")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void MapEvidenceAuditEndpoints(this WebApplication app)
|
||||||
|
{
|
||||||
|
var group = app.MapGroup("/api/v1/evidence")
|
||||||
|
.WithTags("Evidence Audit");
|
||||||
|
|
||||||
|
group.MapGet(string.Empty, GetHome)
|
||||||
|
.WithName("GetEvidenceHome")
|
||||||
|
.WithSummary("Get evidence home summary and quick links.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
|
||||||
|
group.MapGet("/packs", ListPacks)
|
||||||
|
.WithName("ListEvidencePacks")
|
||||||
|
.WithSummary("List evidence packs.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
|
||||||
|
group.MapGet("/packs/{id}", GetPackDetail)
|
||||||
|
.WithName("GetEvidencePack")
|
||||||
|
.WithSummary("Get evidence pack detail.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
|
||||||
|
group.MapGet("/proofs/{subjectDigest}", GetProofChain)
|
||||||
|
.WithName("GetEvidenceProofChain")
|
||||||
|
.WithSummary("Get proof chain by subject digest.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
|
||||||
|
group.MapGet("/audit", ListAudit)
|
||||||
|
.WithName("ListEvidenceAuditLog")
|
||||||
|
.WithSummary("Get unified evidence audit log slice.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
|
||||||
|
group.MapGet("/receipts/cvss/{id}", GetCvssReceipt)
|
||||||
|
.WithName("GetCvssReceipt")
|
||||||
|
.WithSummary("Get CVSS receipt by vulnerability id.")
|
||||||
|
.RequireAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult GetHome()
|
||||||
|
{
|
||||||
|
var home = new EvidenceHomeDto(
|
||||||
|
GeneratedAt: SnapshotAt,
|
||||||
|
QuickStats: new EvidenceQuickStatsDto(
|
||||||
|
LatestPacks24h: 3,
|
||||||
|
SealedBundles7d: 5,
|
||||||
|
FailedVerifications7d: 1,
|
||||||
|
TrustAlerts30d: 1),
|
||||||
|
LatestPacks: Packs.OrderBy(item => item.PackId, StringComparer.Ordinal).Take(3).ToList(),
|
||||||
|
LatestBundles:
|
||||||
|
[
|
||||||
|
"bundle-2026-02-18-us-prod",
|
||||||
|
"bundle-2026-02-18-us-uat",
|
||||||
|
],
|
||||||
|
FailedVerifications:
|
||||||
|
[
|
||||||
|
"rr-002 (determinism mismatch)",
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Results.Ok(home);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult ListPacks()
|
||||||
|
{
|
||||||
|
var items = Packs
|
||||||
|
.OrderBy(item => item.PackId, StringComparer.Ordinal)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Results.Ok(new EvidencePackListResponseDto(items, items.Count, SnapshotAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult GetPackDetail(string id)
|
||||||
|
{
|
||||||
|
return PackDetails.TryGetValue(id, out var detail)
|
||||||
|
? Results.Ok(detail)
|
||||||
|
: Results.NotFound(new { error = "pack_not_found", id });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult GetProofChain(string subjectDigest)
|
||||||
|
{
|
||||||
|
return ProofsByDigest.TryGetValue(subjectDigest, out var proof)
|
||||||
|
? Results.Ok(proof)
|
||||||
|
: Results.NotFound(new { error = "proof_not_found", subjectDigest });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult ListAudit([FromQuery] int? limit = null)
|
||||||
|
{
|
||||||
|
var max = Math.Clamp(limit ?? 50, 1, 200);
|
||||||
|
var events = new[]
|
||||||
|
{
|
||||||
|
new EvidenceAuditEventDto("evt-3001", "export.created", "run-8811", "2026-02-18T08:40:00Z"),
|
||||||
|
new EvidenceAuditEventDto("evt-3002", "pack.sealed", "pack-9001", "2026-02-18T08:33:00Z"),
|
||||||
|
new EvidenceAuditEventDto("evt-3003", "trust.certificate-rotated", "issuer-registryca", "2026-02-18T07:10:00Z"),
|
||||||
|
}.OrderBy(eventRow => eventRow.EventId, StringComparer.Ordinal).Take(max).ToList();
|
||||||
|
|
||||||
|
return Results.Ok(new EvidenceAuditResponseDto(events, events.Count, SnapshotAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult GetCvssReceipt(string id)
|
||||||
|
{
|
||||||
|
return CvssReceipts.TryGetValue(id, out var receipt)
|
||||||
|
? Results.Ok(receipt)
|
||||||
|
: Results.NotFound(new { error = "cvss_receipt_not_found", id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record EvidenceHomeDto(
|
||||||
|
DateTimeOffset GeneratedAt,
|
||||||
|
EvidenceQuickStatsDto QuickStats,
|
||||||
|
IReadOnlyList<EvidencePackSummaryDto> LatestPacks,
|
||||||
|
IReadOnlyList<string> LatestBundles,
|
||||||
|
IReadOnlyList<string> FailedVerifications);
|
||||||
|
|
||||||
|
public sealed record EvidenceQuickStatsDto(
|
||||||
|
int LatestPacks24h,
|
||||||
|
int SealedBundles7d,
|
||||||
|
int FailedVerifications7d,
|
||||||
|
int TrustAlerts30d);
|
||||||
|
|
||||||
|
public sealed record EvidencePackListResponseDto(
|
||||||
|
IReadOnlyList<EvidencePackSummaryDto> Items,
|
||||||
|
int Total,
|
||||||
|
DateTimeOffset GeneratedAt);
|
||||||
|
|
||||||
|
public sealed record EvidencePackSummaryDto(
|
||||||
|
string PackId,
|
||||||
|
string ReleaseId,
|
||||||
|
string Environment,
|
||||||
|
string BundleVersion,
|
||||||
|
string Status,
|
||||||
|
string CreatedAt);
|
||||||
|
|
||||||
|
public sealed record EvidencePackDetailDto(
|
||||||
|
string PackId,
|
||||||
|
string ReleaseId,
|
||||||
|
string Environment,
|
||||||
|
string BundleVersion,
|
||||||
|
string ManifestDigest,
|
||||||
|
string Decision,
|
||||||
|
string PromotionRunId,
|
||||||
|
IReadOnlyList<EvidencePackArtifactDto> Artifacts,
|
||||||
|
string ProofChainId);
|
||||||
|
|
||||||
|
public sealed record EvidencePackArtifactDto(
|
||||||
|
string Kind,
|
||||||
|
string Format,
|
||||||
|
string Digest);
|
||||||
|
|
||||||
|
public sealed record ProofChainDetailDto(
|
||||||
|
string ChainId,
|
||||||
|
string SubjectDigest,
|
||||||
|
string Status,
|
||||||
|
string DsseEnvelope,
|
||||||
|
string RekorEntry,
|
||||||
|
string VerifiedAt);
|
||||||
|
|
||||||
|
public sealed record EvidenceAuditResponseDto(
|
||||||
|
IReadOnlyList<EvidenceAuditEventDto> Items,
|
||||||
|
int Total,
|
||||||
|
DateTimeOffset GeneratedAt);
|
||||||
|
|
||||||
|
public sealed record EvidenceAuditEventDto(
|
||||||
|
string EventId,
|
||||||
|
string EventType,
|
||||||
|
string Subject,
|
||||||
|
string OccurredAt);
|
||||||
|
|
||||||
|
public sealed record CvssReceiptDto(
|
||||||
|
string VulnerabilityId,
|
||||||
|
string CvssVector,
|
||||||
|
decimal BaseScore,
|
||||||
|
string ScoredAt,
|
||||||
|
string Source);
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace StellaOps.EvidenceLocker.Api;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Response for GET /api/v1/evidence/thread/{canonicalId}.
|
||||||
|
/// Represents the Artifact Canonical Record per docs/contracts/artifact-canonical-record-v1.md.
|
||||||
|
/// Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
/// </summary>
|
||||||
|
public sealed record GetEvidenceThreadResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("canonical_id")]
|
||||||
|
public required string CanonicalId { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("format")]
|
||||||
|
public required string Format { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("artifact_digest")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? ArtifactDigest { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("purl")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Purl { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("attestations")]
|
||||||
|
public required IReadOnlyList<EvidenceThreadAttestation> Attestations { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("transparency_status")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public TransparencyStatus? TransparencyStatus { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public required DateTimeOffset CreatedAt { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Individual attestation record within an evidence thread.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record EvidenceThreadAttestation
|
||||||
|
{
|
||||||
|
[JsonPropertyName("predicate_type")]
|
||||||
|
public required string PredicateType { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("dsse_digest")]
|
||||||
|
public required string DsseDigest { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("signer_keyid")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? SignerKeyId { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("rekor_entry_id")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? RekorEntryId { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("rekor_tile")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? RekorTile { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("signed_at")]
|
||||||
|
public required DateTimeOffset SignedAt { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transparency log status for offline/air-gapped deployments.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record TransparencyStatus
|
||||||
|
{
|
||||||
|
[JsonPropertyName("mode")]
|
||||||
|
public required string Mode { get; init; } // "online" | "offline"
|
||||||
|
|
||||||
|
[JsonPropertyName("reason")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Reason { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Response for GET /api/v1/evidence/thread?purl={purl} (PURL-based lookup).
|
||||||
|
/// </summary>
|
||||||
|
public sealed record ListEvidenceThreadsResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("threads")]
|
||||||
|
public required IReadOnlyList<EvidenceThreadSummary> Threads { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pagination")]
|
||||||
|
public required PaginationInfo Pagination { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Summary of an evidence thread (for list responses).
|
||||||
|
/// </summary>
|
||||||
|
public sealed record EvidenceThreadSummary
|
||||||
|
{
|
||||||
|
[JsonPropertyName("canonical_id")]
|
||||||
|
public required string CanonicalId { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("format")]
|
||||||
|
public required string Format { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("purl")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Purl { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("attestation_count")]
|
||||||
|
public required int AttestationCount { get; init; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public required DateTimeOffset CreatedAt { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using StellaOps.EvidenceLocker.Storage;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace StellaOps.EvidenceLocker.Api;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logging category for evidence thread endpoints.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class EvidenceThreadEndpointsLogger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimal API endpoints for the Evidence Thread API.
|
||||||
|
/// Returns Artifact Canonical Records per docs/contracts/artifact-canonical-record-v1.md.
|
||||||
|
/// Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
/// </summary>
|
||||||
|
public static class EvidenceThreadEndpoints
|
||||||
|
{
|
||||||
|
public static void MapEvidenceThreadEndpoints(this WebApplication app)
|
||||||
|
{
|
||||||
|
var group = app.MapGroup("/api/v1/evidence/thread")
|
||||||
|
.WithTags("Evidence Threads");
|
||||||
|
|
||||||
|
// GET /api/v1/evidence/thread/{canonicalId}
|
||||||
|
group.MapGet("/{canonicalId}", GetThreadByCanonicalIdAsync)
|
||||||
|
.WithName("GetEvidenceThread")
|
||||||
|
.WithSummary("Retrieve the evidence thread for an artifact by canonical_id")
|
||||||
|
.Produces<GetEvidenceThreadResponse>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status404NotFound)
|
||||||
|
.Produces(StatusCodes.Status500InternalServerError);
|
||||||
|
|
||||||
|
// GET /api/v1/evidence/thread?purl={purl}
|
||||||
|
group.MapGet("/", ListThreadsByPurlAsync)
|
||||||
|
.WithName("ListEvidenceThreads")
|
||||||
|
.WithSummary("List evidence threads matching a PURL")
|
||||||
|
.Produces<ListEvidenceThreadsResponse>(StatusCodes.Status200OK)
|
||||||
|
.Produces(StatusCodes.Status400BadRequest)
|
||||||
|
.Produces(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> GetThreadByCanonicalIdAsync(
|
||||||
|
string canonicalId,
|
||||||
|
[FromServices] IEvidenceThreadRepository repository,
|
||||||
|
[FromServices] ILogger<EvidenceThreadEndpointsLogger> logger,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
[FromQuery] bool include_attestations = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.LogInformation("Retrieving evidence thread for canonical_id {CanonicalId}", canonicalId);
|
||||||
|
|
||||||
|
var record = await repository.GetByCanonicalIdAsync(canonicalId, cancellationToken);
|
||||||
|
|
||||||
|
if (record is null)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Evidence thread not found for canonical_id {CanonicalId}", canonicalId);
|
||||||
|
return Results.NotFound(new { error = "Evidence thread not found", canonical_id = canonicalId });
|
||||||
|
}
|
||||||
|
|
||||||
|
var attestations = ParseAttestations(record.Attestations);
|
||||||
|
|
||||||
|
var response = new GetEvidenceThreadResponse
|
||||||
|
{
|
||||||
|
CanonicalId = record.CanonicalId,
|
||||||
|
Format = record.Format,
|
||||||
|
ArtifactDigest = record.ArtifactDigest,
|
||||||
|
Purl = record.Purl,
|
||||||
|
Attestations = include_attestations ? attestations : [],
|
||||||
|
CreatedAt = record.CreatedAt
|
||||||
|
};
|
||||||
|
|
||||||
|
return Results.Ok(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error retrieving evidence thread for canonical_id {CanonicalId}", canonicalId);
|
||||||
|
return Results.Problem(
|
||||||
|
title: "Internal server error",
|
||||||
|
detail: "Failed to retrieve evidence thread",
|
||||||
|
statusCode: StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> ListThreadsByPurlAsync(
|
||||||
|
[FromServices] IEvidenceThreadRepository repository,
|
||||||
|
[FromServices] ILogger<EvidenceThreadEndpointsLogger> logger,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
[FromQuery] string? purl = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(purl))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { error = "purl query parameter is required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Listing evidence threads for PURL {Purl}", purl);
|
||||||
|
|
||||||
|
var records = await repository.GetByPurlAsync(purl, cancellationToken);
|
||||||
|
|
||||||
|
var threads = records.Select(r =>
|
||||||
|
{
|
||||||
|
var attestations = ParseAttestations(r.Attestations);
|
||||||
|
return new EvidenceThreadSummary
|
||||||
|
{
|
||||||
|
CanonicalId = r.CanonicalId,
|
||||||
|
Format = r.Format,
|
||||||
|
Purl = r.Purl,
|
||||||
|
AttestationCount = attestations.Count,
|
||||||
|
CreatedAt = r.CreatedAt
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var response = new ListEvidenceThreadsResponse
|
||||||
|
{
|
||||||
|
Threads = threads,
|
||||||
|
Pagination = new PaginationInfo
|
||||||
|
{
|
||||||
|
Total = threads.Count,
|
||||||
|
Limit = 100,
|
||||||
|
Offset = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Results.Ok(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error listing evidence threads for PURL {Purl}", purl);
|
||||||
|
return Results.Problem(
|
||||||
|
title: "Internal server error",
|
||||||
|
detail: "Failed to list evidence threads",
|
||||||
|
statusCode: StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the JSONB attestations array from the materialized view into typed records.
|
||||||
|
/// </summary>
|
||||||
|
private static IReadOnlyList<EvidenceThreadAttestation> ParseAttestations(string attestationsJson)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(attestationsJson) || attestationsJson == "[]")
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(attestationsJson);
|
||||||
|
var results = new List<EvidenceThreadAttestation>();
|
||||||
|
|
||||||
|
foreach (var element in doc.RootElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
var predicateType = element.GetProperty("predicate_type").GetString();
|
||||||
|
var dsseDigest = element.GetProperty("dsse_digest").GetString();
|
||||||
|
var signedAtRaw = element.GetProperty("signed_at").GetString();
|
||||||
|
|
||||||
|
if (predicateType is null || dsseDigest is null || signedAtRaw is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Add(new EvidenceThreadAttestation
|
||||||
|
{
|
||||||
|
PredicateType = predicateType,
|
||||||
|
DsseDigest = dsseDigest,
|
||||||
|
SignerKeyId = element.TryGetProperty("signer_keyid", out var sk) ? sk.GetString() : null,
|
||||||
|
RekorEntryId = element.TryGetProperty("rekor_entry_id", out var re) ? re.GetString() : null,
|
||||||
|
RekorTile = element.TryGetProperty("rekor_tile", out var rt) ? rt.GetString() : null,
|
||||||
|
SignedAt = DateTimeOffset.Parse(signedAtRaw)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic ordering: signed_at ascending, then predicate_type ascending
|
||||||
|
return results
|
||||||
|
.OrderBy(a => a.SignedAt)
|
||||||
|
.ThenBy(a => a.PredicateType, StringComparer.Ordinal)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,6 +90,17 @@ public static class EvidenceLockerInfrastructureServiceCollectionExtensions
|
|||||||
logger);
|
logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Evidence Thread repository (Artifact Canonical Record API)
|
||||||
|
// Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
services.AddScoped<StellaOps.EvidenceLocker.Storage.IEvidenceThreadRepository>(provider =>
|
||||||
|
{
|
||||||
|
var options = provider.GetRequiredService<IOptions<EvidenceLockerOptions>>().Value;
|
||||||
|
var logger = provider.GetRequiredService<ILogger<StellaOps.EvidenceLocker.Storage.PostgresEvidenceThreadRepository>>();
|
||||||
|
return new StellaOps.EvidenceLocker.Storage.PostgresEvidenceThreadRepository(
|
||||||
|
options.Database.ConnectionString,
|
||||||
|
logger);
|
||||||
|
});
|
||||||
|
|
||||||
services.AddSingleton<NullEvidenceTimelinePublisher>();
|
services.AddSingleton<NullEvidenceTimelinePublisher>();
|
||||||
services.AddHttpClient<TimelineIndexerEvidenceTimelinePublisher>((provider, client) =>
|
services.AddHttpClient<TimelineIndexerEvidenceTimelinePublisher>((provider, client) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using StellaOps.Auth.Abstractions;
|
||||||
|
using StellaOps.EvidenceLocker.Api;
|
||||||
|
using StellaOps.TestKit;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace StellaOps.EvidenceLocker.Tests;
|
||||||
|
|
||||||
|
[Collection(EvidenceLockerTestCollection.Name)]
|
||||||
|
public sealed class EvidenceAuditEndpointsTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly EvidenceLockerWebApplicationFactory _factory;
|
||||||
|
private readonly HttpClient _client;
|
||||||
|
|
||||||
|
public EvidenceAuditEndpointsTests(EvidenceLockerWebApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
_factory.ResetTestState();
|
||||||
|
_client = factory.CreateClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task EvidenceHomeAndPacks_AreDeterministic()
|
||||||
|
{
|
||||||
|
ConfigureAuthHeaders(_client, Guid.NewGuid().ToString("D"), StellaOpsScopes.EvidenceRead);
|
||||||
|
|
||||||
|
var homeResponse = await _client.GetAsync("/api/v1/evidence", TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, homeResponse.StatusCode);
|
||||||
|
var home = await homeResponse.Content.ReadFromJsonAsync<EvidenceHomeDto>(TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(home);
|
||||||
|
Assert.True(home!.QuickStats.LatestPacks24h > 0);
|
||||||
|
|
||||||
|
var firstPacksResponse = await _client.GetAsync("/api/v1/evidence/packs", TestContext.Current.CancellationToken);
|
||||||
|
var secondPacksResponse = await _client.GetAsync("/api/v1/evidence/packs", TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, firstPacksResponse.StatusCode);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, secondPacksResponse.StatusCode);
|
||||||
|
|
||||||
|
var first = await firstPacksResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
|
||||||
|
var second = await secondPacksResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(first, second);
|
||||||
|
|
||||||
|
var payload = await firstPacksResponse.Content.ReadFromJsonAsync<EvidencePackListResponseDto>(TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.NotEmpty(payload!.Items);
|
||||||
|
Assert.Equal("pack-9001", payload.Items[0].PackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task EvidenceAuditRoutes_ReturnExpectedPayloads()
|
||||||
|
{
|
||||||
|
ConfigureAuthHeaders(_client, Guid.NewGuid().ToString("D"), StellaOpsScopes.EvidenceRead);
|
||||||
|
|
||||||
|
var packDetail = await _client.GetFromJsonAsync<EvidencePackDetailDto>(
|
||||||
|
"/api/v1/evidence/packs/pack-9001",
|
||||||
|
TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(packDetail);
|
||||||
|
Assert.Equal("chain-9912", packDetail!.ProofChainId);
|
||||||
|
|
||||||
|
var proof = await _client.GetFromJsonAsync<ProofChainDetailDto>(
|
||||||
|
"/api/v1/evidence/proofs/sha256:beef000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(proof);
|
||||||
|
Assert.Equal("valid", proof!.Status);
|
||||||
|
|
||||||
|
var audit = await _client.GetFromJsonAsync<EvidenceAuditResponseDto>(
|
||||||
|
"/api/v1/evidence/audit",
|
||||||
|
TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(audit);
|
||||||
|
Assert.True(audit!.Total >= 1);
|
||||||
|
|
||||||
|
var receipt = await _client.GetFromJsonAsync<CvssReceiptDto>(
|
||||||
|
"/api/v1/evidence/receipts/cvss/CVE-2026-1234",
|
||||||
|
TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(receipt);
|
||||||
|
Assert.Equal(9.8m, receipt!.BaseScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
[Fact]
|
||||||
|
public async Task EvidenceAuditRoutes_UnknownResources_ReturnNotFound()
|
||||||
|
{
|
||||||
|
ConfigureAuthHeaders(_client, Guid.NewGuid().ToString("D"), StellaOpsScopes.EvidenceRead);
|
||||||
|
|
||||||
|
var packResponse = await _client.GetAsync("/api/v1/evidence/packs/missing-pack", TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, packResponse.StatusCode);
|
||||||
|
|
||||||
|
var proofResponse = await _client.GetAsync("/api/v1/evidence/proofs/sha256:missing", TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, proofResponse.StatusCode);
|
||||||
|
|
||||||
|
var receiptResponse = await _client.GetAsync("/api/v1/evidence/receipts/cvss/CVE-0000-0000", TestContext.Current.CancellationToken);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, receiptResponse.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureAuthHeaders(HttpClient client, string tenantId, string scopes)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Clear();
|
||||||
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(EvidenceLockerTestAuthHandler.SchemeName);
|
||||||
|
client.DefaultRequestHeaders.Add("X-Test-Tenant", tenantId);
|
||||||
|
client.DefaultRequestHeaders.Add("X-Test-Scopes", scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_client.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -440,6 +440,12 @@ app.MapExportEndpoints();
|
|||||||
// Verdict attestation endpoints
|
// Verdict attestation endpoints
|
||||||
app.MapVerdictEndpoints();
|
app.MapVerdictEndpoints();
|
||||||
|
|
||||||
|
// Evidence & audit adapter endpoints (Pack v2)
|
||||||
|
app.MapEvidenceAuditEndpoints();
|
||||||
|
|
||||||
|
// Evidence Thread endpoints (Artifact Canonical Record API)
|
||||||
|
app.MapEvidenceThreadEndpoints();
|
||||||
|
|
||||||
// Refresh Router endpoint cache
|
// Refresh Router endpoint cache
|
||||||
app.TryRefreshStellaRouterEndpoints(routerOptions);
|
app.TryRefreshStellaRouterEndpoints(routerOptions);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace StellaOps.EvidenceLocker.Storage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repository for querying the Artifact Canonical Record materialized view.
|
||||||
|
/// Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
/// </summary>
|
||||||
|
public interface IEvidenceThreadRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an artifact canonical record by canonical_id (sha256 hex).
|
||||||
|
/// </summary>
|
||||||
|
Task<ArtifactCanonicalRecord?> GetByCanonicalIdAsync(
|
||||||
|
string canonicalId,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a PURL to artifact canonical records.
|
||||||
|
/// </summary>
|
||||||
|
Task<IReadOnlyList<ArtifactCanonicalRecord>> GetByPurlAsync(
|
||||||
|
string purl,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Row from proofchain.artifact_canonical_records materialized view.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record ArtifactCanonicalRecord
|
||||||
|
{
|
||||||
|
public required string CanonicalId { get; init; }
|
||||||
|
public required string Format { get; init; }
|
||||||
|
public string? ArtifactDigest { get; init; }
|
||||||
|
public string? Purl { get; init; }
|
||||||
|
public required DateTimeOffset CreatedAt { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aggregated attestations as JSONB string from the materialized view.
|
||||||
|
/// </summary>
|
||||||
|
public required string Attestations { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using Dapper;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace StellaOps.EvidenceLocker.Storage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PostgreSQL implementation of <see cref="IEvidenceThreadRepository"/>.
|
||||||
|
/// Reads from the proofchain.artifact_canonical_records materialized view.
|
||||||
|
/// Sprint: SPRINT_20260219_009 (CID-04)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PostgresEvidenceThreadRepository : IEvidenceThreadRepository
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
private readonly ILogger<PostgresEvidenceThreadRepository> _logger;
|
||||||
|
|
||||||
|
public PostgresEvidenceThreadRepository(
|
||||||
|
string connectionString,
|
||||||
|
ILogger<PostgresEvidenceThreadRepository> logger)
|
||||||
|
{
|
||||||
|
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ArtifactCanonicalRecord?> GetByCanonicalIdAsync(
|
||||||
|
string canonicalId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(canonicalId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Canonical ID cannot be null or whitespace.", nameof(canonicalId));
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
canonical_id AS CanonicalId,
|
||||||
|
format AS Format,
|
||||||
|
artifact_digest AS ArtifactDigest,
|
||||||
|
purl AS Purl,
|
||||||
|
created_at AS CreatedAt,
|
||||||
|
attestations::text AS Attestations
|
||||||
|
FROM proofchain.artifact_canonical_records
|
||||||
|
WHERE canonical_id = @CanonicalId;
|
||||||
|
";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var connection = new NpgsqlConnection(_connectionString);
|
||||||
|
await connection.OpenAsync(cancellationToken);
|
||||||
|
|
||||||
|
var record = await connection.QuerySingleOrDefaultAsync<ArtifactCanonicalRecord>(
|
||||||
|
new CommandDefinition(
|
||||||
|
sql,
|
||||||
|
new { CanonicalId = canonicalId },
|
||||||
|
cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to retrieve artifact canonical record for {CanonicalId}", canonicalId);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ArtifactCanonicalRecord>> GetByPurlAsync(
|
||||||
|
string purl,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(purl))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("PURL cannot be null or whitespace.", nameof(purl));
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
canonical_id AS CanonicalId,
|
||||||
|
format AS Format,
|
||||||
|
artifact_digest AS ArtifactDigest,
|
||||||
|
purl AS Purl,
|
||||||
|
created_at AS CreatedAt,
|
||||||
|
attestations::text AS Attestations
|
||||||
|
FROM proofchain.artifact_canonical_records
|
||||||
|
WHERE purl = @Purl
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 100;
|
||||||
|
";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var connection = new NpgsqlConnection(_connectionString);
|
||||||
|
await connection.OpenAsync(cancellationToken);
|
||||||
|
|
||||||
|
var results = await connection.QueryAsync<ArtifactCanonicalRecord>(
|
||||||
|
new CommandDefinition(
|
||||||
|
sql,
|
||||||
|
new { Purl = purl },
|
||||||
|
cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
return results.AsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to retrieve artifact canonical records for PURL {Purl}", purl);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,6 +121,18 @@ public static class IntegrationEndpoints
|
|||||||
.WithName("CheckIntegrationHealth")
|
.WithName("CheckIntegrationHealth")
|
||||||
.WithDescription("Performs a health check on an integration.");
|
.WithDescription("Performs a health check on an integration.");
|
||||||
|
|
||||||
|
// Impact map
|
||||||
|
group.MapGet("/{id:guid}/impact", async (
|
||||||
|
[FromServices] IntegrationService service,
|
||||||
|
Guid id,
|
||||||
|
CancellationToken cancellationToken) =>
|
||||||
|
{
|
||||||
|
var result = await service.GetImpactAsync(id, cancellationToken);
|
||||||
|
return result is null ? Results.NotFound() : Results.Ok(result);
|
||||||
|
})
|
||||||
|
.WithName("GetIntegrationImpact")
|
||||||
|
.WithDescription("Returns affected workflows and severity impact for an integration.");
|
||||||
|
|
||||||
// Get supported providers
|
// Get supported providers
|
||||||
group.MapGet("/providers", ([FromServices] IntegrationService service) =>
|
group.MapGet("/providers", ([FromServices] IntegrationService service) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -269,6 +269,31 @@ public sealed class IntegrationService
|
|||||||
result.Duration);
|
result.Duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IntegrationImpactResponse?> GetImpactAsync(Guid id, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var integration = await _repository.GetByIdAsync(id, cancellationToken);
|
||||||
|
if (integration is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var impactedWorkflows = BuildImpactedWorkflows(integration)
|
||||||
|
.OrderBy(workflow => workflow.Workflow, StringComparer.Ordinal)
|
||||||
|
.ToList();
|
||||||
|
var blockingCount = impactedWorkflows.Count(workflow => workflow.Blocking);
|
||||||
|
|
||||||
|
return new IntegrationImpactResponse(
|
||||||
|
IntegrationId: integration.Id,
|
||||||
|
IntegrationName: integration.Name,
|
||||||
|
Type: integration.Type,
|
||||||
|
Provider: integration.Provider,
|
||||||
|
Status: integration.Status,
|
||||||
|
Severity: DetermineSeverity(integration.Status, blockingCount),
|
||||||
|
BlockingWorkflowCount: blockingCount,
|
||||||
|
TotalWorkflowCount: impactedWorkflows.Count,
|
||||||
|
ImpactedWorkflows: impactedWorkflows);
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyList<ProviderInfo> GetSupportedProviders()
|
public IReadOnlyList<ProviderInfo> GetSupportedProviders()
|
||||||
{
|
{
|
||||||
return _pluginLoader.Plugins.Select(p => new ProviderInfo(
|
return _pluginLoader.Plugins.Select(p => new ProviderInfo(
|
||||||
@@ -277,6 +302,55 @@ public sealed class IntegrationService
|
|||||||
p.Provider)).ToList();
|
p.Provider)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<ImpactedWorkflow> BuildImpactedWorkflows(Integration integration)
|
||||||
|
{
|
||||||
|
var blockedByStatus = integration.Status is IntegrationStatus.Failed or IntegrationStatus.Disabled or IntegrationStatus.Archived;
|
||||||
|
return integration.Type switch
|
||||||
|
{
|
||||||
|
IntegrationType.Registry =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("bundle-materialization", "release-control", blockedByStatus, "Container digest fetch and verification path.", "restore-registry-connectivity"),
|
||||||
|
new ImpactedWorkflow("sbom-attachment", "evidence", blockedByStatus, "SBOM/image digest correlation during pack generation.", "re-run-bundle-sync"),
|
||||||
|
],
|
||||||
|
IntegrationType.Scm =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("bundle-changelog", "release-control", blockedByStatus, "Repository changelog enrichment for bundle versions.", "reconnect-scm-app"),
|
||||||
|
new ImpactedWorkflow("policy-drift-audit", "administration", blockedByStatus, "Policy governance change audit extraction.", "refresh-scm-access-token"),
|
||||||
|
],
|
||||||
|
IntegrationType.CiCd =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("promotion-preflight", "release-control", blockedByStatus, "Deployment signal and gate preflight signal stream.", "revalidate-ci-runner-credentials"),
|
||||||
|
new ImpactedWorkflow("ops-job-health", "platform-ops", blockedByStatus, "Pipeline lag/health insights for nightly report.", "replay-latest-ci-webhooks"),
|
||||||
|
],
|
||||||
|
IntegrationType.RepoSource =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("dependency-resolution", "security-risk", blockedByStatus, "Package advisory resolution and normalization.", "verify-repository-mirror"),
|
||||||
|
new ImpactedWorkflow("hot-lookup-projection", "security-risk", blockedByStatus, "Hot-lookup enrichment for findings explorer.", "resync-package-index"),
|
||||||
|
],
|
||||||
|
IntegrationType.RuntimeHost =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("runtime-reachability", "security-risk", blockedByStatus, "Runtime witness ingestion for reachability confidence.", "restart-runtime-agent"),
|
||||||
|
new ImpactedWorkflow("ops-confidence", "platform-ops", blockedByStatus, "Data-confidence score for approvals and dashboard.", "clear-runtime-dlq"),
|
||||||
|
],
|
||||||
|
IntegrationType.FeedMirror =>
|
||||||
|
[
|
||||||
|
new ImpactedWorkflow("advisory-freshness", "security-risk", blockedByStatus, "Advisory source freshness and conflict views.", "refresh-feed-mirror"),
|
||||||
|
new ImpactedWorkflow("rescore-pipeline", "platform-ops", blockedByStatus, "Nightly rescoring jobs and stale SBOM remediation.", "trigger-feed-replay"),
|
||||||
|
],
|
||||||
|
_ => []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DetermineSeverity(IntegrationStatus status, int blockingCount)
|
||||||
|
{
|
||||||
|
if (status is IntegrationStatus.Failed or IntegrationStatus.Disabled or IntegrationStatus.Archived)
|
||||||
|
{
|
||||||
|
return "high";
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockingCount > 0 ? "medium" : "low";
|
||||||
|
}
|
||||||
|
|
||||||
private static IntegrationConfig BuildConfig(Integration integration, string? resolvedSecret)
|
private static IntegrationConfig BuildConfig(Integration integration, string? resolvedSecret)
|
||||||
{
|
{
|
||||||
IReadOnlyDictionary<string, object>? extendedConfig = null;
|
IReadOnlyDictionary<string, object>? extendedConfig = null;
|
||||||
@@ -321,3 +395,21 @@ public sealed class IntegrationService
|
|||||||
/// Information about a supported provider.
|
/// Information about a supported provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record ProviderInfo(string Name, IntegrationType Type, IntegrationProvider Provider);
|
public sealed record ProviderInfo(string Name, IntegrationType Type, IntegrationProvider Provider);
|
||||||
|
|
||||||
|
public sealed record IntegrationImpactResponse(
|
||||||
|
Guid IntegrationId,
|
||||||
|
string IntegrationName,
|
||||||
|
IntegrationType Type,
|
||||||
|
IntegrationProvider Provider,
|
||||||
|
IntegrationStatus Status,
|
||||||
|
string Severity,
|
||||||
|
int BlockingWorkflowCount,
|
||||||
|
int TotalWorkflowCount,
|
||||||
|
IReadOnlyList<ImpactedWorkflow> ImpactedWorkflows);
|
||||||
|
|
||||||
|
public sealed record ImpactedWorkflow(
|
||||||
|
string Workflow,
|
||||||
|
string Domain,
|
||||||
|
bool Blocking,
|
||||||
|
string Impact,
|
||||||
|
string RecommendedAction);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user