@ornexus/neocortex 4.60.72 → 4.60.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/release-source.json +1 -1
- package/dist/sbom.cdx.json +24 -24
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +108 -1
- package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
- package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
- package/packages/client/dist/adapters/codex-adapter.js +80 -2
- package/packages/client/dist/adapters/cursor-adapter.js +115 -4
- package/packages/client/dist/adapters/gemini-adapter.js +71 -2
- package/packages/client/dist/adapters/grok-adapter.js +42 -2
- package/packages/client/dist/adapters/index.js +22 -1
- package/packages/client/dist/adapters/platform-detector.js +212 -1
- package/packages/client/dist/adapters/target-adapter.js +12 -0
- package/packages/client/dist/adapters/vscode-adapter.js +72 -2
- package/packages/client/dist/agent/refresh-stubs.js +417 -2
- package/packages/client/dist/agent/update-agent-yaml.js +102 -1
- package/packages/client/dist/agent/update-description.js +251 -1
- package/packages/client/dist/cache/crypto-utils.js +78 -1
- package/packages/client/dist/cache/encrypted-cache.js +108 -1
- package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
- package/packages/client/dist/cache/index.js +13 -1
- package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
- package/packages/client/dist/checkpoint/index.js +11 -1
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
- package/packages/client/dist/cli.js +358 -5
- package/packages/client/dist/commands/activate.js +394 -8
- package/packages/client/dist/commands/architecture-answers.js +274 -7
- package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
- package/packages/client/dist/commands/architecture-root-binding.js +51 -1
- package/packages/client/dist/commands/cache-status.js +112 -2
- package/packages/client/dist/commands/config.js +88 -1
- package/packages/client/dist/commands/doctor.js +635 -2
- package/packages/client/dist/commands/invoke.js +7200 -143
- package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
- package/packages/client/dist/commands/legacy-story-branch.js +96 -1
- package/packages/client/dist/commands/legacy-story-pr.js +276 -1
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
- package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
- package/packages/client/dist/commands/mutation-qa-git.js +41 -1
- package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
- package/packages/client/dist/commands/mutation-qa-run.js +219 -3
- package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
- package/packages/client/dist/commands/mutation-qa.js +220 -1
- package/packages/client/dist/commands/native-agent.js +136 -2
- package/packages/client/dist/commands/planning-diagnosis.js +43 -1
- package/packages/client/dist/commands/planning-generation.js +53 -1
- package/packages/client/dist/commands/planning-memory.js +40 -1
- package/packages/client/dist/commands/planning-research.js +42 -1
- package/packages/client/dist/commands/private-session.js +75 -1
- package/packages/client/dist/commands/property-qa.js +139 -1
- package/packages/client/dist/commands/qa-control.js +16 -1
- package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
- package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
- package/packages/client/dist/commands/qa-scan-capture.js +663 -5
- package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
- package/packages/client/dist/commands/qa-scan-review.js +826 -5
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
- package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
- package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
- package/packages/client/dist/commands/refactor-characterization.js +671 -21
- package/packages/client/dist/commands/refactor-commit.js +399 -13
- package/packages/client/dist/commands/refactor-control.js +142 -3
- package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
- package/packages/client/dist/commands/refactor-review.js +283 -5
- package/packages/client/dist/commands/refactor-root.js +588 -7
- package/packages/client/dist/commands/refresh-memory.js +156 -3
- package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
- package/packages/client/dist/commands/story-checkpoint.js +171 -1
- package/packages/client/dist/config/profile-directory.js +13 -1
- package/packages/client/dist/config/resolver-selection.js +278 -1
- package/packages/client/dist/config/secure-config.js +281 -12
- package/packages/client/dist/config.js +223 -2
- package/packages/client/dist/constants.js +25 -1
- package/packages/client/dist/context/context-collector.js +222 -2
- package/packages/client/dist/context/context-sanitizer.js +145 -1
- package/packages/client/dist/continuity/autonomous-governance.js +285 -1
- package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
- package/packages/client/dist/continuity/invoke-hooks.js +129 -1
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
- package/packages/client/dist/continuity/native-agent-store.js +69 -1
- package/packages/client/dist/continuity/native-agent-types.js +90 -1
- package/packages/client/dist/continuity/outbox.js +117 -1
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
- package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
- package/packages/client/dist/continuity/sqlite-store.js +2612 -81
- package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
- package/packages/client/dist/errors/error-messages.js +228 -2
- package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
- package/packages/client/dist/evidence/collector.js +347 -1
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +2 -0
- package/packages/client/dist/fs/atomic-state-replace.js +152 -1
- package/packages/client/dist/generated/command-registry.generated.js +4076 -1
- package/packages/client/dist/generated/public-projections.generated.js +149 -1
- package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
- package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
- package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
- package/packages/client/dist/git/epic-operational-state.js +672 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
- package/packages/client/dist/git/git-lifecycle-store.d.ts +9 -1
- package/packages/client/dist/git/git-lifecycle-store.js +1981 -9
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
- package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
- package/packages/client/dist/git/legacy-story-pr.js +3538 -70
- package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
- package/packages/client/dist/i18n/first-run.js +60 -2
- package/packages/client/dist/index.js +47 -1
- package/packages/client/dist/invocation/invoke-input.js +24 -1
- package/packages/client/dist/license/index.js +5 -1
- package/packages/client/dist/license/license-client.js +304 -1
- package/packages/client/dist/machine/fingerprint.js +163 -2
- package/packages/client/dist/machine/index.js +5 -1
- package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
- package/packages/client/dist/memory/project-memory-writer.js +1007 -40
- package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
- package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +1060 -0
- package/packages/client/dist/migrations/project-identity-capability.js +50 -1
- package/packages/client/dist/migrations/project-identity.js +493 -7
- package/packages/client/dist/migrations/state-json.d.ts +22 -0
- package/packages/client/dist/migrations/state-json.js +393 -4
- package/packages/client/dist/policy/architecture-policy.js +184 -2
- package/packages/client/dist/policy/index.js +7 -1
- package/packages/client/dist/policy/shared-policy-types.js +38 -0
- package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
- package/packages/client/dist/private-execution/clean-baseline.js +279 -2
- package/packages/client/dist/private-execution/codex-executor.js +574 -4
- package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
- package/packages/client/dist/private-execution/delivery.js +410 -13
- package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
- package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
- package/packages/client/dist/private-execution/implementation-validation.js +106 -2
- package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
- package/packages/client/dist/private-execution/isolated-session.js +725 -5
- package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
- package/packages/client/dist/private-execution/pact-test-source.js +258 -1
- package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
- package/packages/client/dist/private-execution/pact-write-set.js +172 -1
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
- package/packages/client/dist/private-execution/private-report-store.js +42 -1
- package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
- package/packages/client/dist/private-execution/property-test-choices.js +147 -3
- package/packages/client/dist/private-execution/property-test-plan.js +271 -7
- package/packages/client/dist/private-execution/property-test-review.js +258 -4
- package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
- package/packages/client/dist/private-execution/property-test-source.js +139 -1
- package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
- package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
- package/packages/client/dist/private-execution/root-memory-request.js +29 -1
- package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
- package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
- package/packages/client/dist/private-execution/session-lease.js +63 -1
- package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
- package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
- package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
- package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
- package/packages/client/dist/private-execution/ux-write-set.js +191 -2
- package/packages/client/dist/registry/registry-reconciliation.js +234 -1
- package/packages/client/dist/resilience/circuit-breaker.js +170 -1
- package/packages/client/dist/resilience/degradation-manager.js +164 -1
- package/packages/client/dist/resilience/freshness-indicator.js +100 -1
- package/packages/client/dist/resilience/index.js +8 -1
- package/packages/client/dist/resilience/recovery-detector.js +74 -1
- package/packages/client/dist/resolvers/asset-resolver.js +13 -0
- package/packages/client/dist/resolvers/local-resolver.js +255 -8
- package/packages/client/dist/resolvers/remote-resolver.js +233 -1
- package/packages/client/dist/runner/binary.js +404 -1
- package/packages/client/dist/runner/cli.js +1571 -17
- package/packages/client/dist/runner/headless/adapters.js +49 -1
- package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
- package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
- package/packages/client/dist/runner/headless/managed-profile.js +132 -1
- package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
- package/packages/client/dist/runner/headless/resolver.js +231 -1
- package/packages/client/dist/runner/headless/run-yolo.js +885 -1
- package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
- package/packages/client/dist/runner/headless/types.js +574 -2
- package/packages/client/dist/runner/local-console.js +396 -13
- package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
- package/packages/client/dist/runner/project-identity.js +106 -1
- package/packages/client/dist/runner/scheduler.js +995 -7
- package/packages/client/dist/runner-cli.js +18 -2
- package/packages/client/dist/state/invocation-shape.js +13 -1
- package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
- package/packages/client/dist/state/project-state-snapshot.js +1731 -1
- package/packages/client/dist/state/state-json-repair.js +785 -3
- package/packages/client/dist/state/structural-dag-projection.js +118 -1
- package/packages/client/dist/telemetry/index.js +5 -1
- package/packages/client/dist/telemetry/offline-queue.js +131 -1
- package/packages/client/dist/tier/index.js +5 -1
- package/packages/client/dist/tier/tier-aware-client.js +405 -1
- package/packages/client/dist/types/index.js +38 -1
- package/packages/client/dist/version.js +27 -1
- package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
- package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
- package/packages/client/dist/yolo/spec-envelope-collector.js +184 -5
- package/packages/client/dist/yoloop/discovery-hook.js +300 -2
- package/packages/client/dist/yoloop/index.js +8 -1
- package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
- package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
- package/packages/client/dist/yoloop/target-normalization.js +180 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1920 -1
- package/packages/shared/dist/yolo/spec-envelope.js +3 -6
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1,4 +1,146 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* P190.06 -- runner root-owned execution plane for run_yoloop directives.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
8
|
+
import { spawn } from 'node:child_process';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import * as os from 'node:os';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { AUTONOMOUS_GOVERNANCE_EVIDENCE_TAG, AUTONOMOUS_GOVERNANCE_REASON_CODES, autonomousGovernanceMetadata, buildAutonomousGovernanceEnvelope, readAutonomousGovernanceEnvelopeFromMetadata, recordAutonomousGovernance, } from '../../continuity/autonomous-governance.js';
|
|
14
|
+
import { createDefaultHeadlessAdapterRegistry } from './adapters.js';
|
|
15
|
+
import { loadHeadlessRuntimeDefaults, resolveHeadlessRuntime } from './resolver.js';
|
|
16
|
+
import { DEFAULT_HEADLESS_EXECUTION_BACKOFF_MS, DEFAULT_HEADLESS_EXECUTION_LEASE_MS, DEFAULT_HEADLESS_EXECUTION_LIMIT, DEFAULT_HEADLESS_EXECUTION_MAX_ATTEMPTS, DEFAULT_HEADLESS_EXECUTION_STALE_LEASE_AFTER_MS, DEFAULT_HEADLESS_EXECUTION_TIMEOUT_MS, headlessExecutionEntityId, runYoloExecutionPhase, } from './run-yolo.js';
|
|
17
|
+
import { HEADLESS_REASON_CODES, boundPublicSummary, hashPublicValue, redaction, } from './types.js';
|
|
18
|
+
import { validatePhasedYoloopPlan } from './prepared-story-bundle.js';
|
|
19
|
+
import { createAnchoredContinuousStoryCommandRunner, createBranchlessContinuousStoryWorkspaceManager, } from './continuous-story-workspace.js';
|
|
20
|
+
import { executeEpicGitReconciliation, executeEpicTerminalGit } from './epic-terminal-git-runner.js';
|
|
21
|
+
import { createCliEpicTerminalGitAdapter } from './epic-terminal-git-cli-adapter.js';
|
|
22
|
+
import { acquireGitLifecyclePhysicalIntegrationLock, releaseGitLifecyclePhysicalIntegrationLock, } from '../../git/git-lifecycle-store.js';
|
|
23
|
+
import { normalizeContinuousWriteRef } from '../../../../shared/dist/yoloop/wave-decision-receipt.js';
|
|
24
|
+
import { parseReleaseBlockedResult, } from '../../../../shared/dist/yoloop/release-blocked.js';
|
|
25
|
+
export const DEFAULT_HEADLESS_YOLOOP_MAX_ITERATIONS = 20;
|
|
26
|
+
export const DEFAULT_HEADLESS_YOLOOP_MAX_DURATION_MS = 30 * 60 * 1000;
|
|
27
|
+
export const DEFAULT_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP = 1;
|
|
28
|
+
export const MAX_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP = 10;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the absent-override default from physical capacity. The explicit CLI
|
|
31
|
+
* option remains authoritative; this resolver is injectable for deterministic
|
|
32
|
+
* Node 18 fallback tests.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveDefaultHeadlessYoloopLocalConcurrencyCap(input = {}) {
|
|
35
|
+
let reported = input.availableParallelism;
|
|
36
|
+
if (reported === undefined) {
|
|
37
|
+
const available = os.availableParallelism;
|
|
38
|
+
if (typeof available === 'function') {
|
|
39
|
+
try {
|
|
40
|
+
reported = available();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
reported = undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!Number.isFinite(reported) || (reported ?? 0) < 1) {
|
|
48
|
+
const cpuCount = input.cpuCount ?? (() => {
|
|
49
|
+
try {
|
|
50
|
+
return os.cpus().length;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return DEFAULT_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP;
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
reported = Number.isFinite(cpuCount) && cpuCount > 0
|
|
57
|
+
? cpuCount
|
|
58
|
+
: DEFAULT_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP;
|
|
59
|
+
}
|
|
60
|
+
return boundedInteger(reported ?? DEFAULT_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP, 1, MAX_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP);
|
|
61
|
+
}
|
|
62
|
+
export const RUN_YOLOOP_DRIVER_STATES = Object.freeze([
|
|
63
|
+
'start',
|
|
64
|
+
'lifecycle_evidence',
|
|
65
|
+
'metadata_parse',
|
|
66
|
+
'child_story_delegation',
|
|
67
|
+
'reinvoke',
|
|
68
|
+
'git_reconciliation',
|
|
69
|
+
'terminal_git',
|
|
70
|
+
'complete',
|
|
71
|
+
'aborted',
|
|
72
|
+
'release_blocked',
|
|
73
|
+
'backoff',
|
|
74
|
+
]);
|
|
75
|
+
const EPIC_TARGET_PATTERN = /^docs\/epics\/epic-[A-Za-z0-9._-]+\.md$/;
|
|
76
|
+
const CHILD_STORY_TARGET_PATTERN = /^docs\/stories\/[A-Za-z0-9._-]+\.story\.md$/;
|
|
77
|
+
const CHILD_PROMPT_PATTERN = /^\*yolo @docs\/stories\/[A-Za-z0-9._-]+\.story\.md(?: branchless-preparation)?$/;
|
|
78
|
+
const BRANCHLESS_PREPARATION_PROMPT_PATTERN = /^\*yolo @docs\/stories\/[A-Za-z0-9._-]+\.story\.md branchless-preparation$/;
|
|
79
|
+
const FRONTMATTER_ENVELOPE_PATTERN = /^(?:\uFEFF)?---(?:\r?\n)/;
|
|
80
|
+
const SAFE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/;
|
|
81
|
+
const SHELL_FRAGMENT_PATTERN = /(?:[;&|`$<>]|\$\(|\n|\r|\0)/;
|
|
82
|
+
const UNSAFE_KEY_PATTERN = /(raw[_-]?(?:logs?|payloads?)|std(?:out|err)|stack(?:[_-]?trace)?|transcript|objective(?:[_-]?(?:text|body|prompt))|prompt(?:[_-]?(?:body|text))?|private[_-]?(?:urls?|prompts?)|secret|token|password|protected(?:[_-]?(?:field|body|bodies|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|\bpii\b|authorization|api[_-]?key|license[_-]?key|argv|args|executable(?:[_-]?path)?|binary(?:[_-]?path)?|shell|cwd|command[_-]?path|vendor(?:[_-]?(?:payload|corpus|response|output)))/i;
|
|
83
|
+
const UNSAFE_VALUE_PATTERN = /(https?:\/\/\S+|(?:^|\s)(?:\/(?:home|Users|media|mnt|tmp|var|etc|private)\/\S+|[A-Za-z]:\\\S+)|ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:|password\s*=|token\s*=|api[_-]?key\s*=|license[_-]?key\s*=|raw[_-]?(?:logs?)\s*[:=]|transcript|stack\s*trace|stderr:|stdout:|system prompt|workflow body|step body|PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|\bPII\b|VENDOR_CORPUS|P190[_-]?SYNTHETIC[_-]?(?:HEADLESS|CONTINUITY))/i;
|
|
84
|
+
const PUBLIC_CHILD_DEBT_CONSTRAINTS = new Set([
|
|
85
|
+
'no_private_prompt_or_history',
|
|
86
|
+
'no_raw_vendor_corpus',
|
|
87
|
+
'no_unrelated_story_context',
|
|
88
|
+
]);
|
|
89
|
+
function hasDelegatedPromptIsolationViolation(prompt) {
|
|
90
|
+
return FRONTMATTER_ENVELOPE_PATTERN.test(prompt) ||
|
|
91
|
+
prompt.includes('@neocortex') ||
|
|
92
|
+
prompt.includes('*yoloop') ||
|
|
93
|
+
SHELL_FRAGMENT_PATTERN.test(prompt) ||
|
|
94
|
+
UNSAFE_VALUE_PATTERN.test(prompt);
|
|
95
|
+
}
|
|
96
|
+
export function isCanonicalDelegatedStoryPrompt(prompt) {
|
|
97
|
+
return !hasDelegatedPromptIsolationViolation(prompt) && CHILD_PROMPT_PATTERN.test(prompt);
|
|
98
|
+
}
|
|
99
|
+
const EXECUTION_DIRECTIVE_ALLOWED_KEYS = new Set([
|
|
100
|
+
'schemaVersion',
|
|
101
|
+
'contractVersion',
|
|
102
|
+
'kind',
|
|
103
|
+
'commandClass',
|
|
104
|
+
'jobId',
|
|
105
|
+
'operationId',
|
|
106
|
+
'idempotencyKey',
|
|
107
|
+
'targetKind',
|
|
108
|
+
'targetRef',
|
|
109
|
+
'commandRef',
|
|
110
|
+
'epicId',
|
|
111
|
+
'runtime',
|
|
112
|
+
'publicSummary',
|
|
113
|
+
'reasonCode',
|
|
114
|
+
'attempts',
|
|
115
|
+
'metadata',
|
|
116
|
+
'redactionSummary',
|
|
117
|
+
'publicSafe',
|
|
118
|
+
]);
|
|
119
|
+
const RUNTIME_ALLOWED_KEYS = new Set(['runtimeId', 'modelId', 'source', 'reasonCode', 'publicSafe']);
|
|
120
|
+
const JOB_ABORTED_REASON_CODE = 'job-aborted';
|
|
121
|
+
const MAX_ITERATIONS_REASON_CODE = 'max-iterations-reached';
|
|
122
|
+
const MAX_DURATION_REASON_CODE = 'max-duration-reached';
|
|
123
|
+
const PUBLIC_TERMINAL_LOCK_RETRY_REASONS = new Set([
|
|
124
|
+
'git-lifecycle-integration-backpressure',
|
|
125
|
+
'git-lifecycle-integration-lock-failed',
|
|
126
|
+
'git-lifecycle-integration-lock-release-failed',
|
|
127
|
+
]);
|
|
128
|
+
const RESUMABLE_YOLOOP_BOUNDARY_REASONS = new Set([
|
|
129
|
+
'yoloop-per-run-cap-resumable',
|
|
130
|
+
'yoloop-session-timeout-resumable',
|
|
131
|
+
]);
|
|
132
|
+
async function defaultIsolatedYoloopRootInvokeTransport(options, boundary) {
|
|
133
|
+
// The thin client's lock belongs to its invocation PID. Keeping invoke in
|
|
134
|
+
// the long-lived runner made the next iteration see its own PID as another
|
|
135
|
+
// live owner forever. Use the normal short-lived client lifetime; only the
|
|
136
|
+
// existing local liveness reconciler may recover its lock after process exit.
|
|
137
|
+
// Loop scheduling and all Git effects remain in this root, not in the child.
|
|
138
|
+
const moduleDirectory = dirname(fileURLToPath(import.meta.url));
|
|
139
|
+
const installedModule = join(moduleDirectory, '../../commands/invoke.js');
|
|
140
|
+
const modulePath = existsSync(installedModule) ? installedModule
|
|
141
|
+
: join(moduleDirectory, '../../../dist/commands/invoke.js');
|
|
142
|
+
return new Promise((resolveResult) => {
|
|
143
|
+
const child = spawn(process.execPath, ['--input-type=module', '-e', `
|
|
2
144
|
import { pathToFileURL } from 'node:url';
|
|
3
145
|
process.once('message', async ({ modulePath, options }) => {
|
|
4
146
|
try {
|
|
@@ -7,4 +149,2434 @@ import{createHash as je,randomBytes as Ke}from"node:crypto";import{spawn as Ue}f
|
|
|
7
149
|
process.send(result, () => process.exit(0));
|
|
8
150
|
} catch { process.exit(1); }
|
|
9
151
|
});
|
|
10
|
-
`],{cwd:r.projectRoot,stdio:["ignore","pipe","pipe","ipc"],windowsHide:!0});let c,d=0,l=!1;const y=setTimeout(()=>{l=!0,n.kill()},r.timeoutMs),f=m=>{d+=m.length,d>32*1024*1024&&(l=!0,n.kill())};n.stdout?.on("data",f),n.stderr?.on("data",f),n.on("message",m=>{if(c||!_(m)||typeof m.success!="boolean"||Buffer.byteLength(JSON.stringify(m))>32*1024*1024){l=!0,n.kill();return}c=m});const u={success:!1,errorCode:"headless-root-invoke-failed",error:"The isolated root metadata invocation did not complete.",exitCode:1};n.on("error",()=>{l=!0}),n.on("close",m=>{clearTimeout(y),s(m===0&&!l&&c?c:u)}),n.send({modulePath:o,options:e},m=>{m&&(l=!0,n.kill())})})}async function vt(e,r=Rt){const t=Date.now(),a={args:e.args,projectRoot:e.projectRoot,format:"json",platformTarget:"runner",serverUrl:e.serverUrl,strictReadOnly:e.strictReadOnly},o=await r(a,{projectRoot:e.projectRoot,timeoutMs:e.timeoutMs});return{success:o.success,instructions:o.instructions,metadata:o.metadata,error:o.error,errorCode:o.errorCode,exitCode:o.exitCode,durationMs:Date.now()-t,publicSafe:!0}}async function Or(e){const r=e.adapterRegistry??We(),t=F(e.limit??Qe,1,10),a=F(e.leaseMs??Je,1e3,3600*1e3),o=F(e.staleLeaseAfterMs??tt,0,1440*60*1e3),s=F(e.maxAttempts??et,1,10),n=F(e.localConcurrencyCap??Pe(),1,ve),c=[],d=[],l=e.now(),y=Pt({store:e.store,metadata:e.metadata,nowIso:l,maxAttempts:s,queueCap:e.queueCap});c.push(...y.reasonCodes);const f=e.store.recoverStaleHeadlessExecutionAttempts({nowIso:l,staleAfterMs:o,limit:t*2});f.recoveredAttemptIds.length>0&&(c.push(p.EXECUTION_BACKOFF),f.failedAttemptIds.length>0&&c.push(p.EXECUTION_TIMEOUT));const u=e.store.listDueHeadlessExecutionAttempts({nowIso:e.now(),limit:Math.max(t*5,10)}).filter(m=>m.directive_kind==="run_yoloop").slice(0,t);for(const m of u){const k=await Tt({store:e.store,projectRoot:e.projectRoot,runnerId:e.runnerId,attempt:m,registry:r,now:e.now,leaseMs:a,timeoutMs:F(e.timeoutMs??rt,1e3,18e5),backoffMs:F(e.backoffMs??Ze,1e3,36e5),maxAttempts:s,maxIterations:F(e.maxIterations??ft,1,100),maxDurationMs:F(e.maxDurationMs??yt,1e3,72e5),localConcurrencyCap:n,queueCap:e.queueCap,adapterOptions:e.adapterOptions,runtimeDefaults:e.runtimeDefaults,runtimeFallback:e.runtimeFallback,yoloopInvoker:e.yoloopInvoker??vt,serverUrl:e.serverUrl,idFactory:e.idFactory??ar,continuousWorkspaceManager:e.continuousWorkspaceManager??st(),terminalGitAdapter:e.terminalGitAdapter??ct(e.projectRoot)});d.push(k),c.push(k.reasonCode,...k.workerPoolSummary.reasonCodes)}return{ok:d.every(m=>m.outcome!=="failed")&&y.rejectedCount===0,enqueuedAttemptCount:y.enqueuedCount,dueAttemptCount:u.length,processedAttemptCount:d.filter(m=>m.outcome!=="ignored").length,recoveredAttemptIds:f.recoveredAttemptIds,receipts:d,reasonCodes:V(c.length>0?c:[p.OK]),publicSafe:!0}}function Pt(e){let r=0,t=0;const a=[];for(const o of Xt(e.metadata)){if(!_(o)||o.kind!=="run_yoloop")continue;const s=Mt(o);if(!s.ok){t+=1,a.push(s.reasonCode);continue}const n=s.directive;if(!e.store.jobExists(n.jobId)){t+=1,a.push(Oe.BYPASS_BLOCKED);continue}const c=Zt(n);if(e.store.hasCompletedHeadlessExecutionDirectiveHash(c)){a.push(p.EXECUTION_COMPLETED);continue}const d=Ve({autonomousKind:"run_yoloop",source:"headless-run-yoloop",targetKind:"epic",targetRef:n.targetRef,epicId:n.epicId??ne(n.targetRef),jobId:n.jobId,operationSeed:n.operationId,commandHash:x(n.commandRef)}),l=qe(e.store,d,e.nowIso,{requireExistingJob:!0});if(!l.ok){t+=1,a.push(l.reasonCode);continue}const y=q("attempt",n.idempotencyKey),f=q("exec",n.operationId),u=e.store.enqueueHeadlessExecutionAttempt({attemptId:y,executionId:f,jobId:n.jobId,directiveHash:c,directiveKind:"run_yoloop",commandClass:n.commandClass,targetKind:"epic",targetRef:n.targetRef,epicId:n.epicId??ne(n.targetRef),runtimeId:n.runtime?.runtimeId,modelId:n.runtime?.modelId,runtimeSource:or(n.runtime?.source),reasonCode:n.reasonCode,publicSummary:n.publicSummary,enqueuedAt:e.nowIso,notBeforeAt:e.nowIso,maxAttempts:e.maxAttempts,queueCap:e.queueCap,operationId:`${n.operationId}:enqueue`,idempotencyKey:n.idempotencyKey,counts:{directives:1},hashes:{directive:c},redactionSummary:n.redactionSummary,relativePaths:[n.targetRef],metadata:Ee(d,{schemaVersion:1,contractVersion:"continuity-v1",jobId:n.jobId,operationId:n.operationId,reasonCode:n.reasonCode??p.OK,targetRef:n.targetRef,directiveHash:c})});if(!u.accepted){t+=1,a.push(u.reasonCode??"queue-cap-reached");continue}u.inserted&&(r+=1),a.push(p.OK)}return{enqueuedCount:r,rejectedCount:t,reasonCodes:V(a)}}async function Tt(e){const r=e.now(),t=e.idFactory("lease",e.attempt.attempt_id),a=Se(r,e.leaseMs);if(!e.store.claimHeadlessExecutionAttempt({attemptId:e.attempt.attempt_id,leaseId:t,runnerId:e.runnerId,claimedAt:r,leaseExpiresAt:a,reasonCode:p.OK}).inserted)return qt(e.attempt,{status:e.attempt.status,receiptStatus:"ignored",reasonCode:"lease-conflict",timestamp:r});if(e.store.hasCompletedHeadlessExecutionDirectiveHash(e.attempt.directive_hash,e.attempt.attempt_id))return _e(e.store,e.attempt,{leaseId:t,runnerId:e.runnerId,status:"skipped",receiptStatus:"ignored",reasonCode:p.EXECUTION_COMPLETED,publicSummary:"Skipped duplicate run_yoloop directive because the directive hash is already completed.",completedAt:e.now(),counts:Ie(),hashes:{directive:e.attempt.directive_hash},redactionSummary:[],relativePaths:[e.attempt.target_ref],childStoryIds:[],stateTransitions:["start","complete"]});const n=xt(e);if(!n.ok)return _e(e.store,e.attempt,{leaseId:t,runnerId:e.runnerId,status:n.status,receiptStatus:n.receiptStatus,reasonCode:n.reasonCode,publicSummary:n.publicSummary,completedAt:e.now(),counts:Ie(),hashes:{directive:e.attempt.directive_hash},redactionSummary:n.redactionSummary,relativePaths:er(e.attempt),childStoryIds:[],stateTransitions:["start"]});const c=await wt({...e,prepared:n.input}),d=Wt(e.attempt,c,e.maxAttempts,e.backoffMs,e.now()),l=d.retry?"backoff":c.status,y=d.retry?"deferred":c.receiptStatus,f=d.retry?Et.has(c.reasonCode)||we.has(c.reasonCode)?c.reasonCode:p.EXECUTION_BACKOFF:c.reasonCode,u=d.retry?"Headless run_yoloop execution scheduled bounded retry after transient metadata/runtime outcome.":c.publicSummary;return _e(e.store,e.attempt,{leaseId:t,runnerId:e.runnerId,status:l,receiptStatus:y,reasonCode:f,publicSummary:u,completedAt:e.now(),durationMs:c.durationMs,notBeforeAt:d.notBeforeAt,retryCount:d.retryCount,counts:c.counts,workerPoolSummary:c.workerPoolSummary,hashes:{...c.hashes,directive:e.attempt.directive_hash},redactionSummary:c.redactionSummary,relativePaths:c.relativePaths,childStoryIds:c.childStoryIds,runtimeId:n.input.runtimeId,modelId:n.input.modelId,runtimeSource:n.input.runtimeSource,stateTransitions:c.stateTransitions,terminalClass:c.terminalClass,releaseBlocked:c.releaseBlocked})}async function wt(e){const r=Date.now(),t=["start"],a=new Set,o=[],s=[],n={root:x(e.prepared.rootPrompt)},c=[h("rootPrompt","prompt_body",n.root)];let d=0,l=0,y=0,f=0,u=0,m=0,k,T=!1;const i=De(e.localConcurrencyCap),E=new Map;let j=!1;const K=new Map,z=new Map,se=P=>{const b=P.child;n[`child-${b.storyId.replace(/[^A-Za-z0-9_.:-]/g,"-")}`]=P.commandHash,c.push(P.promptRedaction);const M=P.receipt;if(P.terminalFailure){if(P.deferredFailure){K.set(b.storyId,P.terminalFailure),i.backpressurePauseCount+=1;return}K.delete(b.storyId),z.set(b.storyId,P.terminalFailure),a.has(b.storyId)||(a.add(b.storyId),o.push(b.storyId),s.push(b.targetRef),y+=1);return}if(!M||M.outcome==="deferred"){K.set(b.storyId,P.reasonCodes.includes("queue-cap-reached")?p.WORKER_POOL_BACKPRESSURE_PAUSED:M?.reasonCode||p.EXECUTION_BACKOFF),P.reasonCodes.includes("queue-cap-reached")&&(i.backpressurePauseCount+=1);return}K.delete(b.storyId),!a.has(b.storyId)&&(a.add(b.storyId),o.push(b.storyId),s.push(b.targetRef),M.outcome==="failed"?(y+=1,z.set(b.storyId,M.reasonCode||p.EXECUTION_FAILED)):M.outcome==="ignored"?f+=1:l+=1)},X=async()=>{const P=await Promise.race([...E.entries()].map(([b,M])=>M.promise.then(S=>({storyId:b,result:S}))));E.delete(P.storyId),i.completedChildCount+=1,i.pendingChildCount=E.size,se(P.result)};for(;;){if(d>=e.maxIterations)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:kt,publicSummary:"Headless yoloop reached the bounded iteration limit before completion metadata.",retryable:!1,startedAt:r,counts:A(d,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(Date.now()-r>e.maxDurationMs)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:Ot,publicSummary:"Headless yoloop reached the bounded duration limit before completion metadata.",retryable:!1,startedAt:r,counts:A(d,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});t.push("metadata_parse");const P=await e.yoloopInvoker({projectRoot:e.projectRoot,args:e.prepared.rootPrompt,attemptId:e.attempt.attempt_id,executionId:e.attempt.execution_id,jobId:e.attempt.job_id,runnerId:e.runnerId,iteration:d,runtimeId:e.prepared.runtimeId,...e.prepared.modelId?{modelId:e.prepared.modelId}:{},timeoutMs:e.timeoutMs,serverUrl:e.serverUrl});P.durationMs!==void 0&&(n[`iter-${d}`]=x(String(P.durationMs)));const b=P.metadata,M=P.errorCode==="YOLOOP_RELEASE_BLOCKED"||b?.yoloopReleaseBlocked===!0||b?.terminalClass==="release-blocked"||b?.releaseBlocked!==void 0;if(!P.success&&!M)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:p.EXECUTION_FAILED,publicSummary:"Headless yoloop root metadata invocation failed with bounded public evidence.",retryable:!0,startedAt:r,counts:A(d,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const S=Yt(b);if(!S.ok)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:S.reasonCode,publicSummary:S.publicSummary,retryable:!1,startedAt:r,counts:A(d,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:[...c,...S.redactionSummary],relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const C=Ft(b);if(S.aborted&&C){for(;E.size>0;)await X();return m+=1,t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:C.reason==="YOLOOP_SESSION_TIMEOUT"?"yoloop-session-timeout-resumable":"yoloop-per-run-cap-resumable",publicSummary:"Headless yoloop reached a finite run boundary and scheduled the same queue after cool-down.",retryable:!0,retryNotBeforeAt:C.coolDownUntil,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t})}if((S.releaseBlocked||S.aborted||S.complete||S.replay||S.outcome&&S.outcome!=="selected"&&S.outcome!=="queue_exhausted")&&j&&E.size>0){await X(),d+=1,t.push("reinvoke");continue}if(S.releaseBlocked)return t.push("release_blocked"),O({status:"failed",receiptStatus:"failed",reasonCode:"yoloop-release-blocked",publicSummary:"Headless yoloop stopped at a terminal release-blocked boundary without declaring success.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:{...n,releaseBlocked:x(S.releaseBlocked.resultId)},redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t,terminalClass:"release-blocked",releaseBlocked:S.releaseBlocked});const $=_(b?.epicIntegration)?b.epicIntegration:void 0,ee=Array.isArray($?.reasonCodes)?$.reasonCodes.filter(I=>typeof I=="string"):[];if(S.aborted&&b?.abortReason==="epic-integration-checkpoints-incomplete"&&$?.required===!0&&$.ready===!1&&ee.length>0&&ee.every(I=>I==="epic-aggregate-qa-receipt-required"||I==="epic-origin-binding-required")&&e.terminalGitAdapter.prepareLifecycleEvidence){t.push("lifecycle_evidence");const I=await e.terminalGitAdapter.prepareLifecycleEvidence({epicId:e.prepared.epicId,nowIso:e.now()});if(!I.ok)return t.push("backoff"),O({status:I.retryEligible?"backoff":"failed",receiptStatus:I.retryEligible?"deferred":"failed",reasonCode:I.reasonCode,publicSummary:"Headless yoloop stopped because physical Git lifecycle evidence could not be produced safely.",retryable:I.retryEligible,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});k=I.p205Authority,T=!0,d+=1,t.push("reinvoke");continue}if(S.aborted)return m+=1,t.push("aborted"),O({status:"failed",receiptStatus:"failed",reasonCode:At,publicSummary:"Headless yoloop stopped because public metadata reported an aborted loop.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(S.outcome&&S.outcome!=="selected"&&S.outcome!=="queue_exhausted")return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:`yoloop-${S.outcome}`,publicSummary:"Headless yoloop stopped on a non-executable graph-first continuation outcome.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(S.replay)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:"yoloop-replay-pending",publicSummary:"Headless yoloop observed pending replay authority and did not delegate a second child.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(S.complete&&j&&K.size>0&&z.size===0)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:[...K.values()].sort()[0],publicSummary:"Headless yoloop rejected terminal completion while previously runnable work remained undispatched.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(S.complete&&j&&z.size>0)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:[...z.values()].sort()[0],publicSummary:"Headless yoloop rejected terminal completion because runnable work failed before an accepted dispatch receipt.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const de=zt(b);if(de){if(!T&&e.terminalGitAdapter.prepareLifecycleEvidence){t.push("lifecycle_evidence");const v=await e.terminalGitAdapter.prepareLifecycleEvidence({epicId:e.prepared.epicId,nowIso:e.now()});if(!v.ok)return t.push("backoff"),O({status:v.retryEligible?"backoff":"failed",receiptStatus:v.retryEligible?"deferred":"failed",reasonCode:v.reasonCode,publicSummary:"Headless yoloop stopped because physical Git lifecycle evidence could not be produced safely.",retryable:v.retryEligible,startedAt:r,counts:A(d,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});k=v.p205Authority,T=!0}t.push("terminal_git");const I=new Date,L=await lt(e.projectRoot,{kind:"continuous",storyId:de.epicId,sessionId:`${e.runnerId}:${e.attempt.attempt_id}`,idempotencyKey:de.idempotencyKey,persistent:!1,expiresAt:new Date(I.getTime()+Math.max(e.timeoutMs,e.maxDurationMs)+6e4).toISOString()});if(!L.ok)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:L.reasonCode,publicSummary:"Headless yoloop deferred terminal Git while another physical writer retained authority.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});let U,H=!1;try{U=await dt(de,e.terminalGitAdapter,e.now,k)}finally{H=await ut(L.path,L.owner).catch(()=>!1)}if(!H)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:"git-lifecycle-integration-lock-release-failed",publicSummary:"Headless yoloop stopped because terminal Git writer ownership could not be released safely.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(n[`terminal-git-${d}`]=x(`${U.receipt.idempotencyKey}:${U.receipt.physicalStatus}`),!U.ok){const v=U.receipt.retryEligible;return t.push("backoff"),O({status:v?"backoff":"failed",receiptStatus:v?"deferred":"failed",reasonCode:U.reasonCodes[0]??"epic-terminal-git-failed",publicSummary:"Headless yoloop stopped at the epic terminal Git boundary with public-safe retry evidence.",retryable:v,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t})}if(S.complete)return u+=1,t.push("complete"),O({status:"completed",receiptStatus:"applied",reasonCode:p.EXECUTION_COMPLETED,publicSummary:"Headless yoloop completed only after fresh root-owned physical Git verification.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});d+=1,t.push("reinvoke");continue}if(S.complete)return u+=1,t.push("complete"),O({status:"completed",receiptStatus:"applied",reasonCode:p.EXECUTION_COMPLETED,publicSummary:"Headless yoloop completed after public metadata reported yoloopComplete.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const ue=$t(b);if(ue){t.push("git_reconciliation");const I=await it(ue,e.terminalGitAdapter,e.now);if(n[`git-reconciliation-${d}`]=x(`${ue.idempotencyKey}:${I.reasonCodes.join(",")}`),!I.ok)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:I.reasonCodes[0]??"git-reconciliation-failed",publicSummary:"Headless yoloop paused at a fail-closed Git reconciliation boundary.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t})}const N=Nt(b,e.prepared.epicId);if(!N.ok)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:N.reasonCode,publicSummary:N.publicSummary,retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:[...c,...N.redactionSummary],relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const ge=N.continuous?void 0:N.prompts.find(I=>a.has(I.storyId));if(ge)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:p.YOLOOP_ISOLATION_VIOLATION,publicSummary:"Headless yoloop rejected cyclic child story delegation evidence.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:[...c,h("storyId","prompt_body",x(ge.storyId))],relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(i.mode=N.mode==="continuous_dag"?"continuous_dag":N.parallelAuthorized?"parallel_wave":"serial",i.serverMaxConcurrency=N.serverMaxConcurrency,N.continuous){j=!0;const I=N.prompts.filter(L=>!a.has(L.storyId)&&!E.has(L.storyId));if(i.pendingChildCount=I.length+E.size,I.length>0){const L=Le({store:e.store,nowIso:e.now(),localCap:e.localConcurrencyCap,serverMaxConcurrency:N.serverMaxConcurrency,parallelAuthorized:!0,continuous:!0,eligibleCount:I.length});if(i.queueCap=L.queueCap,i.availableLeases=L.availableLeases,i.effectiveConcurrency=L.effectiveConcurrency,i.reasonCodes.push(L.reasonCode),L.effectiveConcurrency===0&&E.size===0)return i.backpressurePauseCount+=1,t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:p.WORKER_POOL_BACKPRESSURE_PAUSED,publicSummary:"Headless yoloop retained its eligible frontier while physical worker capacity was unavailable.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const U=I.slice(0,L.effectiveConcurrency);t.push("child_story_delegation");for(const H of U)E.set(H.storyId,{child:H,promise:xe({...e,child:H,iteration:d})});i.startedChildCount+=U.length,i.peakConcurrency=Math.max(i.peakConcurrency,E.size),i.pendingChildCount=I.length-U.length+E.size}if(E.size>0)await X();else{if(K.size>0)return t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:[...K.values()].sort()[0],publicSummary:"Headless yoloop retained deferred continuous work for a bounded retry.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});if(z.size>0)return t.push("backoff"),O({status:"failed",receiptStatus:"failed",reasonCode:[...z.values()].sort()[0],publicSummary:"Headless yoloop completed independent work and stopped on the remaining failed dependency closure.",retryable:!1,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t})}d+=1,t.push("reinvoke");continue}i.pendingChildCount=N.prompts.length;const te=[...N.prompts];for(;te.length>0;){const I=Le({store:e.store,nowIso:e.now(),localCap:e.localConcurrencyCap,serverMaxConcurrency:N.serverMaxConcurrency,parallelAuthorized:N.parallelAuthorized,continuous:!1,eligibleCount:te.length});if(i.queueCap=I.queueCap,i.availableLeases=I.availableLeases,i.effectiveConcurrency=I.effectiveConcurrency,i.reasonCodes.push(I.reasonCode),I.effectiveConcurrency===0)return i.backpressurePauseCount+=1,i.pendingChildCount=te.length,t.push("backoff"),O({status:"backoff",receiptStatus:"deferred",reasonCode:p.WORKER_POOL_BACKPRESSURE_PAUSED,publicSummary:"Headless yoloop paused child workers because bounded queue or lease capacity was unavailable.",retryable:!0,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t});const L=te.splice(0,I.effectiveConcurrency);i.startedChildCount+=L.length,i.peakConcurrency=Math.max(i.peakConcurrency,L.length),i.pendingChildCount=te.length,t.push("child_story_delegation");const U=await Promise.all(L.map(v=>xe({...e,child:v,iteration:d})));i.completedChildCount+=U.length;let H;for(const v of U){const ce=v.child;n[`child-${ce.storyId.replace(/[^A-Za-z0-9_.:-]/g,"-")}`]=v.commandHash,c.push(v.promptRedaction),o.push(ce.storyId),s.push(ce.targetRef),a.add(ce.storyId);const re=v.receipt;if(!re){v.reasonCodes.includes("queue-cap-reached")?(i.backpressurePauseCount+=1,i.pendingChildCount+=1,i.reasonCodes.push(p.WORKER_POOL_BACKPRESSURE_PAUSED),H??={kind:"deferred",reasonCode:p.WORKER_POOL_BACKPRESSURE_PAUSED}):(y+=1,H??={kind:"missing",reasonCode:p.EXECUTION_FAILED});continue}if(re.outcome==="deferred"){H??={kind:"deferred",reasonCode:p.EXECUTION_BACKOFF};continue}if(re.outcome==="failed"){y+=1,H??={kind:"failed",reasonCode:re.reasonCode||p.EXECUTION_FAILED};continue}re.outcome==="ignored"?f+=1:l+=1}if(H){const v=H.kind==="deferred";return O({status:v?"backoff":"failed",receiptStatus:v?"deferred":"failed",reasonCode:H.reasonCode,publicSummary:H.kind==="missing"?"Headless yoloop child story delegation did not produce a public receipt.":v?"Headless yoloop child story delegated bounded backoff to the root attempt.":"Headless yoloop child story execution failed with public receipt evidence.",retryable:v,startedAt:r,counts:A(d+1,o.length,l,y,f,u,m),workerPoolSummary:g(i),hashes:n,redactions:c,relativePaths:[e.prepared.targetRef,...s],childStoryIds:o,stateTransitions:t})}}d+=1,t.push("reinvoke")}}async function xe(e){const r=e.child.dispatchId?await e.continuousWorkspaceManager.prepare({projectRoot:e.projectRoot,epicId:e.prepared.epicId,storyId:e.child.storyId,targetRef:e.child.targetRef,dispatchId:e.child.dispatchId,executionBranch:e.child.executionBranch??"",graphRevision:e.child.graphRevision??"",authorizedWriteSet:e.child.writeSet??[]}):void 0;if(r&&!r.ok)return{child:e.child,reasonCodes:[r.reasonCode],commandHash:x(e.child.commandRef),promptRedaction:h(`child.${e.child.storyId}`,"prompt_body",x(e.child.commandRef)),terminalFailure:r.reasonCode,deferredFailure:/backpressure|lease-active/.test(r.reasonCode)};const t=e.projectRoot,a=r?.ok?nt(r.value):e.adapterOptions?.commandRunner,o=Vt({rootAttempt:e.attempt,child:e.child,iteration:e.iteration,runtimeId:e.prepared.runtimeId,modelId:e.prepared.modelId,runtimeSource:e.prepared.runtimeSource}),s=q("attempt",o.idempotencyKey);let n=!!r;try{const c=await ot({store:e.store,projectRoot:t,runnerId:e.runnerId,now:e.now,metadata:{continuityExecutionDirectives:[o]},adapterRegistry:e.registry,adapterOptions:{...e.adapterOptions,cwd:t,env:{...e.adapterOptions?.env,...r?.ok?{NEOCORTEX_BRANCHLESS_PROJECT_ROOT:r.value.workspacePath,NEOCORTEX_BRANCHLESS_ROOT_AUTHORITY:JSON.stringify(e.child.rootAuthority)}:{}},...a?{commandRunner:a,versionRunner:a}:{}},runtimeDefaults:e.runtimeDefaults,runtimeFallback:e.runtimeFallback,timeoutMs:e.timeoutMs,maxAttempts:1,limit:1,queueCap:e.queueCap,attemptIds:[s],idFactory:e.idFactory}),d=c.receipts.find(l=>l.attemptId===s)??c.receipts[0];if(r?.ok&&d&&d.outcome!=="failed"&&d.outcome!=="deferred"){const l=await e.continuousWorkspaceManager.integrate(r.value,{receiptRefs:[`continuity:${d.attemptId}`],nowIso:d.timestamp});if(!l.ok)return{child:e.child,receipt:d,reasonCodes:[...c.reasonCodes,l.reasonCode],commandHash:x(e.child.commandRef),promptRedaction:h(`child.${e.child.storyId}`,"prompt_body",x(e.child.commandRef)),terminalFailure:l.reasonCode,deferredFailure:/backpressure|graph-revision-stale/.test(l.reasonCode)};n=!1}return{child:e.child,receipt:d,reasonCodes:c.reasonCodes,commandHash:x(e.child.commandRef),promptRedaction:h(`child.${e.child.storyId}`,"prompt_body",x(e.child.commandRef))}}finally{r?.ok&&await e.continuousWorkspaceManager.release(r.value,n)}}function Le(e){const r=e.store.collectSnapshotRows({nowIso:e.nowIso,jobLimit:1,staleLeaseLimit:1,receiptLimit:1,eventLimit:1,snapshotLimit:1}),t=Math.max(1,Math.floor(r.queue.capacity)),a=Math.max(0,Math.floor(r.queue.running)-1),o=e.continuous?Math.max(0,Math.floor(r.queue.available)):Math.max(0,Math.min(Math.max(0,e.localCap-a),Math.floor(r.queue.available))),s=e.parallelAuthorized?Math.max(1,Math.floor(e.serverMaxConcurrency)):1,n=Math.max(0,Math.min(e.eligibleCount,s,e.localCap,t,o));return{queueCap:t,availableLeases:o,effectiveConcurrency:n,reasonCode:n===0?p.WORKER_POOL_BACKPRESSURE_PAUSED:(e.parallelAuthorized||e.continuous)&&n>1?p.WORKER_POOL_PARALLEL_ACTIVE:p.WORKER_POOL_SERIAL_FALLBACK}}function De(e){return{mode:"serial",serverMaxConcurrency:1,localCap:e,queueCap:1,availableLeases:1,effectiveConcurrency:1,peakConcurrency:0,pendingChildCount:0,startedChildCount:0,completedChildCount:0,backpressurePauseCount:0,reasonCodes:[]}}function g(e){return{mode:e.mode,serverMaxConcurrency:Y(e.serverMaxConcurrency),localCap:Y(e.localCap),queueCap:Y(e.queueCap),availableLeases:Y(e.availableLeases),effectiveConcurrency:Y(e.effectiveConcurrency),peakConcurrency:Y(e.peakConcurrency),pendingChildCount:Y(e.pendingChildCount),startedChildCount:Y(e.startedChildCount),completedChildCount:Y(e.completedChildCount),backpressurePauseCount:Y(e.backpressurePauseCount),reasonCodes:V(e.reasonCodes.length>0?e.reasonCodes:[p.WORKER_POOL_SERIAL_FALLBACK]).slice(0,10),publicSafe:!0}}function he(e=Pe()){return g(De(e))}function Y(e){return Number.isFinite(e)?Math.max(0,Math.min(1e4,Math.floor(e))):0}function xt(e){const r=Re(e.attempt.metadata_json,{autonomousKind:"run_yoloop",jobId:e.attempt.job_id,targetKind:"epic",targetRef:e.attempt.target_ref});if(!r.ok)return{ok:!1,status:"failed",receiptStatus:"failed",reasonCode:r.reasonCode,publicSummary:r.publicSummary,redactionSummary:r.redactionSummary};const t=Lt(e.attempt);if(!t.ok)return t;const a=Dt(e);if(!a.ok)return{ok:!1,status:"failed",receiptStatus:"failed",reasonCode:a.reasonCode,publicSummary:a.publicSummary,redactionSummary:a.redactionSummary};const o=e.attempt.epic_id??ne(e.attempt.target_ref);return{ok:!0,input:{rootPrompt:`*yoloop @${e.attempt.target_ref}`,targetRef:e.attempt.target_ref,epicId:o,runtimeId:a.runtimeId,...a.modelId?{modelId:a.modelId}:{},runtimeSource:a.runtimeSource,publicSafe:!0}}}function Lt(e){const r=[];e.directive_kind!=="run_yoloop"&&r.push(h("directiveKind","unsafe_key","value-denied")),e.target_kind!=="epic"&&r.push(h("targetKind","prompt_body","value-denied")),(!fe.test(e.target_ref)||W.test(e.target_ref)||D.test(e.target_ref))&&r.push(h("targetRef","prompt_body","value-denied"));const t=ne(e.target_ref);return e.epic_id&&e.epic_id!==t&&r.push(h("epicId","prompt_body","value-denied")),r.length>0?{ok:!1,status:"failed",receiptStatus:"failed",reasonCode:p.PROMPT_REJECTED,publicSummary:"Headless run_yoloop attempt did not match the canonical public epic prompt shape.",redactionSummary:r}:{ok:!0,input:{rootPrompt:`*yoloop @${e.target_ref}`,targetRef:e.target_ref,epicId:t,runtimeId:"fake",publicSafe:!0}}}function Dt(e){const r=e.runtimeDefaults!==void 0?e.runtimeDefaults:Ge({projectRoot:e.projectRoot});if(_(r)&&r.ok===!1)return{ok:!1,reasonCode:p.RUNTIME_UNTRUSTED,publicSummary:typeof r.publicSummary=="string"?r.publicSummary:"Headless runtime defaults failed public-safety validation.",redactionSummary:Array.isArray(r.redactionSummary)?r.redactionSummary:[]};const t=rr(e.attempt.runtime_source)??"explicit",a=e.attempt.runtime_id||e.attempt.model_id?{...e.attempt.runtime_id?{runtimeId:e.attempt.runtime_id}:{},...e.attempt.model_id?{modelId:e.attempt.model_id}:{},source:t,publicSafe:!0}:void 0,o={registry:e.registry,defaults:r,fallback:e.runtimeFallback};a&&(t==="origin"?o.origin=a:t==="default"?o.projectDefault=a:t==="fallback"?o.fallback=a:o.explicit=a);const s=Xe(o);return s.ok?{ok:!0,runtimeId:s.runtimeId,...s.modelId?{modelId:s.modelId}:{},runtimeSource:s.runtimeSource}:{ok:!1,reasonCode:s.reasonCode,publicSummary:s.publicSummary,redactionSummary:s.redactionSummary}}function Mt(e){const r=[];for(const i of Object.keys(e))(!St.has(i)||oe.test(i))&&r.push(h(i,Q(i),"key-denied"));e.publicSafe!==!0&&r.push(h("publicSafe","unsafe_key","value-denied"));const t=B(e,"kind"),a=B(e,"commandClass"),o=B(e,"jobId"),s=B(e,"operationId"),n=B(e,"idempotencyKey"),c=B(e,"targetKind"),d=B(e,"targetRef"),l=B(e,"commandRef"),y=B(e,"publicSummary"),f=B(e,"epicId");t!=="run_yoloop"&&r.push(h("kind","unsafe_key","value-denied")),a!=="goal-tick"&&a!=="loop-tick"&&r.push(h("commandClass","unsafe_key","value-denied")),be(o)||r.push(h("jobId","unsafe_key","value-denied")),be(s)||r.push(h("operationId","unsafe_key","value-denied")),be(n)||r.push(h("idempotencyKey","unsafe_key","value-denied")),(c!=="epic"||!d||!fe.test(d)||D.test(d))&&r.push(h("targetRef","prompt_body","value-denied"));const u=d?`*yoloop @${d}`:void 0;(!l||l!==u||l.includes("@neocortex")||W.test(l)||D.test(l))&&r.push(h("commandRef","prompt_body","value-denied")),f&&d&&f!==ne(d)&&r.push(h("epicId","prompt_body","value-denied")),(!y||y.length>240||D.test(y))&&r.push(h("publicSummary","unbounded_text","value-denied")),e.reasonCode!==void 0&&(typeof e.reasonCode!="string"||D.test(e.reasonCode))&&r.push(h("reasonCode","unsafe_key","value-denied"));const m=Gt(e.runtime,r);e.attempts!==void 0&&le(e.attempts,"attempts",r),e.metadata!==void 0&&le(e.metadata,"metadata",r);const k=_(e.metadata)?e.metadata:void 0,T=Jt(e.redactionSummary,r);return r.length>0||t!=="run_yoloop"||!a||!o||!s||!n||!d||!l||!y?{ok:!1,reasonCode:r.some(E=>E.reason==="prompt_body")?p.PROMPT_REJECTED:p.DIRECTIVE_REJECTED,redactionSummary:r}:{ok:!0,directive:{kind:"run_yoloop",commandClass:a,jobId:o,operationId:s,idempotencyKey:n,targetKind:"epic",targetRef:d,commandRef:l,...f?{epicId:f}:{},...m?{runtime:m}:{},publicSummary:y,...typeof e.reasonCode=="string"?{reasonCode:e.reasonCode}:{},...k?{metadata:k}:{},...T.length>0?{redactionSummary:T}:{},publicSafe:!0}}}function Nt(e,r){if(!e)return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop metadata was missing.",[]);const t=Bt(e);if(!t.ok)return t;const a=[],o=_(e.yoloopExecutionPlan)?e.yoloopExecutionPlan:void 0,s=o?w(o.storyIds).filter(G):[],n=o?.version===3&&_(o.scheduler)&&Array.isArray(o.scheduler.nodes)?new Map(o.scheduler.nodes.filter(_).map(u=>[String(u.storyId??""),u])):new Map,c=o?.version===3&&_(o.scheduler)&&typeof o.scheduler.graphRevision=="string"?o.scheduler.graphRevision:void 0,d=o?.version===3&&typeof o.executionBranch=="string"?o.executionBranch:void 0;let l;if(o){const u=jt(o);if(!u.ok)return u;l=u;for(const k of Ut(o)){const T=J(k,r,"execution-plan");if(!T.ok)return T;a.push(Me(T.prompt,n.get(T.prompt.storyId),c,d))}if(a.length===0)for(const k of s){const T=J(`*yolo @docs/stories/${k}.story.md`,r,"execution-plan");if(!T.ok)return T;a.push(Me(T.prompt,n.get(T.prompt.storyId),c,d))}const m=Ht(e.skillDirectives,a);if(!m.ok)return m}const y=typeof e.nextStoryId=="string"&&G(e.nextStoryId)?e.nextStoryId:void 0;for(const u of o?[]:Array.isArray(e.skillDirectives)?e.skillDirectives:[]){if(!_(u)||u.kind!=="task")continue;const m=w(u.promptTemplates);if(m.length>0){for(const E of m){const j=J(E,r,"task-directive");if(!j.ok)return j;a.push(j.prompt)}continue}const k=typeof u.promptTemplate=="string"?u.promptTemplate:void 0;if(!k)continue;const T=w(u.storyIds).filter(G),i=T.length>0?T:y?[y]:[];if(i.length===0&&ye.test(k)){const E=J(k,r,"task-directive");if(!E.ok)return E;a.push(E.prompt);continue}for(const E of i){const j=k.includes("{storyId}")?k.replaceAll("{storyId}",E):k,K=J(j,r,"task-directive");if(!K.ok)return K;a.push(K.prompt)}}if(a.length===0&&y){const u=J(`*yolo @docs/stories/${y}.story.md`,r,"next-story");if(!u.ok)return u;a.push(u.prompt)}const f=nr(a,u=>u.commandRef);if(l?.continuous){if(f.length!==s.length||f.some(u=>u.preparationOnly!==!0||u.source!=="execution-plan"))return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless continuous yoloop rejected an ambiguous or non-preparation child prompt.",[h("promptTemplates","prompt_body","operation-denied")])}else if(f.some(u=>u.preparationOnly===!0))return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected branchless preparation outside a validated continuous plan.",[h("promptTemplates","prompt_body","operation-denied")]);if(s.length>0){const u=new Set(s);for(const m of f)if(!u.has(m.storyId))return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected a child prompt outside the server-authorized execution plan.",[h("storyId","prompt_body",x(m.storyId))])}return l?.parallelAuthorized&&!Z(f.map(u=>u.storyId),s)?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop rejected a parallel plan whose executable prompts did not match the selected wave.",[]):f.length===0&&!l?.continuous?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop metadata did not include an authorized per-story child prompt.",[]):{ok:!0,prompts:f,mode:l?.mode??"legacy",serverMaxConcurrency:l?.serverMaxConcurrency??1,parallelAuthorized:l?.parallelAuthorized??!1,continuous:l?.continuous??!1,reasonCodes:[p.OK,l?.parallelAuthorized?p.WORKER_POOL_PARALLEL_ACTIVE:p.WORKER_POOL_SERIAL_FALLBACK]}}function Me(e,r,t,a){if(!r||!t||!a)return e;const o=Array.isArray(r.writeSet)?w(r.writeSet):[],s=Ne(r.childExecutionAuthority,{storyId:e.storyId,invocationPrompt:e.commandRef,authorizedWriteSet:o});return{...e,...typeof r.dispatchId=="string"?{dispatchId:r.dispatchId}:{},...typeof r.idempotencyKey=="string"?{idempotencyKey:r.idempotencyKey}:{},graphRevision:t,executionBranch:a,...o.length>0?{writeSet:o}:{},...s?{rootAuthority:s}:{}}}function Bt(e){const r=[];return e.nextStoryId!==void 0&&ae(e.nextStoryId,"nextStoryId",r),e.yoloopExecutionPlan!==void 0&&ae(e.yoloopExecutionPlan,"yoloopExecutionPlan",r),Array.isArray(e.skillDirectives)&&e.skillDirectives.forEach((t,a)=>{_(t)&&t.kind==="task"&&ae(t,`skillDirectives.${a}`,r)}),r.length>0?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop rejected unsafe public metadata before child delegation.",r):{ok:!0}}function Ht(e,r){if(!Array.isArray(e))return{ok:!0};const t=new Set(r.map(a=>a.commandRef));for(const[a,o]of e.entries()){if(!_(o)||o.kind!=="task")continue;const s=typeof o.promptTemplate=="string"?o.promptTemplate:void 0,n=w(o.promptTemplates);if(s==="[REDACTED]"&&o.subagentType==="neocortex"&&typeof o.reasonCode=="string"&&o.reasonCode.startsWith("directive-prompt-")&&n.length===0)continue;const d=[...s?[s]:[],...n];if(o.subagentType!=="neocortex"||d.length===0||d.some(l=>!t.has(l)))return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected a task directive that conflicted with the server-authorized execution plan.",[h(`skillDirectives.${a}`,"prompt_body","operation-denied")])}return{ok:!0}}function ae(e,r,t){if(typeof e=="string"){if(/\.allowedDebt\.constraints\.\d+$/.test(r)&&bt.has(e))return;(D.test(e)||/\0/.test(e))&&t.push(h(r,Q(r),"value-denied"));return}if(!(e==null||typeof e=="number"||typeof e=="boolean")){if(Array.isArray(e)){e.forEach((a,o)=>ae(a,`${r}.${o}`,t));return}if(_(e)){for(const[a,o]of Object.entries(e)){const s=`${r}.${a}`;!(a==="promptTemplate"||a==="promptTemplates"||a==="invocationPrompt"||a==="childPromptKind"||a==="allowMetaTaskPromptInChild"||a==="authorization")&&oe.test(a)&&t.push(h(s,Q(a),"key-denied")),ae(o,s,t)}return}t.push(h(r,"unsafe_key","value-denied"))}}function jt(e){const r=typeof e.mode=="string"?e.mode:"",t=_(e.isolationPolicy)?e.isolationPolicy:void 0,a=w(e.storyIds);if(e.authorization!=="server_authorized")return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop execution plan was not server-authorized.",[]);if(e.executor!=="root_agent"&&e.executor!=="root_agent_with_child_tasks")return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop execution plan executor was not root-owned.",[]);if(r!=="serial"&&r!=="parallel_wave"&&r!=="continuous_dag"&&r!=="manual")return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop execution plan mode was invalid.",[]);const o=e.version===3&&r==="continuous_dag";if(!Array.isArray(e.storyIds)||!o&&a.length===0||a.length!==e.storyIds.length||a.some(i=>!G(i))||new Set(a).size!==a.length)return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop execution plan story selection was invalid or ambiguous.",[]);if(!t||t.rootOwnsLoop!==!0||t.childPromptKind==="per_story_branchless_preparation_only"&&!o||Object.prototype.hasOwnProperty.call(t,"childExecutionBoundary")&&!o||t.childPromptKind!=="per_story_yolo_only"&&t.childPromptKind!=="per_story_branchless_preparation_only"&&!(t.childPromptKind==="none"&&(r==="serial"&&t.childTaskDelegation==="not_required"||r==="parallel_wave"&&e.executor==="root_agent"&&t.childTaskDelegation==="forbidden"))||t.allowOuterYoloopInChild!==!1||t.allowMetaTaskPromptInChild!==!1)return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected an execution plan that did not preserve root-owned child isolation.",[h("isolationPolicy","prompt_body","value-denied")]);const s=Array.isArray(e.skippedStories)?e.skippedStories:[];for(const i of s){if(!_(i)||typeof i.storyId!="string"||!G(i.storyId)||typeof i.reason!="string")return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop skipped-story evidence was malformed.",[]);if(a.includes(i.storyId))return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop selected a story that server evidence marked skipped.",[])}if(o){const i=_(e.scheduler)?e.scheduler:void 0,E=Array.isArray(i?.nodes)?i.nodes.filter(_):[],j=w(i?.readyStoryIds),K=w(i?.runningStoryIds),z=w(e.promptTemplates),se=_(e.dependencyBasis)?e.dependencyBasis:void 0,X=_(e.readinessBasis)?e.readinessBasis:void 0,P=typeof i?.graphRevision=="string"?i.graphRevision:"",b=typeof e.executionBranch=="string"?e.executionBranch:"",M=E.map(C=>typeof C.storyId=="string"?C.storyId:""),S=E.find(C=>{const $=typeof C.storyId=="string"?C.storyId:"",ee=typeof C.promptTemplate=="string"?C.promptTemplate:"",ie=Array.isArray(C.writeSet)?w(C.writeSet):[];return!G($)||ee!==`*yolo @docs/stories/${$}.story.md branchless-preparation`||typeof C.dispatchId!="string"||!/^[a-f0-9]{64}$/.test(C.dispatchId)||typeof C.idempotencyKey!="string"||!/^[a-f0-9]{64}$/.test(C.idempotencyKey)||C.dependencyBasis!=="verified"||!Array.isArray(C.dependencies)||w(C.dependencies).length!==C.dependencies.length||!Array.isArray(C.writeSet)||ie.length===0||ie.length!==C.writeSet.length||!Ne(C.childExecutionAuthority,{storyId:$,invocationPrompt:ee,authorizedWriteSet:ie})});return e.executor!=="root_agent_with_child_tasks"||e.legacyConcurrencyIgnored!==!0||Object.prototype.hasOwnProperty.call(e,"maxConcurrency")||Object.prototype.hasOwnProperty.call(e,"maxParallel")||t?.rootOwnedScheduling!=="continuous_dag_allowed"||t?.childPromptKind!=="per_story_branchless_preparation_only"||t?.childExecutionBoundary!=="filesystem_only"||t?.childTaskDelegation!=="allowed"||t?.maxDepth!==1||t?.childWorkspace!=="branchless_sandbox"||t?.epicCheckout!=="current_checkout"||t?.workspaceLifecycle!=="user_managed"||t?.integrationOwner!=="root_agent"||!i||i.schemaVersion!==1||i.strategy!=="continuous-event-driven-dag"||i.logicalConcurrency!=="ready-set"||i.capacityPolicy!=="backpressure-only"||i.rootOwnsLoop!==!0||i.maxDepth!==1||!/^[a-f0-9]{64}$/.test(P)||!Qt(b)||new Set(M).size!==M.length||M.some(C=>!G(C))||j.some(C=>!M.includes(C))||K.some(C=>!M.includes(C)||j.includes(C))||!Z(a,j)||z.length!==a.length||z.some((C,$)=>C!==`*yolo @docs/stories/${a[$]}.story.md branchless-preparation`)||!se||se.source!=="state_snapshot"||!X||X.source!=="ready_wave"||!Z(w(X.selectedStoryIds),a)||S?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop rejected an invalid continuous DAG execution plan.",[]):{ok:!0,mode:"continuous_dag",serverMaxConcurrency:a.length,parallelAuthorized:!0,continuous:!0}}if(e.version===2){const i=at(e);return i.ok?{ok:!0,mode:"parallel_wave",serverMaxConcurrency:i.childExecutionConcurrency,parallelAuthorized:!1,continuous:!1}:R(p.YOLOOP_METADATA_INVALID,"Headless yoloop rejected an unsafe phased execution plan.",[])}if(r!=="parallel_wave")return{ok:!0,mode:r,serverMaxConcurrency:1,parallelAuthorized:!1,continuous:!1};const n=e.maxConcurrency,c=e.maxParallel,d=_(e.dependencyBasis)?e.dependencyBasis:void 0,l=_(e.readinessBasis)?e.readinessBasis:void 0,y=Array.isArray(e.parallelWaves)?e.parallelWaves:[],f=y.length===1&&_(y[0])?y[0]:void 0;if(t.rootOwnedScheduling!=="parallel_wave_allowed"||!Number.isInteger(n)||typeof n!="number"||n<2||n>a.length||!Number.isInteger(c)||c!==n||!d||d.source!=="state_snapshot"||!Z(w(d.storyIds),a)||!l||l.source!=="ready_wave"||!Z(w(l.selectedStoryIds),a)||!f||!Z(w(f.storyIds),a)||f.maxConcurrency!==n)return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop parallel plan lacked consistent bounded authorization and readiness evidence.",[]);const u=w(f.promptTemplates);if(u.length!==a.length||new Set(u).size!==u.length)return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop parallel wave prompts were incomplete or ambiguous.",[]);const k=(Array.isArray(f.proofSummaries)?f.proofSummaries:Array.isArray(e.proofSummaries)?e.proofSummaries:[]).find(i=>_(i)&&Z(w(i.storyIds),a));return!_(k)||k.dependencyClosure!=="resolved"||k.contractOwnership!=="non_overlapping"||k.validationSurfaces!=="compatible"?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop parallel plan lacked resolved independence proof.",[]):s.find(i=>_(i)&&typeof i.reason=="string"&&/(cycle|unresolved|ambiguous)/i.test(i.reason))?R(p.YOLOOP_METADATA_INVALID,"Headless yoloop parallel plan contained unresolved dependency evidence.",[]):{ok:!0,mode:"parallel_wave",serverMaxConcurrency:n,parallelAuthorized:!0,continuous:!1}}function Ne(e,r){if(!_(e)||!_(e.artifact))return;const t=e.artifact,a=Array.isArray(e.authorizedWriteSet)?w(e.authorizedWriteSet):[];if(Object.keys(e).sort().join("\0")!==["artifact","authorizedWriteSet","effectScope","invocationPrompt","kind","lifecycleAuthority","publicSafe","schemaVersion","storyId"].sort().join("\0")||Object.keys(t).sort().join("\0")!==["content","contentBinding","kind","path","schemaVersion"].sort().join("\0")||e.schemaVersion!==1||e.kind!=="branchless_story_preparation"||e.storyId!==r.storyId||e.invocationPrompt!==r.invocationPrompt||!Array.isArray(e.authorizedWriteSet)||a.length!==e.authorizedWriteSet.length||a.some(s=>pt(s)!==s)||!Kt(a,r.authorizedWriteSet)||e.effectScope!=="filesystem_only"||e.lifecycleAuthority!=="root_only"||e.publicSafe!==!0||t.schemaVersion!==1||t.kind!=="branchless_root_bound_artifact"||typeof t.path!="string"||!a.includes(t.path)||typeof t.content!="string"||t.content.length===0||t.content.length>4096||typeof t.contentBinding!="string"||!/^[a-f0-9]{64}$/.test(t.contentBinding))return;const o=je("sha256").update(["branchless-root-bound-artifact-v1",r.storyId,r.invocationPrompt,...a,"filesystem_only","root_only",t.path,t.content].join("\0")).digest("hex");if(t.contentBinding===o)return e}function Kt(e,r){return e.length===r.length&&e.every((t,a)=>t===r[a])}function Ut(e){const r=[];r.push(...w(e.promptTemplates));for(const t of Array.isArray(e.serialSteps)?e.serialSteps:[])_(t)&&typeof t.promptTemplate=="string"&&r.push(t.promptTemplate);for(const t of Array.isArray(e.parallelWaves)?e.parallelWaves:[])_(t)&&r.push(...w(t.promptTemplates));return r}function J(e,r,t){if(Te(e))return R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected an unsafe child prompt shape.",[h("promptTemplate","prompt_body",x(e))]);if(!ye.test(e))return R(p.YOLOOP_METADATA_INVALID,"Headless yoloop child prompt did not match the canonical per-story yolo shape.",[h("promptTemplate","prompt_body",x(e))]);const a=_t.test(e),o=e.replace(/^\*yolo @/,"").replace(/ branchless-preparation$/,""),s=tr(o);return!ht.test(o)||!G(s)||!s.startsWith(`${r}.`)?R(p.YOLOOP_ISOLATION_VIOLATION,"Headless yoloop rejected a child prompt outside the targeted public epic.",[h("storyId","prompt_body",x(s))]):{ok:!0,prompt:{storyId:s,targetRef:o,commandRef:e,source:t,...a?{preparationOnly:!0}:{}}}}function zt(e){const r=e?.epicTerminalGitDirective;if(!(!_(r)||r.schemaVersion!==1||r.kind!=="epic_terminal_git"||typeof r.epicId!="string"||typeof r.executionBranch!="string"||typeof r.originBranch!="string"||typeof r.expectedExecutionHeadSha!="string"||!Array.isArray(r.storyCheckpointIds)||!_(r.localGates)||r.localGates.passed!==!0||!Array.isArray(r.localGates.receiptRefs)||!_(r.reconcile)||typeof r.idempotencyKey!="string"||r.publicSafe!==!0))return r}function $t(e){const r=e?.epicGitReconciliationDirective;if(!(!_(r)||r.schemaVersion!==1||r.kind!=="epic_git_reconciliation"||typeof r.epicId!="string"||typeof r.executionBranch!="string"||typeof r.originBranch!="string"||!["start","resume","audit"].includes(String(r.lifecycleBoundary))||typeof r.dryRun!="boolean"||typeof r.idempotencyKey!="string"||r.publicSafe!==!0))return r}function Yt(e){if(!e||!_(e))return{ok:!1,reasonCode:p.YOLOOP_METADATA_INVALID,publicSummary:"Headless yoloop root invocation did not return public metadata.",redactionSummary:[]};const r=_(e.yoloopContinuationDecision)?e.yoloopContinuationDecision:void 0,t=typeof r?.outcome=="string"?r.outcome:void 0,a=t==="queue_exhausted"||e.yoloopComplete===!0,o=e.yoloopAborted===!0,s=mt(e.releaseBlocked),n=t==="selected"&&r?.replay===!0;return t&&!["selected","queue_exhausted","blocked","cycle","invalid","no_progress"].includes(t)?{ok:!1,reasonCode:p.YOLOOP_METADATA_INVALID,publicSummary:"Headless yoloop rejected an unknown continuation outcome.",redactionSummary:[]}:t==="queue_exhausted"&&r?.terminalSuccess!==!0?{ok:!1,reasonCode:p.YOLOOP_METADATA_INVALID,publicSummary:"Headless yoloop rejected inconsistent queue exhaustion metadata.",redactionSummary:[]}:a&&o?{ok:!1,reasonCode:p.YOLOOP_METADATA_INVALID,publicSummary:"Headless yoloop metadata reported mutually exclusive terminal states.",redactionSummary:[h("yoloopTerminal","unsafe_key","value-denied")]}:(e.yoloopReleaseBlocked===!0||e.terminalClass==="release-blocked"||e.releaseBlocked!==void 0)&&(!s||e.yoloopReleaseBlocked!==!0||e.terminalClass!=="release-blocked"||a||o||t!==void 0)?{ok:!1,reasonCode:p.YOLOOP_METADATA_INVALID,publicSummary:"Headless yoloop rejected inconsistent release-blocked terminal metadata.",redactionSummary:[]}:{ok:!0,complete:a,aborted:o,...s?{releaseBlocked:s}:{},outcome:t,replay:n}}function Ft(e){if(!e)return;const r=e.abortReason;if(r!=="YOLOOP_PER_RUN_CAP_REACHED"&&r!=="YOLOOP_SESSION_TIMEOUT")return;const t=e.yoloopResumableBoundary,a=/^[A-Za-z0-9._:-]{1,128}$/;if(!(!_(t)||t.schemaVersion!=="YoloopResumableBoundary/1.0"||t.resumable!==!0||t.reason!==r||t.publicSafe!==!0||typeof t.coolDownUntil!="string"||!Number.isFinite(Date.parse(t.coolDownUntil))||!Number.isSafeInteger(t.retryAfterMs)||t.retryAfterMs<0||typeof t.operationId!="string"||!a.test(t.operationId)||!_(t.receipt)||t.receipt.schemaVersion!=="YoloopSessionRolloverReceipt/1.0"||typeof t.receipt.runId!="string"||!a.test(t.receipt.runId)||t.receipt.operationId!==t.operationId||typeof t.receipt.idempotencyKey!="string"||!a.test(t.receipt.idempotencyKey)||typeof t.receipt.payloadHash!="string"||!/^[a-f0-9]{64}$/.test(t.receipt.payloadHash)||t.receipt.coolDownUntil!==t.coolDownUntil||!Number.isSafeInteger(t.receipt.revision)||t.receipt.revision<0||t.receipt.snapshotHash!==null&&(typeof t.receipt.snapshotHash!="string"||!/^[a-f0-9]{64}$/.test(t.receipt.snapshotHash))||t.receipt.status!=="exhausted"||t.receipt.publicSafe!==!0))return{coolDownUntil:t.coolDownUntil,reason:r}}function Vt(e){const r=`${e.rootAttempt.execution_id}:${e.child.graphRevision??"legacy"}:${e.child.storyId}`,t=e.child.dispatchId??q("child-op",`${r}:${e.iteration}`),a=e.child.idempotencyKey??q("child-idem",`${r}:${e.iteration}`);return{schemaVersion:1,contractVersion:"continuity-v1",kind:"run_yolo",commandClass:e.rootAttempt.command_class==="goal-tick"?"goal-tick":"loop-tick",jobId:e.rootAttempt.job_id,operationId:t,idempotencyKey:a,targetKind:"story",targetRef:e.child.targetRef,commandRef:e.child.commandRef,...e.child.preparationOnly?{executionMode:"branchless_child_preparation"}:{},storyId:e.child.storyId,runtime:{runtimeId:e.runtimeId,...e.modelId?{modelId:e.modelId}:{},source:e.runtimeSource??"explicit",reasonCode:e.runtimeSource==="fallback"?p.RUNTIME_FALLBACK:p.RUNTIME_EXPLICIT,publicSafe:!0},publicSummary:`Root-owned yoloop delegated public story ${e.child.storyId}.`,reasonCode:p.OK,metadata:{governanceSource:"delegated-subagent",parentAttemptId:e.rootAttempt.attempt_id,parentExecutionId:e.rootAttempt.execution_id,iteration:e.iteration,...e.child.dispatchId?{dispatchId:e.child.dispatchId}:{},...e.child.graphRevision?{graphRevision:e.child.graphRevision}:{},...e.child.writeSet?{writeSet:e.child.writeSet}:{},...e.child.executionBranch?{executionBranch:e.child.executionBranch}:{},...e.child.graphRevision?{childWorkspace:"branchless_sandbox",epicCheckout:"current_checkout",workspaceLifecycle:"user_managed",integrationOwner:"root_agent",maxDepth:1}:{},publicSafe:!0},attempts:[],redactionSummary:[h("prompt","prompt_body",x(e.child.commandRef))],publicSafe:!0}}function _e(e,r,t){const a=Be(t.hashes),o=t.workerPoolSummary??he(),s=Be({iterations:t.counts.iterations,childCount:t.counts.childCount,succeededChildCount:t.counts.succeededChildCount,failedChildCount:t.counts.failedChildCount,skippedChildCount:t.counts.skippedChildCount,metadataCompleteCount:t.counts.metadataCompleteCount,metadataAbortedCount:t.counts.metadataAbortedCount,serverMaxConcurrency:o.serverMaxConcurrency,localCap:o.localCap,queueCap:o.queueCap,availableLeases:o.availableLeases,effectiveConcurrency:o.effectiveConcurrency,peakConcurrency:o.peakConcurrency,pendingChildCount:o.pendingChildCount,startedChildCount:o.startedChildCount,completedChildCount:o.completedChildCount,backpressurePauseCount:o.backpressurePauseCount}),n={schemaVersion:1,contractVersion:"continuity-v1",attemptId:r.attempt_id,executionId:r.execution_id,jobId:r.job_id,directiveHash:r.directive_hash,status:t.status,reasonCode:t.reasonCode,storyIds:t.childStoryIds.slice(0,50),counts:s,hashes:a,...t.reasonCode===Oe.BYPASS_BLOCKED?{governanceEvidence:Fe}:{},reasonCodes:V([t.reasonCode,...o.reasonCodes,...t.stateTransitions]),publicSummary:pe(t.publicSummary),...t.terminalClass?{terminalClass:t.terminalClass}:{},...t.releaseBlocked?{releaseBlocked:t.releaseBlocked}:{}},c=Re(r.metadata_json,{autonomousKind:"run_yoloop",jobId:r.job_id,targetKind:"epic",targetRef:r.target_ref}),d=c.ok?Ee(c.envelope,n):n;return e.completeHeadlessExecutionAttempt({attemptId:r.attempt_id,executionId:r.execution_id,jobId:r.job_id,leaseId:t.leaseId,runnerId:t.runnerId,status:t.status,completedAt:t.completedAt,durationMs:t.durationMs,reasonCode:t.reasonCode,publicSummary:pe(t.publicSummary),notBeforeAt:t.notBeforeAt,retryCount:t.retryCount,counts:s,hashes:a,redactionSummary:t.redactionSummary,relativePaths:V([r.target_ref,...t.relativePaths]),metadata:d}),e.recordHeadlessExecutionReceipt({receiptId:`receipt-${q("headless-yoloop",`${r.attempt_id}-${t.status}`)}`,attemptId:r.attempt_id,executionId:r.execution_id,jobId:r.job_id,status:t.receiptStatus,reasonCode:t.reasonCode,createdAt:t.completedAt,operationId:`${r.execution_id}:${t.status}:receipt`,idempotencyKey:`${r.directive_hash}:${t.status}:receipt`,counts:s,hashes:a,redactionSummary:t.redactionSummary,relativePaths:V([r.target_ref,...t.relativePaths]),metadata:d}),e.recordHeadlessExecutionSummary({eventId:`event-${q("headless-yoloop-summary",`${r.attempt_id}-${t.status}`)}`,attemptId:r.attempt_id,executionId:r.execution_id,jobId:r.job_id,status:t.status,runtimeId:t.runtimeId,modelId:t.modelId,publicSummary:pe(t.publicSummary),reasonCode:t.reasonCode,createdAt:t.completedAt,durationMs:t.durationMs,counts:s,hashes:a,redactionSummary:t.redactionSummary,relativePaths:V([r.target_ref,...t.relativePaths]),metadata:d}),{attemptId:r.attempt_id,executionId:r.execution_id,jobId:r.job_id,status:t.status,outcome:t.receiptStatus,reasonCode:t.reasonCode,timestamp:t.completedAt,childStoryIds:t.childStoryIds,aggregateCounts:t.counts,workerPoolSummary:o,redactionSummary:t.redactionSummary,...t.terminalClass?{terminalClass:t.terminalClass}:{},...t.releaseBlocked?{releaseBlocked:t.releaseBlocked}:{},publicSafe:!0}}function qt(e,r){return{attemptId:e.attempt_id,executionId:e.execution_id,jobId:e.job_id,status:r.status,outcome:r.receiptStatus,reasonCode:r.reasonCode,timestamp:r.timestamp,childStoryIds:[],aggregateCounts:Ie(),workerPoolSummary:he(),redactionSummary:[],publicSafe:!0}}function Wt(e,r,t,a,o){if(!r.retryable)return{retry:!1};if(we.has(r.reasonCode))return{retry:!0,retryCount:e.retry_count,notBeforeAt:He(Se(o,a),r.retryNotBeforeAt)};const s=e.retry_count+1;if(s>=Math.max(1,e.max_attempts||t))return{retry:!1,retryCount:s};const n=Math.min(8,2**Math.max(0,e.retry_count));return{retry:!0,retryCount:s,notBeforeAt:He(Se(o,a*n),r.retryNotBeforeAt)}}function O(e){return{status:e.status,receiptStatus:e.receiptStatus,reasonCode:e.reasonCode,publicSummary:e.publicSummary,retryable:e.retryable,...e.retryNotBeforeAt?{retryNotBeforeAt:e.retryNotBeforeAt}:{},durationMs:Math.max(0,Date.now()-e.startedAt),counts:e.counts,workerPoolSummary:e.workerPoolSummary??he(),hashes:e.hashes,redactionSummary:e.redactions.slice(0,50),relativePaths:V(e.relativePaths).slice(0,50),childStoryIds:V(e.childStoryIds).slice(0,50),stateTransitions:V(e.stateTransitions),...e.terminalClass?{terminalClass:e.terminalClass}:{},...e.releaseBlocked?{releaseBlocked:e.releaseBlocked}:{}}}function Gt(e,r){if(e===void 0)return;if(!_(e)){r.push(h("runtime","unsafe_key","value-denied"));return}for(const s of Object.keys(e))(!gt.has(s)||oe.test(s))&&r.push(h(`runtime.${s}`,Q(s),"key-denied"));e.publicSafe!==!0&&r.push(h("runtime.publicSafe","unsafe_key","value-denied"));const t=B(e,"runtimeId"),a=B(e,"modelId"),o=B(e,"source");if(t&&(!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,79}$/.test(t)||D.test(t)||W.test(t))&&r.push(h("runtime.runtimeId","unsafe_key","value-denied")),a&&(!/^[A-Za-z0-9][A-Za-z0-9._:+/-]{0,119}$/.test(a)||D.test(a)||W.test(a))&&r.push(h("runtime.modelId","unsafe_key","value-denied")),o!==void 0&&o!=="explicit"&&o!=="origin"&&o!=="default"&&o!=="fallback"&&o!=="unspecified"&&r.push(h("runtime.source","unsafe_key","value-denied")),e.reasonCode!==void 0&&(typeof e.reasonCode!="string"||D.test(e.reasonCode))&&r.push(h("runtime.reasonCode","unsafe_key","value-denied")),e.publicSafe===!0)return{...t?{runtimeId:t}:{},...a?{modelId:a}:{},...o==="explicit"||o==="origin"||o==="default"||o==="fallback"||o==="unspecified"?{source:o}:{},...typeof e.reasonCode=="string"?{reasonCode:e.reasonCode}:{},publicSafe:!0}}function Xt(e){const r=Array.isArray(e)?e:e?[e]:[],t=[];for(const a of r){const o=a.continuityExecutionDirectives;Array.isArray(o)&&t.push(...o)}return t}function Zt(e){return x(Ce({kind:e.kind,commandClass:e.commandClass,jobId:e.jobId,targetRef:e.targetRef,commandRef:e.commandRef,epicId:e.epicId,runtime:e.runtime}))}function Ce(e){return Array.isArray(e)?`[${e.map(Ce).join(",")}]`:_(e)?`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${Ce(e[r])}`).join(",")}}`:JSON.stringify(e)}function le(e,r,t){if(typeof e=="string"){(D.test(e)||W.test(e))&&t.push(h(r,Q(r),"value-denied"));return}if(!(e==null||typeof e=="number"||typeof e=="boolean")){if(Array.isArray(e)){e.forEach((a,o)=>le(a,`${r}.${o}`,t));return}if(_(e)){for(const[a,o]of Object.entries(e)){const s=`${r}.${a}`;oe.test(a)&&t.push(h(s,Q(a),"key-denied")),le(o,s,t)}return}t.push(h(r,"unsafe_key","value-denied"))}}function Jt(e,r){if(e===void 0)return[];if(!Array.isArray(e))return r.push(h("redactionSummary","unsafe_key","value-denied")),[];const t=[];for(const[a,o]of e.entries()){if(!_(o)||o.publicSafe!==!0){r.push(h(`redactionSummary.${a}`,"unsafe_key","value-denied"));continue}const s=B(o,"field"),n=B(o,"reason"),c=B(o,"evidence");if(!s||!n||!c||D.test(s)||D.test(n)||D.test(c)){r.push(h(`redactionSummary.${a}`,"unsafe_key","value-denied"));continue}t.push({field:s,reason:n,evidence:c,publicSafe:!0})}return t}function R(e,r,t){return{ok:!1,reasonCode:e,publicSummary:r,redactionSummary:t}}function w(e){return Array.isArray(e)?e.filter(r=>typeof r=="string"):[]}function Qt(e){return e.length>0&&e.length<=240&&!e.startsWith("-")&&!e.includes("..")&&!e.includes("\\")&&/^[A-Za-z0-9._/-]+$/.test(e)}function Z(e,r){return e.length===r.length&&new Set(e).size===e.length&&e.every(t=>r.includes(t))}function er(e){return fe.test(e.target_ref)?[e.target_ref]:[]}function Be(e){if(!e)return;const r={};for(const[t,a]of Object.entries(e))!/^[A-Za-z0-9_.:-]{1,80}$/.test(t)||oe.test(t)||D.test(t)||typeof a=="string"&&D.test(a)||(r[t]=a);return Object.keys(r).length>0?r:void 0}function Ie(){return A(0,0,0,0,0,0,0)}function A(e,r,t,a,o,s,n){return{iterations:e,childCount:r,succeededChildCount:t,failedChildCount:a,skippedChildCount:o,metadataCompleteCount:s,metadataAbortedCount:n}}function tr(e){return e.replace(/^docs\/stories\//,"").replace(/\.story\.md$/,"")}function ne(e){return e.replace(/^docs\/epics\/epic-/,"").replace(/\.md$/,"")}function G(e){return/^[A-Za-z0-9][A-Za-z0-9_-]*\.\d+[A-Za-z0-9_-]*$/.test(e)&&!D.test(e)&&!W.test(e)}function rr(e){if(e==="explicit"||e==="origin"||e==="default"||e==="fallback")return e}function or(e){if(e==="explicit"||e==="origin"||e==="default"||e==="fallback"||e==="unspecified")return e}function Q(e){return/prompt|commandRef/i.test(e)?"prompt_body":/std(?:out|err)|raw|log|transcript|stack/i.test(e)?"raw_log":/url/i.test(e)?"private_url":/path|cwd|argv|args|shell|command|binary|executable/i.test(e)?"executable_path":/secret|token|password|authorization|api[_-]?key|license/i.test(e)?"secret_or_pii":"unsafe_key"}function B(e,r){return typeof e[r]=="string"?e[r]:void 0}function be(e){return!!(e&&It.test(e)&&!D.test(e)&&!W.test(e))}function _(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ar(e,r){return q(e,`${r}-${Date.now()}-${Ke(4).toString("hex")}`)}function Se(e,r){const t=Date.parse(e);return new Date((Number.isFinite(t)?t:Date.now())+r).toISOString()}function He(e,r){const t=Date.parse(e),a=r?Date.parse(r):Number.NaN;return Number.isFinite(a)?Number.isFinite(t)?a>t?r:e:r:e}function F(e,r,t){return Number.isFinite(e)?Math.max(r,Math.min(t,Math.floor(e))):r}function V(e){return Array.from(new Set(e))}function nr(e,r){const t=new Set,a=[];for(const o of e){const s=r(o);t.has(s)||(t.add(s),a.push(o))}return a}export{me as DEFAULT_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP,yt as DEFAULT_HEADLESS_YOLOOP_MAX_DURATION_MS,ft as DEFAULT_HEADLESS_YOLOOP_MAX_ITERATIONS,ve as MAX_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP,Ar as RUN_YOLOOP_DRIVER_STATES,vt as defaultYoloopRootInvoker,kr as isCanonicalDelegatedStoryPrompt,Pe as resolveDefaultHeadlessYoloopLocalConcurrencyCap,Or as runYoloopExecutionPhase};
|
|
152
|
+
`], { cwd: boundary.projectRoot, stdio: ['ignore', 'pipe', 'pipe', 'ipc'], windowsHide: true });
|
|
153
|
+
let received;
|
|
154
|
+
let bytes = 0;
|
|
155
|
+
let invalid = false;
|
|
156
|
+
const timer = setTimeout(() => { invalid = true; child.kill(); }, boundary.timeoutMs);
|
|
157
|
+
const countOutput = (chunk) => {
|
|
158
|
+
bytes += chunk.length;
|
|
159
|
+
if (bytes > 32 * 1024 * 1024) {
|
|
160
|
+
invalid = true;
|
|
161
|
+
child.kill();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
child.stdout?.on('data', countOutput);
|
|
165
|
+
child.stderr?.on('data', countOutput);
|
|
166
|
+
child.on('message', (value) => {
|
|
167
|
+
if (received || !isRecord(value) || typeof value.success !== 'boolean'
|
|
168
|
+
|| Buffer.byteLength(JSON.stringify(value)) > 32 * 1024 * 1024) {
|
|
169
|
+
invalid = true;
|
|
170
|
+
child.kill();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
received = value;
|
|
174
|
+
});
|
|
175
|
+
const failed = { success: false, errorCode: 'headless-root-invoke-failed',
|
|
176
|
+
error: 'The isolated root metadata invocation did not complete.', exitCode: 1 };
|
|
177
|
+
child.on('error', () => { invalid = true; });
|
|
178
|
+
child.on('close', (code) => {
|
|
179
|
+
clearTimeout(timer);
|
|
180
|
+
resolveResult(code === 0 && !invalid && received ? received : failed);
|
|
181
|
+
});
|
|
182
|
+
child.send({ modulePath, options }, (error) => { if (error) {
|
|
183
|
+
invalid = true;
|
|
184
|
+
child.kill();
|
|
185
|
+
} });
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
export async function defaultYoloopRootInvoker(input, invokeTransport = defaultIsolatedYoloopRootInvokeTransport) {
|
|
189
|
+
const startedAt = Date.now();
|
|
190
|
+
const options = {
|
|
191
|
+
args: input.args,
|
|
192
|
+
projectRoot: input.projectRoot,
|
|
193
|
+
format: 'json',
|
|
194
|
+
platformTarget: 'runner',
|
|
195
|
+
serverUrl: input.serverUrl,
|
|
196
|
+
strictReadOnly: input.strictReadOnly,
|
|
197
|
+
};
|
|
198
|
+
const result = await invokeTransport(options, {
|
|
199
|
+
projectRoot: input.projectRoot,
|
|
200
|
+
timeoutMs: input.timeoutMs,
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
success: result.success,
|
|
204
|
+
instructions: result.instructions,
|
|
205
|
+
metadata: result.metadata,
|
|
206
|
+
error: result.error,
|
|
207
|
+
errorCode: result.errorCode,
|
|
208
|
+
exitCode: result.exitCode,
|
|
209
|
+
durationMs: Date.now() - startedAt,
|
|
210
|
+
publicSafe: true,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export async function runYoloopExecutionPhase(options) {
|
|
214
|
+
const registry = options.adapterRegistry ?? createDefaultHeadlessAdapterRegistry();
|
|
215
|
+
const limit = boundedInteger(options.limit ?? DEFAULT_HEADLESS_EXECUTION_LIMIT, 1, 10);
|
|
216
|
+
const leaseMs = boundedInteger(options.leaseMs ?? DEFAULT_HEADLESS_EXECUTION_LEASE_MS, 1_000, 60 * 60 * 1000);
|
|
217
|
+
const staleLeaseAfterMs = boundedInteger(options.staleLeaseAfterMs ?? DEFAULT_HEADLESS_EXECUTION_STALE_LEASE_AFTER_MS, 0, 24 * 60 * 60 * 1000);
|
|
218
|
+
const maxAttempts = boundedInteger(options.maxAttempts ?? DEFAULT_HEADLESS_EXECUTION_MAX_ATTEMPTS, 1, 10);
|
|
219
|
+
const localConcurrencyCap = boundedInteger(options.localConcurrencyCap ?? resolveDefaultHeadlessYoloopLocalConcurrencyCap(), 1, MAX_HEADLESS_YOLOOP_LOCAL_CONCURRENCY_CAP);
|
|
220
|
+
const reasonCodes = [];
|
|
221
|
+
const receipts = [];
|
|
222
|
+
const nowIso = options.now();
|
|
223
|
+
const enqueueResult = enqueueRunYoloopExecutionDirectives({
|
|
224
|
+
store: options.store,
|
|
225
|
+
metadata: options.metadata,
|
|
226
|
+
nowIso,
|
|
227
|
+
maxAttempts,
|
|
228
|
+
queueCap: options.queueCap,
|
|
229
|
+
});
|
|
230
|
+
reasonCodes.push(...enqueueResult.reasonCodes);
|
|
231
|
+
const recovered = options.store.recoverStaleHeadlessExecutionAttempts({
|
|
232
|
+
nowIso,
|
|
233
|
+
staleAfterMs: staleLeaseAfterMs,
|
|
234
|
+
limit: limit * 2,
|
|
235
|
+
});
|
|
236
|
+
if (recovered.recoveredAttemptIds.length > 0) {
|
|
237
|
+
reasonCodes.push(HEADLESS_REASON_CODES.EXECUTION_BACKOFF);
|
|
238
|
+
if (recovered.failedAttemptIds.length > 0)
|
|
239
|
+
reasonCodes.push(HEADLESS_REASON_CODES.EXECUTION_TIMEOUT);
|
|
240
|
+
}
|
|
241
|
+
const dueAttempts = options.store
|
|
242
|
+
.listDueHeadlessExecutionAttempts({ nowIso: options.now(), limit: Math.max(limit * 5, 10) })
|
|
243
|
+
.filter((attempt) => attempt.directive_kind === 'run_yoloop')
|
|
244
|
+
.slice(0, limit);
|
|
245
|
+
for (const attempt of dueAttempts) {
|
|
246
|
+
const receipt = await processRunYoloopAttempt({
|
|
247
|
+
store: options.store,
|
|
248
|
+
projectRoot: options.projectRoot,
|
|
249
|
+
runnerId: options.runnerId,
|
|
250
|
+
attempt,
|
|
251
|
+
registry,
|
|
252
|
+
now: options.now,
|
|
253
|
+
leaseMs,
|
|
254
|
+
timeoutMs: boundedInteger(options.timeoutMs ?? DEFAULT_HEADLESS_EXECUTION_TIMEOUT_MS, 1_000, 30 * 60 * 1000),
|
|
255
|
+
backoffMs: boundedInteger(options.backoffMs ?? DEFAULT_HEADLESS_EXECUTION_BACKOFF_MS, 1_000, 60 * 60 * 1000),
|
|
256
|
+
maxAttempts,
|
|
257
|
+
maxIterations: boundedInteger(options.maxIterations ?? DEFAULT_HEADLESS_YOLOOP_MAX_ITERATIONS, 1, 100),
|
|
258
|
+
maxDurationMs: boundedInteger(options.maxDurationMs ?? DEFAULT_HEADLESS_YOLOOP_MAX_DURATION_MS, 1_000, 2 * 60 * 60 * 1000),
|
|
259
|
+
localConcurrencyCap,
|
|
260
|
+
queueCap: options.queueCap,
|
|
261
|
+
adapterOptions: options.adapterOptions,
|
|
262
|
+
runtimeDefaults: options.runtimeDefaults,
|
|
263
|
+
runtimeFallback: options.runtimeFallback,
|
|
264
|
+
yoloopInvoker: options.yoloopInvoker ?? defaultYoloopRootInvoker,
|
|
265
|
+
serverUrl: options.serverUrl,
|
|
266
|
+
idFactory: options.idFactory ?? defaultHeadlessIdFactory,
|
|
267
|
+
continuousWorkspaceManager: options.continuousWorkspaceManager ?? createBranchlessContinuousStoryWorkspaceManager(),
|
|
268
|
+
terminalGitAdapter: options.terminalGitAdapter ?? createCliEpicTerminalGitAdapter(options.projectRoot),
|
|
269
|
+
});
|
|
270
|
+
receipts.push(receipt);
|
|
271
|
+
reasonCodes.push(receipt.reasonCode, ...receipt.workerPoolSummary.reasonCodes);
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
ok: receipts.every((receipt) => receipt.outcome !== 'failed') && enqueueResult.rejectedCount === 0,
|
|
275
|
+
enqueuedAttemptCount: enqueueResult.enqueuedCount,
|
|
276
|
+
dueAttemptCount: dueAttempts.length,
|
|
277
|
+
processedAttemptCount: receipts.filter((receipt) => receipt.outcome !== 'ignored').length,
|
|
278
|
+
recoveredAttemptIds: recovered.recoveredAttemptIds,
|
|
279
|
+
receipts,
|
|
280
|
+
reasonCodes: unique(reasonCodes.length > 0 ? reasonCodes : [HEADLESS_REASON_CODES.OK]),
|
|
281
|
+
publicSafe: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function enqueueRunYoloopExecutionDirectives(input) {
|
|
285
|
+
let enqueuedCount = 0;
|
|
286
|
+
let rejectedCount = 0;
|
|
287
|
+
const reasonCodes = [];
|
|
288
|
+
for (const candidate of extractExecutionDirectiveCandidates(input.metadata)) {
|
|
289
|
+
if (!isRecord(candidate) || candidate.kind !== 'run_yoloop')
|
|
290
|
+
continue;
|
|
291
|
+
const validation = validateRunYoloopExecutionDirective(candidate);
|
|
292
|
+
if (!validation.ok) {
|
|
293
|
+
rejectedCount += 1;
|
|
294
|
+
reasonCodes.push(validation.reasonCode);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const directive = validation.directive;
|
|
298
|
+
if (!input.store.jobExists(directive.jobId)) {
|
|
299
|
+
rejectedCount += 1;
|
|
300
|
+
reasonCodes.push(AUTONOMOUS_GOVERNANCE_REASON_CODES.BYPASS_BLOCKED);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const directiveHash = hashDirective(directive);
|
|
304
|
+
if (input.store.hasCompletedHeadlessExecutionDirectiveHash(directiveHash)) {
|
|
305
|
+
reasonCodes.push(HEADLESS_REASON_CODES.EXECUTION_COMPLETED);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const governanceEnvelope = buildAutonomousGovernanceEnvelope({
|
|
309
|
+
autonomousKind: 'run_yoloop',
|
|
310
|
+
source: 'headless-run-yoloop',
|
|
311
|
+
targetKind: 'epic',
|
|
312
|
+
targetRef: directive.targetRef,
|
|
313
|
+
epicId: directive.epicId ?? epicIdFromTargetRef(directive.targetRef),
|
|
314
|
+
jobId: directive.jobId,
|
|
315
|
+
operationSeed: directive.operationId,
|
|
316
|
+
commandHash: hashPublicValue(directive.commandRef),
|
|
317
|
+
});
|
|
318
|
+
const governance = recordAutonomousGovernance(input.store, governanceEnvelope, input.nowIso, { requireExistingJob: true });
|
|
319
|
+
if (!governance.ok) {
|
|
320
|
+
rejectedCount += 1;
|
|
321
|
+
reasonCodes.push(governance.reasonCode);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const attemptId = headlessExecutionEntityId('attempt', directive.idempotencyKey);
|
|
325
|
+
const executionId = headlessExecutionEntityId('exec', directive.operationId);
|
|
326
|
+
const result = input.store.enqueueHeadlessExecutionAttempt({
|
|
327
|
+
attemptId,
|
|
328
|
+
executionId,
|
|
329
|
+
jobId: directive.jobId,
|
|
330
|
+
directiveHash,
|
|
331
|
+
directiveKind: 'run_yoloop',
|
|
332
|
+
commandClass: directive.commandClass,
|
|
333
|
+
targetKind: 'epic',
|
|
334
|
+
targetRef: directive.targetRef,
|
|
335
|
+
epicId: directive.epicId ?? epicIdFromTargetRef(directive.targetRef),
|
|
336
|
+
runtimeId: directive.runtime?.runtimeId,
|
|
337
|
+
modelId: directive.runtime?.modelId,
|
|
338
|
+
runtimeSource: normalizeStoredRuntimeSource(directive.runtime?.source),
|
|
339
|
+
reasonCode: directive.reasonCode,
|
|
340
|
+
publicSummary: directive.publicSummary,
|
|
341
|
+
enqueuedAt: input.nowIso,
|
|
342
|
+
notBeforeAt: input.nowIso,
|
|
343
|
+
maxAttempts: input.maxAttempts,
|
|
344
|
+
queueCap: input.queueCap,
|
|
345
|
+
operationId: `${directive.operationId}:enqueue`,
|
|
346
|
+
idempotencyKey: directive.idempotencyKey,
|
|
347
|
+
counts: { directives: 1 },
|
|
348
|
+
hashes: { directive: directiveHash },
|
|
349
|
+
redactionSummary: directive.redactionSummary,
|
|
350
|
+
relativePaths: [directive.targetRef],
|
|
351
|
+
metadata: autonomousGovernanceMetadata(governanceEnvelope, {
|
|
352
|
+
schemaVersion: 1,
|
|
353
|
+
contractVersion: 'continuity-v1',
|
|
354
|
+
jobId: directive.jobId,
|
|
355
|
+
operationId: directive.operationId,
|
|
356
|
+
reasonCode: directive.reasonCode ?? HEADLESS_REASON_CODES.OK,
|
|
357
|
+
targetRef: directive.targetRef,
|
|
358
|
+
directiveHash,
|
|
359
|
+
}),
|
|
360
|
+
});
|
|
361
|
+
if (!result.accepted) {
|
|
362
|
+
rejectedCount += 1;
|
|
363
|
+
reasonCodes.push(result.reasonCode ?? 'queue-cap-reached');
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (result.inserted)
|
|
367
|
+
enqueuedCount += 1;
|
|
368
|
+
reasonCodes.push(HEADLESS_REASON_CODES.OK);
|
|
369
|
+
}
|
|
370
|
+
return { enqueuedCount, rejectedCount, reasonCodes: unique(reasonCodes) };
|
|
371
|
+
}
|
|
372
|
+
async function processRunYoloopAttempt(input) {
|
|
373
|
+
const claimedAt = input.now();
|
|
374
|
+
const leaseId = input.idFactory('lease', input.attempt.attempt_id);
|
|
375
|
+
const leaseExpiresAt = addMilliseconds(claimedAt, input.leaseMs);
|
|
376
|
+
const claim = input.store.claimHeadlessExecutionAttempt({
|
|
377
|
+
attemptId: input.attempt.attempt_id,
|
|
378
|
+
leaseId,
|
|
379
|
+
runnerId: input.runnerId,
|
|
380
|
+
claimedAt,
|
|
381
|
+
leaseExpiresAt,
|
|
382
|
+
reasonCode: HEADLESS_REASON_CODES.OK,
|
|
383
|
+
});
|
|
384
|
+
if (!claim.inserted) {
|
|
385
|
+
return emptyYoloopReceipt(input.attempt, {
|
|
386
|
+
status: input.attempt.status,
|
|
387
|
+
receiptStatus: 'ignored',
|
|
388
|
+
reasonCode: 'lease-conflict',
|
|
389
|
+
timestamp: claimedAt,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const duplicateCompletion = input.store.hasCompletedHeadlessExecutionDirectiveHash(input.attempt.directive_hash, input.attempt.attempt_id);
|
|
393
|
+
if (duplicateCompletion) {
|
|
394
|
+
return persistYoloopAttemptOutcome(input.store, input.attempt, {
|
|
395
|
+
leaseId,
|
|
396
|
+
runnerId: input.runnerId,
|
|
397
|
+
status: 'skipped',
|
|
398
|
+
receiptStatus: 'ignored',
|
|
399
|
+
reasonCode: HEADLESS_REASON_CODES.EXECUTION_COMPLETED,
|
|
400
|
+
publicSummary: 'Skipped duplicate run_yoloop directive because the directive hash is already completed.',
|
|
401
|
+
completedAt: input.now(),
|
|
402
|
+
counts: emptyAggregateCounts(),
|
|
403
|
+
hashes: { directive: input.attempt.directive_hash },
|
|
404
|
+
redactionSummary: [],
|
|
405
|
+
relativePaths: [input.attempt.target_ref],
|
|
406
|
+
childStoryIds: [],
|
|
407
|
+
stateTransitions: ['start', 'complete'],
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
const preparation = prepareYoloopAttemptInput(input);
|
|
411
|
+
if (!preparation.ok) {
|
|
412
|
+
return persistYoloopAttemptOutcome(input.store, input.attempt, {
|
|
413
|
+
leaseId,
|
|
414
|
+
runnerId: input.runnerId,
|
|
415
|
+
status: preparation.status,
|
|
416
|
+
receiptStatus: preparation.receiptStatus,
|
|
417
|
+
reasonCode: preparation.reasonCode,
|
|
418
|
+
publicSummary: preparation.publicSummary,
|
|
419
|
+
completedAt: input.now(),
|
|
420
|
+
counts: emptyAggregateCounts(),
|
|
421
|
+
hashes: { directive: input.attempt.directive_hash },
|
|
422
|
+
redactionSummary: preparation.redactionSummary,
|
|
423
|
+
relativePaths: safeAttemptRelativePaths(input.attempt),
|
|
424
|
+
childStoryIds: [],
|
|
425
|
+
stateTransitions: ['start'],
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const driverResult = await executeRootOwnedYoloop({
|
|
429
|
+
...input,
|
|
430
|
+
prepared: preparation.input,
|
|
431
|
+
});
|
|
432
|
+
const retry = retryDecision(input.attempt, driverResult, input.maxAttempts, input.backoffMs, input.now());
|
|
433
|
+
const status = retry.retry ? 'backoff' : driverResult.status;
|
|
434
|
+
const receiptStatus = retry.retry ? 'deferred' : driverResult.receiptStatus;
|
|
435
|
+
const reasonCode = retry.retry
|
|
436
|
+
? (PUBLIC_TERMINAL_LOCK_RETRY_REASONS.has(driverResult.reasonCode)
|
|
437
|
+
|| RESUMABLE_YOLOOP_BOUNDARY_REASONS.has(driverResult.reasonCode)
|
|
438
|
+
? driverResult.reasonCode : HEADLESS_REASON_CODES.EXECUTION_BACKOFF)
|
|
439
|
+
: driverResult.reasonCode;
|
|
440
|
+
const publicSummary = retry.retry
|
|
441
|
+
? 'Headless run_yoloop execution scheduled bounded retry after transient metadata/runtime outcome.'
|
|
442
|
+
: driverResult.publicSummary;
|
|
443
|
+
return persistYoloopAttemptOutcome(input.store, input.attempt, {
|
|
444
|
+
leaseId,
|
|
445
|
+
runnerId: input.runnerId,
|
|
446
|
+
status,
|
|
447
|
+
receiptStatus,
|
|
448
|
+
reasonCode,
|
|
449
|
+
publicSummary,
|
|
450
|
+
completedAt: input.now(),
|
|
451
|
+
durationMs: driverResult.durationMs,
|
|
452
|
+
notBeforeAt: retry.notBeforeAt,
|
|
453
|
+
retryCount: retry.retryCount,
|
|
454
|
+
counts: driverResult.counts,
|
|
455
|
+
workerPoolSummary: driverResult.workerPoolSummary,
|
|
456
|
+
hashes: { ...driverResult.hashes, directive: input.attempt.directive_hash },
|
|
457
|
+
redactionSummary: driverResult.redactionSummary,
|
|
458
|
+
relativePaths: driverResult.relativePaths,
|
|
459
|
+
childStoryIds: driverResult.childStoryIds,
|
|
460
|
+
runtimeId: preparation.input.runtimeId,
|
|
461
|
+
modelId: preparation.input.modelId,
|
|
462
|
+
runtimeSource: preparation.input.runtimeSource,
|
|
463
|
+
stateTransitions: driverResult.stateTransitions,
|
|
464
|
+
terminalClass: driverResult.terminalClass,
|
|
465
|
+
releaseBlocked: driverResult.releaseBlocked,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
async function executeRootOwnedYoloop(input) {
|
|
469
|
+
const startedAt = Date.now();
|
|
470
|
+
const stateTransitions = ['start'];
|
|
471
|
+
const processedStoryIds = new Set();
|
|
472
|
+
const childStoryIds = [];
|
|
473
|
+
const childTargetRefs = [];
|
|
474
|
+
const hashes = { root: hashPublicValue(input.prepared.rootPrompt) };
|
|
475
|
+
const redactions = [redaction('rootPrompt', 'prompt_body', hashes.root)];
|
|
476
|
+
let iterations = 0;
|
|
477
|
+
let succeededChildCount = 0;
|
|
478
|
+
let failedChildCount = 0;
|
|
479
|
+
let skippedChildCount = 0;
|
|
480
|
+
let metadataCompleteCount = 0;
|
|
481
|
+
let metadataAbortedCount = 0;
|
|
482
|
+
let terminalP205Authority;
|
|
483
|
+
let lifecycleEvidencePrepared = false;
|
|
484
|
+
const workerPool = createMutableWorkerPoolSummary(input.localConcurrencyCap);
|
|
485
|
+
const continuousInFlight = new Map();
|
|
486
|
+
let continuousModeObserved = false;
|
|
487
|
+
const continuousDeferredReasons = new Map();
|
|
488
|
+
const continuousFailureReasons = new Map();
|
|
489
|
+
const recordContinuousChild = (result) => {
|
|
490
|
+
const child = result.child;
|
|
491
|
+
hashes[`child-${child.storyId.replace(/[^A-Za-z0-9_.:-]/g, '-')}`] = result.commandHash;
|
|
492
|
+
redactions.push(result.promptRedaction);
|
|
493
|
+
const receipt = result.receipt;
|
|
494
|
+
if (result.terminalFailure) {
|
|
495
|
+
if (result.deferredFailure) {
|
|
496
|
+
continuousDeferredReasons.set(child.storyId, result.terminalFailure);
|
|
497
|
+
workerPool.backpressurePauseCount += 1;
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
continuousDeferredReasons.delete(child.storyId);
|
|
501
|
+
continuousFailureReasons.set(child.storyId, result.terminalFailure);
|
|
502
|
+
if (!processedStoryIds.has(child.storyId)) {
|
|
503
|
+
processedStoryIds.add(child.storyId);
|
|
504
|
+
childStoryIds.push(child.storyId);
|
|
505
|
+
childTargetRefs.push(child.targetRef);
|
|
506
|
+
failedChildCount += 1;
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (!receipt || receipt.outcome === 'deferred') {
|
|
511
|
+
continuousDeferredReasons.set(child.storyId, result.reasonCodes.includes('queue-cap-reached')
|
|
512
|
+
? HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED
|
|
513
|
+
: receipt?.reasonCode || HEADLESS_REASON_CODES.EXECUTION_BACKOFF);
|
|
514
|
+
if (result.reasonCodes.includes('queue-cap-reached'))
|
|
515
|
+
workerPool.backpressurePauseCount += 1;
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
continuousDeferredReasons.delete(child.storyId);
|
|
519
|
+
if (processedStoryIds.has(child.storyId))
|
|
520
|
+
return;
|
|
521
|
+
processedStoryIds.add(child.storyId);
|
|
522
|
+
childStoryIds.push(child.storyId);
|
|
523
|
+
childTargetRefs.push(child.targetRef);
|
|
524
|
+
if (receipt.outcome === 'failed') {
|
|
525
|
+
failedChildCount += 1;
|
|
526
|
+
continuousFailureReasons.set(child.storyId, receipt.reasonCode || HEADLESS_REASON_CODES.EXECUTION_FAILED);
|
|
527
|
+
}
|
|
528
|
+
else if (receipt.outcome === 'ignored')
|
|
529
|
+
skippedChildCount += 1;
|
|
530
|
+
else
|
|
531
|
+
succeededChildCount += 1;
|
|
532
|
+
};
|
|
533
|
+
const settleOneContinuousChild = async () => {
|
|
534
|
+
const settled = await Promise.race([...continuousInFlight.entries()].map(([storyId, entry]) => entry.promise.then((result) => ({ storyId, result }))));
|
|
535
|
+
continuousInFlight.delete(settled.storyId);
|
|
536
|
+
workerPool.completedChildCount += 1;
|
|
537
|
+
workerPool.pendingChildCount = continuousInFlight.size;
|
|
538
|
+
recordContinuousChild(settled.result);
|
|
539
|
+
};
|
|
540
|
+
while (true) {
|
|
541
|
+
if (iterations >= input.maxIterations) {
|
|
542
|
+
stateTransitions.push('backoff');
|
|
543
|
+
return driverResult({
|
|
544
|
+
status: 'failed',
|
|
545
|
+
receiptStatus: 'failed',
|
|
546
|
+
reasonCode: MAX_ITERATIONS_REASON_CODE,
|
|
547
|
+
publicSummary: 'Headless yoloop reached the bounded iteration limit before completion metadata.',
|
|
548
|
+
retryable: false,
|
|
549
|
+
startedAt,
|
|
550
|
+
counts: aggregateCounts(iterations, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
551
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
552
|
+
hashes,
|
|
553
|
+
redactions,
|
|
554
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
555
|
+
childStoryIds,
|
|
556
|
+
stateTransitions,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (Date.now() - startedAt > input.maxDurationMs) {
|
|
560
|
+
stateTransitions.push('backoff');
|
|
561
|
+
return driverResult({
|
|
562
|
+
status: 'failed',
|
|
563
|
+
receiptStatus: 'failed',
|
|
564
|
+
reasonCode: MAX_DURATION_REASON_CODE,
|
|
565
|
+
publicSummary: 'Headless yoloop reached the bounded duration limit before completion metadata.',
|
|
566
|
+
retryable: false,
|
|
567
|
+
startedAt,
|
|
568
|
+
counts: aggregateCounts(iterations, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
569
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
570
|
+
hashes,
|
|
571
|
+
redactions,
|
|
572
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
573
|
+
childStoryIds,
|
|
574
|
+
stateTransitions,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
stateTransitions.push('metadata_parse');
|
|
578
|
+
const rootResult = await input.yoloopInvoker({
|
|
579
|
+
projectRoot: input.projectRoot,
|
|
580
|
+
args: input.prepared.rootPrompt,
|
|
581
|
+
attemptId: input.attempt.attempt_id,
|
|
582
|
+
executionId: input.attempt.execution_id,
|
|
583
|
+
jobId: input.attempt.job_id,
|
|
584
|
+
runnerId: input.runnerId,
|
|
585
|
+
iteration: iterations,
|
|
586
|
+
runtimeId: input.prepared.runtimeId,
|
|
587
|
+
...(input.prepared.modelId ? { modelId: input.prepared.modelId } : {}),
|
|
588
|
+
timeoutMs: input.timeoutMs,
|
|
589
|
+
serverUrl: input.serverUrl,
|
|
590
|
+
});
|
|
591
|
+
if (rootResult.durationMs !== undefined)
|
|
592
|
+
hashes[`iter-${iterations}`] = hashPublicValue(String(rootResult.durationMs));
|
|
593
|
+
const metadata = rootResult.metadata;
|
|
594
|
+
const releaseBlockedTransport = rootResult.errorCode === 'YOLOOP_RELEASE_BLOCKED'
|
|
595
|
+
|| metadata?.yoloopReleaseBlocked === true
|
|
596
|
+
|| metadata?.terminalClass === 'release-blocked'
|
|
597
|
+
|| metadata?.releaseBlocked !== undefined;
|
|
598
|
+
if (!rootResult.success && !releaseBlockedTransport) {
|
|
599
|
+
stateTransitions.push('backoff');
|
|
600
|
+
return driverResult({
|
|
601
|
+
status: 'backoff',
|
|
602
|
+
receiptStatus: 'deferred',
|
|
603
|
+
reasonCode: HEADLESS_REASON_CODES.EXECUTION_FAILED,
|
|
604
|
+
publicSummary: 'Headless yoloop root metadata invocation failed with bounded public evidence.',
|
|
605
|
+
retryable: true,
|
|
606
|
+
startedAt,
|
|
607
|
+
counts: aggregateCounts(iterations, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
608
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
609
|
+
hashes,
|
|
610
|
+
redactions,
|
|
611
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
612
|
+
childStoryIds,
|
|
613
|
+
stateTransitions,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
const terminal = classifyYoloopTerminalMetadata(metadata);
|
|
617
|
+
if (!terminal.ok) {
|
|
618
|
+
stateTransitions.push('backoff');
|
|
619
|
+
return driverResult({
|
|
620
|
+
status: 'failed',
|
|
621
|
+
receiptStatus: 'failed',
|
|
622
|
+
reasonCode: terminal.reasonCode,
|
|
623
|
+
publicSummary: terminal.publicSummary,
|
|
624
|
+
retryable: false,
|
|
625
|
+
startedAt,
|
|
626
|
+
counts: aggregateCounts(iterations, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
627
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
628
|
+
hashes,
|
|
629
|
+
redactions: [...redactions, ...terminal.redactionSummary],
|
|
630
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
631
|
+
childStoryIds,
|
|
632
|
+
stateTransitions,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
const resumableBoundary = extractResumableExhaustionBoundary(metadata);
|
|
636
|
+
if (terminal.aborted && resumableBoundary) {
|
|
637
|
+
while (continuousInFlight.size > 0)
|
|
638
|
+
await settleOneContinuousChild();
|
|
639
|
+
metadataAbortedCount += 1;
|
|
640
|
+
stateTransitions.push('backoff');
|
|
641
|
+
return driverResult({
|
|
642
|
+
status: 'backoff',
|
|
643
|
+
receiptStatus: 'deferred',
|
|
644
|
+
reasonCode: resumableBoundary.reason === 'YOLOOP_SESSION_TIMEOUT'
|
|
645
|
+
? 'yoloop-session-timeout-resumable'
|
|
646
|
+
: 'yoloop-per-run-cap-resumable',
|
|
647
|
+
publicSummary: 'Headless yoloop reached a finite run boundary and scheduled the same queue after cool-down.',
|
|
648
|
+
retryable: true,
|
|
649
|
+
retryNotBeforeAt: resumableBoundary.coolDownUntil,
|
|
650
|
+
startedAt,
|
|
651
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
652
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
653
|
+
hashes,
|
|
654
|
+
redactions,
|
|
655
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
656
|
+
childStoryIds,
|
|
657
|
+
stateTransitions,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
if ((terminal.releaseBlocked || terminal.aborted || terminal.complete || terminal.replay || (terminal.outcome && terminal.outcome !== 'selected' && terminal.outcome !== 'queue_exhausted'))
|
|
661
|
+
&& continuousModeObserved && continuousInFlight.size > 0) {
|
|
662
|
+
await settleOneContinuousChild();
|
|
663
|
+
iterations += 1;
|
|
664
|
+
stateTransitions.push('reinvoke');
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (terminal.releaseBlocked) {
|
|
668
|
+
stateTransitions.push('release_blocked');
|
|
669
|
+
return driverResult({
|
|
670
|
+
status: 'failed',
|
|
671
|
+
receiptStatus: 'failed',
|
|
672
|
+
reasonCode: 'yoloop-release-blocked',
|
|
673
|
+
publicSummary: 'Headless yoloop stopped at a terminal release-blocked boundary without declaring success.',
|
|
674
|
+
retryable: false,
|
|
675
|
+
startedAt,
|
|
676
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
677
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
678
|
+
hashes: { ...hashes, releaseBlocked: hashPublicValue(terminal.releaseBlocked.resultId) },
|
|
679
|
+
redactions,
|
|
680
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
681
|
+
childStoryIds,
|
|
682
|
+
stateTransitions,
|
|
683
|
+
terminalClass: 'release-blocked',
|
|
684
|
+
releaseBlocked: terminal.releaseBlocked,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
const epicIntegration = isRecord(metadata?.epicIntegration) ? metadata.epicIntegration : undefined;
|
|
688
|
+
const integrationReasonCodes = Array.isArray(epicIntegration?.reasonCodes)
|
|
689
|
+
? epicIntegration.reasonCodes.filter((value) => typeof value === 'string')
|
|
690
|
+
: [];
|
|
691
|
+
const lifecycleEvidenceCanRepair = terminal.aborted
|
|
692
|
+
&& metadata?.abortReason === 'epic-integration-checkpoints-incomplete'
|
|
693
|
+
&& epicIntegration?.required === true
|
|
694
|
+
&& epicIntegration.ready === false
|
|
695
|
+
&& integrationReasonCodes.length > 0
|
|
696
|
+
&& integrationReasonCodes.every((reasonCode) => reasonCode === 'epic-aggregate-qa-receipt-required'
|
|
697
|
+
|| reasonCode === 'epic-origin-binding-required');
|
|
698
|
+
// P223 release admission has already completed for this root invocation.
|
|
699
|
+
// Only now may the client produce the physical aggregate/origin evidence
|
|
700
|
+
// needed for the server to emit the terminal Git directive. This avoids a
|
|
701
|
+
// release-blocked request causing Git effects while preserving the
|
|
702
|
+
// pre-P223 terminal-integration convergence path.
|
|
703
|
+
if (lifecycleEvidenceCanRepair && input.terminalGitAdapter.prepareLifecycleEvidence) {
|
|
704
|
+
stateTransitions.push('lifecycle_evidence');
|
|
705
|
+
const evidence = await input.terminalGitAdapter.prepareLifecycleEvidence({
|
|
706
|
+
epicId: input.prepared.epicId,
|
|
707
|
+
nowIso: input.now(),
|
|
708
|
+
});
|
|
709
|
+
if (!evidence.ok) {
|
|
710
|
+
stateTransitions.push('backoff');
|
|
711
|
+
return driverResult({
|
|
712
|
+
status: evidence.retryEligible ? 'backoff' : 'failed',
|
|
713
|
+
receiptStatus: evidence.retryEligible ? 'deferred' : 'failed',
|
|
714
|
+
reasonCode: evidence.reasonCode,
|
|
715
|
+
publicSummary: 'Headless yoloop stopped because physical Git lifecycle evidence could not be produced safely.',
|
|
716
|
+
retryable: evidence.retryEligible,
|
|
717
|
+
startedAt,
|
|
718
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
719
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
720
|
+
hashes,
|
|
721
|
+
redactions,
|
|
722
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
723
|
+
childStoryIds,
|
|
724
|
+
stateTransitions,
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
terminalP205Authority = evidence.p205Authority;
|
|
728
|
+
lifecycleEvidencePrepared = true;
|
|
729
|
+
iterations += 1;
|
|
730
|
+
stateTransitions.push('reinvoke');
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
if (terminal.aborted) {
|
|
734
|
+
metadataAbortedCount += 1;
|
|
735
|
+
stateTransitions.push('aborted');
|
|
736
|
+
return driverResult({
|
|
737
|
+
status: 'failed',
|
|
738
|
+
receiptStatus: 'failed',
|
|
739
|
+
reasonCode: JOB_ABORTED_REASON_CODE,
|
|
740
|
+
publicSummary: 'Headless yoloop stopped because public metadata reported an aborted loop.',
|
|
741
|
+
retryable: false,
|
|
742
|
+
startedAt,
|
|
743
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
744
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
745
|
+
hashes,
|
|
746
|
+
redactions,
|
|
747
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
748
|
+
childStoryIds,
|
|
749
|
+
stateTransitions,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (terminal.outcome && terminal.outcome !== 'selected' && terminal.outcome !== 'queue_exhausted') {
|
|
753
|
+
stateTransitions.push('backoff');
|
|
754
|
+
return driverResult({
|
|
755
|
+
status: 'failed', receiptStatus: 'failed', reasonCode: `yoloop-${terminal.outcome}`,
|
|
756
|
+
publicSummary: 'Headless yoloop stopped on a non-executable graph-first continuation outcome.',
|
|
757
|
+
retryable: false, startedAt,
|
|
758
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
759
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
760
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
if (terminal.replay) {
|
|
764
|
+
stateTransitions.push('backoff');
|
|
765
|
+
return driverResult({
|
|
766
|
+
status: 'backoff', receiptStatus: 'deferred', reasonCode: 'yoloop-replay-pending',
|
|
767
|
+
publicSummary: 'Headless yoloop observed pending replay authority and did not delegate a second child.',
|
|
768
|
+
retryable: true, startedAt,
|
|
769
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
770
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
771
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
if (terminal.complete && continuousModeObserved
|
|
775
|
+
&& continuousDeferredReasons.size > 0 && continuousFailureReasons.size === 0) {
|
|
776
|
+
stateTransitions.push('backoff');
|
|
777
|
+
return driverResult({
|
|
778
|
+
status: 'backoff', receiptStatus: 'deferred',
|
|
779
|
+
reasonCode: [...continuousDeferredReasons.values()].sort()[0],
|
|
780
|
+
publicSummary: 'Headless yoloop rejected terminal completion while previously runnable work remained undispatched.',
|
|
781
|
+
retryable: true, startedAt,
|
|
782
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
783
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
784
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (terminal.complete && continuousModeObserved && continuousFailureReasons.size > 0) {
|
|
788
|
+
stateTransitions.push('backoff');
|
|
789
|
+
return driverResult({
|
|
790
|
+
status: 'failed', receiptStatus: 'failed',
|
|
791
|
+
reasonCode: [...continuousFailureReasons.values()].sort()[0],
|
|
792
|
+
publicSummary: 'Headless yoloop rejected terminal completion because runnable work failed before an accepted dispatch receipt.',
|
|
793
|
+
retryable: false, startedAt,
|
|
794
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
795
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
796
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
// A terminal response can truthfully report queue exhaustion while also
|
|
800
|
+
// carrying the still-required physical Git integration directive. The
|
|
801
|
+
// effect must run before accepting yoloopComplete; the persisted receipt
|
|
802
|
+
// makes the next invocation converge to terminal success.
|
|
803
|
+
const terminalGitDirective = extractEpicTerminalGitDirective(metadata);
|
|
804
|
+
if (terminalGitDirective) {
|
|
805
|
+
if (!lifecycleEvidencePrepared && input.terminalGitAdapter.prepareLifecycleEvidence) {
|
|
806
|
+
stateTransitions.push('lifecycle_evidence');
|
|
807
|
+
const evidence = await input.terminalGitAdapter.prepareLifecycleEvidence({
|
|
808
|
+
epicId: input.prepared.epicId,
|
|
809
|
+
nowIso: input.now(),
|
|
810
|
+
});
|
|
811
|
+
if (!evidence.ok) {
|
|
812
|
+
stateTransitions.push('backoff');
|
|
813
|
+
return driverResult({
|
|
814
|
+
status: evidence.retryEligible ? 'backoff' : 'failed',
|
|
815
|
+
receiptStatus: evidence.retryEligible ? 'deferred' : 'failed',
|
|
816
|
+
reasonCode: evidence.reasonCode,
|
|
817
|
+
publicSummary: 'Headless yoloop stopped because physical Git lifecycle evidence could not be produced safely.',
|
|
818
|
+
retryable: evidence.retryEligible,
|
|
819
|
+
startedAt,
|
|
820
|
+
counts: aggregateCounts(iterations, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
821
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
822
|
+
hashes,
|
|
823
|
+
redactions,
|
|
824
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
825
|
+
childStoryIds,
|
|
826
|
+
stateTransitions,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
terminalP205Authority = evidence.p205Authority;
|
|
830
|
+
lifecycleEvidencePrepared = true;
|
|
831
|
+
}
|
|
832
|
+
stateTransitions.push('terminal_git');
|
|
833
|
+
const lockStartedAt = new Date();
|
|
834
|
+
const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(input.projectRoot, {
|
|
835
|
+
kind: 'continuous', storyId: terminalGitDirective.epicId,
|
|
836
|
+
sessionId: `${input.runnerId}:${input.attempt.attempt_id}`,
|
|
837
|
+
idempotencyKey: terminalGitDirective.idempotencyKey,
|
|
838
|
+
persistent: false,
|
|
839
|
+
expiresAt: new Date(lockStartedAt.getTime() + Math.max(input.timeoutMs, input.maxDurationMs) + 60_000).toISOString(),
|
|
840
|
+
});
|
|
841
|
+
if (!physicalLock.ok) {
|
|
842
|
+
stateTransitions.push('backoff');
|
|
843
|
+
return driverResult({
|
|
844
|
+
status: 'backoff', receiptStatus: 'deferred', reasonCode: physicalLock.reasonCode,
|
|
845
|
+
publicSummary: 'Headless yoloop deferred terminal Git while another physical writer retained authority.',
|
|
846
|
+
retryable: true, startedAt,
|
|
847
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
848
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
849
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
let integration;
|
|
853
|
+
let released = false;
|
|
854
|
+
try {
|
|
855
|
+
integration = await executeEpicTerminalGit(terminalGitDirective, input.terminalGitAdapter, input.now, terminalP205Authority);
|
|
856
|
+
}
|
|
857
|
+
finally {
|
|
858
|
+
released = await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner)
|
|
859
|
+
.catch(() => false);
|
|
860
|
+
}
|
|
861
|
+
if (!released) {
|
|
862
|
+
stateTransitions.push('backoff');
|
|
863
|
+
return driverResult({
|
|
864
|
+
status: 'backoff', receiptStatus: 'deferred', reasonCode: 'git-lifecycle-integration-lock-release-failed',
|
|
865
|
+
publicSummary: 'Headless yoloop stopped because terminal Git writer ownership could not be released safely.',
|
|
866
|
+
retryable: true, startedAt,
|
|
867
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
868
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
869
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
hashes[`terminal-git-${iterations}`] = hashPublicValue(`${integration.receipt.idempotencyKey}:${integration.receipt.physicalStatus}`);
|
|
873
|
+
if (!integration.ok) {
|
|
874
|
+
const retryable = integration.receipt.retryEligible;
|
|
875
|
+
stateTransitions.push('backoff');
|
|
876
|
+
return driverResult({
|
|
877
|
+
status: retryable ? 'backoff' : 'failed',
|
|
878
|
+
receiptStatus: retryable ? 'deferred' : 'failed',
|
|
879
|
+
reasonCode: integration.reasonCodes[0] ?? 'epic-terminal-git-failed',
|
|
880
|
+
publicSummary: 'Headless yoloop stopped at the epic terminal Git boundary with public-safe retry evidence.',
|
|
881
|
+
retryable,
|
|
882
|
+
startedAt,
|
|
883
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
884
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
885
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
// A persisted pushed receipt may let the server render queue exhaustion,
|
|
889
|
+
// but it never lets the root accept completion without this invocation's
|
|
890
|
+
// fresh physical ancestry/provider verification. The same directive is
|
|
891
|
+
// therefore emitted on replay and this successful verification is the
|
|
892
|
+
// terminal authority for the current root run.
|
|
893
|
+
if (terminal.complete) {
|
|
894
|
+
metadataCompleteCount += 1;
|
|
895
|
+
stateTransitions.push('complete');
|
|
896
|
+
return driverResult({
|
|
897
|
+
status: 'completed', receiptStatus: 'applied',
|
|
898
|
+
reasonCode: HEADLESS_REASON_CODES.EXECUTION_COMPLETED,
|
|
899
|
+
publicSummary: 'Headless yoloop completed only after fresh root-owned physical Git verification.',
|
|
900
|
+
retryable: false, startedAt,
|
|
901
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
902
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
903
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
iterations += 1;
|
|
907
|
+
stateTransitions.push('reinvoke');
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (terminal.complete) {
|
|
911
|
+
metadataCompleteCount += 1;
|
|
912
|
+
stateTransitions.push('complete');
|
|
913
|
+
return driverResult({
|
|
914
|
+
status: 'completed',
|
|
915
|
+
receiptStatus: 'applied',
|
|
916
|
+
reasonCode: HEADLESS_REASON_CODES.EXECUTION_COMPLETED,
|
|
917
|
+
publicSummary: 'Headless yoloop completed after public metadata reported yoloopComplete.',
|
|
918
|
+
retryable: false,
|
|
919
|
+
startedAt,
|
|
920
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
921
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
922
|
+
hashes,
|
|
923
|
+
redactions,
|
|
924
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
925
|
+
childStoryIds,
|
|
926
|
+
stateTransitions,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
const reconciliationDirective = extractEpicGitReconciliationDirective(metadata);
|
|
930
|
+
if (reconciliationDirective) {
|
|
931
|
+
stateTransitions.push('git_reconciliation');
|
|
932
|
+
const reconciliation = await executeEpicGitReconciliation(reconciliationDirective, input.terminalGitAdapter, input.now);
|
|
933
|
+
hashes[`git-reconciliation-${iterations}`] = hashPublicValue(`${reconciliationDirective.idempotencyKey}:${reconciliation.reasonCodes.join(',')}`);
|
|
934
|
+
if (!reconciliation.ok) {
|
|
935
|
+
stateTransitions.push('backoff');
|
|
936
|
+
return driverResult({
|
|
937
|
+
status: 'backoff', receiptStatus: 'deferred',
|
|
938
|
+
reasonCode: reconciliation.reasonCodes[0] ?? 'git-reconciliation-failed',
|
|
939
|
+
publicSummary: 'Headless yoloop paused at a fail-closed Git reconciliation boundary.',
|
|
940
|
+
retryable: true, startedAt,
|
|
941
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
942
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
943
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const extraction = extractAuthorizedChildPrompts(metadata, input.prepared.epicId);
|
|
948
|
+
if (!extraction.ok) {
|
|
949
|
+
stateTransitions.push('backoff');
|
|
950
|
+
return driverResult({
|
|
951
|
+
status: 'failed',
|
|
952
|
+
receiptStatus: 'failed',
|
|
953
|
+
reasonCode: extraction.reasonCode,
|
|
954
|
+
publicSummary: extraction.publicSummary,
|
|
955
|
+
retryable: false,
|
|
956
|
+
startedAt,
|
|
957
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
958
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
959
|
+
hashes,
|
|
960
|
+
redactions: [...redactions, ...extraction.redactionSummary],
|
|
961
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
962
|
+
childStoryIds,
|
|
963
|
+
stateTransitions,
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
const repeatedChild = extraction.continuous
|
|
967
|
+
? undefined
|
|
968
|
+
: extraction.prompts.find((child) => processedStoryIds.has(child.storyId));
|
|
969
|
+
if (repeatedChild) {
|
|
970
|
+
stateTransitions.push('backoff');
|
|
971
|
+
return driverResult({
|
|
972
|
+
status: 'failed',
|
|
973
|
+
receiptStatus: 'failed',
|
|
974
|
+
reasonCode: HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION,
|
|
975
|
+
publicSummary: 'Headless yoloop rejected cyclic child story delegation evidence.',
|
|
976
|
+
retryable: false,
|
|
977
|
+
startedAt,
|
|
978
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
979
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
980
|
+
hashes,
|
|
981
|
+
redactions: [...redactions, redaction('storyId', 'prompt_body', hashPublicValue(repeatedChild.storyId))],
|
|
982
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
983
|
+
childStoryIds,
|
|
984
|
+
stateTransitions,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
workerPool.mode = extraction.mode === 'continuous_dag'
|
|
988
|
+
? 'continuous_dag'
|
|
989
|
+
: extraction.parallelAuthorized ? 'parallel_wave' : 'serial';
|
|
990
|
+
workerPool.serverMaxConcurrency = extraction.serverMaxConcurrency;
|
|
991
|
+
if (extraction.continuous) {
|
|
992
|
+
continuousModeObserved = true;
|
|
993
|
+
const eligible = extraction.prompts.filter((child) => !processedStoryIds.has(child.storyId) && !continuousInFlight.has(child.storyId));
|
|
994
|
+
workerPool.pendingChildCount = eligible.length + continuousInFlight.size;
|
|
995
|
+
if (eligible.length > 0) {
|
|
996
|
+
const capacity = resolveWorkerPoolCapacity({
|
|
997
|
+
store: input.store,
|
|
998
|
+
nowIso: input.now(),
|
|
999
|
+
localCap: input.localConcurrencyCap,
|
|
1000
|
+
serverMaxConcurrency: extraction.serverMaxConcurrency,
|
|
1001
|
+
parallelAuthorized: true,
|
|
1002
|
+
continuous: true,
|
|
1003
|
+
eligibleCount: eligible.length,
|
|
1004
|
+
});
|
|
1005
|
+
workerPool.queueCap = capacity.queueCap;
|
|
1006
|
+
workerPool.availableLeases = capacity.availableLeases;
|
|
1007
|
+
workerPool.effectiveConcurrency = capacity.effectiveConcurrency;
|
|
1008
|
+
workerPool.reasonCodes.push(capacity.reasonCode);
|
|
1009
|
+
if (capacity.effectiveConcurrency === 0 && continuousInFlight.size === 0) {
|
|
1010
|
+
workerPool.backpressurePauseCount += 1;
|
|
1011
|
+
stateTransitions.push('backoff');
|
|
1012
|
+
return driverResult({
|
|
1013
|
+
status: 'backoff', receiptStatus: 'deferred', reasonCode: HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED,
|
|
1014
|
+
publicSummary: 'Headless yoloop retained its eligible frontier while physical worker capacity was unavailable.',
|
|
1015
|
+
retryable: true, startedAt,
|
|
1016
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
1017
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
1018
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
const launchable = eligible.slice(0, capacity.effectiveConcurrency);
|
|
1022
|
+
stateTransitions.push('child_story_delegation');
|
|
1023
|
+
for (const child of launchable) {
|
|
1024
|
+
continuousInFlight.set(child.storyId, {
|
|
1025
|
+
child,
|
|
1026
|
+
promise: executeYoloopChild({ ...input, child, iteration: iterations }),
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
workerPool.startedChildCount += launchable.length;
|
|
1030
|
+
workerPool.peakConcurrency = Math.max(workerPool.peakConcurrency, continuousInFlight.size);
|
|
1031
|
+
workerPool.pendingChildCount = eligible.length - launchable.length + continuousInFlight.size;
|
|
1032
|
+
}
|
|
1033
|
+
if (continuousInFlight.size > 0) {
|
|
1034
|
+
await settleOneContinuousChild();
|
|
1035
|
+
}
|
|
1036
|
+
else if (continuousDeferredReasons.size > 0) {
|
|
1037
|
+
stateTransitions.push('backoff');
|
|
1038
|
+
return driverResult({
|
|
1039
|
+
status: 'backoff', receiptStatus: 'deferred', reasonCode: [...continuousDeferredReasons.values()].sort()[0],
|
|
1040
|
+
publicSummary: 'Headless yoloop retained deferred continuous work for a bounded retry.',
|
|
1041
|
+
retryable: true, startedAt,
|
|
1042
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
1043
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
1044
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
else if (continuousFailureReasons.size > 0) {
|
|
1048
|
+
stateTransitions.push('backoff');
|
|
1049
|
+
return driverResult({
|
|
1050
|
+
status: 'failed', receiptStatus: 'failed', reasonCode: [...continuousFailureReasons.values()].sort()[0],
|
|
1051
|
+
publicSummary: 'Headless yoloop completed independent work and stopped on the remaining failed dependency closure.',
|
|
1052
|
+
retryable: false, startedAt,
|
|
1053
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
1054
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool), hashes, redactions,
|
|
1055
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs], childStoryIds, stateTransitions,
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
iterations += 1;
|
|
1059
|
+
stateTransitions.push('reinvoke');
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
workerPool.pendingChildCount = extraction.prompts.length;
|
|
1063
|
+
const pending = [...extraction.prompts];
|
|
1064
|
+
while (pending.length > 0) {
|
|
1065
|
+
const capacity = resolveWorkerPoolCapacity({
|
|
1066
|
+
store: input.store,
|
|
1067
|
+
nowIso: input.now(),
|
|
1068
|
+
localCap: input.localConcurrencyCap,
|
|
1069
|
+
serverMaxConcurrency: extraction.serverMaxConcurrency,
|
|
1070
|
+
parallelAuthorized: extraction.parallelAuthorized,
|
|
1071
|
+
continuous: false,
|
|
1072
|
+
eligibleCount: pending.length,
|
|
1073
|
+
});
|
|
1074
|
+
workerPool.queueCap = capacity.queueCap;
|
|
1075
|
+
workerPool.availableLeases = capacity.availableLeases;
|
|
1076
|
+
workerPool.effectiveConcurrency = capacity.effectiveConcurrency;
|
|
1077
|
+
workerPool.reasonCodes.push(capacity.reasonCode);
|
|
1078
|
+
if (capacity.effectiveConcurrency === 0) {
|
|
1079
|
+
workerPool.backpressurePauseCount += 1;
|
|
1080
|
+
workerPool.pendingChildCount = pending.length;
|
|
1081
|
+
stateTransitions.push('backoff');
|
|
1082
|
+
return driverResult({
|
|
1083
|
+
status: 'backoff',
|
|
1084
|
+
receiptStatus: 'deferred',
|
|
1085
|
+
reasonCode: HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED,
|
|
1086
|
+
publicSummary: 'Headless yoloop paused child workers because bounded queue or lease capacity was unavailable.',
|
|
1087
|
+
retryable: true,
|
|
1088
|
+
startedAt,
|
|
1089
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
1090
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
1091
|
+
hashes,
|
|
1092
|
+
redactions,
|
|
1093
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
1094
|
+
childStoryIds,
|
|
1095
|
+
stateTransitions,
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
const batch = pending.splice(0, capacity.effectiveConcurrency);
|
|
1099
|
+
workerPool.startedChildCount += batch.length;
|
|
1100
|
+
workerPool.peakConcurrency = Math.max(workerPool.peakConcurrency, batch.length);
|
|
1101
|
+
workerPool.pendingChildCount = pending.length;
|
|
1102
|
+
stateTransitions.push('child_story_delegation');
|
|
1103
|
+
const settled = await Promise.all(batch.map((child) => executeYoloopChild({
|
|
1104
|
+
...input,
|
|
1105
|
+
child,
|
|
1106
|
+
iteration: iterations,
|
|
1107
|
+
})));
|
|
1108
|
+
workerPool.completedChildCount += settled.length;
|
|
1109
|
+
let terminalChild;
|
|
1110
|
+
for (const result of settled) {
|
|
1111
|
+
const child = result.child;
|
|
1112
|
+
hashes[`child-${child.storyId.replace(/[^A-Za-z0-9_.:-]/g, '-')}`] = result.commandHash;
|
|
1113
|
+
redactions.push(result.promptRedaction);
|
|
1114
|
+
childStoryIds.push(child.storyId);
|
|
1115
|
+
childTargetRefs.push(child.targetRef);
|
|
1116
|
+
processedStoryIds.add(child.storyId);
|
|
1117
|
+
const receipt = result.receipt;
|
|
1118
|
+
if (!receipt) {
|
|
1119
|
+
if (result.reasonCodes.includes('queue-cap-reached')) {
|
|
1120
|
+
workerPool.backpressurePauseCount += 1;
|
|
1121
|
+
workerPool.pendingChildCount += 1;
|
|
1122
|
+
workerPool.reasonCodes.push(HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED);
|
|
1123
|
+
terminalChild ??= { kind: 'deferred', reasonCode: HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED };
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
failedChildCount += 1;
|
|
1127
|
+
terminalChild ??= { kind: 'missing', reasonCode: HEADLESS_REASON_CODES.EXECUTION_FAILED };
|
|
1128
|
+
}
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (receipt.outcome === 'deferred') {
|
|
1132
|
+
terminalChild ??= { kind: 'deferred', reasonCode: HEADLESS_REASON_CODES.EXECUTION_BACKOFF };
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (receipt.outcome === 'failed') {
|
|
1136
|
+
failedChildCount += 1;
|
|
1137
|
+
terminalChild ??= { kind: 'failed', reasonCode: receipt.reasonCode || HEADLESS_REASON_CODES.EXECUTION_FAILED };
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
if (receipt.outcome === 'ignored')
|
|
1141
|
+
skippedChildCount += 1;
|
|
1142
|
+
else
|
|
1143
|
+
succeededChildCount += 1;
|
|
1144
|
+
}
|
|
1145
|
+
if (terminalChild) {
|
|
1146
|
+
const deferred = terminalChild.kind === 'deferred';
|
|
1147
|
+
return driverResult({
|
|
1148
|
+
status: deferred ? 'backoff' : 'failed',
|
|
1149
|
+
receiptStatus: deferred ? 'deferred' : 'failed',
|
|
1150
|
+
reasonCode: terminalChild.reasonCode,
|
|
1151
|
+
publicSummary: terminalChild.kind === 'missing'
|
|
1152
|
+
? 'Headless yoloop child story delegation did not produce a public receipt.'
|
|
1153
|
+
: deferred
|
|
1154
|
+
? 'Headless yoloop child story delegated bounded backoff to the root attempt.'
|
|
1155
|
+
: 'Headless yoloop child story execution failed with public receipt evidence.',
|
|
1156
|
+
retryable: deferred,
|
|
1157
|
+
startedAt,
|
|
1158
|
+
counts: aggregateCounts(iterations + 1, childStoryIds.length, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount),
|
|
1159
|
+
workerPoolSummary: publicWorkerPoolSummary(workerPool),
|
|
1160
|
+
hashes,
|
|
1161
|
+
redactions,
|
|
1162
|
+
relativePaths: [input.prepared.targetRef, ...childTargetRefs],
|
|
1163
|
+
childStoryIds,
|
|
1164
|
+
stateTransitions,
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
iterations += 1;
|
|
1169
|
+
stateTransitions.push('reinvoke');
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
async function executeYoloopChild(input) {
|
|
1173
|
+
const workspace = input.child.dispatchId
|
|
1174
|
+
? await input.continuousWorkspaceManager.prepare({
|
|
1175
|
+
projectRoot: input.projectRoot,
|
|
1176
|
+
epicId: input.prepared.epicId,
|
|
1177
|
+
storyId: input.child.storyId,
|
|
1178
|
+
targetRef: input.child.targetRef,
|
|
1179
|
+
dispatchId: input.child.dispatchId,
|
|
1180
|
+
executionBranch: input.child.executionBranch ?? '',
|
|
1181
|
+
graphRevision: input.child.graphRevision ?? '',
|
|
1182
|
+
authorizedWriteSet: input.child.writeSet ?? [],
|
|
1183
|
+
})
|
|
1184
|
+
: undefined;
|
|
1185
|
+
if (workspace && !workspace.ok) {
|
|
1186
|
+
return {
|
|
1187
|
+
child: input.child,
|
|
1188
|
+
reasonCodes: [workspace.reasonCode],
|
|
1189
|
+
commandHash: hashPublicValue(input.child.commandRef),
|
|
1190
|
+
promptRedaction: redaction(`child.${input.child.storyId}`, 'prompt_body', hashPublicValue(input.child.commandRef)),
|
|
1191
|
+
terminalFailure: workspace.reasonCode,
|
|
1192
|
+
deferredFailure: /backpressure|lease-active/.test(workspace.reasonCode),
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
const childProjectRoot = input.projectRoot;
|
|
1196
|
+
const childCommandRunner = workspace?.ok
|
|
1197
|
+
? createAnchoredContinuousStoryCommandRunner(workspace.value)
|
|
1198
|
+
: input.adapterOptions?.commandRunner;
|
|
1199
|
+
const childDirective = buildChildRunYoloDirective({
|
|
1200
|
+
rootAttempt: input.attempt,
|
|
1201
|
+
child: input.child,
|
|
1202
|
+
iteration: input.iteration,
|
|
1203
|
+
runtimeId: input.prepared.runtimeId,
|
|
1204
|
+
modelId: input.prepared.modelId,
|
|
1205
|
+
runtimeSource: input.prepared.runtimeSource,
|
|
1206
|
+
});
|
|
1207
|
+
const childAttemptId = headlessExecutionEntityId('attempt', childDirective.idempotencyKey);
|
|
1208
|
+
let preserveWorkspace = Boolean(workspace);
|
|
1209
|
+
try {
|
|
1210
|
+
const childResult = await runYoloExecutionPhase({
|
|
1211
|
+
store: input.store,
|
|
1212
|
+
projectRoot: childProjectRoot,
|
|
1213
|
+
runnerId: input.runnerId,
|
|
1214
|
+
now: input.now,
|
|
1215
|
+
metadata: { continuityExecutionDirectives: [childDirective] },
|
|
1216
|
+
adapterRegistry: input.registry,
|
|
1217
|
+
adapterOptions: {
|
|
1218
|
+
...input.adapterOptions,
|
|
1219
|
+
cwd: childProjectRoot,
|
|
1220
|
+
env: {
|
|
1221
|
+
...input.adapterOptions?.env,
|
|
1222
|
+
...(workspace?.ok ? {
|
|
1223
|
+
NEOCORTEX_BRANCHLESS_PROJECT_ROOT: workspace.value.workspacePath,
|
|
1224
|
+
NEOCORTEX_BRANCHLESS_ROOT_AUTHORITY: JSON.stringify(input.child.rootAuthority),
|
|
1225
|
+
} : {}),
|
|
1226
|
+
},
|
|
1227
|
+
...(childCommandRunner ? { commandRunner: childCommandRunner, versionRunner: childCommandRunner } : {}),
|
|
1228
|
+
},
|
|
1229
|
+
runtimeDefaults: input.runtimeDefaults,
|
|
1230
|
+
runtimeFallback: input.runtimeFallback,
|
|
1231
|
+
timeoutMs: input.timeoutMs,
|
|
1232
|
+
maxAttempts: 1,
|
|
1233
|
+
limit: 1,
|
|
1234
|
+
queueCap: input.queueCap,
|
|
1235
|
+
attemptIds: [childAttemptId],
|
|
1236
|
+
idFactory: input.idFactory,
|
|
1237
|
+
});
|
|
1238
|
+
const receipt = childResult.receipts.find((item) => item.attemptId === childAttemptId) ?? childResult.receipts[0];
|
|
1239
|
+
if (workspace?.ok && receipt && receipt.outcome !== 'failed' && receipt.outcome !== 'deferred') {
|
|
1240
|
+
const integration = await input.continuousWorkspaceManager.integrate(workspace.value, {
|
|
1241
|
+
receiptRefs: [`continuity:${receipt.attemptId}`],
|
|
1242
|
+
nowIso: receipt.timestamp,
|
|
1243
|
+
});
|
|
1244
|
+
if (!integration.ok) {
|
|
1245
|
+
return {
|
|
1246
|
+
child: input.child,
|
|
1247
|
+
receipt,
|
|
1248
|
+
reasonCodes: [...childResult.reasonCodes, integration.reasonCode],
|
|
1249
|
+
commandHash: hashPublicValue(input.child.commandRef),
|
|
1250
|
+
promptRedaction: redaction(`child.${input.child.storyId}`, 'prompt_body', hashPublicValue(input.child.commandRef)),
|
|
1251
|
+
terminalFailure: integration.reasonCode,
|
|
1252
|
+
deferredFailure: /backpressure|graph-revision-stale/.test(integration.reasonCode),
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
preserveWorkspace = false;
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
child: input.child,
|
|
1259
|
+
receipt,
|
|
1260
|
+
reasonCodes: childResult.reasonCodes,
|
|
1261
|
+
commandHash: hashPublicValue(input.child.commandRef),
|
|
1262
|
+
promptRedaction: redaction(`child.${input.child.storyId}`, 'prompt_body', hashPublicValue(input.child.commandRef)),
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
finally {
|
|
1266
|
+
if (workspace?.ok)
|
|
1267
|
+
await input.continuousWorkspaceManager.release(workspace.value, preserveWorkspace);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function resolveWorkerPoolCapacity(input) {
|
|
1271
|
+
const snapshot = input.store.collectSnapshotRows({
|
|
1272
|
+
nowIso: input.nowIso,
|
|
1273
|
+
jobLimit: 1,
|
|
1274
|
+
staleLeaseLimit: 1,
|
|
1275
|
+
receiptLimit: 1,
|
|
1276
|
+
eventLimit: 1,
|
|
1277
|
+
snapshotLimit: 1,
|
|
1278
|
+
});
|
|
1279
|
+
const queueCap = Math.max(1, Math.floor(snapshot.queue.capacity));
|
|
1280
|
+
// The current run_yoloop parent owns one active queue lease. Only other active
|
|
1281
|
+
// headless workers reduce the local child-lease budget.
|
|
1282
|
+
const activeOtherLeases = Math.max(0, Math.floor(snapshot.queue.running) - 1);
|
|
1283
|
+
const availableLeases = input.continuous
|
|
1284
|
+
? Math.max(0, Math.floor(snapshot.queue.available))
|
|
1285
|
+
: Math.max(0, Math.min(Math.max(0, input.localCap - activeOtherLeases), Math.floor(snapshot.queue.available)));
|
|
1286
|
+
const serverMaxConcurrency = input.parallelAuthorized
|
|
1287
|
+
? Math.max(1, Math.floor(input.serverMaxConcurrency))
|
|
1288
|
+
: 1;
|
|
1289
|
+
const effectiveConcurrency = Math.max(0, Math.min(input.eligibleCount, serverMaxConcurrency, input.localCap, queueCap, availableLeases));
|
|
1290
|
+
return {
|
|
1291
|
+
queueCap,
|
|
1292
|
+
availableLeases,
|
|
1293
|
+
effectiveConcurrency,
|
|
1294
|
+
reasonCode: effectiveConcurrency === 0
|
|
1295
|
+
? HEADLESS_REASON_CODES.WORKER_POOL_BACKPRESSURE_PAUSED
|
|
1296
|
+
: (input.parallelAuthorized || input.continuous) && effectiveConcurrency > 1
|
|
1297
|
+
? HEADLESS_REASON_CODES.WORKER_POOL_PARALLEL_ACTIVE
|
|
1298
|
+
: HEADLESS_REASON_CODES.WORKER_POOL_SERIAL_FALLBACK,
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
function createMutableWorkerPoolSummary(localCap) {
|
|
1302
|
+
return {
|
|
1303
|
+
mode: 'serial',
|
|
1304
|
+
serverMaxConcurrency: 1,
|
|
1305
|
+
localCap,
|
|
1306
|
+
queueCap: 1,
|
|
1307
|
+
availableLeases: 1,
|
|
1308
|
+
effectiveConcurrency: 1,
|
|
1309
|
+
peakConcurrency: 0,
|
|
1310
|
+
pendingChildCount: 0,
|
|
1311
|
+
startedChildCount: 0,
|
|
1312
|
+
completedChildCount: 0,
|
|
1313
|
+
backpressurePauseCount: 0,
|
|
1314
|
+
reasonCodes: [],
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
function publicWorkerPoolSummary(summary) {
|
|
1318
|
+
return {
|
|
1319
|
+
mode: summary.mode,
|
|
1320
|
+
serverMaxConcurrency: boundedCount(summary.serverMaxConcurrency),
|
|
1321
|
+
localCap: boundedCount(summary.localCap),
|
|
1322
|
+
queueCap: boundedCount(summary.queueCap),
|
|
1323
|
+
availableLeases: boundedCount(summary.availableLeases),
|
|
1324
|
+
effectiveConcurrency: boundedCount(summary.effectiveConcurrency),
|
|
1325
|
+
peakConcurrency: boundedCount(summary.peakConcurrency),
|
|
1326
|
+
pendingChildCount: boundedCount(summary.pendingChildCount),
|
|
1327
|
+
startedChildCount: boundedCount(summary.startedChildCount),
|
|
1328
|
+
completedChildCount: boundedCount(summary.completedChildCount),
|
|
1329
|
+
backpressurePauseCount: boundedCount(summary.backpressurePauseCount),
|
|
1330
|
+
reasonCodes: unique(summary.reasonCodes.length > 0
|
|
1331
|
+
? summary.reasonCodes
|
|
1332
|
+
: [HEADLESS_REASON_CODES.WORKER_POOL_SERIAL_FALLBACK]).slice(0, 10),
|
|
1333
|
+
publicSafe: true,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function defaultWorkerPoolSummary(localCap = resolveDefaultHeadlessYoloopLocalConcurrencyCap()) {
|
|
1337
|
+
return publicWorkerPoolSummary(createMutableWorkerPoolSummary(localCap));
|
|
1338
|
+
}
|
|
1339
|
+
function boundedCount(value) {
|
|
1340
|
+
if (!Number.isFinite(value))
|
|
1341
|
+
return 0;
|
|
1342
|
+
return Math.max(0, Math.min(10_000, Math.floor(value)));
|
|
1343
|
+
}
|
|
1344
|
+
function prepareYoloopAttemptInput(input) {
|
|
1345
|
+
const governance = readAutonomousGovernanceEnvelopeFromMetadata(input.attempt.metadata_json, {
|
|
1346
|
+
autonomousKind: 'run_yoloop',
|
|
1347
|
+
jobId: input.attempt.job_id,
|
|
1348
|
+
targetKind: 'epic',
|
|
1349
|
+
targetRef: input.attempt.target_ref,
|
|
1350
|
+
});
|
|
1351
|
+
if (!governance.ok) {
|
|
1352
|
+
return {
|
|
1353
|
+
ok: false,
|
|
1354
|
+
status: 'failed',
|
|
1355
|
+
receiptStatus: 'failed',
|
|
1356
|
+
reasonCode: governance.reasonCode,
|
|
1357
|
+
publicSummary: governance.publicSummary,
|
|
1358
|
+
redactionSummary: governance.redactionSummary,
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
const promptValidation = validateRunYoloopAttemptShape(input.attempt);
|
|
1362
|
+
if (!promptValidation.ok)
|
|
1363
|
+
return promptValidation;
|
|
1364
|
+
const runtime = resolveAttemptRuntime(input);
|
|
1365
|
+
if (!runtime.ok) {
|
|
1366
|
+
return {
|
|
1367
|
+
ok: false,
|
|
1368
|
+
status: 'failed',
|
|
1369
|
+
receiptStatus: 'failed',
|
|
1370
|
+
reasonCode: runtime.reasonCode,
|
|
1371
|
+
publicSummary: runtime.publicSummary,
|
|
1372
|
+
redactionSummary: runtime.redactionSummary,
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
const epicId = input.attempt.epic_id ?? epicIdFromTargetRef(input.attempt.target_ref);
|
|
1376
|
+
return {
|
|
1377
|
+
ok: true,
|
|
1378
|
+
input: {
|
|
1379
|
+
rootPrompt: `*yoloop @${input.attempt.target_ref}`,
|
|
1380
|
+
targetRef: input.attempt.target_ref,
|
|
1381
|
+
epicId,
|
|
1382
|
+
runtimeId: runtime.runtimeId,
|
|
1383
|
+
...(runtime.modelId ? { modelId: runtime.modelId } : {}),
|
|
1384
|
+
runtimeSource: runtime.runtimeSource,
|
|
1385
|
+
publicSafe: true,
|
|
1386
|
+
},
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
function validateRunYoloopAttemptShape(attempt) {
|
|
1390
|
+
const redactions = [];
|
|
1391
|
+
if (attempt.directive_kind !== 'run_yoloop')
|
|
1392
|
+
redactions.push(redaction('directiveKind', 'unsafe_key', 'value-denied'));
|
|
1393
|
+
if (attempt.target_kind !== 'epic')
|
|
1394
|
+
redactions.push(redaction('targetKind', 'prompt_body', 'value-denied'));
|
|
1395
|
+
if (!EPIC_TARGET_PATTERN.test(attempt.target_ref) || SHELL_FRAGMENT_PATTERN.test(attempt.target_ref) || UNSAFE_VALUE_PATTERN.test(attempt.target_ref)) {
|
|
1396
|
+
redactions.push(redaction('targetRef', 'prompt_body', 'value-denied'));
|
|
1397
|
+
}
|
|
1398
|
+
const epicId = epicIdFromTargetRef(attempt.target_ref);
|
|
1399
|
+
if (attempt.epic_id && attempt.epic_id !== epicId)
|
|
1400
|
+
redactions.push(redaction('epicId', 'prompt_body', 'value-denied'));
|
|
1401
|
+
if (redactions.length > 0) {
|
|
1402
|
+
return {
|
|
1403
|
+
ok: false,
|
|
1404
|
+
status: 'failed',
|
|
1405
|
+
receiptStatus: 'failed',
|
|
1406
|
+
reasonCode: HEADLESS_REASON_CODES.PROMPT_REJECTED,
|
|
1407
|
+
publicSummary: 'Headless run_yoloop attempt did not match the canonical public epic prompt shape.',
|
|
1408
|
+
redactionSummary: redactions,
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
ok: true,
|
|
1413
|
+
input: {
|
|
1414
|
+
rootPrompt: `*yoloop @${attempt.target_ref}`,
|
|
1415
|
+
targetRef: attempt.target_ref,
|
|
1416
|
+
epicId,
|
|
1417
|
+
runtimeId: 'fake',
|
|
1418
|
+
publicSafe: true,
|
|
1419
|
+
},
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
function resolveAttemptRuntime(input) {
|
|
1423
|
+
const defaults = input.runtimeDefaults !== undefined
|
|
1424
|
+
? input.runtimeDefaults
|
|
1425
|
+
: loadHeadlessRuntimeDefaults({ projectRoot: input.projectRoot });
|
|
1426
|
+
if (isRecord(defaults) && defaults.ok === false) {
|
|
1427
|
+
return {
|
|
1428
|
+
ok: false,
|
|
1429
|
+
reasonCode: HEADLESS_REASON_CODES.RUNTIME_UNTRUSTED,
|
|
1430
|
+
publicSummary: typeof defaults.publicSummary === 'string' ? defaults.publicSummary : 'Headless runtime defaults failed public-safety validation.',
|
|
1431
|
+
redactionSummary: Array.isArray(defaults.redactionSummary) ? defaults.redactionSummary : [],
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
const source = normalizeRuntimeSource(input.attempt.runtime_source) ?? 'explicit';
|
|
1435
|
+
const hint = (input.attempt.runtime_id || input.attempt.model_id)
|
|
1436
|
+
? {
|
|
1437
|
+
...(input.attempt.runtime_id ? { runtimeId: input.attempt.runtime_id } : {}),
|
|
1438
|
+
...(input.attempt.model_id ? { modelId: input.attempt.model_id } : {}),
|
|
1439
|
+
source,
|
|
1440
|
+
publicSafe: true,
|
|
1441
|
+
}
|
|
1442
|
+
: undefined;
|
|
1443
|
+
const resolutionInput = {
|
|
1444
|
+
registry: input.registry,
|
|
1445
|
+
defaults,
|
|
1446
|
+
fallback: input.runtimeFallback,
|
|
1447
|
+
};
|
|
1448
|
+
if (hint) {
|
|
1449
|
+
if (source === 'origin')
|
|
1450
|
+
resolutionInput.origin = hint;
|
|
1451
|
+
else if (source === 'default')
|
|
1452
|
+
resolutionInput.projectDefault = hint;
|
|
1453
|
+
else if (source === 'fallback')
|
|
1454
|
+
resolutionInput.fallback = hint;
|
|
1455
|
+
else
|
|
1456
|
+
resolutionInput.explicit = hint;
|
|
1457
|
+
}
|
|
1458
|
+
const resolution = resolveHeadlessRuntime(resolutionInput);
|
|
1459
|
+
if (!resolution.ok) {
|
|
1460
|
+
return {
|
|
1461
|
+
ok: false,
|
|
1462
|
+
reasonCode: resolution.reasonCode,
|
|
1463
|
+
publicSummary: resolution.publicSummary,
|
|
1464
|
+
redactionSummary: resolution.redactionSummary,
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
return {
|
|
1468
|
+
ok: true,
|
|
1469
|
+
runtimeId: resolution.runtimeId,
|
|
1470
|
+
...(resolution.modelId ? { modelId: resolution.modelId } : {}),
|
|
1471
|
+
runtimeSource: resolution.runtimeSource,
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
function validateRunYoloopExecutionDirective(input) {
|
|
1475
|
+
const redactions = [];
|
|
1476
|
+
for (const key of Object.keys(input)) {
|
|
1477
|
+
if (!EXECUTION_DIRECTIVE_ALLOWED_KEYS.has(key) || UNSAFE_KEY_PATTERN.test(key)) {
|
|
1478
|
+
redactions.push(redaction(key, unsafeKeyReason(key), 'key-denied'));
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
if (input.publicSafe !== true)
|
|
1482
|
+
redactions.push(redaction('publicSafe', 'unsafe_key', 'value-denied'));
|
|
1483
|
+
const kind = stringField(input, 'kind');
|
|
1484
|
+
const commandClass = stringField(input, 'commandClass');
|
|
1485
|
+
const jobId = stringField(input, 'jobId');
|
|
1486
|
+
const operationId = stringField(input, 'operationId');
|
|
1487
|
+
const idempotencyKey = stringField(input, 'idempotencyKey');
|
|
1488
|
+
const targetKind = stringField(input, 'targetKind');
|
|
1489
|
+
const targetRef = stringField(input, 'targetRef');
|
|
1490
|
+
const commandRef = stringField(input, 'commandRef');
|
|
1491
|
+
const publicSummary = stringField(input, 'publicSummary');
|
|
1492
|
+
const epicId = stringField(input, 'epicId');
|
|
1493
|
+
if (kind !== 'run_yoloop')
|
|
1494
|
+
redactions.push(redaction('kind', 'unsafe_key', 'value-denied'));
|
|
1495
|
+
if (commandClass !== 'goal-tick' && commandClass !== 'loop-tick')
|
|
1496
|
+
redactions.push(redaction('commandClass', 'unsafe_key', 'value-denied'));
|
|
1497
|
+
if (!isSafeIdentifier(jobId))
|
|
1498
|
+
redactions.push(redaction('jobId', 'unsafe_key', 'value-denied'));
|
|
1499
|
+
if (!isSafeIdentifier(operationId))
|
|
1500
|
+
redactions.push(redaction('operationId', 'unsafe_key', 'value-denied'));
|
|
1501
|
+
if (!isSafeIdentifier(idempotencyKey))
|
|
1502
|
+
redactions.push(redaction('idempotencyKey', 'unsafe_key', 'value-denied'));
|
|
1503
|
+
if (targetKind !== 'epic' || !targetRef || !EPIC_TARGET_PATTERN.test(targetRef) || UNSAFE_VALUE_PATTERN.test(targetRef)) {
|
|
1504
|
+
redactions.push(redaction('targetRef', 'prompt_body', 'value-denied'));
|
|
1505
|
+
}
|
|
1506
|
+
const expectedCommandRef = targetRef ? `*yoloop @${targetRef}` : undefined;
|
|
1507
|
+
if (!commandRef || commandRef !== expectedCommandRef || commandRef.includes('@neocortex') || SHELL_FRAGMENT_PATTERN.test(commandRef) || UNSAFE_VALUE_PATTERN.test(commandRef)) {
|
|
1508
|
+
redactions.push(redaction('commandRef', 'prompt_body', 'value-denied'));
|
|
1509
|
+
}
|
|
1510
|
+
if (epicId && targetRef && epicId !== epicIdFromTargetRef(targetRef))
|
|
1511
|
+
redactions.push(redaction('epicId', 'prompt_body', 'value-denied'));
|
|
1512
|
+
if (!publicSummary || publicSummary.length > 240 || UNSAFE_VALUE_PATTERN.test(publicSummary)) {
|
|
1513
|
+
redactions.push(redaction('publicSummary', 'unbounded_text', 'value-denied'));
|
|
1514
|
+
}
|
|
1515
|
+
if (input.reasonCode !== undefined && (typeof input.reasonCode !== 'string' || UNSAFE_VALUE_PATTERN.test(input.reasonCode))) {
|
|
1516
|
+
redactions.push(redaction('reasonCode', 'unsafe_key', 'value-denied'));
|
|
1517
|
+
}
|
|
1518
|
+
const runtime = validateDirectiveRuntime(input.runtime, redactions);
|
|
1519
|
+
if (input.attempts !== undefined)
|
|
1520
|
+
assertPublicSafeCandidate(input.attempts, 'attempts', redactions);
|
|
1521
|
+
if (input.metadata !== undefined)
|
|
1522
|
+
assertPublicSafeCandidate(input.metadata, 'metadata', redactions);
|
|
1523
|
+
const metadata = isRecord(input.metadata) ? input.metadata : undefined;
|
|
1524
|
+
const redactionSummary = validateDirectiveRedactions(input.redactionSummary, redactions);
|
|
1525
|
+
if (redactions.length > 0 || kind !== 'run_yoloop' || !commandClass || !jobId || !operationId || !idempotencyKey || !targetRef || !commandRef || !publicSummary) {
|
|
1526
|
+
const promptRejected = redactions.some((item) => item.reason === 'prompt_body');
|
|
1527
|
+
return {
|
|
1528
|
+
ok: false,
|
|
1529
|
+
reasonCode: promptRejected ? HEADLESS_REASON_CODES.PROMPT_REJECTED : HEADLESS_REASON_CODES.DIRECTIVE_REJECTED,
|
|
1530
|
+
redactionSummary: redactions,
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
return {
|
|
1534
|
+
ok: true,
|
|
1535
|
+
directive: {
|
|
1536
|
+
kind: 'run_yoloop',
|
|
1537
|
+
commandClass: commandClass,
|
|
1538
|
+
jobId: jobId,
|
|
1539
|
+
operationId: operationId,
|
|
1540
|
+
idempotencyKey: idempotencyKey,
|
|
1541
|
+
targetKind: 'epic',
|
|
1542
|
+
targetRef: targetRef,
|
|
1543
|
+
commandRef: commandRef,
|
|
1544
|
+
...(epicId ? { epicId } : {}),
|
|
1545
|
+
...(runtime ? { runtime } : {}),
|
|
1546
|
+
publicSummary: publicSummary,
|
|
1547
|
+
...(typeof input.reasonCode === 'string' ? { reasonCode: input.reasonCode } : {}),
|
|
1548
|
+
...(metadata ? { metadata } : {}),
|
|
1549
|
+
...(redactionSummary.length > 0 ? { redactionSummary } : {}),
|
|
1550
|
+
publicSafe: true,
|
|
1551
|
+
},
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function extractAuthorizedChildPrompts(metadata, epicId) {
|
|
1555
|
+
if (!metadata) {
|
|
1556
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop metadata was missing.', []);
|
|
1557
|
+
}
|
|
1558
|
+
const publicSafety = validateYoloopMetadataPublicSubset(metadata);
|
|
1559
|
+
if (!publicSafety.ok)
|
|
1560
|
+
return publicSafety;
|
|
1561
|
+
const prompts = [];
|
|
1562
|
+
const plan = isRecord(metadata.yoloopExecutionPlan) ? metadata.yoloopExecutionPlan : undefined;
|
|
1563
|
+
const planStoryIds = plan ? safeStringArray(plan.storyIds).filter(isStoryId) : [];
|
|
1564
|
+
const continuousNodes = plan?.version === 3 && isRecord(plan.scheduler) && Array.isArray(plan.scheduler.nodes)
|
|
1565
|
+
? new Map(plan.scheduler.nodes.filter(isRecord).map((node) => [String(node.storyId ?? ''), node]))
|
|
1566
|
+
: new Map();
|
|
1567
|
+
const continuousGraphRevision = plan?.version === 3 && isRecord(plan.scheduler) && typeof plan.scheduler.graphRevision === 'string'
|
|
1568
|
+
? plan.scheduler.graphRevision
|
|
1569
|
+
: undefined;
|
|
1570
|
+
const continuousExecutionBranch = plan?.version === 3 && typeof plan.executionBranch === 'string'
|
|
1571
|
+
? plan.executionBranch
|
|
1572
|
+
: undefined;
|
|
1573
|
+
let planValidation;
|
|
1574
|
+
if (plan) {
|
|
1575
|
+
const validation = validateExecutionPlan(plan);
|
|
1576
|
+
if (!validation.ok)
|
|
1577
|
+
return validation;
|
|
1578
|
+
planValidation = validation;
|
|
1579
|
+
for (const prompt of extractPlanPrompts(plan)) {
|
|
1580
|
+
const normalized = normalizeChildPrompt(prompt, epicId, 'execution-plan');
|
|
1581
|
+
if (!normalized.ok)
|
|
1582
|
+
return normalized;
|
|
1583
|
+
prompts.push(enrichContinuousPrompt(normalized.prompt, continuousNodes.get(normalized.prompt.storyId), continuousGraphRevision, continuousExecutionBranch));
|
|
1584
|
+
}
|
|
1585
|
+
if (prompts.length === 0) {
|
|
1586
|
+
for (const storyId of planStoryIds) {
|
|
1587
|
+
const normalized = normalizeChildPrompt(`*yolo @docs/stories/${storyId}.story.md`, epicId, 'execution-plan');
|
|
1588
|
+
if (!normalized.ok)
|
|
1589
|
+
return normalized;
|
|
1590
|
+
prompts.push(enrichContinuousPrompt(normalized.prompt, continuousNodes.get(normalized.prompt.storyId), continuousGraphRevision, continuousExecutionBranch));
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
const directiveCompatibility = validatePlanTaskDirectives(metadata.skillDirectives, prompts);
|
|
1594
|
+
if (!directiveCompatibility.ok)
|
|
1595
|
+
return directiveCompatibility;
|
|
1596
|
+
}
|
|
1597
|
+
const nextStoryId = typeof metadata.nextStoryId === 'string' && isStoryId(metadata.nextStoryId)
|
|
1598
|
+
? metadata.nextStoryId
|
|
1599
|
+
: undefined;
|
|
1600
|
+
// A validated execution plan is the sole child-dispatch authority. Compatible
|
|
1601
|
+
// duplicate task directives were checked above but never become a second
|
|
1602
|
+
// dispatch source.
|
|
1603
|
+
for (const directive of plan ? [] : Array.isArray(metadata.skillDirectives) ? metadata.skillDirectives : []) {
|
|
1604
|
+
if (!isRecord(directive) || directive.kind !== 'task')
|
|
1605
|
+
continue;
|
|
1606
|
+
const explicitPrompts = safeStringArray(directive.promptTemplates);
|
|
1607
|
+
if (explicitPrompts.length > 0) {
|
|
1608
|
+
for (const prompt of explicitPrompts) {
|
|
1609
|
+
const normalized = normalizeChildPrompt(prompt, epicId, 'task-directive');
|
|
1610
|
+
if (!normalized.ok)
|
|
1611
|
+
return normalized;
|
|
1612
|
+
prompts.push(normalized.prompt);
|
|
1613
|
+
}
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
const template = typeof directive.promptTemplate === 'string' ? directive.promptTemplate : undefined;
|
|
1617
|
+
if (!template)
|
|
1618
|
+
continue;
|
|
1619
|
+
const storyIds = safeStringArray(directive.storyIds).filter(isStoryId);
|
|
1620
|
+
const ids = storyIds.length > 0 ? storyIds : nextStoryId ? [nextStoryId] : [];
|
|
1621
|
+
if (ids.length === 0 && CHILD_PROMPT_PATTERN.test(template)) {
|
|
1622
|
+
const normalized = normalizeChildPrompt(template, epicId, 'task-directive');
|
|
1623
|
+
if (!normalized.ok)
|
|
1624
|
+
return normalized;
|
|
1625
|
+
prompts.push(normalized.prompt);
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
for (const storyId of ids) {
|
|
1629
|
+
const prompt = template.includes('{storyId}') ? template.replaceAll('{storyId}', storyId) : template;
|
|
1630
|
+
const normalized = normalizeChildPrompt(prompt, epicId, 'task-directive');
|
|
1631
|
+
if (!normalized.ok)
|
|
1632
|
+
return normalized;
|
|
1633
|
+
prompts.push(normalized.prompt);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
if (prompts.length === 0 && nextStoryId) {
|
|
1637
|
+
const normalized = normalizeChildPrompt(`*yolo @docs/stories/${nextStoryId}.story.md`, epicId, 'next-story');
|
|
1638
|
+
if (!normalized.ok)
|
|
1639
|
+
return normalized;
|
|
1640
|
+
prompts.push(normalized.prompt);
|
|
1641
|
+
}
|
|
1642
|
+
const uniquePrompts = uniqueBy(prompts, (item) => item.commandRef);
|
|
1643
|
+
if (planValidation?.continuous) {
|
|
1644
|
+
if (uniquePrompts.length !== planStoryIds.length
|
|
1645
|
+
|| uniquePrompts.some((prompt) => prompt.preparationOnly !== true || prompt.source !== 'execution-plan')) {
|
|
1646
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless continuous yoloop rejected an ambiguous or non-preparation child prompt.', [redaction('promptTemplates', 'prompt_body', 'operation-denied')]);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
else if (uniquePrompts.some((prompt) => prompt.preparationOnly === true)) {
|
|
1650
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected branchless preparation outside a validated continuous plan.', [redaction('promptTemplates', 'prompt_body', 'operation-denied')]);
|
|
1651
|
+
}
|
|
1652
|
+
if (planStoryIds.length > 0) {
|
|
1653
|
+
const authorized = new Set(planStoryIds);
|
|
1654
|
+
for (const prompt of uniquePrompts) {
|
|
1655
|
+
if (!authorized.has(prompt.storyId)) {
|
|
1656
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected a child prompt outside the server-authorized execution plan.', [redaction('storyId', 'prompt_body', hashPublicValue(prompt.storyId))]);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (planValidation?.parallelAuthorized && !sameStringSet(uniquePrompts.map((item) => item.storyId), planStoryIds)) {
|
|
1661
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop rejected a parallel plan whose executable prompts did not match the selected wave.', []);
|
|
1662
|
+
}
|
|
1663
|
+
if (uniquePrompts.length === 0 && !planValidation?.continuous) {
|
|
1664
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop metadata did not include an authorized per-story child prompt.', []);
|
|
1665
|
+
}
|
|
1666
|
+
return {
|
|
1667
|
+
ok: true,
|
|
1668
|
+
prompts: uniquePrompts,
|
|
1669
|
+
mode: planValidation?.mode ?? 'legacy',
|
|
1670
|
+
serverMaxConcurrency: planValidation?.serverMaxConcurrency ?? 1,
|
|
1671
|
+
parallelAuthorized: planValidation?.parallelAuthorized ?? false,
|
|
1672
|
+
continuous: planValidation?.continuous ?? false,
|
|
1673
|
+
reasonCodes: [
|
|
1674
|
+
HEADLESS_REASON_CODES.OK,
|
|
1675
|
+
planValidation?.parallelAuthorized
|
|
1676
|
+
? HEADLESS_REASON_CODES.WORKER_POOL_PARALLEL_ACTIVE
|
|
1677
|
+
: HEADLESS_REASON_CODES.WORKER_POOL_SERIAL_FALLBACK,
|
|
1678
|
+
],
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
function enrichContinuousPrompt(prompt, node, graphRevision, executionBranch) {
|
|
1682
|
+
if (!node || !graphRevision || !executionBranch)
|
|
1683
|
+
return prompt;
|
|
1684
|
+
const writeSet = Array.isArray(node.writeSet) ? safeStringArray(node.writeSet) : [];
|
|
1685
|
+
const rootAuthority = parseBranchlessRootAuthority(node.childExecutionAuthority, {
|
|
1686
|
+
storyId: prompt.storyId,
|
|
1687
|
+
invocationPrompt: prompt.commandRef,
|
|
1688
|
+
authorizedWriteSet: writeSet,
|
|
1689
|
+
});
|
|
1690
|
+
return {
|
|
1691
|
+
...prompt,
|
|
1692
|
+
...(typeof node.dispatchId === 'string' ? { dispatchId: node.dispatchId } : {}),
|
|
1693
|
+
...(typeof node.idempotencyKey === 'string' ? { idempotencyKey: node.idempotencyKey } : {}),
|
|
1694
|
+
graphRevision,
|
|
1695
|
+
executionBranch,
|
|
1696
|
+
...(writeSet.length > 0 ? { writeSet } : {}),
|
|
1697
|
+
...(rootAuthority ? { rootAuthority } : {}),
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
function validateYoloopMetadataPublicSubset(metadata) {
|
|
1701
|
+
const redactions = [];
|
|
1702
|
+
if (metadata.nextStoryId !== undefined)
|
|
1703
|
+
validatePublicMetadataValue(metadata.nextStoryId, 'nextStoryId', redactions);
|
|
1704
|
+
if (metadata.yoloopExecutionPlan !== undefined)
|
|
1705
|
+
validatePublicMetadataValue(metadata.yoloopExecutionPlan, 'yoloopExecutionPlan', redactions);
|
|
1706
|
+
if (Array.isArray(metadata.skillDirectives)) {
|
|
1707
|
+
metadata.skillDirectives.forEach((directive, index) => {
|
|
1708
|
+
if (isRecord(directive) && directive.kind === 'task')
|
|
1709
|
+
validatePublicMetadataValue(directive, `skillDirectives.${index}`, redactions);
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
if (redactions.length > 0) {
|
|
1713
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop rejected unsafe public metadata before child delegation.', redactions);
|
|
1714
|
+
}
|
|
1715
|
+
return { ok: true };
|
|
1716
|
+
}
|
|
1717
|
+
function validatePlanTaskDirectives(value, planPrompts) {
|
|
1718
|
+
if (!Array.isArray(value))
|
|
1719
|
+
return { ok: true };
|
|
1720
|
+
const authorized = new Set(planPrompts.map((prompt) => prompt.commandRef));
|
|
1721
|
+
for (const [index, directive] of value.entries()) {
|
|
1722
|
+
if (!isRecord(directive) || directive.kind !== 'task')
|
|
1723
|
+
continue;
|
|
1724
|
+
const promptTemplate = typeof directive.promptTemplate === 'string' ? directive.promptTemplate : undefined;
|
|
1725
|
+
const promptTemplates = safeStringArray(directive.promptTemplates);
|
|
1726
|
+
const redactedDuplicate = promptTemplate === '[REDACTED]'
|
|
1727
|
+
&& directive.subagentType === 'neocortex'
|
|
1728
|
+
&& typeof directive.reasonCode === 'string'
|
|
1729
|
+
&& directive.reasonCode.startsWith('directive-prompt-')
|
|
1730
|
+
&& promptTemplates.length === 0;
|
|
1731
|
+
if (redactedDuplicate)
|
|
1732
|
+
continue;
|
|
1733
|
+
const candidates = [...(promptTemplate ? [promptTemplate] : []), ...promptTemplates];
|
|
1734
|
+
if (directive.subagentType !== 'neocortex'
|
|
1735
|
+
|| candidates.length === 0
|
|
1736
|
+
|| candidates.some((candidate) => !authorized.has(candidate))) {
|
|
1737
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected a task directive that conflicted with the server-authorized execution plan.', [redaction(`skillDirectives.${index}`, 'prompt_body', 'operation-denied')]);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
return { ok: true };
|
|
1741
|
+
}
|
|
1742
|
+
function validatePublicMetadataValue(value, path, redactions) {
|
|
1743
|
+
if (typeof value === 'string') {
|
|
1744
|
+
if (/\.allowedDebt\.constraints\.\d+$/.test(path) && PUBLIC_CHILD_DEBT_CONSTRAINTS.has(value))
|
|
1745
|
+
return;
|
|
1746
|
+
if (UNSAFE_VALUE_PATTERN.test(value) || /\0/.test(value))
|
|
1747
|
+
redactions.push(redaction(path, unsafeKeyReason(path), 'value-denied'));
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
if (value === undefined || value === null || typeof value === 'number' || typeof value === 'boolean')
|
|
1751
|
+
return;
|
|
1752
|
+
if (Array.isArray(value)) {
|
|
1753
|
+
value.forEach((item, index) => validatePublicMetadataValue(item, `${path}.${index}`, redactions));
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
if (isRecord(value)) {
|
|
1757
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1758
|
+
const childPath = `${path}.${key}`;
|
|
1759
|
+
const promptTemplateKey = key === 'promptTemplate'
|
|
1760
|
+
|| key === 'promptTemplates'
|
|
1761
|
+
|| key === 'invocationPrompt'
|
|
1762
|
+
|| key === 'childPromptKind'
|
|
1763
|
+
|| key === 'allowMetaTaskPromptInChild'
|
|
1764
|
+
|| key === 'authorization';
|
|
1765
|
+
if (!promptTemplateKey && UNSAFE_KEY_PATTERN.test(key))
|
|
1766
|
+
redactions.push(redaction(childPath, unsafeKeyReason(key), 'key-denied'));
|
|
1767
|
+
validatePublicMetadataValue(child, childPath, redactions);
|
|
1768
|
+
}
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
redactions.push(redaction(path, 'unsafe_key', 'value-denied'));
|
|
1772
|
+
}
|
|
1773
|
+
function validateExecutionPlan(plan) {
|
|
1774
|
+
const mode = typeof plan.mode === 'string' ? plan.mode : '';
|
|
1775
|
+
const isolationPolicy = isRecord(plan.isolationPolicy) ? plan.isolationPolicy : undefined;
|
|
1776
|
+
const storyIds = safeStringArray(plan.storyIds);
|
|
1777
|
+
if (plan.authorization !== 'server_authorized') {
|
|
1778
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop execution plan was not server-authorized.', []);
|
|
1779
|
+
}
|
|
1780
|
+
if (plan.executor !== 'root_agent' && plan.executor !== 'root_agent_with_child_tasks') {
|
|
1781
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop execution plan executor was not root-owned.', []);
|
|
1782
|
+
}
|
|
1783
|
+
if (mode !== 'serial' && mode !== 'parallel_wave' && mode !== 'continuous_dag' && mode !== 'manual') {
|
|
1784
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop execution plan mode was invalid.', []);
|
|
1785
|
+
}
|
|
1786
|
+
const continuousMode = plan.version === 3 && mode === 'continuous_dag';
|
|
1787
|
+
if (!Array.isArray(plan.storyIds) ||
|
|
1788
|
+
(!continuousMode && storyIds.length === 0) ||
|
|
1789
|
+
storyIds.length !== plan.storyIds.length ||
|
|
1790
|
+
storyIds.some((storyId) => !isStoryId(storyId)) ||
|
|
1791
|
+
new Set(storyIds).size !== storyIds.length) {
|
|
1792
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop execution plan story selection was invalid or ambiguous.', []);
|
|
1793
|
+
}
|
|
1794
|
+
if (!isolationPolicy ||
|
|
1795
|
+
isolationPolicy.rootOwnsLoop !== true ||
|
|
1796
|
+
(isolationPolicy.childPromptKind === 'per_story_branchless_preparation_only' && !continuousMode) ||
|
|
1797
|
+
(Object.prototype.hasOwnProperty.call(isolationPolicy, 'childExecutionBoundary') && !continuousMode) ||
|
|
1798
|
+
(isolationPolicy.childPromptKind !== 'per_story_yolo_only' &&
|
|
1799
|
+
isolationPolicy.childPromptKind !== 'per_story_branchless_preparation_only' &&
|
|
1800
|
+
!(isolationPolicy.childPromptKind === 'none' &&
|
|
1801
|
+
((mode === 'serial' && isolationPolicy.childTaskDelegation === 'not_required') ||
|
|
1802
|
+
(mode === 'parallel_wave' && plan.executor === 'root_agent' && isolationPolicy.childTaskDelegation === 'forbidden')))) ||
|
|
1803
|
+
isolationPolicy.allowOuterYoloopInChild !== false ||
|
|
1804
|
+
isolationPolicy.allowMetaTaskPromptInChild !== false) {
|
|
1805
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected an execution plan that did not preserve root-owned child isolation.', [redaction('isolationPolicy', 'prompt_body', 'value-denied')]);
|
|
1806
|
+
}
|
|
1807
|
+
const skippedStories = Array.isArray(plan.skippedStories) ? plan.skippedStories : [];
|
|
1808
|
+
for (const skipped of skippedStories) {
|
|
1809
|
+
if (!isRecord(skipped) || typeof skipped.storyId !== 'string' || !isStoryId(skipped.storyId) || typeof skipped.reason !== 'string') {
|
|
1810
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop skipped-story evidence was malformed.', []);
|
|
1811
|
+
}
|
|
1812
|
+
if (storyIds.includes(skipped.storyId)) {
|
|
1813
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop selected a story that server evidence marked skipped.', []);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
if (continuousMode) {
|
|
1817
|
+
const scheduler = isRecord(plan.scheduler) ? plan.scheduler : undefined;
|
|
1818
|
+
const nodes = Array.isArray(scheduler?.nodes) ? scheduler.nodes.filter(isRecord) : [];
|
|
1819
|
+
const schedulerReady = safeStringArray(scheduler?.readyStoryIds);
|
|
1820
|
+
const schedulerRunning = safeStringArray(scheduler?.runningStoryIds);
|
|
1821
|
+
const promptTemplates = safeStringArray(plan.promptTemplates);
|
|
1822
|
+
const dependencyBasis = isRecord(plan.dependencyBasis) ? plan.dependencyBasis : undefined;
|
|
1823
|
+
const readinessBasis = isRecord(plan.readinessBasis) ? plan.readinessBasis : undefined;
|
|
1824
|
+
const graphRevision = typeof scheduler?.graphRevision === 'string' ? scheduler.graphRevision : '';
|
|
1825
|
+
const executionBranch = typeof plan.executionBranch === 'string' ? plan.executionBranch : '';
|
|
1826
|
+
const nodeIds = nodes.map((node) => typeof node.storyId === 'string' ? node.storyId : '');
|
|
1827
|
+
const unsafeNode = nodes.find((node) => {
|
|
1828
|
+
const storyId = typeof node.storyId === 'string' ? node.storyId : '';
|
|
1829
|
+
const prompt = typeof node.promptTemplate === 'string' ? node.promptTemplate : '';
|
|
1830
|
+
const writeSet = Array.isArray(node.writeSet) ? safeStringArray(node.writeSet) : [];
|
|
1831
|
+
return !isStoryId(storyId) || prompt !== `*yolo @docs/stories/${storyId}.story.md branchless-preparation`
|
|
1832
|
+
|| typeof node.dispatchId !== 'string' || !/^[a-f0-9]{64}$/.test(node.dispatchId)
|
|
1833
|
+
|| typeof node.idempotencyKey !== 'string' || !/^[a-f0-9]{64}$/.test(node.idempotencyKey)
|
|
1834
|
+
|| node.dependencyBasis !== 'verified'
|
|
1835
|
+
|| !Array.isArray(node.dependencies) || safeStringArray(node.dependencies).length !== node.dependencies.length
|
|
1836
|
+
|| !Array.isArray(node.writeSet) || writeSet.length === 0 || writeSet.length !== node.writeSet.length
|
|
1837
|
+
|| !parseBranchlessRootAuthority(node.childExecutionAuthority, {
|
|
1838
|
+
storyId, invocationPrompt: prompt, authorizedWriteSet: writeSet,
|
|
1839
|
+
});
|
|
1840
|
+
});
|
|
1841
|
+
if (plan.executor !== 'root_agent_with_child_tasks'
|
|
1842
|
+
|| plan.legacyConcurrencyIgnored !== true
|
|
1843
|
+
|| Object.prototype.hasOwnProperty.call(plan, 'maxConcurrency')
|
|
1844
|
+
|| Object.prototype.hasOwnProperty.call(plan, 'maxParallel')
|
|
1845
|
+
|| isolationPolicy?.rootOwnedScheduling !== 'continuous_dag_allowed'
|
|
1846
|
+
|| isolationPolicy?.childPromptKind !== 'per_story_branchless_preparation_only'
|
|
1847
|
+
|| isolationPolicy?.childExecutionBoundary !== 'filesystem_only'
|
|
1848
|
+
|| isolationPolicy?.childTaskDelegation !== 'allowed'
|
|
1849
|
+
|| isolationPolicy?.maxDepth !== 1
|
|
1850
|
+
|| isolationPolicy?.childWorkspace !== 'branchless_sandbox'
|
|
1851
|
+
|| isolationPolicy?.epicCheckout !== 'current_checkout'
|
|
1852
|
+
|| isolationPolicy?.workspaceLifecycle !== 'user_managed'
|
|
1853
|
+
|| isolationPolicy?.integrationOwner !== 'root_agent'
|
|
1854
|
+
|| !scheduler
|
|
1855
|
+
|| scheduler.schemaVersion !== 1
|
|
1856
|
+
|| scheduler.strategy !== 'continuous-event-driven-dag'
|
|
1857
|
+
|| scheduler.logicalConcurrency !== 'ready-set'
|
|
1858
|
+
|| scheduler.capacityPolicy !== 'backpressure-only'
|
|
1859
|
+
|| scheduler.rootOwnsLoop !== true
|
|
1860
|
+
|| scheduler.maxDepth !== 1
|
|
1861
|
+
|| !/^[a-f0-9]{64}$/.test(graphRevision)
|
|
1862
|
+
|| !isSafeExecutionBranch(executionBranch)
|
|
1863
|
+
|| new Set(nodeIds).size !== nodeIds.length
|
|
1864
|
+
|| nodeIds.some((storyId) => !isStoryId(storyId))
|
|
1865
|
+
|| schedulerReady.some((storyId) => !nodeIds.includes(storyId))
|
|
1866
|
+
|| schedulerRunning.some((storyId) => !nodeIds.includes(storyId) || schedulerReady.includes(storyId))
|
|
1867
|
+
|| !sameStringSet(storyIds, schedulerReady)
|
|
1868
|
+
|| promptTemplates.length !== storyIds.length
|
|
1869
|
+
|| promptTemplates.some((prompt, index) => prompt !== `*yolo @docs/stories/${storyIds[index]}.story.md branchless-preparation`)
|
|
1870
|
+
|| !dependencyBasis || dependencyBasis.source !== 'state_snapshot'
|
|
1871
|
+
|| !readinessBasis || readinessBasis.source !== 'ready_wave'
|
|
1872
|
+
|| !sameStringSet(safeStringArray(readinessBasis.selectedStoryIds), storyIds)
|
|
1873
|
+
|| unsafeNode) {
|
|
1874
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop rejected an invalid continuous DAG execution plan.', []);
|
|
1875
|
+
}
|
|
1876
|
+
return {
|
|
1877
|
+
ok: true,
|
|
1878
|
+
mode: 'continuous_dag',
|
|
1879
|
+
serverMaxConcurrency: storyIds.length,
|
|
1880
|
+
parallelAuthorized: true,
|
|
1881
|
+
continuous: true,
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
if (plan.version === 2) {
|
|
1885
|
+
const phased = validatePhasedYoloopPlan(plan);
|
|
1886
|
+
if (!phased.ok) {
|
|
1887
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop rejected an unsafe phased execution plan.', []);
|
|
1888
|
+
}
|
|
1889
|
+
// A child *yolo performs the complete story lifecycle and may write. Until
|
|
1890
|
+
// preparation-only child directives exist, full child execution is pinned
|
|
1891
|
+
// to the P207 single writer even when preparation phases allow parallelism.
|
|
1892
|
+
return { ok: true, mode: 'parallel_wave', serverMaxConcurrency: phased.childExecutionConcurrency, parallelAuthorized: false, continuous: false };
|
|
1893
|
+
}
|
|
1894
|
+
if (mode !== 'parallel_wave') {
|
|
1895
|
+
return {
|
|
1896
|
+
ok: true,
|
|
1897
|
+
mode: mode,
|
|
1898
|
+
serverMaxConcurrency: 1,
|
|
1899
|
+
parallelAuthorized: false,
|
|
1900
|
+
continuous: false,
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
const maxConcurrency = plan.maxConcurrency;
|
|
1904
|
+
const maxParallel = plan.maxParallel;
|
|
1905
|
+
const dependencyBasis = isRecord(plan.dependencyBasis) ? plan.dependencyBasis : undefined;
|
|
1906
|
+
const readinessBasis = isRecord(plan.readinessBasis) ? plan.readinessBasis : undefined;
|
|
1907
|
+
const parallelWaves = Array.isArray(plan.parallelWaves) ? plan.parallelWaves : [];
|
|
1908
|
+
const wave = parallelWaves.length === 1 && isRecord(parallelWaves[0]) ? parallelWaves[0] : undefined;
|
|
1909
|
+
if (isolationPolicy.rootOwnedScheduling !== 'parallel_wave_allowed' ||
|
|
1910
|
+
!Number.isInteger(maxConcurrency) || typeof maxConcurrency !== 'number' || maxConcurrency < 2 || maxConcurrency > storyIds.length ||
|
|
1911
|
+
!Number.isInteger(maxParallel) || maxParallel !== maxConcurrency ||
|
|
1912
|
+
!dependencyBasis || dependencyBasis.source !== 'state_snapshot' || !sameStringSet(safeStringArray(dependencyBasis.storyIds), storyIds) ||
|
|
1913
|
+
!readinessBasis || readinessBasis.source !== 'ready_wave' || !sameStringSet(safeStringArray(readinessBasis.selectedStoryIds), storyIds) ||
|
|
1914
|
+
!wave || !sameStringSet(safeStringArray(wave.storyIds), storyIds) || wave.maxConcurrency !== maxConcurrency) {
|
|
1915
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop parallel plan lacked consistent bounded authorization and readiness evidence.', []);
|
|
1916
|
+
}
|
|
1917
|
+
const wavePrompts = safeStringArray(wave.promptTemplates);
|
|
1918
|
+
if (wavePrompts.length !== storyIds.length || new Set(wavePrompts).size !== wavePrompts.length) {
|
|
1919
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop parallel wave prompts were incomplete or ambiguous.', []);
|
|
1920
|
+
}
|
|
1921
|
+
const proofs = Array.isArray(wave.proofSummaries)
|
|
1922
|
+
? wave.proofSummaries
|
|
1923
|
+
: Array.isArray(plan.proofSummaries) ? plan.proofSummaries : [];
|
|
1924
|
+
const waveProof = proofs.find((proof) => isRecord(proof) && sameStringSet(safeStringArray(proof.storyIds), storyIds));
|
|
1925
|
+
if (!isRecord(waveProof) ||
|
|
1926
|
+
waveProof.dependencyClosure !== 'resolved' ||
|
|
1927
|
+
waveProof.contractOwnership !== 'non_overlapping' ||
|
|
1928
|
+
waveProof.validationSurfaces !== 'compatible') {
|
|
1929
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop parallel plan lacked resolved independence proof.', []);
|
|
1930
|
+
}
|
|
1931
|
+
const unsafeSkipped = skippedStories.find((skipped) => isRecord(skipped) && typeof skipped.reason === 'string' && /(cycle|unresolved|ambiguous)/i.test(skipped.reason));
|
|
1932
|
+
if (unsafeSkipped) {
|
|
1933
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop parallel plan contained unresolved dependency evidence.', []);
|
|
1934
|
+
}
|
|
1935
|
+
return {
|
|
1936
|
+
ok: true,
|
|
1937
|
+
mode: 'parallel_wave',
|
|
1938
|
+
serverMaxConcurrency: maxConcurrency,
|
|
1939
|
+
parallelAuthorized: true,
|
|
1940
|
+
continuous: false,
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function parseBranchlessRootAuthority(value, expected) {
|
|
1944
|
+
if (!isRecord(value) || !isRecord(value.artifact))
|
|
1945
|
+
return undefined;
|
|
1946
|
+
const artifact = value.artifact;
|
|
1947
|
+
const writeSet = Array.isArray(value.authorizedWriteSet) ? safeStringArray(value.authorizedWriteSet) : [];
|
|
1948
|
+
if (Object.keys(value).sort().join('\0') !== [
|
|
1949
|
+
'artifact', 'authorizedWriteSet', 'effectScope', 'invocationPrompt', 'kind',
|
|
1950
|
+
'lifecycleAuthority', 'publicSafe', 'schemaVersion', 'storyId',
|
|
1951
|
+
].sort().join('\0')
|
|
1952
|
+
|| Object.keys(artifact).sort().join('\0') !== [
|
|
1953
|
+
'content', 'contentBinding', 'kind', 'path', 'schemaVersion',
|
|
1954
|
+
].sort().join('\0')
|
|
1955
|
+
|| value.schemaVersion !== 1 || value.kind !== 'branchless_story_preparation'
|
|
1956
|
+
|| value.storyId !== expected.storyId || value.invocationPrompt !== expected.invocationPrompt
|
|
1957
|
+
|| !Array.isArray(value.authorizedWriteSet)
|
|
1958
|
+
|| writeSet.length !== value.authorizedWriteSet.length
|
|
1959
|
+
|| writeSet.some((ref) => normalizeContinuousWriteRef(ref) !== ref)
|
|
1960
|
+
|| !sameOrderedStrings(writeSet, expected.authorizedWriteSet)
|
|
1961
|
+
|| value.effectScope !== 'filesystem_only' || value.lifecycleAuthority !== 'root_only'
|
|
1962
|
+
|| value.publicSafe !== true || artifact.schemaVersion !== 1
|
|
1963
|
+
|| artifact.kind !== 'branchless_root_bound_artifact'
|
|
1964
|
+
|| typeof artifact.path !== 'string' || !writeSet.includes(artifact.path)
|
|
1965
|
+
|| typeof artifact.content !== 'string' || artifact.content.length === 0 || artifact.content.length > 4096
|
|
1966
|
+
|| typeof artifact.contentBinding !== 'string' || !/^[a-f0-9]{64}$/.test(artifact.contentBinding)) {
|
|
1967
|
+
return undefined;
|
|
1968
|
+
}
|
|
1969
|
+
const expectedBinding = createHash('sha256').update([
|
|
1970
|
+
'branchless-root-bound-artifact-v1', expected.storyId, expected.invocationPrompt,
|
|
1971
|
+
...writeSet, 'filesystem_only', 'root_only', artifact.path, artifact.content,
|
|
1972
|
+
].join('\0')).digest('hex');
|
|
1973
|
+
if (artifact.contentBinding !== expectedBinding)
|
|
1974
|
+
return undefined;
|
|
1975
|
+
return value;
|
|
1976
|
+
}
|
|
1977
|
+
function sameOrderedStrings(left, right) {
|
|
1978
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1979
|
+
}
|
|
1980
|
+
function extractPlanPrompts(plan) {
|
|
1981
|
+
const prompts = [];
|
|
1982
|
+
prompts.push(...safeStringArray(plan.promptTemplates));
|
|
1983
|
+
for (const step of Array.isArray(plan.serialSteps) ? plan.serialSteps : []) {
|
|
1984
|
+
if (isRecord(step) && typeof step.promptTemplate === 'string')
|
|
1985
|
+
prompts.push(step.promptTemplate);
|
|
1986
|
+
}
|
|
1987
|
+
for (const wave of Array.isArray(plan.parallelWaves) ? plan.parallelWaves : []) {
|
|
1988
|
+
if (isRecord(wave))
|
|
1989
|
+
prompts.push(...safeStringArray(wave.promptTemplates));
|
|
1990
|
+
}
|
|
1991
|
+
return prompts;
|
|
1992
|
+
}
|
|
1993
|
+
function normalizeChildPrompt(prompt, epicId, source) {
|
|
1994
|
+
if (hasDelegatedPromptIsolationViolation(prompt)) {
|
|
1995
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected an unsafe child prompt shape.', [redaction('promptTemplate', 'prompt_body', hashPublicValue(prompt))]);
|
|
1996
|
+
}
|
|
1997
|
+
if (!CHILD_PROMPT_PATTERN.test(prompt)) {
|
|
1998
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, 'Headless yoloop child prompt did not match the canonical per-story yolo shape.', [redaction('promptTemplate', 'prompt_body', hashPublicValue(prompt))]);
|
|
1999
|
+
}
|
|
2000
|
+
const preparationOnly = BRANCHLESS_PREPARATION_PROMPT_PATTERN.test(prompt);
|
|
2001
|
+
const targetRef = prompt.replace(/^\*yolo @/, '').replace(/ branchless-preparation$/, '');
|
|
2002
|
+
const storyId = storyIdFromTargetRef(targetRef);
|
|
2003
|
+
if (!CHILD_STORY_TARGET_PATTERN.test(targetRef) || !isStoryId(storyId) || !storyId.startsWith(`${epicId}.`)) {
|
|
2004
|
+
return rejectPromptExtraction(HEADLESS_REASON_CODES.YOLOOP_ISOLATION_VIOLATION, 'Headless yoloop rejected a child prompt outside the targeted public epic.', [redaction('storyId', 'prompt_body', hashPublicValue(storyId))]);
|
|
2005
|
+
}
|
|
2006
|
+
return {
|
|
2007
|
+
ok: true,
|
|
2008
|
+
prompt: { storyId, targetRef, commandRef: prompt, source, ...(preparationOnly ? { preparationOnly: true } : {}) },
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function extractEpicTerminalGitDirective(metadata) {
|
|
2012
|
+
const value = metadata?.epicTerminalGitDirective;
|
|
2013
|
+
if (!isRecord(value)
|
|
2014
|
+
|| value.schemaVersion !== 1
|
|
2015
|
+
|| value.kind !== 'epic_terminal_git'
|
|
2016
|
+
|| typeof value.epicId !== 'string'
|
|
2017
|
+
|| typeof value.executionBranch !== 'string'
|
|
2018
|
+
|| typeof value.originBranch !== 'string'
|
|
2019
|
+
|| typeof value.expectedExecutionHeadSha !== 'string'
|
|
2020
|
+
|| !Array.isArray(value.storyCheckpointIds)
|
|
2021
|
+
|| !isRecord(value.localGates)
|
|
2022
|
+
|| value.localGates.passed !== true
|
|
2023
|
+
|| !Array.isArray(value.localGates.receiptRefs)
|
|
2024
|
+
|| !isRecord(value.reconcile)
|
|
2025
|
+
|| typeof value.idempotencyKey !== 'string'
|
|
2026
|
+
|| value.publicSafe !== true)
|
|
2027
|
+
return undefined;
|
|
2028
|
+
return value;
|
|
2029
|
+
}
|
|
2030
|
+
function extractEpicGitReconciliationDirective(metadata) {
|
|
2031
|
+
const value = metadata?.epicGitReconciliationDirective;
|
|
2032
|
+
if (!isRecord(value)
|
|
2033
|
+
|| value.schemaVersion !== 1
|
|
2034
|
+
|| value.kind !== 'epic_git_reconciliation'
|
|
2035
|
+
|| typeof value.epicId !== 'string'
|
|
2036
|
+
|| typeof value.executionBranch !== 'string'
|
|
2037
|
+
|| typeof value.originBranch !== 'string'
|
|
2038
|
+
|| !['start', 'resume', 'audit'].includes(String(value.lifecycleBoundary))
|
|
2039
|
+
|| typeof value.dryRun !== 'boolean'
|
|
2040
|
+
|| typeof value.idempotencyKey !== 'string'
|
|
2041
|
+
|| value.publicSafe !== true)
|
|
2042
|
+
return undefined;
|
|
2043
|
+
return value;
|
|
2044
|
+
}
|
|
2045
|
+
function classifyYoloopTerminalMetadata(metadata) {
|
|
2046
|
+
if (!metadata || !isRecord(metadata)) {
|
|
2047
|
+
return {
|
|
2048
|
+
ok: false,
|
|
2049
|
+
reasonCode: HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID,
|
|
2050
|
+
publicSummary: 'Headless yoloop root invocation did not return public metadata.',
|
|
2051
|
+
redactionSummary: [],
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
const decision = isRecord(metadata.yoloopContinuationDecision) ? metadata.yoloopContinuationDecision : undefined;
|
|
2055
|
+
const outcome = typeof decision?.outcome === 'string' ? decision.outcome : undefined;
|
|
2056
|
+
const complete = outcome === 'queue_exhausted' || metadata.yoloopComplete === true;
|
|
2057
|
+
const aborted = metadata.yoloopAborted === true;
|
|
2058
|
+
const releaseBlocked = parseReleaseBlockedResult(metadata.releaseBlocked);
|
|
2059
|
+
const replay = outcome === 'selected' && decision?.replay === true;
|
|
2060
|
+
if (outcome && !['selected', 'queue_exhausted', 'blocked', 'cycle', 'invalid', 'no_progress'].includes(outcome)) {
|
|
2061
|
+
return { ok: false, reasonCode: HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, publicSummary: 'Headless yoloop rejected an unknown continuation outcome.', redactionSummary: [] };
|
|
2062
|
+
}
|
|
2063
|
+
if (outcome === 'queue_exhausted' && decision?.terminalSuccess !== true) {
|
|
2064
|
+
return { ok: false, reasonCode: HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID, publicSummary: 'Headless yoloop rejected inconsistent queue exhaustion metadata.', redactionSummary: [] };
|
|
2065
|
+
}
|
|
2066
|
+
if (complete && aborted) {
|
|
2067
|
+
return {
|
|
2068
|
+
ok: false,
|
|
2069
|
+
reasonCode: HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID,
|
|
2070
|
+
publicSummary: 'Headless yoloop metadata reported mutually exclusive terminal states.',
|
|
2071
|
+
redactionSummary: [redaction('yoloopTerminal', 'unsafe_key', 'value-denied')],
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
const releaseBlockedSignalled = metadata.yoloopReleaseBlocked === true
|
|
2075
|
+
|| metadata.terminalClass === 'release-blocked'
|
|
2076
|
+
|| metadata.releaseBlocked !== undefined;
|
|
2077
|
+
if (releaseBlockedSignalled && (!releaseBlocked || metadata.yoloopReleaseBlocked !== true
|
|
2078
|
+
|| metadata.terminalClass !== 'release-blocked' || complete || aborted || outcome !== undefined)) {
|
|
2079
|
+
return {
|
|
2080
|
+
ok: false,
|
|
2081
|
+
reasonCode: HEADLESS_REASON_CODES.YOLOOP_METADATA_INVALID,
|
|
2082
|
+
publicSummary: 'Headless yoloop rejected inconsistent release-blocked terminal metadata.',
|
|
2083
|
+
redactionSummary: [],
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
return { ok: true, complete, aborted, ...(releaseBlocked ? { releaseBlocked } : {}), outcome, replay };
|
|
2087
|
+
}
|
|
2088
|
+
function extractResumableExhaustionBoundary(metadata) {
|
|
2089
|
+
if (!metadata)
|
|
2090
|
+
return undefined;
|
|
2091
|
+
const reason = metadata.abortReason;
|
|
2092
|
+
if (reason !== 'YOLOOP_PER_RUN_CAP_REACHED' && reason !== 'YOLOOP_SESSION_TIMEOUT')
|
|
2093
|
+
return undefined;
|
|
2094
|
+
const boundary = metadata.yoloopResumableBoundary;
|
|
2095
|
+
const boundedId = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2096
|
+
if (!isRecord(boundary)
|
|
2097
|
+
|| boundary.schemaVersion !== 'YoloopResumableBoundary/1.0'
|
|
2098
|
+
|| boundary.resumable !== true
|
|
2099
|
+
|| boundary.reason !== reason
|
|
2100
|
+
|| boundary.publicSafe !== true
|
|
2101
|
+
|| typeof boundary.coolDownUntil !== 'string'
|
|
2102
|
+
|| !Number.isFinite(Date.parse(boundary.coolDownUntil))
|
|
2103
|
+
|| !Number.isSafeInteger(boundary.retryAfterMs)
|
|
2104
|
+
|| boundary.retryAfterMs < 0
|
|
2105
|
+
|| typeof boundary.operationId !== 'string'
|
|
2106
|
+
|| !boundedId.test(boundary.operationId)
|
|
2107
|
+
|| !isRecord(boundary.receipt)
|
|
2108
|
+
|| boundary.receipt.schemaVersion !== 'YoloopSessionRolloverReceipt/1.0'
|
|
2109
|
+
|| typeof boundary.receipt.runId !== 'string'
|
|
2110
|
+
|| !boundedId.test(boundary.receipt.runId)
|
|
2111
|
+
|| boundary.receipt.operationId !== boundary.operationId
|
|
2112
|
+
|| typeof boundary.receipt.idempotencyKey !== 'string'
|
|
2113
|
+
|| !boundedId.test(boundary.receipt.idempotencyKey)
|
|
2114
|
+
|| typeof boundary.receipt.payloadHash !== 'string'
|
|
2115
|
+
|| !/^[a-f0-9]{64}$/.test(boundary.receipt.payloadHash)
|
|
2116
|
+
|| boundary.receipt.coolDownUntil !== boundary.coolDownUntil
|
|
2117
|
+
|| !Number.isSafeInteger(boundary.receipt.revision)
|
|
2118
|
+
|| boundary.receipt.revision < 0
|
|
2119
|
+
|| (boundary.receipt.snapshotHash !== null
|
|
2120
|
+
&& (typeof boundary.receipt.snapshotHash !== 'string'
|
|
2121
|
+
|| !/^[a-f0-9]{64}$/.test(boundary.receipt.snapshotHash)))
|
|
2122
|
+
|| boundary.receipt.status !== 'exhausted'
|
|
2123
|
+
|| boundary.receipt.publicSafe !== true)
|
|
2124
|
+
return undefined;
|
|
2125
|
+
return { coolDownUntil: boundary.coolDownUntil, reason };
|
|
2126
|
+
}
|
|
2127
|
+
function buildChildRunYoloDirective(input) {
|
|
2128
|
+
const stableBasis = `${input.rootAttempt.execution_id}:${input.child.graphRevision ?? 'legacy'}:${input.child.storyId}`;
|
|
2129
|
+
const operationId = input.child.dispatchId ?? headlessExecutionEntityId('child-op', `${stableBasis}:${input.iteration}`);
|
|
2130
|
+
const idempotencyKey = input.child.idempotencyKey ?? headlessExecutionEntityId('child-idem', `${stableBasis}:${input.iteration}`);
|
|
2131
|
+
return {
|
|
2132
|
+
schemaVersion: 1,
|
|
2133
|
+
contractVersion: 'continuity-v1',
|
|
2134
|
+
kind: 'run_yolo',
|
|
2135
|
+
commandClass: input.rootAttempt.command_class === 'goal-tick' ? 'goal-tick' : 'loop-tick',
|
|
2136
|
+
jobId: input.rootAttempt.job_id,
|
|
2137
|
+
operationId,
|
|
2138
|
+
idempotencyKey,
|
|
2139
|
+
targetKind: 'story',
|
|
2140
|
+
targetRef: input.child.targetRef,
|
|
2141
|
+
commandRef: input.child.commandRef,
|
|
2142
|
+
...(input.child.preparationOnly ? { executionMode: 'branchless_child_preparation' } : {}),
|
|
2143
|
+
storyId: input.child.storyId,
|
|
2144
|
+
runtime: {
|
|
2145
|
+
runtimeId: input.runtimeId,
|
|
2146
|
+
...(input.modelId ? { modelId: input.modelId } : {}),
|
|
2147
|
+
source: input.runtimeSource ?? 'explicit',
|
|
2148
|
+
reasonCode: input.runtimeSource === 'fallback' ? HEADLESS_REASON_CODES.RUNTIME_FALLBACK : HEADLESS_REASON_CODES.RUNTIME_EXPLICIT,
|
|
2149
|
+
publicSafe: true,
|
|
2150
|
+
},
|
|
2151
|
+
publicSummary: `Root-owned yoloop delegated public story ${input.child.storyId}.`,
|
|
2152
|
+
reasonCode: HEADLESS_REASON_CODES.OK,
|
|
2153
|
+
metadata: {
|
|
2154
|
+
governanceSource: 'delegated-subagent',
|
|
2155
|
+
parentAttemptId: input.rootAttempt.attempt_id,
|
|
2156
|
+
parentExecutionId: input.rootAttempt.execution_id,
|
|
2157
|
+
iteration: input.iteration,
|
|
2158
|
+
...(input.child.dispatchId ? { dispatchId: input.child.dispatchId } : {}),
|
|
2159
|
+
...(input.child.graphRevision ? { graphRevision: input.child.graphRevision } : {}),
|
|
2160
|
+
...(input.child.writeSet ? { writeSet: input.child.writeSet } : {}),
|
|
2161
|
+
...(input.child.executionBranch ? { executionBranch: input.child.executionBranch } : {}),
|
|
2162
|
+
...(input.child.graphRevision ? {
|
|
2163
|
+
childWorkspace: 'branchless_sandbox',
|
|
2164
|
+
epicCheckout: 'current_checkout',
|
|
2165
|
+
workspaceLifecycle: 'user_managed',
|
|
2166
|
+
integrationOwner: 'root_agent',
|
|
2167
|
+
maxDepth: 1,
|
|
2168
|
+
} : {}),
|
|
2169
|
+
publicSafe: true,
|
|
2170
|
+
},
|
|
2171
|
+
attempts: [],
|
|
2172
|
+
redactionSummary: [redaction('prompt', 'prompt_body', hashPublicValue(input.child.commandRef))],
|
|
2173
|
+
publicSafe: true,
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
function persistYoloopAttemptOutcome(store, attempt, outcome) {
|
|
2177
|
+
const hashes = sanitizePublicRecord(outcome.hashes);
|
|
2178
|
+
const workerPoolSummary = outcome.workerPoolSummary ?? defaultWorkerPoolSummary();
|
|
2179
|
+
const counts = sanitizePublicRecord({
|
|
2180
|
+
iterations: outcome.counts.iterations,
|
|
2181
|
+
childCount: outcome.counts.childCount,
|
|
2182
|
+
succeededChildCount: outcome.counts.succeededChildCount,
|
|
2183
|
+
failedChildCount: outcome.counts.failedChildCount,
|
|
2184
|
+
skippedChildCount: outcome.counts.skippedChildCount,
|
|
2185
|
+
metadataCompleteCount: outcome.counts.metadataCompleteCount,
|
|
2186
|
+
metadataAbortedCount: outcome.counts.metadataAbortedCount,
|
|
2187
|
+
serverMaxConcurrency: workerPoolSummary.serverMaxConcurrency,
|
|
2188
|
+
localCap: workerPoolSummary.localCap,
|
|
2189
|
+
queueCap: workerPoolSummary.queueCap,
|
|
2190
|
+
availableLeases: workerPoolSummary.availableLeases,
|
|
2191
|
+
effectiveConcurrency: workerPoolSummary.effectiveConcurrency,
|
|
2192
|
+
peakConcurrency: workerPoolSummary.peakConcurrency,
|
|
2193
|
+
pendingChildCount: workerPoolSummary.pendingChildCount,
|
|
2194
|
+
startedChildCount: workerPoolSummary.startedChildCount,
|
|
2195
|
+
completedChildCount: workerPoolSummary.completedChildCount,
|
|
2196
|
+
backpressurePauseCount: workerPoolSummary.backpressurePauseCount,
|
|
2197
|
+
});
|
|
2198
|
+
const outcomeMetadata = {
|
|
2199
|
+
schemaVersion: 1,
|
|
2200
|
+
contractVersion: 'continuity-v1',
|
|
2201
|
+
attemptId: attempt.attempt_id,
|
|
2202
|
+
executionId: attempt.execution_id,
|
|
2203
|
+
jobId: attempt.job_id,
|
|
2204
|
+
directiveHash: attempt.directive_hash,
|
|
2205
|
+
status: outcome.status,
|
|
2206
|
+
reasonCode: outcome.reasonCode,
|
|
2207
|
+
storyIds: outcome.childStoryIds.slice(0, 50),
|
|
2208
|
+
counts,
|
|
2209
|
+
hashes,
|
|
2210
|
+
...(outcome.reasonCode === AUTONOMOUS_GOVERNANCE_REASON_CODES.BYPASS_BLOCKED ? { governanceEvidence: AUTONOMOUS_GOVERNANCE_EVIDENCE_TAG } : {}),
|
|
2211
|
+
reasonCodes: unique([outcome.reasonCode, ...workerPoolSummary.reasonCodes, ...outcome.stateTransitions]),
|
|
2212
|
+
publicSummary: boundPublicSummary(outcome.publicSummary),
|
|
2213
|
+
...(outcome.terminalClass ? { terminalClass: outcome.terminalClass } : {}),
|
|
2214
|
+
...(outcome.releaseBlocked ? { releaseBlocked: outcome.releaseBlocked } : {}),
|
|
2215
|
+
};
|
|
2216
|
+
const governance = readAutonomousGovernanceEnvelopeFromMetadata(attempt.metadata_json, {
|
|
2217
|
+
autonomousKind: 'run_yoloop',
|
|
2218
|
+
jobId: attempt.job_id,
|
|
2219
|
+
targetKind: 'epic',
|
|
2220
|
+
targetRef: attempt.target_ref,
|
|
2221
|
+
});
|
|
2222
|
+
// Backoff reuses the same attempt row. Preserve only its already-validated,
|
|
2223
|
+
// public-safe authority envelope so the next tick cannot lose authorization
|
|
2224
|
+
// when completion metadata replaces the queued metadata.
|
|
2225
|
+
const metadata = governance.ok
|
|
2226
|
+
? autonomousGovernanceMetadata(governance.envelope, outcomeMetadata)
|
|
2227
|
+
: outcomeMetadata;
|
|
2228
|
+
store.completeHeadlessExecutionAttempt({
|
|
2229
|
+
attemptId: attempt.attempt_id,
|
|
2230
|
+
executionId: attempt.execution_id,
|
|
2231
|
+
jobId: attempt.job_id,
|
|
2232
|
+
leaseId: outcome.leaseId,
|
|
2233
|
+
runnerId: outcome.runnerId,
|
|
2234
|
+
status: outcome.status,
|
|
2235
|
+
completedAt: outcome.completedAt,
|
|
2236
|
+
durationMs: outcome.durationMs,
|
|
2237
|
+
reasonCode: outcome.reasonCode,
|
|
2238
|
+
publicSummary: boundPublicSummary(outcome.publicSummary),
|
|
2239
|
+
notBeforeAt: outcome.notBeforeAt,
|
|
2240
|
+
retryCount: outcome.retryCount,
|
|
2241
|
+
counts,
|
|
2242
|
+
hashes,
|
|
2243
|
+
redactionSummary: outcome.redactionSummary,
|
|
2244
|
+
relativePaths: unique([attempt.target_ref, ...outcome.relativePaths]),
|
|
2245
|
+
metadata,
|
|
2246
|
+
});
|
|
2247
|
+
store.recordHeadlessExecutionReceipt({
|
|
2248
|
+
receiptId: `receipt-${headlessExecutionEntityId('headless-yoloop', `${attempt.attempt_id}-${outcome.status}`)}`,
|
|
2249
|
+
attemptId: attempt.attempt_id,
|
|
2250
|
+
executionId: attempt.execution_id,
|
|
2251
|
+
jobId: attempt.job_id,
|
|
2252
|
+
status: outcome.receiptStatus,
|
|
2253
|
+
reasonCode: outcome.reasonCode,
|
|
2254
|
+
createdAt: outcome.completedAt,
|
|
2255
|
+
operationId: `${attempt.execution_id}:${outcome.status}:receipt`,
|
|
2256
|
+
idempotencyKey: `${attempt.directive_hash}:${outcome.status}:receipt`,
|
|
2257
|
+
counts,
|
|
2258
|
+
hashes,
|
|
2259
|
+
redactionSummary: outcome.redactionSummary,
|
|
2260
|
+
relativePaths: unique([attempt.target_ref, ...outcome.relativePaths]),
|
|
2261
|
+
metadata,
|
|
2262
|
+
});
|
|
2263
|
+
store.recordHeadlessExecutionSummary({
|
|
2264
|
+
eventId: `event-${headlessExecutionEntityId('headless-yoloop-summary', `${attempt.attempt_id}-${outcome.status}`)}`,
|
|
2265
|
+
attemptId: attempt.attempt_id,
|
|
2266
|
+
executionId: attempt.execution_id,
|
|
2267
|
+
jobId: attempt.job_id,
|
|
2268
|
+
status: outcome.status,
|
|
2269
|
+
runtimeId: outcome.runtimeId,
|
|
2270
|
+
modelId: outcome.modelId,
|
|
2271
|
+
publicSummary: boundPublicSummary(outcome.publicSummary),
|
|
2272
|
+
reasonCode: outcome.reasonCode,
|
|
2273
|
+
createdAt: outcome.completedAt,
|
|
2274
|
+
durationMs: outcome.durationMs,
|
|
2275
|
+
counts,
|
|
2276
|
+
hashes,
|
|
2277
|
+
redactionSummary: outcome.redactionSummary,
|
|
2278
|
+
relativePaths: unique([attempt.target_ref, ...outcome.relativePaths]),
|
|
2279
|
+
metadata,
|
|
2280
|
+
});
|
|
2281
|
+
return {
|
|
2282
|
+
attemptId: attempt.attempt_id,
|
|
2283
|
+
executionId: attempt.execution_id,
|
|
2284
|
+
jobId: attempt.job_id,
|
|
2285
|
+
status: outcome.status,
|
|
2286
|
+
outcome: outcome.receiptStatus,
|
|
2287
|
+
reasonCode: outcome.reasonCode,
|
|
2288
|
+
timestamp: outcome.completedAt,
|
|
2289
|
+
childStoryIds: outcome.childStoryIds,
|
|
2290
|
+
aggregateCounts: outcome.counts,
|
|
2291
|
+
workerPoolSummary,
|
|
2292
|
+
redactionSummary: outcome.redactionSummary,
|
|
2293
|
+
...(outcome.terminalClass ? { terminalClass: outcome.terminalClass } : {}),
|
|
2294
|
+
...(outcome.releaseBlocked ? { releaseBlocked: outcome.releaseBlocked } : {}),
|
|
2295
|
+
publicSafe: true,
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
function emptyYoloopReceipt(attempt, input) {
|
|
2299
|
+
return {
|
|
2300
|
+
attemptId: attempt.attempt_id,
|
|
2301
|
+
executionId: attempt.execution_id,
|
|
2302
|
+
jobId: attempt.job_id,
|
|
2303
|
+
status: input.status,
|
|
2304
|
+
outcome: input.receiptStatus,
|
|
2305
|
+
reasonCode: input.reasonCode,
|
|
2306
|
+
timestamp: input.timestamp,
|
|
2307
|
+
childStoryIds: [],
|
|
2308
|
+
aggregateCounts: emptyAggregateCounts(),
|
|
2309
|
+
workerPoolSummary: defaultWorkerPoolSummary(),
|
|
2310
|
+
redactionSummary: [],
|
|
2311
|
+
publicSafe: true,
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
function retryDecision(attempt, result, maxAttempts, backoffMs, nowIso) {
|
|
2315
|
+
if (!result.retryable)
|
|
2316
|
+
return { retry: false };
|
|
2317
|
+
if (RESUMABLE_YOLOOP_BOUNDARY_REASONS.has(result.reasonCode)) {
|
|
2318
|
+
return {
|
|
2319
|
+
retry: true,
|
|
2320
|
+
// A valid operational boundary is a resumable checkpoint, not a failed
|
|
2321
|
+
// attempt. Preserve the generic failure budget across any number of
|
|
2322
|
+
// server-authorized timeout/per-run successor boundaries.
|
|
2323
|
+
retryCount: attempt.retry_count,
|
|
2324
|
+
notBeforeAt: laterIsoTimestamp(addMilliseconds(nowIso, backoffMs), result.retryNotBeforeAt),
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
const retryCount = attempt.retry_count + 1;
|
|
2328
|
+
if (retryCount >= Math.max(1, attempt.max_attempts || maxAttempts))
|
|
2329
|
+
return { retry: false, retryCount };
|
|
2330
|
+
const multiplier = Math.min(8, 2 ** Math.max(0, attempt.retry_count));
|
|
2331
|
+
return {
|
|
2332
|
+
retry: true,
|
|
2333
|
+
retryCount,
|
|
2334
|
+
notBeforeAt: laterIsoTimestamp(addMilliseconds(nowIso, backoffMs * multiplier), result.retryNotBeforeAt),
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
function driverResult(input) {
|
|
2338
|
+
return {
|
|
2339
|
+
status: input.status,
|
|
2340
|
+
receiptStatus: input.receiptStatus,
|
|
2341
|
+
reasonCode: input.reasonCode,
|
|
2342
|
+
publicSummary: input.publicSummary,
|
|
2343
|
+
retryable: input.retryable,
|
|
2344
|
+
...(input.retryNotBeforeAt ? { retryNotBeforeAt: input.retryNotBeforeAt } : {}),
|
|
2345
|
+
durationMs: Math.max(0, Date.now() - input.startedAt),
|
|
2346
|
+
counts: input.counts,
|
|
2347
|
+
workerPoolSummary: input.workerPoolSummary ?? defaultWorkerPoolSummary(),
|
|
2348
|
+
hashes: input.hashes,
|
|
2349
|
+
redactionSummary: input.redactions.slice(0, 50),
|
|
2350
|
+
relativePaths: unique(input.relativePaths).slice(0, 50),
|
|
2351
|
+
childStoryIds: unique(input.childStoryIds).slice(0, 50),
|
|
2352
|
+
stateTransitions: unique(input.stateTransitions),
|
|
2353
|
+
...(input.terminalClass ? { terminalClass: input.terminalClass } : {}),
|
|
2354
|
+
...(input.releaseBlocked ? { releaseBlocked: input.releaseBlocked } : {}),
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
function validateDirectiveRuntime(value, redactions) {
|
|
2358
|
+
if (value === undefined)
|
|
2359
|
+
return undefined;
|
|
2360
|
+
if (!isRecord(value)) {
|
|
2361
|
+
redactions.push(redaction('runtime', 'unsafe_key', 'value-denied'));
|
|
2362
|
+
return undefined;
|
|
2363
|
+
}
|
|
2364
|
+
for (const key of Object.keys(value)) {
|
|
2365
|
+
if (!RUNTIME_ALLOWED_KEYS.has(key) || UNSAFE_KEY_PATTERN.test(key))
|
|
2366
|
+
redactions.push(redaction(`runtime.${key}`, unsafeKeyReason(key), 'key-denied'));
|
|
2367
|
+
}
|
|
2368
|
+
if (value.publicSafe !== true)
|
|
2369
|
+
redactions.push(redaction('runtime.publicSafe', 'unsafe_key', 'value-denied'));
|
|
2370
|
+
const runtimeId = stringField(value, 'runtimeId');
|
|
2371
|
+
const modelId = stringField(value, 'modelId');
|
|
2372
|
+
const source = stringField(value, 'source');
|
|
2373
|
+
if (runtimeId && (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,79}$/.test(runtimeId) || UNSAFE_VALUE_PATTERN.test(runtimeId) || SHELL_FRAGMENT_PATTERN.test(runtimeId))) {
|
|
2374
|
+
redactions.push(redaction('runtime.runtimeId', 'unsafe_key', 'value-denied'));
|
|
2375
|
+
}
|
|
2376
|
+
if (modelId && (!/^[A-Za-z0-9][A-Za-z0-9._:+/-]{0,119}$/.test(modelId) || UNSAFE_VALUE_PATTERN.test(modelId) || SHELL_FRAGMENT_PATTERN.test(modelId))) {
|
|
2377
|
+
redactions.push(redaction('runtime.modelId', 'unsafe_key', 'value-denied'));
|
|
2378
|
+
}
|
|
2379
|
+
if (source !== undefined && source !== 'explicit' && source !== 'origin' && source !== 'default' && source !== 'fallback' && source !== 'unspecified') {
|
|
2380
|
+
redactions.push(redaction('runtime.source', 'unsafe_key', 'value-denied'));
|
|
2381
|
+
}
|
|
2382
|
+
if (value.reasonCode !== undefined && (typeof value.reasonCode !== 'string' || UNSAFE_VALUE_PATTERN.test(value.reasonCode))) {
|
|
2383
|
+
redactions.push(redaction('runtime.reasonCode', 'unsafe_key', 'value-denied'));
|
|
2384
|
+
}
|
|
2385
|
+
if (value.publicSafe !== true)
|
|
2386
|
+
return undefined;
|
|
2387
|
+
return {
|
|
2388
|
+
...(runtimeId ? { runtimeId } : {}),
|
|
2389
|
+
...(modelId ? { modelId } : {}),
|
|
2390
|
+
...(source === 'explicit' || source === 'origin' || source === 'default' || source === 'fallback' || source === 'unspecified' ? { source } : {}),
|
|
2391
|
+
...(typeof value.reasonCode === 'string' ? { reasonCode: value.reasonCode } : {}),
|
|
2392
|
+
publicSafe: true,
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
function extractExecutionDirectiveCandidates(metadata) {
|
|
2396
|
+
const records = Array.isArray(metadata) ? metadata : metadata ? [metadata] : [];
|
|
2397
|
+
const directives = [];
|
|
2398
|
+
for (const record of records) {
|
|
2399
|
+
const value = record.continuityExecutionDirectives;
|
|
2400
|
+
if (Array.isArray(value))
|
|
2401
|
+
directives.push(...value);
|
|
2402
|
+
}
|
|
2403
|
+
return directives;
|
|
2404
|
+
}
|
|
2405
|
+
function hashDirective(directive) {
|
|
2406
|
+
return hashPublicValue(stableJson({
|
|
2407
|
+
kind: directive.kind,
|
|
2408
|
+
commandClass: directive.commandClass,
|
|
2409
|
+
jobId: directive.jobId,
|
|
2410
|
+
targetRef: directive.targetRef,
|
|
2411
|
+
commandRef: directive.commandRef,
|
|
2412
|
+
epicId: directive.epicId,
|
|
2413
|
+
runtime: directive.runtime,
|
|
2414
|
+
}));
|
|
2415
|
+
}
|
|
2416
|
+
function stableJson(value) {
|
|
2417
|
+
if (Array.isArray(value))
|
|
2418
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
2419
|
+
if (isRecord(value)) {
|
|
2420
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
|
|
2421
|
+
}
|
|
2422
|
+
return JSON.stringify(value);
|
|
2423
|
+
}
|
|
2424
|
+
function assertPublicSafeCandidate(value, path, redactions) {
|
|
2425
|
+
if (typeof value === 'string') {
|
|
2426
|
+
if (UNSAFE_VALUE_PATTERN.test(value) || SHELL_FRAGMENT_PATTERN.test(value))
|
|
2427
|
+
redactions.push(redaction(path, unsafeKeyReason(path), 'value-denied'));
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
if (value === undefined || value === null || typeof value === 'number' || typeof value === 'boolean')
|
|
2431
|
+
return;
|
|
2432
|
+
if (Array.isArray(value)) {
|
|
2433
|
+
value.forEach((item, index) => assertPublicSafeCandidate(item, `${path}.${index}`, redactions));
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
if (isRecord(value)) {
|
|
2437
|
+
for (const [key, child] of Object.entries(value)) {
|
|
2438
|
+
const field = `${path}.${key}`;
|
|
2439
|
+
if (UNSAFE_KEY_PATTERN.test(key))
|
|
2440
|
+
redactions.push(redaction(field, unsafeKeyReason(key), 'key-denied'));
|
|
2441
|
+
assertPublicSafeCandidate(child, field, redactions);
|
|
2442
|
+
}
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
redactions.push(redaction(path, 'unsafe_key', 'value-denied'));
|
|
2446
|
+
}
|
|
2447
|
+
function validateDirectiveRedactions(value, redactions) {
|
|
2448
|
+
if (value === undefined)
|
|
2449
|
+
return [];
|
|
2450
|
+
if (!Array.isArray(value)) {
|
|
2451
|
+
redactions.push(redaction('redactionSummary', 'unsafe_key', 'value-denied'));
|
|
2452
|
+
return [];
|
|
2453
|
+
}
|
|
2454
|
+
const safe = [];
|
|
2455
|
+
for (const [index, item] of value.entries()) {
|
|
2456
|
+
if (!isRecord(item) || item.publicSafe !== true) {
|
|
2457
|
+
redactions.push(redaction(`redactionSummary.${index}`, 'unsafe_key', 'value-denied'));
|
|
2458
|
+
continue;
|
|
2459
|
+
}
|
|
2460
|
+
const field = stringField(item, 'field');
|
|
2461
|
+
const reason = stringField(item, 'reason');
|
|
2462
|
+
const evidence = stringField(item, 'evidence');
|
|
2463
|
+
if (!field || !reason || !evidence || UNSAFE_VALUE_PATTERN.test(field) || UNSAFE_VALUE_PATTERN.test(reason) || UNSAFE_VALUE_PATTERN.test(evidence)) {
|
|
2464
|
+
redactions.push(redaction(`redactionSummary.${index}`, 'unsafe_key', 'value-denied'));
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
safe.push({ field, reason, evidence, publicSafe: true });
|
|
2468
|
+
}
|
|
2469
|
+
return safe;
|
|
2470
|
+
}
|
|
2471
|
+
function rejectPromptExtraction(reasonCode, publicSummary, redactionSummary) {
|
|
2472
|
+
return { ok: false, reasonCode, publicSummary, redactionSummary };
|
|
2473
|
+
}
|
|
2474
|
+
function safeStringArray(value) {
|
|
2475
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
2476
|
+
}
|
|
2477
|
+
function isSafeExecutionBranch(value) {
|
|
2478
|
+
return value.length > 0 && value.length <= 240 && !value.startsWith('-') && !value.includes('..')
|
|
2479
|
+
&& !value.includes('\\') && /^[A-Za-z0-9._/-]+$/.test(value);
|
|
2480
|
+
}
|
|
2481
|
+
function sameStringSet(left, right) {
|
|
2482
|
+
return left.length === right.length && new Set(left).size === left.length && left.every((item) => right.includes(item));
|
|
2483
|
+
}
|
|
2484
|
+
function safeAttemptRelativePaths(attempt) {
|
|
2485
|
+
return EPIC_TARGET_PATTERN.test(attempt.target_ref) ? [attempt.target_ref] : [];
|
|
2486
|
+
}
|
|
2487
|
+
function sanitizePublicRecord(record) {
|
|
2488
|
+
if (!record)
|
|
2489
|
+
return undefined;
|
|
2490
|
+
const output = {};
|
|
2491
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2492
|
+
if (!/^[A-Za-z0-9_.:-]{1,80}$/.test(key) || UNSAFE_KEY_PATTERN.test(key) || UNSAFE_VALUE_PATTERN.test(key))
|
|
2493
|
+
continue;
|
|
2494
|
+
if (typeof value === 'string' && UNSAFE_VALUE_PATTERN.test(value))
|
|
2495
|
+
continue;
|
|
2496
|
+
output[key] = value;
|
|
2497
|
+
}
|
|
2498
|
+
return Object.keys(output).length > 0 ? output : undefined;
|
|
2499
|
+
}
|
|
2500
|
+
function emptyAggregateCounts() {
|
|
2501
|
+
return aggregateCounts(0, 0, 0, 0, 0, 0, 0);
|
|
2502
|
+
}
|
|
2503
|
+
function aggregateCounts(iterations, childCount, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount) {
|
|
2504
|
+
return { iterations, childCount, succeededChildCount, failedChildCount, skippedChildCount, metadataCompleteCount, metadataAbortedCount };
|
|
2505
|
+
}
|
|
2506
|
+
function storyIdFromTargetRef(targetRef) {
|
|
2507
|
+
return targetRef.replace(/^docs\/stories\//, '').replace(/\.story\.md$/, '');
|
|
2508
|
+
}
|
|
2509
|
+
function epicIdFromTargetRef(targetRef) {
|
|
2510
|
+
return targetRef.replace(/^docs\/epics\/epic-/, '').replace(/\.md$/, '');
|
|
2511
|
+
}
|
|
2512
|
+
function isStoryId(value) {
|
|
2513
|
+
return /^[A-Za-z0-9][A-Za-z0-9_-]*\.\d+[A-Za-z0-9_-]*$/.test(value) && !UNSAFE_VALUE_PATTERN.test(value) && !SHELL_FRAGMENT_PATTERN.test(value);
|
|
2514
|
+
}
|
|
2515
|
+
function normalizeRuntimeSource(value) {
|
|
2516
|
+
if (value === 'explicit' || value === 'origin' || value === 'default' || value === 'fallback')
|
|
2517
|
+
return value;
|
|
2518
|
+
return undefined;
|
|
2519
|
+
}
|
|
2520
|
+
function normalizeStoredRuntimeSource(value) {
|
|
2521
|
+
if (value === 'explicit' || value === 'origin' || value === 'default' || value === 'fallback' || value === 'unspecified')
|
|
2522
|
+
return value;
|
|
2523
|
+
return undefined;
|
|
2524
|
+
}
|
|
2525
|
+
function unsafeKeyReason(key) {
|
|
2526
|
+
if (/prompt|commandRef/i.test(key))
|
|
2527
|
+
return 'prompt_body';
|
|
2528
|
+
if (/std(?:out|err)|raw|log|transcript|stack/i.test(key))
|
|
2529
|
+
return 'raw_log';
|
|
2530
|
+
if (/url/i.test(key))
|
|
2531
|
+
return 'private_url';
|
|
2532
|
+
if (/path|cwd|argv|args|shell|command|binary|executable/i.test(key))
|
|
2533
|
+
return 'executable_path';
|
|
2534
|
+
if (/secret|token|password|authorization|api[_-]?key|license/i.test(key))
|
|
2535
|
+
return 'secret_or_pii';
|
|
2536
|
+
return 'unsafe_key';
|
|
2537
|
+
}
|
|
2538
|
+
function stringField(record, key) {
|
|
2539
|
+
return typeof record[key] === 'string' ? record[key] : undefined;
|
|
2540
|
+
}
|
|
2541
|
+
function isSafeIdentifier(value) {
|
|
2542
|
+
return Boolean(value && SAFE_ID_PATTERN.test(value) && !UNSAFE_VALUE_PATTERN.test(value) && !SHELL_FRAGMENT_PATTERN.test(value));
|
|
2543
|
+
}
|
|
2544
|
+
function isRecord(value) {
|
|
2545
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2546
|
+
}
|
|
2547
|
+
function defaultHeadlessIdFactory(prefix, attemptId) {
|
|
2548
|
+
return headlessExecutionEntityId(prefix, `${attemptId}-${Date.now()}-${randomBytes(4).toString('hex')}`);
|
|
2549
|
+
}
|
|
2550
|
+
function addMilliseconds(iso, ms) {
|
|
2551
|
+
const base = Date.parse(iso);
|
|
2552
|
+
return new Date((Number.isFinite(base) ? base : Date.now()) + ms).toISOString();
|
|
2553
|
+
}
|
|
2554
|
+
function laterIsoTimestamp(left, right) {
|
|
2555
|
+
const leftMs = Date.parse(left);
|
|
2556
|
+
const rightMs = right ? Date.parse(right) : Number.NaN;
|
|
2557
|
+
if (!Number.isFinite(rightMs))
|
|
2558
|
+
return left;
|
|
2559
|
+
if (!Number.isFinite(leftMs))
|
|
2560
|
+
return right;
|
|
2561
|
+
return rightMs > leftMs ? right : left;
|
|
2562
|
+
}
|
|
2563
|
+
function boundedInteger(value, min, max) {
|
|
2564
|
+
if (!Number.isFinite(value))
|
|
2565
|
+
return min;
|
|
2566
|
+
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
2567
|
+
}
|
|
2568
|
+
function unique(values) {
|
|
2569
|
+
return Array.from(new Set(values));
|
|
2570
|
+
}
|
|
2571
|
+
function uniqueBy(values, key) {
|
|
2572
|
+
const seen = new Set();
|
|
2573
|
+
const output = [];
|
|
2574
|
+
for (const value of values) {
|
|
2575
|
+
const itemKey = key(value);
|
|
2576
|
+
if (seen.has(itemKey))
|
|
2577
|
+
continue;
|
|
2578
|
+
seen.add(itemKey);
|
|
2579
|
+
output.push(value);
|
|
2580
|
+
}
|
|
2581
|
+
return output;
|
|
2582
|
+
}
|