Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cfd7966
feat(proto): clone from Git Server snapshots and deliver Revisions ov…
ObsisMc Oct 17, 2026
0b6a15d
feat(proto): expose frozen snapshot clone inputs on operations
ObsisMc Oct 17, 2026
138e2ab
feat(controlgrpc): carry snapshot clone and Git delivery inputs on th…
ObsisMc Oct 17, 2026
24f568c
feat(config): configure the Git Server and query its refs
ObsisMc Oct 17, 2026
fff8351
feat(core): clone Workspaces from the Binding's current snapshot
ObsisMc Oct 17, 2026
051da6a
refactor!: retire tenant-level clones
ObsisMc Oct 17, 2026
8502b28
feat(core): deliver and resume Revisions over Git
ObsisMc Oct 17, 2026
e2d332e
feat(core): grant executions their Git Server refs
ObsisMc Oct 17, 2026
e4b49a7
feat(core): give Agent sessions the Project's Workspace directory
ObsisMc Oct 17, 2026
6871147
docs: describe Git-native Workspaces and Revision delivery
ObsisMc Oct 17, 2026
6c32c69
test(revision): preserve the Git Server across simulator restarts
ObsisMc Oct 17, 2026
1750af7
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 19, 2026
a51ee11
feat(proto)!: reserve the retired BUNDLE_FAILED revision failure reason
ObsisMc Oct 19, 2026
7c1c5ab
fix(core): keep WorkflowRun Workspaces out of the session Workspace d…
ObsisMc Oct 19, 2026
f388862
fix(core): record only the sync's ID when a clone step fails on it
ObsisMc Oct 19, 2026
fd1936b
docs: mark the Git-native snapshot, clone and delivery ADRs approved
ObsisMc Oct 19, 2026
b9f569f
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 19, 2026
afd6b00
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 19, 2026
1668ab5
test(integration): run the external line's tests on snapshot-backed P…
ObsisMc Oct 19, 2026
72cfac1
docs(repository): say publish only confirms the source ref exists
ObsisMc Oct 19, 2026
4b772e6
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 20, 2026
e721cc0
fix(migrations): renumber the internal Git line migrations after main…
ObsisMc Oct 20, 2026
3b0a151
docs: mark the Git Server code-hub decision approved in package READMEs
ObsisMc Oct 20, 2026
61a1564
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 20, 2026
6d51cad
fix(migrations): renumber the internal Git line migrations after main…
ObsisMc Oct 20, 2026
6468f01
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 20, 2026
4e5e00f
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 21, 2026
7a1a2af
Merge upstream/main into feat/183-internal-git-line
ObsisMc Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
571 changes: 34 additions & 537 deletions api/openapi.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion cmd/git-server-preset/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ wires dependencies and sets the exit code.

| Subcommand | Effect |
|---|---|
| `snapshot --project <id> (--commit <oid> \| --synthetic <n>)` | Writes `snapshots/<oid>` and the tracking ref, records a `succeeded` sync and makes it the current snapshot; `--synthetic` first creates a deterministic history of n commits |
| `snapshot --project <id> (--commit <oid> \| --synthetic <n>)` | Writes `snapshots/<oid>` and the tracking ref, advances the Binding's unfinished sync (such as the one a new Project starts with) to `succeeded` or records a new `succeeded` sync when there is none, and makes it the current snapshot; `--synthetic` first creates a deterministic history of n commits |
| `ref --project <id> --ref <name> --commit <oid>` | Writes one ref of any namespace (for example a Workspace branch to publish from); the commit must already be in the repository |
| `operation --project <id> --type sync\|publish --state <state> [--error-code <code>]` | Writes one Operation in the given state for waiting, failure and deletion scenarios; terminal states get `finished_at`. A sync moves the Binding's unfinished sync (such as the one written when the Project was created) to the given state and writes a new row only when there is none, so every sync state can be preset; a publish is always a new row |
| `check --project <id>` | Contract assertion: do the Git Server's refs and Cloud's records satisfy the contract |
Expand Down
2 changes: 1 addition & 1 deletion cmd/git-server-preset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

