Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .fallowrc.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
// Generated by `bun run generate:*` (verified by `generate:check`).
"ignorePatterns": ["packages/gkit/generated/**"],
// Local-only helper that renders .npmrc from the vault; not a dependency.
"ignoreBinaries": ["latch"],
}
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# style: harness oxfmt
38c178094b782ba4326aa7cb47c27c2518e62e66
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
Expand All @@ -38,11 +43,8 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check types
run: bun run check-types

- name: Test
run: bun run test
- name: Check
run: bun run check

- name: Evaluate agent contract
run: bun run eval
Expand Down
13 changes: 7 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ jobs:
fetch-tags: true
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
Expand Down Expand Up @@ -70,13 +75,9 @@ jobs:
if: steps.preflight.outputs.release == 'true'
run: bun install --frozen-lockfile

- name: Check types
if: steps.preflight.outputs.release == 'true'
run: bun run check-types

- name: Test
- name: Check
if: steps.preflight.outputs.release == 'true'
run: bun run test
run: bun run check

- name: Evaluate agent contract
if: steps.preflight.outputs.release == 'true'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ coverage
*.tgz

.npmrc
.claude/worktrees/
1 change: 1 addition & 0 deletions .vite-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vp staged
67 changes: 33 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,38 +84,38 @@ Provider execution must bind exactly one App profile. Create one JSON file at

```json
{
"version": 1,
"name": "my-app",
"providers": {
"posthog": {
"config": {
"host": "https://us.posthog.com",
"projectId": "12345"
},
"secrets": {
"apiToken": "env:MY_APP_POSTHOG_TOKEN"
}
},
"gsc": {
"config": {
"siteUrl": "sc-domain:example.com"
},
"secrets": {
"serviceAccountFile": "env:MY_APP_GSC_SERVICE_ACCOUNT_FILE"
}
},
"hubspot": {
"config": {},
"secrets": {
"accessToken": "env:MY_APP_HUBSPOT_ACCESS_TOKEN"
}
},
"pagespeed": {
"secrets": {
"apiKey": "env:MY_APP_PAGESPEED_API_KEY"
}
}
}
"version": 1,
"name": "my-app",
"providers": {
"posthog": {
"config": {
"host": "https://us.posthog.com",
"projectId": "12345"
},
"secrets": {
"apiToken": "env:MY_APP_POSTHOG_TOKEN"
}
},
"gsc": {
"config": {
"siteUrl": "sc-domain:example.com"
},
"secrets": {
"serviceAccountFile": "env:MY_APP_GSC_SERVICE_ACCOUNT_FILE"
}
},
"hubspot": {
"config": {},
"secrets": {
"accessToken": "env:MY_APP_HUBSPOT_ACCESS_TOKEN"
}
},
"pagespeed": {
"secrets": {
"apiKey": "env:MY_APP_PAGESPEED_API_KEY"
}
}
}
}
```

Expand Down Expand Up @@ -279,7 +279,6 @@ Development dependencies include `@celados/argc` from the private team registry

```bash
bun run eval
bun run check-types
bun run test
bun run check
bun run verify:package
```
406 changes: 381 additions & 25 deletions bun.lock

Large diffs are not rendered by default.

108 changes: 55 additions & 53 deletions docs/cloud-provider-runtime.bdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
## 范围边界

**包含:**

- 云端按业务 profile 选择 provider 配置
- 云端通过平台 secret 注入 provider 凭证
- Agent 在任何真实读取前先执行 readiness 检查
Expand All @@ -14,6 +15,7 @@
- 多业务、多环境并存时的隔离规则

**不包含:**

- secret manager 的具体供应商实现
- provider 业务指标解释、报告、归因判断
- gkit provider capability 的具体查询字段设计
Expand All @@ -35,25 +37,25 @@

**场景 1.1:任务带有明确业务 profile 时启动**
Given 云端任务声明了业务 profile
And 该 profile 对应的 provider secrets 已在平台中配置
And 该 profile 对应的 provider secrets 已在平台中配置
When Agent 启动 provider 读取流程
Then Agent 使用该业务 profile 作为本次任务的唯一业务归属
And 后续读取只使用该 profile 绑定的 provider secrets
And Agent 不需要知道 secret 的明文内容
And 后续读取只使用该 profile 绑定的 provider secrets
And Agent 不需要知道 secret 的明文内容

**场景 1.2:任务没有声明业务 profile 时停止**
Given 云端任务没有声明业务 profile
When Agent 准备读取任意真实 provider 数据
Then Agent 停止读取流程
And 返回“缺少业务 profile”的诊断
And 不尝试使用默认业务或本地配置兜底
And 返回“缺少业务 profile”的诊断
And 不尝试使用默认业务或本地配置兜底

