Gaps fill up, fixes, ui restructuring

This commit is contained in:
master
2026-02-19 22:10:54 +02:00
parent b5829dce5c
commit 04cacdca8a
331 changed files with 42859 additions and 2174 deletions

View 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

View 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

View 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

View File

@@ -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.

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View File

@@ -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`).

View 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 613)
- BE: `src/Integrations/__Libraries/StellaOps.Integrations.Core/IntegrationEnums.cs` (lines 631)
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 018021. 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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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).

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View 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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View 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`

View File

@@ -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 |
| **Quay.io** | Partial | Yes | Limited | Support varies by version and configuration |
| **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 |
| **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
- **Authentication**: Google Cloud service account or gcloud auth
- **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)
@@ -89,16 +92,18 @@ The OCI Distribution Spec v1.1 introduced the native referrers API (), which ena
- **Known Issues**:
- Harbor 1.x does not support referrers API
- 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
- **API Support**: Partial (version-dependent)
- **API Support**: Partial (version-dependent); Red Hat has announced full OCI Referrers API support on Quay.io
- **Fallback**: Yes
- **Authentication**: Robot account or OAuth token
- **Rate Limits**: Account tier dependent
- **Known Issues**:
- Support varies significantly by version
- 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
@@ -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
- 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
### Native Referrers API (OCI 1.1)

View File

@@ -26,15 +26,22 @@ This document captures the gap analysis between the competitive moat advisory an
| Feature | Moat | Current % | Key Gaps | Sprint Coverage |
|---------|------|-----------|----------|-----------------|
| Signed, replayable risk verdicts | 5 | 70% | OCI push, one-command replay | 4300_0001_* |
| VEX decisioning engine | 4 | 85% | Evidence hooks | Minimal |
| Reachability with proof | 4 | 75% | Standalone artifact | 4400_0001_0002 |
| Smart-Diff semantic delta | 4 | 80% | Signed delta verdict | 4400_0001_0001 |
| Unknowns as first-class state | 4 | 75% | Policy budgets, attestations | 4300_0002_* |
| Air-gapped epistemic mode | 4 | 70% | Sealed snapshot workflow | 4300_0003_0001 |
| SBOM ledger + lineage | 3 | 60% | Historical tracking, BYOS | 4600_0001_* |
| Policy engine with proofs | 3 | 85% | Compilation to artifact | Minimal |
| VEX distribution network | 3-4 | 30% | Hub layer entirely | 4500_0001_* |
| Signed, replayable risk verdicts | 5 | 85% | OCI push polish | 4300_0001_* |
| VEX decisioning engine | 4 | 90% | Evidence hooks polish | Minimal |
| Reachability with proof | 4 | 85% | Standalone artifact polish | 4400_0001_0002 |
| Smart-Diff semantic delta | 4 | 85% | Signed delta verdict | 4400_0001_0001 |
| Unknowns as first-class state | 4 | 80% | Policy budgets, attestations | 4300_0002_* |
| Air-gapped epistemic mode | 4 | 80% | Sealed snapshot workflow | 4300_0003_0001 |
| SBOM ledger + lineage | 3 | 70% | Historical tracking, BYOS | 4600_0001_* |
| Policy engine with proofs | 3 | 90% | Compilation to artifact | Minimal |
| 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
### 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
```
### 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
### 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
3. **Policy engine** — OPA/Rego with proof output is mature
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
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
- **Prisma**: Don't compete on CNAPP breadth; compete on decision integrity
- **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
---

View 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.01.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.01.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.*

View 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.*

View 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.01.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

View 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.

View 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/`

View File

