Skip to content
Closed
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
52 changes: 52 additions & 0 deletions .github/ISSUE_TEMPLATE/portfolio-bug.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: 缺陷
description: 记录复现、预期、回归验收与关联
title: "[缺陷] "
body:
- type: markdown
attributes:
value: "创建前请查重。任务创建后关联总看板和 Linear;原生同步仓库先查自动同步任务。"
- type: textarea
id: goal
attributes:
label: 复现步骤、实际结果与预期行为
description: 写版本/平台/环境、复现步骤、预期和实际结果;请勿包含凭据或个人敏感数据
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: 验收条件
description: 写可核验的清单,区分代码、部署与真实用户验收
validations:
required: true
- type: textarea
id: dependencies
attributes:
label: 依赖与关联
description: 写仓库/Issue源链接及真实阻塞;没有依赖请写无
validations:
required: true
- type: textarea
id: verification
attributes:
label: 预期验证方式
description: 写本地检查、指定CI/数据库环境和适用的真实用户验收
validations:
required: true
- type: textarea
id: next
attributes:
label: 下一步
validations:
required: true
- type: dropdown
id: priority
attributes:
label: 建议优先级
options:
- P2
- P0
- P1
- P3
validations:
required: true
52 changes: 52 additions & 0 deletions .github/ISSUE_TEMPLATE/portfolio-task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: 需求 / 任务
description: 建立目标、范围、验收、依赖与下一步
title: "[任务] "
body:
- type: markdown
attributes:
value: "创建前请查重。任务创建后关联总看板和 Linear;原生同步仓库先查自动同步任务。"
- type: textarea
id: goal
attributes:
label: 目标与范围
description: 说明预期行为、受影响范围和边界
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: 验收条件
description: 写可核验的清单,区分代码、部署与真实用户验收
validations:
required: true
- type: textarea
id: dependencies
attributes:
label: 依赖与关联
description: 写仓库/Issue源链接及真实阻塞;没有依赖请写无
validations:
required: true
- type: textarea
id: verification
attributes:
label: 预期验证方式
description: 写本地检查、指定CI/数据库环境和适用的真实用户验收
validations:
required: true
- type: textarea
id: next
attributes:
label: 下一步
validations:
required: true
- type: dropdown
id: priority
attributes:
label: 建议优先级
options:
- P2
- P0
- P1
- P3
validations:
required: true
27 changes: 27 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## 目标与范围

<!-- 写明触发问题、变更后的行为和本次范围。 -->

## 关联

Refs aiStackcoding/aiStack#<Issue编号>
Related to https://linear.app/fxxkrlab/issue/FXX-<编号>/<实际任务路径>
总看板:https://github.com/users/fxxkrlab/projects/6
Branch: <实际分支>
Commit: <当前完整40字符SHA>

## 验收条件

<!-- 从源 Issue 引用可核验的条件,勾选仅表示已有证据。 -->

## 验证证据

<!-- 写检查命令/环境、结果、证据链接及失败/未执行项;区分静态、服务器CI/数据库与真实客户端验收。 -->

## 风险与回滚

<!-- 写兼容性、数据/迁移、权限、回滚方法或说明为何不适用。 -->

## 剩余交付步骤

<!-- 明确审查/合并、部署、生产启用和真实用户验收分别还需什么;无后续动作时说明原因。 -->
18 changes: 18 additions & 0 deletions .github/portfolio.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schemaVersion": 1,
"repository": "aiStackcoding/aiStack",
"product": "aiStack",
"githubProject": "https://github.com/users/fxxkrlab/projects/6",
"linear": {
"workspace": "fxxkrlab",
"team": "FXX",
"projectId": "6d5efdfa-05b0-4e96-b250-58a780ef7b64",
"projectUrl": "https://linear.app/fxxkrlab/project/跨项目研发总览-cf51cdb72517",
"issueAssociation": "manual-source-link"
},
"bootstrapIssue": "https://github.com/aiStackcoding/aiStack/issues/2",
"bootstrapLinearIssue": "https://linear.app/fxxkrlab/issue/FXX-27/流程初始化-aistack接入-issue总看板linear独立分支与-draft-pr",
"defaultPullRequest": "draft",
"completionRequires": "all-issue-acceptance-criteria",
"branchProtection": "verify-plan-and-required-checks-before-enabling"
}
76 changes: 76 additions & 0 deletions .github/workflows/portfolio-traceability.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Portfolio traceability
on:
pull_request:
types: [opened, reopened, edited, synchronize, ready_for_review]
permissions:
contents: read
issues: read
pull-requests: read
concurrency:
group: portfolio-traceability-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
traceability:
name: traceability
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: Verify task and delivery references
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
node --input-type=module <<'PORTFOLIO_NODE'
export function currentPullRequestForEvent(eventPr, currentPr) {
if (eventPr.number !== currentPr.number) throw new Error('The current PR differs from the triggering PR.');
return eventPr.head?.sha === currentPr.head?.sha ? currentPr : null;
}