**场景 1.3:profile 存在但没有绑定目标 provider**
Given 云端任务声明了业务 profile
And 该 profile 没有绑定目标 provider 的 secret
And 该 profile 没有绑定目标 provider 的 secret
When Agent 请求该 provider 的真实数据
Then Agent 返回“该业务未配置目标 provider”的诊断
And 不从其他业务 profile 借用 secret
And 不从其他业务 profile 借用 secret

---

Expand All @@ -63,43 +65,43 @@ Then Agent 返回“该业务未配置目标 provider”的诊断

**场景 2.1:通过环境变量注入 service account JSON**
Given 平台 secret 中保存了目标 provider 的 service account JSON
And 云端任务声明了正确业务 profile
And 云端任务声明了正确业务 profile
When 运行时启动 gkit provider command
Then CLI 可以从运行时环境读取该 provider 的凭证
And 仓库中不会出现真实凭证文件
And 输出日志不会打印 secret 明文
And 仓库中不会出现真实凭证文件
And 输出日志不会打印 secret 明文

**场景 2.2:必须使用临时文件时只写入短生命周期目录**
Given 某个 provider 只能通过文件路径读取凭证
And 平台 secret 中保存了 service account JSON
And 平台 secret 中保存了 service account JSON
When 运行时准备 gkit provider 输入
Then 运行时把凭证写入任务级临时目录
And CLI 只接收该临时文件路径
And 任务结束后该临时文件随运行环境销毁
And CLI 只接收该临时文件路径
And 任务结束后该临时文件随运行环境销毁

**场景 2.3:本地 `.env.live` 不参与云端运行**
Given 仓库中存在本地 live validation 文档
And 开发机可以使用 `.env.live`
And 开发机可以使用 `.env.live`
When 云端任务启动
Then 云端不读取 `.env.live`
And 云端只使用平台注入的 profile 与 secrets
And 本地配置缺失不会影响云端任务
And 云端只使用平台注入的 profile 与 secrets
And 本地配置缺失不会影响云端任务

**场景 2.4:本地开发也按业务 profile 读取凭证**
Given 开发机已经创建业务 profile 目录
And profile 目录中保存了本地只读 provider 凭证
And profile 目录中保存了本地只读 provider 凭证
When Agent 在本地执行 gkit provider command
Then CLI 按业务 profile 读取 provider 配置
And 不要求每个 provider 维护自己的凭证目录
And repo-local `.env.live` 不是默认配置来源
And 不要求每个 provider 维护自己的凭证目录
And repo-local `.env.live` 不是默认配置来源

**场景 2.5:repo 只声明 profile id,不声明 provider 凭证**
Given 开发机已经创建业务 profile 目录
And 当前 repo 的 ignored `.env.local` 只声明了业务 profile id
And 当前 repo 的 ignored `.env.local` 只声明了业务 profile id
When Agent 执行 gkit provider command
Then CLI 先读取 repo 中的 profile id
And 再读取该 profile 目录下的 provider 配置
And profile 中的 provider 配置不会被 repo fallback 配置覆盖
And 再读取该 profile 目录下的 provider 配置
And profile 中的 provider 配置不会被 repo fallback 配置覆盖

---

Expand All @@ -109,27 +111,27 @@ Then CLI 先读取 repo 中的 profile id

**场景 3.1:readiness 通过后执行低量真实读取**
Given 云端任务声明了业务 profile
And 目标 provider 的必要 secrets 已注入
And 目标 provider 的必要 secrets 已注入
When Agent 准备读取真实 provider 数据
Then Agent 先执行 readiness 检查
And readiness 通过后才执行低量真实读取
And 低量读取成功后才允许执行更宽的数据读取
And readiness 通过后才执行低量真实读取
And 低量读取成功后才允许执行更宽的数据读取

**场景 3.2:readiness 未通过时停止业务读取**
Given 云端任务声明了业务 profile
And 目标 provider 的 readiness 检查未通过
And 目标 provider 的 readiness 检查未通过
When Agent 准备读取真实 provider 数据
Then Agent 停止后续 provider 读取
And 返回可恢复诊断
And 不用其他 provider 的数据推断该 provider 的 truth
And 返回可恢复诊断
And 不用其他 provider 的数据推断该 provider 的 truth

