devflow-kit 2.5.0 → 3.0.1
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 +82 -0
- package/README.md +44 -19
- package/dist/agents/git.md +13 -15
- package/dist/cli/commands/ambient.js +160 -145
- package/dist/cli/commands/capture.js +29 -55
- package/dist/cli/commands/compliance.js +32 -61
- package/dist/cli/commands/context.js +17 -32
- package/dist/cli/commands/debug.js +65 -26
- package/dist/cli/commands/flags.js +3 -3
- package/dist/cli/commands/hud.js +34 -10
- package/dist/cli/commands/init-seed.js +40 -4
- package/dist/cli/commands/init.js +249 -271
- package/dist/cli/commands/install-report.js +10 -15
- package/dist/cli/commands/knowledge/index.js +1 -1
- package/dist/cli/commands/knowledge/toggle.js +11 -3
- package/dist/cli/commands/learning.js +52 -37
- package/dist/cli/commands/legacy-hooks.js +11 -14
- package/dist/cli/commands/memory.js +67 -78
- package/dist/cli/commands/proxy.js +23 -41
- package/dist/cli/commands/security.js +5 -13
- package/dist/cli/commands/skills.js +21 -3
- package/dist/cli/commands/tracker.js +100 -228
- package/dist/cli/commands/uninstall.js +343 -138
- package/dist/commands/bug-analysis.md +38 -12
- package/dist/commands/code-review.md +70 -21
- package/dist/commands/debug.md +37 -7
- package/dist/commands/dynamic-build.md +66 -17
- package/dist/commands/dynamic-plan.md +19 -8
- package/dist/commands/dynamic-profile.md +24 -10
- package/dist/commands/dynamic-tickets.md +22 -11
- package/dist/commands/explore.md +37 -7
- package/dist/commands/implement.md +96 -32
- package/dist/commands/plan.md +62 -19
- package/dist/commands/release.md +2 -2
- package/dist/commands/research.md +34 -8
- package/dist/commands/resolve.md +65 -17
- package/dist/commands/self-review.md +45 -9
- package/dist/core/compliance-compose.js +27 -27
- package/dist/core/evidence-policy.js +240 -24
- package/dist/core/feature-config.js +94 -25
- package/dist/core/feature-switch.js +1 -1
- package/dist/core/flags.js +30 -2
- package/dist/core/fs-atomic.js +27 -0
- package/dist/core/hook-log-dirs.js +104 -0
- package/dist/core/learning-tuning-config.js +5 -3
- package/dist/core/ledger-root.js +102 -0
- package/dist/core/manifest.js +6 -4
- package/dist/core/mds-variants.js +34 -97
- package/dist/core/migrations.js +49 -23
- package/dist/core/plugins.js +5 -4
- package/dist/core/project-paths.js +0 -17
- package/dist/core/same-location.js +25 -0
- package/dist/core/tracker.js +226 -139
- package/dist/hud/components/config-counts.js +15 -4
- package/dist/hud/components/learning-counts.js +14 -0
- package/dist/hud/config.js +2 -1
- package/dist/hud/cost-history.js +2 -4
- package/dist/hud/git.js +52 -7
- package/dist/hud/index.js +7 -9
- package/dist/skills/git/references/pr/check-merge-readiness.md +1 -1
- package/dist/skills/git/references/pr/ensure-pr-ready.md +1 -1
- package/dist/skills/git/references/pr/update-pr-evidence.md +1 -1
- package/dist/skills/git/references/tracker/_mcp.md +1 -1
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/github/manage-debt.md +3 -3
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/jira/manage-debt.md +1 -1
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +1 -1
- package/dist/skills/git/references/tracker/jira/setup-task.md +1 -1
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/linear/manage-debt.md +1 -1
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +1 -1
- package/dist/skills/git/references/tracker/linear/setup-task.md +1 -1
- package/dist/targets/claude-code/claude-paths.js +59 -57
- package/dist/targets/claude-code/compliance-install.js +49 -65
- package/dist/targets/claude-code/hooks.js +108 -3
- package/dist/targets/claude-code/installer.js +30 -57
- package/dist/targets/claude-code/post-install.js +246 -139
- package/dist/targets/claude-code/tracker-install.js +38 -65
- package/package.json +5 -4
- package/src/assets/agents/code.md +4 -3
- package/src/assets/agents/design.md +1 -0
- package/src/assets/agents/git.mds +55 -57
- package/src/assets/agents/knowledge.md +2 -2
- package/src/assets/agents/review.md +3 -1
- package/src/assets/agents/tracker.md +37 -30
- package/src/assets/commands/_partials/_compliance.mds +19 -1
- package/src/assets/commands/_partials/_decisions.mds +15 -3
- package/src/assets/commands/_partials/_docs_root.mds +35 -0
- package/src/assets/commands/_partials/_engine.mds +2 -2
- package/src/assets/commands/_partials/_evidence_policy.mds +3 -3
- package/src/assets/commands/_partials/_factory.mds +1 -1
- package/src/assets/commands/_partials/_knowledge.mds +27 -9
- package/src/assets/commands/_partials/_plan_contract.mds +2 -2
- package/src/assets/commands/_partials/_preamble.mds +1 -1
- package/src/assets/commands/_partials/_publication.mds +6 -2
- package/src/assets/commands/_partials/_settings.mds +28 -0
- package/src/assets/commands/_partials/_ticket_template.mds +3 -3
- package/src/assets/commands/_partials/_tracker.mds +4 -4
- package/src/assets/commands/_partials/_wave.mds +4 -4
- package/src/assets/commands/bug-analysis.mds +19 -17
- package/src/assets/commands/code-review.mds +39 -33
- package/src/assets/commands/debug.mds +4 -5
- package/src/assets/commands/dynamic-build.mds +75 -53
- package/src/assets/commands/dynamic-plan.mds +20 -15
- package/src/assets/commands/dynamic-profile.mds +24 -11
- package/src/assets/commands/dynamic-tickets.mds +25 -20
- package/src/assets/commands/explore.mds +4 -5
- package/src/assets/commands/implement.mds +58 -45
- package/src/assets/commands/plan.mds +34 -29
- package/src/assets/commands/release.md +2 -2
- package/src/assets/commands/research.mds +11 -9
- package/src/assets/commands/resolve.mds +41 -39
- package/src/assets/commands/self-review.mds +24 -25
- package/src/assets/mds/git/_pr.mds +61 -61
- package/src/assets/mds/git/_references.mds +19 -19
- package/src/assets/mds/tracker/_common.mds +8 -8
- package/src/assets/mds/tracker/_github.mds +71 -71
- package/src/assets/mds/tracker/_jira.mds +74 -74
- package/src/assets/mds/tracker/_linear.mds +75 -75
- package/src/assets/mds/tracker/_mcp.mds +23 -17
- package/src/assets/scripts/hooks/background-memory-update +35 -19
- package/src/assets/scripts/hooks/capture-prompt +18 -12
- package/src/assets/scripts/hooks/capture-question +18 -12
- package/src/assets/scripts/hooks/capture-turn +27 -17
- package/src/assets/scripts/hooks/debug-trace +11 -6
- package/src/assets/scripts/hooks/ensure-devflow-init +33 -6
- package/src/assets/scripts/hooks/ensure-proxy +9 -8
- package/src/assets/scripts/hooks/ensure-root-gitignore +111 -36
- package/src/assets/scripts/hooks/git-marker +48 -0
- package/src/assets/scripts/hooks/json-helper.cjs +6 -1
- package/src/assets/scripts/hooks/lib/project-paths.cjs +0 -19
- package/src/assets/scripts/hooks/log-paths +80 -0
- package/src/assets/scripts/hooks/memory-worker +17 -15
- package/src/assets/scripts/hooks/pre-compact-memory +41 -16
- package/src/assets/scripts/hooks/queue-append +104 -30
- package/src/assets/scripts/hooks/resolve-project-root +101 -7
- package/src/assets/scripts/hooks/session-start-context +289 -122
- package/src/assets/scripts/hooks/session-start-memory +35 -16
- package/src/assets/scripts/lib/project-config.cjs +633 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +300 -220
- package/src/assets/scripts/resolve-settings.cjs +1054 -0
- package/src/assets/scripts/verify-evidence.cjs +1 -1
- package/src/assets/skills/compliance/SKILL.md +2 -2
- package/src/assets/skills/docs-framework/SKILL.md +6 -7
- package/src/assets/skills/docs-framework/references/patterns.md +10 -17
- package/src/assets/skills/gap-analysis/SKILL.md +2 -2
- package/src/assets/skills/git/references/github-api.md +9 -9
- package/src/assets/skills/git/references/patterns.md +1 -1
- package/src/assets/skills/worktree-support/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +29 -0
- package/src/targets/claude-code/templates/managed-settings.json +25 -9
|
@@ -4,7 +4,6 @@ output-dir: dist/commands
|
|
|
4
4
|
---
|
|
5
5
|
@import { knowledge_load, knowledge_writeback } from "./_partials/_knowledge.mds"
|
|
6
6
|
@import { decisions_load } from "./_partials/_decisions.mds"
|
|
7
|
-
|
|
8
7
|
# Self-Review Command
|
|
9
8
|
|
|
10
9
|
Run Simplify agent and Scrutinize agent sequentially on changed files for post-implementation quality refinement.
|
|
@@ -26,11 +25,11 @@ Detect changed files and build context:
|
|
|
26
25
|
2. Else run `git diff --name-only HEAD` + `git diff --name-only --cached` to get staged + unstaged
|
|
27
26
|
3. If no changes found, report "No changes to review" and exit
|
|
28
27
|
4. Build TASK_DESCRIPTION from recent commit messages or branch name
|
|
29
|
-
{decisions_load()}
|
|
28
|
+
{{decisions_load()}}
|
|
30
29
|
|
|
31
30
|
Pass `DECISIONS_CONTEXT` to Scrutinize agent — the compact index lists active ADR/PF entries; Scrutinize agent uses `devflow:apply-decisions` to Read full entry bodies on demand. Known pitfalls help identify reintroduced issues, prior decisions help validate architectural consistency. (Simplify agent does not consume decisions — it operates at code-shape level and Scrutinize agent runs after to catch any architectural drift.)
|
|
32
31
|
|
|
33
|
-
{knowledge_load()}
|
|
32
|
+
{{knowledge_load()}}
|
|
34
33
|
|
|
35
34
|
Pass `FEATURE_KNOWLEDGE` to Scrutinize agent.
|
|
36
35
|
|
|
@@ -44,8 +43,8 @@ Pass `FEATURE_KNOWLEDGE` to Scrutinize agent.
|
|
|
44
43
|
Spawn Simplify agent to refine code for clarity and consistency:
|
|
45
44
|
|
|
46
45
|
Agent(subagent_type="Simplify", run_in_background=false):
|
|
47
|
-
"TASK_DESCRIPTION:
|
|
48
|
-
FILES_CHANGED:
|
|
46
|
+
"TASK_DESCRIPTION: {task_description}
|
|
47
|
+
FILES_CHANGED: {files_changed}
|
|
49
48
|
Simplify and refine the code for clarity and consistency while preserving functionality."
|
|
50
49
|
|
|
51
50
|
**Wait for completion.** Simplify agent commits changes directly.
|
|
@@ -58,10 +57,10 @@ Simplify and refine the code for clarity and consistency while preserving functi
|
|
|
58
57
|
Spawn Scrutinize agent for quality evaluation and fixing:
|
|
59
58
|
|
|
60
59
|
Agent(subagent_type="Scrutinize", run_in_background=false):
|
|
61
|
-
"TASK_DESCRIPTION:
|
|
62
|
-
FILES_CHANGED:
|
|
63
|
-
DECISIONS_CONTEXT:
|
|
64
|
-
FEATURE_KNOWLEDGE:
|
|
60
|
+
"TASK_DESCRIPTION: {task_description}
|
|
61
|
+
FILES_CHANGED: {files_changed}
|
|
62
|
+
DECISIONS_CONTEXT: {decisions_context}
|
|
63
|
+
FEATURE_KNOWLEDGE: {feature_knowledge}
|
|
65
64
|
Evaluate against 9-pillar framework. Fix P0/P1 issues. Return structured report.
|
|
66
65
|
Follow devflow:apply-decisions to scan DECISIONS_CONTEXT and Read full ADR/PF bodies on demand. Skip if (none).
|
|
67
66
|
Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE. Skip if (none)."
|
|
@@ -76,7 +75,7 @@ Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE. Skip if (none)."
|
|
|
76
75
|
If Scrutinize agent made changes (STATUS == FIXED):
|
|
77
76
|
|
|
78
77
|
Agent(subagent_type="Validate", run_in_background=false):
|
|
79
|
-
"FILES_CHANGED:
|
|
78
|
+
"FILES_CHANGED: {scrutinize_modified_files}
|
|
80
79
|
VALIDATION_SCOPE: changed-only
|
|
81
80
|
Run build, typecheck, lint, test on modified files"
|
|
82
81
|
|
|
@@ -91,31 +90,31 @@ Display summary:
|
|
|
91
90
|
|
|
92
91
|
## Self-Review Complete
|
|
93
92
|
|
|
94
|
-
**Files Reviewed**:
|
|
95
|
-
**Status**:
|
|
93
|
+
**Files Reviewed**: {n}
|
|
94
|
+
**Status**: {PASS|FIXED|BLOCKED}
|
|
96
95
|
|
|
97
96
|
### Simplify agent
|
|
98
|
-
-
|
|
97
|
+
- {n} files refined for clarity
|
|
99
98
|
|
|
100
99
|
### Scrutinize agent (9-Pillar Evaluation)
|
|
101
100
|
| Pillar | Status |
|
|
102
101
|
|--------|--------|
|
|
103
|
-
| Design |
|
|
104
|
-
| Functionality |
|
|
105
|
-
| Security |
|
|
106
|
-
| Complexity |
|
|
107
|
-
| Error Handling |
|
|
108
|
-
| Tests |
|
|
109
|
-
| Naming |
|
|
110
|
-
| Consistency |
|
|
111
|
-
| Documentation |
|
|
102
|
+
| Design | {status} |
|
|
103
|
+
| Functionality | {status} |
|
|
104
|
+
| Security | {status} |
|
|
105
|
+
| Complexity | {status} |
|
|
106
|
+
| Error Handling | {status} |
|
|
107
|
+
| Tests | {status} |
|
|
108
|
+
| Naming | {status} |
|
|
109
|
+
| Consistency | {status} |
|
|
110
|
+
| Documentation | {status} |
|
|
112
111
|
|
|
113
112
|
### Commits Created
|
|
114
|
-
-
|
|
113
|
+
- {sha} {message}
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
{If BLOCKED: ### Blocking Issue\n{description}}
|
|
117
116
|
|
|
118
|
-
{knowledge_writeback()}
|
|
117
|
+
{{knowledge_writeback()}}
|
|
119
118
|
|
|
120
119
|
## Architecture
|
|
121
120
|
|
|
@@ -10,7 +10,7 @@ sub-directory come from `PR_HOST_OPS` / `PR_HOST_DESTINATION_ROOT` in
|
|
|
10
10
|
build fails. Everything above the first section marker is module-level prose and
|
|
11
11
|
is emitted nowhere.
|
|
12
12
|
|
|
13
|
-
Why `pr/` is not `tracker
|
|
13
|
+
Why `pr/` is not `tracker/{provider}/`: pull requests, PR reviews and PR checks
|
|
14
14
|
stay on GitHub under every issue-tracker provider, so these steps are the SAME
|
|
15
15
|
file whatever `TRACKER_PROVIDER` resolves to. A copy per provider would be one
|
|
16
16
|
identical tree per registered provider; a row under `tracker/github/` would make
|
|
@@ -52,18 +52,18 @@ Load for `ensure-pr-ready` under every tracker provider.
|
|
|
52
52
|
2. Check for uncommitted changes - if any, create atomic commit using `devflow:git` patterns
|
|
53
53
|
3. Check if branch pushed to remote - if not, push with `-u` flag. If that push is refused and the branch's open PR is cross-repository with maintainer edits off (`gh pr view --json isCrossRepository,maintainerCanModify`), emit `TRACEABILITY: DEGRADED (cannot push to fork)` and go to 4a (4a–4c edit only the PR).
|
|
54
54
|
4a. Check if PR exists - if not, create PR using guidance from (in priority order): (a) `PR_DESCRIPTION_GUIDANCE` if given and not `(none)`, (b) generated from branch context. Compose the PR body via the `devflow:git` template to `$DEVFLOW_BODY_RAW` (a D11 sink: it publishes at repo visibility), then append the caller blocks. Apply the Comment-sink scrub (D11) — a failed one posts neither block; on success: `gh pr create … --body-file "$DEVFLOW_BODY"`.
|
|
55
|
-
- **Caller blocks**, in order: `PR_WAVE_BLOCK` (`check wave`), then `PR_TEST_PLAN_BLOCK` (`check block`), each when given and not `(none)`. Write it byte for byte to a fresh `mktemp` file with the Write tool, never via a shell string, and run `node "
|
|
55
|
+
- **Caller blocks**, in order: `PR_WAVE_BLOCK` (`check wave`), then `PR_TEST_PLAN_BLOCK` (`check block`), each when given and not `(none)`. Write it byte for byte to a fresh `mktemp` file with the Write tool, never via a shell string, and run `node "$HOME/.devflow/scripts/verify-evidence.cjs" check <wave|block> <file>; echo "exit=$?"`. Only `exit=0` admits it, verbatim; else omit it, never repaired or partly pasted, and emit `TRACEABILITY: DEGRADED (wave block does not match its grammar)` or `TRACEABILITY: DEGRADED (test-plan block does not match its grammar)` with steps 4b/4c's lines. An admitted wave block is the body's only `## Related Issues`: skip 4b.
|
|
56
56
|
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:
|
|
57
57
|
- **Validate before use.** Skip the retitle (leave the PR title as-is, no error) if the composed title contains any of `` $ ` \ " ' ; | & < > `` or a newline.
|
|
58
|
-
- **Pass as argv, never as command text.** Bind it to a shell variable and pass that variable: `gh pr edit
|
|
58
|
+
- **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.
|
|
59
59
|
|
|
60
|
-
On any 4xx/5xx from `gh pr edit`: emit `TRACEABILITY: DEGRADED (
|
|
60
|
+
On any 4xx/5xx from `gh pr edit`: emit `TRACEABILITY: DEGRADED ({reason})` and continue — a failed retitle never blocks the PR.
|
|
61
61
|
5. Get base branch from PR
|
|
62
62
|
6. Derive branch-slug (replace `/` with `-`)
|
|
63
63
|
|
|
64
64
|
### Step 4b's PR-host half
|
|
65
65
|
|
|
66
|
-
The provider reference's step 4b publishes its section only through this: find the open PR with `gh pr list --head
|
|
66
|
+
The provider reference's 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"`.
|
|
67
67
|
@end
|
|
68
68
|
|
|
69
69
|
@define validate_branch():
|
|
@@ -79,12 +79,12 @@ Load for `validate-branch` under every tracker provider.
|
|
|
79
79
|
2. Verify working directory is clean - error if uncommitted changes
|
|
80
80
|
3. Get current branch name
|
|
81
81
|
4. Derive branch-slug (replace `/` with `-`)
|
|
82
|
-
5. Check if reviews exist at
|
|
82
|
+
5. Check if reviews exist at `{WORKTREE_PATH}/.devflow/docs/reviews/{branch-slug}/` (or `.devflow/docs/reviews/{branch-slug}/` if no WORKTREE_PATH)
|
|
83
83
|
6. Determine base branch and fetch PR details if available:
|
|
84
|
-
- If a PR exists — the PR# context, else the current branch's PR: fetch PR details via `gh pr view
|
|
85
|
-
- If no PR exists: resolve the default remote branch via `git -C
|
|
84
|
+
- If a PR exists — the PR# context, else the current branch's PR: fetch PR details via `gh pr view {number} --json baseRefName,isCrossRepository,maintainerCanModify,number,headRepositoryOwner,headRepository` (omit `{number}` for the current branch; `gh` has no `-C` flag, so run this call from `WORKTREE_PATH` (else cwd) — never the orchestrator's own cwd, or a multi-worktree run discovers the wrong PR); use `baseRefName` as `base_branch` and `number` as the PR. If `isCrossRepository` is true, `maintainerCanModify` is false and you cannot push to that fork yourself (`gh api "repos/{headRepositoryOwner.login}/{headRepository.name}" --jq '.permissions.push'` does not print `true`), emit `TRACEABILITY: DEGRADED (cannot push to fork)`: the caller skips pushes, while PR comments and body edits still run.
|
|
85
|
+
- If no PR exists: resolve the default remote branch via `git -C {worktree} rev-parse --abbrev-ref origin/HEAD 2>/dev/null | sed 's|origin/||'`; if that fails, probe common defaults (`main`, then `master`) via `git -C {worktree} rev-parse --verify {default} 2>/dev/null`
|
|
86
86
|
- If `base_branch` still cannot be determined: emit an intentional empty `### Diff Scope` block (so `DIFF_FILES=""` is a deliberate conservative degrade, not a silent error); skip step 7
|
|
87
|
-
7. Compute diff scope (only if `base_branch` was resolved): `git -C
|
|
87
|
+
7. Compute diff scope (only if `base_branch` was resolved): `git -C {worktree} diff {base_branch}...HEAD --name-only` → newline-separated file list
|
|
88
88
|
@end
|
|
89
89
|
|
|
90
90
|
@define post_review_summary():
|
|
@@ -98,37 +98,37 @@ Load for `post-review-summary` under every tracker provider.
|
|
|
98
98
|
|
|
99
99
|
1. Check for existing comment with this run's marker (author-filtered — a third party posting the marker string must not suppress devflow's comment):
|
|
100
100
|
- Fetch viewer login: `gh api user --jq '.login'` → store as VIEWER_LOGIN
|
|
101
|
-
- `gh pr view
|
|
102
|
-
- Search for `<!-- devflow:review-summary cycle
|
|
103
|
-
- If found: skip — report `Skipped: already posted for cycle
|
|
101
|
+
- `gh pr view {PR_NUMBER} --json comments --jq '[.comments[] | select(.author.login == "'"$VIEWER_LOGIN"'")] | .[].body'`
|
|
102
|
+
- Search for `<!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP}` in the viewer-authored comment bodies only (full pair match)
|
|
103
|
+
- If found: skip — report `Skipped: already posted for cycle {CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP}`
|
|
104
104
|
2. Load `references/publication-gate.md` and resolve `REVIEW_PUBLICATION` by its step 2; `off` ends the op without posting.
|
|
105
105
|
3. Probe (if mode not yet determined): `gh repo view --json visibility --jq '.visibility'`, case-insensitive. `PRIVATE`/`INTERNAL` → FULL; `PUBLIC` → `STUB (public repository)`; empty output, an error or any other value → `STUB (visibility undeterminable)`. **Fail-closed: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
|
|
106
106
|
4. Read `REVIEW_SUMMARY_PATH` — repo-relative; read it under `WORKTREE_PATH` (else cwd).
|
|
107
107
|
5. Compose body:
|
|
108
108
|
- **FULL mode:**
|
|
109
109
|
```
|
|
110
|
-
<!-- devflow:review-summary cycle
|
|
111
|
-
## Code Review — Cycle
|
|
110
|
+
<!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP} -->
|
|
111
|
+
## Code Review — Cycle {CYCLE_NUMBER}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
{full content of review-summary.md}
|
|
114
114
|
|
|
115
115
|
---
|
|
116
|
-
*Posted by [devflow](https://github.com/dean0x/devflow) · cycle
|
|
116
|
+
*Posted by [devflow](https://github.com/dean0x/devflow) · cycle {CYCLE_NUMBER}*
|
|
117
117
|
```
|
|
118
118
|
- **STUB mode** (excluded: finding titles, file:line references, Blocking/Escalations/Third-Party/Verification sections, merge recommendation):
|
|
119
119
|
```
|
|
120
|
-
<!-- devflow:review-summary cycle
|
|
121
|
-
## Code Review — Cycle
|
|
120
|
+
<!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP} -->
|
|
121
|
+
## Code Review — Cycle {CYCLE_NUMBER}
|
|
122
122
|
|
|
123
123
|
Full summary withheld (public repository).
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
{counts-by-severity table verbatim from local artifact; if unparseable: "Counts unavailable — see the local artifact."}
|
|
126
126
|
|
|
127
|
-
Full report:
|
|
128
|
-
*Posted by [devflow](https://github.com/dean0x/devflow) · cycle
|
|
127
|
+
Full report: {REVIEW_SUMMARY_PATH} (not committed; ask the author)
|
|
128
|
+
*Posted by [devflow](https://github.com/dean0x/devflow) · cycle {CYCLE_NUMBER}*
|
|
129
129
|
```
|
|
130
|
-
Cap body at 60000 characters (GitHub rejects over 65536 with a 422, which the 4xx rule would silently skip). Truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact
|
|
131
|
-
6. Write body to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit or missing script → DO NOT POST. Re-check the 60000-char cap on the scrubbed body (redaction may grow it; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence; if truncation fires here: emit `NOTE: body exceeded 60k after redaction — truncated/stub posted` in op output and prepend that notice to the body). Post: `gh pr comment
|
|
130
|
+
Cap body at 60000 characters (GitHub rejects over 65536 with a 422, which the 4xx rule would silently skip). Truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact {REVIEW_SUMMARY_PATH} (not committed; ask the author)`.
|
|
131
|
+
6. Write body to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit or missing script → DO NOT POST. Re-check the 60000-char cap on the scrubbed body (redaction may grow it; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence; if truncation fires here: emit `NOTE: body exceeded 60k after redaction — truncated/stub posted` in op output and prepend that notice to the body). Post: `gh pr comment {PR_NUMBER} --body-file "$DEVFLOW_BODY"`.
|
|
132
132
|
7. On 5xx: retry once. If still 5xx: `TRACEABILITY: DEGRADED (5xx on post-review-summary)`, warn, return.
|
|
133
133
|
@end
|
|
134
134
|
|
|
@@ -143,7 +143,7 @@ Load for `check-ci-status` under every tracker provider.
|
|
|
143
143
|
|
|
144
144
|
1. If `PR_NUMBER` not provided, discover it: `gh pr view --json number --jq '.number' 2>/dev/null`
|
|
145
145
|
2. If no PR found → output status `NO_PR`, stop
|
|
146
|
-
3. Fetch checks: `gh pr checks
|
|
146
|
+
3. Fetch checks: `gh pr checks {number} --json name,state,bucket; echo "exit=$?"` — exit 0, or 8 (checks pending), is a result; any other exit is a failure
|
|
147
147
|
4. If the result is `[]`, or the failure says `no checks reported` → output status `NO_CI`; any other failure → output status `INDETERMINATE`
|
|
148
148
|
5. Classify by `bucket` in priority order: any `pending` → `PENDING`; else any `fail` or `cancel` → `FAILING`; else every check `pass` or `skipping` with at least one `pass` → `PASSING`; else → `INDETERMINATE`
|
|
149
149
|
6. List failing/pending checks with names
|
|
@@ -166,7 +166,7 @@ Load for `fetch-review-threads` under every tracker provider.
|
|
|
166
166
|
- (PRIMARY) First comment body contains `<!-- devflow:` marker, OR
|
|
167
167
|
- (SECONDARY) VIEWER_LOGIN matches thread author login AND first comment body does not appear to be a code-style review comment
|
|
168
168
|
4. For each remaining external unresolved thread, create an `ext-*` record:
|
|
169
|
-
- `id`: `ext
|
|
169
|
+
- `id`: `ext-{sequential-number}` (e.g., `ext-1`, `ext-2`, ...)
|
|
170
170
|
- `thread_id`: the GraphQL thread `id` (for reply/resolve mutations)
|
|
171
171
|
- `file`: `path` field
|
|
172
172
|
- `line`: `line` field
|
|
@@ -195,15 +195,15 @@ Each `THREAD_MAP` entry carries one verdict:
|
|
|
195
195
|
|
|
196
196
|
Rate limits: this op fans out, so read the remaining-budget rungs in `references/github-api.md` before the first iteration.
|
|
197
197
|
|
|
198
|
-
For each `ext
|
|
198
|
+
For each `ext-{N}` in THREAD_MAP (sequentially, ≤50, 1s between operations). `fetch-review-threads`
|
|
199
199
|
returns up to 100 threads, so a busy PR can exceed this bound: process the first 50 in THREAD_MAP
|
|
200
|
-
order and report the remainder as `TRUNCATED (
|
|
200
|
+
order and report the remainder as `TRUNCATED ({n} threads beyond the ≤50 bound)` — never report
|
|
201
201
|
`COMPLETE` while threads went untouched, since `check-merge-readiness` will otherwise show them as
|
|
202
202
|
unexplained unresolved threads.
|
|
203
203
|
1. Compose reply based on verdict:
|
|
204
|
-
- **FIXED**: `This has been addressed in commit [
|
|
205
|
-
- **FALSE_POSITIVE**: `After investigation, this appears to be a false positive:
|
|
206
|
-
- **BY_DESIGN**: `This is intentional:
|
|
204
|
+
- **FIXED**: `This has been addressed in commit [{sha}](https://github.com/{owner}/{repo}/pull/{PR_NUMBER}/commits/{commit_sha}). Note: line references may shift on rebase. Resolved automatically by devflow (verification: PASS, commit {sha}).`
|
|
205
|
+
- **FALSE_POSITIVE**: `After investigation, this appears to be a false positive: {evidence}. No code change needed.`
|
|
206
|
+
- **BY_DESIGN**: `This is intentional: {evidence}. No code change needed.`
|
|
207
207
|
- **ESCALATED**: `This thread has been escalated for human review and recorded in the resolution summary.`
|
|
208
208
|
- Reply bodies MUST NOT contain verbatim content from the external thread body — cite only internal evidence (commit SHAs, file:line from this codebase, ADR IDs)
|
|
209
209
|
2. Write reply to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit → DEGRADED for that thread, continue per D4. Post reply via `addPullRequestReviewThreadReply` GraphQL mutation with `-F body=@"$DEVFLOW_BODY"` (file-ref form).
|
|
@@ -223,35 +223,35 @@ Load for `post-resolution-summary` under every tracker provider.
|
|
|
223
223
|
|
|
224
224
|
1. Check for existing marker (author-filtered — a third party posting the marker string must not suppress devflow's comment):
|
|
225
225
|
- Fetch viewer login: `gh api user --jq '.login'` → store as VIEWER_LOGIN
|
|
226
|
-
- `gh pr view
|
|
227
|
-
- Search the viewer-authored bodies only for the exact key `<!-- devflow:resolution-summary ts
|
|
228
|
-
- If found: skip — report `Skipped: already posted for ts
|
|
226
|
+
- `gh pr view {PR_NUMBER} --json comments --jq '[.comments[] | select(.author.login == "'"$VIEWER_LOGIN"'")] | .[].body'`
|
|
227
|
+
- Search the viewer-authored bodies only for the exact key `<!-- devflow:resolution-summary ts:{RESOLUTION_TS} -->`
|
|
228
|
+
- If found: skip — report `Skipped: already posted for ts:{RESOLUTION_TS}`
|
|
229
229
|
2. Load `references/publication-gate.md` and resolve `REVIEW_PUBLICATION` by its step 2; `off` ends the op without posting.
|
|
230
230
|
3. Probe (if mode not yet determined): `gh repo view --json visibility --jq '.visibility'`, case-insensitive. `PRIVATE`/`INTERNAL` → FULL; `PUBLIC` → `STUB (public repository)`; empty output, an error or any other value → `STUB (visibility undeterminable)`. **Fail-closed: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
|
|
231
231
|
4. Read `RESOLUTION_SUMMARY_PATH` — repo-relative; read it under `WORKTREE_PATH` (else cwd).
|
|
232
|
-
5. Compose body (where
|
|
232
|
+
5. Compose body (where `{TS}` = `RESOLUTION_TS`):
|
|
233
233
|
- **FULL mode:**
|
|
234
234
|
```
|
|
235
|
-
<!-- devflow:resolution-summary ts
|
|
236
|
-
|
|
235
|
+
<!-- devflow:resolution-summary ts:{TS} -->
|
|
236
|
+
{full content of resolution-summary.md}
|
|
237
237
|
|
|
238
238
|
---
|
|
239
239
|
*Posted by [devflow](https://github.com/dean0x/devflow)*
|
|
240
240
|
```
|
|
241
241
|
- **STUB mode** (excluded: finding titles, file:line references, Blocking/Escalations/Third-Party/Verification sections):
|
|
242
242
|
```
|
|
243
|
-
<!-- devflow:resolution-summary ts
|
|
243
|
+
<!-- devflow:resolution-summary ts:{TS} -->
|
|
244
244
|
## Resolution Summary
|
|
245
245
|
|
|
246
246
|
Full summary withheld (public repository).
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
{counts-by-severity table verbatim from local artifact; if unparseable: "Counts unavailable — see the local artifact."}
|
|
249
249
|
|
|
250
|
-
Full report:
|
|
250
|
+
Full report: {RESOLUTION_SUMMARY_PATH} (not committed; ask the author)
|
|
251
251
|
*Posted by [devflow](https://github.com/dean0x/devflow)*
|
|
252
252
|
```
|
|
253
|
-
Cap body at 60000 characters (GitHub rejects over 65536 with a 422, which the 4xx rule would silently skip); truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact
|
|
254
|
-
6. Write body to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit or missing script → DO NOT POST. Re-check the 60000-char cap on the scrubbed body (redaction may grow it; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence; if truncation fires here: emit `NOTE: body exceeded 60k after redaction — truncated/stub posted` in op output and prepend that notice to the body). Post: `gh pr comment
|
|
253
|
+
Cap body at 60000 characters (GitHub rejects over 65536 with a 422, which the 4xx rule would silently skip); truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact {RESOLUTION_SUMMARY_PATH} (not committed; ask the author)`.
|
|
254
|
+
6. Write body to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit or missing script → DO NOT POST. Re-check the 60000-char cap on the scrubbed body (redaction may grow it; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence; if truncation fires here: emit `NOTE: body exceeded 60k after redaction — truncated/stub posted` in op output and prepend that notice to the body). Post: `gh pr comment {PR_NUMBER} --body-file "$DEVFLOW_BODY"`.
|
|
255
255
|
7. On 5xx: retry once. If still 5xx: `TRACEABILITY: DEGRADED (5xx on post-resolution-summary)`, warn, return.
|
|
256
256
|
@end
|
|
257
257
|
|
|
@@ -264,22 +264,22 @@ Load for `check-merge-readiness` under every tracker provider.
|
|
|
264
264
|
|
|
265
265
|
### Process
|
|
266
266
|
|
|
267
|
-
1. Fetch unresolved review threads via GraphQL: `reviewThreads(first: 100)
|
|
268
|
-
2. Fetch PR review decision: `gh pr view
|
|
267
|
+
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)`.
|
|
268
|
+
2. Fetch PR review decision: `gh pr view {PR_NUMBER} --json reviewDecision --jq '.reviewDecision'`
|
|
269
269
|
- Values: `APPROVED`, `CHANGES_REQUESTED`, `REVIEW_REQUIRED`, or null
|
|
270
270
|
3. Fetch CI status (same logic as `check-ci-status`)
|
|
271
271
|
Those steps are in `references/pr/check-ci-status.md` — load it and apply them to this `PR_NUMBER`, every arm unchanged.
|
|
272
|
-
4. Read the test-plan evidence at the current head, from `WORKTREE_PATH` (else cwd): `node "
|
|
272
|
+
4. Read the test-plan evidence at the current head, from `WORKTREE_PATH` (else cwd): `node "$HOME/.devflow/scripts/verify-evidence.cjs" verify --pr {PR_NUMBER} --approval; echo "exit=$?"`. The evidence is *known* only on `exit=0` with stdout exactly one `EVIDENCE pr:{PR_NUMBER} …` line; otherwise it is *unknown*. From it read `total`, `VERIFIED-CI`, `ATTESTED-LOCAL` (report the two apart), `exceptions` and `approval`; *verified* = `VERIFIED-CI` + `ATTESTED-LOCAL`, never inferred from an absent field. The script re-derives every state at the head and decides `approval` by the trust rule; it prints nothing it read from the PR.
|
|
273
273
|
5. Classify (first matching rule wins):
|
|
274
|
-
- `NOT_READY (unresolved threads:
|
|
274
|
+
- `NOT_READY (unresolved threads: {n})` — unresolved_threads > 0
|
|
275
275
|
- `NOT_READY (changes requested)` — reviewDecision == `CHANGES_REQUESTED`
|
|
276
|
-
- `NOT_READY (CI failing:
|
|
276
|
+
- `NOT_READY (CI failing: {checks})` — ci_status == `FAILING`
|
|
277
277
|
- `NOT_READY (CI pending)` — ci_status == `PENDING` (expected after a push; non-alarming)
|
|
278
278
|
- `NOT_READY (no approving review)` — reviewDecision == `REVIEW_REQUIRED` or null
|
|
279
279
|
- `NOT_READY (test-plan evidence unavailable)` — the evidence is unknown
|
|
280
280
|
- `NOT_READY (no non-author approval)` — only when `REQUIRE_NON_AUTHOR_APPROVAL` is `true` and the evidence's `approval` is not `yes`
|
|
281
281
|
- `NOT_READY (no test-plan evidence)` — `total` == 0 and `exceptions` has no `test-plan`
|
|
282
|
-
- `NOT_READY (test plan:
|
|
282
|
+
- `NOT_READY (test plan: {v}/{t} verified)` — *verified* < `total`, whatever `exceptions` holds
|
|
283
283
|
- `READY` — only when all hold: unresolved_threads == 0 and not approximate; reviewDecision == `APPROVED`; ci_status == `PASSING` or `NO_CI`; the evidence is known; `approval` is `yes` or `REQUIRE_NON_AUTHOR_APPROVAL` is `false`; *verified* == `total` ≥ 1, or `total` == 0 and `exceptions` has `test-plan`
|
|
284
284
|
- `NOT_READY (status unknown)` — anything else (an `INDETERMINATE` CI status, an approximate thread count, an unrecognised value)
|
|
285
285
|
|
|
@@ -295,37 +295,37 @@ Load for `update-pr-evidence` under every tracker provider.
|
|
|
295
295
|
|
|
296
296
|
### Process
|
|
297
297
|
|
|
298
|
-
Run steps 1–4 as ONE Bash invocation from `WORKTREE_PATH` (else cwd) — the trap removes `$S` and the temp files when it exits — with `V="
|
|
298
|
+
Run steps 1–4 as ONE Bash invocation from `WORKTREE_PATH` (else cwd) — the trap removes `$S` and the temp files when it exits — with `V="$HOME/.devflow/scripts"`.
|
|
299
299
|
|
|
300
|
-
1. **Verify.** Set `S=`, arm the D11 trap with `[ -n "$S" ] &&
|
|
301
|
-
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
|
|
302
|
-
3. **Read back** after an edit: `node "$V/verify-evidence.cjs" readback --pr
|
|
303
|
-
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
|
|
300
|
+
1. **Verify.** Set `S=`, arm the D11 trap with `[ -n "$S" ] && { rm -- "$S/base" "$S/base.sha256"; rmdir -- "$S"; } 2>/dev/null` added before its `exit`, then the four `mktemp`s and `S="$(mktemp -d)"`. Run `node "$V/verify-evidence.cjs" verify --pr {PR_NUMBER} --state "$S" --block-out "$DEVFLOW_NOTES_RAW" --comment-out "$DEVFLOW_BODY_RAW"`, 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. Continue only on exit 0 with stdout exactly one 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>`; otherwise emit `TRACEABILITY: DEGRADED (evidence unavailable)` and stop. The script makes this op's one `gh pr view` read and prints nothing it read from the PR; never read the PR another way.
|
|
301
|
+
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.
|
|
302
|
+
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.
|
|
303
|
+
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})`.
|
|
304
304
|
@end
|
|
305
305
|
|
|
306
306
|
<!-- op: ensure-pr-ready -->
|
|
307
|
-
{ensure_pr_ready()}
|
|
307
|
+
{{ensure_pr_ready()}}
|
|
308
308
|
|
|
309
309
|
<!-- op: validate-branch -->
|
|
310
|
-
{validate_branch()}
|
|
310
|
+
{{validate_branch()}}
|
|
311
311
|
|
|
312
312
|
<!-- op: post-review-summary -->
|
|
313
|
-
{post_review_summary()}
|
|
313
|
+
{{post_review_summary()}}
|
|
314
314
|
|
|
315
315
|
<!-- op: check-ci-status -->
|
|
316
|
-
{check_ci_status()}
|
|
316
|
+
{{check_ci_status()}}
|
|
317
317
|
|
|
318
318
|
<!-- op: fetch-review-threads -->
|
|
319
|
-
{fetch_review_threads()}
|
|
319
|
+
{{fetch_review_threads()}}
|
|
320
320
|
|
|
321
321
|
<!-- op: resolve-review-threads -->
|
|
322
|
-
{resolve_review_threads()}
|
|
322
|
+
{{resolve_review_threads()}}
|
|
323
323
|
|
|
324
324
|
<!-- op: post-resolution-summary -->
|
|
325
|
-
{post_resolution_summary()}
|
|
325
|
+
{{post_resolution_summary()}}
|
|
326
326
|
|
|
327
327
|
<!-- op: check-merge-readiness -->
|
|
328
|
-
{check_merge_readiness()}
|
|
328
|
+
{{check_merge_readiness()}}
|
|
329
329
|
|
|
330
330
|
<!-- op: update-pr-evidence -->
|
|
331
|
-
{update_pr_evidence()}
|
|
331
|
+
{{update_pr_evidence()}}
|
|
@@ -18,7 +18,7 @@ that reason.
|
|
|
18
18
|
@define decision_markers():
|
|
19
19
|
## Decision Markers
|
|
20
20
|
|
|
21
|
-
The `D
|
|
21
|
+
The `D{N}` labels used throughout the Git agent. **D4 (degradation contract) and
|
|
22
22
|
D11 (comment-sink scrub) are NOT here** — their definitions stay inline in the
|
|
23
23
|
agent, because they are the only two whose controls every spawn must already have
|
|
24
24
|
loaded before it can act. The rest are glossary entries: a reader consults them to
|
|
@@ -62,15 +62,15 @@ already written, and never overwrites it.
|
|
|
62
62
|
- Branches: `git branch -r --format='%(refname:short)' | head -50` — detect prefix/separator patterns
|
|
63
63
|
- Tags: `git tag --sort=-version:refname | head -20` — detect version name patterns (e.g., `v1.2.3`, `1.2.3`)
|
|
64
64
|
- Merged PR titles: `gh pr list --state merged --limit 30 --json title --jq '.[].title'` — detect PR title convention
|
|
65
|
-
- Integration branch: of the ≤5 candidates `main`, `master`, `develop`, `integration`, `trunk`, whichever exists on the remote with the most merge commits — one `git rev-list --count --merges --max-count=200 origin
|
|
65
|
+
- Integration branch: of the ≤5 candidates `main`, `master`, `develop`, `integration`, `trunk`, whichever exists on the remote with the most merge commits — one `git rev-list --count --merges --max-count=200 origin/{candidate}` per candidate (bounded to 200 merges — sufficient for heuristic ordering), at most 5 commands.
|
|
66
66
|
3. For each section, apply heuristics with a 50% majority rule. If no clear pattern: apply compliance defaults:
|
|
67
|
-
- Branch Naming:
|
|
68
|
-
- PR Titles:
|
|
69
|
-
- Version PR Titles: `chore(release): v
|
|
70
|
-
- Version Names: `v
|
|
67
|
+
- Branch Naming: `{type}/{description}` (types: feat/fix/docs/refactor/chore)
|
|
68
|
+
- PR Titles: `{type}({scope}): {description}` (conventional commits)
|
|
69
|
+
- Version PR Titles: `chore(release): v{version}`
|
|
70
|
+
- Version Names: `v{semver}` (e.g., `v1.2.3`)
|
|
71
71
|
- Branching Model: trunk-based (main as integration branch)
|
|
72
|
-
4. Write `.devflow/conventions.md`. Every
|
|
73
|
-
placeholder tokens** (
|
|
72
|
+
4. Write `.devflow/conventions.md`. Every `{...}` below is a **pattern shape written in
|
|
73
|
+
placeholder tokens** (`{type}`, `{description}`, `{scope}`, `{semver}`) — never a
|
|
74
74
|
verbatim scanned branch name, tag or PR title. Illustrative examples must be
|
|
75
75
|
synthesized from the placeholder tokens (e.g. `feat/add-login`), never lifted from the
|
|
76
76
|
scan. If a convention cannot be expressed as a shape, write the step-3 default rather
|
|
@@ -79,19 +79,19 @@ already written, and never overwrites it.
|
|
|
79
79
|
# Project Conventions
|
|
80
80
|
|
|
81
81
|
## Branch Naming
|
|
82
|
-
|
|
82
|
+
{detected or default pattern and examples}
|
|
83
83
|
|
|
84
84
|
## PR Titles
|
|
85
|
-
|
|
85
|
+
{detected or default pattern and examples}
|
|
86
86
|
|
|
87
87
|
## Version PR Titles
|
|
88
|
-
|
|
88
|
+
{detected or default pattern and examples}
|
|
89
89
|
|
|
90
90
|
## Version Names
|
|
91
|
-
|
|
91
|
+
{detected or default pattern and examples}
|
|
92
92
|
|
|
93
93
|
## Branching Model
|
|
94
|
-
|
|
94
|
+
{detected branching model description}
|
|
95
95
|
```
|
|
96
96
|
5. Post-composition verification: after composing the file content in step 4 and before writing it to disk, scan the composed content against the raw strings collected in step 2 (branch names, tag names, PR titles). Assert that no output line reproduces any scanned string verbatim (shape-derived patterns only). If a match is found, replace that line with the step-3 generic default for that section and note the substitution in the op's output under `### Substitutions`. If no matches are found, write the file.
|
|
97
97
|
@end
|
|
@@ -117,19 +117,19 @@ Applies to **`post-review-summary` and `post-resolution-summary` only.** No othe
|
|
|
117
117
|
|
|
118
118
|
Who counts as a trusted author of a PR comment, review thread or review. `fetch-review-threads` applies it to a thread's first comment; the evidence scripts implement it (`trust()` in `pr-evidence.cjs`) for the evidence record and the approval check, and a test holds both to these terms.
|
|
119
119
|
|
|
120
|
-
- **Trusted:** `VIEWER_LOGIN` always; otherwise only when `authorAssociation` is `OWNER`, `MEMBER` or `COLLABORATOR` **and** `gh api "repos
|
|
121
|
-
- **The association arm never trusts:** a login ending in `[bot]`; a login outside `^[A-Za-z0-9][A-Za-z0-9-]
|
|
120
|
+
- **Trusted:** `VIEWER_LOGIN` always; otherwise only when `authorAssociation` is `OWNER`, `MEMBER` or `COLLABORATOR` **and** `gh api "repos/{owner}/{repo}/collaborators/{login}/permission" --jq .permission` prints `admin` or `write`. The `maintain` role prints `write`; `triage` and `read` print `read`. Any other association is untrusted, with no lookup; any other output, a 404 or an error is untrusted.
|
|
121
|
+
- **The association arm never trusts:** a login ending in `[bot]`; a login outside `^[A-Za-z0-9][A-Za-z0-9-]{0,38}$`; the PR author when `isCrossRepository` is true or unknown.
|
|
122
122
|
- **Bounded:** look up only a login the association arm can still trust, each at most once per spawn and at most 20 in total; a login past the cap is untrusted. `VIEWER_LOGIN` is never looked up.
|
|
123
123
|
@end
|
|
124
124
|
|
|
125
125
|
<!-- op: decision-markers -->
|
|
126
|
-
{decision_markers()}
|
|
126
|
+
{{decision_markers()}}
|
|
127
127
|
|
|
128
128
|
<!-- op: learn-conventions -->
|
|
129
|
-
{learn_conventions()}
|
|
129
|
+
{{learn_conventions()}}
|
|
130
130
|
|
|
131
131
|
<!-- op: publication-gate -->
|
|
132
|
-
{publication_gate()}
|
|
132
|
+
{{publication_gate()}}
|
|
133
133
|
|
|
134
134
|
<!-- op: trust-rule -->
|
|
135
|
-
{trust_rule()}
|
|
135
|
+
{{trust_rule()}}
|
|
@@ -79,7 +79,7 @@ hoisting it would re-indent one of the three, and indentation is list grammar
|
|
|
79
79
|
rather than whitespace here (PF-063). Normalising those lists is its own edit.
|
|
80
80
|
|
|
81
81
|
@define state_batch_line(subject, subject_ref, ref_token):
|
|
82
|
-
2b. Render each issue's {subject} as a `**State**:
|
|
82
|
+
2b. Render each issue's {{subject}} as a `**State**: {state}` line of its own, between that issue's `### Issue {{ref_token}}:` heading and its `<untrusted-issue-body>` marker — OUTSIDE the wrapper, because {{subject_ref}} is an enum the tracker computed, not remote prose. A caller refreshing a batch reads it to see a ticket closed out of band.
|
|
83
83
|
@end
|
|
84
84
|
|
|
85
85
|
@define bare_number_rule():
|
|
@@ -87,19 +87,19 @@ A **bare number** ⇒ `TRACEABILITY: DEGRADED (ambiguous issue reference)` — u
|
|
|
87
87
|
@end
|
|
88
88
|
|
|
89
89
|
@define ref_preflight_single(provider, grammar, normalise = "", forms = "", tail = ""):
|
|
90
|
-
{normalise}Shape-gate it against {grammar}, anchored at both ends{forms}. {bare_number_rule()} Any other shape ⇒ `TRACEABILITY: DEGRADED (issue reference "
|
|
90
|
+
{{normalise}}Shape-gate it against {{grammar}}, anchored at both ends{{forms}}. {{bare_number_rule()}} Any other shape ⇒ `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match {{provider}} reference grammar)`.{{tail}}
|
|
91
91
|
@end
|
|
92
92
|
|
|
93
93
|
@define ref_preflight_list(provider, grammar, normalise = "", forms = "", tail = ""):
|
|
94
|
-
{normalise}Pre-flight the list against {grammar}, anchored at both ends{forms}, and **drop** every entry that fails, reporting each as `TRACEABILITY: DEGRADED (issue reference "
|
|
94
|
+
{{normalise}}Pre-flight the list against {{grammar}}, anchored at both ends{{forms}}, and **drop** every entry that fails, reporting each as `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match {{provider}} reference grammar)` — a bare number among them goes with them, in silence: `ambiguous issue reference` answers a reference a caller named, not one line of a list nobody chose. Every entry dropped ⇒ `TRACEABILITY: DEGRADED (no parseable refs for provider {p})`{{tail}}
|
|
95
95
|
@end
|
|
96
96
|
|
|
97
97
|
@define ref_preflight_entry(provider, grammar, normalise = "", forms = ""):
|
|
98
|
-
{normalise}Every entry of `SHIPPED_ISSUES` must satisfy {grammar}, anchored at both ends of the STRING (a newline fails it){forms} — this provider's grammar is what the entry gate's shape requirement means here, and the anchored form is what keeps a ref out of a query or a command. **Drop** every entry that fails and report it as `TRACEABILITY: DEGRADED (issue reference "
|
|
98
|
+
{{normalise}}Every entry of `SHIPPED_ISSUES` must satisfy {{grammar}}, anchored at both ends of the STRING (a newline fails it){{forms}} — this provider's grammar is what the entry gate's shape requirement means here, and the anchored form is what keeps a ref out of a query or a command. **Drop** every entry that fails and report it as `TRACEABILITY: DEGRADED (issue reference "{ref}" does not match {{provider}} reference grammar)`.
|
|
99
99
|
@end
|
|
100
100
|
|
|
101
101
|
@define ref_preflight_branch(match_phrase):
|
|
102
|
-
extract the segment {match_phrase} 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.
|
|
102
|
+
extract the segment {{match_phrase}} 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.
|
|
103
103
|
@end
|
|
104
104
|
|
|
105
105
|
@define traceable_issue_rules():
|
|
@@ -129,15 +129,15 @@ extract the segment {match_phrase} and verify it with the *fetch by key* capabil
|
|
|
129
129
|
@end
|
|
130
130
|
|
|
131
131
|
@define handoff_values(id_form, link_form):
|
|
132
|
-
**Handoff Values:** `Issue ID` = {id_form}; `PR link line` = `{link_form}`.
|
|
132
|
+
**Handoff Values:** `Issue ID` = {{id_form}}; `PR link line` = `{{link_form}}`.
|
|
133
133
|
@end
|
|
134
134
|
|
|
135
135
|
@define last_release_tag_step():
|
|
136
|
-
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "
|
|
136
|
+
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" last-tag`: `LAST_TAG <tag>` ⇒ that tag is `{last_tag}`; `LAST_TAG none` ⇒ step 1's initial-commit rule; anything else ⇒ keep step 1's tag and report status `INDETERMINATE (last release tag unresolved)`.
|
|
137
137
|
@end
|
|
138
138
|
|
|
139
139
|
@define trace_map_step(args, arm):
|
|
140
|
-
6. **Per-commit trace map.** {arm}From `WORKTREE_PATH` (else cwd), run `node "
|
|
140
|
+
6. **Per-commit trace map.** {{arm}}From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" map --from {last_tag} {{args}}; echo "exit=$?"`. Accept only `exit=0` after a first line `TRACE from:<ref> scanned:<n> traced:<n> untraced:<n> exempt:<n> unmatched:<n> bound:<ok|hit>`; copy every line above `exit=0` verbatim under `### TRACE_MAP`. Anything else ⇒ `TRACEABILITY: DEGRADED (trace map unavailable)`, `### TRACE_MAP` = `(unavailable)`, status `INDETERMINATE (trace map unavailable)`. `bound:hit` ⇒ status `INDETERMINATE (trace scan bound 500 hit)`. An `INDETERMINATE` status outranks every other.
|
|
141
141
|
@end
|
|
142
142
|
|
|
143
143
|
@export state_batch_line
|