feat(registry): support ad-hoc registry references (workflow@owner/repo)#190
Merged
Conversation
Closes #189. Adds a new reference syntax that doesn't require pre-installing a registry via `conductor registry add`: analysis@myorg/team-a#v1.0.0 # pinned tag analysis@myorg/team-a#main # pinned branch (resolves to current HEAD) analysis@myorg/team-a # default branch HEAD (no #ref) Disambiguation rule: if the part after `@` contains `/`, it's interpreted as a literal `owner/repo` and fetched directly from GitHub. If it doesn't, it's a configured registry name (existing behavior). Registry names disallow `/`, so the two forms can't collide. Works everywhere existing registry references work — `conductor run`, `conductor validate`, `conductor resume`, and as a sub-workflow inside `type: workflow` agents (the engine + validator path added in #172 picks it up transparently). Cache layout: `~/.conductor/cache/registries/_adhoc/<owner>/<repo>/<workflow>/<sha[:12]>/`. The `_adhoc` namespace is reserved (named registries reject names containing `/`). Auth: same as named GitHub registries — public repos work; private uses `gh auth token` automatically. Implementation: * `registry/resolver.py` — extends `ResolvedRef` with `kind="adhoc"` and `(adhoc_owner, adhoc_repo)` fields. `_parse_registry_ref` branches on whether the registry slot contains `/`. The `_looks_like_file_path` heuristic now yields to the registry parser whenever `@` is present in the ref, so refs containing `/` in the registry slot aren't misclassified as file paths. * `registry/cache.py` — adds `fetch_workflow_adhoc()` which constructs a synthetic `RegistryEntry(type=github, source=f"{owner}/{repo}")` and reuses the existing fetch + cache pipeline. Adds `resolve_and_fetch(ResolvedRef) -> Path` as a single dispatcher used by the CLI, engine, and validator so each call site doesn't have to switch on `ResolvedRef.kind`. * `cli/app.py` — collapses 4 duplicated `resolve_ref + fetch_workflow` blocks (run, validate, resume, +1) to one-liners using `resolve_and_fetch`. * `engine/workflow.py` and `config/validator.py` — replace the registry-only branches in `_resolve_subworkflow_path` and `_resolve_subworkflow_ref_for_validation` with `resolve_and_fetch`, picking up adhoc support transparently. Tests: * `test_registry/test_resolver.py` — 10 new tests for adhoc parsing (pinned tag/branch/SHA, omitted ref, dashes/dots, malformed forms, no config lookup, named-registry not affected, error message hints at adhoc fallback) * `test_registry/test_cache.py` — 7 new tests for `fetch_workflow_adhoc` + `resolve_and_fetch` (cache namespace, isolation from named registries, cache hit, dispatcher behavior for each kind) * `test_registry/test_integration.py` — 3 new end-to-end tests (resolve + fetch via unifier, no-config-required, coexistence with named registries) * `test_engine/test_subworkflow.py` — 2 new tests for adhoc as `type: workflow` agent's `workflow:` field * `test_config/test_validator.py` — 2 new tests for adhoc validation * Existing tests updated where the error message changed from "Failed to fetch registry sub-workflow" to "Failed to fetch sub-workflow" (the unifier uses a kind-agnostic message). Docs: * `docs/design/registry.md` — new "Ad-hoc references" section with motivation, syntax, disambiguation rule, caching, auth, and cross-team composition example. * `docs/workflow-syntax.md` — adds adhoc form to the sub-workflow reference examples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Closes #189.
What
Adds a new reference syntax that lets you reference a workflow in any GitHub repo without pre-installing a registry:
Works everywhere existing registry references work —
conductor run,conductor validate,conductor resume, and as a sub-workflow insidetype: workflowagents (the engine + validator path added in #172 picks it up transparently).Disambiguation rule
If the part after
@contains/, it's interpreted as a literalowner/repoand fetched directly from GitHub. If it doesn't, it's a configured registry name (existing behavior). Registry names disallow/, so the two forms can't collide.How
Resolver
registry/resolver.pyextendsResolvedRefwithkind="adhoc"and(adhoc_owner, adhoc_repo)fields._parse_registry_refbranches on whether the registry slot contains/. The_looks_like_file_pathheuristic now yields to the registry parser whenever@is present in the ref, so refs containing/in the registry slot aren't misclassified as file paths.Cache
registry/cache.pyaddsfetch_workflow_adhoc()which constructs a syntheticRegistryEntry(type=github, source=f"{owner}/{repo}")and reuses the existing fetch + cache pipeline. Cache namespace:~/.conductor/cache/registries/_adhoc/<owner>/<repo>/<workflow>/<sha[:12]>/. The_adhocsegment is reserved (named registries reject names containing/).Unifier (small refactor)
Adds
resolve_and_fetch(ResolvedRef) -> Pathas a single dispatcher used by the CLI, engine, and validator so each call site doesn't have to switch onResolvedRef.kind. Collapses 6 duplicatedresolve_ref + fetch_workflowblocks (4 incli/app.py, 1 in engine, 1 in validator) into one-liners. The engine + validator pick up adhoc support transparently as a result.Auth + caching unchanged
Same as named GitHub registries — public repos work; private uses
gh auth tokenautomatically. SHA-pinned refs are deterministic across resume; mutable refs (#main, omitted) re-resolve.Tests
test_resolver.pytest_cache.pyfetch_workflow_adhoccache layout, isolation from named registries, cache hit,resolve_and_fetchdispatcher per kindtest_integration.pytest_subworkflow.pytype: workflowagent'sworkflow:field (success + fetch-failure)test_validator.pyAll 2472 tests pass;
make checkclean.Files