Files
gstack/scripts/resolvers/index.ts
T
Garry TanandClaude Fable 5.1 444f8feff8 fix: pre-landing review fixes for the Aside-first branch
Review army + adversarial passes (Claude and Codex) on the merged branch:

setup
- _prune_stale_generated scans the host dirs too (the generator already
  removed the render before setup ran, so the host branch was dead), skips
  symlinks in the render tree (rm -rf on a slash-terminated link empties its
  target), removes a host symlink only when it resolves into gstack, cleans a
  bannered real dir through _cleanup_weak_dir, recognizes frontmatter-renamed
  skills, and logs through log. The always-run codex render passes every host
  dir that may link to it.
- NEEDS_BUILD checks all three binaries (with $_EXE) and lib/ sources; the
  browser hint and the bootstrap summary honor GSTACK_SKIP_ASIDE, treat a
  requested skip as a request, and derive one skill list.

lib/aside-render.ts + bin/gstack-render.ts
- The loopback server carries a per-render secret path, checks containment on
  the real path (symlink escapes are 403), and rejects malformed encoding.
- Inline eval results are one base64 line, so page text cannot forge
  ASIDE_DIR= or the sentinel; the last ASIDE_DIR wins.
- runProc escalates SIGTERM to SIGKILL, bounds every wait, and clears every
  timer (an uncleared one kept gstack-render alive after printing OK).
- renderTmpDir refuses a shared /tmp name owned by someone else; the work dir
  and server are created inside try; goto's budget follows the render budget.
- probeAside classifies a present-but-failing CLI as ASIDE_NOT_RUNNING like
  the skills' bash probe; render() retries on gstack's own browser when Aside
  could not start or its private CDP bridge is gone (never on a page error
  or a timeout of a running script); the CLI reports the engine that actually
  rendered, exits 0 on --help, rejects non-numeric flags, documents
  --wait-timeout, fences EVAL/PAGE_ERRORS as untrusted content, and names the
  daemon's cookie-import JS lock remedy.
- The browse path passes --scale only when asked (a scale change rebuilds
  the daemon context) and restores the viewport after a sized screenshot.

resolvers / templates
- The bash probe honors GSTACK_SKIP_ASIDE and has a perl deadline on stock
  macOS; .local is no longer LOCAL (mDNS); same-origin filters compare parsed
  origins; link status is HEAD-checked only on LOCAL targets; every
  aside exec goes through the receipted _aside_exec prelude
  ({{ASIDE_EXEC_PRELUDE}}), including nine template blocks that called it
  bare; the design sketch and diagram staging use private directories.
- The generator prunes only bannered renders and never a host whose
  generation failed.

Docs, stale comments and dead code cleaned; goldens re-rendered; tests
updated and added for every behavior above.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 07:23:26 +00:00

111 lines
6.1 KiB
TypeScript

