mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
feat(ship): compact host-neutral ship/DRIVER.md, rendered as a claude asset beside autoplan/DRIVER.md
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
0c6dff70a9
commit
2c866a50b0
8 files changed
+298
-2
No files matched your search
@@ -901,6 +901,15 @@ export function renderAutoplanDriver(options: RenderOptions): string {
|
||||
return GENERATED_HEADER.replace('{{SOURCE}}', `${AUTOPLAN_DRIVER_RELPATH}.tmpl`) + resolvePlaceholders(tmpl, ctx, getHostConfig('claude'), `${AUTOPLAN_DRIVER_RELPATH}.tmpl`, options);
|
||||
}
|
||||
|
||||
/** ship/DRIVER.md (GSTA-167 PR 5): the compact /ship entry for a parent agent, rendered the same way. */
|
||||
export const SHIP_DRIVER_RELPATH = 'ship/DRIVER.md';
|
||||
export function renderShipDriver(options: RenderOptions): string {
|
||||
const tmplPath = path.join(ROOT, `${SHIP_DRIVER_RELPATH}.tmpl`);
|
||||
const tmpl = fs.readFileSync(tmplPath, 'utf-8');
|
||||
const ctx = buildContext(tmpl, tmplPath, 'claude', options, 'ship');
|
||||
return GENERATED_HEADER.replace('{{SOURCE}}', `${SHIP_DRIVER_RELPATH}.tmpl`) + resolvePlaceholders(tmpl, ctx, getHostConfig('claude'), `${SHIP_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,
|
||||
@@ -1092,6 +1101,9 @@ export async function runGeneration(settings: GenerationOptions = {}): Promise<G
|
||||
if (host === 'claude' && includesSkill(hostConfig, 'autoplan') && templates.some(t => t.tmpl === 'autoplan/SKILL.md.tmpl')) {
|
||||
emit(path.join(options.outputRoot, AUTOPLAN_DRIVER_RELPATH), renderAutoplanDriver(options), 'asset', host);
|
||||
}
|
||||
if (host === 'claude' && includesSkill(hostConfig, 'ship') && templates.some(t => t.tmpl === 'ship/SKILL.md.tmpl')) {
|
||||
emit(path.join(options.outputRoot, SHIP_DRIVER_RELPATH), renderShipDriver(options), 'asset', host);
|
||||
}
|
||||
|
||||
if (host === 'openclaw') {
|
||||
for (const variant of ['lite', 'full', 'plan'] as const) {
|
||||
|
||||
@@ -54,6 +54,7 @@ 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 { generateShipDriverSegmentCall, generateShipDriverStops } from './ship-driver';
|
||||
import { generateQAScope, generateQAExploratory, generateQAFunctional, generateQAResource, generateQAReview, generateQAReviewPreflight, generateQAMethodReads } from './qa';
|
||||
import { generateVoiceTriggerRouting } from './voice-triggers';
|
||||
import { generateTranscriptDiscovery, generateTranscriptSources } from './transcripts';
|
||||
@@ -191,6 +192,8 @@ export const RESOLVERS: Record<string, ResolverFn> = {
|
||||
AUTOPLAN_DRIVER_GATE: generateAutoplanDriverGate,
|
||||
AUTOPLAN_DRIVER_FILES: generateAutoplanDriverFiles,
|
||||
AUTOPLAN_DRIVER_CODES: generateAutoplanDriverCodes,
|
||||
SHIP_DRIVER_STOPS: generateShipDriverStops,
|
||||
SHIP_DRIVER_SEGMENT_CALL: generateShipDriverSegmentCall,
|
||||
SECTION,
|
||||
SECTION_INDEX,
|
||||
TRANSCRIPT_SOURCES: generateTranscriptSources,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* ship/DRIVER.md resolvers (GSTA-167 PR 5): the compact entry a parent agent
|
||||
* reads to run an unattended /ship, instead of ship/SKILL.md plus its sections.
|
||||
* The phases live in ship/DRIVER.md.tmpl; this file owns the one list a reader
|
||||
* must trust most: which heading each STOP sends the parent to. test/ship-driver.test.ts
|
||||
* resolves every anchor against the generated files.
|
||||
*/
|
||||
import { DELEGATE_SEGMENT_CALL } from '../../lib/ship-budget';
|
||||
import type { ResolverFn } from './types';
|
||||
|
||||
/** Each STOP (or read-when line) a driver run can meet, and the one heading it reads, relative to <root>/ship/. */
|
||||
export const SHIP_DRIVER_STOPS: Array<{ when: string; read: string }> = [
|
||||
{ when: '`merge conflicts`', read: 'SKILL.md#step-3-merge-the-base-branch-before-tests' },
|
||||
{ when: '`NEXT: carry route`', read: 'SKILL.md#step-1-pre-flight' },
|
||||
{ when: '`DISTRIBUTION: candidates=`', read: 'SKILL.md#step-2-distribution-pipeline-check' },
|
||||
{ when: '`APPLE:` with a store ask', read: 'sections/apple-release.md#apple-app-store-release' },
|
||||
{ when: '`pregate fail` or `incomplete`', read: 'sections/tests.md#step-5-run-tests-on-merged-code' },
|
||||
{ when: 'a failed or zero-run lane', read: 'sections/tests.md#test-failure-ownership-triage' },
|
||||
{ when: 'a red eval or a failed free-suite shard', read: 'sections/measure.md#measure-then-fix-loop-a-red-paid-eval-or-a-failed-free-suite-shard' },
|
||||
{ when: 'Greptile found', read: 'sections/greptile-early.md#step-65-early-greptile-pr-after-the-free-tests-pass' },
|
||||
{ when: 'coverage below target, a failed audit', read: 'sections/test-coverage.md#step-7-test-coverage-audit' },
|
||||
{ when: 'plan items NOT DONE or UNVERIFIABLE', read: 'sections/plan-completion.md#gate-logic' },
|
||||
{ when: '`REVIEW_COVERAGE_INCOMPLETE` or `NEXT: stop`', read: 'sections/review-army.md#decide-whether-to-repeat-step-9' },
|
||||
{ when: 'a `REVIEW_SCOPE_*` refusal, the review-log row', read: 'sections/review-army.md#step-94-fix-first-and-persistence' },
|
||||
{ when: 'a required probe not passed', read: 'sections/review-army.md#step-921-exploratory-qa-before-fix-first' },
|
||||
{ when: 'native adversarial incomplete, queued fixes', read: 'sections/adversarial.md#finish-the-adversarial-phase' },
|
||||
{ when: 'Step 11.5 mismatch', read: 'SKILL.md#step-115-bind-the-reviews' },
|
||||
{ when: 'a `version` STOP', read: 'SKILL.md#step-12-version-bump-auto-decide' },
|
||||
{ when: '`Documentation: blocked` or a `DOCS_CAP:` over', read: 'sections/documentation.md#blocked-recovery' },
|
||||
{ when: 'finish: changed since review', read: 'SKILL.md#2-choose-the-change-route' },
|
||||
{ when: 'finish: documentation inputs changed', read: 'SKILL.md#3-resolve-documentation-freshness' },
|
||||
{ when: 'finish: build, lane or pregate STOP', read: 'SKILL.md#4-verify-the-frozen-candidate' },
|
||||
{ when: 'a push STOP', read: 'SKILL.md#step-17-push' },
|
||||
{ when: 'unattended, no open PR at HEAD', read: 'sections/handoff.md#step-18-h-handoff-without-a-pr-automation-lanes' },
|
||||
{ when: 'a receipt write or `gstack-post` refusal', read: 'sections/pr-body.md#compose-the-body-then-publish-through-gstack-post' },
|
||||
{ when: '`verdict=stop` or `DELEGATE:`', read: 'sections/budget-handoff.md#budget-and-context-handoff-read-when-a-cap-is-set-or-verdictdelegate-prints' },
|
||||
{ when: '`QUEUE_RECEIPT_STALE`', read: '../docs/ship-policy.md#ship-before-ready' },
|
||||
];
|
||||
|
||||
export const generateShipDriverSegmentCall: ResolverFn = () => `${DELEGATE_SEGMENT_CALL / 1000}k`;
|
||||
|
||||
export const generateShipDriverStops: ResolverFn = () =>
|
||||
SHIP_DRIVER_STOPS.map(s => `- ${s.when}: \`${s.read}\``).join('\n');
|
||||
Reference in new issue
Block a user