@ornexus/neocortex 4.60.73 → 4.60.75
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 +1216 -1210
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +1 -108
- package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
- package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
- package/packages/client/dist/adapters/codex-adapter.js +2 -80
- package/packages/client/dist/adapters/cursor-adapter.js +4 -115
- package/packages/client/dist/adapters/gemini-adapter.js +2 -71
- package/packages/client/dist/adapters/grok-adapter.js +2 -42
- package/packages/client/dist/adapters/index.js +1 -22
- package/packages/client/dist/adapters/platform-detector.js +1 -212
- package/packages/client/dist/adapters/target-adapter.js +0 -12
- package/packages/client/dist/adapters/vscode-adapter.js +2 -72
- package/packages/client/dist/agent/refresh-stubs.js +2 -417
- package/packages/client/dist/agent/update-agent-yaml.js +1 -102
- package/packages/client/dist/agent/update-description.js +1 -251
- package/packages/client/dist/cache/crypto-utils.js +1 -78
- package/packages/client/dist/cache/encrypted-cache.d.ts +27 -0
- package/packages/client/dist/cache/encrypted-cache.js +1 -108
- package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
- package/packages/client/dist/cache/index.js +1 -13
- package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
- package/packages/client/dist/checkpoint/index.js +1 -11
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
- package/packages/client/dist/cli.js +5 -358
- package/packages/client/dist/commands/activate.js +8 -394
- package/packages/client/dist/commands/architecture-answers.js +7 -274
- package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
- package/packages/client/dist/commands/architecture-root-binding.js +1 -51
- package/packages/client/dist/commands/cache-status.js +2 -112
- package/packages/client/dist/commands/config.js +1 -88
- package/packages/client/dist/commands/doctor.js +2 -635
- package/packages/client/dist/commands/invoke.d.ts +38 -1
- package/packages/client/dist/commands/invoke.js +143 -7200
- package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
- package/packages/client/dist/commands/legacy-story-branch.js +1 -96
- package/packages/client/dist/commands/legacy-story-pr.js +1 -276
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
- package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
- package/packages/client/dist/commands/mutation-qa-git.js +1 -41
- package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
- package/packages/client/dist/commands/mutation-qa-run.js +3 -219
- package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
- package/packages/client/dist/commands/mutation-qa.js +1 -220
- package/packages/client/dist/commands/native-agent.js +2 -136
- package/packages/client/dist/commands/planning-diagnosis.js +1 -43
- package/packages/client/dist/commands/planning-generation.js +1 -53
- package/packages/client/dist/commands/planning-memory.js +1 -40
- package/packages/client/dist/commands/planning-research.js +1 -42
- package/packages/client/dist/commands/private-session.js +1 -75
- package/packages/client/dist/commands/property-qa.js +1 -139
- package/packages/client/dist/commands/qa-control.js +1 -16
- package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
- package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
- package/packages/client/dist/commands/qa-scan-capture.js +5 -663
- package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
- package/packages/client/dist/commands/qa-scan-review.js +5 -826
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
- package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
- package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
- package/packages/client/dist/commands/refactor-characterization.js +21 -671
- package/packages/client/dist/commands/refactor-commit.js +13 -399
- package/packages/client/dist/commands/refactor-control.js +3 -142
- package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
- package/packages/client/dist/commands/refactor-review.js +5 -283
- package/packages/client/dist/commands/refactor-root.js +7 -588
- package/packages/client/dist/commands/refresh-memory.js +3 -156
- package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
- package/packages/client/dist/commands/story-checkpoint.js +1 -171
- package/packages/client/dist/config/host-auth-broker.d.ts +31 -0
- package/packages/client/dist/config/host-auth-broker.js +1 -0
- package/packages/client/dist/config/host-auth-reference.d.ts +32 -0
- package/packages/client/dist/config/host-auth-reference.js +1 -0
- package/packages/client/dist/config/profile-directory.js +1 -13
- package/packages/client/dist/config/resolver-selection.js +1 -278
- package/packages/client/dist/config/secure-config.js +12 -281
- package/packages/client/dist/config.js +2 -223
- package/packages/client/dist/constants.js +1 -25
- package/packages/client/dist/context/context-collector.js +2 -222
- package/packages/client/dist/context/context-sanitizer.js +1 -145
- package/packages/client/dist/continuity/autonomous-governance.js +1 -285
- package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
- package/packages/client/dist/continuity/invoke-hooks.js +1 -129
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
- package/packages/client/dist/continuity/native-agent-store.js +1 -69
- package/packages/client/dist/continuity/native-agent-types.d.ts +11 -0
- package/packages/client/dist/continuity/native-agent-types.js +1 -90
- package/packages/client/dist/continuity/outbox.js +1 -117
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
- package/packages/client/dist/continuity/sqlite-store.js +81 -2612
- package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
- package/packages/client/dist/errors/error-messages.d.ts +3 -0
- package/packages/client/dist/errors/error-messages.js +2 -228
- package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
- package/packages/client/dist/evidence/collector.js +1 -347
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +0 -2
- package/packages/client/dist/fs/atomic-state-replace.js +1 -152
- package/packages/client/dist/generated/command-registry.generated.js +1 -4076
- package/packages/client/dist/generated/public-projections.generated.js +1 -149
- package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +11 -1
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
- package/packages/client/dist/git/epic-operational-state.js +2 -672
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
- package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
- package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
- package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
- package/packages/client/dist/git/legacy-story-pr.js +70 -3538
- package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
- package/packages/client/dist/i18n/first-run.js +2 -60
- package/packages/client/dist/index.d.ts +2 -0
- package/packages/client/dist/index.js +1 -47
- package/packages/client/dist/invocation/invoke-input.js +1 -24
- package/packages/client/dist/license/host-auth-recovery.d.ts +158 -0
- package/packages/client/dist/license/host-auth-recovery.js +1 -0
- package/packages/client/dist/license/index.js +1 -5
- package/packages/client/dist/license/license-client.d.ts +10 -1
- package/packages/client/dist/license/license-client.js +1 -304
- package/packages/client/dist/machine/fingerprint.js +2 -163
- package/packages/client/dist/machine/index.js +1 -5
- package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
- package/packages/client/dist/memory/project-memory-writer.js +40 -1007
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
- package/packages/client/dist/migrations/project-identity-capability.js +1 -50
- package/packages/client/dist/migrations/project-identity.js +7 -493
- package/packages/client/dist/migrations/state-json.js +4 -393
- package/packages/client/dist/policy/architecture-policy.js +2 -184
- package/packages/client/dist/policy/index.js +1 -7
- package/packages/client/dist/policy/shared-policy-types.js +0 -38
- package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
- package/packages/client/dist/private-execution/clean-baseline.js +2 -279
- package/packages/client/dist/private-execution/codex-executor.js +4 -574
- package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
- package/packages/client/dist/private-execution/delivery.js +13 -410
- package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
- package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
- package/packages/client/dist/private-execution/implementation-validation.js +2 -106
- package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
- package/packages/client/dist/private-execution/isolated-session.js +5 -725
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
- package/packages/client/dist/private-execution/pact-test-source.js +1 -258
- package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
- package/packages/client/dist/private-execution/pact-write-set.js +1 -172
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
- package/packages/client/dist/private-execution/private-report-store.js +1 -42
- package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
- package/packages/client/dist/private-execution/property-test-choices.js +3 -147
- package/packages/client/dist/private-execution/property-test-plan.js +7 -271
- package/packages/client/dist/private-execution/property-test-review.js +4 -258
- package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
- package/packages/client/dist/private-execution/property-test-source.js +1 -139
- package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
- package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
- package/packages/client/dist/private-execution/root-memory-request.js +1 -29
- package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
- package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
- package/packages/client/dist/private-execution/session-lease.js +1 -63
- package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
- package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
- package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
- package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
- package/packages/client/dist/private-execution/ux-write-set.js +2 -191
- package/packages/client/dist/registry/registry-reconciliation.js +1 -234
- package/packages/client/dist/resilience/circuit-breaker.js +1 -170
- package/packages/client/dist/resilience/degradation-manager.js +1 -164
- package/packages/client/dist/resilience/freshness-indicator.js +1 -100
- package/packages/client/dist/resilience/index.js +1 -8
- package/packages/client/dist/resilience/recovery-detector.js +1 -74
- package/packages/client/dist/resolvers/asset-resolver.js +0 -13
- package/packages/client/dist/resolvers/local-resolver.js +8 -255
- package/packages/client/dist/resolvers/remote-resolver.js +1 -233
- package/packages/client/dist/runner/binary.js +1 -404
- package/packages/client/dist/runner/cli.js +17 -1571
- package/packages/client/dist/runner/epic-background-dispatcher.d.ts +61 -0
- package/packages/client/dist/runner/epic-background-dispatcher.js +1 -0
- package/packages/client/dist/runner/headless/adapters.js +1 -49
- package/packages/client/dist/runner/headless/background-root.d.ts +65 -0
- package/packages/client/dist/runner/headless/background-root.js +1 -0
- package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +27 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
- package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
- package/packages/client/dist/runner/headless/managed-environment-cleanup.d.ts +95 -0
- package/packages/client/dist/runner/headless/managed-environment-cleanup.js +1 -0
- package/packages/client/dist/runner/headless/managed-profile.js +1 -132
- package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
- package/packages/client/dist/runner/headless/resolver.js +1 -231
- package/packages/client/dist/runner/headless/run-yolo.js +1 -885
- package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
- package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
- package/packages/client/dist/runner/headless/types.js +2 -574
- package/packages/client/dist/runner/local-console.js +13 -396
- package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
- package/packages/client/dist/runner/project-identity.js +1 -106
- package/packages/client/dist/runner/scheduler.js +7 -995
- package/packages/client/dist/runner-cli.js +2 -18
- package/packages/client/dist/state/invocation-shape.js +1 -13
- package/packages/client/dist/state/project-state-snapshot.d.ts +7 -0
- package/packages/client/dist/state/project-state-snapshot.js +1 -1731
- package/packages/client/dist/state/state-json-repair.js +3 -785
- package/packages/client/dist/state/structural-dag-projection.js +1 -118
- package/packages/client/dist/telemetry/index.js +1 -5
- package/packages/client/dist/telemetry/offline-queue.js +1 -131
- package/packages/client/dist/tier/index.js +1 -5
- package/packages/client/dist/tier/tier-aware-client.js +1 -405
- package/packages/client/dist/types/index.js +1 -38
- package/packages/client/dist/version.js +1 -27
- package/packages/client/dist/yolo/change-evidence-collector.d.ts +21 -1
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
- package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
- package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
- package/packages/client/dist/yoloop/discovery-hook.js +2 -300
- package/packages/client/dist/yoloop/index.js +1 -8
- package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
- package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
- package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
- package/packages/client/dist/yoloop/target-normalization.js +1 -180
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
- package/packages/shared/dist/invocation/explicit-intent-recovery.d.ts +83 -0
- package/packages/shared/dist/invocation/explicit-intent-recovery.js +187 -0
- package/packages/shared/dist/invocation/index.d.ts +7 -0
- package/packages/shared/dist/invocation/index.js +22 -0
- package/packages/shared/dist/yolo/change-evidence.js +1 -1
- package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
- 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 +24 -21
- package/targets-stubs/codex/README.md +6 -1
- package/targets-stubs/codex/neocortex.toml +7 -2
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +16 -1
- package/targets-stubs/opencode/neocortex.md +6 -0
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1,1234 +1,4 @@
|
|
|
1
|
-
/** Root-owned two-phase commit/checkpoint boundary for direct `*yolo` runs. */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { join, resolve } from 'node:path';
|
|
6
|
-
import { promisify } from 'node:util';
|
|
7
|
-
import { readRootMemoryEffectForCheckpoint } from '../private-execution/root-memory-scope.js';
|
|
8
|
-
import { rootMemoryEffectDigest, verifyRootMemoryPhysicalWriteSet } from '../private-execution/root-memory-git-evidence.js';
|
|
9
|
-
import { acquireGitLifecyclePhysicalIntegrationLock, applyGitLifecycleRuntimeOverlay, computeImmutableStoryQaEvidenceDigest, computeGitLifecyclePhysicalIntegrationLockOwnerDigest, computeP205StoryQaEvidenceDigest, computeLegacyDirectYoloRecoveryReceiptRef, ensureRuntimeEpic, ensureRuntimeStory, ensureGitLifecyclePrivateChildDirectory, executeP205TransientAuthority, projectP205TransientExecutionEvidence, publicStoryReviewArtifactFromGit, LEGACY_DIRECT_YOLO_RECOVERY_COMMITS, mutateGitLifecycleRuntime, P205_COMPLETE_COMMAND, readGitLifecycleRuntime, gitLifecycleRuntimeDirectory, assertGitLifecyclePrivateFile, releaseGitLifecyclePhysicalIntegrationLock, releaseGitLifecyclePhysicalIntegrationLockForCheckpoint, verifyGitLifecyclePhysicalIntegrationLock, verifyAcceptedGitLifecycleStoryCheckpoint, } from './git-lifecycle-store.js';
|
|
10
|
-
import { anchoredAtomicWriteFile } from './secure-filesystem-boundary.js';
|
|
11
|
-
import { GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID, GIT_LIFECYCLE_REVIEW_PR_STEP_ID, GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID, } from './git-lifecycle-step-ids.js';
|
|
12
|
-
const execFileAsync = promisify(execFile);
|
|
13
|
-
/**
|
|
14
|
-
* Productive P207 setup boundary. It owns only the current checkout and the
|
|
15
|
-
* epic branch; it never creates, removes or repositions a worktree. Existing
|
|
16
|
-
* staged and unstaged bytes must remain identical across the branch switch.
|
|
17
|
-
*/
|
|
18
|
-
export async function prepareDirectYoloSetup(options) {
|
|
19
|
-
try {
|
|
20
|
-
const authority = await resolveLogicalAuthority(options.projectRoot, options.storyId);
|
|
21
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
22
|
-
const nowMs = Date.parse(nowIso);
|
|
23
|
-
const currentBranch = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
24
|
-
const currentHead = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
|
|
25
|
-
const runtimeBefore = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
26
|
-
const runtimeEpic = asRecord(runtimeBefore.epics[authority.epicId]);
|
|
27
|
-
const currentLease = asRecord(runtimeEpic?.branch_lease);
|
|
28
|
-
const liveLease = currentLease && Number.isFinite(Date.parse(String(currentLease.expiresAt)))
|
|
29
|
-
&& Date.parse(String(currentLease.expiresAt)) > nowMs;
|
|
30
|
-
if (liveLease && currentLease?.ownerStoryId !== options.storyId) {
|
|
31
|
-
return failure('direct-yolo-writer-active');
|
|
32
|
-
}
|
|
33
|
-
let originBinding = validOriginBinding(runtimeEpic?.origin_binding, authority.executionBranch, authority.originBranch);
|
|
34
|
-
if (currentBranch === authority.executionBranch) {
|
|
35
|
-
if (!originBinding)
|
|
36
|
-
return failure('direct-yolo-origin-binding-invalid');
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
if (currentBranch !== authority.originBranch)
|
|
40
|
-
return failure('direct-yolo-current-branch-mismatch');
|
|
41
|
-
if (originBinding && originBinding.originHeadSha !== currentHead) {
|
|
42
|
-
return failure('direct-yolo-origin-binding-conflict');
|
|
43
|
-
}
|
|
44
|
-
originBinding ??= {
|
|
45
|
-
schemaVersion: 1,
|
|
46
|
-
originBranch: authority.originBranch,
|
|
47
|
-
originHeadSha: currentHead,
|
|
48
|
-
executionBranch: authority.executionBranch,
|
|
49
|
-
capturedFromCurrentCheckout: true,
|
|
50
|
-
capturedAt: nowIso,
|
|
51
|
-
};
|
|
52
|
-
const occupiedElsewhere = (await worktreeBranches(authority.projectRoot)).some((item) => item.branch === authority.executionBranch && resolve(item.path) !== authority.projectRoot);
|
|
53
|
-
if (occupiedElsewhere)
|
|
54
|
-
return failure('direct-yolo-execution-branch-checked-out-elsewhere');
|
|
55
|
-
const checkoutBefore = await checkoutPayload(authority.projectRoot);
|
|
56
|
-
const executionRef = `refs/heads/${authority.executionBranch}`;
|
|
57
|
-
if (await gitExitOk(authority.projectRoot, ['show-ref', '--verify', '--quiet', executionRef])) {
|
|
58
|
-
const executionHead = await git(authority.projectRoot, ['rev-parse', executionRef]);
|
|
59
|
-
if (executionHead !== currentHead) {
|
|
60
|
-
if (!(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', executionHead, currentHead]))) {
|
|
61
|
-
return failure('direct-yolo-execution-branch-diverged');
|
|
62
|
-
}
|
|
63
|
-
await git(authority.projectRoot, ['update-ref', executionRef, currentHead, executionHead]);
|
|
64
|
-
}
|
|
65
|
-
await git(authority.projectRoot, ['switch', authority.executionBranch]);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
await git(authority.projectRoot, ['switch', '-c', authority.executionBranch]);
|
|
69
|
-
}
|
|
70
|
-
if (await checkoutPayload(authority.projectRoot) !== checkoutBefore) {
|
|
71
|
-
return failure('direct-yolo-checkout-payload-raced');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const branchAfter = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
75
|
-
const headAfter = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
|
|
76
|
-
if (branchAfter !== authority.executionBranch)
|
|
77
|
-
return failure('direct-yolo-current-branch-mismatch');
|
|
78
|
-
if (liveLease && currentLease?.ownerStoryId === options.storyId) {
|
|
79
|
-
if (currentLease.setupHeadSha !== headAfter)
|
|
80
|
-
return failure('direct-yolo-setup-lease-head-unbound');
|
|
81
|
-
return { ok: true, value: setupReceipt(authority, originBinding, String(currentLease.expiresAt), true) };
|
|
82
|
-
}
|
|
83
|
-
const acquiredAt = nowIso;
|
|
84
|
-
const expiresAt = new Date(nowMs + 60 * 60 * 1000).toISOString();
|
|
85
|
-
const sessionId = `setup-${randomBytes(32).toString('hex')}`;
|
|
86
|
-
const runtimeAtCas = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
87
|
-
await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
88
|
-
const epic = ensureRuntimeEpic(document, authority.epicId);
|
|
89
|
-
const lease = asRecord(epic.branch_lease);
|
|
90
|
-
if (lease && Date.parse(String(lease.expiresAt)) > nowMs && lease.ownerStoryId !== options.storyId) {
|
|
91
|
-
throw new Error('direct-yolo-writer-active');
|
|
92
|
-
}
|
|
93
|
-
const persistedBinding = validOriginBinding(epic.origin_binding, authority.executionBranch, authority.originBranch);
|
|
94
|
-
if (epic.origin_binding && !persistedBinding)
|
|
95
|
-
throw new Error('direct-yolo-origin-binding-conflict');
|
|
96
|
-
if (persistedBinding && JSON.stringify(persistedBinding) !== JSON.stringify(originBinding)) {
|
|
97
|
-
throw new Error('direct-yolo-origin-binding-conflict');
|
|
98
|
-
}
|
|
99
|
-
epic.branch_mode = 'epic';
|
|
100
|
-
epic.execution_branch = authority.executionBranch;
|
|
101
|
-
epic.origin_branch = authority.originBranch;
|
|
102
|
-
epic.graph_revision = authority.graphRevision;
|
|
103
|
-
epic.origin_binding = originBinding;
|
|
104
|
-
epic.branch_lease = {
|
|
105
|
-
schemaVersion: 1, ownerStoryId: options.storyId, sessionId, idempotencyKey: sessionId,
|
|
106
|
-
acquiredAt, expiresAt, setupHeadSha: headAfter,
|
|
107
|
-
};
|
|
108
|
-
}, {
|
|
109
|
-
expectedRevision: runtimeAtCas.revision,
|
|
110
|
-
preCommit: async () => {
|
|
111
|
-
if (await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== authority.executionBranch
|
|
112
|
-
|| await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headAfter) {
|
|
113
|
-
throw new Error('direct-yolo-setup-physical-state-raced');
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
return { ok: true, value: setupReceipt(authority, originBinding, expiresAt, false) };
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
return failure(publicReason(error, 'direct-yolo-setup-failed'));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/** Accept C01 only from the root after its own physical setup and live lease. */
|
|
124
|
-
export async function acceptDirectYoloSetupCheckpoint(options) {
|
|
125
|
-
let physicalLock;
|
|
126
|
-
try {
|
|
127
|
-
const authority = await resolveAuthority(options.projectRoot, options.storyId);
|
|
128
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
129
|
-
const statePath = join(authority.projectRoot, '.neocortex/state.json');
|
|
130
|
-
const trackedBefore = await readFile(statePath, 'utf8');
|
|
131
|
-
const payloadBefore = await checkoutPayload(authority.projectRoot);
|
|
132
|
-
const headSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
|
|
133
|
-
const executionRef = `refs/heads/${authority.executionBranch}`;
|
|
134
|
-
const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
135
|
-
const epic = asRecord(runtime.epics[authority.epicId]);
|
|
136
|
-
const binding = validOriginBinding(epic?.origin_binding, authority.executionBranch, authority.originBranch);
|
|
137
|
-
const lease = asRecord(epic?.branch_lease);
|
|
138
|
-
if (!binding)
|
|
139
|
-
return failure('direct-yolo-origin-binding-invalid');
|
|
140
|
-
if (!lease || !isLiveSameStorySetupLease(lease, options.storyId, nowIso)
|
|
141
|
-
|| lease?.sessionId !== lease?.idempotencyKey)
|
|
142
|
-
return failure('direct-yolo-setup-lease-invalid');
|
|
143
|
-
const acquired = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
144
|
-
kind: 'direct', storyId: options.storyId,
|
|
145
|
-
sessionId: String(lease.sessionId), idempotencyKey: String(lease.idempotencyKey),
|
|
146
|
-
expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString(), persistent: false,
|
|
147
|
-
});
|
|
148
|
-
if (!acquired.ok)
|
|
149
|
-
return failure(acquired.reasonCode);
|
|
150
|
-
physicalLock = acquired;
|
|
151
|
-
if (headSha !== lease.setupHeadSha
|
|
152
|
-
|| !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', binding.originHeadSha, headSha]))
|
|
153
|
-
|| await git(authority.projectRoot, ['rev-parse', executionRef]) !== headSha) {
|
|
154
|
-
return failure('direct-yolo-setup-head-mismatch');
|
|
155
|
-
}
|
|
156
|
-
const occupiedElsewhere = (await worktreeBranches(authority.projectRoot)).some((item) => item.branch === authority.executionBranch && resolve(item.path) !== authority.projectRoot);
|
|
157
|
-
if (occupiedElsewhere)
|
|
158
|
-
return failure('direct-yolo-execution-branch-checked-out-elsewhere');
|
|
159
|
-
const tracked = asRecord(JSON.parse(trackedBefore));
|
|
160
|
-
const trackedStory = asRecord(asRecord(tracked?.stories)?.[options.storyId]);
|
|
161
|
-
const runtimeStory = asRecord(runtime.stories[options.storyId]);
|
|
162
|
-
if (!trackedStory || !['backlog', 'ready_to_start'].includes(String(trackedStory.status))
|
|
163
|
-
|| (trackedStory.steps_completed !== undefined
|
|
164
|
-
&& (!Array.isArray(trackedStory.steps_completed) || trackedStory.steps_completed.length > 0))
|
|
165
|
-
|| trackedStory.branch_name || trackedStory.checkpoint) {
|
|
166
|
-
return failure('direct-yolo-setup-story-progress-conflict');
|
|
167
|
-
}
|
|
168
|
-
const accepted = runtimeStory?.status === 'branch-ready'
|
|
169
|
-
&& runtimeStory.last_step === GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID
|
|
170
|
-
&& Array.isArray(runtimeStory.steps_completed)
|
|
171
|
-
&& runtimeStory.steps_completed.length === 1
|
|
172
|
-
&& runtimeStory.steps_completed[0] === GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID
|
|
173
|
-
&& Object.keys(runtimeStory).sort().join(',') === 'last_step,status,steps_completed';
|
|
174
|
-
if (runtimeStory && Object.keys(runtimeStory).length > 0 && !accepted) {
|
|
175
|
-
return failure('direct-yolo-setup-story-progress-conflict');
|
|
176
|
-
}
|
|
177
|
-
const physicalGuard = async () => {
|
|
178
|
-
if (Date.parse(String(lease.expiresAt)) <= (options.nowIso ? Date.parse(nowIso) : Date.now())
|
|
179
|
-
|| !physicalLock || !(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))
|
|
180
|
-
|| await readFile(statePath, 'utf8') !== trackedBefore
|
|
181
|
-
|| await checkoutPayload(authority.projectRoot) !== payloadBefore
|
|
182
|
-
|| await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== authority.executionBranch
|
|
183
|
-
|| await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headSha
|
|
184
|
-
|| !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', binding.originHeadSha, headSha]))
|
|
185
|
-
|| await git(authority.projectRoot, ['rev-parse', executionRef]) !== headSha) {
|
|
186
|
-
throw new Error('direct-yolo-setup-physical-state-raced');
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
if (accepted) {
|
|
190
|
-
await physicalGuard();
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
194
|
-
const currentEpic = asRecord(document.epics[authority.epicId]);
|
|
195
|
-
const currentStory = ensureRuntimeStory(document, options.storyId);
|
|
196
|
-
if (JSON.stringify(currentEpic?.branch_lease) !== JSON.stringify(lease)
|
|
197
|
-
|| JSON.stringify(currentEpic?.origin_binding) !== JSON.stringify(binding)
|
|
198
|
-
|| currentEpic?.graph_revision !== authority.graphRevision
|
|
199
|
-
|| Object.keys(currentStory).length > 0) {
|
|
200
|
-
throw new Error('direct-yolo-setup-runtime-raced');
|
|
201
|
-
}
|
|
202
|
-
currentStory.status = 'branch-ready';
|
|
203
|
-
currentStory.last_step = GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID;
|
|
204
|
-
currentStory.steps_completed = [GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID];
|
|
205
|
-
}, { expectedRevision: runtime.revision, preCommit: physicalGuard });
|
|
206
|
-
}
|
|
207
|
-
return { ok: true, value: Object.freeze({
|
|
208
|
-
...setupReceipt(authority, binding, String(lease.expiresAt), accepted),
|
|
209
|
-
checkpointAccepted: true, graphRevision: authority.graphRevision, headSha,
|
|
210
|
-
}) };
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
return failure(publicReason(error, 'direct-yolo-setup-acceptance-failed'));
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
if (physicalLock) {
|
|
217
|
-
await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Productive setup-owner handoff emitter used immediately before C08. The
|
|
223
|
-
* one-time token never crosses stdout; only a public receipt is returned.
|
|
224
|
-
*/
|
|
225
|
-
export async function issueDirectYoloSetupLeaseHandoff(options) {
|
|
226
|
-
try {
|
|
227
|
-
const authority = await resolveAuthority(options.projectRoot, options.storyId);
|
|
228
|
-
const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--cached', '--name-only', '-z']));
|
|
229
|
-
const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
|
|
230
|
-
if (!writeSet.ok)
|
|
231
|
-
return failure(writeSet.reasonCode);
|
|
232
|
-
if (authority.rootMemoryEffect)
|
|
233
|
-
verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch,
|
|
234
|
-
baseSha: await git(authority.projectRoot, ['rev-parse', 'HEAD']), headRef: ':', childPaths: authority.authorizedWriteSet });
|
|
235
|
-
const stagedReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, ':');
|
|
236
|
-
if (stagedReview?.verdict !== 'approved')
|
|
237
|
-
return failure('direct-yolo-review-artifact-not-approved');
|
|
238
|
-
const unstagedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--name-only', '-z']));
|
|
239
|
-
if (unstagedPaths.some((path) => changedPaths.includes(path)))
|
|
240
|
-
return failure('direct-yolo-user-overlap');
|
|
241
|
-
const payload = await gitRaw(authority.projectRoot, ['diff', '--cached', '--binary', '--full-index']);
|
|
242
|
-
if (!payload)
|
|
243
|
-
return failure('direct-yolo-staged-diff-empty');
|
|
244
|
-
const bundleDigest = digest(payload);
|
|
245
|
-
const claimRevision = digest([...authority.authorizedWriteSet].sort().join('\0'));
|
|
246
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
247
|
-
const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
248
|
-
const setupLease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
|
|
249
|
-
if (!isLiveSameStorySetupLease(setupLease, options.storyId, nowIso)) {
|
|
250
|
-
return failure('direct-yolo-setup-handoff-owner-invalid');
|
|
251
|
-
}
|
|
252
|
-
const sessionId = String(setupLease.sessionId);
|
|
253
|
-
const leaseExpiresAt = String(setupLease.expiresAt);
|
|
254
|
-
const candidateExpiresAt = new Date(Math.min(Date.parse(leaseExpiresAt), Date.parse(nowIso) + 5 * 60 * 1000)).toISOString();
|
|
255
|
-
const prior = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
|
|
256
|
-
const reused = Boolean(prior?.state === 'issued'
|
|
257
|
-
&& prior.fromSessionId === sessionId && prior.bundleDigest === bundleDigest
|
|
258
|
-
&& prior.claimRevision === claimRevision
|
|
259
|
-
&& Date.parse(prior.expiresAt) > Date.parse(nowIso));
|
|
260
|
-
const expiresAt = reused ? prior.expiresAt : candidateExpiresAt;
|
|
261
|
-
const token = reused ? prior.token : randomBytes(32).toString('base64url');
|
|
262
|
-
const tokenDigest = digest(token);
|
|
263
|
-
const grant = Object.freeze({
|
|
264
|
-
schemaVersion: 1,
|
|
265
|
-
kind: 'direct-yolo-setup-handoff',
|
|
266
|
-
storyId: options.storyId,
|
|
267
|
-
fromSessionId: sessionId,
|
|
268
|
-
tokenDigest,
|
|
269
|
-
bundleDigest,
|
|
270
|
-
claimRevision,
|
|
271
|
-
expiresAt,
|
|
272
|
-
});
|
|
273
|
-
const grantDigest = digest(JSON.stringify(grant));
|
|
274
|
-
const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
275
|
-
kind: 'direct', storyId: options.storyId, sessionId, idempotencyKey: sessionId,
|
|
276
|
-
expiresAt, persistent: false, createdAt: nowIso,
|
|
277
|
-
});
|
|
278
|
-
if (!physicalLock.ok)
|
|
279
|
-
return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
|
|
280
|
-
? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
|
|
281
|
-
try {
|
|
282
|
-
await writeSetupHandoffTokenRecord(authority.projectRoot, {
|
|
283
|
-
schemaVersion: 1, kind: 'direct-yolo-setup-handoff-token', state: 'issued',
|
|
284
|
-
storyId: options.storyId, fromSessionId: sessionId, token, tokenDigest,
|
|
285
|
-
bundleDigest, claimRevision, grantDigest, expiresAt,
|
|
286
|
-
});
|
|
287
|
-
await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
288
|
-
const current = asRecord(ensureRuntimeEpic(document, authority.epicId).branch_lease);
|
|
289
|
-
if (!isLiveSameStorySetupLease(current, options.storyId, nowIso)
|
|
290
|
-
|| current?.sessionId !== sessionId || current.idempotencyKey !== sessionId) {
|
|
291
|
-
throw new Error('direct-yolo-setup-handoff-owner-raced');
|
|
292
|
-
}
|
|
293
|
-
current.handoffGrant = grant;
|
|
294
|
-
}, {
|
|
295
|
-
expectedRevision: runtime.revision,
|
|
296
|
-
preCommit: async () => {
|
|
297
|
-
if (!(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))) {
|
|
298
|
-
throw new Error('direct-yolo-integration-lock-raced');
|
|
299
|
-
}
|
|
300
|
-
const persisted = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
|
|
301
|
-
if (!persisted || persisted.state !== 'issued' || persisted.grantDigest !== grantDigest
|
|
302
|
-
|| persisted.tokenDigest !== tokenDigest)
|
|
303
|
-
throw new Error('direct-yolo-setup-handoff-token-raced');
|
|
304
|
-
},
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
finally {
|
|
308
|
-
await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
|
|
309
|
-
}
|
|
310
|
-
return { ok: true, value: Object.freeze({
|
|
311
|
-
schemaVersion: 1, kind: 'direct-yolo-setup-handoff-receipt', storyId: options.storyId,
|
|
312
|
-
sessionId, bundleDigest, claimRevision, grantDigest, expiresAt, reused, publicSafe: true,
|
|
313
|
-
}) };
|
|
314
|
-
}
|
|
315
|
-
catch (error) {
|
|
316
|
-
return failure(error instanceof Error && /^direct-yolo-/.test(error.message)
|
|
317
|
-
? error.message : 'direct-yolo-setup-handoff-failed');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
export async function beginDirectYoloStoryCommit(options) {
|
|
321
|
-
try {
|
|
322
|
-
const authority = await resolveAuthority(options.projectRoot, options.storyId);
|
|
323
|
-
const baseSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
|
|
324
|
-
const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--cached', '--name-only', '-z']));
|
|
325
|
-
const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
|
|
326
|
-
if (!writeSet.ok)
|
|
327
|
-
return failure(writeSet.reasonCode);
|
|
328
|
-
if (authority.rootMemoryEffect)
|
|
329
|
-
verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch, baseSha, headRef: ':', childPaths: authority.authorizedWriteSet });
|
|
330
|
-
const stagedReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, ':');
|
|
331
|
-
if (stagedReview?.verdict !== 'approved')
|
|
332
|
-
return failure('direct-yolo-review-artifact-not-approved');
|
|
333
|
-
const unstagedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff', '--name-only', '-z']));
|
|
334
|
-
if (unstagedPaths.some((path) => changedPaths.includes(path)))
|
|
335
|
-
return failure('direct-yolo-user-overlap');
|
|
336
|
-
const payload = await gitRaw(authority.projectRoot, ['diff', '--cached', '--binary', '--full-index']);
|
|
337
|
-
if (!payload)
|
|
338
|
-
return failure('direct-yolo-staged-diff-empty');
|
|
339
|
-
const bundleDigest = digest(payload);
|
|
340
|
-
const claimRevision = digest([...authority.authorizedWriteSet].sort().join('\0'));
|
|
341
|
-
const idempotencyKey = digest([
|
|
342
|
-
'direct-yolo-commit-v1', options.storyId, authority.epicId, authority.executionBranch,
|
|
343
|
-
baseSha, bundleDigest, authority.graphRevision, claimRevision,
|
|
344
|
-
].join('\0'));
|
|
345
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
346
|
-
let issuedHandoff = options.setupLeaseHandoff
|
|
347
|
-
?? await resolveIssuedSetupLeaseHandoff(authority.projectRoot, options.storyId, bundleDigest, claimRevision, nowIso);
|
|
348
|
-
const runtimeBeforeLock = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
349
|
-
const priorDirectLease = asRecord(runtimeBeforeLock.epics[authority.epicId]?.branch_lease);
|
|
350
|
-
const reusableDirectLease = priorDirectLease?.ownerStoryId === options.storyId
|
|
351
|
-
&& priorDirectLease.sessionId === idempotencyKey && priorDirectLease.idempotencyKey === idempotencyKey
|
|
352
|
-
&& typeof priorDirectLease.expiresAt === 'string'
|
|
353
|
-
&& Date.parse(priorDirectLease.expiresAt) > Date.parse(nowIso)
|
|
354
|
-
? priorDirectLease : undefined;
|
|
355
|
-
if (reusableDirectLease && options.setupLeaseHandoff) {
|
|
356
|
-
return failure('direct-yolo-setup-handoff-replayed');
|
|
357
|
-
}
|
|
358
|
-
const priorConsumption = asRecord(reusableDirectLease?.handoffConsumption);
|
|
359
|
-
if (priorConsumption) {
|
|
360
|
-
const privateRecord = await readSetupHandoffTokenRecord(authority.projectRoot, options.storyId);
|
|
361
|
-
if (!privateRecord || !['consuming', 'consumed'].includes(privateRecord.state)
|
|
362
|
-
|| privateRecord.fromSessionId !== priorConsumption.fromSessionId
|
|
363
|
-
|| privateRecord.tokenDigest !== priorConsumption.tokenDigest
|
|
364
|
-
|| privateRecord.bundleDigest !== bundleDigest || privateRecord.claimRevision !== claimRevision
|
|
365
|
-
|| priorConsumption.bundleDigest !== bundleDigest || priorConsumption.claimRevision !== claimRevision
|
|
366
|
-
|| privateRecord.consumedBySessionId !== idempotencyKey
|
|
367
|
-
|| priorConsumption.consumedBySessionId !== idempotencyKey) {
|
|
368
|
-
return failure('direct-yolo-setup-handoff-consume-invalid');
|
|
369
|
-
}
|
|
370
|
-
// The runtime CAS already consumed this exact grant. Recovery may finish
|
|
371
|
-
// its private receipt after expiry; it never promotes a second writer.
|
|
372
|
-
if (privateRecord.state === 'consuming') {
|
|
373
|
-
if (!privateRecord.token || digest(privateRecord.token) !== privateRecord.tokenDigest) {
|
|
374
|
-
return failure('direct-yolo-setup-handoff-consume-invalid');
|
|
375
|
-
}
|
|
376
|
-
issuedHandoff = { sessionId: privateRecord.fromSessionId, token: privateRecord.token };
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
const expiresAt = reusableDirectLease
|
|
380
|
-
? String(reusableDirectLease.expiresAt)
|
|
381
|
-
: new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString();
|
|
382
|
-
const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
383
|
-
kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
|
|
384
|
-
expiresAt, persistent: true,
|
|
385
|
-
createdAt: reusableDirectLease && typeof reusableDirectLease.acquiredAt === 'string'
|
|
386
|
-
? reusableDirectLease.acquiredAt : nowIso,
|
|
387
|
-
});
|
|
388
|
-
if (!physicalLock.ok)
|
|
389
|
-
return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
|
|
390
|
-
? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
|
|
391
|
-
try {
|
|
392
|
-
if (issuedHandoff && options.setupLeaseHandoff === undefined) {
|
|
393
|
-
await markSetupHandoffTokenConsuming(authority.projectRoot, options.storyId, issuedHandoff.sessionId, issuedHandoff.token, idempotencyKey, nowIso);
|
|
394
|
-
await options.setupHandoffFault?.('after-consuming-persisted');
|
|
395
|
-
}
|
|
396
|
-
await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
397
|
-
const epic = ensureRuntimeEpic(document, authority.epicId);
|
|
398
|
-
const current = asRecord(epic.branch_lease);
|
|
399
|
-
const live = current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso);
|
|
400
|
-
const alreadyPromoted = current?.ownerStoryId === options.storyId
|
|
401
|
-
&& current.sessionId === idempotencyKey && current.idempotencyKey === idempotencyKey;
|
|
402
|
-
const sameStorySetup = current?.ownerStoryId === options.storyId
|
|
403
|
-
&& current.sessionId === current.idempotencyKey
|
|
404
|
-
&& typeof current.idempotencyKey === 'string'
|
|
405
|
-
&& /^setup-[a-f0-9]{64}$/.test(current.idempotencyKey);
|
|
406
|
-
const setupHandoffAuthorized = live && sameStorySetup && validSetupLeaseHandoff({
|
|
407
|
-
current,
|
|
408
|
-
handoff: issuedHandoff,
|
|
409
|
-
storyId: options.storyId,
|
|
410
|
-
bundleDigest,
|
|
411
|
-
claimRevision,
|
|
412
|
-
nowIso,
|
|
413
|
-
});
|
|
414
|
-
if (issuedHandoff && live && !alreadyPromoted && !setupHandoffAuthorized) {
|
|
415
|
-
throw new Error('direct-yolo-setup-handoff-invalid');
|
|
416
|
-
}
|
|
417
|
-
if (live && !alreadyPromoted && !setupHandoffAuthorized) {
|
|
418
|
-
throw new Error('direct-yolo-writer-active');
|
|
419
|
-
}
|
|
420
|
-
const setupGrant = setupHandoffAuthorized ? asRecord(current?.handoffGrant) : undefined;
|
|
421
|
-
const handoffConsumption = setupGrant ? {
|
|
422
|
-
schemaVersion: 1,
|
|
423
|
-
kind: 'direct-yolo-setup-handoff-consumption',
|
|
424
|
-
state: 'consumed',
|
|
425
|
-
storyId: options.storyId,
|
|
426
|
-
fromSessionId: String(current?.sessionId),
|
|
427
|
-
tokenDigest: String(setupGrant.tokenDigest),
|
|
428
|
-
bundleDigest,
|
|
429
|
-
claimRevision,
|
|
430
|
-
consumedBySessionId: idempotencyKey,
|
|
431
|
-
consumedAt: nowIso,
|
|
432
|
-
} : undefined;
|
|
433
|
-
epic.branch_mode = 'epic';
|
|
434
|
-
epic.execution_branch = authority.executionBranch;
|
|
435
|
-
epic.origin_branch = authority.originBranch;
|
|
436
|
-
epic.graph_revision = authority.graphRevision;
|
|
437
|
-
epic.branch_lease = live && alreadyPromoted ? current : {
|
|
438
|
-
schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
|
|
439
|
-
idempotencyKey, acquiredAt: nowIso, expiresAt,
|
|
440
|
-
...(handoffConsumption ? { handoffConsumption } : {}),
|
|
441
|
-
};
|
|
442
|
-
}, {
|
|
443
|
-
expectedRevision: runtimeBeforeLock.revision,
|
|
444
|
-
preCommit: async () => {
|
|
445
|
-
if (!(await verifyGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner))) {
|
|
446
|
-
throw new Error('direct-yolo-integration-lock-raced');
|
|
447
|
-
}
|
|
448
|
-
},
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
catch (error) {
|
|
452
|
-
await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
|
|
453
|
-
throw error;
|
|
454
|
-
}
|
|
455
|
-
if (issuedHandoff && options.setupLeaseHandoff === undefined) {
|
|
456
|
-
await options.setupHandoffFault?.('before-consumed-persist');
|
|
457
|
-
await markSetupHandoffTokenConsumed(authority.projectRoot, authority.epicId, options.storyId, issuedHandoff.sessionId, issuedHandoff.token, bundleDigest, claimRevision, idempotencyKey, nowIso);
|
|
458
|
-
}
|
|
459
|
-
const trailers = Object.freeze([
|
|
460
|
-
`Neocortex-Story: ${options.storyId}`,
|
|
461
|
-
`Neocortex-Epic: ${authority.epicId}`,
|
|
462
|
-
`Neocortex-Execution-Branch: ${authority.executionBranch}`,
|
|
463
|
-
`Neocortex-Prepared-Base: ${baseSha}`,
|
|
464
|
-
`Neocortex-Bundle-Digest: ${bundleDigest}`,
|
|
465
|
-
`Neocortex-Review-Artifact-Digest: ${stagedReview.digest}`,
|
|
466
|
-
`Neocortex-Graph-Revision: ${authority.graphRevision}`,
|
|
467
|
-
`Neocortex-Claim-Revision: ${claimRevision}`,
|
|
468
|
-
...(authority.rootMemoryEffect ? [`Neocortex-Root-Memory-Effect: ${rootMemoryEffectDigest(authority.rootMemoryEffect)}`] : []),
|
|
469
|
-
]);
|
|
470
|
-
return { ok: true, value: Object.freeze({
|
|
471
|
-
schemaVersion: 1, storyId: options.storyId, epicId: authority.epicId,
|
|
472
|
-
executionBranch: authority.executionBranch, baseSha, bundleDigest,
|
|
473
|
-
reviewArtifactDigest: stagedReview.digest,
|
|
474
|
-
graphRevision: authority.graphRevision, claimRevision, idempotencyKey,
|
|
475
|
-
changedPaths: Object.freeze(changedPaths), trailers,
|
|
476
|
-
}) };
|
|
477
|
-
}
|
|
478
|
-
catch (error) {
|
|
479
|
-
return failure(publicReason(error, 'direct-yolo-begin-failed'));
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
export async function finalizeDirectYoloStoryCheckpoint(options) {
|
|
483
|
-
let cleanupAuthority;
|
|
484
|
-
let cleanupLease;
|
|
485
|
-
let cleanupPhysical;
|
|
486
|
-
try {
|
|
487
|
-
const authority = await resolveAuthority(options.projectRoot, options.storyId);
|
|
488
|
-
cleanupAuthority = authority;
|
|
489
|
-
const aggregateHeadSha = await git(authority.projectRoot, ['rev-parse', 'HEAD']);
|
|
490
|
-
let runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
491
|
-
const existing = asRecord(runtime.stories[options.storyId]?.checkpoint);
|
|
492
|
-
if (existing) {
|
|
493
|
-
const checkpointHeadSha = typeof existing.headSha === 'string' ? existing.headSha : '';
|
|
494
|
-
const checkpointGraphRevision = typeof existing.graphRevision === 'string' ? existing.graphRevision : '';
|
|
495
|
-
const checkpointPaths = /^[a-f0-9]{40,64}$/.test(checkpointHeadSha)
|
|
496
|
-
? splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', checkpointHeadSha]))
|
|
497
|
-
: [];
|
|
498
|
-
const replayWriteSet = validateDirectWriteSet(checkpointPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
|
|
499
|
-
const ancestral = /^[a-f0-9]{40,64}$/.test(checkpointHeadSha)
|
|
500
|
-
&& await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', checkpointHeadSha, aggregateHeadSha]);
|
|
501
|
-
if (!ancestral || !/^[a-f0-9]{64}$/.test(checkpointGraphRevision) || !replayWriteSet.ok) {
|
|
502
|
-
return failure('direct-yolo-replay-invalid');
|
|
503
|
-
}
|
|
504
|
-
const replayQa = await runQa(options, authority.projectRoot, aggregateHeadSha, checkpointGraphRevision);
|
|
505
|
-
if (!(await verifyAcceptedGitLifecycleStoryCheckpoint(authority.projectRoot, existing, options.storyId, authority.epicId, authority.executionBranch, replayQa.transientAuthority, authority.authorizedWriteSet)))
|
|
506
|
-
return failure('direct-yolo-replay-invalid');
|
|
507
|
-
if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, aggregateHeadSha, checkpointPaths)))
|
|
508
|
-
return failure('direct-yolo-replay-head-raced');
|
|
509
|
-
if (options.push && !(await remoteContainsHead(authority.projectRoot, options.remote ?? 'origin', authority.executionBranch, checkpointHeadSha)))
|
|
510
|
-
return failure('direct-yolo-remote-head-mismatch');
|
|
511
|
-
const physicalOwnerRef = Array.isArray(existing.receiptRefs)
|
|
512
|
-
? existing.receiptRefs.find((item) => typeof item === 'string' && /^physical-lock-owner:[a-f0-9]{64}$/.test(item))
|
|
513
|
-
: undefined;
|
|
514
|
-
if (physicalOwnerRef && typeof existing.idempotencyKey === 'string') {
|
|
515
|
-
await releaseGitLifecyclePhysicalIntegrationLockForCheckpoint(authority.projectRoot, {
|
|
516
|
-
storyId: options.storyId,
|
|
517
|
-
idempotencyKey: existing.idempotencyKey,
|
|
518
|
-
ownerDigest: physicalOwnerRef.slice('physical-lock-owner:'.length),
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
return { ok: true, value: existing };
|
|
522
|
-
}
|
|
523
|
-
const headSha = aggregateHeadSha;
|
|
524
|
-
const baseSha = await git(authority.projectRoot, ['rev-parse', `${headSha}^`]);
|
|
525
|
-
const commitBody = await gitRaw(authority.projectRoot, ['show', '--no-patch', '--format=%B', headSha]);
|
|
526
|
-
const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', headSha]));
|
|
527
|
-
const writeSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet, options.storyId, authority.rootMemoryEffect);
|
|
528
|
-
if (!writeSet.ok)
|
|
529
|
-
return failure(writeSet.reasonCode);
|
|
530
|
-
if (authority.rootMemoryEffect)
|
|
531
|
-
verifyRootMemoryPhysicalWriteSet(authority.projectRoot, authority.rootMemoryEffect, { storyId: options.storyId, executionBranch: authority.executionBranch, baseSha, headRef: headSha, childPaths: authority.authorizedWriteSet });
|
|
532
|
-
const bundleDigest = trailer(commitBody, 'Neocortex-Bundle-Digest');
|
|
533
|
-
const graphRevision = trailer(commitBody, 'Neocortex-Graph-Revision');
|
|
534
|
-
const claimRevision = trailer(commitBody, 'Neocortex-Claim-Revision');
|
|
535
|
-
const required = new Map([
|
|
536
|
-
['Neocortex-Story', options.storyId], ['Neocortex-Epic', authority.epicId],
|
|
537
|
-
['Neocortex-Execution-Branch', authority.executionBranch], ['Neocortex-Prepared-Base', baseSha],
|
|
538
|
-
['Neocortex-Graph-Revision', authority.graphRevision],
|
|
539
|
-
['Neocortex-Claim-Revision', digest([...authority.authorizedWriteSet].sort().join('\0'))],
|
|
540
|
-
]);
|
|
541
|
-
if (!bundleDigest || !/^[a-f0-9]{64}$/.test(bundleDigest) || !graphRevision || !claimRevision
|
|
542
|
-
|| [...required].some(([key, value]) => trailer(commitBody, key) !== value)) {
|
|
543
|
-
return failure('direct-yolo-trailers-invalid');
|
|
544
|
-
}
|
|
545
|
-
const publicReview = publicStoryReviewArtifactFromGit(authority.projectRoot, options.storyId, headSha);
|
|
546
|
-
if (publicReview?.verdict !== 'approved')
|
|
547
|
-
return failure('direct-yolo-review-artifact-not-approved');
|
|
548
|
-
const reviewTrailer = trailer(commitBody, 'Neocortex-Review-Artifact-Digest');
|
|
549
|
-
if (reviewTrailer !== undefined && reviewTrailer !== publicReview.digest) {
|
|
550
|
-
return failure('direct-yolo-review-artifact-digest-mismatch');
|
|
551
|
-
}
|
|
552
|
-
const expectedPayloadDigest = digest(await gitRaw(authority.projectRoot, ['diff', '--binary', '--full-index', `${baseSha}..${headSha}`]));
|
|
553
|
-
if (bundleDigest !== expectedPayloadDigest)
|
|
554
|
-
return failure('direct-yolo-bundle-digest-mismatch');
|
|
555
|
-
const idempotencyKey = digest([
|
|
556
|
-
'direct-yolo-commit-v1', options.storyId, authority.epicId, authority.executionBranch,
|
|
557
|
-
baseSha, bundleDigest, graphRevision, claimRevision,
|
|
558
|
-
].join('\0'));
|
|
559
|
-
const currentLease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
|
|
560
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
561
|
-
const matchingLease = currentLease?.ownerStoryId === options.storyId
|
|
562
|
-
&& currentLease.idempotencyKey === idempotencyKey
|
|
563
|
-
&& currentLease.sessionId === idempotencyKey
|
|
564
|
-
&& Date.parse(String(currentLease.expiresAt)) > Date.now()
|
|
565
|
-
? currentLease : undefined;
|
|
566
|
-
if (currentLease && Date.parse(String(currentLease.expiresAt)) > Date.now() && !matchingLease) {
|
|
567
|
-
return failure('direct-yolo-writer-active');
|
|
568
|
-
}
|
|
569
|
-
const requestedLease = matchingLease ?? {
|
|
570
|
-
schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
|
|
571
|
-
idempotencyKey, acquiredAt: nowIso,
|
|
572
|
-
expiresAt: new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString(),
|
|
573
|
-
};
|
|
574
|
-
const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
575
|
-
kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
|
|
576
|
-
expiresAt: String(requestedLease.expiresAt), persistent: true,
|
|
577
|
-
createdAt: String(requestedLease.acquiredAt),
|
|
578
|
-
});
|
|
579
|
-
if (!physicalLock.ok)
|
|
580
|
-
return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
|
|
581
|
-
? 'direct-yolo-integration-backpressure' : 'direct-yolo-integration-lock-failed');
|
|
582
|
-
cleanupPhysical = physicalLock;
|
|
583
|
-
if (!matchingLease) {
|
|
584
|
-
const leased = await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
585
|
-
const epic = ensureRuntimeEpic(document, authority.epicId);
|
|
586
|
-
const current = asRecord(epic.branch_lease);
|
|
587
|
-
if (current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso)) {
|
|
588
|
-
throw new Error('direct-yolo-writer-active');
|
|
589
|
-
}
|
|
590
|
-
if (epic.graph_revision !== authority.graphRevision)
|
|
591
|
-
throw new Error('direct-yolo-graph-revision-raced');
|
|
592
|
-
epic.branch_lease = requestedLease;
|
|
593
|
-
}, { expectedRevision: runtime.revision });
|
|
594
|
-
runtime = leased.document;
|
|
595
|
-
}
|
|
596
|
-
cleanupLease = requestedLease;
|
|
597
|
-
{
|
|
598
|
-
const qaExecution = await runQa(options, authority.projectRoot, headSha, authority.graphRevision);
|
|
599
|
-
if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, headSha, changedPaths))) {
|
|
600
|
-
return failure('direct-yolo-head-raced');
|
|
601
|
-
}
|
|
602
|
-
if (options.push) {
|
|
603
|
-
const remote = safeRemote(options.remote ?? 'origin');
|
|
604
|
-
await git(authority.projectRoot, ['push', remote, authority.executionBranch]);
|
|
605
|
-
if (!(await remoteHeadIsExact(authority.projectRoot, remote, authority.executionBranch, headSha))) {
|
|
606
|
-
return failure('direct-yolo-remote-head-mismatch');
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const checkpoint = await buildCheckpoint({
|
|
610
|
-
projectRoot: authority.projectRoot, storyId: options.storyId, epicId: authority.epicId,
|
|
611
|
-
executionBranch: authority.executionBranch, baseSha, headSha, bundleDigest,
|
|
612
|
-
graphRevision, claimRevision, idempotencyKey, qaExecution,
|
|
613
|
-
physicalLockOwnerDigest: computeGitLifecyclePhysicalIntegrationLockOwnerDigest(physicalLock.owner),
|
|
614
|
-
nowIso: normalizedTime(options.nowIso), legacyReceiptRef: undefined, rootMemoryEffect: authority.rootMemoryEffect,
|
|
615
|
-
});
|
|
616
|
-
await options.beforeCheckpointPersist?.();
|
|
617
|
-
const persisted = await persistCheckpoint({
|
|
618
|
-
projectRoot: authority.projectRoot, checkpoint,
|
|
619
|
-
remote: options.push ? options.remote ?? 'origin' : undefined,
|
|
620
|
-
expectedRevision: runtime.revision, expectedLease: requestedLease,
|
|
621
|
-
expectedGraphRevision: authority.graphRevision,
|
|
622
|
-
physicalLockPath: physicalLock.path, physicalLockOwner: physicalLock.owner,
|
|
623
|
-
transientAuthority: qaExecution.transientAuthority,
|
|
624
|
-
authorizedWriteSet: authority.authorizedWriteSet,
|
|
625
|
-
});
|
|
626
|
-
if (!persisted)
|
|
627
|
-
return failure('direct-yolo-checkpoint-persist-failed');
|
|
628
|
-
await options.afterCheckpointPersist?.();
|
|
629
|
-
return { ok: true, value: checkpoint };
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
catch (error) {
|
|
633
|
-
return failure(publicReason(error, 'direct-yolo-finalize-failed'));
|
|
634
|
-
}
|
|
635
|
-
finally {
|
|
636
|
-
if (cleanupPhysical) {
|
|
637
|
-
await releaseGitLifecyclePhysicalIntegrationLock(cleanupPhysical.path, cleanupPhysical.owner).catch(() => undefined);
|
|
638
|
-
}
|
|
639
|
-
if (cleanupAuthority && cleanupLease) {
|
|
640
|
-
await releaseOwnedDirectLease(cleanupAuthority.projectRoot, cleanupAuthority.epicId, cleanupLease).catch(() => undefined);
|
|
641
|
-
}
|
|
642
|
-
else if (cleanupAuthority) {
|
|
643
|
-
await cleanupOwnedDirectAuthority(cleanupAuthority, options.storyId).catch(() => undefined);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
export async function recoverLegacyDirectYoloStoryCheckpoint(options) {
|
|
648
|
-
try {
|
|
649
|
-
const authority = await resolveAuthority(options.projectRoot, options.storyId);
|
|
650
|
-
if (!/^[a-f0-9]{40,64}$/.test(options.commitSha))
|
|
651
|
-
return failure('direct-yolo-recovery-commit-invalid');
|
|
652
|
-
const headSha = await git(authority.projectRoot, ['rev-parse', options.commitSha]);
|
|
653
|
-
if (LEGACY_DIRECT_YOLO_RECOVERY_COMMITS[options.storyId] !== headSha) {
|
|
654
|
-
return failure('direct-yolo-recovery-not-allowlisted');
|
|
655
|
-
}
|
|
656
|
-
if (await git(authority.projectRoot, ['rev-parse', 'HEAD']) !== headSha) {
|
|
657
|
-
return failure('direct-yolo-recovery-head-mismatch');
|
|
658
|
-
}
|
|
659
|
-
await git(authority.projectRoot, ['merge-base', '--is-ancestor', headSha, `refs/heads/${authority.executionBranch}`]);
|
|
660
|
-
const baseSha = await git(authority.projectRoot, ['rev-parse', `${headSha}^`]);
|
|
661
|
-
const payload = await gitRaw(authority.projectRoot, ['diff', '--binary', '--full-index', `${baseSha}..${headSha}`]);
|
|
662
|
-
const changedPaths = splitNull(await gitRaw(authority.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', headSha]));
|
|
663
|
-
const recoveryWriteSet = validateDirectWriteSet(changedPaths, authority.authorizedWriteSet.length > 0 ? authority.authorizedWriteSet : changedPaths, options.storyId);
|
|
664
|
-
if (!recoveryWriteSet.ok)
|
|
665
|
-
return failure('direct-yolo-recovery-write-set-invalid');
|
|
666
|
-
const bundleDigest = digest(payload);
|
|
667
|
-
const claimRevision = digest([...changedPaths].sort().join('\0'));
|
|
668
|
-
const idempotencyKey = digest(['legacy-direct-yolo-recovery-v1', options.storyId, headSha].join('\0'));
|
|
669
|
-
const nowIso = normalizedTime(options.nowIso);
|
|
670
|
-
const expiresAt = new Date(Date.parse(nowIso) + 30 * 60 * 1000).toISOString();
|
|
671
|
-
const initialRuntime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
672
|
-
const physicalLock = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
673
|
-
kind: 'direct', storyId: options.storyId, sessionId: idempotencyKey, idempotencyKey,
|
|
674
|
-
expiresAt, persistent: true, createdAt: nowIso,
|
|
675
|
-
});
|
|
676
|
-
if (!physicalLock.ok)
|
|
677
|
-
return failure(physicalLock.reasonCode === 'git-lifecycle-integration-backpressure'
|
|
678
|
-
? 'direct-yolo-recovery-integration-backpressure' : 'direct-yolo-recovery-integration-lock-failed');
|
|
679
|
-
const requestedLease = {
|
|
680
|
-
schemaVersion: 1, ownerStoryId: options.storyId, sessionId: idempotencyKey,
|
|
681
|
-
idempotencyKey, acquiredAt: nowIso, expiresAt,
|
|
682
|
-
};
|
|
683
|
-
let leaseAcquired = false;
|
|
684
|
-
try {
|
|
685
|
-
const leased = await mutateGitLifecycleRuntime(authority.projectRoot, (document) => {
|
|
686
|
-
const epic = ensureRuntimeEpic(document, authority.epicId);
|
|
687
|
-
const current = asRecord(epic.branch_lease);
|
|
688
|
-
if (current && Date.parse(String(current.expiresAt)) > Date.parse(nowIso)
|
|
689
|
-
&& !sameLease(current, requestedLease))
|
|
690
|
-
throw new Error('direct-yolo-recovery-writer-active');
|
|
691
|
-
if (epic.graph_revision !== authority.graphRevision)
|
|
692
|
-
throw new Error('direct-yolo-recovery-graph-raced');
|
|
693
|
-
epic.branch_mode = 'epic';
|
|
694
|
-
epic.execution_branch = authority.executionBranch;
|
|
695
|
-
epic.origin_branch = authority.originBranch;
|
|
696
|
-
epic.branch_lease = requestedLease;
|
|
697
|
-
}, { expectedRevision: initialRuntime.revision });
|
|
698
|
-
leaseAcquired = true;
|
|
699
|
-
const qaExecution = await runQa(options, authority.projectRoot, headSha, authority.graphRevision);
|
|
700
|
-
if (!(await physicalCheckoutUnchanged(authority.projectRoot, authority.executionBranch, headSha, changedPaths))) {
|
|
701
|
-
return failure('direct-yolo-recovery-head-raced');
|
|
702
|
-
}
|
|
703
|
-
if (options.push) {
|
|
704
|
-
const remote = safeRemote(options.remote ?? 'origin');
|
|
705
|
-
const remoteHead = (await git(authority.projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${authority.executionBranch}`])).split(/\s+/)[0];
|
|
706
|
-
if (!remoteHead || !(await gitExitOk(authority.projectRoot, ['merge-base', '--is-ancestor', headSha, remoteHead]))) {
|
|
707
|
-
return failure('direct-yolo-recovery-remote-proof-missing');
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
const provisional = await buildCheckpoint({
|
|
711
|
-
projectRoot: authority.projectRoot, storyId: options.storyId, epicId: authority.epicId,
|
|
712
|
-
executionBranch: authority.executionBranch, baseSha, headSha, bundleDigest,
|
|
713
|
-
graphRevision: authority.graphRevision, claimRevision, idempotencyKey,
|
|
714
|
-
qaExecution,
|
|
715
|
-
physicalLockOwnerDigest: computeGitLifecyclePhysicalIntegrationLockOwnerDigest(physicalLock.owner),
|
|
716
|
-
nowIso, legacyReceiptRef: undefined,
|
|
717
|
-
});
|
|
718
|
-
const legacyReceiptRef = await computeLegacyDirectYoloRecoveryReceiptRef(authority.projectRoot, {
|
|
719
|
-
storyId: options.storyId,
|
|
720
|
-
epicId: authority.epicId,
|
|
721
|
-
executionBranch: authority.executionBranch,
|
|
722
|
-
baseSha,
|
|
723
|
-
headSha,
|
|
724
|
-
bundleDigest,
|
|
725
|
-
graphRevision: authority.graphRevision,
|
|
726
|
-
});
|
|
727
|
-
if (!legacyReceiptRef)
|
|
728
|
-
return failure('direct-yolo-recovery-evidence-invalid');
|
|
729
|
-
const checkpoint = Object.freeze({
|
|
730
|
-
...provisional,
|
|
731
|
-
receiptRefs: Object.freeze([...provisional.receiptRefs, legacyReceiptRef]),
|
|
732
|
-
});
|
|
733
|
-
const persisted = await persistCheckpoint({
|
|
734
|
-
projectRoot: authority.projectRoot, checkpoint,
|
|
735
|
-
remote: options.push ? options.remote ?? 'origin' : undefined,
|
|
736
|
-
remotePolicy: 'ancestor-recovery',
|
|
737
|
-
expectedRevision: leased.document.revision,
|
|
738
|
-
expectedLease: requestedLease,
|
|
739
|
-
expectedGraphRevision: authority.graphRevision,
|
|
740
|
-
physicalLockPath: physicalLock.path,
|
|
741
|
-
physicalLockOwner: physicalLock.owner,
|
|
742
|
-
transientAuthority: qaExecution.transientAuthority,
|
|
743
|
-
authorizedWriteSet: authority.authorizedWriteSet,
|
|
744
|
-
});
|
|
745
|
-
return persisted ? { ok: true, value: checkpoint } : failure('direct-yolo-recovery-persist-failed');
|
|
746
|
-
}
|
|
747
|
-
finally {
|
|
748
|
-
await releaseGitLifecyclePhysicalIntegrationLock(physicalLock.path, physicalLock.owner).catch(() => undefined);
|
|
749
|
-
if (leaseAcquired)
|
|
750
|
-
await releaseOwnedDirectLease(authority.projectRoot, authority.epicId, requestedLease).catch(() => undefined);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
catch (error) {
|
|
754
|
-
return failure(publicReason(error, 'direct-yolo-recovery-failed'));
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
async function resolveLogicalAuthority(projectRootInput, storyId) {
|
|
758
|
-
if (!/^[A-Za-z][A-Za-z0-9_-]*\.[A-Za-z0-9._-]+$/.test(storyId))
|
|
759
|
-
throw new Error('direct-yolo-story-id-invalid');
|
|
760
|
-
const projectRoot = resolve(projectRootInput);
|
|
761
|
-
const tracked = asRecord(JSON.parse(await readFile(join(projectRoot, '.neocortex/state.json'), 'utf8')));
|
|
762
|
-
const runtime = await readGitLifecycleRuntime(projectRoot, { required: true });
|
|
763
|
-
const effective = applyGitLifecycleRuntimeOverlay(tracked, runtime);
|
|
764
|
-
const stories = asRecord(effective.stories);
|
|
765
|
-
const epics = asRecord(effective.epics);
|
|
766
|
-
const story = asRecord(stories?.[storyId]);
|
|
767
|
-
const epicId = String(story?.epic_id ?? storyId.split('.')[0]);
|
|
768
|
-
const epic = asRecord(epics?.[epicId]);
|
|
769
|
-
const executionBranch = String(epic?.execution_branch ?? '');
|
|
770
|
-
const originBranch = String(epic?.origin_branch ?? '');
|
|
771
|
-
const graphRevision = String(epic?.graph_revision ?? '');
|
|
772
|
-
const authorizedWriteSet = Array.isArray(story?.files_to_modify)
|
|
773
|
-
? [...new Set(story.files_to_modify.filter((item) => typeof item === 'string' && safePath(item)))].sort()
|
|
774
|
-
: [];
|
|
775
|
-
if (!story)
|
|
776
|
-
throw new Error('direct-yolo-story-state-missing');
|
|
777
|
-
if (epic?.branch_mode !== 'epic')
|
|
778
|
-
throw new Error('direct-yolo-branch-mode-invalid');
|
|
779
|
-
if (!safeRef(executionBranch)
|
|
780
|
-
|| !(await gitExitOk(projectRoot, ['check-ref-format', '--branch', executionBranch]))) {
|
|
781
|
-
throw new Error('direct-yolo-execution-branch-invalid');
|
|
782
|
-
}
|
|
783
|
-
if (!safeRef(originBranch)
|
|
784
|
-
|| !(await gitExitOk(projectRoot, ['check-ref-format', '--branch', originBranch]))) {
|
|
785
|
-
throw new Error('direct-yolo-origin-branch-invalid');
|
|
786
|
-
}
|
|
787
|
-
if (!/^[a-f0-9]{64}$/.test(graphRevision))
|
|
788
|
-
throw new Error('direct-yolo-graph-revision-invalid');
|
|
789
|
-
const rootMemoryEffect = await readRootMemoryEffectForCheckpoint({ projectRoot, storyId, graphRevision,
|
|
790
|
-
executionBranch, childPaths: authorizedWriteSet });
|
|
791
|
-
return { projectRoot, storyId, story, epicId, executionBranch, originBranch, graphRevision, authorizedWriteSet, rootMemoryEffect };
|
|
792
|
-
}
|
|
793
|
-
async function resolveAuthority(projectRootInput, storyId) {
|
|
794
|
-
const authority = await resolveLogicalAuthority(projectRootInput, storyId);
|
|
795
|
-
const currentBranch = await git(authority.projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
796
|
-
if (currentBranch !== authority.executionBranch)
|
|
797
|
-
throw new Error('direct-yolo-current-branch-mismatch');
|
|
798
|
-
return authority;
|
|
799
|
-
}
|
|
800
|
-
async function buildCheckpoint(input) {
|
|
801
|
-
const checkedAt = input.nowIso;
|
|
802
|
-
const commitBody = await gitRaw(input.projectRoot, ['show', '--no-patch', '--format=%H%n%P%n%B', input.headSha]);
|
|
803
|
-
const changedPaths = splitNull(await gitRaw(input.projectRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', '-z', input.headSha]));
|
|
804
|
-
const gitEvidenceDigest = computeImmutableStoryQaEvidenceDigest({
|
|
805
|
-
storyId: input.storyId, baseSha: input.baseSha, headSha: input.headSha,
|
|
806
|
-
bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
|
|
807
|
-
commitBody, changedPaths,
|
|
808
|
-
});
|
|
809
|
-
const qaDigest = computeP205StoryQaEvidenceDigest({
|
|
810
|
-
gitEvidenceDigest, headSha: input.headSha, graphRevision: input.graphRevision,
|
|
811
|
-
manifestDigest: input.qaExecution.manifestDigest, gateIds: input.qaExecution.gateIds,
|
|
812
|
-
resultDigest: input.qaExecution.resultDigest,
|
|
813
|
-
});
|
|
814
|
-
const qaReceiptRef = `story-qa:${qaDigest}`;
|
|
815
|
-
const qaReceipt = Object.freeze({
|
|
816
|
-
schemaVersion: 1, kind: 'story_qa_evidence', producer: 'local-yolo-tdd', evidenceVersion: 'p205-execution-v2',
|
|
817
|
-
storyId: input.storyId, epicId: input.epicId, baseSha: input.baseSha, headSha: input.headSha,
|
|
818
|
-
bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
|
|
819
|
-
contract: input.qaExecution.contract, command: input.qaExecution.command,
|
|
820
|
-
manifestDigest: input.qaExecution.manifestDigest, gateIds: input.qaExecution.gateIds,
|
|
821
|
-
resultDigest: input.qaExecution.resultDigest,
|
|
822
|
-
sourceEvidenceDigest: qaDigest, verdict: 'passed', checkedAt, receiptRef: qaReceiptRef,
|
|
823
|
-
});
|
|
824
|
-
const publicReview = publicStoryReviewArtifactFromGit(input.projectRoot, input.storyId, input.headSha);
|
|
825
|
-
if (publicReview?.verdict !== 'approved')
|
|
826
|
-
throw new Error('direct-yolo-review-artifact-not-approved');
|
|
827
|
-
const reviewDigest = digest([
|
|
828
|
-
'root-commit-range-review-v2', input.storyId, input.baseSha, input.headSha,
|
|
829
|
-
input.bundleDigest, input.graphRevision, qaReceiptRef, digest(commitBody), [...changedPaths].sort().join(','),
|
|
830
|
-
publicReview.digest,
|
|
831
|
-
].join('\0'));
|
|
832
|
-
const reviewReceiptRef = `story-review:${reviewDigest}`;
|
|
833
|
-
const reviewReceipt = Object.freeze({
|
|
834
|
-
schemaVersion: 1, kind: 'story_review_evidence', producer: 'root-commit-range-review',
|
|
835
|
-
storyId: input.storyId, epicId: input.epicId, baseSha: input.baseSha, headSha: input.headSha,
|
|
836
|
-
bundleDigest: input.bundleDigest, graphRevision: input.graphRevision,
|
|
837
|
-
sourceEvidenceDigest: reviewDigest, reviewArtifactDigest: publicReview.digest,
|
|
838
|
-
qaReceiptRef, verdict: 'approved', reviewedAt: checkedAt,
|
|
839
|
-
receiptRef: reviewReceiptRef,
|
|
840
|
-
});
|
|
841
|
-
const evidenceRefs = [
|
|
842
|
-
`.neocortex/stories/${input.storyId}/qa-gate-result.json`,
|
|
843
|
-
`.neocortex/stories/${input.storyId}/validation-evidence.md`, qaReceiptRef, reviewReceiptRef,
|
|
844
|
-
...(input.physicalLockOwnerDigest ? [`physical-lock-owner:${input.physicalLockOwnerDigest}`] : []),
|
|
845
|
-
...(input.legacyReceiptRef ? [input.legacyReceiptRef] : []),
|
|
846
|
-
];
|
|
847
|
-
return Object.freeze({
|
|
848
|
-
schemaVersion: 1, storyId: input.storyId, epicId: input.epicId, branch: input.executionBranch,
|
|
849
|
-
baseSha: input.baseSha, headSha: input.headSha, commits: Object.freeze([input.headSha]),
|
|
850
|
-
idempotencyKey: input.idempotencyKey, bundleDigest: input.bundleDigest,
|
|
851
|
-
graphRevision: input.graphRevision, claimRevision: input.claimRevision,
|
|
852
|
-
receiptRefs: Object.freeze(evidenceRefs), qaVerdict: 'passed', reviewVerdict: 'approved',
|
|
853
|
-
qaReceipt, reviewReceipt, recordedAt: checkedAt,
|
|
854
|
-
...(input.rootMemoryEffect ? { rootMemoryEffect: input.rootMemoryEffect } : {}),
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
async function persistCheckpoint(input) {
|
|
858
|
-
const { projectRoot, checkpoint, remote } = input;
|
|
859
|
-
let conflict = false;
|
|
860
|
-
await mutateGitLifecycleRuntime(projectRoot, (document) => {
|
|
861
|
-
const epic = ensureRuntimeEpic(document, checkpoint.epicId);
|
|
862
|
-
if (input.expectedLease) {
|
|
863
|
-
const currentLease = asRecord(epic.branch_lease);
|
|
864
|
-
if (!sameLease(currentLease, input.expectedLease))
|
|
865
|
-
throw new Error('direct-yolo-runtime-revision-conflict');
|
|
866
|
-
if (Date.parse(String(currentLease?.expiresAt)) <= Date.now())
|
|
867
|
-
throw new Error('direct-yolo-writer-lease-expired');
|
|
868
|
-
if (epic.graph_revision !== input.expectedGraphRevision || checkpoint.graphRevision !== input.expectedGraphRevision) {
|
|
869
|
-
throw new Error('direct-yolo-graph-revision-raced');
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
const story = ensureRuntimeStory(document, checkpoint.storyId);
|
|
873
|
-
const existing = asRecord(story.checkpoint);
|
|
874
|
-
if (existing && existing.headSha !== checkpoint.headSha) {
|
|
875
|
-
conflict = true;
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
story.checkpoint = checkpoint;
|
|
879
|
-
story.status = 'done';
|
|
880
|
-
story.last_step = GIT_LIFECYCLE_REVIEW_PR_STEP_ID;
|
|
881
|
-
const steps = Array.isArray(story.steps_completed) ? story.steps_completed.filter((item) => typeof item === 'string') : [];
|
|
882
|
-
story.steps_completed = [...new Set([...steps, GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID, GIT_LIFECYCLE_REVIEW_PR_STEP_ID])];
|
|
883
|
-
epic.branch_lease = null;
|
|
884
|
-
}, { expectedRevision: input.expectedRevision, preCommit: async () => {
|
|
885
|
-
if (conflict)
|
|
886
|
-
throw new Error('direct-yolo-checkpoint-conflict');
|
|
887
|
-
const currentBranch = await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
888
|
-
if (currentBranch !== checkpoint.branch || await git(projectRoot, ['rev-parse', 'HEAD']) !== checkpoint.headSha) {
|
|
889
|
-
throw new Error('direct-yolo-checkpoint-head-raced');
|
|
890
|
-
}
|
|
891
|
-
if (!(await verifyAcceptedGitLifecycleStoryCheckpoint(projectRoot, checkpoint, checkpoint.storyId, checkpoint.epicId, checkpoint.branch, input.transientAuthority, input.authorizedWriteSet)))
|
|
892
|
-
throw new Error('direct-yolo-checkpoint-physical-invalid');
|
|
893
|
-
if (input.physicalLockPath && input.physicalLockOwner
|
|
894
|
-
&& !(await verifyGitLifecyclePhysicalIntegrationLock(input.physicalLockPath, input.physicalLockOwner))) {
|
|
895
|
-
throw new Error('direct-yolo-integration-lock-raced');
|
|
896
|
-
}
|
|
897
|
-
if (remote) {
|
|
898
|
-
const accepted = input.remotePolicy === 'ancestor-recovery'
|
|
899
|
-
? await remoteContainsHead(projectRoot, remote, checkpoint.branch, checkpoint.headSha)
|
|
900
|
-
: await remoteHeadIsExact(projectRoot, remote, checkpoint.branch, checkpoint.headSha);
|
|
901
|
-
if (!accepted) {
|
|
902
|
-
throw new Error('direct-yolo-checkpoint-remote-raced');
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
} });
|
|
906
|
-
return !conflict;
|
|
907
|
-
}
|
|
908
|
-
async function remoteContainsHead(projectRoot, remoteInput, branch, headSha) {
|
|
909
|
-
const remote = safeRemote(remoteInput);
|
|
910
|
-
const remoteHead = (await git(projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${branch}`])).split(/\s+/)[0];
|
|
911
|
-
return Boolean(remoteHead && await gitExitOk(projectRoot, ['merge-base', '--is-ancestor', headSha, remoteHead]));
|
|
912
|
-
}
|
|
913
|
-
function isLiveSameStorySetupLease(lease, storyId, nowIso) {
|
|
914
|
-
return Boolean(lease?.ownerStoryId === storyId
|
|
915
|
-
&& lease.sessionId === lease.idempotencyKey
|
|
916
|
-
&& typeof lease.idempotencyKey === 'string'
|
|
917
|
-
&& /^setup-[a-f0-9]{64}$/.test(lease.idempotencyKey)
|
|
918
|
-
&& typeof lease.expiresAt === 'string'
|
|
919
|
-
&& Date.parse(lease.expiresAt) > Date.parse(nowIso));
|
|
920
|
-
}
|
|
921
|
-
async function setupHandoffTokenLocation(projectRoot, storyId) {
|
|
922
|
-
const runtimeDirectory = await gitLifecycleRuntimeDirectory(projectRoot);
|
|
923
|
-
const parent = await ensureGitLifecyclePrivateChildDirectory(runtimeDirectory, 'handoffs');
|
|
924
|
-
const name = `direct-${digest(storyId)}.json`;
|
|
925
|
-
return { parent, name, path: join(parent, name) };
|
|
926
|
-
}
|
|
927
|
-
async function writeSetupHandoffTokenRecord(projectRoot, record) {
|
|
928
|
-
const location = await setupHandoffTokenLocation(projectRoot, record.storyId);
|
|
929
|
-
await anchoredAtomicWriteFile(location.parent, location.name, `${JSON.stringify(record, null, 2)}\n`);
|
|
930
|
-
await assertGitLifecyclePrivateFile(location.path, location.parent);
|
|
931
|
-
}
|
|
932
|
-
async function readSetupHandoffTokenRecord(projectRoot, storyId) {
|
|
933
|
-
try {
|
|
934
|
-
const location = await setupHandoffTokenLocation(projectRoot, storyId);
|
|
935
|
-
await assertGitLifecyclePrivateFile(location.path, location.parent);
|
|
936
|
-
const item = JSON.parse(await readFile(location.path, 'utf8'));
|
|
937
|
-
const exactKeys = item.state === 'issued'
|
|
938
|
-
? ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'token', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt']
|
|
939
|
-
: item.state === 'consuming'
|
|
940
|
-
? ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'token', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt', 'consumedBySessionId', 'consumeStartedAt']
|
|
941
|
-
: ['schemaVersion', 'kind', 'state', 'storyId', 'fromSessionId', 'tokenDigest', 'bundleDigest', 'claimRevision', 'grantDigest', 'expiresAt', 'consumedBySessionId', 'consumedAt'];
|
|
942
|
-
if (Object.keys(item).sort().join('\0') !== [...exactKeys].sort().join('\0')
|
|
943
|
-
|| item.schemaVersion !== 1 || item.kind !== 'direct-yolo-setup-handoff-token'
|
|
944
|
-
|| (item.state !== 'issued' && item.state !== 'consuming' && item.state !== 'consumed') || item.storyId !== storyId
|
|
945
|
-
|| typeof item.fromSessionId !== 'string' || !/^setup-[a-f0-9]{64}$/.test(item.fromSessionId)
|
|
946
|
-
|| ![item.tokenDigest, item.bundleDigest, item.claimRevision, item.grantDigest].every((value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value))
|
|
947
|
-
|| typeof item.expiresAt !== 'string' || !Number.isFinite(Date.parse(item.expiresAt))
|
|
948
|
-
|| (item.state === 'issued' || item.state === 'consuming')
|
|
949
|
-
&& (typeof item.token !== 'string' || !/^[A-Za-z0-9_-]{32,256}$/.test(item.token))
|
|
950
|
-
|| item.state === 'consuming' && (typeof item.consumedBySessionId !== 'string'
|
|
951
|
-
|| !/^[a-f0-9]{64}$/.test(item.consumedBySessionId)
|
|
952
|
-
|| typeof item.consumeStartedAt !== 'string' || !Number.isFinite(Date.parse(item.consumeStartedAt)))
|
|
953
|
-
|| item.state === 'consumed' && (typeof item.consumedBySessionId !== 'string'
|
|
954
|
-
|| !/^[a-f0-9]{64}$/.test(item.consumedBySessionId)
|
|
955
|
-
|| typeof item.consumedAt !== 'string' || !Number.isFinite(Date.parse(item.consumedAt))))
|
|
956
|
-
return undefined;
|
|
957
|
-
return item;
|
|
958
|
-
}
|
|
959
|
-
catch {
|
|
960
|
-
return undefined;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
async function resolveIssuedSetupLeaseHandoff(projectRoot, storyId, bundleDigest, claimRevision, nowIso) {
|
|
964
|
-
const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
|
|
965
|
-
if (!record || record.state === 'consumed' || !record.token
|
|
966
|
-
|| record.bundleDigest !== bundleDigest || record.claimRevision !== claimRevision
|
|
967
|
-
|| Date.parse(record.expiresAt) <= Date.parse(nowIso)
|
|
968
|
-
|| digest(record.token) !== record.tokenDigest)
|
|
969
|
-
return undefined;
|
|
970
|
-
return { sessionId: record.fromSessionId, token: record.token };
|
|
971
|
-
}
|
|
972
|
-
async function markSetupHandoffTokenConsuming(projectRoot, storyId, fromSessionId, token, consumedBySessionId, consumeStartedAt) {
|
|
973
|
-
const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
|
|
974
|
-
if (!record || record.state === 'consumed' || record.fromSessionId !== fromSessionId
|
|
975
|
-
|| record.token !== token || record.tokenDigest !== digest(token)) {
|
|
976
|
-
throw new Error('direct-yolo-setup-handoff-consume-invalid');
|
|
977
|
-
}
|
|
978
|
-
if (record.state === 'consuming') {
|
|
979
|
-
if (record.consumedBySessionId !== consumedBySessionId) {
|
|
980
|
-
throw new Error('direct-yolo-setup-handoff-consume-raced');
|
|
981
|
-
}
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
await writeSetupHandoffTokenRecord(projectRoot, {
|
|
985
|
-
...record,
|
|
986
|
-
state: 'consuming',
|
|
987
|
-
consumedBySessionId,
|
|
988
|
-
consumeStartedAt,
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
async function markSetupHandoffTokenConsumed(projectRoot, epicId, storyId, fromSessionId, token, bundleDigest, claimRevision, consumedBySessionId, consumedAt) {
|
|
992
|
-
const record = await readSetupHandoffTokenRecord(projectRoot, storyId);
|
|
993
|
-
if (record?.state === 'consumed' && record.fromSessionId === fromSessionId
|
|
994
|
-
&& record.consumedBySessionId === consumedBySessionId)
|
|
995
|
-
return;
|
|
996
|
-
const runtime = await readGitLifecycleRuntime(projectRoot, { required: true });
|
|
997
|
-
const lease = asRecord(runtime.epics[epicId]?.branch_lease);
|
|
998
|
-
const consumption = asRecord(lease?.handoffConsumption);
|
|
999
|
-
if (!record || record.state !== 'consuming' || record.fromSessionId !== fromSessionId
|
|
1000
|
-
|| record.token !== token || record.tokenDigest !== digest(token)
|
|
1001
|
-
|| record.bundleDigest !== bundleDigest || record.claimRevision !== claimRevision
|
|
1002
|
-
|| record.consumedBySessionId !== consumedBySessionId
|
|
1003
|
-
|| lease?.ownerStoryId !== storyId || lease.sessionId !== consumedBySessionId
|
|
1004
|
-
|| consumption?.kind !== 'direct-yolo-setup-handoff-consumption'
|
|
1005
|
-
|| consumption.state !== 'consumed' || consumption.fromSessionId !== fromSessionId
|
|
1006
|
-
|| consumption.tokenDigest !== record.tokenDigest || consumption.bundleDigest !== bundleDigest
|
|
1007
|
-
|| consumption.claimRevision !== claimRevision || consumption.consumedBySessionId !== consumedBySessionId) {
|
|
1008
|
-
throw new Error('direct-yolo-setup-handoff-consume-invalid');
|
|
1009
|
-
}
|
|
1010
|
-
try {
|
|
1011
|
-
await writeSetupHandoffTokenRecord(projectRoot, {
|
|
1012
|
-
schemaVersion: 1, kind: record.kind, state: 'consumed', storyId,
|
|
1013
|
-
fromSessionId, tokenDigest: record.tokenDigest, bundleDigest: record.bundleDigest,
|
|
1014
|
-
claimRevision: record.claimRevision, grantDigest: record.grantDigest, expiresAt: record.expiresAt,
|
|
1015
|
-
consumedBySessionId, consumedAt,
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
|
-
catch {
|
|
1019
|
-
throw new Error('direct-yolo-setup-handoff-consume-persist-failed');
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
function validSetupLeaseHandoff(input) {
|
|
1023
|
-
const grant = asRecord(input.current?.handoffGrant);
|
|
1024
|
-
const token = input.handoff?.token;
|
|
1025
|
-
const tokenDigest = typeof token === 'string' && /^[A-Za-z0-9_-]{32,256}$/.test(token)
|
|
1026
|
-
? digest(token)
|
|
1027
|
-
: undefined;
|
|
1028
|
-
return Boolean(grant && tokenDigest && input.handoff
|
|
1029
|
-
&& input.handoff.sessionId === input.current?.sessionId
|
|
1030
|
-
&& grant.schemaVersion === 1
|
|
1031
|
-
&& grant.kind === 'direct-yolo-setup-handoff'
|
|
1032
|
-
&& grant.storyId === input.storyId
|
|
1033
|
-
&& grant.fromSessionId === input.current?.sessionId
|
|
1034
|
-
&& grant.bundleDigest === input.bundleDigest
|
|
1035
|
-
&& grant.claimRevision === input.claimRevision
|
|
1036
|
-
&& typeof grant.expiresAt === 'string'
|
|
1037
|
-
&& Date.parse(grant.expiresAt) > Date.parse(input.nowIso)
|
|
1038
|
-
&& typeof grant.tokenDigest === 'string'
|
|
1039
|
-
&& /^[a-f0-9]{64}$/.test(grant.tokenDigest)
|
|
1040
|
-
&& safeDigestEqual(grant.tokenDigest, tokenDigest));
|
|
1041
|
-
}
|
|
1042
|
-
function safeDigestEqual(left, right) {
|
|
1043
|
-
const leftBytes = Buffer.from(left, 'hex');
|
|
1044
|
-
const rightBytes = Buffer.from(right, 'hex');
|
|
1045
|
-
return leftBytes.length === 32 && rightBytes.length === 32 && timingSafeEqual(leftBytes, rightBytes);
|
|
1046
|
-
}
|
|
1047
|
-
function sameLease(left, right) {
|
|
1048
|
-
return Boolean(left && ['schemaVersion', 'ownerStoryId', 'sessionId', 'idempotencyKey', 'acquiredAt', 'expiresAt']
|
|
1049
|
-
.every((key) => left[key] === right[key]));
|
|
1050
|
-
}
|
|
1051
|
-
async function releaseOwnedDirectLease(projectRoot, epicId, expected) {
|
|
1052
|
-
await mutateGitLifecycleRuntime(projectRoot, (document) => {
|
|
1053
|
-
const epic = ensureRuntimeEpic(document, epicId);
|
|
1054
|
-
const current = asRecord(epic.branch_lease);
|
|
1055
|
-
if (sameLease(current, expected))
|
|
1056
|
-
epic.branch_lease = null;
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
async function cleanupOwnedDirectAuthority(authority, storyId) {
|
|
1060
|
-
const runtime = await readGitLifecycleRuntime(authority.projectRoot, { required: true });
|
|
1061
|
-
const lease = asRecord(runtime.epics[authority.epicId]?.branch_lease);
|
|
1062
|
-
if (!lease || lease.ownerStoryId !== storyId || !safeTokenValue(lease.sessionId)
|
|
1063
|
-
|| !safeTokenValue(lease.idempotencyKey) || !Number.isFinite(Date.parse(String(lease.acquiredAt)))
|
|
1064
|
-
|| !Number.isFinite(Date.parse(String(lease.expiresAt))))
|
|
1065
|
-
return;
|
|
1066
|
-
const physical = await acquireGitLifecyclePhysicalIntegrationLock(authority.projectRoot, {
|
|
1067
|
-
kind: 'direct', storyId, sessionId: String(lease.sessionId), idempotencyKey: String(lease.idempotencyKey),
|
|
1068
|
-
createdAt: String(lease.acquiredAt), expiresAt: String(lease.expiresAt), persistent: true,
|
|
1069
|
-
});
|
|
1070
|
-
if (!physical.ok)
|
|
1071
|
-
return;
|
|
1072
|
-
const released = await releaseGitLifecyclePhysicalIntegrationLock(physical.path, physical.owner);
|
|
1073
|
-
if (released)
|
|
1074
|
-
await releaseOwnedDirectLease(authority.projectRoot, authority.epicId, lease);
|
|
1075
|
-
}
|
|
1076
|
-
async function runQa(options, projectRoot, headSha, graphRevision) {
|
|
1077
|
-
const transientAuthority = await executeP205TransientAuthority(projectRoot, {
|
|
1078
|
-
headSha,
|
|
1079
|
-
graphRevision,
|
|
1080
|
-
nowIso: options.nowIso,
|
|
1081
|
-
});
|
|
1082
|
-
await options.qaRunner?.({ projectRoot, storyId: options.storyId, headSha });
|
|
1083
|
-
const evidence = projectP205TransientExecutionEvidence(transientAuthority);
|
|
1084
|
-
if (!evidence)
|
|
1085
|
-
throw new Error('p205-transient-authority-invalid');
|
|
1086
|
-
return {
|
|
1087
|
-
contract: 'P205-CI-02', command: P205_COMPLETE_COMMAND,
|
|
1088
|
-
manifestDigest: evidence.manifestDigest, gateIds: evidence.gateIds,
|
|
1089
|
-
resultDigest: evidence.resultDigest, transientAuthority,
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
async function physicalCheckoutUnchanged(projectRoot, executionBranch, headSha, committedPaths) {
|
|
1093
|
-
if (await git(projectRoot, ['rev-parse', 'HEAD']) !== headSha
|
|
1094
|
-
|| await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== executionBranch
|
|
1095
|
-
|| !(await gitExitOk(projectRoot, ['diff', '--cached', '--quiet'])))
|
|
1096
|
-
return false;
|
|
1097
|
-
return gitExitOk(projectRoot, ['diff', '--quiet', 'HEAD', '--', ...committedPaths]);
|
|
1098
|
-
}
|
|
1099
|
-
async function remoteHeadIsExact(projectRoot, remoteInput, branch, headSha) {
|
|
1100
|
-
const remote = safeRemote(remoteInput);
|
|
1101
|
-
const remoteHead = (await git(projectRoot, ['ls-remote', '--heads', remote, `refs/heads/${branch}`])).split(/\s+/)[0];
|
|
1102
|
-
return remoteHead === headSha;
|
|
1103
|
-
}
|
|
1104
|
-
function validateDirectWriteSet(paths, authorizedWriteSet, storyId, rootMemoryEffect) {
|
|
1105
|
-
if (paths.length === 0 || paths.some((path) => !safePath(path))) {
|
|
1106
|
-
return { ok: false, reasonCode: 'direct-yolo-write-set-invalid' };
|
|
1107
|
-
}
|
|
1108
|
-
const required = [
|
|
1109
|
-
'.neocortex/state.json', `docs/stories/${storyId}.story.md`,
|
|
1110
|
-
`.neocortex/stories/${storyId}/qa-gate-result.json`,
|
|
1111
|
-
`.neocortex/stories/${storyId}/validation-evidence.md`,
|
|
1112
|
-
`.neocortex/stories/${storyId}/review.md`,
|
|
1113
|
-
];
|
|
1114
|
-
if (!required.every((path) => paths.includes(path)) || !required.every((path) => authorizedWriteSet.includes(path))) {
|
|
1115
|
-
return { ok: false, reasonCode: 'direct-yolo-write-set-invalid' };
|
|
1116
|
-
}
|
|
1117
|
-
if (rootMemoryEffect && (rootMemoryEffect.storyId !== storyId
|
|
1118
|
-
|| rootMemoryEffect.childClaimRevision !== digest([...authorizedWriteSet].sort().join('\0')))) {
|
|
1119
|
-
return { ok: false, reasonCode: 'direct-yolo-root-memory-claim-mismatch' };
|
|
1120
|
-
}
|
|
1121
|
-
if (paths.some((path) => !authorizedWriteSet.includes(path) && !rootMemoryEffect?.files.some(file => file.path === path))) {
|
|
1122
|
-
return { ok: false, reasonCode: 'direct-yolo-write-set-unauthorized' };
|
|
1123
|
-
}
|
|
1124
|
-
return { ok: true };
|
|
1125
|
-
}
|
|
1126
|
-
function safePath(value) {
|
|
1127
|
-
return value.length > 0 && value.length <= 1024 && !value.startsWith('/') && !value.includes('\\')
|
|
1128
|
-
&& !value.split('/').includes('..') && !value.startsWith('.git/') && !value.includes('\0');
|
|
1129
|
-
}
|
|
1130
|
-
function safeTokenValue(value) {
|
|
1131
|
-
return typeof value === 'string' && value.length > 0 && value.length <= 512
|
|
1132
|
-
&& /^[A-Za-z0-9._:@/+\-=]+$/.test(value);
|
|
1133
|
-
}
|
|
1134
|
-
function safeRef(value) {
|
|
1135
|
-
return value.length > 0 && value.length <= 240 && !value.startsWith('-') && !/[\s~^:?*[\\]/.test(value) && !value.includes('..');
|
|
1136
|
-
}
|
|
1137
|
-
function safeRemote(value) {
|
|
1138
|
-
if (!/^[A-Za-z0-9._-]{1,128}$/.test(value))
|
|
1139
|
-
throw new Error('direct-yolo-remote-invalid');
|
|
1140
|
-
return value;
|
|
1141
|
-
}
|
|
1142
|
-
function trailer(body, key) {
|
|
1143
|
-
const prefix = `${key}: `;
|
|
1144
|
-
const values = body.split('\n').map((line) => line.trim()).filter((line) => line.startsWith(prefix)).map((line) => line.slice(prefix.length));
|
|
1145
|
-
return values.length === 1 ? values[0] : undefined;
|
|
1146
|
-
}
|
|
1147
|
-
function digest(value) { return createHash('sha256').update(value).digest('hex'); }
|
|
1148
|
-
function splitNull(value) { return value.split('\0').filter(Boolean); }
|
|
1149
|
-
function normalizedTime(value) {
|
|
1150
|
-
return value && Number.isFinite(Date.parse(value)) ? new Date(Date.parse(value)).toISOString() : new Date().toISOString();
|
|
1151
|
-
}
|
|
1152
|
-
function asRecord(value) {
|
|
1153
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
1154
|
-
}
|
|
1155
|
-
function validOriginBinding(value, executionBranch, originBranch) {
|
|
1156
|
-
const item = asRecord(value);
|
|
1157
|
-
if (!item || item.schemaVersion !== 1 || item.originBranch !== originBranch
|
|
1158
|
-
|| item.executionBranch !== executionBranch || item.capturedFromCurrentCheckout !== true
|
|
1159
|
-
|| typeof item.originHeadSha !== 'string' || !/^[a-f0-9]{40,64}$/.test(item.originHeadSha)
|
|
1160
|
-
|| typeof item.capturedAt !== 'string' || !Number.isFinite(Date.parse(item.capturedAt)))
|
|
1161
|
-
return undefined;
|
|
1162
|
-
return item;
|
|
1163
|
-
}
|
|
1164
|
-
function setupReceipt(authority, binding, leaseExpiresAt, reused) {
|
|
1165
|
-
return Object.freeze({
|
|
1166
|
-
schemaVersion: 1, kind: 'direct-yolo-setup-receipt', storyId: authority.storyId,
|
|
1167
|
-
epicId: authority.epicId, executionBranch: authority.executionBranch, originBranch: authority.originBranch,
|
|
1168
|
-
originHeadSha: binding.originHeadSha, leaseExpiresAt, reused, publicSafe: true,
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
async function checkoutPayload(projectRoot) {
|
|
1172
|
-
const untrackedPaths = splitNull(await gitRaw(projectRoot, [
|
|
1173
|
-
'ls-files', '--others', '--exclude-standard', '-z',
|
|
1174
|
-
])).sort();
|
|
1175
|
-
const untrackedBlobs = await Promise.all(untrackedPaths.map(async (path) => {
|
|
1176
|
-
if (!safePath(path))
|
|
1177
|
-
throw new Error('direct-yolo-checkout-payload-invalid');
|
|
1178
|
-
return `${path}\0${await git(projectRoot, ['hash-object', '--no-filters', '--', path])}`;
|
|
1179
|
-
}));
|
|
1180
|
-
return digest([
|
|
1181
|
-
await gitRaw(projectRoot, ['diff', '--cached', '--binary', '--full-index']),
|
|
1182
|
-
await gitRaw(projectRoot, ['diff', '--binary', '--full-index']),
|
|
1183
|
-
await gitRaw(projectRoot, ['status', '--porcelain=v1', '-z', '--untracked-files=all']),
|
|
1184
|
-
...untrackedBlobs,
|
|
1185
|
-
].join('\0'));
|
|
1186
|
-
}
|
|
1187
|
-
/** Physical observation for a private YOLO agent step; this is not actor attribution. */
|
|
1188
|
-
export async function captureDirectYoloCheckoutEvidence(projectRootInput) {
|
|
1189
|
-
const projectRoot = resolve(projectRootInput);
|
|
1190
|
-
const headSha = await git(projectRoot, ['rev-parse', 'HEAD']);
|
|
1191
|
-
const branch = await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']);
|
|
1192
|
-
const payloadDigest = await checkoutPayload(projectRoot);
|
|
1193
|
-
if (await git(projectRoot, ['rev-parse', 'HEAD']) !== headSha
|
|
1194
|
-
|| await git(projectRoot, ['symbolic-ref', '--quiet', '--short', 'HEAD']) !== branch) {
|
|
1195
|
-
throw new Error('direct-yolo-checkout-observation-raced');
|
|
1196
|
-
}
|
|
1197
|
-
return Object.freeze({ headSha, branch, payloadDigest });
|
|
1198
|
-
}
|
|
1199
|
-
async function worktreeBranches(projectRoot) {
|
|
1200
|
-
const records = [];
|
|
1201
|
-
let current;
|
|
1202
|
-
for (const line of (await gitRaw(projectRoot, ['worktree', 'list', '--porcelain'])).split('\n')) {
|
|
1203
|
-
if (line.startsWith('worktree ')) {
|
|
1204
|
-
current = { path: line.slice('worktree '.length) };
|
|
1205
|
-
records.push(current);
|
|
1206
|
-
}
|
|
1207
|
-
else if (current && line.startsWith('branch refs/heads/')) {
|
|
1208
|
-
current.branch = line.slice('branch refs/heads/'.length);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
return records;
|
|
1212
|
-
}
|
|
1213
|
-
function publicReason(error, fallback) {
|
|
1214
|
-
if (error instanceof Error && error.message === 'git-lifecycle-runtime-revision-conflict') {
|
|
1215
|
-
return 'direct-yolo-runtime-revision-conflict';
|
|
1216
|
-
}
|
|
1217
|
-
return error instanceof Error && /^direct-yolo-[a-z0-9-]+$/.test(error.message) ? error.message : fallback;
|
|
1218
|
-
}
|
|
1219
|
-
function failure(reasonCode) { return { ok: false, reasonCode }; }
|
|
1220
|
-
async function git(cwd, args) {
|
|
1221
|
-
return String((await execFileAsync('git', [...args], { cwd, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })).stdout).trim();
|
|
1222
|
-
}
|
|
1223
|
-
async function gitRaw(cwd, args) {
|
|
1224
|
-
return String((await execFileAsync('git', [...args], { cwd, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })).stdout);
|
|
1225
|
-
}
|
|
1226
|
-
async function gitExitOk(cwd, args) {
|
|
1227
|
-
try {
|
|
1228
|
-
await execFileAsync('git', [...args], { cwd, maxBuffer: 8 * 1024 * 1024 });
|
|
1229
|
-
return true;
|
|
1230
|
-
}
|
|
1231
|
-
catch {
|
|
1232
|
-
return false;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1
|
+
import{execFile as He}from"node:child_process";import{createHash as Le,randomBytes as me,timingSafeEqual as Ce}from"node:crypto";import{readFile as Z}from"node:fs/promises";import{join as te,resolve as M}from"node:path";import{promisify as Oe}from"node:util";import{readRootMemoryEffectForCheckpoint as qe}from"../private-execution/root-memory-scope.js";import{rootMemoryEffectDigest as $e,verifyRootMemoryPhysicalWriteSet as re}from"../private-execution/root-memory-git-evidence.js";import{acquireGitLifecyclePhysicalIntegrationLock as V,applyGitLifecycleRuntimeOverlay as Pe,computeImmutableStoryQaEvidenceDigest as ze,computeGitLifecyclePhysicalIntegrationLockOwnerDigest as we,computeP205StoryQaEvidenceDigest as Ne,computeLegacyDirectYoloRecoveryReceiptRef as Ke,ensureRuntimeEpic as N,ensureRuntimeStory as Re,ensureGitLifecyclePrivateChildDirectory as Me,executeP205TransientAuthority as Ve,projectP205TransientExecutionEvidence as We,publicStoryReviewArtifactFromGit as Q,LEGACY_DIRECT_YOLO_RECOVERY_COMMITS as Ge,mutateGitLifecycleRuntime as P,P205_COMPLETE_COMMAND as Fe,readGitLifecycleRuntime as C,gitLifecycleRuntimeDirectory as Te,assertGitLifecyclePrivateFile as Ie,releaseGitLifecyclePhysicalIntegrationLock as W,releaseGitLifecyclePhysicalIntegrationLockForCheckpoint as Je,verifyGitLifecyclePhysicalIntegrationLock as U,verifyAcceptedGitLifecycleStoryCheckpoint as ve}from"./git-lifecycle-store.js";import{anchoredAtomicWriteFile as Ye}from"./secure-filesystem-boundary.js";import{GIT_LIFECYCLE_COMMIT_PUSH_STEP_ID as Ze,GIT_LIFECYCLE_REVIEW_PR_STEP_ID as xe,GIT_LIFECYCLE_SETUP_BRANCH_STEP_ID as X}from"./git-lifecycle-step-ids.js";const oe=Oe(He),ie=3600*1e3;async function gt(e){let t,o=!1;try{const r=await Se(e.projectRoot,e.storyId),i=K(e.nowIso),n=Date.parse(i),g=await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"]),s=await y(r.projectRoot,["rev-parse","HEAD"]),h=await C(r.projectRoot,{required:!0}),c=w(h.epics[r.epicId]),d=w(c?.branch_lease),l=e.expiredSetupRecovery;if(l&&!ot(l,h.revision,d,e.storyId,n))return a("direct-yolo-expired-setup-recovery-invalid");const u=d&&Number.isFinite(Date.parse(String(d.expiresAt)))&&Date.parse(String(d.expiresAt))>n;if(u&&d?.ownerStoryId!==e.storyId)return a("direct-yolo-writer-active");let m=he(c?.origin_binding,r.executionBranch,r.originBranch);if(g===r.executionBranch){if(!m)return a("direct-yolo-origin-binding-invalid")}else{if(g!==r.originBranch)return a("direct-yolo-current-branch-mismatch");if(m&&m.originHeadSha!==s){if(!l||!await B(r.projectRoot,["merge-base","--is-ancestor",m.originHeadSha,s]))return a("direct-yolo-origin-binding-conflict");const p=`refs/heads/${r.executionBranch}`;if(!await B(r.projectRoot,["show-ref","--verify","--quiet",p]))return a("direct-yolo-execution-branch-missing");const I=await y(r.projectRoot,["rev-parse",p]);if(!await B(r.projectRoot,["merge-base","--is-ancestor",I,s]))return a("direct-yolo-execution-branch-diverged")}if(m??={schemaVersion:1,originBranch:r.originBranch,originHeadSha:s,executionBranch:r.executionBranch,capturedFromCurrentCheckout:!0,capturedAt:i},(await ye(r.projectRoot)).some(p=>p.branch===r.executionBranch&&M(p.path)!==r.projectRoot))return a("direct-yolo-execution-branch-checked-out-elsewhere");const f=await q(r.projectRoot),R=`refs/heads/${r.executionBranch}`;if(await B(r.projectRoot,["show-ref","--verify","--quiet",R])){const p=await y(r.projectRoot,["rev-parse",R]);if(p!==s){if(!await B(r.projectRoot,["merge-base","--is-ancestor",p,s]))return a("direct-yolo-execution-branch-diverged");l||await y(r.projectRoot,["update-ref",R,s,p])}l?t={checkoutBefore:f,executionRef:R,executionBranch:r.executionBranch,executionHead:p,originBranch:r.originBranch,originHead:s}:await y(r.projectRoot,["switch",r.executionBranch])}else{if(l)return a("direct-yolo-execution-branch-missing");await y(r.projectRoot,["switch","-c",r.executionBranch])}if(!t&&await q(r.projectRoot)!==f)return a("direct-yolo-checkout-payload-raced")}const S=t?.originHead??await y(r.projectRoot,["rev-parse","HEAD"]);if(!t&&await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.executionBranch)return a("direct-yolo-current-branch-mismatch");if(u&&d?.ownerStoryId===e.storyId)return d.setupHeadSha!==S?a("direct-yolo-setup-lease-head-unbound"):{ok:!0,value:pe(r,m,String(d.expiresAt),!0)};const v=i,x=new Date(n+3600*1e3).toISOString(),b=`setup-${me(32).toString("hex")}`,D=l?h:await C(r.projectRoot,{required:!0});return await P(r.projectRoot,A=>{const f=N(A,r.epicId),R=w(f.branch_lease);if(l&&JSON.stringify(R)!==JSON.stringify(l.expectedLease))throw new Error("direct-yolo-expired-setup-recovery-raced");if(R&&Date.parse(String(R.expiresAt))>n&&R.ownerStoryId!==e.storyId)throw new Error("direct-yolo-writer-active");const p=he(f.origin_binding,r.executionBranch,r.originBranch);if(f.origin_binding&&!p)throw new Error("direct-yolo-origin-binding-conflict");if(p&&JSON.stringify(p)!==JSON.stringify(m))throw new Error("direct-yolo-origin-binding-conflict");f.branch_mode="epic",f.execution_branch=r.executionBranch,f.origin_branch=r.originBranch,f.graph_revision=r.graphRevision,f.origin_binding=m,f.branch_lease={schemaVersion:1,ownerStoryId:e.storyId,sessionId:b,idempotencyKey:b,acquiredAt:v,expiresAt:x,setupHeadSha:S}},{expectedRevision:l?.expectedRuntimeRevision??D.revision,preCommit:async()=>{if(t){if((await ye(r.projectRoot)).some(f=>f.branch===r.executionBranch&&M(f.path)!==r.projectRoot)||await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.originBranch||await y(r.projectRoot,["rev-parse","HEAD"])!==t.originHead||await q(r.projectRoot)!==t.checkoutBefore||!await B(r.projectRoot,["show-ref","--verify","--quiet",t.executionRef])||await y(r.projectRoot,["rev-parse",t.executionRef])!==t.executionHead)throw new Error("direct-yolo-expired-setup-recovery-raced");if(o=!0,t.executionHead!==t.originHead&&await y(r.projectRoot,["update-ref",t.executionRef,t.originHead,t.executionHead]),await y(r.projectRoot,["switch",r.executionBranch]),await q(r.projectRoot)!==t.checkoutBefore)throw new Error("direct-yolo-checkout-payload-raced")}if(await y(r.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==r.executionBranch||await y(r.projectRoot,["rev-parse","HEAD"])!==S)throw new Error("direct-yolo-setup-physical-state-raced")}}),{ok:!0,value:pe(r,m,x,!1)}}catch(r){return o&&t&&await it(M(e.projectRoot),t).catch(()=>{}),a(Y(r,"direct-yolo-setup-failed"))}}async function mt(e){let t;try{const o=await F(e.projectRoot,e.storyId),r=K(e.nowIso),i=te(o.projectRoot,".neocortex/state.json"),n=await Z(i,"utf8"),g=await q(o.projectRoot),s=await y(o.projectRoot,["rev-parse","HEAD"]),h=`refs/heads/${o.executionBranch}`,c=await C(o.projectRoot,{required:!0}),d=w(c.epics[o.epicId]),l=he(d?.origin_binding,o.executionBranch,o.originBranch),u=w(d?.branch_lease);if(!l)return a("direct-yolo-origin-binding-invalid");if(!u||!ne(u,e.storyId,r)||u?.sessionId!==u?.idempotencyKey)return a("direct-yolo-setup-lease-invalid");const m=await V(o.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:String(u.sessionId),idempotencyKey:String(u.idempotencyKey),expiresAt:new Date(Date.now()+300*1e3).toISOString(),persistent:!1});if(!m.ok)return a(m.reasonCode);if(t=m,s!==u.setupHeadSha||!await B(o.projectRoot,["merge-base","--is-ancestor",l.originHeadSha,s])||await y(o.projectRoot,["rev-parse",h])!==s)return a("direct-yolo-setup-head-mismatch");if((await ye(o.projectRoot)).some(f=>f.branch===o.executionBranch&&M(f.path)!==o.projectRoot))return a("direct-yolo-execution-branch-checked-out-elsewhere");const v=w(JSON.parse(n)),x=w(w(v?.stories)?.[e.storyId]),b=w(c.stories[e.storyId]);if(!x||!["backlog","ready_to_start"].includes(String(x.status))||x.steps_completed!==void 0&&(!Array.isArray(x.steps_completed)||x.steps_completed.length>0)||x.branch_name||x.checkpoint)return a("direct-yolo-setup-story-progress-conflict");const D=b?.status==="branch-ready"&&b.last_step===X&&Array.isArray(b.steps_completed)&&b.steps_completed.length===1&&b.steps_completed[0]===X&&Object.keys(b).sort().join(",")==="last_step,status,steps_completed";if(b&&Object.keys(b).length>0&&!D)return a("direct-yolo-setup-story-progress-conflict");const A=async()=>{if(Date.parse(String(u.expiresAt))<=(e.nowIso?Date.parse(r):Date.now())||!t||!await U(t.path,t.owner)||await Z(i,"utf8")!==n||await q(o.projectRoot)!==g||await y(o.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==o.executionBranch||await y(o.projectRoot,["rev-parse","HEAD"])!==s||!await B(o.projectRoot,["merge-base","--is-ancestor",l.originHeadSha,s])||await y(o.projectRoot,["rev-parse",h])!==s)throw new Error("direct-yolo-setup-physical-state-raced")};return D?await A():await P(o.projectRoot,f=>{const R=w(f.epics[o.epicId]),p=Re(f,e.storyId);if(JSON.stringify(R?.branch_lease)!==JSON.stringify(u)||JSON.stringify(R?.origin_binding)!==JSON.stringify(l)||R?.graph_revision!==o.graphRevision||Object.keys(p).length>0)throw new Error("direct-yolo-setup-runtime-raced");p.status="branch-ready",p.last_step=X,p.steps_completed=[X]},{expectedRevision:c.revision,preCommit:A}),{ok:!0,value:Object.freeze({...pe(o,l,String(u.expiresAt),D),checkpointAccepted:!0,graphRevision:o.graphRevision,headSha:s})}}catch(o){return a(Y(o,"direct-yolo-setup-acceptance-failed"))}finally{t&&await W(t.path,t.owner).catch(()=>{})}}async function wt(e){try{const t=await F(e.projectRoot,e.storyId),o=O(await j(t.projectRoot,["diff","--cached","--name-only","-z"])),r=T(o,t.authorizedWriteSet,e.storyId,t.rootMemoryEffect);if(!r.ok)return a(r.reasonCode);if(t.rootMemoryEffect&&re(t.projectRoot,t.rootMemoryEffect,{storyId:e.storyId,executionBranch:t.executionBranch,baseSha:await y(t.projectRoot,["rev-parse","HEAD"]),headRef:":",childPaths:t.authorizedWriteSet}),Q(t.projectRoot,e.storyId,":")?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");if(O(await j(t.projectRoot,["diff","--name-only","-z"])).some(I=>o.includes(I)))return a("direct-yolo-user-overlap");const g=await j(t.projectRoot,["diff","--cached","--binary","--full-index"]);if(!g)return a("direct-yolo-staged-diff-empty");const s=k(g),h=k([...t.authorizedWriteSet].sort().join("\0")),c=K(e.nowIso),d=await C(t.projectRoot,{required:!0}),l=w(d.epics[t.epicId]?.branch_lease);if(!ne(l,e.storyId,c))return a("direct-yolo-setup-handoff-owner-invalid");const u=String(l.sessionId),m=String(l.expiresAt),S=new Date(Math.min(Date.parse(m),Date.parse(c)+300*1e3)).toISOString(),v=await G(t.projectRoot,e.storyId),x=v?.state==="issued"&&v.fromSessionId===u&&v.bundleDigest===s&&v.claimRevision===h&&Date.parse(v.expiresAt)>Date.parse(c),b=x?v.expiresAt:S,D=x?v.token:me(32).toString("base64url"),A=k(D),f=Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-handoff",storyId:e.storyId,fromSessionId:u,tokenDigest:A,bundleDigest:s,claimRevision:h,expiresAt:b}),R=k(JSON.stringify(f)),p=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:u,idempotencyKey:u,expiresAt:b,persistent:!1,createdAt:c});if(!p.ok)return a(p.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");try{await ae(t.projectRoot,{schemaVersion:1,kind:"direct-yolo-setup-handoff-token",state:"issued",storyId:e.storyId,fromSessionId:u,token:D,tokenDigest:A,bundleDigest:s,claimRevision:h,grantDigest:R,expiresAt:b}),await P(t.projectRoot,I=>{const E=w(N(I,t.epicId).branch_lease);if(!ne(E,e.storyId,c)||E?.sessionId!==u||E.idempotencyKey!==u)throw new Error("direct-yolo-setup-handoff-owner-raced");E.handoffGrant=f},{expectedRevision:d.revision,preCommit:async()=>{if(!await U(p.path,p.owner))throw new Error("direct-yolo-integration-lock-raced");const I=await G(t.projectRoot,e.storyId);if(!I||I.state!=="issued"||I.grantDigest!==R||I.tokenDigest!==A)throw new Error("direct-yolo-setup-handoff-token-raced")}})}finally{await W(p.path,p.owner).catch(()=>{})}return{ok:!0,value:Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-handoff-receipt",storyId:e.storyId,sessionId:u,bundleDigest:s,claimRevision:h,grantDigest:R,expiresAt:b,reused:x,publicSafe:!0})}}catch(t){return a(t instanceof Error&&/^direct-yolo-/.test(t.message)?t.message:"direct-yolo-setup-handoff-failed")}}async function Rt(e){try{const t=await F(e.projectRoot,e.storyId),o=await y(t.projectRoot,["rev-parse","HEAD"]),r=O(await j(t.projectRoot,["diff","--cached","--name-only","-z"])),i=T(r,t.authorizedWriteSet,e.storyId,t.rootMemoryEffect);if(!i.ok)return a(i.reasonCode);t.rootMemoryEffect&&re(t.projectRoot,t.rootMemoryEffect,{storyId:e.storyId,executionBranch:t.executionBranch,baseSha:o,headRef:":",childPaths:t.authorizedWriteSet});const n=Q(t.projectRoot,e.storyId,":");if(n?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");if(O(await j(t.projectRoot,["diff","--name-only","-z"])).some(f=>r.includes(f)))return a("direct-yolo-user-overlap");const s=await j(t.projectRoot,["diff","--cached","--binary","--full-index"]);if(!s)return a("direct-yolo-staged-diff-empty");const h=k(s),c=k([...t.authorizedWriteSet].sort().join("\0")),d=k(["direct-yolo-commit-v1",e.storyId,t.epicId,t.executionBranch,o,h,t.graphRevision,c].join("\0")),l=K(e.nowIso);let u=e.setupLeaseHandoff??await Qe(t.projectRoot,e.storyId,h,c,l);const m=await C(t.projectRoot,{required:!0}),S=w(m.epics[t.epicId]?.branch_lease),v=S?.ownerStoryId===e.storyId&&S.sessionId===d&&S.idempotencyKey===d&&typeof S.expiresAt=="string"&&Date.parse(S.expiresAt)>Date.parse(l)?S:void 0;if(v&&e.setupLeaseHandoff)return a("direct-yolo-setup-handoff-replayed");const x=w(v?.handoffConsumption);if(x){const f=await G(t.projectRoot,e.storyId);if(!f||!["consuming","consumed"].includes(f.state)||f.fromSessionId!==x.fromSessionId||f.tokenDigest!==x.tokenDigest||f.bundleDigest!==h||f.claimRevision!==c||x.bundleDigest!==h||x.claimRevision!==c||f.consumedBySessionId!==d||x.consumedBySessionId!==d)return a("direct-yolo-setup-handoff-consume-invalid");if(f.state==="consuming"){if(!f.token||k(f.token)!==f.tokenDigest)return a("direct-yolo-setup-handoff-consume-invalid");u={sessionId:f.fromSessionId,token:f.token}}}const b=v?String(v.expiresAt):new Date(Date.parse(l)+ie).toISOString(),D=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:d,idempotencyKey:d,expiresAt:b,persistent:!0,createdAt:v&&typeof v.acquiredAt=="string"?v.acquiredAt:l});if(!D.ok)return a(D.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");try{u&&e.setupLeaseHandoff===void 0&&(await Ue(t.projectRoot,e.storyId,u.sessionId,u.token,d,l),await e.setupHandoffFault?.("after-consuming-persisted")),await P(t.projectRoot,f=>{const R=N(f,t.epicId),p=w(R.branch_lease),I=p&&Date.parse(String(p.expiresAt))>Date.parse(l),E=p?.ownerStoryId===e.storyId&&p.sessionId===d&&p.idempotencyKey===d,$=p?.ownerStoryId===e.storyId&&p.sessionId===p.idempotencyKey&&typeof p.idempotencyKey=="string"&&/^setup-[a-f0-9]{64}$/.test(p.idempotencyKey),_=I&&$&&et({current:p,handoff:u,storyId:e.storyId,bundleDigest:h,claimRevision:c,nowIso:l});if(u&&I&&!E&&!_)throw new Error("direct-yolo-setup-handoff-invalid");if(I&&!E&&!_)throw new Error("direct-yolo-writer-active");const H=_?w(p?.handoffGrant):void 0,L=H?{schemaVersion:1,kind:"direct-yolo-setup-handoff-consumption",state:"consumed",storyId:e.storyId,fromSessionId:String(p?.sessionId),tokenDigest:String(H.tokenDigest),bundleDigest:h,claimRevision:c,consumedBySessionId:d,consumedAt:l}:void 0;R.branch_mode="epic",R.execution_branch=t.executionBranch,R.origin_branch=t.originBranch,R.graph_revision=t.graphRevision,R.branch_lease=I&&E?p:{schemaVersion:1,ownerStoryId:e.storyId,sessionId:d,idempotencyKey:d,acquiredAt:l,expiresAt:b,...L?{handoffConsumption:L}:{}}},{expectedRevision:m.revision,preCommit:async()=>{if(!await U(D.path,D.owner))throw new Error("direct-yolo-integration-lock-raced")}})}catch(f){throw await W(D.path,D.owner).catch(()=>{}),f}u&&e.setupLeaseHandoff===void 0&&(await e.setupHandoffFault?.("before-consumed-persist"),await Xe(t.projectRoot,t.epicId,e.storyId,u.sessionId,u.token,h,c,d,l));const A=Object.freeze([`Neocortex-Story: ${e.storyId}`,`Neocortex-Epic: ${t.epicId}`,`Neocortex-Execution-Branch: ${t.executionBranch}`,`Neocortex-Prepared-Base: ${o}`,`Neocortex-Bundle-Digest: ${h}`,`Neocortex-Review-Artifact-Digest: ${n.digest}`,`Neocortex-Graph-Revision: ${t.graphRevision}`,`Neocortex-Claim-Revision: ${c}`,...t.rootMemoryEffect?[`Neocortex-Root-Memory-Effect: ${$e(t.rootMemoryEffect)}`]:[]]);return{ok:!0,value:Object.freeze({schemaVersion:1,storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:o,bundleDigest:h,reviewArtifactDigest:n.digest,graphRevision:t.graphRevision,claimRevision:c,idempotencyKey:d,changedPaths:Object.freeze(r),trailers:A})}}catch(t){return a(Y(t,"direct-yolo-begin-failed"))}}async function It(e){let t,o,r;try{const i=await F(e.projectRoot,e.storyId);t=i;const n=await y(i.projectRoot,["rev-parse","HEAD"]);let g=await C(i.projectRoot,{required:!0});const s=w(g.stories[e.storyId]?.checkpoint);if(s){const _=typeof s.headSha=="string"?s.headSha:"",H=typeof s.graphRevision=="string"?s.graphRevision:"",L=/^[a-f0-9]{40,64}$/.test(_)?O(await j(i.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",_])):[],z=T(L,i.authorizedWriteSet,e.storyId,i.rootMemoryEffect);if(!(/^[a-f0-9]{40,64}$/.test(_)&&await B(i.projectRoot,["merge-base","--is-ancestor",_,n]))||!/^[a-f0-9]{64}$/.test(H)||!z.ok)return a("direct-yolo-replay-invalid");const _e=await de(e,i.projectRoot,n,H);if(!await ve(i.projectRoot,s,e.storyId,i.epicId,i.executionBranch,_e.transientAuthority,i.authorizedWriteSet))return a("direct-yolo-replay-invalid");if(!await fe(i.projectRoot,i.executionBranch,n,L))return a("direct-yolo-replay-head-raced");if(e.push&&!await De(i.projectRoot,e.remote??"origin",i.executionBranch,_))return a("direct-yolo-remote-head-mismatch");const le=Array.isArray(s.receiptRefs)?s.receiptRefs.find(ge=>typeof ge=="string"&&/^physical-lock-owner:[a-f0-9]{64}$/.test(ge)):void 0;return le&&typeof s.idempotencyKey=="string"&&await Je(i.projectRoot,{storyId:e.storyId,idempotencyKey:s.idempotencyKey,ownerDigest:le.slice(20)}),{ok:!0,value:s}}const h=n,c=await y(i.projectRoot,["rev-parse",`${h}^`]),d=await j(i.projectRoot,["show","--no-patch","--format=%B",h]),l=O(await j(i.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",h])),u=T(l,i.authorizedWriteSet,e.storyId,i.rootMemoryEffect);if(!u.ok)return a(u.reasonCode);i.rootMemoryEffect&&re(i.projectRoot,i.rootMemoryEffect,{storyId:e.storyId,executionBranch:i.executionBranch,baseSha:c,headRef:h,childPaths:i.authorizedWriteSet});const m=J(d,"Neocortex-Bundle-Digest"),S=J(d,"Neocortex-Graph-Revision"),v=J(d,"Neocortex-Claim-Revision"),x=new Map([["Neocortex-Story",e.storyId],["Neocortex-Epic",i.epicId],["Neocortex-Execution-Branch",i.executionBranch],["Neocortex-Prepared-Base",c],["Neocortex-Graph-Revision",i.graphRevision],["Neocortex-Claim-Revision",k([...i.authorizedWriteSet].sort().join("\0"))]]);if(!m||!/^[a-f0-9]{64}$/.test(m)||!S||!v||[...x].some(([_,H])=>J(d,_)!==H))return a("direct-yolo-trailers-invalid");const b=Q(i.projectRoot,e.storyId,h);if(b?.verdict!=="approved")return a("direct-yolo-review-artifact-not-approved");const D=J(d,"Neocortex-Review-Artifact-Digest");if(D!==void 0&&D!==b.digest)return a("direct-yolo-review-artifact-digest-mismatch");const A=k(await j(i.projectRoot,["diff","--binary","--full-index",`${c}..${h}`]));if(m!==A)return a("direct-yolo-bundle-digest-mismatch");const f=k(["direct-yolo-commit-v1",e.storyId,i.epicId,i.executionBranch,c,m,S,v].join("\0")),R=w(g.epics[i.epicId]?.branch_lease),p=K(e.nowIso),I=R?.ownerStoryId===e.storyId&&R.idempotencyKey===f&&R.sessionId===f&&Date.parse(String(R.expiresAt))>Date.now()?R:void 0;if(R&&Date.parse(String(R.expiresAt))>Date.now()&&!I)return a("direct-yolo-writer-active");const E=I??{schemaVersion:1,ownerStoryId:e.storyId,sessionId:f,idempotencyKey:f,acquiredAt:p,expiresAt:new Date(Date.parse(p)+ie).toISOString()},$=await V(i.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:f,idempotencyKey:f,expiresAt:String(E.expiresAt),persistent:!0,createdAt:String(E.acquiredAt)});if(!$.ok)return a($.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-integration-backpressure":"direct-yolo-integration-lock-failed");r=$,I||(g=(await P(i.projectRoot,H=>{const L=N(H,i.epicId),z=w(L.branch_lease);if(z&&Date.parse(String(z.expiresAt))>Date.parse(p))throw new Error("direct-yolo-writer-active");if(L.graph_revision!==i.graphRevision)throw new Error("direct-yolo-graph-revision-raced");L.branch_lease=E},{expectedRevision:g.revision})).document),o=E;{const _=await de(e,i.projectRoot,h,i.graphRevision);if(!await fe(i.projectRoot,i.executionBranch,h,l))return a("direct-yolo-head-raced");if(e.push){const z=ee(e.remote??"origin");if(await y(i.projectRoot,["push",z,i.executionBranch]),!await Ae(i.projectRoot,z,i.executionBranch,h))return a("direct-yolo-remote-head-mismatch")}const H=await be({projectRoot:i.projectRoot,storyId:e.storyId,epicId:i.epicId,executionBranch:i.executionBranch,baseSha:c,headSha:h,bundleDigest:m,graphRevision:S,claimRevision:v,idempotencyKey:f,qaExecution:_,physicalLockOwnerDigest:we($.owner),nowIso:K(e.nowIso),legacyReceiptRef:void 0,rootMemoryEffect:i.rootMemoryEffect});return await e.beforeCheckpointPersist?.(),await ke({projectRoot:i.projectRoot,checkpoint:H,remote:e.push?e.remote??"origin":void 0,expectedRevision:g.revision,expectedLease:E,expectedGraphRevision:i.graphRevision,physicalLockPath:$.path,physicalLockOwner:$.owner,transientAuthority:_.transientAuthority,authorizedWriteSet:i.authorizedWriteSet})?(await e.afterCheckpointPersist?.(),{ok:!0,value:H}):a("direct-yolo-checkpoint-persist-failed")}}catch(i){return a(Y(i,"direct-yolo-finalize-failed"))}finally{r&&await W(r.path,r.owner).catch(()=>{}),t&&o?await se(t.projectRoot,t.epicId,o).catch(()=>{}):t&&await rt(t,e.storyId).catch(()=>{})}}async function vt(e){try{const t=await F(e.projectRoot,e.storyId);if(!/^[a-f0-9]{40,64}$/.test(e.commitSha))return a("direct-yolo-recovery-commit-invalid");const o=await y(t.projectRoot,["rev-parse",e.commitSha]);if(Ge[e.storyId]!==o)return a("direct-yolo-recovery-not-allowlisted");if(await y(t.projectRoot,["rev-parse","HEAD"])!==o)return a("direct-yolo-recovery-head-mismatch");await y(t.projectRoot,["merge-base","--is-ancestor",o,`refs/heads/${t.executionBranch}`]);const r=await y(t.projectRoot,["rev-parse",`${o}^`]),i=await j(t.projectRoot,["diff","--binary","--full-index",`${r}..${o}`]),n=O(await j(t.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",o]));if(!T(n,t.authorizedWriteSet.length>0?t.authorizedWriteSet:n,e.storyId).ok)return a("direct-yolo-recovery-write-set-invalid");const s=k(i),h=k([...n].sort().join("\0")),c=k(["legacy-direct-yolo-recovery-v1",e.storyId,o].join("\0")),d=K(e.nowIso),l=new Date(Date.parse(d)+ie).toISOString(),u=await C(t.projectRoot,{required:!0}),m=await V(t.projectRoot,{kind:"direct",storyId:e.storyId,sessionId:c,idempotencyKey:c,expiresAt:l,persistent:!0,createdAt:d});if(!m.ok)return a(m.reasonCode==="git-lifecycle-integration-backpressure"?"direct-yolo-recovery-integration-backpressure":"direct-yolo-recovery-integration-lock-failed");const S={schemaVersion:1,ownerStoryId:e.storyId,sessionId:c,idempotencyKey:c,acquiredAt:d,expiresAt:l};let v=!1;try{const x=await P(t.projectRoot,p=>{const I=N(p,t.epicId),E=w(I.branch_lease);if(E&&Date.parse(String(E.expiresAt))>Date.parse(d)&&!ce(E,S))throw new Error("direct-yolo-recovery-writer-active");if(I.graph_revision!==t.graphRevision)throw new Error("direct-yolo-recovery-graph-raced");I.branch_mode="epic",I.execution_branch=t.executionBranch,I.origin_branch=t.originBranch,I.branch_lease=S},{expectedRevision:u.revision});v=!0;const b=await de(e,t.projectRoot,o,t.graphRevision);if(!await fe(t.projectRoot,t.executionBranch,o,n))return a("direct-yolo-recovery-head-raced");if(e.push){const p=ee(e.remote??"origin"),I=(await y(t.projectRoot,["ls-remote","--heads",p,`refs/heads/${t.executionBranch}`])).split(/\s+/)[0];if(!I||!await B(t.projectRoot,["merge-base","--is-ancestor",o,I]))return a("direct-yolo-recovery-remote-proof-missing")}const D=await be({projectRoot:t.projectRoot,storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:r,headSha:o,bundleDigest:s,graphRevision:t.graphRevision,claimRevision:h,idempotencyKey:c,qaExecution:b,physicalLockOwnerDigest:we(m.owner),nowIso:d,legacyReceiptRef:void 0}),A=await Ke(t.projectRoot,{storyId:e.storyId,epicId:t.epicId,executionBranch:t.executionBranch,baseSha:r,headSha:o,bundleDigest:s,graphRevision:t.graphRevision});if(!A)return a("direct-yolo-recovery-evidence-invalid");const f=Object.freeze({...D,receiptRefs:Object.freeze([...D.receiptRefs,A])});return await ke({projectRoot:t.projectRoot,checkpoint:f,remote:e.push?e.remote??"origin":void 0,remotePolicy:"ancestor-recovery",expectedRevision:x.document.revision,expectedLease:S,expectedGraphRevision:t.graphRevision,physicalLockPath:m.path,physicalLockOwner:m.owner,transientAuthority:b.transientAuthority,authorizedWriteSet:t.authorizedWriteSet})?{ok:!0,value:f}:a("direct-yolo-recovery-persist-failed")}finally{await W(m.path,m.owner).catch(()=>{}),v&&await se(t.projectRoot,t.epicId,S).catch(()=>{})}}catch(t){return a(Y(t,"direct-yolo-recovery-failed"))}}async function Se(e,t){if(!/^[A-Za-z][A-Za-z0-9_-]*\.[A-Za-z0-9._-]+$/.test(t))throw new Error("direct-yolo-story-id-invalid");const o=M(e),r=w(JSON.parse(await Z(te(o,".neocortex/state.json"),"utf8"))),i=await C(o,{required:!0}),n=Pe(r,i),g=w(n.stories),s=w(n.epics),h=w(g?.[t]),c=String(h?.epic_id??t.split(".")[0]),d=w(s?.[c]),l=String(d?.execution_branch??""),u=String(d?.origin_branch??""),m=String(d?.graph_revision??""),S=Array.isArray(h?.files_to_modify)?[...new Set(h.files_to_modify.filter(x=>typeof x=="string"&&ue(x)))].sort():[];if(!h)throw new Error("direct-yolo-story-state-missing");if(d?.branch_mode!=="epic")throw new Error("direct-yolo-branch-mode-invalid");if(!Be(l)||!await B(o,["check-ref-format","--branch",l]))throw new Error("direct-yolo-execution-branch-invalid");if(!Be(u)||!await B(o,["check-ref-format","--branch",u]))throw new Error("direct-yolo-origin-branch-invalid");if(!/^[a-f0-9]{64}$/.test(m))throw new Error("direct-yolo-graph-revision-invalid");const v=await qe({projectRoot:o,storyId:t,graphRevision:m,executionBranch:l,childPaths:S});return{projectRoot:o,storyId:t,story:h,epicId:c,executionBranch:l,originBranch:u,graphRevision:m,authorizedWriteSet:S,rootMemoryEffect:v}}async function F(e,t){const o=await Se(e,t);if(await y(o.projectRoot,["symbolic-ref","--quiet","--short","HEAD"])!==o.executionBranch)throw new Error("direct-yolo-current-branch-mismatch");return o}async function be(e){const t=e.nowIso,o=await j(e.projectRoot,["show","--no-patch","--format=%H%n%P%n%B",e.headSha]),r=O(await j(e.projectRoot,["diff-tree","--no-commit-id","--name-only","-r","-z",e.headSha])),i=ze({storyId:e.storyId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,commitBody:o,changedPaths:r}),n=Ne({gitEvidenceDigest:i,headSha:e.headSha,graphRevision:e.graphRevision,manifestDigest:e.qaExecution.manifestDigest,gateIds:e.qaExecution.gateIds,resultDigest:e.qaExecution.resultDigest}),g=`story-qa:${n}`,s=Object.freeze({schemaVersion:1,kind:"story_qa_evidence",producer:"local-yolo-tdd",evidenceVersion:"p205-execution-v2",storyId:e.storyId,epicId:e.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,contract:e.qaExecution.contract,command:e.qaExecution.command,manifestDigest:e.qaExecution.manifestDigest,gateIds:e.qaExecution.gateIds,resultDigest:e.qaExecution.resultDigest,sourceEvidenceDigest:n,verdict:"passed",checkedAt:t,receiptRef:g}),h=Q(e.projectRoot,e.storyId,e.headSha);if(h?.verdict!=="approved")throw new Error("direct-yolo-review-artifact-not-approved");const c=k(["root-commit-range-review-v2",e.storyId,e.baseSha,e.headSha,e.bundleDigest,e.graphRevision,g,k(o),[...r].sort().join(","),h.digest].join("\0")),d=`story-review:${c}`,l=Object.freeze({schemaVersion:1,kind:"story_review_evidence",producer:"root-commit-range-review",storyId:e.storyId,epicId:e.epicId,baseSha:e.baseSha,headSha:e.headSha,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,sourceEvidenceDigest:c,reviewArtifactDigest:h.digest,qaReceiptRef:g,verdict:"approved",reviewedAt:t,receiptRef:d}),u=[`.neocortex/stories/${e.storyId}/qa-gate-result.json`,`.neocortex/stories/${e.storyId}/validation-evidence.md`,g,d,...e.physicalLockOwnerDigest?[`physical-lock-owner:${e.physicalLockOwnerDigest}`]:[],...e.legacyReceiptRef?[e.legacyReceiptRef]:[]];return Object.freeze({schemaVersion:1,storyId:e.storyId,epicId:e.epicId,branch:e.executionBranch,baseSha:e.baseSha,headSha:e.headSha,commits:Object.freeze([e.headSha]),idempotencyKey:e.idempotencyKey,bundleDigest:e.bundleDigest,graphRevision:e.graphRevision,claimRevision:e.claimRevision,receiptRefs:Object.freeze(u),qaVerdict:"passed",reviewVerdict:"approved",qaReceipt:s,reviewReceipt:l,recordedAt:t,...e.rootMemoryEffect?{rootMemoryEffect:e.rootMemoryEffect}:{}})}async function ke(e){const{projectRoot:t,checkpoint:o,remote:r}=e;let i=!1;return await P(t,n=>{const g=N(n,o.epicId);if(e.expectedLease){const d=w(g.branch_lease);if(!ce(d,e.expectedLease))throw new Error("direct-yolo-runtime-revision-conflict");if(Date.parse(String(d?.expiresAt))<=Date.now())throw new Error("direct-yolo-writer-lease-expired");if(g.graph_revision!==e.expectedGraphRevision||o.graphRevision!==e.expectedGraphRevision)throw new Error("direct-yolo-graph-revision-raced")}const s=Re(n,o.storyId),h=w(s.checkpoint);if(h&&h.headSha!==o.headSha){i=!0;return}s.checkpoint=o,s.status="done",s.last_step=xe;const c=Array.isArray(s.steps_completed)?s.steps_completed.filter(d=>typeof d=="string"):[];s.steps_completed=[...new Set([...c,Ze,xe])],g.branch_lease=null},{expectedRevision:e.expectedRevision,preCommit:async()=>{if(i)throw new Error("direct-yolo-checkpoint-conflict");if(await y(t,["symbolic-ref","--quiet","--short","HEAD"])!==o.branch||await y(t,["rev-parse","HEAD"])!==o.headSha)throw new Error("direct-yolo-checkpoint-head-raced");if(!await ve(t,o,o.storyId,o.epicId,o.branch,e.transientAuthority,e.authorizedWriteSet))throw new Error("direct-yolo-checkpoint-physical-invalid");if(e.physicalLockPath&&e.physicalLockOwner&&!await U(e.physicalLockPath,e.physicalLockOwner))throw new Error("direct-yolo-integration-lock-raced");if(r&&!(e.remotePolicy==="ancestor-recovery"?await De(t,r,o.branch,o.headSha):await Ae(t,r,o.branch,o.headSha)))throw new Error("direct-yolo-checkpoint-remote-raced")}}),!i}async function De(e,t,o,r){const i=ee(t),n=(await y(e,["ls-remote","--heads",i,`refs/heads/${o}`])).split(/\s+/)[0];return!!(n&&await B(e,["merge-base","--is-ancestor",r,n]))}function ne(e,t,o){return!!(e?.ownerStoryId===t&&e.sessionId===e.idempotencyKey&&typeof e.idempotencyKey=="string"&&/^setup-[a-f0-9]{64}$/.test(e.idempotencyKey)&&typeof e.expiresAt=="string"&&Date.parse(e.expiresAt)>Date.parse(o))}async function je(e,t){const o=await Te(e),r=await Me(o,"handoffs"),i=`direct-${k(t)}.json`;return{parent:r,name:i,path:te(r,i)}}async function ae(e,t){const o=await je(e,t.storyId);await Ye(o.parent,o.name,`${JSON.stringify(t,null,2)}
|
|
2
|
+
`),await Ie(o.path,o.parent)}async function G(e,t){try{const o=await je(e,t);await Ie(o.path,o.parent);const r=JSON.parse(await Z(o.path,"utf8")),i=r.state==="issued"?["schemaVersion","kind","state","storyId","fromSessionId","token","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt"]:r.state==="consuming"?["schemaVersion","kind","state","storyId","fromSessionId","token","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt","consumedBySessionId","consumeStartedAt"]:["schemaVersion","kind","state","storyId","fromSessionId","tokenDigest","bundleDigest","claimRevision","grantDigest","expiresAt","consumedBySessionId","consumedAt"];return Object.keys(r).sort().join("\0")!==[...i].sort().join("\0")||r.schemaVersion!==1||r.kind!=="direct-yolo-setup-handoff-token"||r.state!=="issued"&&r.state!=="consuming"&&r.state!=="consumed"||r.storyId!==t||typeof r.fromSessionId!="string"||!/^setup-[a-f0-9]{64}$/.test(r.fromSessionId)||![r.tokenDigest,r.bundleDigest,r.claimRevision,r.grantDigest].every(n=>typeof n=="string"&&/^[a-f0-9]{64}$/.test(n))||typeof r.expiresAt!="string"||!Number.isFinite(Date.parse(r.expiresAt))||(r.state==="issued"||r.state==="consuming")&&(typeof r.token!="string"||!/^[A-Za-z0-9_-]{32,256}$/.test(r.token))||r.state==="consuming"&&(typeof r.consumedBySessionId!="string"||!/^[a-f0-9]{64}$/.test(r.consumedBySessionId)||typeof r.consumeStartedAt!="string"||!Number.isFinite(Date.parse(r.consumeStartedAt)))||r.state==="consumed"&&(typeof r.consumedBySessionId!="string"||!/^[a-f0-9]{64}$/.test(r.consumedBySessionId)||typeof r.consumedAt!="string"||!Number.isFinite(Date.parse(r.consumedAt)))?void 0:r}catch{return}}async function Qe(e,t,o,r,i){const n=await G(e,t);if(!(!n||n.state==="consumed"||!n.token||n.bundleDigest!==o||n.claimRevision!==r||Date.parse(n.expiresAt)<=Date.parse(i)||k(n.token)!==n.tokenDigest))return{sessionId:n.fromSessionId,token:n.token}}async function Ue(e,t,o,r,i,n){const g=await G(e,t);if(!g||g.state==="consumed"||g.fromSessionId!==o||g.token!==r||g.tokenDigest!==k(r))throw new Error("direct-yolo-setup-handoff-consume-invalid");if(g.state==="consuming"){if(g.consumedBySessionId!==i)throw new Error("direct-yolo-setup-handoff-consume-raced");return}await ae(e,{...g,state:"consuming",consumedBySessionId:i,consumeStartedAt:n})}async function Xe(e,t,o,r,i,n,g,s,h){const c=await G(e,o);if(c?.state==="consumed"&&c.fromSessionId===r&&c.consumedBySessionId===s)return;const d=await C(e,{required:!0}),l=w(d.epics[t]?.branch_lease),u=w(l?.handoffConsumption);if(!c||c.state!=="consuming"||c.fromSessionId!==r||c.token!==i||c.tokenDigest!==k(i)||c.bundleDigest!==n||c.claimRevision!==g||c.consumedBySessionId!==s||l?.ownerStoryId!==o||l.sessionId!==s||u?.kind!=="direct-yolo-setup-handoff-consumption"||u.state!=="consumed"||u.fromSessionId!==r||u.tokenDigest!==c.tokenDigest||u.bundleDigest!==n||u.claimRevision!==g||u.consumedBySessionId!==s)throw new Error("direct-yolo-setup-handoff-consume-invalid");try{await ae(e,{schemaVersion:1,kind:c.kind,state:"consumed",storyId:o,fromSessionId:r,tokenDigest:c.tokenDigest,bundleDigest:c.bundleDigest,claimRevision:c.claimRevision,grantDigest:c.grantDigest,expiresAt:c.expiresAt,consumedBySessionId:s,consumedAt:h})}catch{throw new Error("direct-yolo-setup-handoff-consume-persist-failed")}}function et(e){const t=w(e.current?.handoffGrant),o=e.handoff?.token,r=typeof o=="string"&&/^[A-Za-z0-9_-]{32,256}$/.test(o)?k(o):void 0;return!!(t&&r&&e.handoff&&e.handoff.sessionId===e.current?.sessionId&&t.schemaVersion===1&&t.kind==="direct-yolo-setup-handoff"&&t.storyId===e.storyId&&t.fromSessionId===e.current?.sessionId&&t.bundleDigest===e.bundleDigest&&t.claimRevision===e.claimRevision&&typeof t.expiresAt=="string"&&Date.parse(t.expiresAt)>Date.parse(e.nowIso)&&typeof t.tokenDigest=="string"&&/^[a-f0-9]{64}$/.test(t.tokenDigest)&&tt(t.tokenDigest,r))}function tt(e,t){const o=Buffer.from(e,"hex"),r=Buffer.from(t,"hex");return o.length===32&&r.length===32&&Ce(o,r)}function ce(e,t){return!!(e&&["schemaVersion","ownerStoryId","sessionId","idempotencyKey","acquiredAt","expiresAt"].every(o=>e[o]===t[o]))}async function se(e,t,o){await P(e,r=>{const i=N(r,t),n=w(i.branch_lease);ce(n,o)&&(i.branch_lease=null)})}async function rt(e,t){const o=await C(e.projectRoot,{required:!0}),r=w(o.epics[e.epicId]?.branch_lease);if(!r||r.ownerStoryId!==t||!Ee(r.sessionId)||!Ee(r.idempotencyKey)||!Number.isFinite(Date.parse(String(r.acquiredAt)))||!Number.isFinite(Date.parse(String(r.expiresAt))))return;const i=await V(e.projectRoot,{kind:"direct",storyId:t,sessionId:String(r.sessionId),idempotencyKey:String(r.idempotencyKey),createdAt:String(r.acquiredAt),expiresAt:String(r.expiresAt),persistent:!0});if(!i.ok)return;await W(i.path,i.owner)&&await se(e.projectRoot,e.epicId,r)}async function de(e,t,o,r){const i=await Ve(t,{headSha:o,graphRevision:r,nowIso:e.nowIso});await e.qaRunner?.({projectRoot:t,storyId:e.storyId,headSha:o});const n=We(i);if(!n)throw new Error("p205-transient-authority-invalid");return{contract:"P205-CI-02",command:Fe,manifestDigest:n.manifestDigest,gateIds:n.gateIds,resultDigest:n.resultDigest,transientAuthority:i}}async function fe(e,t,o,r){return await y(e,["rev-parse","HEAD"])!==o||await y(e,["symbolic-ref","--quiet","--short","HEAD"])!==t||!await B(e,["diff","--cached","--quiet"])?!1:B(e,["diff","--quiet","HEAD","--",...r])}async function Ae(e,t,o,r){const i=ee(t);return(await y(e,["ls-remote","--heads",i,`refs/heads/${o}`])).split(/\s+/)[0]===r}function T(e,t,o,r){if(e.length===0||e.some(n=>!ue(n)))return{ok:!1,reasonCode:"direct-yolo-write-set-invalid"};const i=[".neocortex/state.json",`docs/stories/${o}.story.md`,`.neocortex/stories/${o}/qa-gate-result.json`,`.neocortex/stories/${o}/validation-evidence.md`,`.neocortex/stories/${o}/review.md`];return!i.every(n=>e.includes(n))||!i.every(n=>t.includes(n))?{ok:!1,reasonCode:"direct-yolo-write-set-invalid"}:r&&(r.storyId!==o||r.childClaimRevision!==k([...t].sort().join("\0")))?{ok:!1,reasonCode:"direct-yolo-root-memory-claim-mismatch"}:e.some(n=>!t.includes(n)&&!r?.files.some(g=>g.path===n))?{ok:!1,reasonCode:"direct-yolo-write-set-unauthorized"}:{ok:!0}}function ue(e){return e.length>0&&e.length<=1024&&!e.startsWith("/")&&!e.includes("\\")&&!e.split("/").includes("..")&&!e.startsWith(".git/")&&!e.includes("\0")}function Ee(e){return typeof e=="string"&&e.length>0&&e.length<=512&&/^[A-Za-z0-9._:@/+\-=]+$/.test(e)}function Be(e){return e.length>0&&e.length<=240&&!e.startsWith("-")&&!/[\s~^:?*[\\]/.test(e)&&!e.includes("..")}function ee(e){if(!/^[A-Za-z0-9._-]{1,128}$/.test(e))throw new Error("direct-yolo-remote-invalid");return e}function J(e,t){const o=`${t}: `,r=e.split(`
|
|
3
|
+
`).map(i=>i.trim()).filter(i=>i.startsWith(o)).map(i=>i.slice(o.length));return r.length===1?r[0]:void 0}function k(e){return Le("sha256").update(e).digest("hex")}function O(e){return e.split("\0").filter(Boolean)}function K(e){return e&&Number.isFinite(Date.parse(e))?new Date(Date.parse(e)).toISOString():new Date().toISOString()}function w(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:void 0}function ot(e,t,o,r,i){const n=w(e.expectedLease),g=n?.sessionId,s=Date.parse(String(n?.expiresAt??""));return e.schemaVersion===1&&Number.isSafeInteger(e.expectedRuntimeRevision)&&e.expectedRuntimeRevision===t&&!!o&&JSON.stringify(o)===JSON.stringify(n)&&n?.schemaVersion===1&&n.ownerStoryId===r&&typeof g=="string"&&/^setup-[a-f0-9]{64}$/.test(g)&&n.idempotencyKey===g&&Number.isFinite(s)&&s<=i}async function it(e,t){let o=await y(e,["symbolic-ref","--quiet","--short","HEAD"]);if(o===t.executionBranch){if(await y(e,["rev-parse","HEAD"])!==t.originHead||await q(e)!==t.checkoutBefore)return;await y(e,["switch",t.originBranch]),o=t.originBranch}if(o!==t.originBranch||await y(e,["rev-parse","HEAD"])!==t.originHead||await q(e)!==t.checkoutBefore||t.executionHead===t.originHead||!await B(e,["show-ref","--verify","--quiet",t.executionRef]))return;await y(e,["rev-parse",t.executionRef])===t.originHead&&await y(e,["update-ref",t.executionRef,t.executionHead,t.originHead])}function he(e,t,o){const r=w(e);if(!(!r||r.schemaVersion!==1||r.originBranch!==o||r.executionBranch!==t||r.capturedFromCurrentCheckout!==!0||typeof r.originHeadSha!="string"||!/^[a-f0-9]{40,64}$/.test(r.originHeadSha)||typeof r.capturedAt!="string"||!Number.isFinite(Date.parse(r.capturedAt))))return r}function pe(e,t,o,r){return Object.freeze({schemaVersion:1,kind:"direct-yolo-setup-receipt",storyId:e.storyId,epicId:e.epicId,executionBranch:e.executionBranch,originBranch:e.originBranch,originHeadSha:t.originHeadSha,leaseExpiresAt:o,reused:r,publicSafe:!0})}async function q(e){const t=O(await j(e,["ls-files","--others","--exclude-standard","-z"])).sort(),o=await Promise.all(t.map(async r=>{if(!ue(r))throw new Error("direct-yolo-checkout-payload-invalid");return`${r}\0${await y(e,["hash-object","--no-filters","--",r])}`}));return k([await j(e,["diff","--cached","--binary","--full-index"]),await j(e,["diff","--binary","--full-index"]),await j(e,["status","--porcelain=v1","-z","--untracked-files=all"]),...o].join("\0"))}async function xt(e){const t=M(e),o=await y(t,["rev-parse","HEAD"]),r=await y(t,["symbolic-ref","--quiet","--short","HEAD"]),i=await q(t);if(await y(t,["rev-parse","HEAD"])!==o||await y(t,["symbolic-ref","--quiet","--short","HEAD"])!==r)throw new Error("direct-yolo-checkout-observation-raced");return Object.freeze({headSha:o,branch:r,payloadDigest:i})}async function ye(e){const t=[];let o;for(const r of(await j(e,["worktree","list","--porcelain"])).split(`
|
|
4
|
+
`))r.startsWith("worktree ")?(o={path:r.slice(9)},t.push(o)):o&&r.startsWith("branch refs/heads/")&&(o.branch=r.slice(18));return t}function Y(e,t){return e instanceof Error&&e.message==="git-lifecycle-runtime-revision-conflict"?"direct-yolo-runtime-revision-conflict":e instanceof Error&&/^direct-yolo-[a-z0-9-]+$/.test(e.message)?e.message:t}function a(e){return{ok:!1,reasonCode:e}}async function y(e,t){return String((await oe("git",[...t],{cwd:e,encoding:"utf8",maxBuffer:32*1024*1024})).stdout).trim()}async function j(e,t){return String((await oe("git",[...t],{cwd:e,encoding:"utf8",maxBuffer:32*1024*1024})).stdout)}async function B(e,t){try{return await oe("git",[...t],{cwd:e,maxBuffer:8*1024*1024}),!0}catch{return!1}}export{mt as acceptDirectYoloSetupCheckpoint,Rt as beginDirectYoloStoryCommit,xt as captureDirectYoloCheckoutEvidence,It as finalizeDirectYoloStoryCheckpoint,wt as issueDirectYoloSetupLeaseHandoff,gt as prepareDirectYoloSetup,vt as recoverLegacyDirectYoloStoryCheckpoint};
|