/**
* RESOLVERS record — maps {{PLACEHOLDER}} names to generator functions
* or gated entries.
*
* Each resolver takes a TemplateContext and returns the replacement string.
* Resolvers may be either a bare function (always fires) or a gated entry
* ({ resolve, appliesTo }) where appliesTo can return false to skip the
* resolver for a given skill. See ./types.ts: ResolverEntry.
*
* Most resolvers don't need a gate — the {{NAME}} placeholder system is
* already conditional at the template level (the resolver only fires for
* skills that reference it). Use a gate when you want a structural
* guardrail that says "this placeholder is meaningful only in skills X, Y, Z"
* even if someone later adds {{NAME}} to skill W.
*/
import type { TemplateContext, ResolverFn } from './types';
// Domain modules
import { generatePreamble } from './preamble';
import { generateTestFailureTriage } from './preamble';
import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup, generateDesignShotgunLoop, generateTasteProfile, generateUXPrinciples } from './design';
import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip } from './testing';
import { generateReviewDashboard, generatePlanFileReviewReport, generateExitPlanModeGate, generateAntiShortcutClause, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generateCodexDocReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec, generateScopeDrift, generateCrossReviewDedup } from './review';
import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer, generateChangelogWorkflow, generateCodexWebSearchFlag, generateSetupCommand } from './utility';
import { generateLearningsSearch, generateLearningsLog } from './learnings';
import { generateConfidenceCalibration } from './confidence';
import { generateInvokeSkill } from './composition';
import { generateReviewArmy } from './review-army';
import { generateDxFramework } from './dx';
import { generateGBrainContextLoad, generateGBrainSaveResults, generateBrainPreflight, generateBrainCacheRefresh, generateBrainWriteBack } from './gbrain';
import { generateTasksSectionEmit, generateTasksSectionAggregate } from './tasks-section';
import { SECTION, SECTION_INDEX } from './sections';
import { generateRedactInvocationBlock } from './redact-doc';
import { FOREGROUND_DISPATCH_NOTE } from './constants';
import { generateThirdPartyActions } from './third-party-actions';
import { generateAsideSetup, generateAsideCookbook, generateAsideResearch, generateUntrustedContentWarning, asideExecPrelude } from './aside';
import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup, generateBrowseFallback } from './browse';
import { generateDesignDocDiscovery } from './design-doc-discovery';
export const RESOLVERS: Record<string, ResolverFn> = {
SLUG_EVAL: generateSlugEval,
SLUG_SETUP: generateSlugSetup,
CODEX_WEB_SEARCH_FLAG: generateCodexWebSearchFlag,
REDACT_INVOCATION_BLOCK: generateRedactInvocationBlock,
THIRD_PARTY_ACTIONS: generateThirdPartyActions,
DESIGN_DOC_DISCOVERY: generateDesignDocDiscovery,
UNTRUSTED_CONTENT_WARNING: generateUntrustedContentWarning,
COMMAND_REFERENCE: generateCommandReference,
SNAPSHOT_FLAGS: generateSnapshotFlags,
BROWSE_SETUP: generateBrowseSetup,
BROWSE_FALLBACK: generateBrowseFallback,
PREAMBLE: generatePreamble,
ASIDE_SETUP: generateAsideSetup,
ASIDE_COOKBOOK: generateAsideCookbook,
ASIDE_RESEARCH: generateAsideResearch,
ASIDE_EXEC_PRELUDE: asideExecPrelude,
BASE_BRANCH_DETECT: generateBaseBranchDetect,
QA_METHODOLOGY: generateQAMethodology,
DESIGN_METHODOLOGY: generateDesignMethodology,
DESIGN_HARD_RULES: generateDesignHardRules,
UX_PRINCIPLES: generateUXPrinciples,
DESIGN_OUTSIDE_VOICES: generateDesignOutsideVoices,
DESIGN_REVIEW_LITE: generateDesignReviewLite,
REVIEW_DASHBOARD: generateReviewDashboard,
PLAN_FILE_REVIEW_REPORT: generatePlanFileReviewReport,
EXIT_PLAN_MODE_GATE: generateExitPlanModeGate,
ANTI_SHORTCUT_CLAUSE: generateAntiShortcutClause,
TEST_BOOTSTRAP: generateTestBootstrap,
TEST_COVERAGE_AUDIT_PLAN: generateTestCoverageAuditPlan,
TEST_COVERAGE_AUDIT_SHIP: generateTestCoverageAuditShip,
TEST_FAILURE_TRIAGE: generateTestFailureTriage,
SPEC_REVIEW_LOOP: generateSpecReviewLoop,
DESIGN_SKETCH: generateDesignSketch,
DESIGN_SETUP: generateDesignSetup,
DESIGN_MOCKUP: generateDesignMockup,
DESIGN_SHOTGUN_LOOP: generateDesignShotgunLoop,
BENEFITS_FROM: generateBenefitsFrom,
CODEX_SECOND_OPINION: generateCodexSecondOpinion,
ADVERSARIAL_STEP: generateAdversarialStep,
SCOPE_DRIFT: generateScopeDrift,
DEPLOY_BOOTSTRAP: generateDeployBootstrap,
CODEX_PLAN_REVIEW: generateCodexPlanReview,
CODEX_DOC_REVIEW: generateCodexDocReview,
PLAN_COMPLETION_AUDIT_SHIP: generatePlanCompletionAuditShip,
PLAN_COMPLETION_AUDIT_REVIEW: generatePlanCompletionAuditReview,
PLAN_VERIFICATION_EXEC: generatePlanVerificationExec,
CO_AUTHOR_TRAILER: generateCoAuthorTrailer,
SETUP_COMMAND: generateSetupCommand,
LEARNINGS_SEARCH: generateLearningsSearch,
LEARNINGS_LOG: generateLearningsLog,
CONFIDENCE_CALIBRATION: generateConfidenceCalibration,
INVOKE_SKILL: generateInvokeSkill,
CHANGELOG_WORKFLOW: generateChangelogWorkflow,
REVIEW_ARMY: generateReviewArmy,
CROSS_REVIEW_DEDUP: generateCrossReviewDedup,
DX_FRAMEWORK: generateDxFramework,
TASTE_PROFILE: generateTasteProfile,
BIN_DIR: (ctx) => ctx.paths.binDir,
FOREGROUND_DISPATCH_NOTE: () => FOREGROUND_DISPATCH_NOTE,
GBRAIN_CONTEXT_LOAD: generateGBrainContextLoad,
GBRAIN_SAVE_RESULTS: generateGBrainSaveResults,
BRAIN_PREFLIGHT: generateBrainPreflight,
BRAIN_CACHE_REFRESH: generateBrainCacheRefresh,
BRAIN_WRITE_BACK: generateBrainWriteBack,
TASKS_SECTION_EMIT: generateTasksSectionEmit,
TASKS_SECTION_AGGREGATE: generateTasksSectionAggregate,
SECTION,
SECTION_INDEX,
};