--- name: app-dev description: 在独立仓库按 TickShield(刻盾云)平台契约开发、审查、发布生态应用(micro/service):listingKey 四位一体、app.manifest.json、tickshield-node-runtime 基础镜像、@aigrego/guard 四道闸与 tenant_id 租户隔离、micro 前端 portal-sdk 宿主桥、release-cli 发布与商店上架部署。凡用户要求「开发一个平台应用 / 新建 app / 写个 micro 应用 / 做个像 secret-manager / demo-app 那样的应用 / 在门户里加个自己的应用 / 接入 TickShield / 上架应用到商店 / 审查这个 app 能不能部署到平台 / 帮我看看接入还缺什么」,或带着 注册不上去 / 401 missing app credentials / 心跳 404 / host must equal listingKey / 四道闸 403 / 跨租户数据串了 / image registry not registered / invalid manifest skipped / 应用中心导入失败 / 应用装上了但 /svc 不通 / PROD 白屏 CSP 报错 等症状,或提到 listingKey / app.manifest.json / 四道闸 / TDT / tenant_id / portal-sdk / release-cli / tickshield-node-runtime 时,务必使用本 skill——即使用户没说「应用接入」四个字。Use whenever the user develops, scaffolds, audits, onboards, publishes or deploys a TickShield platform app (micro or service) from an independent repository. 边界:非平台场景的通用 Dockerfile 选型评审用 runtime-image;通用 Gitea Actions 工作流规范用 gitea-actions;CI 工作流的发布 job 改造/新建发布工作流用 app-publish;git 提交推送与日报用 push-and-report;平台基座本身(portal/gateway/packages/* 控制面)的开发不归本 skill。 --- # app-dev — TickShield 生态应用开发规范(独立仓库 → 可直接部署上架) TickShield 生态应用的交付物只有两样:**镜像 + `app.manifest.json`**。注册/心跳/优雅下线/健康检查由基础镜像 `tickshield-node-runtime:base` 的 `runtime-entry.js` 全包;应用代码只需一个导出 `{handler, dbCheck?}` 的 ESM 入口,全量 SQL 走 `@aigrego/guard` 租户数据源,路由挂四道闸。本 skill 把这套契约锻成可执行的流程,两种工作模式: - **新建模式**:仓库没有 `app.manifest.json` / `Dockerfile` —— 交互采集四要素 → 照 `references/templates/` 生成骨架 - **审查模式**:仓库已有接入痕迹 —— 对照 `references/checklist.md` 逐项审计 → 出修正清单 契约锚点:本 skill 基线 = TickShield 平台 v4.0 + APPSTORE-RUNTIME + FR-101 registries 白名单 + FR-102/TKT-557 导航与图标规范(2026-10)。与平台冲突时,以平台仓 `docs/reference/应用开发与上架指引.md` 和 `apps/portal/internal/modules/registry/manifest.go` 的校验代码为准,并回本仓库更新本 skill。`docs/reference/App出仓与集成指引.md`、`App开发者指南.html` 是**已废弃的历史文档**(@xgent 旧体系),禁止照搬。 ## 总流程 ``` 第 0 步 备料分流(判模式 + 采集四要素) 新建:第 1 步 生成骨架 → 第 2 步 静态自检(scripts/check-app.sh) 审查:第 1 步 逐项审计(checklist.md)→ 第 2 步 确认后修正 + 静态自检 汇合:第 3 步 联调验证(需 dev 栈)→ 第 4 步 发布指引 → 第 5 步 汇报 ``` 除非用户明确只要聊聊思路,完整走全部 5 步。 **调用形态分流(硬规则)**:裸调用(无任务输入)= **只读审计态**——只出审计报告与修复/新建方案,**永不主动改文件、永不执行任何写操作**;报告与方案交付即停,等用户确认后才进入对应模式的执行段。空仓库裸调用同理:只输出「接入差距评估 + 新建方案」供确认,不直接生成骨架。带任务输入(如「帮我开发 X 应用」「按报告修」)= 任务即授权对应动作,按流程走;但发布等写平台操作仍需显式确认(见「人机权责」)。 ## 前置:先备齐 4 样(缺一样就不要开始) | 要什么 | 从哪来 | 放哪 | |---|---|---| | listingKey | 用户拍板(正则 `^[a-z0-9][a-z0-9-]{0,62}$`) | `app.manifest.json` + `.aigrego-registry.env` | | 应用类型 | 用户拍板:`micro`(有门户内前端)/ `service`(无头后端) | `app.manifest.json` 的 `type` | | 平台地址 | 用户告知(dev 默认 `http://localhost:8080`) | env `AIGREGO_PORTAL_URL`,不写死 | | 应用凭证 `APP_SECRET` | **平台管理员在 console「应用市场」新建应用签发**,仅此一次明文可见 | 绝不落盘;CI 用 secrets 注入 env | 缺料时停下来教用户怎么补(凭证找平台管理员签发;listingKey 让用户起个全局唯一的);**不要猜域名,不要从别的仓库/文档里翻凭证**。 ## 第 0 步:备料分流 1. 看仓库根:无 `app.manifest.json` 且无 `Dockerfile` → 新建模式;否则 → 审查模式。**审查模式第一项先查 `app.manifest.json` 在不在**:它是平台基座接入应用的配置,`aigrego-release` 与 console「应用中心」向导导入两条上架路径消费的都是仓库里这份文件——缺失 = 接入阻断(die 级首要修复项),照 `references/templates/` 按应用类型补一份,不是可选项。 2. 新建模式采集四要素(上表)。listingKey 必须用户亲口确认——它是**四位一体** key(== TDT `aud` == 容器名 == Docker 网络别名 == scope 命名空间),全局唯一、**不可变更**,改 key 等于换应用。 3. scopes 约定 `.`(如 `my-app.read`/`my-app.write`),格式 `^[a-z0-9][a-z0-9_-]*(\.[a-z0-9][a-z0-9_-]*)+$`(至少两段)。 → 验收:四要素齐备且 listingKey 过正则、经用户确认;缺料则停在补料指引。 ## 第 1 步(新建):生成骨架 照 `references/templates/` 复制并替换占位(`my-app` → 实际 listingKey),生成前列出文件清单给用户过目: - `app.manifest.json`(service / micro 两个模板选一) - `Dockerfile`(`FROM tickshield-node-runtime:base`,照模板,不加 CMD) - `server.js`(service 骨架:guard + tdb + assertTenantTables + logger + tracing + 示例路由) - micro 型追加:`web/`(index.html / package.json / tsconfig.json / src/sdk.ts / src/api.ts / src/main.tsx) - `.gitea/workflows/release.yaml`(照 `gitea-app-release.yaml`,提示配 secrets) - `docs/PRD_app-.md` 草稿(照 `PRD_app-template.md`,文档状态标「草稿」) - `.aigrego-registry.env.example` + 在 `.gitignore` 追加 `.aigrego-registry.env` 生成后逐文件说明「这个文件哪几处必须改、哪几处永远别动」。 → 验收:文件齐、占位全替换、用户确认清单。 ## 第 1~2 步(审查):逐项审计 → 修正 1. 读 `references/checklist.md`,对照逐项审计,输出三档结论表:**通过 / 不符(die 级)/ 风险(warn 级)**,每项给证据(文件:行号)。 2. 修正清单列给用户,**经确认后**逐项修;修完跑静态自检。 **裸调用到此为止**:审计报告 + 修复方案就是交付物,不执行任何修正;用户回复确认(如「按方案修」)后才进入第 2 步修正。 → 验收:审计表覆盖 checklist 全部 die 级项;修正经用户逐批确认。 ## 第 2 步:静态自检 ```bash bash <本skill目录>/scripts/check-app.sh <仓库根> ``` die 级不过 → 修了再往下走;warn 级列进汇报。check-app.sh 只查静态契约(manifest 字段/Dockerfile 三要素/入口接线/保留路径/密钥卫生),不替代第 3 步真环境联调。 → 验收:脚本 exit 0(或仅剩 warn 且已列清单)。 ## 第 3 步:联调验证(绝不可跳过的闸口) 完成定义 = **dev 栈旁真环境走通**(平台 dev 栈未运行时,明说「未验证」并停在这里,不许拿静态自检绿冒充完成): 1. 起容器(`container_name` == 网络别名 == listingKey,注入 `APP_KEY/APP_SECRET/PORTAL_API_URL` + 业务 env)→ portal `/api/services` 出现该实例且心跳鲜活。 2. `/health` 直连与经网关 `/svc//health` 均返回**恰两键** `{"status":"ok","db":"ok"}`。 3. 四道闸矩阵:匿名 → 401;跨 aud → 网关 401 / 直连 403 `gate=audience`;无 scope → 403 `gate=scope`;member 调 admin 路由 → 403 `gate=role`;无页面权限 → 403 `gate=acl`;合法调用 → 200。 4. 租户隔离:A 租户写入,B 租户读 = 空集。 5. micro 型追加:门户内打开应用,`sdk.ready()` 握手成功、acl 驱动页面显隐、`callService` 通。 平台仓 `scripts/verify-onboarding.sh` 是这六项的出口门基准(按其样式改写成自己 app 的演练路由)。 → 验收:六项全过并留档;做不到的项在汇报里逐项声明「未验证」及原因。 ## 第 4 步:发布指引 发布是**写操作**:默认只给命令草稿,用户显式说「发布」才执行。详见 `references/publish-deploy.md`: ```bash export AIGREGO_PORTAL_URL=<平台地址> APP_KEY= APP_SECRET= aigrego-release whoami # 先验凭证(放在构建之前) aigrego-release publish --manifest app.manifest.json [--image ] [--store] [--wait 600] # micro 前端产物:aigrego-release publish --dist (包根必须直接含 index.html) ``` - **manifest 进平台是双通道、同一份文件**:CLI `publish --manifest`(上)或平台侧 console「应用中心」按向导导入——同一条 submit 管道(同一套校验、治理字段变更同样 202 进审核队列)。不管走哪条,仓库里的 `app.manifest.json` 都必须最新且合法;走手动通道时把这份文件交付给平台管理员。 - 治理字段(`scopes`/`aclManifest`/`exchangeTargets`/`dependencies`)变更 → 202 进审核队列(exit 0 但**不生效**),批准前不影响已安装租户;首个版本直接生效。 - 商店上架/授权/安装/启停/卸载是平台侧 console 动作;镜像 host 必须在 registries 白名单(空表=全拒)。 - 输出「待平台侧操作」清单:auto_publish 开关、registries 登记、治理审核批准、授权安装——这些 Agent 做不到,不绕道不假装完成。 → 验收:命令草稿正确(版本号 semver、镜像 tag 与 manifest.version 对齐);已执行的发布如实记录响应(200/202/4xx)。 ## 第 5 步:汇报 固定汇报结构: - 做了什么:模式 / 生成或修改的文件 / 执行过的命令 - 关键标识:listingKey、版本号、镜像 ref、关键文件路径 - 被跳过的:未生成/未修的项及原因 - 没写成的:占位残留、未替换项 - 验证做到哪一步:静态自检结果;联调六项各自 过/未验证(**没跑就说没跑**) - 待平台侧操作:凭证签发 / auto_publish / registries 白名单 / 审核 / 授权安装 - 开放问题:用户未拍板的事项 ## 硬约束 - ① **listingKey 一经确认不变更**。为什么:它是 TDT aud、容器名、网络别名、scope 命名空间的同一事实源,平台按 `UNIQUE(listing_key,host,port)` 记账;改 key = 新应用,旧数据/授权/安装全部悬空。 - ② **Dockerfile 必须 `FROM tickshield-node-runtime:base`,不覆盖 CMD**。为什么:注册重试循环、心跳 404 自愈/409 停服等待、SIGTERM 先 deregister 再摘流、`/health` 三态信封全在 `runtime-entry.js`;自造入口 = 商店安装 30s 等自注册超时 op failed。 - ③ **业务 SQL 一律走 `tdb.query`,INSERT 的 tenant_id 由应用显式写入**。为什么:SELECT/UPDATE/DELETE 自动改写追加 `tenant_id=$N` 参数;直连 pg 即绕开租户隔离与 FR-30 数据源路由——跨租户串数据是安全事故不是 bug。JOIN/子查询/UNION/CTE 被改写器 fail-closed 拒绝,确需时用 `tdb.queryUnscoped(sql, params, tenantId)` 并显式写租户过滤。 - ④ **启动必须 `await guard.assertTenantTables(tdb, [业务表...])`,失败 `process.exit(1)`**。为什么:表缺 `tenant_id` 列时改写器静默失效变全表扫描;带病启动比不启动更糟(钉版原则同理:构建期缺料必须让构建失败可见)。 - ⑤ **应用 API 路径禁用 `/api`、`/auth`、`/svc` 前缀**(含自定义域名下),用 `/v1/` 之类。为什么:这三个前缀是平台保留通道,网关静态转发/验签规则会截胡或放行错流量。 - ⑥ **密钥/令牌不进日志、不落配置文件、不进 argv**。为什么:`@aigrego/logger` 的字段名自动脱敏只是兜底;CI 里命令行参数会进 `ps` 和构建日志。凭证走 env 注入。 - ⑦ **manifest 不携带 `deployDescriptor`**。为什么:它仅平台管理员可写;自注册时会被剥离(记 warn+审计),submit 携带非管理员直接 403——带了也没用,还制造「以为生效」的错觉。 - ⑧ **micro 前端:portal-sdk 用 vendor 版(`