@@ -1,45 +1,132 @@
# S00 Advisory Sources Specification
# S00 Advisory Sources Specification
Status: Draft (created for sprint planning pointer integrity)
Date: 2026-02-18
Status: Frozen (implemented backend contracts reconciled)
Date: 2026-02-19
Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-02`
## Purpose
Define `Security and Risk -> Advisory Sources` as the decision-impact view of advisory-source health.
## Ownership split
- `Integrations` owns source connector configuration, credentials, and connectivity checks.
- `Platform Ops` owns mirror/freshness operation workflows.
- `Security and Risk` owns advisory decision impact (gate relevance, risk confidence impact).
Define `Security and Risk -> Advisory Sources` as the decision-impact view of advisory-source health.
This is the security gating interpretation surface; operations on connectors/mirrors belong to other domains.
## 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
- Header: scope filters (region, env, source family, freshness severity).
- Summary cards: healthy sources, stale sources, unavailable sources, conflicting-source warnings.
- Source table columns:
- Source name
- Last successful ingest
- Freshness SLA
- Current freshness age
- Signature/trust status
- Impacted decisions count
- Impact severity
- Actions: open connector config, open mirror ops, open impacted findings/gates
- Detail panel:
- Source status timeline
- Conflict diagnostics
- Signed/unsigned advisory ratio
- Impacted release/approval/environment references
### Header
- Page title: `Advisory Sources`
- Scope filters: region, environment, source family (feed type), freshness severity.
- Quick stats bar: total sources, healthy count, stale count, unavailable count.
### Summary cards (4 cards)
- Healthy sources — count with trend.
- Stale sources — count with worst freshness age and SLA breach delta.
- Unavailable sources — count; includes sources with connectivity failure or mirror lag > threshold.
- Conflicting-source warnings — count of active advisory conflicts with unresolved triage status.
### Source table
Required columns:
| 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
- 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
- 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.
### Per-source states
## Contract notes
- This screen likely requires an aggregate endpoint composed from integrations + ops + security data.
- Initial classification expected: `MISSING_NEW` pending contract definition.
| State | Trigger | UI treatment |
| --- | --- | --- |
| 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).

View File

@@ -1,27 +1,50 @@
# S00 Endpoint Contract Ledger v1 (Starter)
# S00 Endpoint Contract Ledger v1
Status: Starter sheet
Instructions: replace placeholder values with discovered implementation reality.
Status: Frozen baseline (reconciled with backend implementation)
Date: 2026-02-19
Working directory: `docs/modules/ui/v2-rewire`
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 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 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` |
| 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 | 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` |
| 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 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` |
| 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` |
| 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 | 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` |
| 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` |
| 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` |
| 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` |
| 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` |
| 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` | `/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` | `/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` |
| 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` |
| 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/*` | `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, 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`, `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/*` | `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 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, 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 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.
- [ ] Verify one status class per row.
- [ ] Add rows for additional active-authority screens discovered during route audit.
- [ ] Link each `Action ticket` to a concrete sprint task.
Before readiness closure, frontend and backend leads must confirm:
- All previously `MISSING_NEW` rows are either shipped or formally deferred with owner/date.
- Any `EXISTS_ADAPT` rows (none at this revision) have backend team acknowledgment of planned schema delta.
- No active-authority screen remains unclassified.
Sign-off is captured in `S00_handoff_packet.md`.

View File

@@ -1,19 +1,64 @@
# S00 Handoff Packet
# S00 Handoff Packet
Status: Placeholder (created for sprint planning pointer integrity)
Date: 2026-02-18
Status: Published (reconciled to reopened 20260219 sprint wave)
Date: 2026-02-19
Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-07`
## Upstream artifacts
- `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`
## Purpose
## Downstream target sprints
- `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`
This packet is the authoritative handoff from sprint `20260218_005` (Spec Freeze) to implementation sprints.
All frozen decisions are referenced here.
## Current status
- This packet is a planning placeholder and will be expanded when sprint `20260218_005` reaches DONE.
Implementation execution for this handoff was the reopened sprint set:
- `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.

View File

@@ -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
Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-03`
## 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
- Root domains remain canonical.
- Shortcuts allowed for `Releases` and `Approvals` when they route to Release Control-owned routes.
- `Bundles`, `Deployments`, and `Regions and Environments` remain under Release Control navigation hierarchy.
Release Control-owned capabilities may be rendered as direct shortcuts in the sidebar if and only if:
1. Ownership is labeled as **Release Control** in breadcrumbs and page headers.
2. The canonical routes for those capabilities live under `/release-control/*`.
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
- Any shortcut route must render breadcrumb prefix `Release Control`.
- Header titles use canonical naming; optional compatibility labels may be temporary.
## Non-allowed model
- Dual ownership labels for same screen.
- Divergent mobile vs desktop ownership paths.
- Legacy settings-first entry as primary owner path.
Canonical format: `Root Domain > Capability > [Sub-page]`
## Route guidance
- Use alias redirects for historical direct paths.
- Canonical targets must live under final IA route families.
| Scenario | Breadcrumb | Notes |
| --- | --- | --- |
| 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.

View File

@@ -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
Working directory: `docs/modules/ui/v2-rewire`
Canonical source: `source-of-truth.md`, `authority-matrix.md`
## Purpose
Baseline mapping for legacy route families to canonical IA targets.
## Route action values
- `keep`
- `redirect`
- `alias`
- `remove-later`
Complete route baseline mapping current v1 canonical paths to v2 target IA families.
Every major route family must have exactly one migration action.
This map governs all implementation in sprints 006 through 016.
## Baseline mapping examples
| Legacy family | Canonical target family | Action |
## Route action definitions
| 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` |
| `/settings/security-data` | split to `/integrations/*` and `/security/*` contexts | `redirect` |
| `/integrations/*` legacy settings paths | `/integrations/*` canonical root | `alias` |
| historical trust routes | `/administration/trust*` | `redirect` |
| historical ops aliases | `/operations/*` canonical root | `alias` |
| `/home` | Already redirects to `/` | Sprint 016: confirm and remove from app.routes |
| `/orchestrator/*` | Already redirects to `/operations/*` → sprint 008 will update to `/platform-ops/*` | Sprint 016 |
| `/release-orchestrator/*` | Already redirects to root routes | Sprint 016 |
| `/ops/*` | Already redirects to `/operations/*` → sprint 008 will update | Sprint 016 |
| `/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
- Full detailed map is completed in sprint `20260218_005` task `R0-05`.
- Query and fragment preservation is required for redirect families.
## Section 9 — High-risk deep-link mitigation
| 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 |

View File

@@ -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
Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-04`
## 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
- `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
- Legacy trust routes redirect or alias to Administration trust pages.
- Evidence and Security pages must not host owner-duplicate trust management screens.
Two domains consume trust state without owning it:
## UX policy
- Trust actions (rotate, issuer management, cert lifecycle) remain in Administration.
- Consumer pages provide contextual links with preserved entity ids.
### Evidence & Audit (consumer)
- Displays trust indicators on proof chain, attestation, and evidence node views.
- 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
- Prevent duplicate owner surfaces.
- Ensure breadcrumbs and page headers always indicate Administration ownership.
### Security & Risk (consumer)
- Displays issuer/signature confidence as a decision context field in security findings, advisory sources, and approval tabs.
- 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`.

View 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/`.

View File

@@ -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.
**Last Updated:** 2025-12-20
**Next Review:** 2026-03-20
**Last Updated:** 2026-02-19
**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-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-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
@@ -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-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-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
@@ -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-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
@@ -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 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 |
| 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 |
---

View File

@@ -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 |
| **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 |
| **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
@@ -100,9 +100,9 @@ These comparisons focus on where release governance, evidence export, and audit
| Field | Value |
|-------|-------|
| **Last Updated** | 2026-01-03 |
| **Last Verified** | 2025-12-14 |
| **Next Review** | 2026-03-14 |
| **Last Updated** | 2026-02-19 |
| **Last Verified** | 2026-02-19 |
| **Next Review** | 2026-05-19 |
| **Claims Index** | [`docs/product/claims-citation-index.md`](claims-citation-index.md) |
| **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 |
| **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 |
| **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
@@ -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 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 |
| **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)
| 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 |
| 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 |
| Prisma | Yes | Limited | No | No | Strong | No attest/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 | |
| Microsoft Defender | Partial | Partial | No | No | Weak | No attest/reachability | |
| 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 | |
| Qualys | Limited | Limited | No | No | Medium | No attest/lattice | |
| 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 | |
## How to use this doc
@@ -281,4 +288,5 @@ This isn't a feature gap—it's a category difference. Retrofitting it requires:
## Sources
- 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`

View File

@@ -85,6 +85,11 @@ Use these in sales conversations, marketing materials, and internal alignment.
| Trust scoring of VEX sources | P1 | 4500_0001_0002 |
| Tier 4 binary fingerprinting | P1 | 7204-7206 |
| 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
@@ -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 |
| **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 |
| **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
@@ -108,11 +116,16 @@ Use these in sales conversations, marketing materials, and internal alignment.
### 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
3. **Signed reachability** — DSSE graphs + edge bundles; unique
4. **Deterministic replay** — Bit-for-bit reproducibility; 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. JFrog Evidence Collection centralizes evidence but can't replay verdicts
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)
@@ -159,4 +172,4 @@ stella scan --offline --image <digest>
---
**Last Updated**: 2026-01-03
**Last Updated**: 2026-02-19

View 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_006016.
**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).

View 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 |

View File

@@ -23,7 +23,7 @@ stella doctor --tag registry --format json --output registry-report.json
| Registry | Referrers API | Recommendation |
|----------|---------------|----------------|
| 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

View File

@@ -24,8 +24,9 @@ This runbook covers diagnosing and resolving OCI referrer discovery issues durin
| ECR | Yes | Yes | Requires proper IAM permissions |
| ACR | 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 |
| GitLab | No | Yes | Stores `subject` field but no referrers endpoint |
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`
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
**Symptoms:**

View File

@@ -19,9 +19,11 @@ using StellaOps.Attestor.Core.Storage;
using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Infrastructure;
using StellaOps.Attestor.Persistence;
using StellaOps.Attestor.ProofChain;
using StellaOps.Attestor.Spdx3;
using StellaOps.Attestor.Watchlist;
using StellaOps.Attestor.WebService.Endpoints;
using StellaOps.Attestor.WebService.Options;
using StellaOps.Auth.ServerIntegration;
using StellaOps.Configuration;
@@ -141,6 +143,13 @@ internal static class AttestorWebServiceComposition
builder.Services.AddAttestorInfrastructure();
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.IProofVerificationService, Services.ProofVerificationService>();
@@ -410,6 +419,7 @@ internal static class AttestorWebServiceComposition
app.MapControllers();
app.MapAttestorEndpoints(attestorOptions);
app.MapWatchlistEndpoints();
app.MapPredicateRegistryEndpoints();
app.TryRefreshStellaRouterEndpoints(routerOptions);
}

View File

@@ -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; }
}

View File

@@ -32,5 +32,6 @@
<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.Watchlist\StellaOps.Attestor.Watchlist.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Persistence\StellaOps.Attestor.Persistence.csproj" />
</ItemGroup>
</Project>

View File

@@ -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;

View File

@@ -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.';

View File

@@ -6,6 +6,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Attestor.Persistence.Repositories;
using System.Diagnostics.Metrics;
namespace StellaOps.Attestor.Persistence;
@@ -28,4 +29,18 @@ public static class PersistenceServiceCollectionExtensions
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;
}
}

View File

@@ -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; }
}

View File

@@ -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),
};
}
}

View File

@@ -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; }
}

View File

@@ -72,6 +72,9 @@ components:
signals:read: Read Signals events and state.
signals:write: Publish Signals events or mutate state.
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.
vex:ingest: Submit VEX ingestion payloads.
vex:read: Read VEX ingestion data.
@@ -127,6 +130,9 @@ components:
signals:read: Read Signals events and state.
signals:write: Publish Signals events or mutate state.
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.
vex:ingest: Submit VEX ingestion payloads.
vex:read: Read VEX ingestion data.
@@ -184,6 +190,9 @@ components:
signals:read: Read Signals events and state.
signals:write: Publish Signals events or mutate state.
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.
vex:ingest: Submit VEX ingestion payloads.
vex:read: Read VEX ingestion data.

View File

@@ -71,6 +71,9 @@ public class StellaOpsScopesTests
[InlineData(StellaOpsScopes.EvidenceHold)]
[InlineData(StellaOpsScopes.AttestRead)]
[InlineData(StellaOpsScopes.ObservabilityIncident)]
[InlineData(StellaOpsScopes.TrustRead)]
[InlineData(StellaOpsScopes.TrustWrite)]
[InlineData(StellaOpsScopes.TrustAdmin)]
[InlineData(StellaOpsScopes.AuthorityTenantsRead)]
public void All_IncludesNewScopes(string scope)
{
@@ -93,6 +96,7 @@ public class StellaOpsScopesTests
[InlineData("Packs.Run", StellaOpsScopes.PacksRun)]
[InlineData("Packs.Approve", StellaOpsScopes.PacksApprove)]
[InlineData("Notify.Escalate", StellaOpsScopes.NotifyEscalate)]
[InlineData("TRUST:WRITE", StellaOpsScopes.TrustWrite)]
[InlineData("VULN:VIEW", StellaOpsScopes.VulnView)]
[InlineData("VULN:INVESTIGATE", StellaOpsScopes.VulnInvestigate)]
[InlineData("VULN:OPERATE", StellaOpsScopes.VulnOperate)]

View File

@@ -442,6 +442,21 @@ public static class StellaOpsScopes
/// </summary>
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>
/// Scope granting read-only access to Scanner scan results and metadata.
/// </summary>

View File

@@ -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; }
}

View File

@@ -909,6 +909,7 @@ app.MapConcelierMirrorEndpoints(authorityConfigured, enforceAuthority);
// Canonical advisory endpoints (Sprint 8200.0012.0003)
app.MapCanonicalAdvisoryEndpoints();
app.MapAdvisorySourceEndpoints();
app.MapInterestScoreEndpoints();
// Federation endpoints for site-to-site bundle sync

View File

@@ -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-T | DONE | Revalidated 2026-01-07. |
| 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. |

View File

@@ -40,6 +40,7 @@ public static class ConcelierPersistenceExtensions
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
services.AddScoped<ISourceRepository, SourceRepository>();
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
@@ -87,6 +88,7 @@ public static class ConcelierPersistenceExtensions
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
services.AddScoped<ISourceRepository, SourceRepository>();
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();

View File

@@ -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();

View File

@@ -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.';

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -41,6 +41,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
services.AddScoped<ISourceRepository, SourceRepository>();
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
@@ -90,6 +91,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
services.AddScoped<ISourceRepository, SourceRepository>();
services.AddScoped<IAdvisorySourceReadRepository, AdvisorySourceReadRepository>();
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();

View File

@@ -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. |
| TASK-015-011 | DONE | Added enriched SBOM storage table + Postgres repository. |
| 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. |

View File

@@ -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;
}
}

View File

@@ -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-T | DONE | 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. |

View File

@@ -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);

View File

@@ -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; }
}

View File

@@ -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 [];
}
}
}

View File

@@ -90,6 +90,17 @@ public static class EvidenceLockerInfrastructureServiceCollectionExtensions
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.AddHttpClient<TimelineIndexerEvidenceTimelinePublisher>((provider, client) =>
{

View File

@@ -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();
}
}

View File

@@ -440,6 +440,12 @@ app.MapExportEndpoints();
// Verdict attestation endpoints
app.MapVerdictEndpoints();
// Evidence & audit adapter endpoints (Pack v2)
app.MapEvidenceAuditEndpoints();
// Evidence Thread endpoints (Artifact Canonical Record API)
app.MapEvidenceThreadEndpoints();
// Refresh Router endpoint cache
app.TryRefreshStellaRouterEndpoints(routerOptions);

View File

@@ -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; }
}

View File

@@ -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;
}
}
}

View File

@@ -121,6 +121,18 @@ public static class IntegrationEndpoints
.WithName("CheckIntegrationHealth")
.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
group.MapGet("/providers", ([FromServices] IntegrationService service) =>
{

View File

@@ -269,6 +269,31 @@ public sealed class IntegrationService
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()
{
return _pluginLoader.Plugins.Select(p => new ProviderInfo(
@@ -277,6 +302,55 @@ public sealed class IntegrationService
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)
{
IReadOnlyDictionary<string, object>? extendedConfig = null;
@@ -321,3 +395,21 @@ public sealed class IntegrationService
/// Information about a supported provider.
/// </summary>
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