packer (WIP)
This commit is contained in:
parent
c5b10907c9
commit
83546149e4
@ -0,0 +1,166 @@
|
|||||||
|
# PR-11 Pack Wizard Shell and Packer Contract Consumption
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
Cross-domain impact: `docs/packer`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
The `Pack` action in the `Assets` workspace is now closed as a Studio wizard over packer-owned operations.
|
||||||
|
|
||||||
|
The Studio decision already fixes the operational boundary:
|
||||||
|
|
||||||
|
- Studio is the shell;
|
||||||
|
- packer owns summary, validation, pack execution, progress, and result semantics;
|
||||||
|
- the wizard has four phases:
|
||||||
|
`Summary`, `Validation`, `Packing`, `Result`;
|
||||||
|
- validation runs only on the current `registered + included in build` set;
|
||||||
|
- blocking diagnostics stop the flow before execution;
|
||||||
|
- the first wave is non-cancelable.
|
||||||
|
|
||||||
|
The packer-side API PR also defines the contracts that Studio must consume:
|
||||||
|
|
||||||
|
- pack summary
|
||||||
|
- pack validation
|
||||||
|
- pack execution
|
||||||
|
|
||||||
|
This PR implements the Studio side of that contract.
|
||||||
|
|
||||||
|
References:
|
||||||
|
|
||||||
|
- [`../decisions/Pack Wizard in Assets Workspace Decision.md`](../decisions/Pack%20Wizard%20in%20Assets%20Workspace%20Decision.md)
|
||||||
|
- [`../../packer/pull-requests/PR-28-pack-wizard-public-contracts-summary-validation-and-execution.md`](../../packer/pull-requests/PR-28-pack-wizard-public-contracts-summary-validation-and-execution.md)
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Deliver the `Pack` wizard shell in the `Assets` workspace and bind it to the public packer contracts for summary, validation, progress, and result.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`../decisions/Pack Wizard in Assets Workspace Decision.md`](../decisions/Pack%20Wizard%20in%20Assets%20Workspace%20Decision.md)
|
||||||
|
- [`./PR-05d-assets-activity-progress-and-logs-integration.md`](./PR-05d-assets-activity-progress-and-logs-integration.md)
|
||||||
|
- [`./PR-07c-asset-details-and-form-lifecycle.md`](./PR-07c-asset-details-and-form-lifecycle.md)
|
||||||
|
- [`./PR-07d-asset-mutation-and-structural-sync-orchestration.md`](./PR-07d-asset-mutation-and-structural-sync-orchestration.md)
|
||||||
|
- cross-domain reference:
|
||||||
|
[`../../packer/pull-requests/PR-28-pack-wizard-public-contracts-summary-validation-and-execution.md`](../../packer/pull-requests/PR-28-pack-wizard-public-contracts-summary-validation-and-execution.md)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- add a real `Pack` action entry point in the `Assets` workspace action bar
|
||||||
|
- open a dedicated modal wizard from that button
|
||||||
|
- implement the wizard shell with four explicit phases:
|
||||||
|
- `Summary`
|
||||||
|
- `Validation`
|
||||||
|
- `Packing`
|
||||||
|
- `Result`
|
||||||
|
- consume packer summary API for the first phase
|
||||||
|
- consume packer validation API for the second phase
|
||||||
|
- consume packer pack execution API for the third phase
|
||||||
|
- bind packer operation progress to the wizard progress UI
|
||||||
|
- render result data from the final pack execution response
|
||||||
|
- show blocking diagnostics by default in `Validation`
|
||||||
|
- allow developer drill-down on per-asset validation entries
|
||||||
|
- keep `Packing` non-editable and non-cancelable in the first wave
|
||||||
|
- keep companion artifacts in secondary drill-down in `Result`
|
||||||
|
- allow a `dumb` future-facing export/copy button without real behavior if the shell needs a visible reminder
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no local Studio recomputation of build-set semantics
|
||||||
|
- no local Studio validation engine for pack gating
|
||||||
|
- no fake timer-based progress
|
||||||
|
- no cancellation in the first wave
|
||||||
|
- no direct implementation of packer semantics inside Studio
|
||||||
|
- no asset-details-local `Pack`; this remains a workspace-level flow
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Wire the `Pack` action in the workspace action bar to open the new wizard.
|
||||||
|
|
||||||
|
2. Build a dedicated wizard shell control for the `Assets` workspace.
|
||||||
|
The shell should own:
|
||||||
|
- phase navigation
|
||||||
|
- modal presentation
|
||||||
|
- loading states
|
||||||
|
- error surfaces
|
||||||
|
- binding to packer responses and progress events
|
||||||
|
|
||||||
|
3. Implement the `Summary` phase as packer-backed preflight.
|
||||||
|
Rules:
|
||||||
|
- request pack summary on open;
|
||||||
|
- do not reconstruct counts from navigator rows;
|
||||||
|
- show the canonical artifact name `assets.pa`;
|
||||||
|
- show the build-set counts returned by packer.
|
||||||
|
|
||||||
|
4. Implement the `Validation` phase as packer-backed gate inspection.
|
||||||
|
Rules:
|
||||||
|
- call the validation API explicitly;
|
||||||
|
- render aggregate counts and per-asset entries;
|
||||||
|
- show blocking diagnostics by default;
|
||||||
|
- allow drill-down to inspect more context on each asset;
|
||||||
|
- block advance into `Packing` when validation fails.
|
||||||
|
|
||||||
|
5. Implement the `Packing` phase as an operational waiting surface.
|
||||||
|
Rules:
|
||||||
|
- call pack execution explicitly only after successful validation;
|
||||||
|
- show progress bar and current-step text from packer-driven state;
|
||||||
|
- keep the modal non-editable;
|
||||||
|
- do not expose real cancel behavior in the first wave.
|
||||||
|
|
||||||
|
6. Implement the `Result` phase as packer-result rendering.
|
||||||
|
Rules:
|
||||||
|
- show final status;
|
||||||
|
- show total assets packed;
|
||||||
|
- show elapsed time when available from the contract;
|
||||||
|
- keep `assets.pa` visible in the main summary;
|
||||||
|
- move companion artifacts into secondary drill-down.
|
||||||
|
|
||||||
|
7. Integrate the operation with Studio progress and activity surfaces where appropriate.
|
||||||
|
The wizard-local progress surface is primary during execution, but existing global operational surfaces should remain coherent with the same operation.
|
||||||
|
|
||||||
|
8. Keep the implementation boundary strict.
|
||||||
|
Studio may orchestrate calls and render states, but it must not decide:
|
||||||
|
- what the pack set is;
|
||||||
|
- what counts as blocking;
|
||||||
|
- how `asset_table` ordering is determined;
|
||||||
|
- or what artifacts were emitted beyond what packer reports.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- clicking `Pack` in the `Assets` workspace opens a dedicated modal wizard
|
||||||
|
- the wizard has explicit `Summary`, `Validation`, `Packing`, and `Result` phases
|
||||||
|
- `Summary` is populated from packer summary data rather than local UI reconstruction
|
||||||
|
- `Validation` is populated from packer validation data
|
||||||
|
- validation failure blocks transition into `Packing`
|
||||||
|
- blocking diagnostics are visible by default in the validation phase
|
||||||
|
- per-asset drill-down exists for developer inspection
|
||||||
|
- `Packing` shows packer-driven progress and remains non-editable
|
||||||
|
- the first wave does not expose real cancellation
|
||||||
|
- `Result` renders final packer result data and keeps companion artifacts secondary
|
||||||
|
- the Studio implementation remains a shell over packer contracts rather than a second semantic engine
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- unit tests for wizard phase state transitions
|
||||||
|
- unit tests for mapping summary response into the `Summary` phase
|
||||||
|
- unit tests for validation gating and blocked advance into `Packing`
|
||||||
|
- unit tests for per-asset validation drill-down state
|
||||||
|
- unit tests for progress binding from packer operation state into the wizard progress UI
|
||||||
|
- unit tests for result rendering from pack execution response
|
||||||
|
- smoke test for:
|
||||||
|
- open wizard
|
||||||
|
- load summary
|
||||||
|
- run validation
|
||||||
|
- stop on blockers
|
||||||
|
- advance on valid state
|
||||||
|
- show packing progress
|
||||||
|
- render final result
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `docs/studio/specs/4. Assets Workspace Specification.md` if the wizard shell behavior needs more explicit normative wording
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/**`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/**`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/**` if new UI-local orchestration events are needed
|
||||||
|
- `prometeu-studio/src/main/resources/i18n/messages.properties`
|
||||||
|
- `prometeu-studio/src/test/java/p/studio/workspaces/assets/**`
|
||||||
@ -11,6 +11,12 @@ public interface PackerWorkspaceService {
|
|||||||
|
|
||||||
GetAssetActionsResult getAssetActions(GetAssetActionsRequest request);
|
GetAssetActionsResult getAssetActions(GetAssetActionsRequest request);
|
||||||
|
|
||||||
|
PackWorkspaceSummaryResult getPackWorkspaceSummary(PackWorkspaceSummaryRequest request);
|
||||||
|
|
||||||
|
ValidatePackWorkspaceResult validatePackWorkspace(ValidatePackWorkspaceRequest request);
|
||||||
|
|
||||||
|
PackWorkspaceResult packWorkspace(PackWorkspaceRequest request);
|
||||||
|
|
||||||
CreateAssetResult createAsset(CreateAssetRequest request);
|
CreateAssetResult createAsset(CreateAssetRequest request);
|
||||||
|
|
||||||
RegisterAssetResult registerAsset(RegisterAssetRequest request);
|
RegisterAssetResult registerAsset(RegisterAssetRequest request);
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
package p.packer.dtos;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackerEmittedArtifactDTO(
|
||||||
|
String label,
|
||||||
|
Path path,
|
||||||
|
boolean canonical,
|
||||||
|
long sizeBytes) {
|
||||||
|
|
||||||
|
public PackerEmittedArtifactDTO {
|
||||||
|
label = Objects.requireNonNull(label, "label").trim();
|
||||||
|
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||||
|
if (label.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("label must not be blank");
|
||||||
|
}
|
||||||
|
if (sizeBytes < 0L) {
|
||||||
|
throw new IllegalArgumentException("sizeBytes must not be negative");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package p.packer.dtos;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackerPackExecutionSummaryDTO(
|
||||||
|
String canonicalArtifactName,
|
||||||
|
int packedAssetCount,
|
||||||
|
long elapsedMillis,
|
||||||
|
List<PackerEmittedArtifactDTO> emittedArtifacts) {
|
||||||
|
|
||||||
|
public PackerPackExecutionSummaryDTO {
|
||||||
|
canonicalArtifactName = Objects.requireNonNull(canonicalArtifactName, "canonicalArtifactName").trim();
|
||||||
|
emittedArtifacts = List.copyOf(Objects.requireNonNull(emittedArtifacts, "emittedArtifacts"));
|
||||||
|
if (canonicalArtifactName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("canonicalArtifactName must not be blank");
|
||||||
|
}
|
||||||
|
if (packedAssetCount < 0) {
|
||||||
|
throw new IllegalArgumentException("packedAssetCount must not be negative");
|
||||||
|
}
|
||||||
|
if (elapsedMillis < 0L) {
|
||||||
|
throw new IllegalArgumentException("elapsedMillis must not be negative");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package p.packer.dtos;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackerPackSummaryDTO(
|
||||||
|
int includedRegisteredAssetCount,
|
||||||
|
int outsideBuildSetAssetCount,
|
||||||
|
String canonicalArtifactName) {
|
||||||
|
|
||||||
|
public PackerPackSummaryDTO {
|
||||||
|
if (includedRegisteredAssetCount < 0) {
|
||||||
|
throw new IllegalArgumentException("includedRegisteredAssetCount must not be negative");
|
||||||
|
}
|
||||||
|
if (outsideBuildSetAssetCount < 0) {
|
||||||
|
throw new IllegalArgumentException("outsideBuildSetAssetCount must not be negative");
|
||||||
|
}
|
||||||
|
canonicalArtifactName = Objects.requireNonNull(canonicalArtifactName, "canonicalArtifactName").trim();
|
||||||
|
if (canonicalArtifactName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("canonicalArtifactName must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package p.packer.dtos;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackerPackValidationAssetDTO(
|
||||||
|
PackerAssetSummaryDTO asset,
|
||||||
|
boolean blocked,
|
||||||
|
List<PackerDiagnosticDTO> diagnostics) {
|
||||||
|
|
||||||
|
public PackerPackValidationAssetDTO {
|
||||||
|
Objects.requireNonNull(asset, "asset");
|
||||||
|
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||||
|
if (blocked && diagnostics.stream().noneMatch(PackerDiagnosticDTO::blocking)) {
|
||||||
|
throw new IllegalArgumentException("blocked validation asset must include at least one blocking diagnostic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package p.packer.dtos;
|
||||||
|
|
||||||
|
public record PackerPackValidationSummaryDTO(
|
||||||
|
int totalAssetsInScope,
|
||||||
|
int validAssetCount,
|
||||||
|
int blockedAssetCount,
|
||||||
|
boolean canPack) {
|
||||||
|
|
||||||
|
public PackerPackValidationSummaryDTO {
|
||||||
|
if (totalAssetsInScope < 0) {
|
||||||
|
throw new IllegalArgumentException("totalAssetsInScope must not be negative");
|
||||||
|
}
|
||||||
|
if (validAssetCount < 0) {
|
||||||
|
throw new IllegalArgumentException("validAssetCount must not be negative");
|
||||||
|
}
|
||||||
|
if (blockedAssetCount < 0) {
|
||||||
|
throw new IllegalArgumentException("blockedAssetCount must not be negative");
|
||||||
|
}
|
||||||
|
if (validAssetCount + blockedAssetCount > totalAssetsInScope) {
|
||||||
|
throw new IllegalArgumentException("validAssetCount + blockedAssetCount must not exceed totalAssetsInScope");
|
||||||
|
}
|
||||||
|
if (canPack && blockedAssetCount > 0) {
|
||||||
|
throw new IllegalArgumentException("canPack must be false when blockedAssetCount is greater than zero");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackWorkspaceRequest(
|
||||||
|
PackerProjectContext project) {
|
||||||
|
|
||||||
|
public PackWorkspaceRequest {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import p.packer.dtos.PackerPackExecutionSummaryDTO;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackWorkspaceResult(
|
||||||
|
PackerOperationStatus status,
|
||||||
|
String summary,
|
||||||
|
PackerPackExecutionSummaryDTO result) {
|
||||||
|
|
||||||
|
public PackWorkspaceResult {
|
||||||
|
Objects.requireNonNull(status, "status");
|
||||||
|
summary = Objects.requireNonNull(summary, "summary").trim();
|
||||||
|
Objects.requireNonNull(result, "result");
|
||||||
|
if (summary.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("summary must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackWorkspaceSummaryRequest(
|
||||||
|
PackerProjectContext project) {
|
||||||
|
|
||||||
|
public PackWorkspaceSummaryRequest {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import p.packer.dtos.PackerPackSummaryDTO;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PackWorkspaceSummaryResult(
|
||||||
|
PackerOperationStatus status,
|
||||||
|
String summary,
|
||||||
|
PackerPackSummaryDTO packSummary) {
|
||||||
|
|
||||||
|
public PackWorkspaceSummaryResult {
|
||||||
|
Objects.requireNonNull(status, "status");
|
||||||
|
summary = Objects.requireNonNull(summary, "summary").trim();
|
||||||
|
Objects.requireNonNull(packSummary, "packSummary");
|
||||||
|
if (summary.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("summary must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record ValidatePackWorkspaceRequest(
|
||||||
|
PackerProjectContext project) {
|
||||||
|
|
||||||
|
public ValidatePackWorkspaceRequest {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package p.packer.messages;
|
||||||
|
|
||||||
|
import p.packer.dtos.PackerPackValidationAssetDTO;
|
||||||
|
import p.packer.dtos.PackerPackValidationSummaryDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record ValidatePackWorkspaceResult(
|
||||||
|
PackerOperationStatus status,
|
||||||
|
String summary,
|
||||||
|
PackerPackValidationSummaryDTO validation,
|
||||||
|
List<PackerPackValidationAssetDTO> assets) {
|
||||||
|
|
||||||
|
public ValidatePackWorkspaceResult {
|
||||||
|
Objects.requireNonNull(status, "status");
|
||||||
|
summary = Objects.requireNonNull(summary, "summary").trim();
|
||||||
|
Objects.requireNonNull(validation, "validation");
|
||||||
|
assets = List.copyOf(Objects.requireNonNull(assets, "assets"));
|
||||||
|
if (summary.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("summary must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -139,6 +139,21 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
|||||||
return actionReadService.getAssetActions(request);
|
return actionReadService.getAssetActions(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PackWorkspaceSummaryResult getPackWorkspaceSummary(PackWorkspaceSummaryRequest request) {
|
||||||
|
throw new UnsupportedOperationException("pack workspace summary is not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidatePackWorkspaceResult validatePackWorkspace(ValidatePackWorkspaceRequest request) {
|
||||||
|
throw new UnsupportedOperationException("pack workspace validation is not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PackWorkspaceResult packWorkspace(PackWorkspaceRequest request) {
|
||||||
|
throw new UnsupportedOperationException("pack workspace execution is not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CreateAssetResult createAsset(CreateAssetRequest request) {
|
public CreateAssetResult createAsset(CreateAssetRequest request) {
|
||||||
final CreateAssetRequest safeRequest = Objects.requireNonNull(request, "request");
|
final CreateAssetRequest safeRequest = Objects.requireNonNull(request, "request");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user