mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
v1.91.299.0 fix(owner-brief): Captain brief resolves rec like gstack-gate render when a gate carries decisions (#3546) (#3549)
* fix(owner-brief): the Captain brief resolves rec through parseReply, so carried decisions match gstack-gate render (#3546) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(owner-brief): the Captain brief keeps answers already recorded at this gate, as answer --reply rec does Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(owner-brief): the do-it line says every choice when rec keeps earlier answers Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
415e343b52
commit
4c745eec10
6 files changed
+117
-14
No files matched your search
+15
-10
@@ -13,8 +13,8 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { gateCost, gateCostSentence } from './autoplan-gate-cost';
|
||||
import { recHash, type GateItem } from './gate-list';
|
||||
import { gateCost, gateCostSentence, priorChoices } from './autoplan-gate-cost';
|
||||
import { parseReply, recHash, type GateItem } from './gate-list';
|
||||
import { renderUrgentBlock } from './headless-artifacts';
|
||||
import { findSection, firstTitle } from './markdown-sections';
|
||||
import type { BriefInputs } from './owner-brief';
|
||||
@@ -75,7 +75,12 @@ export function renderCaptainBrief(inputs: BriefInputs, o: { outDir: string }):
|
||||
const missing = sections.filter(s => !s.found || !s.found.body.trim()).map(s => s.title);
|
||||
const urgent = inputs.findings.some(r => r.urgent === true || r.outside_plan === true) ? [renderUrgentBlock(inputs.findings).trimEnd(), ''] : [];
|
||||
const items = list.items.filter(i => i.id !== 'p1');
|
||||
const recText = (i: GateItem) => plainOption(i.options.find(x => x.key === i.recommended)?.text ?? i.recommended);
|
||||
const prior = priorChoices(o.outDir, list.gate_rev);
|
||||
const rec = parseReply('rec', list, undefined, prior).choices;
|
||||
const recKey = (i: GateItem) => rec.find(c => c.id === i.id)?.chosen ?? i.recommended;
|
||||
const recOption = (i: GateItem) => i.options.find(x => x.key === recKey(i));
|
||||
const kept = items.filter(i => rec.find(c => c.id === i.id)?.prior);
|
||||
const keptNote = (i: GateItem) => i.carried?.chosen === recKey(i) ? ` (your answer at gate_rev ${i.carried.gate_rev}, carried forward)` : ' (your answer at this gate)';
|
||||
|
||||
let header: string[];
|
||||
if (!pending) {
|
||||
@@ -84,8 +89,8 @@ export function renderCaptainBrief(inputs: BriefInputs, o: { outDir: string }):
|
||||
header = [`**Not ready for approval:** the plan "${title}" is missing ${missing.join(', ')}. The thread will add them and send this again; do not approve from this draft.`];
|
||||
} else {
|
||||
header = [
|
||||
`**Decision needed:** approve the plan "${title}". Reply **rec** to take every recommendation (recommended), or say in words what to change.`,
|
||||
`${items.length ? `${items.length} choice${items.length === 1 ? '' : 's'} below; rec takes the recommended answer for each.` : 'No open choices besides the approval itself.'} Nothing is built until you answer.`,
|
||||
`**Decision needed:** approve the plan "${title}". Reply **rec** to ${kept.length ? 'keep the answers you already gave and take every other recommendation' : 'take every recommendation'} (recommended), or say in words what to change.`,
|
||||
`${items.length ? `${items.length} choice${items.length === 1 ? '' : 's'} below; rec ${kept.length ? `keeps the ${kept.length} you already answered and takes the recommended answer for the rest` : 'takes the recommended answer for each'}.` : 'No open choices besides the approval itself.'} Nothing is built until you answer.`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -94,15 +99,15 @@ export function renderCaptainBrief(inputs: BriefInputs, o: { outDir: string }):
|
||||
if (!pending) need.push('Nothing. This gate is answered.', '');
|
||||
else {
|
||||
need.push(`One decision: approve the plan.`, '');
|
||||
need.push(`- **rec (recommended):** ${p1 ? plainOption(p1.options.find(x => x.key === p1.recommended)?.text ?? 'approve') : 'approve'}${items.length ? ', taking these recommendations:' : '.'}`);
|
||||
items.forEach((i, n) => need.push(` ${n + 1}. ${stripRef(i.title)}: ${recText(i)}${i.options.find(x => x.key === i.recommended)?.changes_plan ? ' (changes the plan)' : ''}.`));
|
||||
need.push(`- **rec (recommended):** ${p1 ? plainOption(recOption(p1)?.text ?? 'approve') : 'approve'}${items.length ? `, taking these ${kept.length ? 'answers' : 'recommendations'}:` : '.'}`);
|
||||
items.forEach((i, n) => need.push(` ${n + 1}. ${stripRef(i.title)}: ${plainOption(recOption(i)?.text ?? recKey(i))}${kept.includes(i) ? keptNote(i) : recOption(i)?.changes_plan ? ' (changes the plan)' : ''}.`));
|
||||
const example = items.find(i => i.options.length > 1);
|
||||
const alt = example?.options.find(x => x.key !== example.recommended);
|
||||
const alt = example?.options.find(x => x.key !== recKey(example));
|
||||
need.push(`- **Change something:** say which choice and what you want instead, in words${example && alt ? ` (for example "${clip(stripRef(example.title), 60)}: ${plainOption(alt.text)}")` : ''}. A change that alters the plan reopens its review before anything is built.`);
|
||||
need.push('- **Reject:** nothing is built.', '');
|
||||
if (!missing.length) need.push(`- **do it:** rec at full scope: every recommendation above, the follow-ons the plan names (docs, trains, cleanups) included, carried through to done with no further ask.`, '', 'Reply: **rec**, or **do it**', '');
|
||||
if (!missing.length) need.push(`- **do it:** rec at full scope: every ${kept.length ? 'choice' : 'recommendation'} above, the follow-ons the plan names (docs, trains, cleanups) included, carried through to done with no further ask.`, '', 'Reply: **rec**, or **do it**', '');
|
||||
}
|
||||
const tokens = list.items.map(i => `${i.id}${i.recommended}`).join(' ');
|
||||
const tokens = rec.map(c => `${c.id}${c.chosen}`).join(' ');
|
||||
const trailer = `_For the relaying agent, not the person:_ run ${list.run}, gate_rev=${list.gate_rev}, rec_hash=${hash}; rec resolves to ${tokens}; plan ${plan.file ?? '(missing)'} (sha256 ${plan.sha}). Answer with ` +
|
||||
`\`gstack-autoplan answer --out ${o.outDir} --gate-rev ${list.gate_rev} --reply rec --rec-hash ${hash} --by agent:<id> --owner-reply "<their exact words>"\`, then \`gstack-approvals import --autoplan ${o.outDir} --by <name>\`.`;
|
||||
// The price sits above the trailer: the relaying agent sends everything above that line, so the person approving sees it.
|
||||
|
||||
Reference in new issue
Block a user