Preserve live scope across evidence and registry flows

This commit is contained in:
master
2026-03-09 22:11:08 +02:00
parent dfd22281ed
commit 3ecafc49a3
7 changed files with 104 additions and 1 deletions

View File

@@ -0,0 +1,43 @@
# Sprint 20260309_015 - Live Scope Preservation Follow-ups
## Topic & Scope
- Repair user-visible scope loss uncovered by live Playwright after the runtime-fault rebuild.
- Keep tenant and region context stable when users move between evidence-thread and integration-admin actions.
- Working directory: `src/Web/StellaOps.Web/**`.
- Expected evidence: focused Angular specs, rebuilt web bundle, live Playwright changed-surfaces recheck.
## Dependencies & Concurrency
- Depends on `SPRINT_20260309_014_Platform_live_runtime_fault_repair.md` because the backend/runtime repair must be stable before UI scope regressions are meaningful.
- Safe parallelism: avoid unrelated search and shell work already in flight; stage only evidence-thread, registry-admin, Playwright harness, and sprint-doc files.
## Documentation Prerequisites
- `docs/modules/platform/architecture-overview.md`
- `docs/code-of-conduct/CODE_OF_CONDUCT.md`
- `docs/code-of-conduct/TESTING_PRACTICES.md`
## Delivery Tracker
### TASK-015-001 - Preserve scope on changed-surface action flows
Status: DONE
Dependency: none
Owners: QA, Developer
Task description:
- Fix changed-surface actions that drop tenant and region query scope during navigation, then tighten the live Playwright harness so the same regressions fail immediately.
Completion criteria:
- [x] Evidence thread "Back to Search" keeps active scope query params.
- [x] Registry admin tab navigation keeps active scope query params.
- [x] Focused Angular specs and live Playwright changed-surfaces verification pass.
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-03-09 | Sprint created after live changed-surfaces Playwright reported scoped actions navigating without tenant/region query preservation. | Codex |
| 2026-03-09 | Root causes confirmed in `EvidenceThreadViewComponent.onBack()` and registry-admin tab links. Added focused feature-spec coverage, rebuilt `dist/stellaops-web/browser`, synced the live `compose_console-dist` volume, and re-ran `live-frontdoor-changed-surfaces.mjs`; Playwright now records tenant/region-preserving URLs for `back-to-search` and `audit-tab`. | Codex |
## Decisions & Risks
- This sprint treats scope-preservation regressions as product defects even when the destination page still renders, because silent context loss breaks reproducibility and link sharing.
- Feature specs remain excluded from the default Angular test target to keep routine unit runs lightweight; targeted UI feature coverage for this slice is registered in `tsconfig.spec.features.json` and executed explicitly.
## Next Checkpoints
- Next defect cluster from the same live Playwright sweep: release-investigation `deploy-diff` still lands in a `Missing Parameters` state, and `change-trace` still renders with `No Change Trace Loaded`.

View File

@@ -83,6 +83,7 @@ const surfaceConfigs = [
selector: 'main button:has-text("Back to Search")',
expectedUrlPattern: '/evidence/threads',
expectedTextPattern: /evidence threads/i,
requiredUrlFragments: ['tenant=', 'regions='],
},
],
},
@@ -115,6 +116,7 @@ const surfaceConfigs = [
selector: 'a[href*="/registry-admin/audit"], button:has-text("Audit")',
expectedUrlPattern: '/registry-admin/audit',
expectedTextPattern: /audit/i,
requiredUrlFragments: ['tenant=', 'regions='],
},
],
},
@@ -389,7 +391,9 @@ async function verifySurfaceActions(context, surface) {
const bodyText = await collectBodyText(page);
const headingText = firstMatchingHeading(action.expectedTextPattern, headings);
const finalUrl = page.url();
const hasRequiredUrlFragments = (action.requiredUrlFragments ?? []).every((fragment) => finalUrl.includes(fragment));
const ok = finalUrl.includes(action.expectedUrlPattern)
&& hasRequiredUrlFragments
&& matchesPattern(action.expectedTextPattern, headings, bodyText);
results.push({

View File

@@ -89,6 +89,7 @@ describe('EvidenceThreadViewComponent', () => {
expect(router.navigate).toHaveBeenCalledWith(['/evidence/threads'], {
queryParams: { purl: 'pkg:oci/acme/api@sha256:abc123' },
queryParamsHandling: 'merge',
});
});

View File

@@ -87,7 +87,8 @@ export class EvidenceThreadViewComponent implements OnInit, OnDestroy {
onBack(): void {
const purl = this.returnPurl();
void this.router.navigate(['/evidence/threads'], {
queryParams: purl ? { purl } : {},
queryParams: purl ? { purl } : undefined,
queryParamsHandling: 'merge',
});
}

View File

@@ -0,0 +1,40 @@
import { TestBed } from '@angular/core/testing';
import { RouterLink, provideRouter } from '@angular/router';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { REGISTRY_ADMIN_API } from '../../core/api/registry-admin.client';
import { RegistryAdminComponent } from './registry-admin.component';
describe('RegistryAdminComponent', () => {
const registryAdminApiStub = {
listPlans: () => of([]),
};
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RegistryAdminComponent],
providers: [
provideRouter([]),
],
})
.overrideComponent(RegistryAdminComponent, {
set: {
providers: [{ provide: REGISTRY_ADMIN_API, useValue: registryAdminApiStub }],
},
})
.compileComponents();
});
it('merges the active query scope across registry admin tabs', () => {
const fixture = TestBed.createComponent(RegistryAdminComponent);
fixture.detectChanges();
const links = fixture.debugElement
.queryAll(By.directive(RouterLink))
.map((debugElement) => debugElement.injector.get(RouterLink));
expect(links.length).toBe(2);
expect(links.every((link) => link.queryParamsHandling === 'merge')).toBeTrue();
});
});

View File

@@ -49,6 +49,7 @@ type TabType = 'plans' | 'audit';
class="registry-admin__tab"
[class.registry-admin__tab--active]="activeTab() === 'plans'"
routerLink="plans"
queryParamsHandling="merge"
role="tab"
[attr.aria-selected]="activeTab() === 'plans'"
>
@@ -58,6 +59,7 @@ type TabType = 'plans' | 'audit';
class="registry-admin__tab"
[class.registry-admin__tab--active]="activeTab() === 'audit'"
routerLink="audit"
queryParamsHandling="merge"
role="tab"
[attr.aria-selected]="activeTab() === 'audit'"
>

View File

@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.spec.json",
"include": [],
"files": [
"src/test-setup.ts",
"src/app/core/api/first-signal.client.spec.ts",
"src/app/core/console/console-status.service.spec.ts",
"src/app/features/policy-simulation/simulation-dashboard.component.spec.ts",
"src/app/features/registry-admin/registry-admin.component.spec.ts",
"src/app/features/evidence-thread/__tests__/evidence-thread-view.component.spec.ts"
]
}