docs: add next stage cicd dispatch matrix

This commit is contained in:
Codex
2026-05-21 08:48:34 +00:00
parent 3d28faa811
commit f055e18523
12 changed files with 865 additions and 4 deletions
+317
View File
@@ -0,0 +1,317 @@
const requiredDryRunMessage = "This first-phase commander contract only supports dry-run planning; live daemon/control operations are not implemented.";
const highRiskActions = [
"code-queue-backend-restart",
"code-queue-backend-rebuild",
"code-queue-execution-plane-restart",
"code-queue-task-interrupt",
"code-queue-task-cancel",
"prod-runtime-mutation",
] as const;
type HighRiskAction = typeof highRiskActions[number];
function hasFlag(args: string[], flag: string): boolean {
return args.includes(flag);
}
function optionValue(args: string[], name: string): string | undefined {
const index = args.indexOf(name);
if (index === -1) return undefined;
const value = args[index + 1];
if (value === undefined || value.startsWith("--")) throw new Error(`${name} requires a value`);
return value;
}
function isHelpToken(value: string | undefined): boolean {
return value === "help" || value === "--help" || value === "-h";
}
function isHighRiskAction(value: string): value is HighRiskAction {
return highRiskActions.some((action) => action === value);
}
function commanderHelp(): Record<string, unknown> {
return {
command: "commander",
output: "json",
description: "First-phase source/contract stub for the host Codex commander control microservice; no daemon or live control action is implemented.",
usage: [
"bun scripts/cli.ts commander contract",
"bun scripts/cli.ts commander plan --dry-run [--session-id id]",
"bun scripts/cli.ts commander approval request --action <action> --dry-run [--reason text] [--task-id id]",
],
highRiskActions,
reference: "docs/reference/host-codex-commander.md",
};
}
export function commanderContract(): Record<string, unknown> {
return {
ok: true,
phase: "source-contract",
serviceId: "host-codex-commander",
currentImplementation: "cli-contract-stub-only",
daemonImplemented: false,
liveOperationsImplemented: false,
purpose: "Keep a host Codex commander session observable and controllable through a future direct-managed microservice without replacing Code Queue runners.",
ownershipBoundary: {
hostCodexProcess: "Long-lived Codex process on the master server host.",
controlMicroservice: "Future direct-managed bridge that records state, mediates PTY/stdio/SSH streams, injects prompts, and summarizes traces.",
codeQueue: "Remains the task execution plane; the commander only supervises through existing safe CLI/API contracts.",
claudeqq: "Approval and user-notification path for high-risk actions.",
},
requiredCapabilities: [
"host-codex-process-discovery",
"host-codex-start-plan",
"ssh-bridge-contract",
"pty-bridge-contract",
"stdio-bridge-contract",
"prompt-guidance-plan",
"trace-summary-plan",
"issue-20-board-read-write-entry",
"issue-46-brief-read-write-entry",
"claudeqq-high-risk-approval-entry",
],
apiContract: {
health: "GET /health",
contract: "GET /api/commander/contract",
sessions: "GET /api/commander/sessions",
sessionPlan: "POST /api/commander/sessions/:sessionId/plan-start",
promptPlan: "POST /api/commander/sessions/:sessionId/prompt-plan",
traceSummary: "GET /api/commander/trace-summary?taskId=<taskId>",
issueWritePlan: "POST /api/commander/issues/:issueNumber/write-plan",
approvalRequest: "POST /api/commander/approvals",
},
stateModel: {
sessionStates: ["unknown", "discovered", "planned", "starting", "running", "attention_required", "stopping", "stopped", "degraded"],
promptStates: ["draft", "planned", "queued_for_injection", "injected", "rejected", "failed"],
approvalStates: ["draft", "requested", "approved", "rejected", "expired", "consumed"],
storageRoot: ".state/commander/",
redactionPolicy: "Never persist or print token, secret, password, key, cookie, or authorization values in cleartext.",
},
safetyBoundary: safetyBoundary(),
};
}
function safetyBoundary(): Record<string, unknown> {
return {
phaseOneMutationAllowed: false,
forbiddenWithoutExplicitUserApproval: highRiskActions,
alwaysForbidden: [
"print-token-values",
"read-token-files-for-display",
"direct-database-state-patch",
"bypass-code-queue-backend-confirmation-policy",
"replace-code-queue-runner",
"deploy-or-restart-production-runtime-from-this-contract-stub",
],
confirmationPolicy: "High-risk actions must draft a ClaudeQQ request, wait for explicit user approval, bind approval to one exact action, and record the decision before any future live executor may proceed.",
};
}
function processDiscoveryPlan(sessionId: string): Record<string, unknown> {
return {
sessionId,
mutation: false,
signals: [
".state/commander/sessions/<sessionId>.json",
"host process table filtered by executable and cwd markers",
"PTY/stdio bridge heartbeat file",
"last prompt/trace event sequence",
],
startPlan: {
enabled: false,
commandShape: "codex <reviewed host profile> --cwd /workspace/unidesk",
supervisor: "future direct-managed microservice on the master server host",
reason: requiredDryRunMessage,
},
};
}
function bridgePlan(): Record<string, unknown> {
return {
mutation: false,
adapters: {
ssh: {
purpose: "Reach provider hosts through existing UniDesk Host SSH / WSL SSH maintenance bridge.",
allowedUse: ["readonly diagnostics", "bounded reviewed maintenance commands", "future approved recovery commands"],
},
pty: {
purpose: "Keep the host Codex interactive session alive and observable.",
allowedUse: ["windowed stdout/stderr capture", "prompt injection after policy checks", "heartbeat"],
},
stdio: {
purpose: "Bridge non-interactive Codex or helper subprocesses with bounded transcript capture.",
allowedUse: ["contracted command execution", "trace collection", "structured result capture"],
},
},
guardrails: [
"bounded output by default",
"redact secret-like values before state persistence",
"persist event sequence before acknowledging prompt injection",
"never open an interactive shell from dry-run commands",
],
};
}
function issueEntryPlan(): Record<string, unknown> {
return {
mutation: false,
issues: [
{
number: 20,
role: "Code Queue total board",
readEntrypoints: ["bun scripts/cli.ts gh issue board-audit --board-issue 20 --dry-run", "bun scripts/cli.ts gh issue board-row list --board-issue 20"],
writeEntrypoints: ["bun scripts/cli.ts gh issue board-row update <issueNumber> --board-issue 20 --field <field> --value <text> --expect-body-sha <sha>"],
},
{
number: 46,
role: "daily commander brief",
readEntrypoints: ["bun scripts/cli.ts gh issue read 46 --json body,title,state,updatedAt"],
writeEntrypoints: ["bun scripts/cli.ts gh issue update 46 --body-profile commander-brief --body-file <file> --expect-updated-at <ts>"],
},
],
writeGuards: ["body-file-only", "dry-run-first", "body-sha-or-updated-at-required", "no-token-output"],
};
}
function traceSummaryPlan(): Record<string, unknown> {
return {
mutation: false,
sources: [
"bounded host Codex PTY/stdio event window",
"Code Queue task trace via codex task --trace",
"Code Queue output pages via codex output",
"commander approval and prompt event JSONL",
],
summaryShape: {
taskId: "string|null",
sessionId: "string",
lastSeq: "number",
status: "running|attention_required|blocked|terminal|unknown",
keyEvents: "bounded array",
openQuestions: "bounded array",
recommendedNextActions: "bounded array",
redactionsApplied: "number",
},
outputPolicy: "Default summaries must omit raw transcript text unless explicitly requested by a future reviewed endpoint.",
};
}
function promptGuidancePlan(): Record<string, unknown> {
return {
mutation: false,
stages: [
"classify intent and risk",
"attach current #20/#46/task context summary",
"apply forbidden-action guard",
"redact secret-like strings",
"persist prompt plan",
"inject only through future live executor after policy checks",
],
highRiskEscalation: "code-queue restart/rebuild, task interrupt/cancel, production mutation, token access, and destructive Git operations require ClaudeQQ approval first.",
};
}
function commanderPlan(args: string[]): Record<string, unknown> {
if (!hasFlag(args, "--dry-run")) {
return {
ok: false,
error: "dry-run-required",
message: requiredDryRunMessage,
command: "bun scripts/cli.ts commander plan --dry-run",
};
}
const sessionId = optionValue(args, "--session-id") ?? "primary";
return {
ok: true,
phase: "source-contract",
mode: "dry-run",
mutation: false,
serviceId: "host-codex-commander",
processDiscovery: processDiscoveryPlan(sessionId),
bridge: bridgePlan(),
promptGuidance: promptGuidancePlan(),
traceSummary: traceSummaryPlan(),
issueEntries: issueEntryPlan(),
claudeqqApproval: {
mutation: false,
commandShape: "bun scripts/cli.ts commander approval request --action <action> --dry-run",
highRiskActions,
},
safetyBoundary: safetyBoundary(),
};
}
function commanderApprovalRequest(args: string[]): Record<string, unknown> {
if (!hasFlag(args, "--dry-run")) {
return {
ok: false,
error: "dry-run-required",
message: requiredDryRunMessage,
command: "bun scripts/cli.ts commander approval request --action <action> --dry-run",
};
}
const action = optionValue(args, "--action");
if (action === undefined) {
return {
ok: false,
error: "validation-failed",
message: "--action is required",
highRiskActions,
};
}
if (!isHighRiskAction(action)) {
return {
ok: false,
error: "validation-failed",
message: `unsupported high-risk action: ${action}`,
highRiskActions,
};
}
const reason = optionValue(args, "--reason") ?? "operator-supplied reason required before live execution";
const taskId = optionValue(args, "--task-id") ?? null;
return {
ok: true,
phase: "source-contract",
mode: "dry-run",
mutation: false,
action,
taskId,
reason,
requiresExplicitUserApproval: true,
claudeqq: {
mutation: false,
endpointShape: "POST /api/microservices/claudeqq/proxy/api/push/text",
target: "configured primary user private chat",
messageTemplate: `Approval required for ${action}. Reason: ${reason}. Reply with explicit approval id before execution.`,
sendImplemented: false,
},
approvalRecordShape: {
id: "commander-approval-<stable-id>",
action,
taskId,
reason,
status: "draft",
approvedBy: null,
approvedAt: null,
expiresAt: "future reviewed timeout",
},
blockedUntilApproved: [action],
};
}
export function runCommanderCommand(args: string[]): Record<string, unknown> {
const [sub, second] = args;
if (sub === undefined || isHelpToken(sub)) return commanderHelp();
if (sub === "contract") return commanderContract();
if (sub === "plan") return commanderPlan(args.slice(1));
if (sub === "approval" && second === "request") return commanderApprovalRequest(args.slice(2));
return {
ok: false,
error: "unsupported-command",
message: `Unsupported commander command: ${args.join(" ")}`,
help: commanderHelp(),
};
}