@yagni-app/code-staging 0.0.0 → 0.1.0-staging.1002.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/LICENSE.md +55 -0
- package/README.md +166 -0
- package/dist/branding.d.ts +25 -0
- package/dist/branding.js +27 -0
- package/dist/claudeCompat.d.ts +78 -0
- package/dist/claudeCompat.js +198 -0
- package/dist/cli.d.ts +55 -0
- package/dist/cli.js +326 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +28 -0
- package/dist/credentials.d.ts +41 -0
- package/dist/credentials.js +75 -0
- package/dist/distribution.d.ts +15 -0
- package/dist/distribution.js +45 -0
- package/dist/doctor.d.ts +94 -0
- package/dist/doctor.js +343 -0
- package/dist/extension/advisor.d.ts +84 -0
- package/dist/extension/advisor.js +101 -0
- package/dist/extension/askAdvisorTool.d.ts +83 -0
- package/dist/extension/askAdvisorTool.js +188 -0
- package/dist/extension/askYagniTool.d.ts +39 -0
- package/dist/extension/askYagniTool.js +63 -0
- package/dist/extension/bless.d.ts +45 -0
- package/dist/extension/bless.js +74 -0
- package/dist/extension/branding.d.ts +48 -0
- package/dist/extension/branding.js +119 -0
- package/dist/extension/chipEditor.d.ts +117 -0
- package/dist/extension/chipEditor.js +369 -0
- package/dist/extension/config.d.ts +112 -0
- package/dist/extension/config.js +108 -0
- package/dist/extension/costHud.d.ts +73 -0
- package/dist/extension/costHud.js +123 -0
- package/dist/extension/decisionCapture.d.ts +52 -0
- package/dist/extension/decisionCapture.js +66 -0
- package/dist/extension/decisions.d.ts +83 -0
- package/dist/extension/decisions.js +200 -0
- package/dist/extension/diagnostics.d.ts +41 -0
- package/dist/extension/diagnostics.js +110 -0
- package/dist/extension/index.d.ts +116 -0
- package/dist/extension/index.js +388 -0
- package/dist/extension/initDone.d.ts +28 -0
- package/dist/extension/initDone.js +66 -0
- package/dist/extension/initPass.d.ts +152 -0
- package/dist/extension/initPass.js +394 -0
- package/dist/extension/nextWorkTool.d.ts +51 -0
- package/dist/extension/nextWorkTool.js +80 -0
- package/dist/extension/permission.d.ts +91 -0
- package/dist/extension/permission.js +236 -0
- package/dist/extension/pipeline/activity.d.ts +37 -0
- package/dist/extension/pipeline/activity.js +151 -0
- package/dist/extension/pipeline/activityFeed.d.ts +59 -0
- package/dist/extension/pipeline/activityFeed.js +175 -0
- package/dist/extension/pipeline/budget.d.ts +48 -0
- package/dist/extension/pipeline/budget.js +68 -0
- package/dist/extension/pipeline/checkpoint.d.ts +31 -0
- package/dist/extension/pipeline/checkpoint.js +176 -0
- package/dist/extension/pipeline/eval.d.ts +168 -0
- package/dist/extension/pipeline/eval.js +182 -0
- package/dist/extension/pipeline/events.d.ts +56 -0
- package/dist/extension/pipeline/events.js +147 -0
- package/dist/extension/pipeline/findings.d.ts +42 -0
- package/dist/extension/pipeline/findings.js +144 -0
- package/dist/extension/pipeline/finish.d.ts +128 -0
- package/dist/extension/pipeline/finish.js +307 -0
- package/dist/extension/pipeline/goCommand.d.ts +128 -0
- package/dist/extension/pipeline/goCommand.js +972 -0
- package/dist/extension/pipeline/goCompareCommand.d.ts +48 -0
- package/dist/extension/pipeline/goCompareCommand.js +184 -0
- package/dist/extension/pipeline/goFlags.d.ts +40 -0
- package/dist/extension/pipeline/goFlags.js +46 -0
- package/dist/extension/pipeline/goStatusCommands.d.ts +59 -0
- package/dist/extension/pipeline/goStatusCommands.js +188 -0
- package/dist/extension/pipeline/invocation.d.ts +45 -0
- package/dist/extension/pipeline/invocation.js +64 -0
- package/dist/extension/pipeline/orchestrator.d.ts +131 -0
- package/dist/extension/pipeline/orchestrator.js +636 -0
- package/dist/extension/pipeline/personas.d.ts +44 -0
- package/dist/extension/pipeline/personas.js +248 -0
- package/dist/extension/pipeline/resilience.d.ts +85 -0
- package/dist/extension/pipeline/resilience.js +166 -0
- package/dist/extension/pipeline/resume.d.ts +18 -0
- package/dist/extension/pipeline/resume.js +106 -0
- package/dist/extension/pipeline/runRegistry.d.ts +112 -0
- package/dist/extension/pipeline/runRegistry.js +202 -0
- package/dist/extension/pipeline/runSession.d.ts +152 -0
- package/dist/extension/pipeline/runSession.js +167 -0
- package/dist/extension/pipeline/runState.d.ts +158 -0
- package/dist/extension/pipeline/runState.js +264 -0
- package/dist/extension/pipeline/runner.d.ts +79 -0
- package/dist/extension/pipeline/runner.js +298 -0
- package/dist/extension/pipeline/scrubSecrets.d.ts +15 -0
- package/dist/extension/pipeline/scrubSecrets.js +32 -0
- package/dist/extension/pipeline/stages.d.ts +51 -0
- package/dist/extension/pipeline/stages.js +113 -0
- package/dist/extension/pipeline/ticketResolution.d.ts +34 -0
- package/dist/extension/pipeline/ticketResolution.js +75 -0
- package/dist/extension/pipeline/types.d.ts +436 -0
- package/dist/extension/pipeline/types.js +48 -0
- package/dist/extension/pipeline/verify.d.ts +277 -0
- package/dist/extension/pipeline/verify.js +758 -0
- package/dist/extension/pipeline/workspace.d.ts +32 -0
- package/dist/extension/pipeline/workspace.js +73 -0
- package/dist/extension/pipeline/worktree.d.ts +107 -0
- package/dist/extension/pipeline/worktree.js +200 -0
- package/dist/extension/provider.d.ts +11 -0
- package/dist/extension/provider.js +26 -0
- package/dist/extension/recall.d.ts +93 -0
- package/dist/extension/recall.js +187 -0
- package/dist/extension/recordContextTool.d.ts +38 -0
- package/dist/extension/recordContextTool.js +85 -0
- package/dist/extension/recordDecisionTool.d.ts +52 -0
- package/dist/extension/recordDecisionTool.js +102 -0
- package/dist/extension/repoDocs.d.ts +81 -0
- package/dist/extension/repoDocs.js +260 -0
- package/dist/extension/resilientFetch.d.ts +60 -0
- package/dist/extension/resilientFetch.js +133 -0
- package/dist/extension/reviewTool.d.ts +34 -0
- package/dist/extension/reviewTool.js +81 -0
- package/dist/extension/spool.d.ts +92 -0
- package/dist/extension/spool.js +266 -0
- package/dist/extension/stateHome.d.ts +2 -0
- package/dist/extension/stateHome.js +6 -0
- package/dist/extension/subagents.d.ts +135 -0
- package/dist/extension/subagents.js +281 -0
- package/dist/extension/surface.d.ts +10 -0
- package/dist/extension/surface.js +12 -0
- package/dist/extension/todos.d.ts +110 -0
- package/dist/extension/todos.js +217 -0
- package/dist/extension/tokenProvider.d.ts +93 -0
- package/dist/extension/tokenProvider.js +234 -0
- package/dist/launch.d.ts +76 -0
- package/dist/launch.js +111 -0
- package/dist/login.d.ts +45 -0
- package/dist/login.js +142 -0
- package/dist/logout.d.ts +14 -0
- package/dist/logout.js +34 -0
- package/dist/paths.d.ts +31 -0
- package/dist/paths.js +87 -0
- package/dist/piPackage.d.ts +33 -0
- package/dist/piPackage.js +71 -0
- package/dist/profiles.d.ts +80 -0
- package/dist/profiles.js +222 -0
- package/dist/refresh.d.ts +70 -0
- package/dist/refresh.js +117 -0
- package/dist/upgrade.d.ts +97 -0
- package/dist/upgrade.js +284 -0
- package/package.json +39 -3
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grounded persona system-prompt bodies as DATA (spec §5).
|
|
3
|
+
*
|
|
4
|
+
* Each body is the pi `subagent` sample role (scout / planner / worker /
|
|
5
|
+
* reviewer) PLUS the explicit grounding clauses that make every node of the
|
|
6
|
+
* pipeline aware of how THIS company works — the only-YAGNI differentiator:
|
|
7
|
+
* - scout / planner: call `ask_yagni` before inferring a convention,
|
|
8
|
+
* - worker: call `record_decision` for any product-intent call,
|
|
9
|
+
* - reviewer (business-fit lens): call `review_business_match` and treat a
|
|
10
|
+
* conflict with a recorded decision as at least High.
|
|
11
|
+
*
|
|
12
|
+
* The reviewer also carries the strict, parseable findings output contract so
|
|
13
|
+
* the `{previous}` handoff into the fix stage parses (see `findings.ts`).
|
|
14
|
+
*
|
|
15
|
+
* Model + tools are NOT in the persona — the stage config (`stages.ts`) owns
|
|
16
|
+
* those. The body here is exactly the text written to the child's
|
|
17
|
+
* `--append-system-prompt` temp file. Inline TS constants (no .md copy step)
|
|
18
|
+
* keep the whole thing pure and unit-testable.
|
|
19
|
+
*/
|
|
20
|
+
import type { PipelineStage } from "./types.js";
|
|
21
|
+
/** Persona body keyed by the agent name referenced in `stages.ts`. */
|
|
22
|
+
export declare const PERSONA_BODIES: Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Grounding-free persona bodies, keyed by agent name (M6 blind lane only).
|
|
25
|
+
*
|
|
26
|
+
* `advisor` is present for completeness rather than use: no eval lane runs an
|
|
27
|
+
* advisor today, but `personaBody` throws on an unknown agent, so omitting it
|
|
28
|
+
* would turn any future blind-lane consult into a crash instead of a degraded
|
|
29
|
+
* measurement.
|
|
30
|
+
*/
|
|
31
|
+
export declare const BLIND_PERSONA_BODIES: Record<string, string>;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the system-prompt body for a stage. For non-review stages this is the
|
|
34
|
+
* role body; for the review stage it appends the lens-specific clause and the
|
|
35
|
+
* required findings output contract so the reviewer's output parses.
|
|
36
|
+
*
|
|
37
|
+
* `grounded` defaults true (every real /go stage). The M6 eval passes
|
|
38
|
+
* `grounded: false` to select the grounding-free bodies + lens clauses for the
|
|
39
|
+
* blind lane; nothing else in the pipeline ever sets it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function personaBody(stage: PipelineStage, opts?: {
|
|
42
|
+
grounded?: boolean;
|
|
43
|
+
}): string;
|
|
44
|
+
//# sourceMappingURL=personas.d.ts.map
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grounded persona system-prompt bodies as DATA (spec §5).
|
|
3
|
+
*
|
|
4
|
+
* Each body is the pi `subagent` sample role (scout / planner / worker /
|
|
5
|
+
* reviewer) PLUS the explicit grounding clauses that make every node of the
|
|
6
|
+
* pipeline aware of how THIS company works — the only-YAGNI differentiator:
|
|
7
|
+
* - scout / planner: call `ask_yagni` before inferring a convention,
|
|
8
|
+
* - worker: call `record_decision` for any product-intent call,
|
|
9
|
+
* - reviewer (business-fit lens): call `review_business_match` and treat a
|
|
10
|
+
* conflict with a recorded decision as at least High.
|
|
11
|
+
*
|
|
12
|
+
* The reviewer also carries the strict, parseable findings output contract so
|
|
13
|
+
* the `{previous}` handoff into the fix stage parses (see `findings.ts`).
|
|
14
|
+
*
|
|
15
|
+
* Model + tools are NOT in the persona — the stage config (`stages.ts`) owns
|
|
16
|
+
* those. The body here is exactly the text written to the child's
|
|
17
|
+
* `--append-system-prompt` temp file. Inline TS constants (no .md copy step)
|
|
18
|
+
* keep the whole thing pure and unit-testable.
|
|
19
|
+
*/
|
|
20
|
+
const SCOUT_BODY = `You are a scout. Quickly investigate this codebase and return compressed, structured findings another agent can act on without re-reading everything. Your output is handed to an agent who has NOT seen the files you explored.
|
|
21
|
+
|
|
22
|
+
You are grounded in how THIS company works. Before inferring a convention, an ownership rule, or "the way we do it here" from the repo alone, call ask_yagni and fold the cited answer into your map. Prefer recorded company conventions over guesses.
|
|
23
|
+
|
|
24
|
+
Strategy:
|
|
25
|
+
1. grep/find to locate the relevant code.
|
|
26
|
+
2. Read only the key sections (not whole files).
|
|
27
|
+
3. Identify the types, interfaces, and functions in play.
|
|
28
|
+
4. Note dependencies between files and any conventions ask_yagni surfaces.
|
|
29
|
+
|
|
30
|
+
Output:
|
|
31
|
+
## Files
|
|
32
|
+
\`path\` (lines) - what is here
|
|
33
|
+
## Key code
|
|
34
|
+
The critical types/functions, quoted from the files.
|
|
35
|
+
## Conventions
|
|
36
|
+
Anything ask_yagni confirmed about how this is done here.
|
|
37
|
+
## Start here
|
|
38
|
+
Which file to open first, and why.`;
|
|
39
|
+
const PLANNER_BODY = `You are a planning specialist. You receive a scout's map plus the ticket and produce a concrete, ordered implementation plan. You must NOT change any files — only read, analyze, and plan.
|
|
40
|
+
|
|
41
|
+
You are grounded. Call ask_yagni before inferring a convention or making any organization- or codebase-specific assumption; build the plan on cited company decisions, not priors.
|
|
42
|
+
|
|
43
|
+
Output:
|
|
44
|
+
## Goal
|
|
45
|
+
One sentence.
|
|
46
|
+
## Plan
|
|
47
|
+
Numbered, small, actionable steps — each names the file/function to touch.
|
|
48
|
+
## Files to modify / create
|
|
49
|
+
- \`path\` - what changes and why
|
|
50
|
+
## Risks
|
|
51
|
+
What to watch for, including any decision the worker will be forced to make.
|
|
52
|
+
|
|
53
|
+
Finish the job in ONE turn: do not end your turn on an interstitial like "now let me check X". Your FINAL message MUST be the complete plan in the format above (## Goal / ## Plan / ## Files to modify or create / ## Risks). Keep exploring with your tools until you can write the whole plan, then write it as your last message.
|
|
54
|
+
|
|
55
|
+
Budget discipline: you have a hard output budget, and a plan that gets cut off mid-thought is worth less than a short plan that ships. Explore only until you can name the files and the steps — do not read broadly for completeness, and do not re-verify what you have already established. Aim for 5-10 short steps; the worker fills small gaps from the ticket. When in doubt, write the plan NOW.
|
|
56
|
+
|
|
57
|
+
Keep it concrete; the worker executes it verbatim.`;
|
|
58
|
+
const WORKER_BODY = `You are a worker with full capabilities, operating in an isolated context to implement a plan. Work autonomously and use the tools as needed.
|
|
59
|
+
|
|
60
|
+
You are grounded. Call ask_yagni before guessing about anything organization- or codebase-specific. Critically: for ANY product-intent call you are forced to make that the plan did not settle — a behavior choice, a tradeoff, an interpretation of intent — call record_decision so the company's decision corpus captures it and the next agent inherits the call instead of re-litigating it.
|
|
61
|
+
|
|
62
|
+
You MUST make the change. If the plan is missing, partial, or appears cut off, do not stop at exploring: implement the ticket directly from the ticket text and the code, calling record_decision for any intent you infer. Ending your turn with no write/edit is a failure.
|
|
63
|
+
|
|
64
|
+
Output:
|
|
65
|
+
## Completed
|
|
66
|
+
What was done.
|
|
67
|
+
## Files changed
|
|
68
|
+
- \`path\` - what changed
|
|
69
|
+
## Decisions recorded
|
|
70
|
+
Any record_decision calls you made, and why.
|
|
71
|
+
## Notes
|
|
72
|
+
Anything the reviewer should know (exact file paths, key functions touched).`;
|
|
73
|
+
const REVIEWER_BODY = `You are a senior, adversarial code reviewer. Analyze the change for real problems, not style nits. Bash is read-only here (\`git diff\`, \`git log\`, \`git show\`); do NOT modify files or run builds.
|
|
74
|
+
|
|
75
|
+
You are grounded in how THIS company works — that context is your edge over a repo-only reviewer.
|
|
76
|
+
|
|
77
|
+
Strategy:
|
|
78
|
+
1. \`git diff\` to see the change.
|
|
79
|
+
2. Read the modified files and their surroundings.
|
|
80
|
+
3. Judge the change through your assigned lens (below).`;
|
|
81
|
+
const ADVISOR_BODY = `You are a senior advisor, brought in on ONE hard call by an agent that is already working the problem. You are the strongest model in this system and you are expensive, so you are consulted, not driven — the caller does the work, you supply the judgment.
|
|
82
|
+
|
|
83
|
+
Do NOT take the caller's framing on trust. They have told you what they think the question is; half your value is looking where they did not think to look. Read the relevant code yourself before forming a view.
|
|
84
|
+
|
|
85
|
+
You are grounded. Call ask_yagni before guessing about anything organization- or codebase-specific — conventions, prior decisions, product intent, ownership. The caller's brief may not include the company context that decides this question, and a strong model guessing about how this company works is exactly the failure mode you exist to avoid.
|
|
86
|
+
|
|
87
|
+
You are READ-ONLY. You have read, grep, find and ls. You must not write, edit, build, or run anything — the caller applies whatever you recommend.
|
|
88
|
+
|
|
89
|
+
Output, and nothing else:
|
|
90
|
+
## Recommendation
|
|
91
|
+
The call, stated plainly. Take a position; "it depends" is not an answer unless you then say what it depends on and how to check.
|
|
92
|
+
## Why
|
|
93
|
+
The reasoning, grounded in what you actually read. Cite files and lines.
|
|
94
|
+
## What would change my mind
|
|
95
|
+
The specific evidence that would flip this. If you were not able to verify something load-bearing, say so here rather than hedging the recommendation.
|
|
96
|
+
|
|
97
|
+
Be terse and decisive. The caller is mid-task and paying peak rates for your turn.`;
|
|
98
|
+
/** Persona body keyed by the agent name referenced in `stages.ts`. */
|
|
99
|
+
export const PERSONA_BODIES = {
|
|
100
|
+
scout: SCOUT_BODY,
|
|
101
|
+
planner: PLANNER_BODY,
|
|
102
|
+
worker: WORKER_BODY,
|
|
103
|
+
reviewer: REVIEWER_BODY,
|
|
104
|
+
advisor: ADVISOR_BODY,
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Grounding-FREE persona bodies for the M6 grounded-vs-blind eval ONLY. These are
|
|
108
|
+
* the same capable roles with every "you are grounded / call ask_yagni /
|
|
109
|
+
* record_decision / consult the decision corpus" clause removed, so the blind lane
|
|
110
|
+
* measures what the model produces WITHOUT this company's captured judgment in
|
|
111
|
+
* front of it. They are never used by the real /go path (which is always grounded);
|
|
112
|
+
* the eval pairs them with grounding-tool-stripped stages (see eval.ts). Kept
|
|
113
|
+
* separate from PERSONA_BODIES so the grounded prompt text stays byte-identical.
|
|
114
|
+
*/
|
|
115
|
+
const SCOUT_BLIND = `You are a scout. Quickly investigate this codebase and return compressed, structured findings another agent can act on without re-reading everything. Your output is handed to an agent who has NOT seen the files you explored.
|
|
116
|
+
|
|
117
|
+
Strategy:
|
|
118
|
+
1. grep/find to locate the relevant code.
|
|
119
|
+
2. Read only the key sections (not whole files).
|
|
120
|
+
3. Identify the types, interfaces, and functions in play.
|
|
121
|
+
4. Note dependencies between files and any conventions you can infer from the code.
|
|
122
|
+
|
|
123
|
+
Output:
|
|
124
|
+
## Files
|
|
125
|
+
\`path\` (lines) - what is here
|
|
126
|
+
## Key code
|
|
127
|
+
The critical types/functions, quoted from the files.
|
|
128
|
+
## Conventions
|
|
129
|
+
Anything you infer about how this is done here.
|
|
130
|
+
## Start here
|
|
131
|
+
Which file to open first, and why.`;
|
|
132
|
+
const PLANNER_BLIND = `You are a planning specialist. You receive a scout's map plus the ticket and produce a concrete, ordered implementation plan. You must NOT change any files — only read, analyze, and plan.
|
|
133
|
+
|
|
134
|
+
Output:
|
|
135
|
+
## Goal
|
|
136
|
+
One sentence.
|
|
137
|
+
## Plan
|
|
138
|
+
Numbered, small, actionable steps — each names the file/function to touch.
|
|
139
|
+
## Files to modify / create
|
|
140
|
+
- \`path\` - what changes and why
|
|
141
|
+
## Risks
|
|
142
|
+
What to watch for, including any decision the worker will be forced to make.
|
|
143
|
+
|
|
144
|
+
Finish the job in ONE turn: do not end your turn on an interstitial like "now let me check X". Your FINAL message MUST be the complete plan in the format above (## Goal / ## Plan / ## Files to modify or create / ## Risks). Keep exploring with your tools until you can write the whole plan, then write it as your last message.
|
|
145
|
+
|
|
146
|
+
Budget discipline: you have a hard output budget, and a plan that gets cut off mid-thought is worth less than a short plan that ships. Explore only until you can name the files and the steps — do not read broadly for completeness, and do not re-verify what you have already established. Aim for 5-10 short steps; the worker fills small gaps from the ticket. When in doubt, write the plan NOW.
|
|
147
|
+
|
|
148
|
+
Keep it concrete; the worker executes it verbatim.`;
|
|
149
|
+
const WORKER_BLIND = `You are a worker with full capabilities, operating in an isolated context to implement a plan. Work autonomously and use the tools as needed.
|
|
150
|
+
|
|
151
|
+
You MUST make the change. If the plan is missing, partial, or appears cut off, do not stop at exploring: implement the ticket directly from the ticket text and the code. Ending your turn with no write/edit is a failure.
|
|
152
|
+
|
|
153
|
+
Output:
|
|
154
|
+
## Completed
|
|
155
|
+
What was done.
|
|
156
|
+
## Files changed
|
|
157
|
+
- \`path\` - what changed
|
|
158
|
+
## Notes
|
|
159
|
+
Anything the reviewer should know (exact file paths, key functions touched).`;
|
|
160
|
+
const REVIEWER_BLIND = `You are a senior, adversarial code reviewer. Analyze the change for real problems, not style nits. Bash is read-only here (\`git diff\`, \`git log\`, \`git show\`); do NOT modify files or run builds.
|
|
161
|
+
|
|
162
|
+
Strategy:
|
|
163
|
+
1. \`git diff\` to see the change.
|
|
164
|
+
2. Read the modified files and their surroundings.
|
|
165
|
+
3. Judge the change through your assigned lens (below).`;
|
|
166
|
+
const ADVISOR_BLIND = `You are a senior advisor, brought in on ONE hard call by an agent that is already working the problem. You are the strongest model in this system and you are expensive, so you are consulted, not driven — the caller does the work, you supply the judgment.
|
|
167
|
+
|
|
168
|
+
Do NOT take the caller's framing on trust. They have told you what they think the question is; half your value is looking where they did not think to look. Read the relevant code yourself before forming a view.
|
|
169
|
+
|
|
170
|
+
You are READ-ONLY. You have read, grep, find and ls. You must not write, edit, build, or run anything — the caller applies whatever you recommend.
|
|
171
|
+
|
|
172
|
+
Output, and nothing else:
|
|
173
|
+
## Recommendation
|
|
174
|
+
The call, stated plainly. Take a position; "it depends" is not an answer unless you then say what it depends on and how to check.
|
|
175
|
+
## Why
|
|
176
|
+
The reasoning, grounded in what you actually read. Cite files and lines.
|
|
177
|
+
## What would change my mind
|
|
178
|
+
The specific evidence that would flip this. If you were not able to verify something load-bearing, say so here rather than hedging the recommendation.
|
|
179
|
+
|
|
180
|
+
Be terse and decisive. The caller is mid-task and paying peak rates for your turn.`;
|
|
181
|
+
/**
|
|
182
|
+
* Grounding-free persona bodies, keyed by agent name (M6 blind lane only).
|
|
183
|
+
*
|
|
184
|
+
* `advisor` is present for completeness rather than use: no eval lane runs an
|
|
185
|
+
* advisor today, but `personaBody` throws on an unknown agent, so omitting it
|
|
186
|
+
* would turn any future blind-lane consult into a crash instead of a degraded
|
|
187
|
+
* measurement.
|
|
188
|
+
*/
|
|
189
|
+
export const BLIND_PERSONA_BODIES = {
|
|
190
|
+
scout: SCOUT_BLIND,
|
|
191
|
+
planner: PLANNER_BLIND,
|
|
192
|
+
worker: WORKER_BLIND,
|
|
193
|
+
reviewer: REVIEWER_BLIND,
|
|
194
|
+
advisor: ADVISOR_BLIND,
|
|
195
|
+
};
|
|
196
|
+
/** The lens-specific clause appended to the reviewer body, one per review angle. */
|
|
197
|
+
const LENS_CLAUSES = {
|
|
198
|
+
correctness: "Lens: CORRECTNESS. Hunt bugs, broken logic, unhandled edge cases, race conditions, and incorrect error handling. A real defect that can ship is at least High.",
|
|
199
|
+
business_fit: "Lens: BUSINESS-FIT. This is the only-YAGNI lens. Call review_business_match and consult the decision corpus: does this change match the recorded decisions, conventions, and current priorities of this company? Right code doing the wrong thing is exactly the failure you exist to catch. A conflict with a recorded decision is at least High.",
|
|
200
|
+
does_it_hold: "Lens: DOES-IT-HOLD. Does the change actually accomplish the ticket, and does it build/test as far as read-only bash lets you verify? Missing tests for new behavior, or a change that does not do the task, is at least High.",
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* The machine-readable findings contract every reviewer must emit so the
|
|
204
|
+
* orchestrator can parse the handoff (kept in lockstep with `parseFindings`).
|
|
205
|
+
*/
|
|
206
|
+
const FINDINGS_CONTRACT = `=== Findings output contract (required) ===
|
|
207
|
+
End your review with a fenced block in EXACTLY this form, one line per finding:
|
|
208
|
+
|
|
209
|
+
\`\`\`findings
|
|
210
|
+
SEVERITY | file:line | message
|
|
211
|
+
\`\`\`
|
|
212
|
+
|
|
213
|
+
SEVERITY is one of: critical, high, medium, low. Use \`file:line\` when you can point to a location; otherwise give a short location or omit it. Put one finding per line and nothing else inside the block. If you found no problems, emit an empty \`\`\`findings block. Only critical and high findings block the change.
|
|
214
|
+
|
|
215
|
+
Be terse. Do not narrate your process, restate the diff, or quote code back at length — spend your output on the findings themselves, at most a few short paragraphs before the block. Cap the block at the 12 most important findings, most severe first; a review cut off by its own length limit helps nobody.`;
|
|
216
|
+
/**
|
|
217
|
+
* The blind lens clauses (M6): correctness + does_it_hold are not grounding
|
|
218
|
+
* specific, so they are reused verbatim; business_fit drops review_business_match
|
|
219
|
+
* and the decision-corpus instruction, judging only against apparent product
|
|
220
|
+
* intent, so the blind lane has no path to this company's captured judgment.
|
|
221
|
+
*/
|
|
222
|
+
const BLIND_LENS_CLAUSES = {
|
|
223
|
+
correctness: LENS_CLAUSES.correctness,
|
|
224
|
+
business_fit: "Lens: BUSINESS-FIT. Does this change match the apparent product intent and the conventions visible in the code? Right code doing the wrong thing is exactly the failure you exist to catch. A clear mismatch is at least High.",
|
|
225
|
+
does_it_hold: LENS_CLAUSES.does_it_hold,
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Resolve the system-prompt body for a stage. For non-review stages this is the
|
|
229
|
+
* role body; for the review stage it appends the lens-specific clause and the
|
|
230
|
+
* required findings output contract so the reviewer's output parses.
|
|
231
|
+
*
|
|
232
|
+
* `grounded` defaults true (every real /go stage). The M6 eval passes
|
|
233
|
+
* `grounded: false` to select the grounding-free bodies + lens clauses for the
|
|
234
|
+
* blind lane; nothing else in the pipeline ever sets it.
|
|
235
|
+
*/
|
|
236
|
+
export function personaBody(stage, opts = {}) {
|
|
237
|
+
const grounded = opts.grounded !== false;
|
|
238
|
+
const bodies = grounded ? PERSONA_BODIES : BLIND_PERSONA_BODIES;
|
|
239
|
+
const base = bodies[stage.agent];
|
|
240
|
+
if (!base)
|
|
241
|
+
throw new Error(`No persona body for agent "${stage.agent}"`);
|
|
242
|
+
if (stage.id !== "review")
|
|
243
|
+
return base;
|
|
244
|
+
const lens = stage.lens ?? "correctness";
|
|
245
|
+
const clause = (grounded ? LENS_CLAUSES : BLIND_LENS_CLAUSES)[lens];
|
|
246
|
+
return [base, clause, FINDINGS_CONTRACT].join("\n\n");
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=personas.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* R1 - in-loop resilience for the /go pipeline.
|
|
3
|
+
*
|
|
4
|
+
* `withResilience(runStage, policy)` is a composable higher-order function that
|
|
5
|
+
* wraps the per-stage child spawn (`runner.ts#runStage`) with the one axis the
|
|
6
|
+
* roadmap calls the whole competitive gap: a per-stage IDLE timeout (no NDJSON
|
|
7
|
+
* event for N ms) and a total WALL-CLOCK timeout, both firing the runner's
|
|
8
|
+
* existing SIGTERM -> SIGKILL abort; bounded exponential backoff with jitter; and
|
|
9
|
+
* retry of CLASSIFIED-TRANSIENT outcomes only. One structured telemetry record is
|
|
10
|
+
* emitted per attempt.
|
|
11
|
+
*
|
|
12
|
+
* It is net-new vs the crash-resume work (#1225): that resumed a /go ACROSS
|
|
13
|
+
* invocations; this survives a transient blip IN-loop so a single 429/5xx/socket
|
|
14
|
+
* reset across the up-to-8 child spawns no longer terminates the run.
|
|
15
|
+
*
|
|
16
|
+
* Load-bearing corrections (from the design deep-dive):
|
|
17
|
+
* - The wrapper passes the base a COMPOSED signal (the caller's signal + a
|
|
18
|
+
* per-attempt timeout controller) so a timeout reuses the runner's real
|
|
19
|
+
* SIGTERM/SIGKILL path rather than a second kill mechanism.
|
|
20
|
+
* - A WRITE stage (implement / fix) is retried ONLY when it failed BEFORE its
|
|
21
|
+
* first event. Once it has emitted an event a partial edit may exist, so a
|
|
22
|
+
* re-run could double-apply; we let the no_changes / diff guard + review loop
|
|
23
|
+
* catch it instead. Read-only stages (map / plan / review) retry freely.
|
|
24
|
+
* - A genuine user abort (the CALLER's signal aborted) is never retried.
|
|
25
|
+
*
|
|
26
|
+
* Pure-ish: all impurity (timers, sleep, clock, randomness) sits behind injectable
|
|
27
|
+
* seams so the wrapper is unit-tested without real waits.
|
|
28
|
+
*/
|
|
29
|
+
import type { RunStageDeps } from "./runner.js";
|
|
30
|
+
import { type PipelineStage, type ResiliencePolicy, type ReviewLens, type StageResult } from "./types.js";
|
|
31
|
+
export type { ResiliencePolicy } from "./types.js";
|
|
32
|
+
/** The exact shape of `runner.ts#runStage` (the seam this HOF wraps and returns). */
|
|
33
|
+
export type RunStageFn = (stage: PipelineStage, ctx: {
|
|
34
|
+
ticket: string;
|
|
35
|
+
previous?: string;
|
|
36
|
+
lens?: ReviewLens;
|
|
37
|
+
grounded?: boolean;
|
|
38
|
+
}, deps: RunStageDeps) => Promise<StageResult>;
|
|
39
|
+
/** One telemetry record per attempt (routed through the orchestrator logger). */
|
|
40
|
+
export interface ResilienceAttemptRecord {
|
|
41
|
+
stageId: string;
|
|
42
|
+
lens?: ReviewLens;
|
|
43
|
+
/** 1-based attempt number. */
|
|
44
|
+
attempt: number;
|
|
45
|
+
outcome: "ok" | "transient" | "fatal" | "timeout" | "aborted";
|
|
46
|
+
exitCode: number;
|
|
47
|
+
stopReason?: string;
|
|
48
|
+
elapsedMs: number;
|
|
49
|
+
willRetry: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** Injectable seams so the wrapper is deterministic and fast in tests. */
|
|
52
|
+
export interface ResilienceSeams {
|
|
53
|
+
/** Backoff sleep (default: real setTimeout). */
|
|
54
|
+
sleep?: (ms: number) => Promise<void>;
|
|
55
|
+
/** Monotonic-ish clock for elapsed timing (default: Date.now). */
|
|
56
|
+
now?: () => number;
|
|
57
|
+
/** Jitter source in [0, 1) (default: Math.random). */
|
|
58
|
+
random?: () => number;
|
|
59
|
+
}
|
|
60
|
+
export interface WithResilienceOpts {
|
|
61
|
+
telemetry?: (rec: ResilienceAttemptRecord) => void;
|
|
62
|
+
seams?: ResilienceSeams;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compose a caller signal (possibly undefined) with one or more internal signals
|
|
66
|
+
* into a single AbortSignal that fires when ANY of them fires. Exported so the
|
|
67
|
+
* verify gate (P4) can reuse the same idle/wall composition for its deterministic
|
|
68
|
+
* command. A single signal is returned as-is (no needless wrapper).
|
|
69
|
+
*/
|
|
70
|
+
export declare function composeAbortSignal(caller: AbortSignal | undefined, ...others: AbortSignal[]): AbortSignal;
|
|
71
|
+
/**
|
|
72
|
+
* PURE retry classifier: is this stage outcome a transient blip worth retrying?
|
|
73
|
+
* Transient iff it timed out, OR exited non-zero with a stopReason of `error`, OR
|
|
74
|
+
* its stderr carries a transient transport fingerprint. NEVER transient on a user
|
|
75
|
+
* abort (`stopReason === "aborted"` with no timeout) or a clean non-transient
|
|
76
|
+
* failure, so a deterministic bug is surfaced honestly instead of retried.
|
|
77
|
+
*/
|
|
78
|
+
export declare function classifyTransient(result: StageResult, timedOut: boolean): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Wrap a `runStage` with idle + wall timeout and transient-only retry. The
|
|
81
|
+
* returned function is drop-in compatible with the bare runner, so it slots into
|
|
82
|
+
* the orchestrator's `deps.runStage ?? withResilience(defaultRunStage, …)` seam.
|
|
83
|
+
*/
|
|
84
|
+
export declare function withResilience(base: RunStageFn, policy: ResiliencePolicy, opts?: WithResilienceOpts): RunStageFn;
|
|
85
|
+
//# sourceMappingURL=resilience.d.ts.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* R1 - in-loop resilience for the /go pipeline.
|
|
3
|
+
*
|
|
4
|
+
* `withResilience(runStage, policy)` is a composable higher-order function that
|
|
5
|
+
* wraps the per-stage child spawn (`runner.ts#runStage`) with the one axis the
|
|
6
|
+
* roadmap calls the whole competitive gap: a per-stage IDLE timeout (no NDJSON
|
|
7
|
+
* event for N ms) and a total WALL-CLOCK timeout, both firing the runner's
|
|
8
|
+
* existing SIGTERM -> SIGKILL abort; bounded exponential backoff with jitter; and
|
|
9
|
+
* retry of CLASSIFIED-TRANSIENT outcomes only. One structured telemetry record is
|
|
10
|
+
* emitted per attempt.
|
|
11
|
+
*
|
|
12
|
+
* It is net-new vs the crash-resume work (#1225): that resumed a /go ACROSS
|
|
13
|
+
* invocations; this survives a transient blip IN-loop so a single 429/5xx/socket
|
|
14
|
+
* reset across the up-to-8 child spawns no longer terminates the run.
|
|
15
|
+
*
|
|
16
|
+
* Load-bearing corrections (from the design deep-dive):
|
|
17
|
+
* - The wrapper passes the base a COMPOSED signal (the caller's signal + a
|
|
18
|
+
* per-attempt timeout controller) so a timeout reuses the runner's real
|
|
19
|
+
* SIGTERM/SIGKILL path rather than a second kill mechanism.
|
|
20
|
+
* - A WRITE stage (implement / fix) is retried ONLY when it failed BEFORE its
|
|
21
|
+
* first event. Once it has emitted an event a partial edit may exist, so a
|
|
22
|
+
* re-run could double-apply; we let the no_changes / diff guard + review loop
|
|
23
|
+
* catch it instead. Read-only stages (map / plan / review) retry freely.
|
|
24
|
+
* - A genuine user abort (the CALLER's signal aborted) is never retried.
|
|
25
|
+
*
|
|
26
|
+
* Pure-ish: all impurity (timers, sleep, clock, randomness) sits behind injectable
|
|
27
|
+
* seams so the wrapper is unit-tested without real waits.
|
|
28
|
+
*/
|
|
29
|
+
/** Stages whose value is landed edits: a re-run after a partial write can double-apply. */
|
|
30
|
+
const WRITE_STAGE_IDS = ["implement", "fix"];
|
|
31
|
+
/** Honest message stamped on a stage we aborted for exceeding its time budget. */
|
|
32
|
+
const TIMEOUT_MESSAGE = "stage exceeded its idle or wall-clock timeout";
|
|
33
|
+
/** stderr fingerprints of a transient transport/provider blip worth retrying. */
|
|
34
|
+
const TRANSIENT_STDERR = /\b429\b|\b5\d\d\b|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|socket hang up/i;
|
|
35
|
+
/**
|
|
36
|
+
* Compose a caller signal (possibly undefined) with one or more internal signals
|
|
37
|
+
* into a single AbortSignal that fires when ANY of them fires. Exported so the
|
|
38
|
+
* verify gate (P4) can reuse the same idle/wall composition for its deterministic
|
|
39
|
+
* command. A single signal is returned as-is (no needless wrapper).
|
|
40
|
+
*/
|
|
41
|
+
export function composeAbortSignal(caller, ...others) {
|
|
42
|
+
const signals = [caller, ...others].filter((s) => s != null);
|
|
43
|
+
if (signals.length === 1)
|
|
44
|
+
return signals[0];
|
|
45
|
+
return AbortSignal.any(signals);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* PURE retry classifier: is this stage outcome a transient blip worth retrying?
|
|
49
|
+
* Transient iff it timed out, OR exited non-zero with a stopReason of `error`, OR
|
|
50
|
+
* its stderr carries a transient transport fingerprint. NEVER transient on a user
|
|
51
|
+
* abort (`stopReason === "aborted"` with no timeout) or a clean non-transient
|
|
52
|
+
* failure, so a deterministic bug is surfaced honestly instead of retried.
|
|
53
|
+
*/
|
|
54
|
+
export function classifyTransient(result, timedOut) {
|
|
55
|
+
if (timedOut)
|
|
56
|
+
return true;
|
|
57
|
+
if (result.stopReason === "aborted")
|
|
58
|
+
return false;
|
|
59
|
+
if (result.exitCode !== 0 && result.stopReason === "error")
|
|
60
|
+
return true;
|
|
61
|
+
if (result.stderr && TRANSIENT_STDERR.test(result.stderr))
|
|
62
|
+
return true;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
/** A stage outcome the wrapper should consider for retry (mirror of the orchestrator's isFailed, minus R3-a). */
|
|
66
|
+
function stageFailed(result, timedOut) {
|
|
67
|
+
return timedOut || result.exitCode !== 0 || result.stopReason === "error" || result.stopReason === "aborted";
|
|
68
|
+
}
|
|
69
|
+
/** Bounded exponential backoff with +/- jitter, never negative. */
|
|
70
|
+
function backoffDelay(policy, attempt, random) {
|
|
71
|
+
const raw = Math.min(policy.backoffMaxMs, policy.backoffBaseMs * 2 ** (attempt - 1));
|
|
72
|
+
const jitter = raw * policy.jitterRatio * (random() * 2 - 1);
|
|
73
|
+
return Math.max(0, Math.round(raw + jitter));
|
|
74
|
+
}
|
|
75
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
|
+
/**
|
|
77
|
+
* Wrap a `runStage` with idle + wall timeout and transient-only retry. The
|
|
78
|
+
* returned function is drop-in compatible with the bare runner, so it slots into
|
|
79
|
+
* the orchestrator's `deps.runStage ?? withResilience(defaultRunStage, …)` seam.
|
|
80
|
+
*/
|
|
81
|
+
export function withResilience(base, policy, opts = {}) {
|
|
82
|
+
const sleep = opts.seams?.sleep ?? defaultSleep;
|
|
83
|
+
const now = opts.seams?.now ?? Date.now;
|
|
84
|
+
const random = opts.seams?.random ?? Math.random;
|
|
85
|
+
const telemetry = opts.telemetry;
|
|
86
|
+
return async (stage, ctx, deps) => {
|
|
87
|
+
const callerSignal = deps.signal;
|
|
88
|
+
const isWriteStage = WRITE_STAGE_IDS.includes(stage.id);
|
|
89
|
+
let last;
|
|
90
|
+
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
91
|
+
// A caller abort during a backoff window: stop before spending another attempt.
|
|
92
|
+
if (callerSignal?.aborted && last)
|
|
93
|
+
return last;
|
|
94
|
+
const timeoutController = new AbortController();
|
|
95
|
+
const composed = composeAbortSignal(callerSignal, timeoutController.signal);
|
|
96
|
+
let timedOut = false;
|
|
97
|
+
let sawAnyEvent = false;
|
|
98
|
+
const fireTimeout = () => {
|
|
99
|
+
timedOut = true;
|
|
100
|
+
if (!timeoutController.signal.aborted)
|
|
101
|
+
timeoutController.abort();
|
|
102
|
+
};
|
|
103
|
+
let idleTimer;
|
|
104
|
+
const armIdle = () => {
|
|
105
|
+
if (idleTimer)
|
|
106
|
+
clearTimeout(idleTimer);
|
|
107
|
+
idleTimer = setTimeout(fireTimeout, policy.idleTimeoutMs);
|
|
108
|
+
idleTimer.unref?.();
|
|
109
|
+
};
|
|
110
|
+
const wallTimer = setTimeout(fireTimeout, policy.wallTimeoutMs);
|
|
111
|
+
wallTimer.unref?.();
|
|
112
|
+
armIdle();
|
|
113
|
+
const originalOnEvent = deps.onEvent;
|
|
114
|
+
const onEvent = (ev) => {
|
|
115
|
+
sawAnyEvent = true;
|
|
116
|
+
armIdle(); // reset the idle window on every live event
|
|
117
|
+
originalOnEvent?.(ev);
|
|
118
|
+
};
|
|
119
|
+
const startedAt = now();
|
|
120
|
+
try {
|
|
121
|
+
last = await base(stage, ctx, { ...deps, signal: composed, onEvent });
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
clearTimeout(wallTimer);
|
|
125
|
+
if (idleTimer)
|
|
126
|
+
clearTimeout(idleTimer);
|
|
127
|
+
}
|
|
128
|
+
const elapsedMs = now() - startedAt;
|
|
129
|
+
const callerAborted = callerSignal?.aborted === true;
|
|
130
|
+
const record = (outcome, willRetry) => {
|
|
131
|
+
telemetry?.({
|
|
132
|
+
stageId: stage.id,
|
|
133
|
+
...(ctx.lens ? { lens: ctx.lens } : {}),
|
|
134
|
+
attempt,
|
|
135
|
+
outcome,
|
|
136
|
+
exitCode: last.exitCode,
|
|
137
|
+
...(last.stopReason ? { stopReason: last.stopReason } : {}),
|
|
138
|
+
elapsedMs,
|
|
139
|
+
willRetry,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
// Genuine user cancel: surface honestly, never retry.
|
|
143
|
+
if (callerAborted) {
|
|
144
|
+
record("aborted", false);
|
|
145
|
+
return last;
|
|
146
|
+
}
|
|
147
|
+
if (!stageFailed(last, timedOut)) {
|
|
148
|
+
record("ok", false);
|
|
149
|
+
return last;
|
|
150
|
+
}
|
|
151
|
+
if (timedOut)
|
|
152
|
+
last.errorMessage = TIMEOUT_MESSAGE;
|
|
153
|
+
const transient = classifyTransient(last, timedOut);
|
|
154
|
+
// No double-apply: a write stage that already started working is never re-run.
|
|
155
|
+
const blockedByWriteGate = isWriteStage && sawAnyEvent;
|
|
156
|
+
const willRetry = transient && !blockedByWriteGate && attempt < policy.maxAttempts;
|
|
157
|
+
record(timedOut ? "timeout" : transient ? "transient" : "fatal", willRetry);
|
|
158
|
+
if (!willRetry)
|
|
159
|
+
return last;
|
|
160
|
+
await sleep(backoffDelay(policy, attempt, random));
|
|
161
|
+
}
|
|
162
|
+
// Exhausted maxAttempts on a retryable failure: return the last honest result.
|
|
163
|
+
return last;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=resilience.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE resume-decision core — the analogue of `findings.ts#shouldStop`.
|
|
3
|
+
*
|
|
4
|
+
* Given the append-only checkpoint journal for a run key and a LIVE workspace
|
|
5
|
+
* snapshot, decide whether /go can safely resume the bounded review→fix loop,
|
|
6
|
+
* must warn about a partially-built / diverged tree, or should just run fresh.
|
|
7
|
+
*
|
|
8
|
+
* Safety boundary (the whole point): we only auto-resume the EXPENSIVE half (the
|
|
9
|
+
* review→fix loop) and only when the build half fully completed AND the live git
|
|
10
|
+
* tree still matches the boundary we checkpointed. A crash mid-build, or a tree
|
|
11
|
+
* that drifted since the checkpoint, would risk double-applying edits if we
|
|
12
|
+
* replayed — so those resolve to `warn_partial` (warn + run fresh), never a
|
|
13
|
+
* silent replay. Anything unknowable fails OPEN to `fresh` (mirrors
|
|
14
|
+
* `workspaceChanged`). The fold never throws.
|
|
15
|
+
*/
|
|
16
|
+
import type { CheckpointRecord, ResumePlan, WorkspaceSnapshot } from "./types.js";
|
|
17
|
+
export declare function planResume(records: CheckpointRecord[], liveSnapshot: WorkspaceSnapshot): ResumePlan;
|
|
18
|
+
//# sourceMappingURL=resume.d.ts.map
|