| 子命令 | 作用 |
|---|---|
| `snapshot --project <id> (--commit <oid> \| --synthetic <n>)` | 写入 `snapshots/<oid>` 与跟踪 ref,登记一行 `succeeded` 的同步,推进当前快照;`--synthetic` 先生成有 n 个提交的确定性历史 |
| `snapshot --project <id> (--commit <oid> \| --synthetic <n>)` | 写入 `snapshots/<oid>` 与跟踪 ref,把该 Binding 未结束的同步(例如建 Project 时的那一行)推进为 `succeeded`,没有时登记一行新的 `succeeded` 同步,并推进当前快照;`--synthetic` 先生成有 n 个提交的确定性历史 |
| `ref --project <id> --ref <name> --commit <oid>` | 写入任意 namespace 的一个 ref(例如作为发布源的 Workspace 分支);提交必须已在仓库中 |
| `operation --project <id> --type sync\|publish --state <state> [--error-code <code>]` | 写入一行给定状态的 Operation,用于等待、失败、删除等场景;终态写 `finished_at`。同步改写该 Binding 未结束的同步(例如建 Project 时的那一行)为给定状态,没有时才新写一行,因此任何同步状态都能预置;发布总是新写一行 |
| `check --project <id>` | 契约断言:Git Server 上的 ref 与 Cloud 记录是否符合契约 |
Expand Down
4 changes: 4 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ func run() (runErr error) {
if e != nil {
return e
}
store.GitServer, e = cfg.GitServer.Open()
if e != nil {
return e
}
configureCollaboration(store, cfg.Collaboration.DevelopmentFixtures, log)
// The B side of the Agent Run seam: Cloud's IssueRun/Thread lifecycle is driven by these five
// callbacks, each of which runs inside the control-plane transaction that just persisted the
Expand Down
5 changes: 3 additions & 2 deletions cmd/simulator/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

- **Durable demo fixture**: Initializes a real local Git repository fixture under `.local/demo/fixture` with initial commits and branches.
- **In-process cloud server**: Launches an ephemeral `httptest.Server` serving the complete Gin router, backed by a real PostgreSQL database.
- **Git Server double**: Keeps Project repositories under `.local/demo/git` in the Git Server volume layout and, standing in for the external line, records the fixture commit as the demo Project's snapshot (`SeedSnapshot`); the external repository URL is never contacted.
- **Substrate simulation**: Launches a local HTTP server exposing Substrate storage and effect simulation under `.local/demo/substrate`.
- **Ephemeral cryptography**: Generates in-memory Ed25519 keypairs for `gateway`, `controller`, `node`, and `user` roles to sign and verify short-lived JWT tokens without external IdP infrastructure.
- **Controller execution loop**:
1. Bootstraps a demo tenant and user.
2. Acquires a controller lease via `/internal/v1/controller-lease/acquire`.
3. Dispatches a project creation request via the public API (`POST /api/v1/tenants/{tid}/projects`).
4. Simulates Controller queue draining: executes the effect plan (scheduling sandboxes, registering nodes, and cloning the repository into the Workspace's data through a loopback gRPC `ExecutionService`).
4. Simulates Controller queue draining: executes the effect plan (scheduling sandboxes, registering nodes, registering Cloud's frozen clone input through a loopback gRPC `ExecutionService`, and cloning the snapshot from the Git Server double into the Workspace's data).
5. Verifies that the workspace reaches `ready` state and queries it through the public API.
6. Releases the controller lease cleanly.
7. Emits JSON summary to `stdout`.
Expand All @@ -26,4 +27,4 @@

See [cmd overview](../README.en.md), [Simulator internals](../../internal/simulator/README.en.md), and [Workspace lifecycle](../../docs/runtime/workspaces.en.md).

The in-process simulator explicitly uses the development-only retired clone fixture. Its plugin selection remains durable pending because no real plugin executor is available. It is not evidence of runtime control, management authentication or workload isolation; use cluster Compose and the real acceptance script.
The in-process simulator's Node clones snapshots only from the Git Server double. Its plugin selection remains durable pending because no real plugin executor is available. It is not evidence of runtime control, management authentication or workload isolation; use cluster Compose and the real acceptance script.
5 changes: 3 additions & 2 deletions cmd/simulator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

- **持久化演示夹具**:在 `.local/demo/fixture` 下初始化真实的本地 Git 仓库测试夹具,并包含初始提交与分支。
- **进程内 Cloud 服务**:拉起基于真实 PostgreSQL 数据库的临时 `httptest.Server`,提供完整的 Gin 路由服务。
- **Git Server 替身**:在 `.local/demo/git` 下按 Git Server 卷布局保存 Project 仓库,并代替外部线把夹具提交登记为演示 Project 的快照(`SeedSnapshot`);外部仓库 URL 从不访问。
- **Substrate 模拟**:在 `.local/demo/substrate` 下启动本地 HTTP 服务,提供 Substrate 存储和 Effect 模拟。
- **临时密钥材料**:在内存中为 `gateway`、`controller`、`node` 和 `user` 四种角色生成 Ed25519 密钥对,以便在不依赖外部 IdP 基础设施的情况下签发和验证短期 JWT token。
- **Controller 驱动执行主循环**:
1. 引导配置演示租户和用户。
2. 通过 `/internal/v1/controller-lease/acquire` 获取 Controller 独占租约。
3. 通过公开 API 发起项目创建请求(`POST /api/v1/tenants/{tid}/projects`)。
4. 模拟 Controller 清空队列:执行 Effect 计划(调度沙箱、注册节点,并通过 loopback gRPC `ExecutionService` 把仓库 clone 到 Workspace 数据中)。
4. 模拟 Controller 清空队列:执行 Effect 计划(调度沙箱、注册节点,并通过 loopback gRPC `ExecutionService` 登记 Cloud 冻结的 clone 输入,把快照从 Git Server 替身 clone 到 Workspace 数据中)。
5. 验证 Workspace 成功达到 `ready` 就绪状态,并通过公开 API 查询校验。
6. 正常释放 Controller 租约。
7. 向 `stdout` 输出 JSON 摘要。
Expand All @@ -26,4 +27,4 @@

参见 [cmd 入口总览](../README.md)、[模拟器内部实现](../../internal/simulator/README.md) 与 [Workspace 生命周期](../../docs/runtime/workspaces.md)。

进程内模拟器显式使用仅供开发的旧 clone 测试夹具;插件选择因缺少真实执行器保留持久 pending(待执行)。它不证明运行时独占、管理认证或工作负载隔离;这些保障须通过 cluster Compose 和真实验收脚本验证。
进程内模拟器的 Node 只从 Git Server 替身取快照;插件选择因缺少真实执行器保留持久 pending(待执行)。它不证明运行时独占、管理认证或工作负载隔离;这些保障须通过 cluster Compose 和真实验收脚本验证。
17 changes: 14 additions & 3 deletions cmd/simulator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func run() error {
if e != nil {
return e
}
store, e := core.NewDevelopmentStore(db)
store, e := core.NewStore(db)
if e != nil {
return e
}
Expand Down Expand Up @@ -95,7 +95,14 @@ func run() error {
gin.SetMode(gin.ReleaseMode)
cloud := httptest.NewServer(router.NewDevelopment(store, auth, zap.NewNop()))
defer cloud.Close()
substrate, e := simulator.NewSubstrate(filepath.Join(absolute, "substrate"), map[string]string{"https://example.invalid/repo.git": repo})
// The Git Server double holds the Project repositories; Workspaces clone its snapshots and
// deliveries push to it. The external repository URL below is never contacted.
gitServer, e := simulator.NewGitServer(filepath.Join(absolute, "git"))
if e != nil {
return e
}
store.GitServer = gitServer.Config()
substrate, e := simulator.NewSubstrate(filepath.Join(absolute, "substrate"), gitServer)
if e != nil {
return e
}
Expand Down Expand Up @@ -125,7 +132,7 @@ func run() error {
return e
}
defer func() { _ = conn.Close() }()
controller := &simulator.Controller{Client: client, SubstrateURL: external.URL, Executions: controlpb.NewExecutionServiceClient(conn)}
controller := &simulator.Controller{Client: client, SubstrateURL: external.URL, Executions: controlpb.NewExecutionServiceClient(conn), Git: gitServer}
controller.AgentRuns = controlpb.NewAgentRunServiceClient(conn)
controller.AgentNode, e = simulator.NewAgentNode(filepath.Join(*root, "agent-node"))
if e != nil {
Expand All @@ -140,6 +147,10 @@ func run() error {
if e != nil || status != 202 {
return fmt.Errorf("create: %d %v %v", status, e, created)
}
// Stand in for the external line's first sync so the main Workspace has a snapshot to clone.
if _, e := gitServer.SeedSnapshot(ctx, store.Pool, created.O("resource").S("id"), repo); e != nil {
return e
}
if e := controller.Drain(ctx); e != nil {
return e
}
Expand Down
16 changes: 16 additions & 0 deletions configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,22 @@ object_store:
secret_access_key_file: ""
upload_grant_ttl: 15m

git_server:
# The Git Server holding every Project repository (Revision D2). Workspaces clone Binding
# snapshots from it and Revisions are delivered to it; Cloud writes `repository_base_url` into
# clone, session and delivery inputs and verifies refs through the ref query listener with its own
# client certificate. Leaving `repository_base_url` empty runs without a Git Server: clone steps
# block as git_server_unconfigured and deliveries are skipped. The key is a file reference, never a
# value here.
# Overrides: CLOUD_GIT_SERVER_REPOSITORY_BASE_URL / _REF_QUERY_URL / _CA_FILE / _CLIENT_CERT_FILE /
# _CLIENT_KEY_FILE / _REF_QUERY_TIMEOUT.
repository_base_url: ""
ref_query_url: ""
ca_file: ""
client_cert_file: ""
client_key_file: ""
ref_query_timeout: 10s

repository:
# The external Git line (cloud/repo-executor, identity-access Credential Store, cloud/project sync
# and publish). Disabled, Projects are created but no sync runs and Git credentials cannot be
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-runs/issue-runs.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ written together with the phase on entering `releasing`, and accompanied by a ti

Whether the output was saved is stated by `result.deliveryState`: `saved` (a Revision with changes was
registered), `unchanged` (a Revision without changes was registered), `failed` (delivery was given up),
`skipped` (the deployment has no object storage, or the run was cancelled before any session). `failed`
`skipped` (the deployment has no object storage or no Git Server, or the run was cancelled before any session). `failed`
and `cancelled` runs still try to save their output and can have a Revision too.

On the timeline an Agent run has one `run.enqueued` activity, and on entering `releasing` a
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-runs/issue-runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Cloud 在接管上一阶段证据的事务里写入。
| `agent_failed`(Agent 失败)、`interrupted`(Node 中断或结果未知) | `failed` |

成果是否保存由 `result.deliveryState` 表达:`saved`(登记了有改动的 Revision)、`unchanged`(登记了没有改动的 Revision)、
`failed`(交付被放弃)、`skipped`(部署没有对象存储,或会话开始前就被取消)。`failed` 和 `cancelled` 的运行同样会尽量保存成果,
`failed`(交付被放弃)、`skipped`(部署没有对象存储或 Git Server,或会话开始前就被取消)。`failed` 和 `cancelled` 的运行同样会尽量保存成果,
也可以有 Revision。

时间线上,Agent 运行有一条 `run.enqueued` 活动,进入 `releasing` 时再追加 `run.completed`、`run.failed` 或 `run.cancelled`,
Expand Down
Loading
Loading