export function inspectPullRequest(pr, repository) {
const errors = [];
const branch = pr.head?.ref || '';
const match = /^codex\/(?:fxx-(\d+)-)?gh-(\d+)-[a-z0-9][a-z0-9-]*$/.exec(branch);
if (!match) errors.push('Use codex/fxx-N-gh-N-description (or codex/gh-N-description while the Linear number is pending).');
const body = String(pr.body || '').replace(/<!--[\s\S]*?-->/g, '');
const escaped = repository.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const refs = [...body.matchAll(new RegExp(`\\bRefs\\s+${escaped}#(\\d+)\\b`, 'gi'))];
const issueNumber = match ? Number(match[2]) : 0;
if (!refs.some(x => Number(x[1]) === issueNumber)) errors.push('Refs must identify the branch ticket in this repository.');
const linear = /https:\/\/linear\.app\/fxxkrlab\/issue\/FXX-(\d+)(?:\/[^\s)>]*)?/i.exec(body);
if (!linear) errors.push('Include the actual FXX Linear issue URL.');
if (match?.[1] && linear && Number(match[1]) !== Number(linear[1])) errors.push('The branch Linear number and Linear link differ.');
if (!body.includes('https://github.com/users/fxxkrlab/projects/6')) errors.push('Include the portfolio board URL; membership is verified separately.');
if (!body.includes(`Branch: ${branch}`)) errors.push('Record the actual Branch: value.');
if (!pr.head?.sha || !body.includes(`Commit: ${pr.head.sha}`)) errors.push('Record the current 40-character head SHA in Commit:.');
for (const heading of ['目标与范围', '验收条件', '验证证据', '风险与回滚', '剩余交付步骤']) {
const section = new RegExp(`^## ${heading}\\s*\\n([\\s\\S]*?)(?=^## |$(?![\\s\\S]))`, 'm').exec(body);
const value = section?.[1]?.trim() || '';
if (!value || /^(?:待填写|TBD|TODO|<[^>]+>)$/i.test(value)) errors.push(`Complete section: ${heading}.`);
}
return {errors, issueNumber};
}

export async function verifyIssue(repository, number, request) {
const response = await request(`https://api.github.com/repos/${repository}/issues/${number}`);
if (!response.ok) throw new Error(`Unable to verify source Issue: HTTP ${response.status}.`);
const issue = await response.json();
if (issue.pull_request || issue.number !== number || issue.html_url?.toLowerCase() !== `https://github.com/${repository}/issues/${number}`.toLowerCase()) throw new Error('The source must be an actual Issue in this repository.');
if (issue.state !== 'open') throw new Error('Use an open delivery Issue; completed work requires a new task.');
return issue;
}

import fs from 'node:fs';
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'));
const request = url => fetch(url, {
headers: {accept:'application/vnd.github+json',authorization:`Bearer ${process.env.GH_TOKEN}`,'X-GitHub-Api-Version':'2022-11-28'},
redirect:'error',signal:AbortSignal.timeout(15000)
});
const response = await request(`https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pulls/${event.pull_request.number}`);
if (!response.ok) throw new Error(`Unable to read current PR: HTTP ${response.status}.`);
const current = currentPullRequestForEvent(event.pull_request, await response.json());
if (!current) { console.log('Superseded head; this event does not validate the current delivery.'); process.exit(0); }
const result = inspectPullRequest(current, process.env.GITHUB_REPOSITORY);
if (result.errors.length) { console.error(result.errors.join('\n')); process.exit(1); }
await verifyIssue(process.env.GITHUB_REPOSITORY, result.issueNumber, request);
console.log('PR metadata and source Issue verified. Linear membership, board membership and delivery evidence require separate readback/review.');
PORTFOLIO_NODE
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!-- managed:portfolio-workflow:start -->
# Codex 默认项目交付流程

## 适用范围与授权
这些规则是用户于 2026-09-30 要求为现有及未来新增项目建立的默认流程,适用于实施功能、修复缺陷、项目配置和交付文档的工作。普通问答、只读排查、翻译和单次文档处理不必人为创建工程工单。尊重当前任务的更具体限制、既有发布负责人及指定验证环境。

