@yagni-app/code-staging 1.2.12-staging.2140.1 → 1.2.12-staging.2145.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.
@@ -36,7 +36,7 @@ import type { PipelineStage } from "./types.js";
36
36
  * each fix. Its words never name the harness (the branding scrub rewrites
37
37
  * that word) and open with no bullet.
38
38
  */
39
- export declare const WORK_A_TICKET_SKILL = "---\nname: work-a-ticket\ndescription: How YAGNI works a ticket in one session, from reading it to a committed, honestly reported change. Read, run and plan with options and a recommendation, stop for approval, build the smallest change with subagents, verify with a fresh one, commit and report.\n---\n\n# How YAGNI works a ticket\n\nYou work one ticket for a Team, in its repository, checked out in your working directory on a machine that is yours for the whole run. A person approves your plan before anything is built. YAGNI pushes your commits, opens the pull request, waits for CI and the review, and brings you back with what they asked. Each time YAGNI resumes you, its message says which turn this is and what it needs back. This is a playbook, not a contract: use your judgement, and say what you did.\n\n## 1. Understand the ticket\n\nRead the ticket, its evidence and every image it names, the design, the memo, the checks, the Team's playbook and the Plan line's instructions. Read the code the ticket touches, and send searcher subagents for wide reads, in parallel. Settle facts by running things rather than guessing: the tests, a type check, the app, Docker, a query. Installing what a check needs is fine. Read the decision ledger (team_read with yagni_decisions_read_for_repository) and ask_yagni before you ask a person anything.\n\n## 2. Plan, then stop\n\nName the files and the steps. Where there is a real choice of approach, give the options, each with what it changes and what it costs, and recommend one. End the turn with the plan in the format the message gives, and stop. You have no edit or write tool until the plan is approved, and anything you change in tracked files before then is put back.\n\n## 3. Build\n\nThe option the person approved is binding. The plan's steps are guidance: when the code shows a better step, take it, and record the departure and why. Prefer the smallest change that meets the ticket: no drive-by refactors and no files the ticket does not need. Hand fully specified chunks of work (the files, the change, the check that proves it) to the implementer subagent, and keep the judgement calls yourself. Run the checks that prove the change. A check that already failed on the unchanged code is not yours to fix; say so.\n\n## 4. Verify\n\nBefore you finish, dispatch a fresh verification subagent on your diff, with the ticket and the plan, and fix what it finds that is real. For a large change, send two or three with different lenses and treat their agreement as confirmation.\n\n## 5. Commit and report\n\nCommit on the branch you are on, with clear messages. Never push, never open or edit a pull request, and never change the git identity or config. Write the report the message asks for last, and honestly: each check you ran with its real exit code, what you could not verify and why, and every departure from the plan and why. A check you did not run is not a pass.\n\n## Rules that always hold\n\nNever fabricate a path, a result or a check: \"not found\" and \"not verified\" are answers. Never print, commit or send a token or a variable's value. If the disk fills (\"no space left on device\"), stop and say so; do not delete or prune to go on. Call record_decision only for a consequential product or architecture call the plan did not settle.\n";
39
+ export declare const WORK_A_TICKET_SKILL = "---\nname: work-a-ticket\ndescription: How YAGNI works a ticket in one session, from reading it to a committed, honestly reported change. Read, run and plan with options and a recommendation, stop for approval, build the smallest change with subagents, verify with a fresh one, commit and report.\n---\n\n# How YAGNI works a ticket\n\nYou work one ticket for a Team, in its repository, checked out in your working directory on a machine that is yours for the whole run. A person approves your plan before anything is built. YAGNI pushes your commits, opens the pull request, waits for CI and the review, and brings you back with what they asked. Each time YAGNI resumes you, its message says which turn this is and what it needs back. This is a playbook, not a contract: use your judgement, and say what you did.\n\n## 1. Understand the ticket\n\nRead the ticket, its evidence and every image it names, the design, the memo, the checks, the Team's playbook and the Plan line's instructions. Read the code the ticket touches, and send searcher subagents for wide reads, in parallel. Settle facts by running things rather than guessing: the tests, a type check, the app, Docker, a query. Installing what a check needs is fine. Read the decision ledger (team_read with yagni_decisions_read_for_repository) and ask_yagni before you ask a person anything.\n\n## 2. Plan, then stop\n\nName the files and the steps. Where there is a real choice of approach, give the options, each with what it changes and what it costs, and recommend one. End the turn with the plan in the format the message gives, and stop. You have no edit or write tool until the plan is approved, and anything you change in tracked files before then is put back.\n\n## 3. Build\n\nThe option the person approved is binding. The plan's steps are guidance: when the code shows a better step, take it, and record the departure and why. Prefer the smallest change that meets the ticket: no drive-by refactors and no files the ticket does not need. Hand fully specified chunks of work (the files, the change, the check that proves it) to the implementer subagent, and keep the judgement calls yourself. Run the checks that prove the change. A check that already failed on the unchanged code is not yours to fix; say so.\n\n## 4. Verify\n\nBefore you finish, dispatch a fresh verification subagent on your diff, with the ticket and the plan, and fix what it finds that is real. Send one. Only for a risky change (a migration, auth, money, data that cannot be restored) send two or three with different lenses and treat their agreement as confirmation.\n\n## 5. Commit and report\n\nCommit on the branch you are on, with clear messages. Never push, never open or edit a pull request, and never change the git identity or config. Write the report the message asks for last, and honestly: each check you ran with its real exit code, what you could not verify and why, and every departure from the plan and why. A check you did not run is not a pass.\n\n## Rules that always hold\n\nNever fabricate a path, a result or a check: \"not found\" and \"not verified\" are answers. Never print, commit or send a token or a variable's value. If the disk fills (\"no space left on device\"), stop and say so; do not delete or prune to go on. Call record_decision only for a consequential product or architecture call the plan did not settle.\n";
40
40
  /** Persona body keyed by the agent name referenced in `stages.ts`. */
