docs(forge): clarify discovery limits and provider contracts
This commit is contained in:
1 parent
6abae82a41
commit
1abf0f55fe
10 files changed
+169
-12
No files matched your search
@@ -32,6 +32,11 @@ interface ChangeRequestRef {
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Canonical identity of a change request across providers; built by `changeRequestKey`. */
|
||||
type ChangeRequestKey = Branded<'ChangeRequestKey'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Host-specific access registered with `ctx.forge`. One provider serves one
|
||||
@@ -60,6 +65,55 @@ interface ForgeProvider {
|
||||
|
||||
`ctx.forge.watch` shares one polling loop among the watchers of a change request and delivers every result as an update. Each forge has at most one fetch in flight. The [registry README](../../packages/experimental/forge/README.md#use-this-package) owns the poll cadence, backoff, and timeout rules.
|
||||
|
||||
```ts type-equiv
|
||||
/** Lifecycle state of a change request. */
|
||||
type ChangeRequestState = 'open' | 'closed' | 'merged'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Normalized outcome of one status check. */
|
||||
type CheckState = 'pending' | 'success' | 'failure' | 'neutral' | 'skipped' | 'cancelled'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** One status check reported for the head commit of a change request. */
|
||||
interface ChangeRequestCheck {
|
||||
/** Check name as the host displays it. */
|
||||
readonly name: string
|
||||
/** Normalized check outcome. */
|
||||
readonly state: CheckState
|
||||
/** Web page for the check, or null when the host reports none. */
|
||||
readonly url: string | null
|
||||
/** Workflow that produced the check, or null when the host reports none. */
|
||||
readonly workflow: string | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Combined outcome of all checks on the head commit. */
|
||||
type ChecksRollup = 'none' | 'pending' | 'success' | 'failure'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Whether the change request can merge into its base without conflicts. */
|
||||
type Mergeability = 'mergeable' | 'conflicting' | 'unknown'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Aggregate review outcome required by the host's merge rules. */
|
||||
type ReviewDecision = 'approved' | 'changes-requested' | 'review-required' | 'none'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Why a provider could not read a change request. */
|
||||
interface ForgeFailure {
|
||||
/** `not-found`: the host does not expose it; `unauthenticated`: credentials are missing or rejected; `unavailable`: any other failure. */
|
||||
readonly code: 'not-found' | 'unauthenticated' | 'unavailable'
|
||||
/** Human-readable cause. */
|
||||
readonly message: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Status of one change request at the time its provider read it. */
|
||||
interface ChangeRequestSnapshot {
|
||||
|
||||
@@ -32,6 +32,11 @@ interface ChangeRequestRef {
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Canonical identity of a change request across providers; built by `changeRequestKey`. */
|
||||
type ChangeRequestKey = Branded<'ChangeRequestKey'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Host-specific access registered with `ctx.forge`. One provider serves one
|
||||
@@ -60,6 +65,55 @@ interface ForgeProvider {
|
||||
|
||||
`ctx.forge.watch` 让同一变更请求的所有监听方共享一个轮询循环,并把每次结果作为更新送达。每个 forge 同时最多只有一次读取在进行。轮询节奏、退避与超时规则见[注册表 README](../../packages/experimental/forge/README.zh.md#use-this-package)。
|
||||
|
||||
```ts type-equiv
|
||||
/** Lifecycle state of a change request. */
|
||||
type ChangeRequestState = 'open' | 'closed' | 'merged'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Normalized outcome of one status check. */
|
||||
type CheckState = 'pending' | 'success' | 'failure' | 'neutral' | 'skipped' | 'cancelled'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** One status check reported for the head commit of a change request. */
|
||||
interface ChangeRequestCheck {
|
||||
/** Check name as the host displays it. */
|
||||
readonly name: string
|
||||
/** Normalized check outcome. */
|
||||
readonly state: CheckState
|
||||
/** Web page for the check, or null when the host reports none. */
|
||||
readonly url: string | null
|
||||
/** Workflow that produced the check, or null when the host reports none. */
|
||||
readonly workflow: string | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Combined outcome of all checks on the head commit. */
|
||||
type ChecksRollup = 'none' | 'pending' | 'success' | 'failure'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Whether the change request can merge into its base without conflicts. */
|
||||
type Mergeability = 'mergeable' | 'conflicting' | 'unknown'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Aggregate review outcome required by the host's merge rules. */
|
||||
type ReviewDecision = 'approved' | 'changes-requested' | 'review-required' | 'none'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Why a provider could not read a change request. */
|
||||
interface ForgeFailure {
|
||||
/** `not-found`: the host does not expose it; `unauthenticated`: credentials are missing or rejected; `unavailable`: any other failure. */
|
||||
readonly code: 'not-found' | 'unauthenticated' | 'unavailable'
|
||||
/** Human-readable cause. */
|
||||
readonly message: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Status of one change request at the time its provider read it. */
|
||||
interface ChangeRequestSnapshot {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: d152484eb41ac6b4
|
||||
zh: 2f8a621b991260a1
|
||||
/deepseek-ai-dsh-experimental-forge-session/use-this-package:
|
||||
en: 23898b4f5a21b3b5
|
||||
zh: 6d1251b9b862fad8
|
||||
en: 0b75d06236040dd9
|
||||
zh: ff30c83707003bca
|
||||
/deepseek-ai-dsh-experimental-forge-session/understand-the-implementation:
|
||||
en: ccc7ad69f5837528
|
||||
zh: 369c08bf4f7e6e4d
|
||||
@@ -30,8 +30,8 @@
|
||||
en: 1bdaeb7d102c23d8
|
||||
zh: 37a3f0bd58f68363
|
||||
/deepseek-ai-dsh-experimental-forge-session/known-limitations-and-deferred-work:
|
||||
en: 3a8277e1f874da4a
|
||||
zh: 987b0e6030583112
|
||||
en: 08bc0a8f37be268f
|
||||
zh: 23e748f82384238b
|
||||
/deepseek-ai-dsh-experimental-forge-session/known-limitations-and-deferred-work/dev-note:
|
||||
en: e67f633dc6fe7773
|
||||
zh: 3bb0c20c93f16f98
|
||||
@@ -36,7 +36,7 @@ Mount the service with the forge registry, a provider, and the Session projectio
|
||||
|
||||
`ctx.forgeSession.changeRequests(session)` returns the visible bindings in first-recorded order. Each entry carries the URL, its `origin` (`created`, `discovered`, or `tracked`), the provider reference or `null` when no provider serves the URL, and the latest polling result or `null`. `watch(session, listener)` is an effect on the calling fiber: while a Session has a watcher, every visible binding that a provider serves is polled, and listeners receive the complete list after each later binding change or polling result. `track(session, url)` binds a URL under the provider's canonical form, or restores a dismissed binding, and throws when no provider recognizes the URL. `dismiss(session, url)` hides a visible binding; other URLs leave the log unchanged.
|
||||
|
||||
A `bash` or `pwsh` call binds evidence only from its first result, and only when the tool execution itself succeeded; the command's exit status does not matter. A command containing `gh pr create` or `gh pr new` binds every `/pull/<number>` URL in its text output. A command containing `git push`, optionally with leading `-C`, `-c`, or long Git options, records each branch listed under a `To <remote>` line as created, updated, forced, or already up to date; rejected, deleted, tag, and non-branch refs are skipped. For each pushed branch whose remote a provider serves, the service runs `ctx.forge.findByHead` within `discoveryTimeoutMs` and appends a `discover` record for every returned change request that is not bound, including dismissed ones. Every push repeats the lookup; failures are logged as warnings. URLs that differ only in scheme, letter case, query, fragment, or trailing slashes name the same binding.
|
||||
A `bash` or `pwsh` call binds evidence only from its first result, and only when the tool execution itself succeeded; the command's exit status does not matter. A command containing `gh pr create` or `gh pr new` binds every `/pull/<number>` URL in its text output. A command containing `git push`, optionally with leading `-C`, `-c`, or long Git options, records each branch listed under a `To <remote>` line as created, updated, forced, or already up to date; rejected and deleted refs, new tags, and explicit refs outside `refs/heads/` are skipped. For each pushed branch whose remote a provider serves, the service runs `ctx.forge.findByHead` within `discoveryTimeoutMs` and appends a `discover` record for every returned change request that is not bound. Dismissed bindings remain bound and are skipped; only `track` restores them. Every push repeats the lookup; failures are logged as warnings. URLs that differ only in scheme, letter case, query, fragment, or trailing slashes name the same binding.
|
||||
|
||||
Binding actions are `plugin:forge-session/binding` records listed in the [experimental persistence catalog](../../../docs/experimental-persistence-catalog.md); fork and resume carry them with the shell events, so a fork starts with its parent's bindings. `discoveryTimeoutMs` is listed in the [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-forge-session).
|
||||
|
||||
@@ -85,6 +85,8 @@ None. Binding records are ignorable plugin records outside the model-visible con
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- **Textual command recognition** — only `bash` and `pwsh` commands containing `gh pr create`, `gh pr new`, or `git push` count, and only `/pull/<number>` URLs bind. Scripts, aliases, `gh api` calls, other CLIs such as `glab`, and `git push --porcelain` output are not recognized; `track` binds such change requests.
|
||||
- **Background output** — `run_in_background: true` returns a job id without binding evidence. A foreground command moved to the background contributes only the output in its first result; later job output does not add bindings. Use `track` for change requests missing from that result.
|
||||
- **Unqualified tag updates** — a forced or up-to-date tag printed without `refs/tags/` is treated as a branch. Discovery may bind an open change request for a branch with the same name; use `dismiss` to hide an incorrect binding.
|
||||
- **Push-triggered discovery** — a change request opened elsewhere after the latest push appears only after another push or a `track`.
|
||||
- **Host API only** — no client view or model tool reads the bindings yet.
|
||||
- **Per-Session bindings** — subagent Sessions keep their own bindings; a parent Session does not aggregate them.
|
||||
|
||||
@@ -36,7 +36,7 @@ kind: "package-reference"
|
||||
|
||||
`ctx.forgeSession.changeRequests(session)` 按首次记录的顺序返回可见的绑定。每一项包含 URL、其 `origin`(`created`、`discovered` 或 `tracked`)、提供方引用(没有提供方服务该 URL 时为 `null`)以及最近一次轮询结果(尚无结果时为 `null`)。`watch(session, listener)` 是调用方 fiber 上的 effect:Session 有监听方期间,提供方服务的每个可见绑定都会被轮询;此后每次绑定变化或轮询结果到达时,监听方都会收到完整列表。`track(session, url)` 以提供方的规范形式绑定 URL,或恢复已隐藏的绑定;没有提供方识别该 URL 时抛错。`dismiss(session, url)` 隐藏一个可见绑定;其他 URL 不会改变日志。
|
||||
|
||||
`bash` 或 `pwsh` 调用只从其第一个结果中提取证据,且仅当工具执行本身成功时才提取;命令的退出状态不影响结果。包含 `gh pr create` 或 `gh pr new` 的命令会绑定其文本输出中的每个 `/pull/<number>` URL。包含 `git push` 的命令(可带前置的 `-C`、`-c` 或 Git 长选项)会记录 `To <remote>` 行下列出的每个已创建、已更新、已强制推送或已是最新的分支;被拒绝、已删除、标签以及非分支的引用会被跳过。对远端由提供方服务的每个被推送分支,服务在 `discoveryTimeoutMs` 内调用 `ctx.forge.findByHead`,并为返回的每个尚未绑定(包括已隐藏)的变更请求追加一条 `discover` 记录。每次推送都会重新查找;失败记录为警告日志。仅在协议、大小写、查询、片段或末尾斜杠上不同的 URL 指向同一个绑定。
|
||||
`bash` 或 `pwsh` 调用只从其第一个结果中提取证据,且仅当工具执行本身成功时才提取;命令的退出状态不影响结果。包含 `gh pr create` 或 `gh pr new` 的命令会绑定其文本输出中的每个 `/pull/<number>` URL。包含 `git push` 的命令(可带前置的 `-C`、`-c` 或 Git 长选项)会记录 `To <remote>` 行下列出的每个已创建、已更新、已强制推送或已是最新的分支;被拒绝、已删除的引用、新标签,以及明确位于 `refs/heads/` 之外的引用会被跳过。对远端由提供方服务的每个被推送分支,服务在 `discoveryTimeoutMs` 内调用 `ctx.forge.findByHead`,并为返回的每个尚未绑定的变更请求追加一条 `discover` 记录。已隐藏的绑定仍视为已绑定而被跳过;只有 `track` 会恢复它们。每次推送都会重新查找;失败记录为警告日志。仅在协议、大小写、查询、片段或末尾斜杠上不同的 URL 指向同一个绑定。
|
||||
|
||||
绑定操作是 `plugin:forge-session/binding` 插件记录,列于[实验性持久化目录](../../../docs/experimental-persistence-catalog.zh.md);fork 与恢复会把它们与 shell 事件一同带上,因此 fork 出的 Session 从父 Session 的绑定开始。`discoveryTimeoutMs` 见[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-forge-session)。
|
||||
|
||||
@@ -85,6 +85,8 @@ kind: "package-reference"
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- **按文本识别命令**——只有包含 `gh pr create`、`gh pr new` 或 `git push` 的 `bash` 与 `pwsh` 命令计入,也只有 `/pull/<number>` URL 会被绑定。脚本、别名、`gh api` 调用、`glab` 等其他 CLI 以及 `git push --porcelain` 输出都无法识别;这类变更请求需用 `track` 绑定。
|
||||
- **后台输出**——`run_in_background: true` 只返回作业 id,不含绑定证据。转入后台的前台命令只贡献首个结果中的输出;后续作业输出不会补充绑定。首个结果中未出现的变更请求需用 `track` 绑定。
|
||||
- **未带完整引用的标签更新**——强制更新或已是最新的标签,如果输出未带 `refs/tags/`,会被当成分支。发现过程可能绑定同名分支的打开状态变更请求;可用 `dismiss` 隐藏错误绑定。
|
||||
- **仅由推送触发发现**——最近一次推送之后在别处打开的变更请求,要等到下一次推送或 `track` 才会出现。
|
||||
- **只有宿主 API**——目前还没有客户端视图或模型工具读取这些绑定。
|
||||
- **按 Session 绑定**——subagent 的 Session 保有自己的绑定;父 Session 不会汇总它们。
|
||||
|
||||
@@ -62,8 +62,9 @@ export function changeRequestUrls(text: string): string[] {
|
||||
/**
|
||||
* Find branches that `git push` output reports as created, updated, forced,
|
||||
* or already up to date. Each ref line belongs to the nearest preceding
|
||||
* `To <remote>` line. Rejected and deleted refs, tags, and refs outside
|
||||
* `refs/heads/` are excluded.
|
||||
* `To <remote>` line. Rejected and deleted refs, new tags, and explicit refs
|
||||
* outside `refs/heads/` are excluded. Unqualified tag updates cannot be
|
||||
* distinguished from branch updates and are included.
|
||||
* @param text - rendered shell result, stdout and stderr.
|
||||
* @returns distinct pushed branches in order of first appearance.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: d152484eb41ac6b4
|
||||
zh: 2f8a621b991260a1
|
||||
/deepseek-ai-dsh-experimental-forge/use-this-package:
|
||||
en: bbb605854ab9590d
|
||||
zh: 5b7426a09b125569
|
||||
en: 9bc1c89b6c9a3130
|
||||
zh: 196f7bb36c99743a
|
||||
/deepseek-ai-dsh-experimental-forge/understand-the-implementation:
|
||||
en: e193cb547c47069b
|
||||
zh: faa517522402ea41
|
||||
|
||||
@@ -34,7 +34,9 @@ Mount the registry with at least one provider package, such as the GitHub provid
|
||||
|
||||
`ctx.forge.register(provider)` is an effect on the calling fiber and returns its disposer. One provider serves one forge family; registering a second provider for the same family throws. `repositoryFromRemote(url)` and `changeRequestFromUrl(url)` ask providers in registration order and return the first match. `changeRequestUrl(ref)` and `findByHead(repository, branch, signal)` delegate to the provider of the reference's forge and throw when none is registered.
|
||||
|
||||
`ctx.forge.watch(ref, listener)` is also an effect. The first watcher of a change request polls it at once; `latest(ref)` returns the most recent result. Each forge has at most one fetch in flight, and that fetch includes every due change request of the forge. An open change request is polled every `activeIntervalMs` while checks are pending or mergeability is unknown, and every `idleIntervalMs` otherwise. Closed and merged change requests are not polled again. A failed read repeats after `activeIntervalMs` doubled for each consecutive failure, up to `maxBackoffMs`. A fetch that exceeds `fetchTimeoutMs` settles as an `unavailable` failure whose message names `FORGE_FETCH_TIMEOUT`. Unregistering a provider settles its watched change requests as `unavailable`. Polling stops when the last watcher leaves. The [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-forge) lists the defaults.
|
||||
`ctx.forge.watch(ref, listener)` is also an effect. The first watcher of a change request polls it at once; `latest(ref)` returns the most recent result. Each forge has at most one fetch in flight, and that fetch includes every due change request of the forge. An open change request is polled every `activeIntervalMs` while checks are pending or mergeability is unknown, and every `idleIntervalMs` otherwise. Closed and merged change requests are not polled again. A failed read repeats after `activeIntervalMs` doubled for each consecutive failure, up to `maxBackoffMs`. Unregistering a provider settles its watched change requests as `unavailable`. Polling stops when the last watcher leaves. The [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-forge) lists the defaults.
|
||||
|
||||
Reaching `fetchTimeoutMs` aborts the fetch signal with `FORGE_FETCH_TIMEOUT`. Providers must stop their work and settle when the signal aborts; rejected fetches become `unavailable` failures. A provider that ignores cancellation can stall polling and service disposal.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@ kind: "package-reference"
|
||||
|
||||
`ctx.forge.register(provider)` 是调用方 fiber 上的 effect,返回对应的 disposer。一个提供方服务一个 forge 家族;为同一家族注册第二个提供方会抛错。`repositoryFromRemote(url)` 与 `changeRequestFromUrl(url)` 按注册顺序询问提供方并返回第一个匹配结果。`changeRequestUrl(ref)` 与 `findByHead(repository, branch, signal)` 委托给引用所属 forge 的提供方;没有注册提供方时抛错。
|
||||
|
||||
`ctx.forge.watch(ref, listener)` 同样是 effect。变更请求的第一个监听方会立即触发轮询;`latest(ref)` 返回最近一次结果。每个 forge 同时最多只有一次读取在进行,这次读取包含该 forge 所有到期的变更请求。打开状态的变更请求在检查未完成或可合并性未知时每 `activeIntervalMs` 轮询一次,否则每 `idleIntervalMs` 轮询一次。已关闭和已合并的变更请求不再轮询。读取失败后,重试间隔为 `activeIntervalMs` 按连续失败次数翻倍,最长 `maxBackoffMs`。读取超过 `fetchTimeoutMs` 时,以消息中包含 `FORGE_FETCH_TIMEOUT` 的 `unavailable` 失败结束。注销提供方会让其被监听的变更请求以 `unavailable` 结束。最后一个监听方离开后轮询停止。默认值见[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-forge)。
|
||||
`ctx.forge.watch(ref, listener)` 同样是 effect。变更请求的第一个监听方会立即触发轮询;`latest(ref)` 返回最近一次结果。每个 forge 同时最多只有一次读取在进行,这次读取包含该 forge 所有到期的变更请求。打开状态的变更请求在检查未完成或可合并性未知时每 `activeIntervalMs` 轮询一次,否则每 `idleIntervalMs` 轮询一次。已关闭和已合并的变更请求不再轮询。读取失败后,重试间隔为 `activeIntervalMs` 按连续失败次数翻倍,最长 `maxBackoffMs`。注销提供方会让其被监听的变更请求以 `unavailable` 结束。最后一个监听方离开后轮询停止。默认值见[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-forge)。
|
||||
|
||||
达到 `fetchTimeoutMs` 时,读取信号会以 `FORGE_FETCH_TIMEOUT` 中止。提供方必须在信号中止时停止工作并结束请求;被拒绝的读取会转为 `unavailable` 失败。忽略取消的提供方可能阻塞轮询和服务销毁。
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -2532,6 +2532,46 @@
|
||||
"symbol": "ForgeProvider",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ChangeRequestKey",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ChangeRequestState",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "CheckState",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ChangeRequestCheck",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ChecksRollup",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "Mergeability",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ReviewDecision",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ForgeFailure",
|
||||
"source": "packages/experimental/forge/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/forge.md",
|
||||
"symbol": "ChangeRequestSnapshot",
|
||||
|
||||
Reference in new issue
Block a user