## 开始实施前
1. 核对真实 Git 根目录、GitHub owner/repo、当前分支、未提交改动和适用 AGENTS 文件。不得把聊天中的目录名当成真实仓库归属。
2. 找到对应 GitHub Issue;没有时先查重再创建。写清目标、范围、验收条件、依赖、预期验证方式和下一步。日常任务追踪属于用户授权的工作,不要反复请求创建工单的确认。
3. 将 Issue 纳入 [跨项目研发总看板](https://github.com/users/fxxkrlab/projects/6),设置项目、优先级和工作类型;未核验状态使用“待整理 / 待核验”。
4. 关联 Linear 工作区 fxxkrlab、团队 FXX 及“跨项目研发总览”项目。对已配置 GitHub → Linear 新任务同步的仓库,先查到自动同步的 Issue,再关联项目;不得另建重复镜像。未配置同步的仓库创建一个有 GitHub 源链接的 Linear 关联任务,并明确当前同步方式。
5. 跨仓库工作建立协调 Issue 和各仓库实施 Issue,用显式链接及真实阻塞关系连接;不要仅保留在 Codex 聊天里。

## 分支、提交与 PR
- 在用户指定或新建的独立工作分支/worktree 开发,保护现有未提交改动。不得在默认分支直接实施或直接推送。
- 新分支采用 codex/fxx-<Linear编号>-gh-<Issue编号>-<简短描述>;同步尚未提供 Linear 编号时可先采用 codex/gh-<Issue编号>-<简短描述>,随后记录关联。
- 提交和 PR 必须引用对应 GitHub Issue。PR 正文还须含真实 Linear 链接、分支/提交、验收条件、验证证据、风险和仍需完成的交付步骤。
- 默认创建 Draft PR 供集中审查;遵守当前任务的推送限制。用户限定本地/禁止推送时交付本地提交及后续命令,不绕过限制。
- 完成工作后把 PR 加入总看板并更新对应任务。GitHub Issue、PR、Linear 任务采用明确链接,不凭相似标题猜测关联。
- PR 中使用 “Refs owner/repo#N” 及 “Related to <Linear URL>” 保留交付任务;需要部署/启用/真实用户验收的 Issue 不使用自动关闭关键词代替验收。
- 合并、发布、部署、生产启用以及外部资金/通知动作,仍由用户的明确操作授权和指定负责人控制;初始化流程不授权这些动作。

## 证据与状态
- 分别记录本地/静态验证、指定服务器 CI/数据库验证、已合并、已部署、已启用和真实用户/客户端验收。PR 合并不代表已经部署或验收。
- 仅在本 Issue 的全部验收条件满足后标记完成;整理任务按其文档验收范围判定,不能据此宣称产品交付。
- 有阻塞时写明原因、解除阻塞所需证据和下一步;环境失败、未知状态和未执行检查不标记通过。
- 原生 GitHub–Linear 集成不自动同步 Projects 自定义状态。根据实际证据分别维护这些字段,不宣称未验证的自动化已经生效。

## 新项目接入
首次实施新项目时完成相同的仓库核对、Issue、总看板、Linear、独立分支和 Draft PR 流程。优先复用既有仓库,尊重用户指定的 owner、可见性和名称;没有远端时先确认仓库归属与可见性,不将私有内容公开。
通过专门的初始化 Issue 和独立 PR 添加任务/缺陷模板、PR 模板、仓库流程说明及必要的追踪检查;保留原有 AGENTS、模板和 CI。GitHub App 未获新组织/仓库权限时报告准确缺口,不擅自扩大代码访问。
本仓库接入数据位于 `.github/portfolio.json`,完整说明见 `docs/development/portfolio-workflow.md`。平台分支保护能力须现场核验;当前私有仓库套餐限制不能通过改公开或虚报保护生效来绕过。
<!-- managed:portfolio-workflow:end -->
44 changes: 44 additions & 0 deletions docs/development/portfolio-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 项目交付与追踪流程

仓库:`aiStackcoding/aiStack`。GitHub Issue 保存任务的目标、范围、验收与证据;[跨项目研发总看板](https://github.com/users/fxxkrlab/projects/6) 汇总跨仓库工作;[Linear 跨项目研发总览](https://linear.app/fxxkrlab/project/跨项目研发总览-cf51cdb72517) 关联规划与依赖。

## 开始一项工作

1. 核对真实 Git 根目录、远端归属、当前分支、未提交改动及适用 AGENTS;保护现有工作区。
2. 在本仓库按目标和源链接查重,然后复用或新建 Issue。写明范围、验收条件、依赖、预期验证环境和下一步。普通问答与只读排查无需人为创建工程工单。
3. 将 Issue 加入总看板,填写项目、优先级、工作类型;未知状态使用“待整理 / 待核验”。
4. 关联 Linear 工作区 fxxkrlab、团队 FXX、本项目。当前 GitHub App 未连接 aiStackcoding 组织,采用有 GitHub 源 Issue 链接的手动 Linear 关联。尚未配置原生自动同步;补接 App 后先按源链接去重,不扩大代码访问权限。
5. 从核验的基线创建独立分支/worktree。使用 `codex/fxx-<数字>-gh-<Issue数字>-<简短描述>`;同步尚未提供编号时先用 `codex/gh-<Issue数字>-<简短描述>`,获得 Linear 编号后记录准确链接。

## 提交与 PR

提交引用 `Refs aiStackcoding/aiStack#N`。默认创建 Draft PR;正文填写真实 Linear Issue URL、分支、当前完整 head SHA、目标、验收条件、验证证据、风险/回滚和剩余交付步骤。使用 `Related to <Linear URL>` 保留任务关联,需要部署或真实用户验收的任务避免自动关闭关键词。

将 PR 加入总看板,并更新 Issue 与 Linear 的明确链接。跨仓库工作建立协调 Issue 和双方实施 Issue,实际阻塞关系写明解除所需证据;不能仅凭相似标题关联。

仓库的 `Portfolio traceability / traceability` 检查验证分支中的 GitHub/Linear 编号、同仓库的开放 Issue、实际 head SHA 及 PR 说明。它只读取事件和当前 GitHub PR/Issue API,使用只读 GITHUB_TOKEN,无 checkout、部署或业务操作。读取当前 PR 正文避免提交和正文先后更新引发旧事件误报;并发取消过期作业,已被新 head 替代的事件不验证当前交付。旧 PR 后续触发检查时也需补齐流程;在迁移前保留旧分支和已有证据,通过独立任务决定迁移方式。

该检查验证 Linear/总看板链接格式与正文信息;它没有跨产品凭据,**不能证明 Linear 任务存在、关联正确、总看板成员关系或验收证据真实**。这些内容由执行者回读核验并由审查人确认;GitHub App 未获权限时记录缺口。不能用一条绿色元数据检查代替原有产品 CI。

## 状态与完成

总看板:待整理 → 待开发 → 开发中 → 待审核 → 待合并 → 待部署 → 待验收 → 完成;按实际任务跳过不适用阶段,阻塞时填写原因、解除证据和下一步。

证据分别记录本地/静态、指定服务器 CI/数据库、合并、部署、启用与真实用户/客户端验收。未知、未执行和环境失败不标记通过。仅本 Issue 的全部验收条件满足后关闭 Issue 并完成 Linear 任务;文档整理验收不能计为产品发布。

原生 GitHub–Linear 集成不负责 GitHub Projects 自定义状态。原生同步 Issue 的标题/描述/开放关闭变化可能双向传播;将状态更新视为真实变更,只在相应验收满足后关闭,不用 PR 合并替代交付完成。

合并、发布、部署、生产启用和外部资金/通知动作仍需明确操作授权及指定负责人。默认分支保护和 required check 只有平台实际允许、配置成功且回读核验后才能记为生效;当前套餐限制不能通过公开私有仓库绕过。PR 检查本身不阻止直接推送。

## 后续新增项目

首次实施前复用真实仓库并完成相同核对、去重 Issue、总看板、Linear、独立分支和 Draft PR 接入。没有远端时先确认 owner、名称和可见性。用专门初始化 Issue/PR 添加这些模板与规则,保留既有 AGENTS、模板和 CI,核验新仓库的 App 权限与关联方式。全局 Codex 规则指导本机未来项目;团队成员和其他设备通过仓库 AGENTS/模板继承。

## 本次初始化

- GitHub:https://github.com/aiStackcoding/aiStack/issues/2
- Linear:https://linear.app/fxxkrlab/issue/FXX-27/流程初始化-aistack接入-issue总看板linear独立分支与-draft-pr
- 关联方式:`manual-source-link`
- 合并后核验默认分支模板和工作流;本初始化不会关闭产品交付任务。

技术参考:[GitHub GITHUB_TOKEN 权限](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token)、[Issues API](https://docs.github.com/en/rest/issues/issues)、[Linear GitHub 集成](https://linear.app/docs/github)。
Loading