Browse Source

fix(issue-management): complete policy token guards

Turtle 1 month ago
parent
commit
692b9b59f7

+ 2 - 2
.agents/notes/implemented/process/2026-09-02-project-local-issue-planning-fields.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-09-02-project-local-issue-planning-fields.md
-2026-09-02-project-local-issue-planning-fields.md: efd0383c9701f9f4caf797d9bf1544b225e5f71c
-2026-09-02-project-local-issue-planning-fields.zh.md: 3c22a506743621ac3204342575fac90d81ee8a37
+2026-09-02-project-local-issue-planning-fields.md: e44fe0f002c40309beaeb4af3ccdd975714777e6
+2026-09-02-project-local-issue-planning-fields.zh.md: 28308d48994dbb144f7e7934a1058cef35942c20

+ 1 - 1
.agents/notes/implemented/process/2026-09-02-project-local-issue-planning-fields.md

@@ -16,7 +16,7 @@ The `DSH Issue Management` Project owns `Priority`, `Severity`, `Cost`, `Start D
 
 Repository policy resolves `Priority` and `Start Date` from the configured Project. It rejects an Issue-backed field or the wrong data type, reads Priority from the Project item, and writes Start Date through `updateProjectV2ItemFieldValue`. Organization Issue fields are retained only as `Legacy ...` migration sources and are not read by repository workflows.
 
-The pull-request policy workflow uses the repository `GITHUB_TOKEN` for repository Issue and pull-request reads, and a GitHub App token restricted to organization Projects read access for ProjectV2 queries. Lifecycle mutations continue to use the write-capable App token.
+The pull-request policy workflow uses the repository `GITHUB_TOKEN` for REST Issue and pull-request reads, and a GitHub App token restricted to repository Issues and organization Projects read access for ProjectV2 queries. Lifecycle mutations continue to use the write-capable App token.
 
 The Issue lifecycle workflow initializes `Start Date` only for `pull_request.opened`. It reads the pull request's live body, retains every same-repository reference that resolves to an Issue, converts `created_at` to a calendar date in the configured Project time zone, ensures the Issue is a Project item, and writes the date only when the current Project value is empty.
 

+ 1 - 1
.agents/notes/implemented/process/2026-09-02-project-local-issue-planning-fields.zh.md

@@ -16,7 +16,7 @@ Priority、影响面、解决代价和日期用于在 `DSH Issue Management` 中
 
 仓库策略从配置的 Project 解析 `Priority` 和 `Start Date`。策略拒绝 Issue 字段投影或错误的数据类型,从 Project item 读取 Priority,并通过 `updateProjectV2ItemFieldValue` 写入 Start Date。组织 Issue 字段仅作为带有 `Legacy ...` 前缀的迁移源保留,仓库工作流不会读取它们。
 
-PR 策略工作流使用仓库 `GITHUB_TOKEN` 读取仓库 Issue 和 PR,并使用仅有组织 Projects 读取权限的 GitHub App token 执行 ProjectV2 查询。生命周期 mutation 继续使用有写权限的 App token。
+PR 策略工作流使用仓库 `GITHUB_TOKEN` 执行 REST Issue 和 PR 读取,并使用仅有仓库 Issues 与组织 Projects 读取权限的 GitHub App token 执行 ProjectV2 查询。生命周期 mutation 继续使用有写权限的 App token。
 
 Issue 生命周期工作流仅在 `pull_request.opened` 时初始化 `Start Date`。工作流读取 PR 的实时正文,保留每个能解析为 Issue 的同仓库引用,把 `created_at` 按配置的 Project 时区转换为日历日期,确保 Issue 是 Project item,并仅在当前 Project 值为空时写入日期。
 

+ 3 - 0
.github/workflows/issue-policy.yml

@@ -23,14 +23,17 @@ jobs:
           persist-credentials: false
       - name: Create Project read token
         id: app-token
+        if: ${{ github.event.pull_request.user.type != 'Bot' && github.event.pull_request.user.type != 'App' }}
         uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
         with:
           client-id: ${{ vars.DSH_ISSUE_APP_CLIENT_ID }}
           private-key: ${{ secrets.DSH_ISSUE_APP_PRIVATE_KEY }}
           owner: deepseek-harness
           repositories: deepseek-harness
+          permission-issues: read
           permission-organization-projects: read
       - name: Validate pull request
+        if: ${{ github.event.pull_request.user.type != 'Bot' && github.event.pull_request.user.type != 'App' }}
         env:
           GITHUB_TOKEN: ${{ github.token }}
           PROJECT_TOKEN: ${{ steps.app-token.outputs.token }}

+ 6 - 1
scripts/ci-workflow.spec.ts

@@ -684,26 +684,31 @@ describe('Issue lifecycle workflow', () => {
     expect(policyPullRequest.types).toContain('ready_for_review')
   })
 
-  it('uses a read-only Project token for pull request policy metadata', () => {
+  it('uses a read-only Project token only for human pull request policy metadata', () => {
     const policy = loadWorkflow('.github/workflows/issue-policy.yml')
     const policyJob = workflowJob(policy, 'policy')
     if (!Array.isArray(policyJob.steps)) throw new TypeError('Issue policy job must define steps')
     const steps = policyJob.steps.filter(isRecord)
     const tokenStep = steps.find(step => step.name === 'Create Project read token')
     const validateStep = steps.find(step => step.name === 'Validate pull request')
+    const humanPullRequest =
+      "${{ github.event.pull_request.user.type != 'Bot' && github.event.pull_request.user.type != 'App' }}"
 
     expect(tokenStep).toMatchObject({
       id: 'app-token',
+      if: humanPullRequest,
       uses: 'actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1',
       with: {
         'client-id': '${{ vars.DSH_ISSUE_APP_CLIENT_ID }}',
         'private-key': '${{ secrets.DSH_ISSUE_APP_PRIVATE_KEY }}',
         owner: 'deepseek-harness',
         repositories: 'deepseek-harness',
+        'permission-issues': 'read',
         'permission-organization-projects': 'read',
       },
     })
     expect(validateStep).toMatchObject({
+      if: humanPullRequest,
       env: {
         GITHUB_TOKEN: '${{ github.token }}',
         PROJECT_TOKEN: '${{ steps.app-token.outputs.token }}',