22 KiB
Implementation Plan: Decision Register Evidence / OperationRun Link Polish
Branch: 307-decision-register-evidence-operationrun-link-polish | Date: 2026-05-15 | Spec: /specs/307-decision-register-evidence-operationrun-link-polish/spec.md
Input: Feature specification from /specs/307-decision-register-evidence-operationrun-link-polish/spec.md
Summary
Polish the existing Decision Register proof trail by adding truthful, scoped, read-only evidence/report and OperationRun link metadata to the existing register row projection and native Filament table. The implementation must reuse current FindingException / FindingExceptionDecision decision truth, current artifact truth (EvidenceSnapshot, StoredReport), current execution truth (OperationRun), and existing canonical route/link helpers. It must not add decision persistence, lifecycle actions, evidence payload storage, OperationRun lifecycle behavior, provider integration, or broad navigation changes.
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12.52, Filament 5.2.1, Livewire 4.1.4, Laravel Sail, Pest 4.3.1
Storage: PostgreSQL; no new table or migration expected
Testing: Pest feature/unit tests through Sail-first commands
Validation Lanes: fast-feedback, confidence, focused browser smoke
Target Platform: Laravel admin platform under apps/platform
Project Type: monorepo with Laravel platform app plus separate website; this feature is platform-only
Performance Goals: Keep register row derivation bounded to the already scoped workspace/environment query; avoid broad unscoped artifact/run lookups
Constraints: No fake links, no legacy /admin/t URLs, no evidence payload duplication, no lifecycle mutation from the register, no new assets unless repo conventions prove unavoidable
Scale/Scope: Narrow operator UI productization over the existing Decision Register page and builder
UI / Surface Guardrail Plan
- Guardrail scope: changed operator-facing Decision Register surface; possible minimal FindingException detail proof-label polish only if needed.
- Native vs custom classification summary: native Filament page/table/infolist patterns. Do not introduce custom CSS, local card systems, custom assets, or ad-hoc button systems.
- Shared-family relevance: proof/evidence viewers, stored report viewers, operation links, canonical navigation context, read-only governance register.
- State layers in scope: page, table row projection, URL-query context for existing detail handoff.
- Audience modes in scope: operator-MSP.
- Decision/diagnostic/raw hierarchy plan: decision-first register row; diagnostics and raw proof remain on existing detail/artifact/run pages.
- Raw/support gating plan: raw JSON, fingerprints, provider payload details, and unsupported metadata remain secondary on detail/artifact pages.
- One-primary-action / duplicate-truth control: existing row-to-FindingException detail handoff remains the primary inspect path. Proof and operation links are compact secondary affordances only when real and authorized.
- Handling modes by drift class or surface: report-only for unsupported/loose references; show truthful missing/unavailable state instead of inventing a URL.
- Repository-signal treatment: review-mandatory for any proposal to add a route, migration, asset, or new link resolver abstraction.
- Special surface test profiles: standard-native-filament plus global-context-shell for register routing and tenant/workspace context.
- Required tests or manual smoke: functional-core, state-contract, isolation, legacy route guard, manual browser smoke.
- Exception path and spread control: none planned.
- Active feature PR close-out entry: Guardrail / Smoke Coverage.
Shared Pattern & System Fit
- Cross-cutting feature marker: yes.
- Systems touched:
DecisionRegister,GovernanceDecisionRegisterBuilder,FindingExceptionResource,ViewFindingException,FindingExceptionEvidenceReference,EvidenceSnapshotResource,StoredReportResource,OperationRunLinks,OperationRunUrl,CanonicalNavigationContext, and existing governance/findings/evidence/operation tests. - Shared abstractions reused: existing builder, Filament resource
getUrl()methods,WorkspaceScopedTenantRoutes, tenant-owned record authorization,OperationRunLinks::tenantlessView()/OperationRunUrl, existing policies. - New abstraction introduced? why?: none planned. If implementation proves a helper is needed, it must be a small internal helper with a proportionality note and no new registry/framework.
- Why the existing abstraction was sufficient or insufficient: Existing resources and link helpers are sufficient for canonical destination URLs and access checks. The existing builder/page is insufficient because it exposes count/missing proof state but no safe link metadata.
- Bounded deviation / spread control: Any derived link metadata must remain read-only, local to the register projection, and backed by scoped queries from the current row.
OperationRun UX Impact
- Touches OperationRun start/completion/link UX?: yes, link-only.
- Central contract reused:
OperationRunLinksand, where locally established,App\Support\OpsUx\OperationRunUrl. - Delegated UX behaviors: run link, tenant/workspace-safe URL resolution.
- Surface-owned behavior kept local: compact secondary operation link display or truthful missing/unavailable state.
- Queued DB-notification policy: N/A.
- Terminal notification path: N/A.
- Exception path: none.
Provider Boundary & Portability Fit
- Shared provider/platform boundary touched?: no.
- Provider-owned seams: existing artifact/report payload pages only.
- Platform-core seams: Decision Register row projection, proof/evidence/report/operation wording, scope-safe route generation.
- Neutral platform terms / contracts preserved: proof, evidence, linked evidence, stored report, operation, workspace, environment.
- Retained provider-specific semantics and why: Existing artifact pages may display provider-specific details because they already own artifact truth. The register must not copy or summarize provider payload detail into platform-core decision truth.
- Bounded extraction or follow-up path: none.
Constitution Check
GATE: Must pass before implementation. Re-check after design and before close-out.
- Inventory-first: Pass. This feature links existing proof/artifact/run truth and does not change inventory semantics.
- Read/write separation: Pass. Register changes are read-only. Existing lifecycle actions remain on queue/detail surfaces with their current confirmation, authorization, and audit paths.
- Graph contract path: Pass. No provider/Graph calls are introduced.
- Deterministic capabilities: Pass. Existing capabilities remain the gate; new tests must prove rendering and destination denial.
- RBAC-UX: Pass if implementation preserves non-member/out-of-scope 404, member-missing-capability destination denial, and no UI-only authorization.
- Workspace isolation: Pass if links resolve only from the row's workspace and destination policies remain authoritative.
- Tenant/environment isolation: Pass if artifacts/runs are resolved only within the row's managed environment and no cross-environment URL is emitted.
- Run observability / OperationRun UX: Pass. This feature deep-links to existing runs only; it does not start, complete, dedupe, or mutate runs.
- Data minimization: Pass. No payloads are copied into the register row projection.
- TEST-GOV-001: Pass with focused unit, feature, isolation, link guard, lifecycle regression, and browser smoke coverage.
- PROP-001 / BLOAT-001: Pass. No new persisted truth, table, enum family, registry, provider seam, or UI taxonomy is planned.
- ABSTR-001 / LAYER-001: Pass if implementation stays in existing builder/page or a small local helper only when duplication or security need proves it.
- PERSIST-001 / STATE-001: Pass. Link states are presentation-only derived metadata.
- XCUT-001: Pass. Existing resource URL helpers, policies, and OperationRun link helpers are reused.
- PROV-001: Pass. Provider-specific semantics do not enter platform-core register logic.
- UI-FIL-001 / UI-HARD-001: Pass if native Filament actions/columns are used, the row has one primary inspect path, and lifecycle/destructive actions stay out of the register.
Test Governance Check
- Test purpose / classification by changed surface: Unit tests for builder projection; feature tests for Filament page rendering, authorization, canonical URLs, and boundary behavior; browser smoke for the final operator flow.
- Affected validation lanes: fast-feedback, confidence, browser.
- Why this lane mix is the narrowest sufficient proof: Builder unit tests catch projection mistakes cheaply. Feature tests prove URL/capability/scope behavior. Browser smoke catches Filament UI regressions in the actual table surface.
- Narrowest proving command(s): Use the focused commands listed in the Validation Commands section.
- Fixture / helper / factory / seed / context cost risks: Moderate. Tests need workspaces, managed environments, scoped users/capabilities, finding exceptions, evidence references, optional evidence snapshots/stored reports, and optional operation runs.
- Expensive defaults or shared helper growth introduced?: no. Any fixture helpers should remain feature-local or opt-in.
- Heavy-family additions, promotions, or visibility changes: none.
- Surface-class relief / special coverage rule: standard-native-filament and global-context-shell.
- Closing validation and reviewer handoff: Re-run focused Decision Register, FindingException lifecycle, evidence/artifact, and OperationRun link guard lanes; run
git diff --check; document browser smoke pass or reason not run. - Budget / baseline / trend follow-up: none.
- Review-stop questions: Are any links inferred from loose text? Did any URL contain
/admin/t? Did any cross-scope reference leak? Did any lifecycle action move into the register? Did any payload get duplicated? - Escalation path: split or reject if implementation needs persistence, a new route family, a link registry, or stored proof payloads.
- Active feature PR close-out entry: Guardrail / Smoke Coverage.
- Why no dedicated follow-up spec is needed: This is already the narrow proof-link follow-up selected by Spec 306; review-pack/customer-safe work remains separate follow-up candidates.
Project Structure
Documentation (this feature)
specs/307-decision-register-evidence-operationrun-link-polish/
├── checklists/
│ └── requirements.md
├── plan.md
├── spec.md
└── tasks.md
Future implementation paths to inspect/edit
apps/platform/app/Filament/Pages/Governance/DecisionRegister.php
apps/platform/resources/views/filament/pages/governance/decision-register.blade.php
apps/platform/app/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilder.php
apps/platform/app/Filament/Resources/FindingExceptionResource.php
apps/platform/app/Filament/Resources/FindingExceptionResource/Pages/ViewFindingException.php
apps/platform/app/Models/FindingException.php
apps/platform/app/Models/FindingExceptionDecision.php
apps/platform/app/Models/FindingExceptionEvidenceReference.php
apps/platform/app/Models/EvidenceSnapshot.php
apps/platform/app/Models/StoredReport.php
apps/platform/app/Models/OperationRun.php
apps/platform/app/Support/OperationRunLinks.php
apps/platform/app/Support/OpsUx/OperationRunUrl.php
apps/platform/tests/Unit/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilderTest.php
apps/platform/tests/Feature/Governance/DecisionRegisterPageTest.php
apps/platform/tests/Feature/Governance/DecisionRegisterAuthorizationTest.php
apps/platform/tests/Feature/Findings/FindingExceptionDecisionRegisterNavigationTest.php
apps/platform/tests/Feature/Findings/FindingExceptionDecisionRegisterBoundariesTest.php
apps/platform/tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactDeepLinkContractTest.php
apps/platform/tests/Feature/Monitoring/OperationsDashboardDrillthroughTest.php
apps/platform/tests/Feature/Operations/LegacyRunRoutesNotFoundTest.php
Structure Decision: Keep the feature within existing platform app structure. The prep phase updates only the spec directory. Runtime implementation, when requested later, should use the files above and avoid new base directories.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| None | N/A | N/A |
Proportionality Review
- Current operator problem: The register shows decisions but not enough direct proof/run affordance for confident operator inspection.
- Existing structure is insufficient because: Current proof display is count/missing-copy oriented and does not expose safe destination metadata for real proof artifacts or operation runs.
- Narrowest correct implementation: Add derived link metadata to the existing builder/page, with safe fallbacks to detail or missing states.
- Ownership cost created: Focused tests and small row/UI contract maintenance.
- Alternative intentionally rejected: New decision/proof tables, route families, payload storage, workflow engine, OperationRun lifecycle integration, and customer-safe export.
- Release truth: Current-release productization polish over already implemented Decision Register truth.
Phase 0: Repo Verification Findings
GovernanceDecisionRegisterBuilderalready scopes rows by workspace and visible managed environments, and returns register row fields without proof link metadata.DecisionRegisteralready renders a Filament table atgovernance/decisions, keeps detail handoff throughFindingExceptionResource::getUrl('view', ...), and shows proof count fromevidence_summary.reference_count.FindingExceptionEvidenceReferenceexists but usessource_typeand nullable stringsource_id; these are not guaranteed local foreign keys. Implementation must resolve only known, real, scoped artifact IDs and otherwise avoid direct links.EvidenceSnapshotResourceandStoredReportResourceprovide canonical existing views with destination authorization.EvidenceSnapshotcan referenceoperation_run_id.StoredReporthas no directoperation_run_idfield in the inspected model.OperationRunLinks/OperationRunUrlprovide canonical run URLs under workspace context; implementation must not build operation URLs manually.FindingExceptionResource,EvidenceSnapshotResource, andStoredReportResourceare not globally searchable, so Filament global-search Edit/View-page hard rule is not newly triggered.- Filament panel provider registration remains in
apps/platform/bootstrap/providers.php; this feature does not add a panel or provider.
Phase 1: Technical Approach
- Extend builder tests first for proof counts, no proof, single resolvable artifact, multiple references, stored report truth/no fake link, real/missing OperationRun links, and no legacy URLs.
- Extend
GovernanceDecisionRegisterBuilderto eager-load scoped evidence references only where needed and derive a compact proof/run metadata projection. - Resolve evidence/report links only from known real references:
- one same-scope
EvidenceSnapshotreference ->EvidenceSnapshotResource::getUrl('view', ...) - one same-scope
StoredReportreference ->StoredReportResource::getUrl('view', ...) - multiple references or unsupported source -> existing FindingException detail proof handoff or truthful unavailable/missing state
- one same-scope
- Resolve operation links only from real same-scope
OperationRunrelationships, such asEvidenceSnapshot.operation_run_id, or other repo-real run references discovered during implementation. - Render compact proof/run affordances in the existing Decision Register UI while preserving the existing primary detail handoff.
- Add feature/isolation tests for rendered links, missing states, legacy URL guard, cross-workspace denial, cross-environment denial, and read-only lifecycle ownership.
- Keep existing lifecycle/audit tests green.
Data Model / Contract Decisions
- No new table or migration is expected.
- No evidence/report payloads are copied into decision rows.
- Derived proof metadata may include:
proof_countproof_stateproof_labelproof_urlproof_url_labeloperation_run_stateoperation_run_urloperation_run_labelunavailable_reason
- Allowed proof states:
linked_evidence,linked_report,linked_detail_section,unavailable,not_linked. - Allowed operation states:
linked_run,run_not_available,not_linked. - URLs are nullable and present only when scoped, canonical, and access-safe.
Filament / Laravel Compliance Plan
- Livewire v4.0+ compliance: The project runs Livewire 4.1.4 with Filament 5.2.1. The implementation must not introduce Livewire v3 references or Filament v3/v4 APIs.
- Provider registration: No provider changes are planned. Laravel 12 panel providers remain registered in
apps/platform/bootstrap/providers.php, notbootstrap/app.php. - Global search: No globally searchable resource is added. Existing relevant resources are globally searchable disabled, so no new Edit/View-page global-search requirement is introduced.
- Destructive actions: No destructive or lifecycle actions are added to the register. Existing FindingException detail actions retain their current confirmation and authorization behavior.
- Asset strategy: No new assets are expected. If registered assets become unavoidable, deployment must include
cd apps/platform && php artisan filament:assets; otherwise this feature remains asset-neutral. - Testing plan: Test builder metadata, Filament page rendering, action/link visibility, destination authorization, cross-workspace/environment denial, lifecycle action absence, and browser smoke.
Validation Commands
Primary Decision Register lane:
cd apps/platform && ./vendor/bin/sail artisan test --compact \
tests/Unit/Support/GovernanceDecisions/GovernanceDecisionRegisterBuilderTest.php \
tests/Unit/Support/GovernanceInbox/GovernanceInboxSectionBuilderTest.php \
tests/Feature/Governance/DecisionRegisterPageTest.php \
tests/Feature/Governance/DecisionRegisterAuthorizationTest.php \
tests/Feature/Governance/GovernanceInboxPageTest.php \
tests/Feature/Governance/GovernanceInboxAuthorizationTest.php \
tests/Feature/Findings/FindingExceptionDecisionRegisterNavigationTest.php \
tests/Feature/Findings/FindingExceptionDetailDecisionSummaryTest.php \
tests/Feature/Findings/FindingExceptionDecisionRegisterBoundariesTest.php
FindingException lifecycle/audit lane:
cd apps/platform && ./vendor/bin/sail artisan test --compact \
tests/Unit/Findings/FindingExceptionDecisionTest.php \
tests/Feature/Findings/FindingExceptionWorkflowTest.php \
tests/Feature/Findings/FindingExceptionRenewalTest.php \
tests/Feature/Findings/FindingExceptionRevocationTest.php
Evidence/review/artifact lane:
cd apps/platform && ./vendor/bin/sail artisan test --compact \
tests/Feature/Evidence/EvidenceSnapshotResourceTest.php \
tests/Feature/Evidence/EvidenceSnapshotAuditLogTest.php \
tests/Feature/EnvironmentReview/EnvironmentReviewAuditLogTest.php \
tests/Feature/EnvironmentReview/EnvironmentReviewRegisterTest.php \
tests/Feature/EnvironmentReview/EnvironmentReviewRegisterRbacTest.php \
tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php \
tests/Feature/Reviews/CustomerReviewWorkspaceAuthorizationTest.php \
tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php \
tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php \
tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactDeepLinkContractTest.php
OperationRun/link guard lane:
cd apps/platform && ./vendor/bin/sail artisan test --compact \
tests/Feature/Monitoring/OperationsDashboardDrillthroughTest.php \
tests/Feature/Operations/LegacyRunRoutesNotFoundTest.php \
tests/Feature/ProviderConnections/LegacyRedirectTest.php \
tests/Feature/RequiredPermissions/RequiredPermissionsLegacyRouteTest.php \
tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php \
tests/Feature/Filament/PolicyVersionResolvedReferenceLinksTest.php
Whitespace:
git diff --check
Browser smoke:
- Open Governance > Decisions.
- Confirm rows render.
- Confirm a decision with proof shows a proof affordance.
- Confirm a decision without proof shows
No linked proofor equivalent. - Open a proof link where available.
- Open an OperationRun link where available.
- Confirm row detail handoff still opens FindingException detail.
- Confirm no lifecycle actions moved into the register.
- Confirm no
/admin/tURL appears.
Documentation Impact
No product docs are changed during prep. During implementation close-out, update docs/product/implementation-ledger.md and/or docs/product/spec-candidates.md only if runtime behavior is completed and product truth changes. Do not mark the Decision Register as sellable unless customer-safe/review-pack gaps are separately solved.
Open Questions
- None blocking preparation.
- Implementation must verify exact source-type strings and ID semantics before deciding whether an evidence reference can be resolved directly to
EvidenceSnapshotorStoredReport. - Implementation must document if OperationRun links are not repo-real for any decision path beyond
EvidenceSnapshot.operation_run_id.