You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Proposal] Make the code host an extension point of Source, so a deployment can run on GitLab (or any non-GitHub host)
#11845
We want to run dsh-superagent-prototype against a self-hosted GitLab (17.x CE, nested groups) while the team discusses work in Feishu. Reading docs/008-cookbook.md plus src/sources/kind.ts, the Source protocol looked sufficient: a gitlab SourceKind can translate MR notes and diff discussions into Triggers, define its Threads, reply through a Channel, serve assessment context, own its Policy part and admission rows, and even end a Ticket when the MR merges, because SourceContext already exposes threads.describe/patch, tickets.bindThread, tickets.threadClosed, notify, state and report (src/sources/host.ts:31-66). We also confirmed that Policy validation, admission tables, channels, links, thread context, find/repliesTo and execution-environment variables are all generic over registered kinds (src/sources/registry.ts:20-125, src/app/execution-environment.ts:31-34).
So the plugin-shaped work is doable. What is not doable from a plugin is the code host itself: the deployment still assumes GitHub in four places that a Source kind cannot reach.
The four hard-coded points
Startup requires a GitHub repository and a gh account.Setup.resolve unconditionally calls resolveDeployment(machine, settings.sources.github?.config, probe) (src/app/setup.ts:76-98), and resolveDeployment throws repositoryUnset / agentCliUnset / repositoryUnreadable when the config or the CLI is missing or cannot push (src/deployment.ts:87-106). There is no way to start a deployment whose code host is GitLab. Related: Deployment is { repository, github: { cli, login, noreply } } (src/deployment.ts:53-56), and the repository regex accepts a single namespace (src/deployment.ts:46), which rejects nested GitLab group paths such as group/sub/project.
The worker environment only knows gh and github.com.writeEnvironment writes a gh shim, a credential helper bound to https://github.com, and insteadOf git@github.com: rewrites (src/infra/environment.ts:79-116); verifyEnvironment asserts the credential.https://github.com.helper value (:138-143); WORKER_COMMANDS is the constant ['gh', 'git', 'lark-cli'] (:18). A Source kind can declare environment variables but cannot contribute a CLI shim, a credential helper, or a git host.
The API transport is GitHub-shaped.Commands.api always prefixes repos/<repository>/ and refuses escaped paths, and Commands.pages relies on gh api --paginate --slurp (src/infra/commands.ts:82-98). A non-GitHub Source has to call run() directly and re-implement paging and error-code mapping, so the transport cannot be shared.
The Ticket ↔ change-request binding lives outside the Source protocol.orchestrator_report's pr_number is resolved with readPull and validated by claimPull (src/app/tools/executor.ts:82-91, src/sources/github-pull.ts:35-84), and the same GitHub modules are imported directly by cards.pullOf (src/app/cards.ts:4), runtime.pullEndNotices (src/app/runtime.ts:36,151), assessment.readRepositoryPath (src/app/assessment.ts:11) and the github/ resources (src/app/resources.ts:54). A Source kind has no way to say 'this is how a change request is read, claimed and linked' or 'this is how you read a file from the repository'.
Impact
Any deployment on another code host has to patch the core, and the patched files are exactly the ones covered by the GitHub-only specs (tests/sources.spec.ts, tests/commands.spec.ts, tests/deployment.spec.ts, tests/environment.spec.ts, tests/github-read.spec.ts). A private fork then drifts from the prototype, which contradicts the README's 'review and adapt it' framing: adaptation currently means forking, not extending.
Proposed minimal change (all of it additive)
We are happy to send a PR. Our suggested shape, chosen to keep the existing GitHub path as the default implementation so the current suite stays green:
Add an optional codeHost to SourceKind (or a sibling registered port) providing:
project: { id, path, url }
api(schema, path) and pages(item, path) (GitLab addresses sub-resources by numeric project id)
readFile(path, ref)
change: { read(n), claim(ticketId, change) } — the PR/MR rules of github-pull.ts
link(kind, n)
account(): { cli, login, commitEmail }
Setup.resolve resolves the code host from whichever configured Source declares one, keeping resolveDeployment as the GitHub implementation. A deployment with no GitHub then starts normally.
Deployment gains codeHost: { kind, host, project, account, commitEmail }; repository/github stay as the GitHub specialization, and repository-name validation moves into the kind's schema (so nested namespaces work).
writeEnvironment / verifyEnvironment take { cli, host, credentialHelper, caFile? } from the code host instead of the GitHub account, and WORKER_COMMANDS is derived from the registered kinds.
Commands.api/pages delegate to the port; orchestrator_report takes a change-request reference resolved by the port instead of pr_number; cards, runtime.pullEndNotices, assessment.readRepositoryPath and the source resources look up the port of the change's source instead of importing the GitHub modules.
Notes from a real instance, in case it helps shape the port: nested group paths are the norm there (most projects are 3 or 4 segments); usernames contain . and _, which the current allowed-list regex ^[A-Za-z0-9-]+$ (src/sources/github.ts:72-73) silently rejects; gitlab api has no --jq and no --slurp, so the port should validate JSON with the existing zod schemas rather than parse in the CLI; and the approvals API exists but approval rules are a paid tier, so any 'approval covers this commit' delivery rule cannot be relied on there.
Questions
Is GitHub-only an intentional scope for the prototype? If yes, we will keep a small private patch and stop here — a clear answer is more useful to us than a patch that rots.
If not, is the shape above acceptable, or would you prefer a narrower cut (for example only points 1 and 4, leaving the transport and the environment to the kind)?
We can also contribute the gitlab SourceKind itself, shipped in its own plugin entry and disabled by default, exactly as the Feishu Source is (cordis.patch.yml). Would that be welcome, or is the repository meant to stay a two-Source reference implementation?
Thanks — the domain model and the cookbook are unusually clear, which is why this is a question about one seam rather than about the architecture.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
What we are trying to do
We want to run
dsh-superagent-prototypeagainst a self-hosted GitLab (17.x CE, nested groups) while the team discusses work in Feishu. Readingdocs/008-cookbook.mdplussrc/sources/kind.ts, the Source protocol looked sufficient: agitlabSourceKind can translate MR notes and diff discussions into Triggers, define its Threads, reply through a Channel, serve assessment context, own its Policy part and admission rows, and even end a Ticket when the MR merges, becauseSourceContextalready exposesthreads.describe/patch,tickets.bindThread,tickets.threadClosed,notify,stateandreport(src/sources/host.ts:31-66). We also confirmed that Policy validation, admission tables, channels, links, thread context,find/repliesToand execution-environment variables are all generic over registered kinds (src/sources/registry.ts:20-125,src/app/execution-environment.ts:31-34).So the plugin-shaped work is doable. What is not doable from a plugin is the code host itself: the deployment still assumes GitHub in four places that a Source kind cannot reach.
The four hard-coded points
ghaccount.Setup.resolveunconditionally callsresolveDeployment(machine, settings.sources.github?.config, probe)(src/app/setup.ts:76-98), andresolveDeploymentthrowsrepositoryUnset/agentCliUnset/repositoryUnreadablewhen the config or the CLI is missing or cannot push (src/deployment.ts:87-106). There is no way to start a deployment whose code host is GitLab. Related:Deploymentis{ repository, github: { cli, login, noreply } }(src/deployment.ts:53-56), and therepositoryregex accepts a single namespace (src/deployment.ts:46), which rejects nested GitLab group paths such asgroup/sub/project.ghandgithub.com.writeEnvironmentwrites aghshim, a credential helper bound tohttps://github.com, andinsteadOf git@github.com:rewrites (src/infra/environment.ts:79-116);verifyEnvironmentasserts thecredential.https://github.com.helpervalue (:138-143);WORKER_COMMANDSis the constant['gh', 'git', 'lark-cli'](:18). A Source kind can declare environment variables but cannot contribute a CLI shim, a credential helper, or a git host.Commands.apialways prefixesrepos/<repository>/and refuses escaped paths, andCommands.pagesrelies ongh api --paginate --slurp(src/infra/commands.ts:82-98). A non-GitHub Source has to callrun()directly and re-implement paging and error-code mapping, so the transport cannot be shared.orchestrator_report'spr_numberis resolved withreadPulland validated byclaimPull(src/app/tools/executor.ts:82-91,src/sources/github-pull.ts:35-84), and the same GitHub modules are imported directly bycards.pullOf(src/app/cards.ts:4),runtime.pullEndNotices(src/app/runtime.ts:36,151),assessment.readRepositoryPath(src/app/assessment.ts:11) and thegithub/resources (src/app/resources.ts:54). A Source kind has no way to say 'this is how a change request is read, claimed and linked' or 'this is how you read a file from the repository'.Impact
Any deployment on another code host has to patch the core, and the patched files are exactly the ones covered by the GitHub-only specs (
tests/sources.spec.ts,tests/commands.spec.ts,tests/deployment.spec.ts,tests/environment.spec.ts,tests/github-read.spec.ts). A private fork then drifts from the prototype, which contradicts the README's 'review and adapt it' framing: adaptation currently means forking, not extending.Proposed minimal change (all of it additive)
We are happy to send a PR. Our suggested shape, chosen to keep the existing GitHub path as the default implementation so the current suite stays green:
codeHosttoSourceKind(or a sibling registered port) providing:project: { id, path, url }api(schema, path)andpages(item, path)(GitLab addresses sub-resources by numeric project id)readFile(path, ref)change: { read(n), claim(ticketId, change) }— the PR/MR rules ofgithub-pull.tslink(kind, n)account(): { cli, login, commitEmail }Setup.resolveresolves the code host from whichever configured Source declares one, keepingresolveDeploymentas the GitHub implementation. A deployment with no GitHub then starts normally.DeploymentgainscodeHost: { kind, host, project, account, commitEmail };repository/githubstay as the GitHub specialization, and repository-name validation moves into the kind's schema (so nested namespaces work).writeEnvironment/verifyEnvironmenttake{ cli, host, credentialHelper, caFile? }from the code host instead of the GitHub account, andWORKER_COMMANDSis derived from the registered kinds.Commands.api/pagesdelegate to the port;orchestrator_reporttakes a change-request reference resolved by the port instead ofpr_number;cards,runtime.pullEndNotices,assessment.readRepositoryPathand the source resources look up the port of the change's source instead of importing the GitHub modules.Notes from a real instance, in case it helps shape the port: nested group paths are the norm there (most projects are 3 or 4 segments); usernames contain
.and_, which the current allowed-list regex^[A-Za-z0-9-]+$(src/sources/github.ts:72-73) silently rejects;gitlab apihas no--jqand no--slurp, so the port should validate JSON with the existing zod schemas rather than parse in the CLI; and the approvals API exists but approval rules are a paid tier, so any 'approval covers this commit' delivery rule cannot be relied on there.Questions
gitlabSourceKind itself, shipped in its own plugin entry and disabled by default, exactly as the Feishu Source is (cordis.patch.yml). Would that be welcome, or is the repository meant to stay a two-Source reference implementation?Thanks — the domain model and the cookbook are unusually clear, which is why this is a question about one seam rather than about the architecture.
All reactions