41
41
  export declare const PERSONA_BODIES: Record<string, string>;
42
42
  /**
@@ -103,7 +103,7 @@ The option the person approved is binding. The plan's steps are guidance: when t
103
103
 
104
104
  ## 4. Verify
105
105
 
106
- Before you finish, dispatch a fresh verification subagent on your diff, with the ticket and the plan, and fix what it finds that is real. For a large change, send two or three with different lenses and treat their agreement as confirmation.
106
+ Before you finish, dispatch a fresh verification subagent on your diff, with the ticket and the plan, and fix what it finds that is real. Send one. Only for a risky change (a migration, auth, money, data that cannot be restored) send two or three with different lenses and treat their agreement as confirmation.
107
107
 
108
108
  ## 5. Commit and report
109
109
 
@@ -62,7 +62,13 @@ export interface SubagentDef {
62
62
  /** The agent's system prompt (the markdown body). */
63
63
  body: string;
64
64
  source: SubagentSource;
65
+ /** This agent's own wall clock, when shorter than {@link SUBAGENT_WALL_TIMEOUT_MS}. */
66
+ wallTimeoutMs?: number;
65
67
  }
68
+ /** How long one verification subagent may run: a review of a finished diff,
69
+ * not a second build (on staging 2026-10-28, advanced-tier verifiers ran 34
70
+ * minutes after an 11-minute build). */
71
+ export declare const VERIFICATION_WALL_TIMEOUT_MS: number;
66
72
  /** Claude Code model names → YAGNI tiers; YAGNI tiers pass through. */
67
73
  export declare function mapModelTier(model: string | undefined): ModelTier;
68
74
  /**
@@ -154,7 +160,7 @@ export declare const SUBAGENT_WALL_TIMEOUT_MS: number;
154
160
  * Exported so a test can pin the composed policy the production path actually
155
161
  * runs (every unit test injects runStageImpl instead).
156
162
  */
157
- export declare function subagentRunPolicy(): ResiliencePolicy;
163
+ export declare function subagentRunPolicy(wallTimeoutMs?: number): ResiliencePolicy;
158
164
  /**
159
165
  * The per-attempt telemetry adapter: one content-free error-sink row per
160
166
  * resilience attempt (agent, taskIndex, outcome, timeoutKind, exit code,
@@ -212,23 +212,29 @@ What you could not check, and why.
212
212
 
213
213
  A HOLDS after real digging is valuable; a rubber stamp is not. If you found
214
214
  nothing, say exactly what you tried to break and how.`;
215
+ /** How long one verification subagent may run: a review of a finished diff,
216
+ * not a second build (on staging 2026-10-28, advanced-tier verifiers ran 34
217
+ * minutes after an 11-minute build). */
218
+ export const VERIFICATION_WALL_TIMEOUT_MS = 15 * 60_000;
215
219
  /** The diamond's reduce layer: refute-first review of completed work on the
216
- * advanced tier (judgment is the whole job, so the premium is worth paying —
217
- * the server clamps subagent children at advanced anyway). It has no
218
- * edit/write tools; bash is included for `git diff`-style inspection and is
219
- * restricted to read-only USE by the persona, the same prompt-level stance as
220
- * /go's reviewer — not a technical guarantee. */
220
+ * peak tier (judgment is the whole job, so the premium is worth paying; the
221
+ * server exempts this caller from its advanced ceiling for subagents). One by
222
+ * default, on a fifteen-minute clock. It has no edit/write tools; bash is
223
+ * included for `git diff`-style inspection and is restricted to read-only USE
224
+ * by the persona, the same prompt-level stance as /go's reviewer — not a
225
+ * technical guarantee. */
221
226
  const VERIFICATION_AGENT = {
222
227
  name: "verification",
223
228
  description: "Adversarial verification of completed work: tries to refute a change, " +
224
229
  "plan, or claim and reports concrete failure scenarios. Does not edit " +
225
- "files. Fan out 2-3 with different lenses (correctness, edge cases, " +
226
- "codebase fit) for anything significant and treat agreement as " +
227
- "confirmation.",
228
- model: "advanced",
230
+ "files. Send one; send two or three with different lenses (correctness, " +
231
+ "edge cases, codebase fit) only for a risky change such as a migration, " +
232
+ "auth or money, and treat agreement as confirmation.",
233
+ model: "peak",
229
234
  tools: ["read", "grep", "find", "ls", "bash", "ask_yagni"],
230
235
  body: VERIFICATION_BODY,
231
236
  source: "builtin",
237
+ wallTimeoutMs: VERIFICATION_WALL_TIMEOUT_MS,
232
238
  };
