mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-21 04:10:47 +02:00
feat(gstack2): infer execution profiles
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import type { ExecutionProfile } from './types';
|
||||
|
||||
export interface ExecutionProfileContract {
|
||||
profile: ExecutionProfile;
|
||||
inferWhen: string;
|
||||
mandatoryModules: string;
|
||||
legalSkips: string;
|
||||
artifacts: string;
|
||||
allowedClaims: string;
|
||||
}
|
||||
|
||||
export const EXECUTION_PROFILE_CONTRACTS: Record<ExecutionProfile, ExecutionProfileContract> = {
|
||||
readiness: {
|
||||
profile: 'readiness',
|
||||
inferWhen: 'A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness.',
|
||||
mandatoryModules: 'Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question.',
|
||||
legalSkips: 'Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence.',
|
||||
artifacts: 'A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step.',
|
||||
allowedClaims: 'Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope.',
|
||||
},
|
||||
standard: {
|
||||
profile: 'standard',
|
||||
inferWhen: 'Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow.',
|
||||
mandatoryModules: 'Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks.',
|
||||
legalSkips: 'Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase.',
|
||||
artifacts: 'Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger.',
|
||||
allowedClaims: 'Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence.',
|
||||
},
|
||||
deep: {
|
||||
profile: 'deep',
|
||||
inferWhen: 'Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence.',
|
||||
mandatoryModules: 'Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates.',
|
||||
legalSkips: 'Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip.',
|
||||
artifacts: 'All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger.',
|
||||
allowedClaims: 'Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence.',
|
||||
},
|
||||
};
|
||||
|
||||
/** Infer from structured operating conditions, never prompt text. */
|
||||
export function inferExecutionProfile(
|
||||
signals: Record<string, unknown>,
|
||||
specialistDefault: ExecutionProfile,
|
||||
): ExecutionProfile {
|
||||
const highRisk = signals.risk === 'high'
|
||||
|| signals.blast_radius === 'broad'
|
||||
|| signals.irreversible === true
|
||||
|| signals.audit_focus === 'security'
|
||||
|| signals.audit_focus === 'deep'
|
||||
|| signals.evidence_need === 'independent'
|
||||
|| signals.failure_impact === 'critical'
|
||||
|| signals.mutation_scope === 'consequential'
|
||||
|| signals.external_mutation_authorized === true
|
||||
|| signals.deployment_state === 'production'
|
||||
|| signals.release_stage === 'approved-pr'
|
||||
|| signals.release_stage === 'landed';
|
||||
if (highRisk) return 'deep';
|
||||
|
||||
const boundedReadiness = signals.evidence_need === 'readiness'
|
||||
&& signals.scope === 'narrow'
|
||||
&& signals.irreversible !== true
|
||||
&& signals.mutation_scope !== 'consequential'
|
||||
&& signals.external_mutation_authorized !== true
|
||||
&& signals.deployment_state !== 'production'
|
||||
&& signals.release_stage !== 'approved-pr'
|
||||
&& signals.release_stage !== 'landed';
|
||||
if (boundedReadiness) return 'readiness';
|
||||
|
||||
return specialistDefault;
|
||||
}
|
||||
|
||||
export function renderExecutionProfiles(): string {
|
||||
const rows = (['readiness', 'standard', 'deep'] as const).map((name) => {
|
||||
const contract = EXECUTION_PROFILE_CONTRACTS[name];
|
||||
return `## ${name === 'readiness' ? 'Smoke/readiness' : name[0].toUpperCase() + name.slice(1)}\n\n- Infer when: ${contract.inferWhen}\n- Mandatory modules: ${contract.mandatoryModules}\n- Legal skips: ${contract.legalSkips}\n- Artifacts: ${contract.artifacts}\n- Claims: ${contract.allowedClaims}`;
|
||||
});
|
||||
return `# Inferred execution profiles\n\nChoose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior.\n\n${rows.join('\n\n')}\n`;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import * as path from 'path';
|
||||
import { BUG_FIX_OVERLAYS, overlaysForSource } from './bug-fix-overlays';
|
||||
import { renderBrowserProviderContract } from './browser-provider-contract';
|
||||
import { EXECUTION_RESULT_SCHEMA } from '../../runtime/execution-result.js';
|
||||
import { renderExecutionProfiles } from './execution-profiles';
|
||||
import { contractFor, DISPATCHERS, SOURCE_ASSIGNMENTS } from './assignments';
|
||||
import { SCENARIOS } from './scenarios';
|
||||
import { runDeterministicSemanticParity } from './semantic-parity';
|
||||
@@ -221,7 +222,7 @@ Before any substantive output, print these exact labels in this exact order. Res
|
||||
\`\`\`text
|
||||
Target: <concrete repository, product, URL, device, PR, or artifact>
|
||||
Mode: <selected top-level mode>
|
||||
Depth: <quick, standard, or deep>
|
||||
Depth: <readiness, standard, or deep>
|
||||
Mutation: <report-only or exact authorized mutation boundary>
|
||||
Active modules: <comma-separated internal specialist modules>
|
||||
Skipped modules: <comma-separated non-active mandatory modules with compact reasons>
|
||||
@@ -233,7 +234,7 @@ Web context: <none, optional, local-browser, or production>
|
||||
1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone.
|
||||
2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output.
|
||||
3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference.
|
||||
4. Read \`references/SHARED-JUDGMENT.md\` and \`references/AUTHORITY-POLICY.md\` for every invocation. Read \`references/RUNTIME.md\` before capability-dependent work and \`references/WEB-CONTEXT.md\` before public-web work.
|
||||
4. Read \`references/EXECUTION-PROFILES.md\`, \`references/SHARED-JUDGMENT.md\`, and \`references/AUTHORITY-POLICY.md\` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read \`references/RUNTIME.md\` before capability-dependent work and \`references/WEB-CONTEXT.md\` before public-web work.
|
||||
5. If an old asset path is unavailable, use \`references/ASSETS.md\`. If legacy prose invokes another retired skill, resolve it through \`references/COMPATIBILITY.md\` and stay inside these six dispatchers.
|
||||
6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user.
|
||||
7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy.
|
||||
@@ -567,6 +568,7 @@ function writeSharedContracts(): void {
|
||||
const bootstrap = fs.readFileSync(path.join(ROOT, 'runtime', 'runtime-bootstrap.mjs'));
|
||||
const browserSmoke = fs.readFileSync(path.join(ROOT, 'runtime', 'browser-provider-smoke.mjs'));
|
||||
for (const tree of TREE_NAMES) {
|
||||
write(path.join(ROOT, 'skills', tree, 'references', 'EXECUTION-PROFILES.md'), `${GENERATED}\n${renderExecutionProfiles()}`);
|
||||
write(path.join(ROOT, 'skills', tree, 'references', 'SHARED-JUDGMENT.md'), sharedJudgmentContract());
|
||||
write(path.join(ROOT, 'skills', tree, 'references', 'AUTHORITY-POLICY.md'), authorityPolicyContract());
|
||||
write(path.join(ROOT, 'skills', tree, 'references', 'WEB-CONTEXT.md'), webContextContract());
|
||||
|
||||
@@ -116,7 +116,7 @@ export interface StructuredHostResult {
|
||||
target: string;
|
||||
skill: PublicSkill;
|
||||
mode: string;
|
||||
depth: 'quick' | 'standard' | 'deep';
|
||||
depth: 'readiness' | 'standard' | 'deep';
|
||||
mutation: string;
|
||||
active_modules: string[];
|
||||
skipped_modules: string[];
|
||||
@@ -236,7 +236,7 @@ export const FINAL_OUTPUT_SCHEMA = {
|
||||
target: { type: 'string' },
|
||||
skill: { type: 'string', enum: [...PUBLIC_SKILLS] },
|
||||
mode: { type: 'string' },
|
||||
depth: { type: 'string', enum: ['quick', 'standard', 'deep'] },
|
||||
depth: { type: 'string', enum: ['readiness', 'standard', 'deep'] },
|
||||
mutation: { type: 'string' },
|
||||
active_modules: { type: 'array', items: { type: 'string' } },
|
||||
skipped_modules: { type: 'array', items: { type: 'string' } },
|
||||
@@ -625,7 +625,7 @@ export function validateStructuredResult(value: unknown): value is StructuredHos
|
||||
route && typeof route.target === 'string'
|
||||
&& PUBLIC_SKILLS.includes(route.skill)
|
||||
&& typeof route.mode === 'string'
|
||||
&& ['quick', 'standard', 'deep'].includes(route.depth)
|
||||
&& ['readiness', 'standard', 'deep'].includes(route.depth)
|
||||
&& typeof route.mutation === 'string'
|
||||
&& isStringArray(route.active_modules)
|
||||
&& isStringArray(route.skipped_modules)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DISPATCHERS, SOURCE_ASSIGNMENTS, assignmentBySource } from './assignments';
|
||||
import type { ScenarioFixture, TreeName } from './types';
|
||||
import { evaluateAuthorityPolicy, type AdversarialAttempt } from './authority-policy';
|
||||
import { inferExecutionProfile } from './execution-profiles';
|
||||
|
||||
export interface StructuredRoute {
|
||||
tree: TreeName;
|
||||
@@ -154,7 +155,7 @@ export function routeStructured(signals: Record<string, unknown>): StructuredRou
|
||||
return {
|
||||
tree,
|
||||
mode,
|
||||
depth: specialist.defaultDepth,
|
||||
depth: inferExecutionProfile(signals, specialist.defaultDepth),
|
||||
mutation,
|
||||
active_modules: active,
|
||||
skipped_modules: primary.filter((candidate) => !active.includes(candidate)),
|
||||
|
||||
@@ -2,6 +2,7 @@ export const GSTACK2_BASE_SHA = 'bb57306d98c97011b0919c6132705a15b1579781';
|
||||
|
||||
export const TREE_NAMES = ['plan', 'design', 'qa', 'debug', 'review', 'ship'] as const;
|
||||
export type TreeName = (typeof TREE_NAMES)[number];
|
||||
export type ExecutionProfile = 'readiness' | 'standard' | 'deep';
|
||||
|
||||
export type ModuleVisibility = 'primary' | 'internal';
|
||||
|
||||
@@ -27,7 +28,7 @@ export interface SourceAssignment {
|
||||
mandatory: boolean;
|
||||
replacement: string;
|
||||
summary: string;
|
||||
defaultDepth: 'quick' | 'standard' | 'deep';
|
||||
defaultDepth: ExecutionProfile;
|
||||
defaultMutation: string;
|
||||
webContext: 'none' | 'optional' | 'local-browser' | 'production';
|
||||
overlays?: number[];
|
||||
@@ -39,7 +40,7 @@ export interface DispatcherMode {
|
||||
target: string;
|
||||
modules: string[];
|
||||
inferWhen: string;
|
||||
depth: 'quick' | 'standard' | 'deep';
|
||||
depth: ExecutionProfile;
|
||||
mutation: string;
|
||||
webContext: 'none' | 'optional' | 'local-browser' | 'production';
|
||||
}
|
||||
@@ -75,7 +76,7 @@ export interface ScenarioFixture {
|
||||
expected: {
|
||||
tree: TreeName;
|
||||
mode: string;
|
||||
depth: 'quick' | 'standard' | 'deep';
|
||||
depth: ExecutionProfile;
|
||||
mutation: string;
|
||||
active_modules: string[];
|
||||
skipped_modules: string[];
|
||||
|
||||
Reference in New Issue
Block a user