From 5282f34dfd4fd35e99e72ae401c3fe4db04ca199 Mon Sep 17 00:00:00 2001 From: garrytan Date: Fri, 30 Oct 2026 04:05:43 +0000 Subject: [PATCH] feat(ship-budget): --delegate measured: an unknown charge continues, a measured call 150k above the floor delegates Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-ship-budget | 2 +- docs/ship-budget.md | 5 ++++- lib/ship-budget-cli.ts | 6 +++--- lib/ship-budget.ts | 2 +- test/ship-budget-chain.test.ts | 16 ++++++++++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/bin/gstack-ship-budget b/bin/gstack-ship-budget index f28c62255..f2b8a464e 100755 --- a/bin/gstack-ship-budget +++ b/bin/gstack-ship-budget @@ -6,7 +6,7 @@ * reserve fit under the cap. * * start --record [--token-cap ] [--meter capy|claude|estimate] [--reading ] [--prior-reading ] - * [--session |--nonce ] [--reserve ] [--delegate auto|never] [--calibration ] [--ledger ] + * [--session |--nonce ] [--reserve ] [--delegate auto|never|measured] [--calibration ] [--ledger ] * check --run |--record --step --next [,...]> [--reading ] [--prior-reading ] [--returned [,...]] [--runtime-lines ] * settle --run|--record --reservation [--unknown] [--reading ] release --run|--record --reservation * handoff --run|--record --from --to --base origin/ [--reviewed-tree ] [--trail --trail-run ] diff --git a/docs/ship-budget.md b/docs/ship-budget.md index 05fbdfac0..3d1c6828f 100644 --- a/docs/ship-budget.md +++ b/docs/ship-budget.md @@ -169,7 +169,10 @@ line prints `call`, `call_floor` and `above_floor`, so each verdict can be check grandchild's. `--delegate never` turns delegate into stop for a parent on a host without children; a child -continues with a `CONTEXT_HIGH:` note instead. +continues with a `CONTEXT_HIGH:` note instead. `--delegate measured` (what `ship/DRIVER.md` starts +with) delegates only when a measured call is 150k or more above the floor at a segment boundary; an +unknown charge continues there. A parent driving /ship from the driver carries no skill text a fresh +child would shed, so a child pays its fixed cost only when the parent's own context grew. **Small runs keep their segment.** `check --runtime-lines ` (runtime_lines from `gstack-review-plan --json`, which `gstack-ship-preflight` prints) continues at the Step 7 and diff --git a/lib/ship-budget-cli.ts b/lib/ship-budget-cli.ts index e08ce9890..507df92b4 100644 --- a/lib/ship-budget-cli.ts +++ b/lib/ship-budget-cli.ts @@ -129,7 +129,7 @@ export function writeReturnFile(file: string, lines: string[]): void { /** * Admission and the delegate rule for one boundary; reserves the admitted work * under a cap (a dispatch batch goes to admitBatch). The rule compares the call's context above the host floor (an - * unknown charge delegates at a segment boundary, never at start). A light diff + * unknown charge delegates at a segment boundary, never at start; under --delegate measured it continues). A light diff * whose segment costs less than a child keeps it (keepsSmallRun). A child * never stops on it: after a completed step it hands its remaining steps to a * grandchild, and before one, at the maximum depth or past its segment it @@ -148,7 +148,7 @@ export function decide(l: Ledger, step: string, next: Next, call: number | null, const boundary = (startThreshold ? next.step === '1' : next.step === '7' || next.step === '12'); const to = l.parent ? l.parent.to : SEGMENTS[next.step]!; const small = boundary && !startThreshold ? keepsSmallRun(loadCalibration(l.calibration), { runtimeLines, from: next.step, to, call }) : null; - const wants = boundary && !small && (above === null ? !startThreshold : above >= threshold); + const wants = boundary && !small && (above === null ? !startThreshold && l.delegate !== 'measured' : above >= threshold); const depth = l.depth ?? (l.parent ? 1 : 0); const hold = !wants || !l.parent ? undefined : !l.steps_done?.length ? 'no step completed since verify-handoff' @@ -234,7 +234,7 @@ export function cmdStart(argv: string[], ctx: Ctx): Outcome { const cap = rawCap === undefined || rawCap === 'none' ? null : parseTokens(rawCap, '--token-cap'); const callFloor = tokensOpt(o, 'call-floor'); const delegate = str(o, 'delegate') ?? 'auto'; - if (delegate !== 'auto' && delegate !== 'never') throw usageError(`--delegate '${delegate}' is not auto or never`); + if (delegate !== 'auto' && delegate !== 'never' && delegate !== 'measured') throw usageError(`--delegate '${delegate}' is not auto, never or measured`); const existing = parseRecordBlock(record); if (existing?.ledger && fs.existsSync(existing.ledger) && !readLedger(existing.ledger).closed_at) { const l = readLedger(existing.ledger); diff --git a/lib/ship-budget.ts b/lib/ship-budget.ts index 63981031a..b1d8f1e6f 100644 --- a/lib/ship-budget.ts +++ b/lib/ship-budget.ts @@ -70,7 +70,7 @@ export interface Handoff { } export interface Ledger { schema_version: 1; kind: 'gstack-ship-budget'; run: string; dir: string; file: string; record?: string; created_at: string; - cap: number | null; tail: number; stop_overhead: number; delegate: 'auto' | 'never'; role: 'parent' | 'child'; + cap: number | null; tail: number; stop_overhead: number; delegate: 'auto' | 'never' | 'measured'; role: 'parent' | 'child'; parent?: { run: string; ledger: string; handoff_sha: string; from: string; to: string; return_file: string }; meter: { contract: MeterContract; epoch: number; baseline: number; last: number | null; last_at?: string; sessions?: string[]; config_dir?: string }; epochs: Array<{ epoch: number; contract: MeterContract; baseline: number; last: number | null; used: number }>; diff --git a/test/ship-budget-chain.test.ts b/test/ship-budget-chain.test.ts index f5ade9979..082081487 100644 --- a/test/ship-budget-chain.test.ts +++ b/test/ship-budget-chain.test.ts @@ -73,6 +73,22 @@ describe('per-call thresholds above the host floor', () => { expect(h.field(unknown.out, 'BUDGET', 'verdict')).toBe('delegate'); }); + test('--delegate measured (ship/DRIVER.md): an unknown charge continues at a segment boundary; 150k above the floor still delegates', () => { + const h = fresh(); + const s = start(h, ['--token-cap', '80M', '--reading', '1000000', '--delegate', 'measured']); + const unknown = check(h, s.run, '6', '7', ['--reading', '1100000']); + expect(unknown.code, unknown.out + unknown.err).toBe(0); + expect(h.field(unknown.out, 'BUDGET', 'above_floor')).toBe('unknown'); + expect(h.field(unknown.out, 'BUDGET', 'verdict')).toBe('continue'); + expect(unknown.out).not.toContain('DELEGATE:'); + const high = check(h, s.run, '11.5', '12', ['--reading', '2000000', '--prior-reading', '1700000']); + expect(h.field(high.out, 'BUDGET', 'above_floor')).toBe('150000'); + expect(h.field(high.out, 'BUDGET', 'verdict')).toBe('delegate'); + const bad = h.sb(['start', '--record', h.record(), '--token-cap', '60M', '--reading', '0', '--delegate', 'sometimes']); + expect(bad.code).toBe(2); + expect(bad.err).toContain("--delegate 'sometimes' is not auto, never or measured"); + }); + test('--call-floor overrides the calibrated floor and is recorded in the ledger', () => { const h = fresh(); const s = start(h, ['--token-cap', '60M', '--reading', '218000', '--prior-reading', '98000', '--call-floor', '0']);