devflow-kit 3.1.0 → 3.2.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 +36 -0
- package/README.md +1 -1
- package/dist/cli/agents-view/render.js +69 -15
- package/dist/cli/agents-view/state.js +40 -14
- package/dist/cli/commands/agents.js +135 -45
- package/dist/cli/commands/init.js +128 -53
- package/dist/cli/commands/learning.js +36 -8
- package/dist/cli/commands/memory.js +35 -14
- package/dist/cli/commands/uninstall.js +163 -39
- package/dist/commands/code-review.md +0 -2
- package/dist/commands/debug.md +14 -11
- package/dist/commands/dynamic-build.md +33 -43
- package/dist/commands/dynamic-plan.md +8 -2
- package/dist/commands/explore.md +9 -3
- package/dist/commands/implement.md +20 -16
- package/dist/commands/plan.md +13 -9
- package/dist/commands/release.md +8 -2
- package/dist/commands/research.md +8 -2
- package/dist/commands/resolve.md +1 -3
- package/dist/commands/self-review.md +0 -2
- package/dist/core/agent-frontmatter.js +25 -0
- package/dist/core/agent-models.js +198 -36
- package/dist/core/agent-state.js +27 -5
- package/dist/core/assets.js +1 -1
- package/dist/core/feature-config.js +68 -10
- package/dist/core/flags.js +24 -0
- package/dist/core/learning-queue-cleanup.js +10 -11
- package/dist/core/linked-path.js +46 -0
- package/dist/core/plugins.js +9 -3
- package/dist/core/queue-drain.js +31 -0
- package/dist/hud/components/learning-counts.js +54 -8
- package/dist/skills/git/references/tracker/github/create-release.md +2 -2
- package/dist/skills/git/references/tracker/jira/create-release.md +2 -2
- package/dist/skills/git/references/tracker/linear/create-release.md +2 -2
- package/dist/targets/claude-code/installer.js +36 -9
- package/dist/targets/claude-code/post-install.js +128 -38
- package/package.json +1 -1
- package/src/assets/agents/code.md +14 -17
- package/src/assets/agents/design.md +2 -0
- package/src/assets/agents/diagnose.md +2 -0
- package/src/assets/agents/evaluate.md +4 -0
- package/src/assets/agents/knowledge.md +2 -0
- package/src/assets/agents/research.md +2 -0
- package/src/assets/agents/review.md +2 -0
- package/src/assets/agents/scrutinize.md +4 -0
- package/src/assets/agents/simplify.md +4 -0
- package/src/assets/agents/skim.md +3 -1
- package/src/assets/agents/synthesize.md +6 -0
- package/src/assets/agents/test.md +18 -10
- package/src/assets/agents/triage.md +2 -0
- package/src/assets/agents/validate.md +14 -10
- package/src/assets/commands/_partials/_engine.mds +15 -31
- package/src/assets/commands/_partials/_knowledge.mds +0 -2
- package/src/assets/commands/_partials/_tracker.mds +1 -1
- package/src/assets/commands/code-review.mds +0 -2
- package/src/assets/commands/debug.mds +13 -8
- package/src/assets/commands/dynamic-build.mds +17 -11
- package/src/assets/commands/dynamic-plan.mds +7 -1
- package/src/assets/commands/explore.mds +9 -1
- package/src/assets/commands/implement.mds +19 -13
- package/src/assets/commands/plan.mds +12 -8
- package/src/assets/commands/release.md +8 -2
- package/src/assets/commands/research.mds +8 -2
- package/src/assets/commands/resolve.mds +1 -1
- package/src/assets/mds/tracker/_github.mds +2 -2
- package/src/assets/mds/tracker/_jira.mds +2 -2
- package/src/assets/mds/tracker/_linear.mds +2 -2
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +3 -2
- package/src/assets/scripts/hooks/background-memory-update +69 -11
- package/src/assets/scripts/hooks/capture-prompt +4 -3
- package/src/assets/scripts/hooks/capture-question +4 -3
- package/src/assets/scripts/hooks/capture-turn +4 -3
- package/src/assets/scripts/hooks/ensure-devflow-init +13 -1
- package/src/assets/scripts/hooks/ensure-root-gitignore +122 -10
- package/src/assets/scripts/hooks/git-marker +71 -0
- package/src/assets/scripts/hooks/json-helper.cjs +12 -145
- package/src/assets/scripts/hooks/json-parse +24 -129
- package/src/assets/scripts/hooks/lib/learning-store.cjs +169 -64
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +1 -1
- package/src/assets/scripts/hooks/memory-worker +10 -0
- package/src/assets/scripts/hooks/pre-compact-memory +66 -14
- package/src/assets/scripts/hooks/preamble +9 -1
- package/src/assets/scripts/hooks/queue-append +53 -21
- package/src/assets/scripts/hooks/session-start-context +108 -29
- package/src/assets/scripts/hooks/session-start-memory +33 -11
- package/src/assets/skills/test-driven-development/SKILL.md +6 -4
|
@@ -25,7 +25,13 @@ Orchestrate a single task through implementation by spawning specialized agents.
|
|
|
25
25
|
|
|
26
26
|
## Input
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
What follows `/implement` is bound once, here. Every later step names it `COMMAND_INPUT` and never restates it:
|
|
29
|
+
|
|
30
|
+
<command-input>
|
|
31
|
+
$ARGUMENTS
|
|
32
|
+
</command-input>
|
|
33
|
+
|
|
34
|
+
`COMMAND_INPUT` is one of:
|
|
29
35
|
- Plan document path: `.devflow/docs/design/42-jwt-auth.2026-04-07_1430.md` (path to an existing `.md` file)
|
|
30
36
|
- Issue reference: `#42`
|
|
31
37
|
- Task description: "implement JWT auth"
|
|
@@ -53,15 +59,13 @@ If the user prompt does NOT match re-validation, proceed with the full pipeline
|
|
|
53
59
|
|
|
54
60
|
**Produces:** TASK_ID, BASE_BRANCH, EXECUTION_PLAN, DECISIONS_CONTEXT, FEATURE_KNOWLEDGE, PR_DESCRIPTION_GUIDANCE, ISSUE_NUMBER, EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL, PR_EXCEPTIONS, TEST_PLAN, EVIDENCE_FILE, PR_TEST_PLAN_BLOCK, REVIEW_PUBLICATION
|
|
55
61
|
|
|
56
|
-
**Load Companion Skills** — Load via Skill tool: `devflow:test-driven-development`, `devflow:patterns`, `devflow:dependency-research`. If a skill fails to load, continue without it.
|
|
57
|
-
|
|
58
62
|
Record the current branch name as `BASE_BRANCH` - this will be the PR target.
|
|
59
63
|
|
|
60
64
|
{{docs_root()}}
|
|
61
65
|
|
|
62
66
|
{{evidence_policy()}}
|
|
63
67
|
|
|
64
|
-
**Plan Document Handling** (when
|
|
68
|
+
**Plan Document Handling** (when `COMMAND_INPUT` is a path ending in `.md`):
|
|
65
69
|
1. Read the plan document from the path provided
|
|
66
70
|
2. Extract from YAML frontmatter: `execution-strategy`, `context-risk`, `issue` number
|
|
67
71
|
3. Extract from body: Subtask Breakdown, Implementation Plan, Patterns to Follow, Acceptance Criteria
|
|
@@ -72,23 +76,25 @@ Record the current branch name as `BASE_BRANCH` - this will be the PR target.
|
|
|
72
76
|
|
|
73
77
|
If `PR_DESCRIPTION_GUIDANCE` was not set above (non-plan paths: issue input or task description), set it to `(none)`.
|
|
74
78
|
|
|
79
|
+
**Empty input.** When `COMMAND_INPUT` is empty — a plan handoff arrives this way, with the plan already in the conversation — there is no argument to name the branch from, and the Git agent sees none of this conversation. Write the description yourself: one line, taken from the plan's title or, with no plan, from the conversation. Send it as the setup-task `TASK_DESCRIPTION` below.
|
|
80
|
+
|
|
75
81
|
Spawn Git agent to set up task environment. The Git agent derives the branch name automatically from the issue or task description:
|
|
76
82
|
|
|
77
83
|
```
|
|
78
84
|
Agent(subagent_type="Git"):
|
|
79
85
|
"OPERATION: setup-task
|
|
80
86
|
BASE_BRANCH: {current branch name}
|
|
81
|
-
ISSUE_INPUT: {
|
|
82
|
-
TASK_DESCRIPTION: {
|
|
87
|
+
ISSUE_INPUT: {COMMAND_INPUT verbatim, when it is a single whitespace-delimited token that does not end in .md; when it ends in .md, the plan frontmatter's issue value verbatim unless absent or pending — otherwise omit}
|
|
88
|
+
TASK_DESCRIPTION: {COMMAND_INPUT verbatim, when it is two or more whitespace-delimited tokens; when COMMAND_INPUT is empty, the one-line description you wrote above — otherwise omit}
|
|
83
89
|
ISSUE_REQUIRED: {ISSUE_REQUIRED}
|
|
84
90
|
APPLY_CONVENTIONS: {APPLY_CONVENTIONS}
|
|
85
|
-
PLAN_ARTIFACT_PATH: {path to plan document if
|
|
91
|
+
PLAN_ARTIFACT_PATH: {path to plan document if COMMAND_INPUT ends in .md, otherwise (none)}
|
|
86
92
|
Derive branch name from issue or description, create feature branch, and fetch issue if specified.
|
|
87
93
|
Return the branch setup summary."
|
|
88
94
|
```
|
|
89
95
|
|
|
90
96
|
The issue token is forwarded **unclassified**, and the routing is decided by
|
|
91
|
-
SHAPE alone — how many tokens
|
|
97
|
+
SHAPE alone — how many tokens `COMMAND_INPUT` has, and whether it ends in `.md`.
|
|
92
98
|
|
|
93
99
|
`setup-task` is the one step that has resolved a provider, and therefore the only
|
|
94
100
|
one that knows what an issue reference looks like on this machine: `#123`,
|
|
@@ -133,7 +139,7 @@ visible, a silently discarded request is not.
|
|
|
133
139
|
|
|
134
140
|
**Test plan.** Before any Code spawn, give the task a test plan in the evidence file `{worktree}/.devflow/docs/evidence-{branch_slug}.md` (`EVIDENCE_FILE`) — unlike the handoff file, it stays after the PR exists. It holds up to three sections, in this order and nothing else: `## Test Plan`; `## Evidence Exceptions`, a byte copy of `PR_EXCEPTIONS` present only while that is not `(none)`; and `## Claims`, always last, so every claim is appended at the end of the file. Create the file if absent; if it exists, replace its `## Test Plan` and `## Evidence Exceptions` sections and keep `## Claims` byte-identical.
|
|
135
141
|
|
|
136
|
-
Write the `## Test Plan` section: when
|
|
142
|
+
Write the `## Test Plan` section: when `COMMAND_INPUT` is a plan document with a `## Test Plan` section, copy that section's lines verbatim; otherwise write one TP line per acceptance criterion the plan, the issue or the task text states, numbered from `TP-1`. Never invent a criterion, and word every scenario yourself in plain words: the lines reach the PR body, so a scenario holds no `#`, `@` or `/` — no issue reference, mention, closing keyword target or URL — and the files a TP covers go in its `files:` field. Each line follows the TP-line contract:
|
|
137
143
|
|
|
138
144
|
{{test_plan_line()}}
|
|
139
145
|
|
|
@@ -314,7 +320,7 @@ ISSUE_PR_LINK: {ISSUE_PR_LINK captured in Phase 1, or (none)}"
|
|
|
314
320
|
After Code agent completes, spawn Validate agent to verify correctness:
|
|
315
321
|
|
|
316
322
|
```
|
|
317
|
-
Agent(subagent_type="Validate"
|
|
323
|
+
Agent(subagent_type="Validate"):
|
|
318
324
|
"FILES_CHANGED: {list of files from Code agent output}
|
|
319
325
|
VALIDATION_SCOPE: full
|
|
320
326
|
Run build, typecheck, lint, test. Report pass/fail with failure details."
|
|
@@ -394,7 +400,7 @@ If Scrutinize agent returns BLOCKED, report to user and halt.
|
|
|
394
400
|
If Scrutinize agent made code changes (status: FIXED), spawn Validate agent to verify:
|
|
395
401
|
|
|
396
402
|
```
|
|
397
|
-
Agent(subagent_type="Validate"
|
|
403
|
+
Agent(subagent_type="Validate"):
|
|
398
404
|
"FILES_CHANGED: {files modified by Scrutinize agent}
|
|
399
405
|
VALIDATION_SCOPE: changed-only
|
|
400
406
|
Verify Scrutinize agent's fixes didn't break anything."
|
|
@@ -442,7 +448,7 @@ Validate alignment with request and plan. Report ALIGNED or MISALIGNED with deta
|
|
|
442
448
|
```
|
|
443
449
|
- Spawn Validate agent to verify fix didn't break tests:
|
|
444
450
|
```
|
|
445
|
-
Agent(subagent_type="Validate"
|
|
451
|
+
Agent(subagent_type="Validate"):
|
|
446
452
|
"FILES_CHANGED: {files modified by fix Code agent}
|
|
447
453
|
VALIDATION_SCOPE: changed-only"
|
|
448
454
|
```
|
|
@@ -490,7 +496,7 @@ After every Test agent run — PASS or FAIL, first run or retry — append its T
|
|
|
490
496
|
```
|
|
491
497
|
- Spawn Validate agent to verify fix didn't break tests:
|
|
492
498
|
```
|
|
493
|
-
Agent(subagent_type="Validate"
|
|
499
|
+
Agent(subagent_type="Validate"):
|
|
494
500
|
"FILES_CHANGED: {files modified by fix Code agent}
|
|
495
501
|
VALIDATION_SCOPE: changed-only"
|
|
496
502
|
```
|
|
@@ -26,7 +26,13 @@ The orchestrator only spawns agents and gates — all analytical work is done by
|
|
|
26
26
|
|
|
27
27
|
## Input
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
What follows `/plan` is bound once, here. Every later step names it `COMMAND_INPUT` and never restates it:
|
|
30
|
+
|
|
31
|
+
<command-input>
|
|
32
|
+
$ARGUMENTS
|
|
33
|
+
</command-input>
|
|
34
|
+
|
|
35
|
+
`COMMAND_INPUT` is one of:
|
|
30
36
|
- Opens with a candidate issue reference → issue mode (one candidate = single-ref, more than one = multi-issue)
|
|
31
37
|
- Path to existing `.md` file → **error**: "Use /implement with plan documents"
|
|
32
38
|
- Other text → feature description
|
|
@@ -66,7 +72,7 @@ Explore the user's intent through focused Socratic questioning before spawning a
|
|
|
66
72
|
|
|
67
73
|
**Step 0 — Fetch issue(s)** (issue mode only; skip for feature-description and empty modes):
|
|
68
74
|
|
|
69
|
-
- **Single-ref** (one candidate ref in
|
|
75
|
+
- **Single-ref** (one candidate ref in `COMMAND_INPUT`):
|
|
70
76
|
|
|
71
77
|
```
|
|
72
78
|
Agent(subagent_type="Git"):
|
|
@@ -106,8 +112,6 @@ If the user says "skip" or "just proceed" — skip remaining questions, present
|
|
|
106
112
|
**Produces:** SKIM_CONTEXT, DECISIONS_CONTEXT, FEATURE_KNOWLEDGE
|
|
107
113
|
**Requires:** CONFIRMED_SCOPE
|
|
108
114
|
|
|
109
|
-
**Load Companion Skills** — Load via Skill tool: `devflow:test-driven-development`, `devflow:patterns`, `devflow:software-design`, `devflow:security`, `devflow:design-review`. If a skill fails to load, continue without it.
|
|
110
|
-
|
|
111
115
|
Spawn Skim agent for codebase context:
|
|
112
116
|
|
|
113
117
|
```
|
|
@@ -136,7 +140,7 @@ Pass `FEATURE_KNOWLEDGE` alongside `DECISIONS_CONTEXT` to Explore and Design age
|
|
|
136
140
|
**Produces:** EXPLORE_OUTPUTS
|
|
137
141
|
**Requires:** SKIM_CONTEXT, DECISIONS_CONTEXT
|
|
138
142
|
|
|
139
|
-
Spawn 4 Explore agents **in a single message**, each with Skim agent context, `DECISIONS_CONTEXT` (from Phase 2), and `FEATURE_KNOWLEDGE` (from Phase 2). Include instructions: "follow `devflow:apply-decisions` for DECISIONS_CONTEXT" and "The FEATURE_KNOWLEDGE is a baseline — VALIDATE, EXTEND, and CORRECT it, don't repeat it. Focus on areas the feature knowledge doesn't cover and changes since it was last updated."
|
|
143
|
+
Spawn 4 Explore agents **in a single message**, each with Skim agent context, `DECISIONS_CONTEXT` (from Phase 2), and `FEATURE_KNOWLEDGE` (from Phase 2). Include instructions: "follow `devflow:apply-decisions` for DECISIONS_CONTEXT" and "The FEATURE_KNOWLEDGE is a baseline — VALIDATE, EXTEND, and CORRECT it, don't repeat it. Focus on areas the feature knowledge doesn't cover and changes since it was last updated." Ask each agent for a final report of at most about 1,500 tokens: findings with file:line references, not file dumps.
|
|
140
144
|
|
|
141
145
|
| Focus | Thoroughness | Find |
|
|
142
146
|
|-------|-------------|------|
|
|
@@ -265,7 +269,7 @@ User can:
|
|
|
265
269
|
**Produces:** IMPL_EXPLORE_OUTPUTS
|
|
266
270
|
**Requires:** SKIM_CONTEXT, ACCEPTED_SCOPE
|
|
267
271
|
|
|
268
|
-
Spawn 4 Explore agents **in a single message**, each with Skim agent context + accepted scope:
|
|
272
|
+
Spawn 4 Explore agents **in a single message**, each with Skim agent context + accepted scope. Ask each agent for a final report of at most about 1,500 tokens: findings with file:line references, not file dumps.
|
|
269
273
|
|
|
270
274
|
| Focus | Thoroughness | Find |
|
|
271
275
|
|-------|-------------|------|
|
|
@@ -294,7 +298,7 @@ Combine into: patterns to follow, integration points, reusable code, edge cases"
|
|
|
294
298
|
**Produces:** PLAN_OUTPUTS
|
|
295
299
|
**Requires:** IMPL_EXPLORATION_SYNTHESIS, GAP_SYNTHESIS, DECISIONS_CONTEXT
|
|
296
300
|
|
|
297
|
-
Spawn 3 Plan agents **in a single message**, each with implementation exploration synthesis:
|
|
301
|
+
Spawn 3 Plan agents **in a single message**, each with implementation exploration synthesis. Ask each agent for a final report of at most about 1,500 tokens: the plan itself, not a restatement of the exploration.
|
|
298
302
|
|
|
299
303
|
| Focus | Output |
|
|
300
304
|
|-------|--------|
|
|
@@ -470,7 +474,7 @@ Spawn a Git agent with `OPERATION: ensure-traceable-issue`:
|
|
|
470
474
|
```
|
|
471
475
|
Agent(subagent_type="Git"):
|
|
472
476
|
"OPERATION: ensure-traceable-issue
|
|
473
|
-
ISSUE_INPUT: {the raw candidate token from
|
|
477
|
+
ISSUE_INPUT: {the raw candidate token from COMMAND_INPUT if /plan was invoked with an issue reference, else omit}
|
|
474
478
|
TASK_DESCRIPTION: {Gate 0 confirmed scope — one-line title}
|
|
475
479
|
INITIAL_REQUEST: {the Gate 0 confirmed scope statement}
|
|
476
480
|
REQUIREMENTS: {discovered requirements summary from Phase 6 gap synthesis}
|
|
@@ -17,13 +17,19 @@ Release the project using adaptive learned configuration. On first run, scans th
|
|
|
17
17
|
|
|
18
18
|
## Input
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
What follows `/release` is bound once, here. Every later step names it `COMMAND_INPUT` and never restates it:
|
|
21
|
+
|
|
22
|
+
<command-input>
|
|
23
|
+
$ARGUMENTS
|
|
24
|
+
</command-input>
|
|
25
|
+
|
|
26
|
+
`COMMAND_INPUT` is one of:
|
|
21
27
|
- Explicit version: `v1.2.3` or `1.2.3`
|
|
22
28
|
- Bump type: `patch`, `minor`, `major`
|
|
23
29
|
- Flag: `--dry-run`
|
|
24
30
|
- Empty: interactive mode (will ask for version)
|
|
25
31
|
|
|
26
|
-
Parse from
|
|
32
|
+
Parse from `COMMAND_INPUT`:
|
|
27
33
|
- `VERSION`: explicit version string if present (strip leading `v`)
|
|
28
34
|
- `BUMP_TYPE`: `patch | minor | major` if bump type provided
|
|
29
35
|
- `DRY_RUN`: true if `--dry-run` present, false otherwise
|
|
@@ -20,7 +20,13 @@ Research a topic by spawning parallel Research agents across multiple research t
|
|
|
20
20
|
|
|
21
21
|
## Input
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
What follows `/research` is bound once, here. Every later step names it `COMMAND_INPUT` and never restates it:
|
|
24
|
+
|
|
25
|
+
<command-input>
|
|
26
|
+
$ARGUMENTS
|
|
27
|
+
</command-input>
|
|
28
|
+
|
|
29
|
+
`COMMAND_INPUT` is one of:
|
|
24
30
|
- Research question: "best caching strategies"
|
|
25
31
|
- Comparison question: "compare React vs Svelte for our use case"
|
|
26
32
|
- Empty: use conversation context
|
|
@@ -111,7 +117,7 @@ If external research was skipped due to tool unavailability: inform user.
|
|
|
111
117
|
1. If `codebase` type was not in RESEARCH_PLAN → skip
|
|
112
118
|
2. Check if matching feature knowledge already exists by reading `{worktree}/.devflow/features/index.md` (or globbing frontmatter if absent). If covered → skip
|
|
113
119
|
3. Use AskUserQuestion: "No feature knowledge exists for {researched area}. Create one?"
|
|
114
|
-
4. If user accepts: spawn `Agent(subagent_type="Knowledge")` with researched area context + worktree root, instructing it to
|
|
120
|
+
4. If user accepts: spawn `Agent(subagent_type="Knowledge")` with researched area context + worktree root, instructing it to write `KNOWLEDGE.md` and update `index.md` directly
|
|
115
121
|
5. Set FEATURE_KNOWLEDGE_STATUS = created or skipped
|
|
116
122
|
|
|
117
123
|
**Failure handling**: Non-blocking. If Knowledge agent fails, log and continue.
|
|
@@ -275,7 +275,7 @@ If no fixes were made (CODE_AGENT_RESULTS contains 0 commits) → set VERIFICATI
|
|
|
275
275
|
Otherwise, spawn Validate agent:
|
|
276
276
|
|
|
277
277
|
```
|
|
278
|
-
Agent(subagent_type="Validate"
|
|
278
|
+
Agent(subagent_type="Validate"):
|
|
279
279
|
"FILES_CHANGED: {list of files from Code agent output}
|
|
280
280
|
VALIDATION_SCOPE: full
|
|
281
281
|
Run build, typecheck, lint, test. Report pass/fail with failure details."
|
|
@@ -226,13 +226,13 @@ This issue reached the size limit.
|
|
|
226
226
|
|
|
227
227
|
Load when the resolved tracker provider is `github` and the operation is `create-release`.
|
|
228
228
|
|
|
229
|
-
**Mechanics held here:** the
|
|
229
|
+
**Mechanics held here:** the shipped-issues step only. Tag creation, release creation and notes composition stay with the operation.
|
|
230
230
|
|
|
231
231
|
### Process
|
|
232
232
|
|
|
233
233
|
Inside step 5 (compose release notes):
|
|
234
234
|
|
|
235
|
-
- If `SHIPPED_ISSUES` provided: append a `##
|
|
235
|
+
- If `SHIPPED_ISSUES` provided: append a `## Shipped Issues` section with issue references — **first ≤50 issues** (the same bound `backlink-shipped-issues` applies); if truncated, add a final `…and {n} more issues` line (D4 degrade if enrichment fails)
|
|
236
236
|
@end
|
|
237
237
|
|
|
238
238
|
@define gather_release_evidence():
|
|
@@ -180,13 +180,13 @@ Over `{{comment_cap()}}` characters the rolling item is closed and a successor i
|
|
|
180
180
|
|
|
181
181
|
Load when the resolved tracker provider is `jira` and the operation is `create-release`.
|
|
182
182
|
|
|
183
|
-
**Mechanics held here:** the
|
|
183
|
+
**Mechanics held here:** the shipped-issues step only. Tag creation, release creation and notes composition stay with the operation and are unchanged — they are release-host mechanics, not tracker mechanics.
|
|
184
184
|
|
|
185
185
|
### Process
|
|
186
186
|
|
|
187
187
|
Inside step 5 (compose release notes):
|
|
188
188
|
|
|
189
|
-
- If `SHIPPED_ISSUES` is provided: append a `##
|
|
189
|
+
- If `SHIPPED_ISSUES` is provided: append a `## Shipped Issues` section rendering each entry through `## Reference Rendering` — **first ≤50 issues** (the same bound `backlink-shipped-issues` applies); if truncated, add a final `…and {n} more issues` line (D4 degrade if enrichment fails).
|
|
190
190
|
- {{common.ref_preflight_list("jira", "`^[A-Z][A-Z0-9_]{1,9}-[1-9][0-9]{0,8}$`", "", "", " and the section is omitted rather than rendered empty.")}}
|
|
191
191
|
- `## Reference Rendering` absent, or its token discarded by the gate below ⇒ render the key itself on its own line, and record the discard under `### Substitutions`.
|
|
192
192
|
|
|
@@ -218,13 +218,13 @@ Over `{{comment_cap()}}` characters the rolling item is closed and a successor i
|
|
|
218
218
|
|
|
219
219
|
Load when the resolved tracker provider is `linear` and the operation is `create-release`.
|
|
220
220
|
|
|
221
|
-
**Mechanics held here:** the
|
|
221
|
+
**Mechanics held here:** the shipped-issues step only. Tag creation, release creation and notes composition stay with the operation and are unchanged — they are release-host mechanics, not tracker mechanics.
|
|
222
222
|
|
|
223
223
|
### Process
|
|
224
224
|
|
|
225
225
|
Inside step 5 (compose release notes):
|
|
226
226
|
|
|
227
|
-
- If `SHIPPED_ISSUES` is provided: append a `##
|
|
227
|
+
- If `SHIPPED_ISSUES` is provided: append a `## Shipped Issues` section rendering each entry through `## Reference Rendering` — **first ≤50 issues** (the same bound `backlink-shipped-issues` applies); if truncated, add a final `…and {n} more issues` line (D4 degrade if enrichment fails).
|
|
228
228
|
- {{common.ref_preflight_list("linear", "**either** anchored form — the team-key form `^[A-Z][A-Z0-9]{0,9}-[1-9][0-9]{0,8}$` or the internal-id form `^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$`", "**ASCII-upper-normalise every entry first.** ", ", never joined into one alternation", " and the section is omitted rather than rendered empty.")}}
|
|
229
229
|
- `## Reference Rendering` absent, or its token discarded by the gate below ⇒ render the reference itself on its own line, and record the discard under `### Substitutions`.
|
|
230
230
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- ORCHESTRATOR CHARTER ---
|
|
2
2
|
You are the orchestrator of this session: you coordinate, agents produce.
|
|
3
3
|
|
|
4
|
-
Never do work-product mainline: no file edits, no builds, no multi-file reads, no codebase orientation, no debug loops. Delegate all of it.
|
|
4
|
+
Never do work-product mainline: no file edits, no builds, no multi-file reads, no codebase orientation, no debug loops. Delegate all of it, with one bounded exception: the main thread may run only a single git, gh or script command whose output stays under about 40 lines, and never a diff, log or test run.
|
|
5
5
|
|
|
6
6
|
Routing (Agent tool) — pick the roster agent that fits the work:
|
|
7
7
|
- Search and listing: Explore. Codebase orientation: Skim.
|
|
@@ -14,7 +14,8 @@ Stays mainline (judgment work): conversation, decisions, routing, synthesizing a
|
|
|
14
14
|
Operating rules:
|
|
15
15
|
- Decompose mainline. Subagents cannot spawn subagents — you own task breakdown, then delegate leaf tasks.
|
|
16
16
|
- Subagents see none of this conversation. Make every delegation self-contained: goal, constraints, relevant session decisions and facts, exact paths. A deliverable that draws on the conversation (issue, PR, report) needs the substance in the prompt — not a pointer to it.
|
|
17
|
+
- Report cap: ask every direct delegation for a final report of at most about 1,500 tokens — findings, paths and verdicts, not file dumps.
|
|
17
18
|
- Parallelize independent delegations in one message. Git operations stay sequential.
|
|
18
19
|
- Feature knowledge (direct delegations only — workflow skills handle their own): before delegating non-trivial code work, match the task area against .devflow/features/index.md and pass matching KNOWLEDGE.md content as FEATURE_KNOWLEDGE; after delegated changes to a covered area, spawn Knowledge to refresh that KB.
|
|
19
20
|
- Decisions (direct delegations only — workflow skills load their own): pass the index named under PROJECT DECISIONS as DECISIONS_CONTEXT — its content, read once — to every agent that takes it.
|
|
20
|
-
- Plan handoff: if the user's first message begins with `Implement the following plan:`, say so in one sentence, then immediately invoke devflow:implement via the Skill tool with the
|
|
21
|
+
- Plan handoff: if the user's first message begins with `Implement the following plan:`, say so in one sentence, then immediately invoke devflow:implement via the Skill tool with no arguments — the plan is already in this conversation. Do not pause to ask.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# background-memory-update — Eager working-memory refresh worker
|
|
4
4
|
# Spawned as a detached background process by memory-worker (Stop hook) after the
|
|
5
|
-
# 120s throttle expires. Drains .pending-turns.jsonl → calls `claude -p` (sonnet
|
|
5
|
+
# 120s throttle expires. Drains .pending-turns.jsonl → calls `claude -p` (sonnet 5.5) →
|
|
6
6
|
# rewrites WORKING-MEMORY.md with a git-reconciliation stamp on line 1.
|
|
7
7
|
#
|
|
8
8
|
# Applies the LLM-vs-plumbing principle: artifact content (WORKING-MEMORY.md) is
|
|
@@ -106,6 +106,18 @@ if ! command -v cksum >/dev/null 2>&1; then
|
|
|
106
106
|
exit 0
|
|
107
107
|
fi
|
|
108
108
|
|
|
109
|
+
# --- Never write through a symbolic link (D-HOOKS-NO-SYMLINK, git-marker) ---
|
|
110
|
+
# The lock (`mkdir`), the batch merge (`>>`), the claim heartbeat and the success
|
|
111
|
+
# stamp (`touch`) write these paths in place; the claim and the trim rename onto the
|
|
112
|
+
# batch and the swap onto the working memory, and `mv` moves a file into the folder
|
|
113
|
+
# a link at its target names. The trim's copy is created only where nothing stands.
|
|
114
|
+
# So the run is skipped, before it takes the lock or starts an LLM run, when any of
|
|
115
|
+
# these paths or a folder above them is a link.
|
|
116
|
+
if ! df_no_symlink_below "$PROJECT_ROOT" "$LOCK_DIR" "$QUEUE_FILE" "$PROCESSING_FILE" "$OK_FILE" "$MEMORY_FILE"; then
|
|
117
|
+
log "SKIP: a symbolic link sits on the path to the memory files in $MEMORY_DIR; nothing written"
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
109
121
|
# --- Worker-level lock (300s stale-break — much longer than learning-lock's 30s) ---
|
|
110
122
|
# This prevents a second worker (spawned 121s later) from double-writing WORKING-MEMORY.md
|
|
111
123
|
# while the first worker's claude -p call (up to 120s) is still in flight.
|
|
@@ -210,8 +222,20 @@ if [ -f "$PROCESSING_FILE" ]; then
|
|
|
210
222
|
_PROC_LINES=$(wc -l < "$PROCESSING_FILE" | tr -d ' ')
|
|
211
223
|
if [ "$_PROC_LINES" -gt "$PROC_OVERFLOW_CAP" ]; then
|
|
212
224
|
_PROC_TMP="$PROCESSING_FILE.tmp.$$"
|
|
213
|
-
|
|
214
|
-
|
|
225
|
+
# SEC-2: the batch is the claimed queue, 0600 like the queue itself, and the
|
|
226
|
+
# cold path at session start moves it back to the queue. mv gives it the
|
|
227
|
+
# trimmed copy's inode and mode, so the copy is created under umask 077, and
|
|
228
|
+
# only where nothing stands at its name: builtin tests come first, so an entry
|
|
229
|
+
# already there, a link to a FIFO or a device included, is never opened
|
|
230
|
+
# (noclobber alone would open one, as no regular file stands there), and
|
|
231
|
+
# noclobber then makes the create itself exclusive. Such an entry is removed
|
|
232
|
+
# instead, and the batch is left untrimmed.
|
|
233
|
+
if [ ! -e "$_PROC_TMP" ] && [ ! -L "$_PROC_TMP" ] && (umask 077 && set -o noclobber && tail -"$PROC_OVERFLOW_TARGET" "$PROCESSING_FILE" > "$_PROC_TMP") && mv "$_PROC_TMP" "$PROCESSING_FILE"; then
|
|
234
|
+
log "Processing overflow: truncated from $_PROC_LINES to $PROC_OVERFLOW_TARGET lines"
|
|
235
|
+
else
|
|
236
|
+
rm -f "$_PROC_TMP" 2>/dev/null || true
|
|
237
|
+
log "Processing overflow: not truncated, its copy could not be written ($_PROC_LINES lines)"
|
|
238
|
+
fi
|
|
215
239
|
fi
|
|
216
240
|
elif [ -f "$QUEUE_FILE" ]; then
|
|
217
241
|
mv "$QUEUE_FILE" "$PROCESSING_FILE" 2>/dev/null || { log "SKIP: failed to claim queue (race condition — another worker got it)"; exit 0; }
|
|
@@ -340,15 +364,22 @@ log "Built $TURN_COUNT turns from queue"
|
|
|
340
364
|
# on either side, the CAS must treat it as CONFLICT rather than a match — separating
|
|
341
365
|
# the "cksum invocation failure" sentinel from "file absent" keeps the "resolves toward
|
|
342
366
|
# false-conflict, never false-success" invariant true even when cksum errors.
|
|
367
|
+
#
|
|
368
|
+
# D-HOOKS-NO-SYMLINK (git-marker): the existing memory goes into the prompt, so a
|
|
369
|
+
# working memory a symbolic link leads to is treated as absent. A link at its own
|
|
370
|
+
# path has already skipped the run, since the swap renames onto it (above), so
|
|
371
|
+
# only one that appeared since can reach this read.
|
|
372
|
+
MEMORY_PRESENT=""
|
|
373
|
+
if df_file_below "$PROJECT_ROOT" "$MEMORY_FILE"; then MEMORY_PRESENT="yes"; fi
|
|
343
374
|
CKSUM_FAILED="false"
|
|
344
375
|
PRE_RUN_CKSUM="ABSENT"
|
|
345
|
-
if [ -
|
|
376
|
+
if [ -n "$MEMORY_PRESENT" ]; then
|
|
346
377
|
PRE_RUN_CKSUM=$(cksum "$MEMORY_FILE" 2>/dev/null) || CKSUM_FAILED="true"
|
|
347
378
|
fi
|
|
348
379
|
|
|
349
380
|
EXISTING_MEMORY=""
|
|
350
381
|
MEMORY_READ_LIMIT=65536 # bytes — keep prompt under context limit
|
|
351
|
-
if [ -
|
|
382
|
+
if [ -n "$MEMORY_PRESENT" ]; then
|
|
352
383
|
EXISTING_MEMORY=$(head -c "$MEMORY_READ_LIMIT" "$MEMORY_FILE")
|
|
353
384
|
fi
|
|
354
385
|
|
|
@@ -469,7 +500,7 @@ Instructions:
|
|
|
469
500
|
EOF
|
|
470
501
|
)
|
|
471
502
|
|
|
472
|
-
log "Spawning claude -p (model claude-sonnet-
|
|
503
|
+
log "Spawning claude -p (model claude-sonnet-5-5, ${TURN_COUNT} turns)"
|
|
473
504
|
# SECURITY: never log PROMPT — it contains turn content which may include secrets
|
|
474
505
|
|
|
475
506
|
# --- Watchdog timeout (overridable for tests: DEVFLOW_BG_WATCHDOG_SECS=2) ---
|
|
@@ -508,7 +539,7 @@ fi
|
|
|
508
539
|
# Job-control notifications go to stderr which is redirected to the log — suppress them.
|
|
509
540
|
set -m
|
|
510
541
|
DEVFLOW_BG_UPDATER=1 "$CLAUDE_BIN" -p \
|
|
511
|
-
--model claude-sonnet-
|
|
542
|
+
--model claude-sonnet-5-5 \
|
|
512
543
|
--dangerously-skip-permissions \
|
|
513
544
|
--allowedTools 'Read,Write' \
|
|
514
545
|
--output-format text \
|
|
@@ -572,13 +603,28 @@ fi
|
|
|
572
603
|
# Each state is assigned exactly once at the point it is decided (single OUTCOME
|
|
573
604
|
# variable, three states).
|
|
574
605
|
verify_and_swap() {
|
|
575
|
-
|
|
606
|
+
# A symbolic link at the staged path counts as no staged file (D-HOOKS-NO-SYMLINK,
|
|
607
|
+
# git-marker): `head` below would read the file it names, and the swap's `mv`
|
|
608
|
+
# would rename the link itself into the working memory's place. The worker
|
|
609
|
+
# removed that path before the LLM run, so a link there was planted during it.
|
|
610
|
+
# df_file_below checks it before both, and logs the refusal once.
|
|
611
|
+
df_file_below "$PROJECT_ROOT" "$STAGED_FILE" && [ -s "$STAGED_FILE" ] || {
|
|
576
612
|
log "WARN: staged file missing or empty after claude -p run"; OUTCOME="failed"; return; }
|
|
577
613
|
case "$(head -1 "$STAGED_FILE" 2>/dev/null || echo "")" in
|
|
578
614
|
"<!-- memory-head:"*) : ;;
|
|
579
615
|
*) log "WARN: staged file exists but stamp missing on line 1 — treating as failure"
|
|
580
616
|
rm -f "$STAGED_FILE" 2>/dev/null || true; OUTCOME="failed"; return ;;
|
|
581
617
|
esac
|
|
618
|
+
# A symbolic link on the working memory's path refuses the swap
|
|
619
|
+
# (D-HOOKS-NO-SYMLINK, git-marker): the run began without one, so a link there now
|
|
620
|
+
# is a change made during the run, and `mv` would move the staged memory into a
|
|
621
|
+
# folder it names. When the run began with no working memory, the cksum
|
|
622
|
+
# comparison below would take the link for that absent file.
|
|
623
|
+
if ! df_no_symlink_below "$PROJECT_ROOT" "$MEMORY_FILE"; then
|
|
624
|
+
rm -f "$STAGED_FILE" 2>/dev/null || true
|
|
625
|
+
log "CONFLICT: a symbolic link appeared on the path to $MEMORY_FILE during the run — not swapped, leaving .processing for retry"
|
|
626
|
+
OUTCOME="conflict"; return
|
|
627
|
+
fi
|
|
582
628
|
# Re-check real file cksum before swapping.
|
|
583
629
|
# Residual TOCTOU: a human edit landing in the O(ms) window between this
|
|
584
630
|
# cksum re-check and the mv would be clobbered; this is accepted — the
|
|
@@ -613,14 +659,26 @@ verify_and_swap() {
|
|
|
613
659
|
OUTCOME="failed"
|
|
614
660
|
verify_and_swap
|
|
615
661
|
|
|
662
|
+
# Each stamp below is a `touch`, which follows a symbolic link, and its path was
|
|
663
|
+
# checked before the LLM run, which can take minutes. So, like the swap's target,
|
|
664
|
+
# it is checked again just before the touch: a link there now is a change made
|
|
665
|
+
# during the run, and the stamp is skipped and logged (D-HOOKS-NO-SYMLINK, git-marker).
|
|
616
666
|
if [ "$OUTCOME" = "updated" ]; then
|
|
617
667
|
rm -f "$PROCESSING_FILE" 2>/dev/null || true
|
|
618
|
-
|
|
619
|
-
|
|
668
|
+
if df_no_symlink_below "$PROJECT_ROOT" "$OK_FILE"; then
|
|
669
|
+
touch "$OK_FILE" 2>/dev/null || true
|
|
670
|
+
log "SUCCESS: queue drained, .last-refresh-ok touched"
|
|
671
|
+
else
|
|
672
|
+
log "SUCCESS: queue drained; .last-refresh-ok not touched, a symbolic link appeared on its path during the run"
|
|
673
|
+
fi
|
|
620
674
|
elif [ "$OUTCOME" = "conflict" ]; then
|
|
621
675
|
# Heartbeat: this batch is still owned by the retry cycle, not orphaned.
|
|
622
676
|
# Keeps session-start-memory's 300s cold path from reclaiming a live retry.
|
|
623
|
-
|
|
677
|
+
if df_no_symlink_below "$PROJECT_ROOT" "$PROCESSING_FILE"; then
|
|
678
|
+
touch "$PROCESSING_FILE" 2>/dev/null || true
|
|
679
|
+
else
|
|
680
|
+
log "Heartbeat skipped: a symbolic link appeared on the path to $PROCESSING_FILE during the run"
|
|
681
|
+
fi
|
|
624
682
|
log "CONFLICT: queue retained in .processing, .last-refresh-ok untouched"
|
|
625
683
|
else
|
|
626
684
|
# Leave .processing for session-start-memory's D56c cold path; do NOT touch .last-refresh-ok
|
|
@@ -84,11 +84,12 @@ source "$SCRIPT_DIR/ensure-devflow-init" "$CWD" || exit 0
|
|
|
84
84
|
source "$SCRIPT_DIR/get-mtime" || { echo "capture-prompt: failed to source get-mtime" >&2; exit 1; }
|
|
85
85
|
source "$SCRIPT_DIR/learning-lock" || { echo "capture-prompt: failed to source learning-lock" >&2; exit 1; }
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
# Each queue is checked from the root it lies below, and its folder created there,
|
|
88
|
+
# by queue_append_row (D-HOOKS-NO-SYMLINK, git-marker).
|
|
89
89
|
TS=$(date +%s)
|
|
90
90
|
queue_append_both \
|
|
91
|
-
"$
|
|
91
|
+
"$PROJECT_ROOT" "$MEMORY_DIR/.pending-turns.jsonl" \
|
|
92
|
+
"$LEDGER_ROOT" "$LEARNING_DIR/.pending-turns.jsonl" \
|
|
92
93
|
"$MEMORY_ENABLED" "$LEARNING_ENABLED" \
|
|
93
94
|
"user" "$PROMPT" "$TS"
|
|
94
95
|
|
|
@@ -140,8 +140,8 @@ source "$SCRIPT_DIR/ensure-devflow-init" "$CWD" || exit 0
|
|
|
140
140
|
source "$SCRIPT_DIR/get-mtime" || { echo "capture-question: failed to source get-mtime" >&2; exit 1; }
|
|
141
141
|
source "$SCRIPT_DIR/learning-lock" || { echo "capture-question: failed to source learning-lock" >&2; exit 1; }
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
# Each queue is checked from the root it lies below, and its folder created there,
|
|
144
|
+
# by queue_append_row (D-HOOKS-NO-SYMLINK, git-marker).
|
|
145
145
|
_QUESTION_COUNT=0
|
|
146
146
|
while IFS=$'\t' read -r Q A; do
|
|
147
147
|
[ -z "$Q" ] && continue
|
|
@@ -150,7 +150,8 @@ while IFS=$'\t' read -r Q A; do
|
|
|
150
150
|
A: ${A}"
|
|
151
151
|
TS=$(date +%s)
|
|
152
152
|
queue_append_both \
|
|
153
|
-
"$
|
|
153
|
+
"$PROJECT_ROOT" "$MEMORY_DIR/.pending-turns.jsonl" \
|
|
154
|
+
"$LEDGER_ROOT" "$LEARNING_DIR/.pending-turns.jsonl" \
|
|
154
155
|
"$MEMORY_ENABLED" "$LEARNING_ENABLED" \
|
|
155
156
|
"qa" "$CONTENT" "$TS"
|
|
156
157
|
_QUESTION_COUNT=$(( _QUESTION_COUNT + 1 ))
|
|
@@ -90,11 +90,12 @@ source "$SCRIPT_DIR/ensure-devflow-init" "$CWD" || exit 0
|
|
|
90
90
|
source "$SCRIPT_DIR/get-mtime" || { echo "capture-turn: failed to source get-mtime" >&2; exit 1; }
|
|
91
91
|
source "$SCRIPT_DIR/learning-lock" || { echo "capture-turn: failed to source learning-lock" >&2; exit 1; }
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
# Each queue is checked from the root it lies below, and its folder created there,
|
|
94
|
+
# by queue_append_row (D-HOOKS-NO-SYMLINK, git-marker).
|
|
95
95
|
TS=$(date +%s)
|
|
96
96
|
queue_append_both \
|
|
97
|
-
"$
|
|
97
|
+
"$PROJECT_ROOT" "$MEMORY_DIR/.pending-turns.jsonl" \
|
|
98
|
+
"$LEDGER_ROOT" "$LEARNING_DIR/.pending-turns.jsonl" \
|
|
98
99
|
"$MEMORY_ENABLED" "$LEARNING_ENABLED" \
|
|
99
100
|
"assistant" "$ASSISTANT_MSG" "$TS"
|
|
100
101
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# Ensures .devflow/ and all subdirectories exist and the project root .gitignore
|
|
3
3
|
# ignores .devflow/. Called from capture-prompt, capture-turn, capture-question,
|
|
4
4
|
# memory-worker, and pre-compact-memory. Idempotent.
|
|
5
|
-
# Returns non-zero, having written nothing, outside a git project
|
|
5
|
+
# Returns non-zero, having written nothing, outside a git project, at HOME, or when
|
|
6
|
+
# .devflow is a symbolic link.
|
|
6
7
|
# Usage: source ensure-devflow-init "$CWD"
|
|
7
8
|
|
|
8
9
|
[ -z "$1" ] && return 1
|
|
@@ -33,6 +34,17 @@ df_is_project_root "$_EDI_ROOT" 2>/dev/null || return 1
|
|
|
33
34
|
|
|
34
35
|
_DEVFLOW_DIR="$_EDI_ROOT/.devflow"
|
|
35
36
|
|
|
37
|
+
# D-HOOKS-NO-SYMLINK (git-marker): a `.devflow` that is itself a symbolic link
|
|
38
|
+
# would take every folder below and the carve-out marker into the folder it names,
|
|
39
|
+
# so nothing is written and the caller stops, as at any root it may not write to.
|
|
40
|
+
# The skip is logged once, through log() where the caller defines it. A folder below
|
|
41
|
+
# `.devflow` that is a link is left to the write under it: `mkdir -p` creates
|
|
42
|
+
# nothing through a link it ends at.
|
|
43
|
+
if ! df_no_symlink_below "$_EDI_ROOT" "$_DEVFLOW_DIR"; then
|
|
44
|
+
declare -F log >/dev/null 2>&1 && log "Skipped: $_DEVFLOW_DIR is a symbolic link; nothing written under it"
|
|
45
|
+
return 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
36
48
|
# learning/ is scaffolded only where the ledger lives. In a linked worktree whose
|
|
37
49
|
# main checkout holds the ledger (D-LEDGER-MAIN-WORKTREE, resolve-project-root) a
|
|
38
50
|
# learning/ here would be an empty directory nothing writes to; the capture hooks
|