mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-04 15:57:33 +01:00
feat(autoplan): generated DRIVER.md, the compact entry for a parent driving gstack-autoplan
autoplan/DRIVER.md (from autoplan/DRIVER.md.tmpl) is what a parent agent on any host reads instead of autoplan/SKILL.md: phase order and scope flags, the next/submit/ answer/revise/export loop with the plan-specified submit --record/--attest/ --unavailable, revise --incorporates/--withdraw and next --scaffold, the reviewer RESULT FORMAT, the gate grammar including all as-is, the run files, the result codes and the coordinator contract, every list rendered from code (117 lines). gen:skill-docs emits it as a Claude asset, so normal, out-dir and dry-run inventories and the freshness check cover it. gstack-autoplan --help and the first next print DRIVER: <root>/autoplan/DRIVER.md, resolved through an install root's recorded .source-path (new lib/source-root.ts) (field wave item 13). Intentional pin change: test/gen-skill-docs-checks.test.ts asset inventory gains autoplan/DRIVER.md. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
2f65e8a810
commit
17f69f4dae
12 files changed
+410
-4
No files matched your search
@@ -878,6 +878,15 @@ function processTemplate(tmplPath: string, host: Host, options: RenderOptions):
|
||||
return { outputPath, content, symlinkLoop, metadata };
|
||||
}
|
||||
|
||||
/** autoplan/DRIVER.md (field wave item 13): one host-neutral render of autoplan/DRIVER.md.tmpl through the same resolvers. */
|
||||
export const AUTOPLAN_DRIVER_RELPATH = 'autoplan/DRIVER.md';
|
||||
export function renderAutoplanDriver(options: RenderOptions): string {
|
||||
const tmplPath = path.join(ROOT, `${AUTOPLAN_DRIVER_RELPATH}.tmpl`);
|
||||
const tmpl = fs.readFileSync(tmplPath, 'utf-8');
|
||||
const ctx = buildContext(tmpl, tmplPath, 'claude', options, 'autoplan');
|
||||
return GENERATED_HEADER.replace('{{SOURCE}}', `${AUTOPLAN_DRIVER_RELPATH}.tmpl`) + resolvePlaceholders(tmpl, ctx, getHostConfig('claude'), `${AUTOPLAN_DRIVER_RELPATH}.tmpl`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate one on-demand section file (`<skill>/sections/<name>.md.tmpl` →
|
||||
* `<name>.md`). Sections are BODY FRAGMENTS — no frontmatter, no catalog trim,
|
||||
@@ -1061,6 +1070,9 @@ export async function runGeneration(settings: GenerationOptions = {}): Promise<G
|
||||
generateDesignChecklistMd(), 'asset', host);
|
||||
emit(path.join(options.outputRoot, DOM_DUMP_FILE), DOM_DUMP_SCRIPT + '\n', 'asset', host);
|
||||
}
|
||||
if (host === 'claude' && includesSkill(hostConfig, 'autoplan')) {
|
||||
emit(path.join(options.outputRoot, AUTOPLAN_DRIVER_RELPATH), renderAutoplanDriver(options), 'asset', host);
|
||||
}
|
||||
|
||||
if (host === 'openclaw') {
|
||||
for (const variant of ['lite', 'full', 'plan'] as const) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* autoplan/DRIVER.md resolvers (field wave item 13): the compact entry a parent
|
||||
* agent on any host reads to drive an unattended /autoplan run through
|
||||
* `gstack-autoplan`, instead of autoplan/SKILL.md. Every list comes from code:
|
||||
* the phase order (PHASES), the scope flags (SCOPE_FLAG_TOKENS), the run
|
||||
* directory files (FILES), the reviewer RESULT FORMAT, the result codes
|
||||
* (RESULT_CODES). The command lines and the gate reply forms are stated once
|
||||
* here; test/autoplan-driver.test.ts checks them against `gstack-autoplan
|
||||
* --help` and the gate parser.
|
||||
*/
|
||||
import { FILES } from '../../lib/autoplan-state';
|
||||
import { PHASES, RESULT_FORMAT } from '../../lib/autoplan-prompts';
|
||||
import { SCOPE_FLAG_TOKENS } from '../../lib/autoplan-scope';
|
||||
import { RESULT_CODES } from '../../lib/result-codes';
|
||||
import type { ResolverFn } from './types';
|
||||
|
||||
/** One entry per command line the loop runs, as the field-wave plan specifies them. */
|
||||
export const AUTOPLAN_DRIVER_COMMANDS: Array<{ cmd: string; does: string }> = [
|
||||
{ cmd: 'gstack-autoplan next --out <dir> --plan <plan.md> [scope flags] [--light] [--spend-cap <usd>] [--scaffold]', does: 'first call: init the run, print PHASE:, SCOPE:, DRIVER: and one ATTEMPT: per voice' },
|
||||
{ cmd: 'gstack-autoplan next --out <dir> [--redispatch <attempt>]', does: 'later calls: open the next phase or reprint open attempts; never redispatches on its own' },
|
||||
{ cmd: 'gstack-autoplan submit --out <dir> --phase <p> --voice native|outside --record <file.md> --model <id> [--attempt <id>]', does: 'bind a free-form review record (canonical fence, one findings table or list, or `No findings.`)' },
|
||||
{ cmd: 'gstack-autoplan submit ... --record <file.md> --attest "<reason>" --attempt <id>', does: 'bind a record with no INPUT: line as receipt: attested (a wrong INPUT: always refuses)' },
|
||||
{ cmd: 'gstack-autoplan submit --out <dir> --phase <p> --voice native|outside --result <file> --model <id> --attempt <id>', does: 'bind a canonical result (the RESULT FORMAT below)' },
|
||||
{ cmd: 'gstack-autoplan submit --out <dir> --phase <p> --voice outside --unavailable|--failed "<reason>"', does: 'close the phase native-only when no outside voice can run' },
|
||||
{ cmd: 'gstack-autoplan answer --out <dir> --gate-rev <n> --reply "<reply>"', does: 'answer the gate with the reply grammar below' },
|
||||
{ cmd: 'gstack-autoplan revise --out <dir> --plan <plan v2> --incorporates <decision ids>', does: 'bind the amended plan; reopens every phase whose reviewed plan changed, Eng last' },
|
||||
{ cmd: 'gstack-autoplan revise --out <dir> --withdraw <decision id>', does: 'drop a pending amendment instead of applying it' },
|
||||
{ cmd: 'gstack-autoplan export --out <dir>', does: 'write the artifact set; then `gstack-artifact validate <dir>/run.json`' },
|
||||
{ cmd: 'gstack-autoplan status|resume --out <dir>', does: 'read the manifest; take ownership after a crash (EXECUTION_UNKNOWN lists unfinished attempts)' },
|
||||
];
|
||||
|
||||
/** The reply grammar `gstack-gate` parses, one form per line. */
|
||||
export const GATE_REPLY_FORMS: Array<{ reply: string; means: string }> = [
|
||||
{ reply: 'all', means: 'every item takes its recommendation (an agreed change prints APPLY: and needs revise)' },
|
||||
{ reply: 'all as-is', means: 'keep every direction; no plan change' },
|
||||
{ reply: 'd3b uc1a', means: 'one <id><option> token per item you set; unanswered auto items take their default' },
|
||||
{ reply: 'all except d3b', means: 'recommendations, with the listed overrides' },
|
||||
];
|
||||
|
||||
/** Result codes the loop can print, in the order a run meets them. */
|
||||
export const DRIVER_CODES = ['RUN_NOT_INITIALIZED', 'PLAN_SECTIONS_INVALID', 'RUN_LOCKED', 'EXECUTION_UNKNOWN', 'SPEND_CAP_EXCEEDED', 'PHASE_NOT_AWAITING', 'ATTEMPT_MISMATCH',
|
||||
'RESULT_RECEIPT_MISSING', 'RESULT_FINDINGS_MISSING', 'MODEL_FAMILY_CONFLICT', 'CONSENSUS_MISSING', 'GATE_REV_STALE', 'GATE_REPLY_UNPARSED', 'PLAN_AMEND_PENDING', 'GSTACK_VERSION_CHANGED'] as const;
|
||||
/** Fix text for codes the plan specifies and lane B-core adds to lib/result-codes.ts; the table's own fix wins once it exists. */
|
||||
const PENDING_FIX: Record<string, string> = {
|
||||
PLAN_SECTIONS_INVALID: 'the plan needs one `## Implementation plan` and one `## Review record`; rerun `next --scaffold` to wrap a plan that has neither',
|
||||
PLAN_AMEND_PENDING: 'edit the plan for each APPLY: line and run `revise --incorporates <ids>`, or `revise --withdraw <id>`',
|
||||
GSTACK_VERSION_CHANGED: 'notice only: the run was started on another gstack; forward migrations ran and were journaled',
|
||||
};
|
||||
|
||||
export const generateAutoplanDriverPhases: ResolverFn = () => [
|
||||
`Order: ${PHASES.join(' → ')}. Design runs only with UI scope, DX only with DX scope, Eng always and last.`,
|
||||
`Scope flags on the first \`next\`: ${SCOPE_FLAG_TOKENS.map(t => `\`${t}\``).join(', ')}. \`--developer-tool\` and \`--agent-primary\` only enable DX.`,
|
||||
].join('\n');
|
||||
|
||||
export const generateAutoplanDriverCommands: ResolverFn = () =>
|
||||
AUTOPLAN_DRIVER_COMMANDS.map(c => `- \`${c.cmd}\`\n ${c.does}.`).join('\n');
|
||||
|
||||
export const generateAutoplanDriverGate: ResolverFn = () =>
|
||||
GATE_REPLY_FORMS.map(f => `- \`${f.reply}\`: ${f.means}.`).join('\n');
|
||||
|
||||
export const generateAutoplanDriverFiles: ResolverFn = () =>
|
||||
`Runner state: ${Object.values(FILES).map(f => `\`${f}\``).join(', ')}.`;
|
||||
|
||||
export const generateAutoplanDriverResultFormat: ResolverFn = () =>
|
||||
RESULT_FORMAT('<phase>', '<snapshot sha256>', '<result file>').replace(/^\n---\n/, '').trimEnd();
|
||||
|
||||
export const generateAutoplanDriverCodes: ResolverFn = () => DRIVER_CODES.map(code => {
|
||||
const row = (RESULT_CODES as Record<string, { fix: string }>)[code];
|
||||
return `- \`${code}\`: ${row?.fix ?? PENDING_FIX[code]}`;
|
||||
}).join('\n');
|
||||
@@ -52,6 +52,7 @@ import { generateImplementerDispatchRules } from './dispatch-rules';
|
||||
import { generatePlanRealityRows } from './plan-reality';
|
||||
import { generateSidewaysSweep, generateFlakeFixNumber } from './sideways';
|
||||
import { generateCoordinatorContract } from './coordinator-contract';
|
||||
import { generateAutoplanDriverCodes, generateAutoplanDriverCommands, generateAutoplanDriverFiles, generateAutoplanDriverGate, generateAutoplanDriverPhases, generateAutoplanDriverResultFormat } from './autoplan-driver';
|
||||
import { generateQAScope, generateQAExploratory, generateQAFunctional, generateQAResource, generateQAReview, generateQAReviewPreflight, generateQAMethodReads } from './qa';
|
||||
|
||||
export const RESOLVERS: Record<string, ResolverFn> = {
|
||||
@@ -176,6 +177,12 @@ export const RESOLVERS: Record<string, ResolverFn> = {
|
||||
TASKS_SECTION_EMIT: generateTasksSectionEmit,
|
||||
TASKS_SECTION_AGGREGATE: generateTasksSectionAggregate,
|
||||
COORDINATOR_CONTRACT: generateCoordinatorContract,
|
||||
AUTOPLAN_DRIVER_PHASES: generateAutoplanDriverPhases,
|
||||
AUTOPLAN_DRIVER_COMMANDS: generateAutoplanDriverCommands,
|
||||
AUTOPLAN_DRIVER_RESULT_FORMAT: generateAutoplanDriverResultFormat,
|
||||
AUTOPLAN_DRIVER_GATE: generateAutoplanDriverGate,
|
||||
AUTOPLAN_DRIVER_FILES: generateAutoplanDriverFiles,
|
||||
AUTOPLAN_DRIVER_CODES: generateAutoplanDriverCodes,
|
||||
SECTION,
|
||||
SECTION_INDEX,
|
||||
};
|
||||
Reference in new issue
Block a user