feat(workflow): mount the remaining Work/workflow product surface - #512
Conversation
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
…-workflow-product-surface-2353
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…github.com/ScriptedAlchemy/tracedecay into cursor/mount-workflow-product-surface-2353 Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 431e7cf168
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| callWork( | ||
| WORKFLOW_LIST_DEFINITIONS_ROUTE, | ||
| {}, | ||
| scopedUrl(scope, WORKFLOW_LIST_DEFINITIONS_ROUTE.path), |
There was a problem hiding this comment.
Add Workflow reads to the project-scoped gateway
When the scope bar names any project, this rewrites the request to /api/projects/{id}/application/workflow/list-definitions, but selected_project_application_read in crates/tracedecay-dashboard-api/src/lib.rs recognizes only feedback and read-only Work operations. Non-active projects therefore reject the POST with 405, while an explicitly selected active project falls through to project_api_router, where the application Workflow routes are not mounted. Consequently definition listing—and the analogous get-run and lifecycle calls—cannot work in any project-selected scope; route Workflow reads through the selected project's application authority and restrict mutations according to scope writability.
AGENTS.md reference: AGENTS.md:L75-L77
Useful? React with 👍 / 👎.
| : { | ||
| available: true, | ||
| value: `${coverage.observed} observed native integrations across ${dimensionalCells.length} kind/outcome ${dimensionalCells.length === 1 ? 'cell' : 'cells'} — counts are the projector's own cells, never summed here`, | ||
| }; |
There was a problem hiding this comment.
Keep fully suppressed integration cells unavailable
When the Rust projector's support floor suppresses every dimensional integration cell, it retains those dimensions but sets each cell's value to null and publishes a typed support_floor_unmet absence. Because this branch checks only dimensionalCells.length, the card still reports an available “0 observed ... across N cells” headline and increments the page's measured count even though every displayed row is unavailable. Require at least one readable cell, or propagate the cells' typed absence, before marking the headline measured.
AGENTS.md reference: AGENTS.md:L95-L97
Useful? React with 👍 / 👎.
| DaemonInvocationProblem::NotFoundOrNotAuthorized | ||
| } | ||
| tracedecay_application::WorkflowCoordinationError::InvalidDefinition | ||
| | tracedecay_application::WorkflowCoordinationError::CatalogAdmissionDenied(_) |
There was a problem hiding this comment.
Report catalog composition failures as unavailable
This arm also catches CatalogAdmissionDenied(CatalogUnavailable(_)), which the new admission code returns when the canonical executable registry or its digest cannot be composed. During activation that internal authority failure is consequently converted—and journaled by workflow_dispatch.rs—as a permanent caller-side invalid request, so retrying the request cannot recover when the catalog becomes available. Map the CatalogUnavailable subtype to DaemonInvocationProblem::Unavailable while retaining invalid-request handling for unknown operations and pin mismatches.
AGENTS.md reference: AGENTS.md:L147-L148
Useful? React with 👍 / 👎.
| {selectedDefinition === null ? null : ( | ||
| <DefinitionDetail definition={selectedDefinition} /> | ||
| )} |
There was a problem hiding this comment.
Reset lifecycle state when switching definitions
If an operator selects definition A, submits or edits a revision, and then clicks definition B directly, React reuses the same DefinitionDetail/LifecycleControls instance because it occupies the same unkeyed position. The revision input and useMutation().data therefore remain from A: B can display A's disposition receipt and can be submitted with A's expected revision. Key this subtree by definition identity/version or explicitly reset both pieces of state when the definition changes.
AGENTS.md reference: AGENTS.md:L78-L83
Useful? React with 👍 / 👎.
| : metricsProvenance( | ||
| model, | ||
| model.github_stack_capability.coverage, | ||
| 'execution-topology-metrics.v1', | ||
| 'capability observations', |
There was a problem hiding this comment.
Do not fabricate the capability descriptor revision
ExecutionGitHubStackCapabilityReadingV1 carries capability, fallback, coverage, and unavailability fields but no descriptor revision, yet every successful metrics response is reported here as pinned to execution-topology-metrics.v1. This presents provenance the wire authority never supplied and will silently retain the old pin if the backend's capability descriptor changes. Keep this facet typed-absent until the canonical response publishes a revision, or add that revision to the Rust-owned contract.
AGENTS.md reference: AGENTS.md:L95-L97
Useful? React with 👍 / 👎.
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…rface-2353' into pr512 # Conflicts: # crates/tracedecay-application/src/workflow_admission.rs Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
676a9d6 to
9bb9cc2
Compare
3cfccec
into
codex/tracedecay-total-redesign-plan
Summary
Mounts the remaining Work/workflow product surface on
codex/tracedecay-total-redesign-plan, closing audit items B3, B8, and A19 without recreatingWorkflowStepExecutionService/execute_ready_stepor the retired WorkProjection routes (1be58ce45). No version bumps.B3 — catalog semantic admission gates workflow activation
tracedecay_application::workflow_admission: every step operation must resolve to an available executable binding in the canonical Work executable catalog, and the definition'spinned_catalog_digestmust name the livework_executable_catalog_digest()(the digest hashes every capability manifest and request/result schema authority, so the pin is the schema/capability compatibility check). Denials are typed (UnknownOperation,OperationUnavailable,CatalogPinMismatch).WorkflowDefinitionService::admit_activationis the one authority both activation paths run: the serviceactivate, and the daemon's journaled effect path (workflow_dispatch.rs), which previously journaled the CAS transition without even structural revalidation.validate_definitionanswers the same admission so preflight cannot diverge from activation.workflow_coordination.rs), and the daemon journey registers an uncataloged candidate and asserts the mounted activate route refuses it as a typedinvalid_request. Fixtures using the never-cataloged idoperation.work.attempt_startare corrected to the mountedoperation.work.start_attempt— exactly the drift the admission now catches.B8 — dashboard workflow definition/run workspace
WorkflowDefinition, lifecycle requests,WorkflowDefinitionDisposition,WorkflowRunGetRequest,WorkflowRunProjection) joinDashboardContractCatalogV1and the generated Zod contracts; handoff and run-control requests are asserted not published (the browser never holds a bearer or mints fences/command ids).workflowsworkspace (channel 14): definitions list, decoded step tables with pinned digests, the three compare-and-swap lifecycle transitions (activate/retire/reject), and run projections offget_run— all through the same application envelope walker and/api/application/workflow/<segment>routes the catalog advertises, bound to the same operation ids. Refusals render the daemon's own typed state; the empty registry is drawn only when the daemon answered one.A19 — Work-context integration/stack
Investigation outcome: nothing was removed from
WorkOperationfor integration/stack — apply/receipt/stack-freeze live in the Plan 36 native-integration family, and Plan 24 keeps accepted integration lowered only through that family, so no Work mutation op is added (the "advertised" ops exist only in stale dated mount lists in Plans 24/11c). What the Work workspace truthfully claims is the observed accounting: the dashboard's integration-outcomes and GitHub-stack-capability cards were typed absences with a stale "read model is not published" reason. They now decode the mountedoperation.work.topology_metricsprojection (work_merge_attempts_totalkind × outcome cells; thegithub_stack_capabilityreading) through the existing application use case, cell by cell, with the projector's own typed absences and no summed or derived figures. The remaining undecoded dimensions keep truthful absence wording (unsupported, naming the event family). The decision is recorded inNEXT.md.Codex review follow-ups (
25d14190e)WorkflowOperationnow declaresis_read_only()(parity with the catalog effect class is pinned by a test), the project-scoped gateway admits exactly that read set by POST (SelectedProjectApplicationRead::Workflow), and answers it from the selected project's own canonical application router; lifecycle mutations stay refused under the read-only gateway exactly like Work commands.CatalogAdmissionDenied(CatalogUnavailable)maps toUnavailable, notinvalid_request.DefinitionDetailis keyed by the selected definition identity so the revision draft and last transition result reset when switching definitions.execution-topology-metrics.v1; the integration card reads the revision off a real cell only.Verification
cargo check --workspace --all-featuresgreen on the merged tree (base merge51b5623cdintegrated).tracedecay-application,tracedecay-dashboard-api, andtracedecay-rusqlite-runtimetest suites green, including the new admission, contract-registration, gateway read-set, and effect-class parity tests. One pre-existing failure reproduces identically at the base commita707c84f7and is unrelated:doctor_findings_api::tests::findings_route_preserves_canonical_unknown_entries(environment-sensitive entry count, 11 vs 10 on this VM).npm test1693/1693,tsc --noEmit, production build,contracts:generateoutputs committed, andcontracts:checkreports up to date.daemon_suite advanced_workflow_journey_test, run with the real 615 MB byte-pinned FastEmbed fixture on a tmpfs-backed profile): registration, the new catalog-admission denial, mounted activation, run start, fan-out, synthesis, and restart all pass; the run then stops at the direct semantic evaluator publication (semantic activation input was rejected), a host-sensitive stage this diff does not touch and thatNEXT.mdalready tracks as remaining work for this journey (the earliercode graph database conflictblocker disappeared once PR fix(graph-db): bind supplied sealed manifests so first publish seats #510's fix was merged in). The B3 daemon-path assertion is upstream of that stage and passed.