feat(api): add pack workflow execution via scope/name URL grammar#359
Merged
Merged
Conversation
- `.go-arch-lint.yml`: allow infra-workflowpkg to depend on domain-errors - `CHANGELOG.md`: document F101 route changes, fixes, and breaking change notes - `docs/user-guide/api.md`: update all endpoint examples to (scope, name) two-segment URLs - `docs/user-guide/workflow-packs.md`: add reserved pack names section for local/global/env - `internal/application/service.go`: use ListWithSource for entry population; add Scope/Workflow fields; extract promptFileError helper - `internal/application/service_test.go`: add tests for Scope/Workflow/Source propagation in ListAllWorkflows - `internal/domain/ports/repository.go`: add WorkflowSource, WorkflowInfo types and ListWithSource to WorkflowRepository interface - `internal/domain/ports/repository_test.go`: implement ListWithSource on mock - `internal/domain/workflow/entry.go`: add Scope and Workflow fields with full doc comment - `internal/domain/workflow/entry_test.go`: unit tests for WorkflowEntry field semantics - `internal/infrastructure/repository/composite_repository.go`: migrate ListWithSource return type to ports.WorkflowInfo - `internal/infrastructure/repository/composite_repository_test.go`: update assertions to ports.SourceLocal/SourceGlobal - `internal/infrastructure/repository/yaml_repository.go`: add ListWithSource and WithSource for configurable source reporting - `internal/infrastructure/workflowpkg/discoverer.go`: populate Scope/Workflow on pack entries; add defense-in-depth nameRegex guards - `internal/infrastructure/workflowpkg/discoverer_test.go`: add traversal, invalid name, and scope/workflow field tests - `internal/infrastructure/workflowpkg/manifest.go`: reject reserved scope names (local/global/env) and validate workflow names against nameRegex - `internal/infrastructure/workflowpkg/manifest_test.go`: add reserved scope and invalid workflow name validation tests - `internal/interfaces/api/bridge.go`: expand TrackResumedExecution comment explaining intentional persistence - `internal/interfaces/api/bridge_test.go`: add Scope/Workflow fields to mock entries; add TrackResumedExecution persistence test - `internal/interfaces/api/doc.go`: update route documentation to (scope, name) grammar - `internal/interfaces/api/handlers_executions.go`: use recomposeIdentifier(scope, name) for workflow lookup; update route path - `internal/interfaces/api/handlers_executions_test.go`: add pack/local scope run tests; extract newBlockingExecutionHandlerAPI helper - `internal/interfaces/api/handlers_workflows.go`: pass Scope/Workflow to WorkflowSummary; use recomposeIdentifier; update route paths - `internal/interfaces/api/handlers_workflows_test.go`: add scope/workflow field assertions; update paths to two-segment form; extract newWorkflowHandlerAPI helper - `internal/interfaces/api/routing.go`: add recomposeIdentifier helper to build canonical workflow identifiers from scope+name - `internal/interfaces/api/routing_test.go`: table-driven tests for recomposeIdentifier - `internal/interfaces/api/server_test.go`: update test routes to (scope, name) form - `internal/interfaces/api/types.go`: add Scope and Workflow fields to WorkflowSummary; add Scope to input structs - `internal/interfaces/tui/tab_workflows.go`: fix nil-pointer panic in handleValidate by using fully-qualified entry name - `internal/interfaces/tui/tab_workflows_test.go`: add tests for pack workflow validate path - `internal/testutil/mocks/mocks.go`: implement ListWithSource on mock repository - `tests/fixtures/api/packs/speckit/manifest.yaml`: add speckit pack fixture manifest - `tests/fixtures/api/packs/speckit/state.json`: add speckit pack state fixture - `tests/fixtures/api/packs/speckit/workflows/specify.yaml`: add speckit/specify workflow fixture - `tests/integration/api/functional_test.go`: update route paths to (scope, name) form - `tests/integration/api/pack_workflow_test.go`: integration tests for pack workflow list/get/run/validate via HTTP API - `tests/integration/api/server_integration_test.go`: update route path in integration test Closes #358
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
speckit/specify) but every action endpoint (/run,/validate, GET) returned 404 because chi's single-segment{name}placeholder cannot match identifiers containing a/. The new(scope, name)two-segment URL grammar resolves this natively./api/workflows/{name}[/run|/validate]) with{scope}/{name}equivalents. Thescopesentinellocaladdresses non-pack workflows; a pack's vendor name serves as scope for pack workflows. This is a breaking change relative to the unreleased F097 API surface.Workflowstab when validating a pack-sourced workflow: thewfMaplookup missed on fully-qualifiedpackName/workflowNamekeys, causinghandleValidateto dereference a nilwfpointer.manifest.yamlwere previously not validated against the name regex, allowing a crafted../../etc/passwdentry to escape theworkflows/directory viafilepath.Join.Changes
Domain / Ports
internal/domain/ports/repository.go: AddWorkflowSourcetype (env,local,global),WorkflowInfostruct, andListWithSourcemethod toWorkflowRepositoryinterfaceinternal/domain/ports/repository_test.go: ImplementListWithSourceon test mockinternal/domain/workflow/entry.go: AddScopeandWorkflowfields toWorkflowEntrywith full doc commentApplication
internal/application/service.go: SwitchListAllWorkflowsfromListtoListWithSourceto populateScope,Workflow,Sourceon each entry; extractpromptFileErrorhelper to eliminate repeated error constructioninternal/application/service_test.go: Add tests for local/pack/mixed source propagation (AC2, AC3, AC5, AC6)Infrastructure — Repository
internal/infrastructure/repository/yaml_repository.go: AdddefaultSourcefield,WithSourceconstructor, andListWithSourceimplementationinternal/infrastructure/repository/composite_repository.go: MigrateListWithSourcereturn type from internalWorkflowInfotoports.WorkflowInfo; convert Source iota toports.WorkflowSourceinternal/infrastructure/repository/composite_repository_test.go: Update assertions to useports.SourceLocal/ports.SourceGlobalInfrastructure — Pack Discovery
internal/infrastructure/workflowpkg/manifest.go: Reject reserved scope names (local,global,env) with a structuredUSER.INPUT.VALIDATION_FAILEDerror; enforce name regex on every workflow entry inWorkflowslistinternal/infrastructure/workflowpkg/discoverer.go: PopulateScopeandWorkflowon eachWorkflowEntry; add defense-in-depth regex guards for pack and workflow names inDiscoverWorkflowsandloadWorkflowDescriptioninternal/infrastructure/workflowpkg/manifest_test.go: Add tests for invalid workflow names, valid kebab-case names, and reserved scope rejection with structured error assertionsinternal/infrastructure/workflowpkg/discoverer_test.go: Add tests for path-traversal pack/workflow name skipping andScope/Workflowfield populationHTTP API Interface
internal/interfaces/api/routing.go: New file —recomposeIdentifier(scope, name)helper that mapslocalscope to bare name and any other scope toscope/nameinternal/interfaces/api/routing_test.go: New file — table-driven tests forrecomposeIdentifierinternal/interfaces/api/types.go: AddScopepath param toGetWorkflowInput,ValidateWorkflowInput,RunWorkflowInput; addScopeandWorkflowfields toWorkflowSummaryinternal/interfaces/api/handlers_workflows.go: UserecomposeIdentifierinGetandValidate; populateScope/WorkflowinList; update route paths to{scope}/{name}internal/interfaces/api/handlers_executions.go: UserecomposeIdentifierinRun; update route path to{scope}/{name}/runinternal/interfaces/api/bridge.go: ExpandTrackResumedExecutioncomment explaining intentional entry persistenceinternal/interfaces/api/bridge_test.go: ExtendmockWorkflowListerto recordlastGetName/lastValidateName; addTestBridge_TrackResumedExecution_PersistsEntryForSubsequentQueriesinternal/interfaces/api/doc.go: Update package doc to reflect{scope}/{name}route grammarinternal/interfaces/api/server_test.go: Update route path assertions to new grammarinternal/interfaces/api/handlers_workflows_test.go: AddnewWorkflowHandlerAPIhelper; add scope-routing tests; update all existing tests to new URL grammarinternal/interfaces/api/handlers_executions_test.go: AddnewBlockingExecutionHandlerAPIhelper; add pack/local scope execution tests; update all existing tests to new URL grammarTUI
internal/interfaces/tui/tab_workflows.go: FixhandleValidateto forwarditem.entry.Name(fully-qualified) instead ofitem.wf.Name(nil for pack entries); extractresetInputFormhelper to deduplicate identical five-field resetinternal/interfaces/tui/tab_workflows_test.go: AddTestWorkflowsTab_handleValidate_PackWorkflow_NoPanicandTestWorkflowsTab_resetInputForm_ClearsAllFieldsTest Infrastructure & Fixtures
internal/testutil/mocks/mocks.go: ImplementListWithSourceonMockWorkflowRepositorytests/fixtures/api/packs/speckit/manifest.yaml: New fixture pack manifesttests/fixtures/api/packs/speckit/state.json: New fixture pack statetests/fixtures/api/packs/speckit/workflows/specify.yaml: New fixture workflow for integration teststests/integration/api/pack_workflow_test.go: New integration test file — GET, run (with poll), validate, and SSE stream tests for pack workflowstests/integration/api/functional_test.go: Update URLs tolocal/scope prefixtests/integration/api/server_integration_test.go: UpdatepostRunWorkflowhelper tolocal/scope prefixDocs & Config
docs/user-guide/api.md: Document(scope, name)grammar, update all examples to new URL shape, add pack workflow cURL/JS/Python examplesdocs/user-guide/workflow-packs.md: Add "Reserved Pack Names" section documentinglocal/globalrestrictionsCHANGELOG.md: Document F101 breaking route change, pack loader reservation, TUI nil-pointer fix, path-traversal fix.go-arch-lint.yml: Allowinfra-workflowpkgto depend ondomain-errors(needed for structured reservation error)Test plan
make testpasses with zero failures (unit tests coverrecomposeIdentifier, scope field propagation, reserved name rejection, and TUI nil-pointer guard)make test-integrationpasses:TestAPI_PackWorkflow_Get,TestAPI_PackWorkflow_Run,TestAPI_PackWorkflow_Validate, andTestAPI_PackWorkflow_SSEall reach 200/202/completedmake lintreports zero violationsawf serve, thencurl http://localhost:2511/api/workflows/speckit/specify/runreturns 202;curl http://localhost:2511/api/workflows/local/deploy-prod/runalso returns 202Closes #358
Generated with awf commit workflow