**场景 3.3:可选配置缺失不阻断不需要它的读取**
Given 云端任务声明了业务 profile
And 某个 provider 的可选配置未注入
And 本次读取不需要该可选配置
And 某个 provider 的可选配置未注入
And 本次读取不需要该可选配置
When Agent 执行 readiness 检查
Then readiness 可以带 warning 通过
And Agent 可以继续执行不依赖该可选配置的读取
And Agent 可以继续执行不依赖该可选配置的读取

---

Expand All @@ -139,27 +141,27 @@ Then readiness 可以带 warning 通过

**场景 4.1:缺失必要 secret 时返回配置诊断**
Given 云端任务声明了业务 profile
And 目标 provider 缺少必要 secret
And 目标 provider 缺少必要 secret
When Agent 执行 readiness 检查
Then Agent 收到“必要 secret 缺失”的诊断
And 诊断包含缺失项名称
And 不发起真实 provider 查询
And 诊断包含缺失项名称
And 不发起真实 provider 查询

**场景 4.2:凭证存在但无权访问目标业务资源**
Given 目标 provider 的凭证已注入
And 该凭证无权访问当前 profile 绑定的业务资源
And 该凭证无权访问当前 profile 绑定的业务资源
When Agent 执行低量真实读取
Then Agent 收到“provider 权限不足”的诊断
And 诊断能与“查询结果为空”区分开
And Agent 不把该失败解释为业务资源没有数据
And 诊断能与“查询结果为空”区分开
And Agent 不把该失败解释为业务资源没有数据

**场景 4.3:provider 暂时不可用时保留原始失败**
Given 目标 provider 的凭证和 profile 配置都正确
And provider 返回临时失败或限流
And provider 返回临时失败或限流
When Agent 执行真实读取
Then Agent 返回“provider 暂不可用”的诊断
And 标记该失败是否可以重试
And 不用缓存、猜测或其他 provider 数据替代本次 provider truth
And 标记该失败是否可以重试
And 不用缓存、猜测或其他 provider 数据替代本次 provider truth

---

Expand All @@ -169,36 +171,36 @@ Then Agent 返回“provider 暂不可用”的诊断

**场景 5.1:两个业务 profile 同时存在时互不串用**
Given 平台中同时存在多个业务 profile 的 secrets
And 云端任务声明使用其中一个 profile
And 云端任务声明使用其中一个 profile
When Agent 读取 provider truth
Then Agent 只使用该 profile 绑定的 secrets
And 不读取其他 profile 的 provider secrets
And 输出中标明本次使用的业务 profile
And 不读取其他 profile 的 provider secrets
And 输出中标明本次使用的业务 profile

**场景 5.2:生产与测试环境同时存在时必须显式选择**
Given 同一业务同时存在生产和测试 secrets
When Agent 准备读取真实 provider 数据
Then Agent 必须使用任务声明的环境
And 未声明环境时停止读取流程
And 不默认使用生产环境
And 未声明环境时停止读取流程
And 不默认使用生产环境

**场景 5.3:只读 provider 读取不产生业务侧副作用**
Given Agent 使用业务 profile 读取 provider truth
When Agent 执行只读 gkit provider command
Then provider 侧不会产生写入、变更、投放调整或账务动作
And 本次任务只产出可审计的事实输出或诊断
And 本次任务只产出可审计的事实输出或诊断

---

## 配置状态真值表

| 业务 profile | 必要 secrets | readiness | 真实读取 | Agent 行为 |
|---|---|---|---|---|
| 已声明 | 完整 | 通过 | 允许低量读取 | 先验证再扩大读取 |
| 已声明 | 缺失 | 失败 | 不允许 | 返回缺失配置诊断 |
| 未声明 | * | 不执行 | 不允许 | 返回缺少 profile 诊断 |
| 已声明 | 完整但无权限 | 可通过或读取失败 | 不允许扩大读取 | 返回权限诊断 |
| 已声明 | 完整但 provider 限流 | 失败或读取失败 | 可稍后重试 | 返回可重试诊断 |
| 业务 profile | 必要 secrets | readiness | 真实读取 | Agent 行为 |
| ------------ | -------------------- | ---------------- | -------------- | --------------------- |
| 已声明 | 完整 | 通过 | 允许低量读取 | 先验证再扩大读取 |
| 已声明 | 缺失 | 失败 | 不允许 | 返回缺失配置诊断 |
| 未声明 | * | 不执行 | 不允许 | 返回缺少 profile 诊断 |
| 已声明 | 完整但无权限 | 可通过或读取失败 | 不允许扩大读取 | 返回权限诊断 |
| 已声明 | 完整但 provider 限流 | 失败或读取失败 | 可稍后重试 | 返回可重试诊断 |

## 验收说明

Expand Down
Loading
Loading