233
239
  // Concrete tiers a subagent can actually run on. `balanced` is deliberately NOT
234
240
  // a member here even though it is a member of `ModelTier`: a subagent needs
@@ -485,8 +491,8 @@ export const SUBAGENT_WALL_TIMEOUT_MS = 2 * 60 * 60_000;
485
491
  * Exported so a test can pin the composed policy the production path actually
486
492
  * runs (every unit test injects runStageImpl instead).
487
493
  */
488
- export function subagentRunPolicy() {
489
- return { ...DEFAULT_RESILIENCE_POLICY, wallTimeoutMs: SUBAGENT_WALL_TIMEOUT_MS, retryWallTimeout: false };
494
+ export function subagentRunPolicy(wallTimeoutMs = SUBAGENT_WALL_TIMEOUT_MS) {
495
+ return { ...DEFAULT_RESILIENCE_POLICY, wallTimeoutMs, retryWallTimeout: false };
490
496
  }
491
497
  /**
492
498
  * The per-attempt telemetry adapter: one content-free error-sink row per
@@ -525,10 +531,22 @@ export function makeSubagentTool(deps = {}) {
525
531
  // text). The write-gate keys on the resolved agent's tools,
526
532
  // passed per task below — the synthetic stage id is "implement" and would
527
533
  // otherwise block retry of a read-only verifier forever.
528
- const run = deps.runStageImpl ??
529
- withResilience(runStage, subagentRunPolicy(), {
530
- telemetry: subagentTelemetry(deps.env ?? process.env),
531
- });
534
+ // One wrapped runner per wall clock: an agent with its own (verification)
535
+ // gets a runner built on it, every other agent shares the lane's default.
536
+ const runners = new Map();
537
+ const runFor = (def) => {
538
+ if (deps.runStageImpl)
539
+ return deps.runStageImpl;
540
+ const wall = def.wallTimeoutMs ?? SUBAGENT_WALL_TIMEOUT_MS;
541
+ let runner = runners.get(wall);
542
+ if (!runner) {
543
+ runner = withResilience(runStage, subagentRunPolicy(wall), {
544
+ telemetry: subagentTelemetry(deps.env ?? process.env),
545
+ });
546
+ runners.set(wall, runner);
547
+ }
548
+ return runner;
549
+ };
532
550
  const discover = deps.discover ?? discoverSubagents;
533
551
  const grounded = deps.grounded !== false;
534
552
  // Resolve the effective body for a spawned subagent: the grounding-free
@@ -647,7 +665,7 @@ export function makeSubagentTool(deps = {}) {
647
665
  outcomes = await Promise.all(resolved.map(async ({ def, task }, index) => {
648
666
  const progress = progresses[index];
649
667
  const { stage, ctx: stageCtx } = buildSubagentStage(def, task, { grounded });
650
- const result = await run(stage, stageCtx, {
668
+ const result = await runFor(def)(stage, stageCtx, {
651
669
  cwd,
652
670
  signal,
653
671
  // Write-gate input: whether the agent's DECLARED tool list has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yagni-app/code-staging",
3
- "version": "1.2.12-staging.2140.1",
3
+ "version": "1.2.12-staging.2145.1",
4
4
  "description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
@@ -58,5 +58,5 @@
58
58
  "turndown": "^7.2.4",
59
59
  "typebox": "^1.3.15"
60
60
  },
61
- "yagniSourceSha": "1d9c69f9bf3799e4a3ff4650aacebb1ba5f3820e"
61
+ "yagniSourceSha": "4064b7ebffbbfcea04ce43830817cd6e35df7d77"
62
62
  }