devflow-kit 3.5.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +178 -1
- package/README.md +10 -6
- package/dist/agents/code.md +7 -11
- package/dist/agents/{diagnose.md → debug.md} +4 -4
- package/dist/agents/{code-fix.md → fix.md} +7 -8
- package/dist/agents/{scrutinize.md → harden.md} +6 -6
- package/dist/agents/{code-edit.md → refactor.md} +8 -9
- package/dist/agents/review.md +2 -0
- package/dist/cli/agents-view/render.js +1 -1
- package/dist/cli/commands/gates.js +276 -0
- package/dist/cli/commands/init-seed.js +9 -1
- package/dist/cli/commands/init.js +34 -11
- package/dist/cli/commands/learning.js +12 -12
- package/dist/cli/commands/legacy-hooks.js +1 -1
- package/dist/cli/commands/memory.js +18 -0
- package/dist/cli/commands/uninstall.js +111 -5
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +21 -17
- package/dist/commands/code-review.md +65 -66
- package/dist/commands/debug.md +17 -17
- package/dist/commands/dynamic-build.md +104 -116
- package/dist/commands/dynamic-plan.md +13 -11
- package/dist/commands/dynamic-profile.md +9 -7
- package/dist/commands/dynamic-tickets.md +13 -11
- package/dist/commands/explore.md +15 -15
- package/dist/commands/implement.md +80 -154
- package/dist/commands/plan.md +34 -66
- package/dist/commands/release.md +11 -7
- package/dist/commands/research.md +14 -10
- package/dist/commands/resolve.md +44 -88
- package/dist/commands/self-review.md +44 -44
- package/dist/core/agent-models.js +47 -11
- package/dist/core/{learning-tuning-config.js → curate-tuning-config.js} +14 -14
- package/dist/core/echo-value.js +42 -0
- package/dist/core/evidence-policy.js +17 -5
- package/dist/core/gate-level.js +213 -0
- package/dist/core/learning-queue-cleanup.js +1 -1
- package/dist/core/manifest.js +26 -10
- package/dist/core/migrations.js +63 -44
- package/dist/core/model-discovery.js +7 -1
- package/dist/core/plugins.js +14 -12
- package/dist/core/project-paths.js +3 -3
- package/dist/core/tracker.js +5 -33
- package/dist/hud/codex-usage.js +247 -0
- package/dist/hud/components/codex-quota.js +37 -0
- package/dist/hud/components/usage-quota.js +1 -1
- package/dist/hud/config.js +1 -0
- package/dist/hud/index.js +16 -3
- package/dist/hud/render.js +3 -1
- package/dist/learning-off/agents/code.md +7 -11
- package/dist/learning-off/agents/{diagnose.md → debug.md} +4 -4
- package/dist/learning-off/agents/{code-fix.md → fix.md} +7 -8
- package/dist/learning-off/agents/{scrutinize.md → harden.md} +6 -6
- package/dist/learning-off/agents/{code-edit.md → refactor.md} +8 -9
- package/dist/learning-off/agents/review.md +2 -0
- package/dist/learning-off/commands/bug-analysis.md +9 -9
- package/dist/learning-off/commands/code-review.md +54 -59
- package/dist/learning-off/commands/debug.md +6 -10
- package/dist/learning-off/commands/dynamic-build.md +98 -112
- package/dist/learning-off/commands/dynamic-plan.md +5 -5
- package/dist/learning-off/commands/dynamic-profile.md +1 -1
- package/dist/learning-off/commands/dynamic-tickets.md +5 -5
- package/dist/learning-off/commands/explore.md +4 -8
- package/dist/learning-off/commands/implement.md +68 -146
- package/dist/learning-off/commands/plan.md +23 -58
- package/dist/learning-off/commands/release.md +4 -2
- package/dist/learning-off/commands/research.md +2 -2
- package/dist/learning-off/commands/resolve.md +33 -81
- package/dist/learning-off/commands/self-review.md +32 -36
- package/dist/skills/git/references/pr/check-merge-readiness.md +2 -0
- package/dist/skills/git/references/pr/update-pr-evidence.md +4 -6
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +7 -9
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +7 -15
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +7 -15
- package/dist/targets/claude-code/installer.js +117 -84
- package/dist/targets/claude-code/learning-install.js +15 -3
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +1 -1
- package/src/assets/agents/_partials/_code.mds +7 -8
- package/src/assets/agents/code.mds +4 -7
- package/src/assets/agents/{learning.md → curate.md} +8 -10
- package/src/assets/agents/{diagnose.mds → debug.mds} +4 -4
- package/src/assets/agents/evaluate.md +4 -4
- package/src/assets/agents/{code-fix.mds → fix.mds} +5 -5
- package/src/assets/agents/{scrutinize.mds → harden.mds} +6 -6
- package/src/assets/agents/{simplify.md → polish.md} +4 -4
- package/src/assets/agents/{code-edit.mds → refactor.mds} +6 -6
- package/src/assets/agents/review.mds +2 -0
- package/src/assets/agents/synthesize.md +2 -2
- package/src/assets/agents/test.md +2 -2
- package/src/assets/commands/_partials/_compliance.mds +1 -1
- package/src/assets/commands/_partials/_decisions.mds +26 -6
- package/src/assets/commands/_partials/_engine.mds +15 -15
- package/src/assets/commands/_partials/_evidence_policy.mds +0 -17
- package/src/assets/commands/_partials/_knowledge.mds +14 -9
- package/src/assets/commands/_partials/_preamble.mds +4 -2
- package/src/assets/commands/_partials/_roster.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/_partials/_wave.mds +10 -17
- package/src/assets/commands/bug-analysis.mds +7 -7
- package/src/assets/commands/code-review.mds +51 -56
- package/src/assets/commands/debug.mds +2 -2
- package/src/assets/commands/dynamic-build.mds +67 -74
- package/src/assets/commands/implement.mds +65 -127
- package/src/assets/commands/plan.mds +21 -61
- package/src/assets/commands/release.mds +5 -1
- package/src/assets/commands/resolve.mds +28 -76
- package/src/assets/commands/self-review.mds +27 -27
- package/src/assets/mds/git/_ops_steps.mds +3 -3
- package/src/assets/mds/git/_pr.mds +6 -6
- package/src/assets/mds/tracker/_common.mds +23 -12
- package/src/assets/mds/tracker/_github.mds +2 -9
- package/src/assets/mds/tracker/_jira.mds +2 -15
- package/src/assets/mds/tracker/_linear.mds +2 -15
- package/src/assets/scripts/ci-wait.cjs +22 -4
- package/src/assets/scripts/claude-md-audit.cjs +66 -51
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +4 -3
- package/src/assets/scripts/hooks/background-memory-update +404 -95
- package/src/assets/scripts/hooks/capture-prompt +1 -1
- package/src/assets/scripts/hooks/capture-question +1 -1
- package/src/assets/scripts/hooks/capture-turn +1 -1
- package/src/assets/scripts/hooks/json-helper.cjs +13 -2
- package/src/assets/scripts/hooks/json-parse +18 -3
- package/src/assets/scripts/hooks/lib/learning-store.cjs +1 -1
- package/src/assets/scripts/hooks/lib/project-paths.cjs +4 -4
- package/src/assets/scripts/hooks/memory-permission +358 -0
- package/src/assets/scripts/hooks/preamble +1 -1
- package/src/assets/scripts/hooks/session-start-context +60 -47
- package/src/assets/scripts/hooks/session-start-memory +26 -5
- package/src/assets/scripts/lib/project-config.cjs +19 -4
- package/src/assets/scripts/resolve-settings.cjs +200 -32
- package/src/assets/scripts/verify-evidence.cjs +726 -9
- package/src/assets/skills/agent-references/SKILL.md +14 -6
- package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
- package/src/assets/skills/agent-references/references/commands/implement-evidence-asks.md +35 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +15 -0
- package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
- package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +1 -1
- package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
- package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
- package/src/assets/skills/apply-decisions/SKILL.md +6 -4
- package/src/assets/skills/docs-framework/SKILL.md +3 -3
- package/src/assets/skills/qa/SKILL.md +1 -1
- package/src/assets/skills/quality-gates/SKILL.md +7 -7
- package/src/assets/skills/quality-gates/references/report-template.md +7 -7
- /package/src/assets/skills/agent-references/references/{learning → curate}/maintain.md +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Self-review workflow -
|
|
2
|
+
description: Self-review workflow - Polish agent (code clarity) then Harden agent (9-pillar quality gate)
|
|
3
3
|
---
|
|
4
4
|
# Self-Review Command
|
|
5
5
|
|
|
6
|
-
Run
|
|
6
|
+
Run Polish agent and Harden agent sequentially on changed files for post-implementation quality refinement.
|
|
7
7
|
|
|
8
8
|
## Usage
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@ Detect changed files and build context:
|
|
|
22
22
|
2. Else run `git diff --name-only HEAD` + `git diff --name-only --cached` to get staged + unstaged
|
|
23
23
|
3. If no changes found, report "No changes to review" and exit
|
|
24
24
|
4. Build TASK_DESCRIPTION from recent commit messages or branch name
|
|
25
|
-
5. Record `HEAD_BEFORE` (`git rev-parse HEAD`) now, before
|
|
25
|
+
5. Record `HEAD_BEFORE` (`git rev-parse HEAD`) now, before Polish runs — Phase 3 compares against it
|
|
26
26
|
|
|
27
27
|
**Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
|
|
28
28
|
|
|
@@ -30,9 +30,9 @@ Detect changed files and build context:
|
|
|
30
30
|
node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
|
|
33
|
+
Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off GATE_LEVEL=full` instead.
|
|
34
34
|
|
|
35
|
-
The accepted line is the only source of these values
|
|
35
|
+
The accepted line is the only source of these values.
|
|
36
36
|
|
|
37
37
|
### Load Feature Knowledge
|
|
38
38
|
|
|
@@ -56,7 +56,7 @@ If `index.md` exists and contains at least one entry line, use it for relevance
|
|
|
56
56
|
|
|
57
57
|
**Step 2 — Fallback: glob frontmatter (if `index.md` is absent or empty):**
|
|
58
58
|
|
|
59
|
-
Glob `{worktree}/.devflow/features/*/KNOWLEDGE.md`. For each file found, read only its YAML frontmatter block (between the opening and closing `---` delimiters).
|
|
59
|
+
Glob `{worktree}/.devflow/features/*/KNOWLEDGE.md`. For each file found, read only its YAML frontmatter block (between the opening and closing `---` delimiters).
|
|
60
60
|
|
|
61
61
|
**Step 3 — Pick relevant KBs:**
|
|
62
62
|
|
|
@@ -87,35 +87,35 @@ Headings: L5 Rules · L40 Overview · L62 Anti-Patterns · L118 Key Files
|
|
|
87
87
|
|
|
88
88
|
A KB with no Rules section labels its entries `Rules ({section name}):` and gives them no ID. `FEATURE_KNOWLEDGE` is these blocks; `FEATURE_KNOWLEDGE_RULES` is the same blocks without the `Headings:` line. Both come from this one selection, and each spawn names the variable its recipient takes. If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set both to `(none)`.
|
|
89
89
|
|
|
90
|
-
**One git call, then direct reads — no `.cjs` script.**
|
|
90
|
+
**One git call, then direct reads — no `.cjs` script.**
|
|
91
91
|
|
|
92
|
-
Pass `FEATURE_KNOWLEDGE_RULES` to
|
|
92
|
+
Pass `FEATURE_KNOWLEDGE_RULES` to Harden agent.
|
|
93
93
|
|
|
94
94
|
**Extract:** FILES_CHANGED (list), TASK_DESCRIPTION (string), FEATURE_KNOWLEDGE (string, optional), FEATURE_KNOWLEDGE_RULES (string, optional), HEAD_BEFORE (40-hex SHA)
|
|
95
95
|
|
|
96
|
-
### Phase 1:
|
|
96
|
+
### Phase 1: Polish agent (Code Refinement)
|
|
97
97
|
|
|
98
|
-
**Produces:**
|
|
98
|
+
**Produces:** POLISH_COMMITS
|
|
99
99
|
**Requires:** FILES_CHANGED, TASK_DESCRIPTION
|
|
100
100
|
|
|
101
|
-
Spawn
|
|
101
|
+
Spawn Polish agent to refine code for clarity and consistency:
|
|
102
102
|
|
|
103
|
-
Agent(subagent_type="
|
|
103
|
+
Agent(subagent_type="Polish", run_in_background=false):
|
|
104
104
|
"TASK_DESCRIPTION: {task_description}
|
|
105
105
|
FILES_CHANGED: {files_changed}
|
|
106
|
-
|
|
106
|
+
Polish and refine the code for clarity and consistency while preserving functionality.
|
|
107
107
|
Commit any improvements with a conventional-commit message."
|
|
108
108
|
|
|
109
|
-
**Wait for completion.**
|
|
109
|
+
**Wait for completion.** Polish agent commits changes directly.
|
|
110
110
|
|
|
111
|
-
### Phase 2:
|
|
111
|
+
### Phase 2: Harden agent (9-Pillar Quality Gate)
|
|
112
112
|
|
|
113
|
-
**Produces:**
|
|
113
|
+
**Produces:** HARDEN_STATUS
|
|
114
114
|
**Requires:** FILES_CHANGED, TASK_DESCRIPTION
|
|
115
115
|
|
|
116
|
-
Spawn
|
|
116
|
+
Spawn Harden agent for quality evaluation and fixing:
|
|
117
117
|
|
|
118
|
-
Agent(subagent_type="
|
|
118
|
+
Agent(subagent_type="Harden", run_in_background=false):
|
|
119
119
|
"TASK_DESCRIPTION: {task_description}
|
|
120
120
|
FILES_CHANGED: {files_changed}
|
|
121
121
|
FEATURE_KNOWLEDGE: {feature_knowledge_rules}
|
|
@@ -127,11 +127,11 @@ Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE. Skip if (none)."
|
|
|
127
127
|
### Phase 3: Conditional Validation
|
|
128
128
|
|
|
129
129
|
**Produces:** VALIDATION_RESULT
|
|
130
|
-
**Requires:** HEAD_BEFORE,
|
|
130
|
+
**Requires:** HEAD_BEFORE, HARDEN_STATUS
|
|
131
131
|
|
|
132
132
|
If STATUS is BLOCKED, skip this phase: the report (Phase 4) names the blocker.
|
|
133
133
|
|
|
134
|
-
Run this phase only if HEAD moved — `git rev-parse HEAD` differs from `HEAD_BEFORE`, meaning
|
|
134
|
+
Run this phase only if HEAD moved — `git rev-parse HEAD` differs from `HEAD_BEFORE`, meaning Polish or Harden committed (Polish alone counts, whatever Harden's STATUS is). Otherwise skip it. Its scope is changed-only over what the two agents changed:
|
|
135
135
|
|
|
136
136
|
Agent(subagent_type="Validate", run_in_background=false):
|
|
137
137
|
"FILES_CHANGED: {output of `git diff --name-only HEAD_BEFORE..HEAD`}
|
|
@@ -143,7 +143,7 @@ Run build, typecheck, lint, test on modified files"
|
|
|
143
143
|
|
|
144
144
|
### Phase 4: Report
|
|
145
145
|
|
|
146
|
-
**Requires:**
|
|
146
|
+
**Requires:** HARDEN_STATUS, VALIDATION_RESULT
|
|
147
147
|
|
|
148
148
|
Display summary:
|
|
149
149
|
|
|
@@ -152,10 +152,10 @@ Display summary:
|
|
|
152
152
|
**Files Reviewed**: {n}
|
|
153
153
|
**Status**: {PASS|FIXED|BLOCKED}
|
|
154
154
|
|
|
155
|
-
###
|
|
155
|
+
### Polish agent
|
|
156
156
|
- {n} files refined for clarity
|
|
157
157
|
|
|
158
|
-
###
|
|
158
|
+
### Harden agent (9-Pillar Evaluation)
|
|
159
159
|
| Pillar | Status |
|
|
160
160
|
|--------|--------|
|
|
161
161
|
| Design | {status} |
|
|
@@ -209,16 +209,12 @@ FILES_CHANGED: {list of files changed}
|
|
|
209
209
|
Write the knowledge base to:
|
|
210
210
|
{worktree}/.devflow/features/{slug}/KNOWLEDGE.md
|
|
211
211
|
|
|
212
|
-
Then update the index cache
|
|
212
|
+
Then update the index cache:
|
|
213
213
|
{worktree}/.devflow/features/index.md
|
|
214
214
|
|
|
215
215
|
Index line format: `- **{slug}** — {areas} — {Use-when description}` — at most 300 characters, the description at most 220; reword a longer one, never cut it.
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
The frontmatter in KNOWLEDGE.md is the source of truth — index.md is only a cache. Write the two files directly — no intermediate result JSON files, no external scripts.
|
|
220
|
-
|
|
221
|
-
After writing, commit the two files to the current worktree branch yourself by running git via your Bash tool (do not use a script). Stage ONLY .devflow/features/index.md and .devflow/features/{slug}/KNOWLEDGE.md, then commit just those paths with a docs(knowledge): message. Do NOT push, do NOT force, do NOT stage anything else. Follow your Commit Protocol — it is non-blocking, so if any git step fails, report KB_COMMIT and finish normally."
|
|
217
|
+
Then commit the two files as your Commit Protocol directs."
|
|
222
218
|
```
|
|
223
219
|
|
|
224
220
|
**Step 4 — Surface an uncommitted knowledge base:**
|
|
@@ -235,14 +231,14 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
|
|
|
235
231
|
│ ├─ Git diff for changed files
|
|
236
232
|
│ └─ Record HEAD_BEFORE
|
|
237
233
|
│
|
|
238
|
-
├─ Phase 1:
|
|
234
|
+
├─ Phase 1: Polish agent
|
|
239
235
|
│ └─ Code refinement (commits directly)
|
|
240
236
|
│
|
|
241
|
-
├─ Phase 2:
|
|
237
|
+
├─ Phase 2: Harden agent
|
|
242
238
|
│ └─ 9-pillar quality gate (may fix and commit)
|
|
243
239
|
│
|
|
244
240
|
├─ Phase 3: Validate agent (conditional)
|
|
245
|
-
│ └─ If HEAD moved since HEAD_BEFORE (
|
|
241
|
+
│ └─ If HEAD moved since HEAD_BEFORE (Polish or Harden committed), changed-only Validate over HEAD_BEFORE..HEAD
|
|
246
242
|
│
|
|
247
243
|
└─ Phase 4: Report
|
|
248
244
|
└─ Display summary with pillar status
|
|
@@ -252,15 +248,15 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
|
|
|
252
248
|
| Case | Handling |
|
|
253
249
|
|------|----------|
|
|
254
250
|
| No changes | Report "No changes to review" and exit |
|
|
255
|
-
|
|
|
256
|
-
|
|
|
251
|
+
| Polish agent finds nothing | Normal, continue to Harden agent |
|
|
252
|
+
| Harden agent BLOCKED | Report blocking issue, halt workflow |
|
|
257
253
|
| Validation fails | Report failures, halt (don't create broken state) |
|
|
258
254
|
| Not in git repo | Report error, suggest running in git repo |
|
|
259
255
|
|
|
260
256
|
## Principles
|
|
261
257
|
|
|
262
258
|
1. **Orchestration only** - Command spawns agents, doesn't do the work
|
|
263
|
-
2. **Sequential execution** -
|
|
264
|
-
3. **Validation gate** - If
|
|
259
|
+
2. **Sequential execution** - Polish agent must complete before Harden agent
|
|
260
|
+
3. **Validation gate** - If Polish agent or Harden agent committed, the changes must pass validation
|
|
265
261
|
4. **Honest reporting** - Display actual agent outputs
|
|
266
262
|
5. **Fail fast** - Stop on BLOCKED or validation failure
|
|
@@ -25,6 +25,8 @@ Load for `check-merge-readiness` under every tracker provider.
|
|
|
25
25
|
|
|
26
26
|
### Process
|
|
27
27
|
|
|
28
|
+
Run the script from `WORKTREE_PATH` (else cwd) and print its stdout, nothing else: `node "$HOME/.devflow/scripts/verify-evidence.cjs" readiness --pr {PR_NUMBER}; echo "exit=$?"`, adding `--require-non-author-approval {REQUIRE_NON_AUTHOR_APPROVAL}` only when that is `true` or `false`, with the Bash `timeout` at 180000. On exit 0 or 2 its stdout is the `## Merge Readiness` block (exit 2: `DEGRADED`, closed by a `TRACEABILITY: DEGRADED ({reason})` line); any other exit, or empty stdout, is `TRACEABILITY: DEGRADED (readiness unavailable)`. The script makes the reads and applies the ladder below, so the steps are its specification: run none of them yourself.
|
|
29
|
+
|
|
28
30
|
1. Fetch unresolved review threads via GraphQL: `reviewThreads(first: 100) { nodes { isResolved } totalCount }`. Count unresolved from nodes (`isResolved == false`). If `totalCount > 100`, report the unresolved count as approximate: prefix with `>` and note `(count approximate — PR has more than 100 threads)`.
|
|
29
31
|
2. Fetch PR review decision: `gh pr view {PR_NUMBER} --json reviewDecision --jq '.reviewDecision'`
|
|
30
32
|
- Values: `APPROVED`, `CHANGES_REQUESTED`, `REVIEW_REQUIRED`, or null
|
|
@@ -15,13 +15,11 @@ Update the PR's test-plan block and evidence comment.
|
|
|
15
15
|
|
|
16
16
|
Load for `update-pr-evidence` under every tracker provider.
|
|
17
17
|
|
|
18
|
-
**PR mechanics held here:** the
|
|
18
|
+
**PR mechanics held here:** the one script run. `verify-evidence.cjs publish` owns every step, marker, grammar and state rule, and scrubs (D11) each body before it posts one; nothing here restates any of them.
|
|
19
19
|
|
|
20
20
|
### Process
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
1. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/verify-evidence.cjs" publish --pr {PR_NUMBER}; echo "exit=$?"`, adding `--publication {REVIEW_PUBLICATION}` only when that is `auto`, `full`, `off` or `stub`, and `--evidence "{EVIDENCE_FILE}"` when given — only a value matching `^[A-Za-z0-9._/-]{1,255}$` reaches the shell; any other is a failed run. Set the Bash `timeout` to 180000.
|
|
23
|
+
2. Print the script's stdout and nothing else. On exit 0 it is the `## PR Evidence` block: the heading, the line `EVIDENCE pr:<n> head:<sha> total:<n> VERIFIED-CI:<n> ATTESTED-LOCAL:<n> UNVERIFIED:<n> STALE:<n> FAILED:<n> INDETERMINATE:<n> stale:<ids|none> exceptions:<kinds|none> approval:<yes|no|unchecked> key:<hex> posted:<yes|no|n/a> body:<same|changed>`, and the `**Body**:` / `**Comment**:` line, then one `TRACEABILITY: DEGRADED ({reason})` line for each part that degraded, such as `DEGRADED (concurrent edit)` (the body edit then reads `SKIPPED`), `DEGRADED (body read-back mismatch)` or `DEGRADED (redaction unavailable)`: a body the script cannot scrub is never posted. On exit 2 it is `TRACEABILITY: DEGRADED (evidence unavailable)`. Any other exit, or empty stdout, is a failed run: emit that same line.
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
2. **Body**, only on `body:changed` (else `UNCHANGED`): `node "$V/redact-secrets.cjs" "$DEVFLOW_NOTES_RAW" "$DEVFLOW_NOTES" && node "$V/verify-evidence.cjs" splice --pr {PR_NUMBER} --state "$S" --block "$DEVFLOW_NOTES" --out "$DEVFLOW_BODY" && gh pr edit {PR_NUMBER} --body-file "$DEVFLOW_BODY"`. Only the composed block is scrubbed (D11); every byte outside its markers is the PR's own and stays identical. `splice` re-reads the body: unchanged since step 1 → it writes; changed → it splices once onto the fresh body and re-reads; changed again → `SPLICE conflict`: `SKIPPED` and `TRACEABILITY: DEGRADED (concurrent edit)`. Any other non-zero → `DEGRADED ({reason})`, naming a printed `SPLICE` token. No edit either way; go to step 4.
|
|
26
|
-
3. **Read back** after an edit: `node "$V/verify-evidence.cjs" readback --pr {PR_NUMBER} --expect "$DEVFLOW_BODY"`; anything but `READBACK ok` → `TRACEABILITY: DEGRADED (body read-back mismatch)`, else `EDITED`. GitHub has no conditional body edit: a human edit landing between the last re-read and `gh pr edit` is overwritten (it stays in the PR's edit history), and the read-back proves only that these bytes landed.
|
|
27
|
-
4. **Comment.** `posted:yes` → `SKIPPED`; `posted:n/a` → `OFF`. Otherwise apply the Comment-sink scrub (D11): `node "$V/redact-secrets.cjs" "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" && gh pr comment {PR_NUMBER} --body-file "$DEVFLOW_BODY"` → `POSTED`. Never edit or delete an evidence comment. On 5xx retry once; still 5xx → `DEGRADED ({reason})`.
|
|
25
|
+
Run nothing else: the script makes the one PR read, the compare-and-swap body edit, its read-back and the append-only comment itself, and posts nothing it could not scrub.
|
|
@@ -32,7 +32,7 @@ Pre-flight: make the branch ready for review and open its PR.
|
|
|
32
32
|
|
|
33
33
|
Load when the resolved tracker provider is `github` and the operation is `ensure-pr-ready`.
|
|
34
34
|
|
|
35
|
-
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`); step 4b
|
|
35
|
+
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`); step 4b takes its link line and refuses it the same way under every provider; the line itself, `Closes #{n}`, is this provider's.
|
|
36
36
|
|
|
37
37
|
### Process
|
|
38
38
|
|
|
@@ -66,11 +66,11 @@ Load when the resolved tracker provider is `github` and the operation is `ensure
|
|
|
66
66
|
|
|
67
67
|
This is a sink check, not a provider check: the spawn that resolved the provider rendered the line, this spawn does not re-derive it, and you never decide it. Two bounds sit outside the pattern because an anchor cannot express them, and you apply both: the value is **rejected if it carries a newline** — anchors are read as end-of-LINE by some engines, and everything after the first line would land in the PR body as free text — and rejected if it exceeds **60 characters**, which no valid line approaches.
|
|
68
68
|
|
|
69
|
-
`(none)
|
|
69
|
+
`(none)` is **not a mismatch**: it means no line was captured — never compose one from `ISSUE_NUMBER`. A bare issue number is not a reference at all — the same digits name a different issue under each provider — which is why `TRACEABILITY: DEGRADED (ambiguous issue reference)` exists rather than a `#`-prefixed guess. On a MISMATCH, do not paste it and do not repair it — emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)`.
|
|
70
70
|
|
|
71
|
-
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN
|
|
71
|
+
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN`; if the block is absent, say so.
|
|
72
72
|
|
|
73
|
-
**A given `ISSUE_PR_LINK` is final.** When it is given
|
|
73
|
+
**A given `ISSUE_PR_LINK` is final.** When it is given, `(none)` included, it is the section's whole answer, whether the gate above admitted it or refused it: step 4b does not resolve a number or render a line. An admitted line goes under `## Related Issues` in the body this step creates; on an existing PR whose body has no `## Related Issues` section, step 4b's PR-host half publishes it. Only an absent `ISSUE_PR_LINK` goes to step 4b, and only when no admitted wave block exists. When `ISSUE_NUMBER` is set and no line is admitted, emit the heading with no reference and `TRACEABILITY: DEGRADED (ambiguous issue reference)`; when it is `(none)`, write no heading.
|
|
74
74
|
|
|
75
75
|
**Pasting `PR_EXCEPTIONS`.** When `PR_EXCEPTIONS` is provided (not `(none)`), append it verbatim as the body's last section — it is scrubbed with the body. Re-check its shape first: its first line must be exactly `## Evidence Exceptions`, and every line after it must match this pattern as the WHOLE line:
|
|
76
76
|
|
|
@@ -82,14 +82,12 @@ Load when the resolved tracker provider is `github` and the operation is `ensure
|
|
|
82
82
|
|
|
83
83
|
**D11 scrub (PR body is a GitHub-visible sink):** Compose the final PR body to `$DEVFLOW_BODY_RAW` (`DEVFLOW_BODY_RAW="$(mktemp)"`); scrub via `node "$HOME/.devflow/scripts/redact-secrets.cjs" "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY"` (where `DEVFLOW_BODY="$(mktemp)"`). On success: create PR with `gh pr create … --body-file "$DEVFLOW_BODY"`. **On scrubber failure** (non-zero exit or script missing): still create the PR — PR existence is the deliverable — but with a minimal body containing only the task reference, plan path (if available), and issue link (if ISSUE_NUMBER provided), plus the literal line `TRACEABILITY: DEGRADED (redaction unavailable)`. Never post `$DEVFLOW_BODY_RAW`.
|
|
84
84
|
|
|
85
|
-
4b. (ALWAYS-ON) Ensure PR body contains a `## Related Issues` section with `Closes #{n}`
|
|
86
|
-
a. Prefer the issue number returned by `setup-task` / `ensure-traceable-issue` for this branch.
|
|
87
|
-
b. If unavailable, fall back to the branch name pattern `{type}/{number}-{slug}`: extract the numeric segment and verify with `gh issue view {n} --json number,state`. If the call fails or `.state` is not `"open"`, skip silently — never add a `Closes` link for an unverified number. Branches like `chore/2026-cleanup` or `fix/2fa-login` may produce false matches; the existence check is the guard.
|
|
85
|
+
4b. (ALWAYS-ON) Ensure the PR body contains a `## Related Issues` section with the `Closes #{n}` line of this branch's issue. A given `ISSUE_PR_LINK` is already decided in step 4a: skip the resolution below. Resolution: the only source is the `PR link line` of the `### Handoff Values` block that `setup-task` or `fetch-issue` returned for this branch. Publish it only if it passes step 4a's shape check. A line that fails the check is refused as step 4a refuses it, not repaired: emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)` and publish nothing. This step composes no line itself. Never compose a line from a bare issue number or a branch name.
|
|
88
86
|
|
|
89
87
|
Publish the section through step 4b's PR-host half.
|
|
90
88
|
|
|
91
|
-
If no
|
|
92
|
-
|
|
89
|
+
If no such line exists, skip silently.
|
|
90
|
+
A failure while updating the PR body emits `TRACEABILITY: DEGRADED ({reason})` and continues — a failed Related Issues update never blocks the PR.
|
|
93
91
|
4c. Retitle, only when `APPLY_CONVENTIONS` is `true`: if the PR title breaks the convention in the PR Titles section of `.devflow/conventions.md`, retitle it; skip silently when that file is absent. Two rules, because the title derives from third-party PR titles:
|
|
94
92
|
- **Validate before use.** Skip the retitle (leave the PR title as-is, no error) if the composed title contains any of `` $ ` \ " ' ; | & < > `` or a newline.
|
|
95
93
|
- **Pass as argv, never as command text.** Bind it to a shell variable and pass that variable: `gh pr edit {PR_NUMBER} --title "$DEVFLOW_PR_TITLE"`. Never interpolate it: `$(...)`, backticks and `${...}` all expand inside double quotes.
|
|
@@ -32,7 +32,7 @@ Pre-flight: make the branch ready for review and open its PR.
|
|
|
32
32
|
|
|
33
33
|
Load when the resolved tracker provider is `jira` and the operation is `ensure-pr-ready`.
|
|
34
34
|
|
|
35
|
-
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`): devflow deliberately keeps pull requests on their existing host while the tracker is this one, so nothing about the PR surface is provider-dependent. Step 4b
|
|
35
|
+
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`): devflow deliberately keeps pull requests on their existing host while the tracker is this one, so nothing about the PR surface is provider-dependent. Step 4b takes its link line and refuses it the same way under every provider; only its note on closing effects is this provider's.
|
|
36
36
|
|
|
37
37
|
### Process
|
|
38
38
|
|
|
@@ -66,11 +66,11 @@ Load when the resolved tracker provider is `jira` and the operation is `ensure-p
|
|
|
66
66
|
|
|
67
67
|
This is a sink check, not a provider check: the spawn that resolved the provider rendered the line, this spawn does not re-derive it, and you never decide it. Two bounds sit outside the pattern because an anchor cannot express them, and you apply both: the value is **rejected if it carries a newline** — anchors are read as end-of-LINE by some engines, and everything after the first line would land in the PR body as free text — and rejected if it exceeds **60 characters**, which no valid line approaches.
|
|
68
68
|
|
|
69
|
-
`(none)
|
|
69
|
+
`(none)` is **not a mismatch**: it means no line was captured — never compose one from `ISSUE_NUMBER`. A bare issue number is not a reference at all — the same digits name a different issue under each provider — which is why `TRACEABILITY: DEGRADED (ambiguous issue reference)` exists rather than a `#`-prefixed guess. On a MISMATCH, do not paste it and do not repair it — emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)`.
|
|
70
70
|
|
|
71
|
-
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN
|
|
71
|
+
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN`; if the block is absent, say so.
|
|
72
72
|
|
|
73
|
-
**A given `ISSUE_PR_LINK` is final.** When it is given
|
|
73
|
+
**A given `ISSUE_PR_LINK` is final.** When it is given, `(none)` included, it is the section's whole answer, whether the gate above admitted it or refused it: step 4b does not resolve a number or render a line. An admitted line goes under `## Related Issues` in the body this step creates; on an existing PR whose body has no `## Related Issues` section, step 4b's PR-host half publishes it. Only an absent `ISSUE_PR_LINK` goes to step 4b, and only when no admitted wave block exists. When `ISSUE_NUMBER` is set and no line is admitted, emit the heading with no reference and `TRACEABILITY: DEGRADED (ambiguous issue reference)`; when it is `(none)`, write no heading.
|
|
74
74
|
|
|
75
75
|
**Pasting `PR_EXCEPTIONS`.** When `PR_EXCEPTIONS` is provided (not `(none)`), append it verbatim as the body's last section — it is scrubbed with the body. Re-check its shape first: its first line must be exactly `## Evidence Exceptions`, and every line after it must match this pattern as the WHOLE line:
|
|
76
76
|
|
|
@@ -82,16 +82,12 @@ Load when the resolved tracker provider is `jira` and the operation is `ensure-p
|
|
|
82
82
|
|
|
83
83
|
**D11 scrub (PR body is a GitHub-visible sink):** Compose the final PR body to `$DEVFLOW_BODY_RAW` (`DEVFLOW_BODY_RAW="$(mktemp)"`); scrub via `node "$HOME/.devflow/scripts/redact-secrets.cjs" "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY"` (where `DEVFLOW_BODY="$(mktemp)"`). On success: create PR with `gh pr create … --body-file "$DEVFLOW_BODY"`. **On scrubber failure** (non-zero exit or script missing): still create the PR — PR existence is the deliverable — but with a minimal body containing only the task reference, plan path (if available), and issue link (if ISSUE_NUMBER provided), plus the literal line `TRACEABILITY: DEGRADED (redaction unavailable)`. Never post `$DEVFLOW_BODY_RAW`.
|
|
84
84
|
|
|
85
|
-
4b. (ALWAYS-ON) Ensure the PR body contains a `## Related Issues` section
|
|
86
|
-
a. Prefer the issue key returned by `setup-task` / `ensure-traceable-issue` for this branch — it was verified at creation time.
|
|
87
|
-
b. Otherwise fall back to the branch name pattern `{type}/{KEY}-{slug}`: extract the segment matching `^[A-Z][A-Z0-9_]{1,9}-[1-9][0-9]{0,8}$` and verify it with the *fetch by key* capability. If the call fails, or the issue is not open, **skip silently** — never render a link for an unverified reference. A branch name can carry a token that merely looks like one, and the existence check is the guard.
|
|
88
|
-
c. The *fetch by key* capability absent or denied ⇒ `TRACEABILITY: DEGRADED (no tracker tool for fetch by key)` and skip the section; the PR is never blocked on it.
|
|
89
|
-
|
|
90
|
-
Render the line through `## Reference Rendering`. **This provider has no closing-reference magic** — a reference in a PR body does not transition or close anything here, and claiming otherwise in the rendered text would promise an effect that never happens; closing is a `## Transitions` matter and `gather-release-evidence` reports the absence as `TRACEABILITY: DEGRADED (unsupported by jira)`. `## Reference Rendering` absent, or its token discarded by the gate below ⇒ render the key on its own line under the section heading, and record the discard under `### Substitutions`.
|
|
85
|
+
4b. (ALWAYS-ON) Ensure the PR body contains a `## Related Issues` section with the `Refs {KEY}-{n}` line of this branch's issue. A given `ISSUE_PR_LINK` is already decided in step 4a: skip the resolution below. Resolution: the only source is the `PR link line` of the `### Handoff Values` block that `setup-task` or `fetch-issue` returned for this branch. Publish it only if it passes step 4a's shape check. A line that fails the check is refused as step 4a refuses it, not repaired: emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)` and publish nothing. This step composes no line itself. Never compose a line from a bare issue number or a branch name. **This provider has no closing-reference magic** — a reference in a PR body does not transition or close anything here, and claiming otherwise in the rendered text would promise an effect that never happens; closing is a `## Transitions` matter and `gather-release-evidence` reports the absence as `TRACEABILITY: DEGRADED (unsupported by jira)`.
|
|
91
86
|
|
|
92
87
|
Publish the section through step 4b's PR-host half.
|
|
93
88
|
|
|
94
|
-
If no
|
|
89
|
+
If no such line exists, skip silently.
|
|
90
|
+
A failure while updating the PR body emits `TRACEABILITY: DEGRADED ({reason})` and continues — a failed Related Issues update never blocks the PR.
|
|
95
91
|
4c. Retitle, only when `APPLY_CONVENTIONS` is `true`: if the PR title breaks the convention in the PR Titles section of `.devflow/conventions.md`, retitle it; skip silently when that file is absent. Two rules, because the title derives from third-party PR titles:
|
|
96
92
|
- **Validate before use.** Skip the retitle (leave the PR title as-is, no error) if the composed title contains any of `` $ ` \ " ' ; | & < > `` or a newline.
|
|
97
93
|
- **Pass as argv, never as command text.** Bind it to a shell variable and pass that variable: `gh pr edit {PR_NUMBER} --title "$DEVFLOW_PR_TITLE"`. Never interpolate it: `$(...)`, backticks and `${...}` all expand inside double quotes.
|
|
@@ -100,10 +96,6 @@ Load when the resolved tracker provider is `jira` and the operation is `ensure-p
|
|
|
100
96
|
5. Get base branch from PR
|
|
101
97
|
6. Derive branch-slug (replace `/` with `-`)
|
|
102
98
|
|
|
103
|
-
**The read-site shape gate for `## Reference Rendering`.** The token arrives from the tracker configuration file, which is hand-editable and machine-wide, so it is parsed HERE — at the sink that renders it, and never on the writer's word. Require `^[A-Za-z0-9 #{}/_.-]{1,60}$`, anchored at both ends, and **discard** any token carrying a backtick, a `$`, a `"`, a `\`, a `;` or a newline. The anchored shape is the gate; the metachar denylist is a second, independent control, named separately so widening the shape for a new token form cannot silently relax it. **Discard, never repair** — a repaired token is one nobody can predict — and a discarded token falls back to **the resolved provider's** documented default, stated once in that provider's own mechanics, with a `### Substitutions` row recording what was dropped. An absent `## Reference Rendering` section, an absent file and a discarded token are the SAME outcome: the documented default. This gate never yields `# UNRESOLVED:`.
|
|
104
|
-
|
|
105
|
-
**This provider's documented default is `Refs {KEY}-{n}`.** It is stated here because it is a provider fact, and the gate above is what routes to it: a section that is absent, a file that is absent and a token the gate discarded all render this, and none of them is a degradation.
|
|
106
|
-
|
|
107
99
|
### Step 4b's PR-host half
|
|
108
100
|
|
|
109
101
|
Step 4b publishes its section only through this: find the open PR with `gh pr list --head {branch} --state open --limit 1`; compose the existing body plus the `## Related Issues` section to `$DEVFLOW_BODY_RAW` — the existing body is third-party-editable, so never interpolate it into a command string; apply the Comment-sink scrub (D11); on success: `gh pr edit {PR_NUMBER} --body-file "$DEVFLOW_BODY"`.
|
|
@@ -32,7 +32,7 @@ Pre-flight: make the branch ready for review and open its PR.
|
|
|
32
32
|
|
|
33
33
|
Load when the resolved tracker provider is `linear` and the operation is `ensure-pr-ready`.
|
|
34
34
|
|
|
35
|
-
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`): devflow deliberately keeps pull requests on their existing host while the tracker is this one, so nothing about the PR surface is provider-dependent. Step 4b
|
|
35
|
+
**Mechanics held here:** the whole operation. Steps 1–4a, the PR-host half of step 4b and steps 4c–6 are PR-host mechanics, the same under every provider (written once in `_ops.mds`): devflow deliberately keeps pull requests on their existing host while the tracker is this one, so nothing about the PR surface is provider-dependent. Step 4b takes its link line and refuses it the same way under every provider; only its note on closing effects is this provider's.
|
|
36
36
|
|
|
37
37
|
### Process
|
|
38
38
|
|
|
@@ -66,11 +66,11 @@ Load when the resolved tracker provider is `linear` and the operation is `ensure
|
|
|
66
66
|
|
|
67
67
|
This is a sink check, not a provider check: the spawn that resolved the provider rendered the line, this spawn does not re-derive it, and you never decide it. Two bounds sit outside the pattern because an anchor cannot express them, and you apply both: the value is **rejected if it carries a newline** — anchors are read as end-of-LINE by some engines, and everything after the first line would land in the PR body as free text — and rejected if it exceeds **60 characters**, which no valid line approaches.
|
|
68
68
|
|
|
69
|
-
`(none)
|
|
69
|
+
`(none)` is **not a mismatch**: it means no line was captured — never compose one from `ISSUE_NUMBER`. A bare issue number is not a reference at all — the same digits name a different issue under each provider — which is why `TRACEABILITY: DEGRADED (ambiguous issue reference)` exists rather than a `#`-prefixed guess. On a MISMATCH, do not paste it and do not repair it — emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)`.
|
|
70
70
|
|
|
71
|
-
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN
|
|
71
|
+
This re-check is the only gate on that value — no operation checks the rendered line's shape before returning it — and it belongs at this paste because a value that was well-formed when it was produced is still attacker-influenceable text by the time it reaches a GitHub-visible sink. Never re-derive `ISSUE_BRANCH_TOKEN`; if the block is absent, say so.
|
|
72
72
|
|
|
73
|
-
**A given `ISSUE_PR_LINK` is final.** When it is given
|
|
73
|
+
**A given `ISSUE_PR_LINK` is final.** When it is given, `(none)` included, it is the section's whole answer, whether the gate above admitted it or refused it: step 4b does not resolve a number or render a line. An admitted line goes under `## Related Issues` in the body this step creates; on an existing PR whose body has no `## Related Issues` section, step 4b's PR-host half publishes it. Only an absent `ISSUE_PR_LINK` goes to step 4b, and only when no admitted wave block exists. When `ISSUE_NUMBER` is set and no line is admitted, emit the heading with no reference and `TRACEABILITY: DEGRADED (ambiguous issue reference)`; when it is `(none)`, write no heading.
|
|
74
74
|
|
|
75
75
|
**Pasting `PR_EXCEPTIONS`.** When `PR_EXCEPTIONS` is provided (not `(none)`), append it verbatim as the body's last section — it is scrubbed with the body. Re-check its shape first: its first line must be exactly `## Evidence Exceptions`, and every line after it must match this pattern as the WHOLE line:
|
|
76
76
|
|
|
@@ -82,16 +82,12 @@ Load when the resolved tracker provider is `linear` and the operation is `ensure
|
|
|
82
82
|
|
|
83
83
|
**D11 scrub (PR body is a GitHub-visible sink):** Compose the final PR body to `$DEVFLOW_BODY_RAW` (`DEVFLOW_BODY_RAW="$(mktemp)"`); scrub via `node "$HOME/.devflow/scripts/redact-secrets.cjs" "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY"` (where `DEVFLOW_BODY="$(mktemp)"`). On success: create PR with `gh pr create … --body-file "$DEVFLOW_BODY"`. **On scrubber failure** (non-zero exit or script missing): still create the PR — PR existence is the deliverable — but with a minimal body containing only the task reference, plan path (if available), and issue link (if ISSUE_NUMBER provided), plus the literal line `TRACEABILITY: DEGRADED (redaction unavailable)`. Never post `$DEVFLOW_BODY_RAW`.
|
|
84
84
|
|
|
85
|
-
4b. (ALWAYS-ON) Ensure the PR body contains a `## Related Issues` section
|
|
86
|
-
a. Prefer the issue reference returned by `setup-task` / `ensure-traceable-issue` for this branch — it was verified at creation time.
|
|
87
|
-
b. Otherwise fall back to the branch name pattern `{type}/{REF}-{slug}`: extract the segment that, after ASCII-upper normalisation, satisfies `^[A-Z][A-Z0-9]{0,9}-[1-9][0-9]{0,8}$` and verify it with the *fetch by key* capability. If the call fails, or the issue is not open, **skip silently** — never render a link for an unverified reference. A branch name can carry a token that merely looks like one, and the existence check is the guard.
|
|
88
|
-
c. The *fetch by key* capability absent or denied ⇒ `TRACEABILITY: DEGRADED (no tracker tool for fetch by key)` and skip the section; the PR is never blocked on it.
|
|
89
|
-
|
|
90
|
-
Render the line through `## Reference Rendering`. **This provider's magic words are the SERVER's behaviour, not a capability this operation controls.** A reference rendered in a PR body may or may not transition or close the issue depending on how the workspace is configured, and on how the PR host and the tracker are connected — so the rendered text **never claims an effect**: closing is a `## Transitions` matter, and `gather-release-evidence` reports the absence of a closing-reference capability as `TRACEABILITY: DEGRADED (unsupported by linear)`. Promising an effect that may not happen is worse than rendering a plain reference that always does. `## Reference Rendering` absent, or its token discarded by the gate below ⇒ render the reference on its own line under the section heading, and record the discard under `### Substitutions`.
|
|
85
|
+
4b. (ALWAYS-ON) Ensure the PR body contains a `## Related Issues` section with the `Refs {REF}-{n}` line of this branch's issue. A given `ISSUE_PR_LINK` is already decided in step 4a: skip the resolution below. Resolution: the only source is the `PR link line` of the `### Handoff Values` block that `setup-task` or `fetch-issue` returned for this branch. Publish it only if it passes step 4a's shape check. A line that fails the check is refused as step 4a refuses it, not repaired: emit `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match any tracker reference grammar)` and publish nothing. This step composes no line itself. Never compose a line from a bare issue number or a branch name. **This provider's magic words are the SERVER's behaviour, not a capability this operation controls.** A reference rendered in a PR body may or may not transition or close the issue depending on how the workspace is configured, and on how the PR host and the tracker are connected — so the rendered text **never claims an effect**: closing is a `## Transitions` matter, and `gather-release-evidence` reports the absence of a closing-reference capability as `TRACEABILITY: DEGRADED (unsupported by linear)`. Promising an effect that may not happen is worse than rendering a plain reference that always does.
|
|
91
86
|
|
|
92
87
|
Publish the section through step 4b's PR-host half.
|
|
93
88
|
|
|
94
|
-
If no
|
|
89
|
+
If no such line exists, skip silently.
|
|
90
|
+
A failure while updating the PR body emits `TRACEABILITY: DEGRADED ({reason})` and continues — a failed Related Issues update never blocks the PR.
|
|
95
91
|
4c. Retitle, only when `APPLY_CONVENTIONS` is `true`: if the PR title breaks the convention in the PR Titles section of `.devflow/conventions.md`, retitle it; skip silently when that file is absent. Two rules, because the title derives from third-party PR titles:
|
|
96
92
|
- **Validate before use.** Skip the retitle (leave the PR title as-is, no error) if the composed title contains any of `` $ ` \ " ' ; | & < > `` or a newline.
|
|
97
93
|
- **Pass as argv, never as command text.** Bind it to a shell variable and pass that variable: `gh pr edit {PR_NUMBER} --title "$DEVFLOW_PR_TITLE"`. Never interpolate it: `$(...)`, backticks and `${...}` all expand inside double quotes.
|
|
@@ -100,10 +96,6 @@ Load when the resolved tracker provider is `linear` and the operation is `ensure
|
|
|
100
96
|
5. Get base branch from PR
|
|
101
97
|
6. Derive branch-slug (replace `/` with `-`)
|
|
102
98
|
|
|
103
|
-
**The read-site shape gate for `## Reference Rendering`.** The token arrives from the tracker configuration file, which is hand-editable and machine-wide, so it is parsed HERE — at the sink that renders it, and never on the writer's word. Require `^[A-Za-z0-9 #{}/_.-]{1,60}$`, anchored at both ends, and **discard** any token carrying a backtick, a `$`, a `"`, a `\`, a `;` or a newline. The anchored shape is the gate; the metachar denylist is a second, independent control, named separately so widening the shape for a new token form cannot silently relax it. **Discard, never repair** — a repaired token is one nobody can predict — and a discarded token falls back to **the resolved provider's** documented default, stated once in that provider's own mechanics, with a `### Substitutions` row recording what was dropped. An absent `## Reference Rendering` section, an absent file and a discarded token are the SAME outcome: the documented default. This gate never yields `# UNRESOLVED:`.
|
|
104
|
-
|
|
105
|
-
**This provider's documented default is `Refs {REF}-{n}`.** It is stated here because it is a provider fact, and the gate above is what routes to it: a section that is absent, a file that is absent and a token the gate discarded all render this, and none of them is a degradation.
|
|
106
|
-
|
|
107
99
|
### Step 4b's PR-host half
|
|
108
100
|
|
|
109
101
|
Step 4b publishes its section only through this: find the open PR with `gh pr list --head {branch} --state open --limit 1`; compose the existing body plus the `## Related Issues` section to `$DEVFLOW_BODY_RAW` — the existing body is third-party-editable, so never interpolate it into a command string; apply the Comment-sink scrub (D11); on success: `gh pr edit {PR_NUMBER} --body-file "$DEVFLOW_BODY"`.
|