@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,672 +1,2 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
|
|
3
|
-
import { existsSync, fsyncSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, realpathSync, writeFileSync, } from 'node:fs';
|
|
4
|
-
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
|
-
import { GIT_LIFECYCLE_RUNTIME_DIRECTORY, mutateGitLifecycleRuntime, readGitLifecycleRuntime, readGitLifecycleRuntimeSync, } from './git-lifecycle-store.js';
|
|
6
|
-
export const EPIC_OPERATIONAL_SOURCE_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
7
|
-
export const EPIC_OPERATIONAL_PAYLOAD_LIMIT_BYTES = 1024 * 1024;
|
|
8
|
-
export const EPIC_OPERATIONAL_RECEIPT_CAP = 64;
|
|
9
|
-
export const EPIC_OPERATIONAL_IDENTITY_CAP = 2_048;
|
|
10
|
-
const HASH = /^[a-f0-9]{64}$/;
|
|
11
|
-
const ID = /^P[1-9][0-9]*(?:\.[0-9]+)*$/;
|
|
12
|
-
const SAFE_TOKEN = /^[A-Za-z0-9._:/#\-[\]]{1,240}$/;
|
|
13
|
-
export function epicOperationalCanonicalJson(value) {
|
|
14
|
-
if (value === null || typeof value !== 'object')
|
|
15
|
-
return JSON.stringify(value);
|
|
16
|
-
if (Array.isArray(value))
|
|
17
|
-
return `[${value.map(epicOperationalCanonicalJson).join(',')}]`;
|
|
18
|
-
return `{${Object.entries(value)
|
|
19
|
-
.filter(([, child]) => child !== undefined)
|
|
20
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
21
|
-
.map(([key, child]) => `${JSON.stringify(key)}:${epicOperationalCanonicalJson(child)}`)
|
|
22
|
-
.join(',')}}`;
|
|
23
|
-
}
|
|
24
|
-
export function epicOperationalHash(domain, value) {
|
|
25
|
-
return createHash('sha256').update(`${domain}\0${epicOperationalCanonicalJson(value)}`).digest('hex');
|
|
26
|
-
}
|
|
27
|
-
export function deriveEpicOperationalRepositoryFingerprint(projectRoot) {
|
|
28
|
-
const canonicalRoot = realpathSync(resolve(projectRoot));
|
|
29
|
-
const raw = execFileSync('git', ['rev-parse', '--git-common-dir'], {
|
|
30
|
-
cwd: canonicalRoot,
|
|
31
|
-
encoding: 'utf8',
|
|
32
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
33
|
-
}).trim();
|
|
34
|
-
const absolute = isAbsolute(raw) ? resolve(raw) : resolve(canonicalRoot, raw);
|
|
35
|
-
const commonDir = realpathSync(absolute);
|
|
36
|
-
if (commonDir !== absolute)
|
|
37
|
-
throw new Error('epic-operational-path-unsafe');
|
|
38
|
-
const info = lstatSync(commonDir);
|
|
39
|
-
if (!info.isDirectory() || info.isSymbolicLink())
|
|
40
|
-
throw new Error('epic-operational-path-unsafe');
|
|
41
|
-
return epicOperationalHash('p224-epic-operational-repository-v1', {
|
|
42
|
-
commonDir,
|
|
43
|
-
dev: Number(info.dev),
|
|
44
|
-
ino: Number(info.ino),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
export function createEpicOperationalIdentity(projectRoot, input) {
|
|
48
|
-
const repositoryFingerprint = deriveEpicOperationalRepositoryFingerprint(projectRoot);
|
|
49
|
-
const base = {
|
|
50
|
-
schemaVersion: 'EpicOperationalIdentity/1.0',
|
|
51
|
-
epicId: input.epicId,
|
|
52
|
-
executionBranch: input.executionBranch,
|
|
53
|
-
graphRevision: input.graphRevision,
|
|
54
|
-
originBindingDigest: input.originBindingDigest,
|
|
55
|
-
repositoryFingerprint,
|
|
56
|
-
};
|
|
57
|
-
const identity = Object.freeze({
|
|
58
|
-
...base,
|
|
59
|
-
identityHash: epicOperationalHash('p224-epic-operational-identity-v1', base),
|
|
60
|
-
});
|
|
61
|
-
assertEpicOperationalIdentity(identity);
|
|
62
|
-
return identity;
|
|
63
|
-
}
|
|
64
|
-
export function createInitialEpicOperationalState(input) {
|
|
65
|
-
const value = {
|
|
66
|
-
schemaVersion: 'EpicOperationalState/1.0',
|
|
67
|
-
repositoryFingerprint: input.identity.repositoryFingerprint,
|
|
68
|
-
writerEpoch: 1,
|
|
69
|
-
capabilities: ['epic-operational-state-v1', 'epic-lifecycle-backfill-v1'],
|
|
70
|
-
identities: { [input.identity.identityHash]: input.identity },
|
|
71
|
-
yoloop: { schemaVersion: 'CommonYoloopAuthority/1.0', ...input.yoloop },
|
|
72
|
-
projectionBindings: {
|
|
73
|
-
schemaVersion: 'EpicOperationalProjectionBindings/1.0',
|
|
74
|
-
trackedState: {
|
|
75
|
-
status: 'bound',
|
|
76
|
-
sourceDigest: input.trackedStateDigest,
|
|
77
|
-
authorityRevision: 0,
|
|
78
|
-
reasonCodes: ['epic-operational-tracked-state-bound'],
|
|
79
|
-
},
|
|
80
|
-
sqlite: {
|
|
81
|
-
status: input.sqliteStatus ?? 'unbound',
|
|
82
|
-
sourceDigest: null,
|
|
83
|
-
authorityRevision: 0,
|
|
84
|
-
reasonCodes: input.sqliteReasonCodes ?? ['epic-operational-projection-unbound'],
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
activeJournal: null,
|
|
88
|
-
backfillReceipts: [],
|
|
89
|
-
reconciliationReceipts: [],
|
|
90
|
-
rollbackReceipts: [],
|
|
91
|
-
};
|
|
92
|
-
assertEpicOperationalState(value);
|
|
93
|
-
return Object.freeze(value);
|
|
94
|
-
}
|
|
95
|
-
export function assertEpicOperationalState(value) {
|
|
96
|
-
const item = record(value);
|
|
97
|
-
if (!item)
|
|
98
|
-
throw new Error('epic-operational-store-corrupt');
|
|
99
|
-
exact(item, [
|
|
100
|
-
'schemaVersion', 'repositoryFingerprint', 'writerEpoch', 'capabilities',
|
|
101
|
-
'identities', 'yoloop', 'projectionBindings', 'activeJournal',
|
|
102
|
-
'backfillReceipts', 'reconciliationReceipts', 'rollbackReceipts',
|
|
103
|
-
]);
|
|
104
|
-
const identities = record(item.identities);
|
|
105
|
-
const yoloop = record(item.yoloop);
|
|
106
|
-
const bindings = record(item.projectionBindings);
|
|
107
|
-
if (item.schemaVersion !== 'EpicOperationalState/1.0' || !hash(item.repositoryFingerprint)
|
|
108
|
-
|| !Number.isSafeInteger(item.writerEpoch) || Number(item.writerEpoch) < 1
|
|
109
|
-
|| !Array.isArray(item.capabilities)
|
|
110
|
-
|| !sameStringSet(item.capabilities, ['epic-operational-state-v1', 'epic-lifecycle-backfill-v1'])
|
|
111
|
-
|| !identities || Object.keys(identities).length > EPIC_OPERATIONAL_IDENTITY_CAP
|
|
112
|
-
|| !yoloop || !hasExactKeys(yoloop, ['schemaVersion', 'lock', 'state', 'capabilities'])
|
|
113
|
-
|| yoloop.schemaVersion !== 'CommonYoloopAuthority/1.0'
|
|
114
|
-
|| !record(yoloop.state) || !(yoloop.lock === null || record(yoloop.lock))
|
|
115
|
-
|| !Array.isArray(yoloop.capabilities) || yoloop.capabilities.length > 16
|
|
116
|
-
|| new Set(yoloop.capabilities).size !== yoloop.capabilities.length
|
|
117
|
-
|| yoloop.capabilities.some((capability) => ![
|
|
118
|
-
'rollover-session-v1', 'successor-transition-v1', 'continuation-replan-v1',
|
|
119
|
-
'cross-epic-continuation-reconcile-v1',
|
|
120
|
-
].includes(String(capability)))
|
|
121
|
-
|| !bindings || !hasExactKeys(bindings, ['schemaVersion', 'trackedState', 'sqlite'])
|
|
122
|
-
|| bindings.schemaVersion !== 'EpicOperationalProjectionBindings/1.0'
|
|
123
|
-
|| !validProjectionBinding(bindings.trackedState) || !validProjectionBinding(bindings.sqlite)
|
|
124
|
-
|| !(item.activeJournal === null || validJournal(item.activeJournal))
|
|
125
|
-
|| !boundedReceipts(item.backfillReceipts, 'EpicLifecycleBackfillReceipt/1.0')
|
|
126
|
-
|| !boundedReceipts(item.reconciliationReceipts, 'YoloopCrossEpicReconciliationReceipt/1.0')
|
|
127
|
-
|| !boundedReceipts(item.rollbackReceipts, 'EpicOperationalRollbackReceipt/1.0')) {
|
|
128
|
-
throw new Error('epic-operational-store-corrupt');
|
|
129
|
-
}
|
|
130
|
-
for (const [identityHash, identity] of Object.entries(identities)) {
|
|
131
|
-
assertEpicOperationalIdentity(identity);
|
|
132
|
-
if (identityHash !== identity.identityHash || identity.repositoryFingerprint !== item.repositoryFingerprint) {
|
|
133
|
-
throw new Error('epic-operational-identity-mismatch');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (Buffer.byteLength(epicOperationalCanonicalJson(item), 'utf8') > EPIC_OPERATIONAL_SOURCE_LIMIT_BYTES) {
|
|
137
|
-
throw new Error('epic-operational-source-oversized');
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
export function readEpicOperationalState(projectRoot, context) {
|
|
141
|
-
const document = readGitLifecycleRuntimeSync(projectRoot, { required: true });
|
|
142
|
-
if (document.schemaVersion !== 2 || document.operational === undefined) {
|
|
143
|
-
throw new Error('epic-operational-store-missing');
|
|
144
|
-
}
|
|
145
|
-
assertEpicOperationalState(document.operational);
|
|
146
|
-
assertOperationalContext(document, document.operational, context);
|
|
147
|
-
return { document, operational: document.operational };
|
|
148
|
-
}
|
|
149
|
-
export async function mutateEpicOperationalState(projectRoot, context, mutate) {
|
|
150
|
-
try {
|
|
151
|
-
return await mutateGitLifecycleRuntime(projectRoot, (document) => {
|
|
152
|
-
if (document.schemaVersion !== 2 || document.operational === undefined) {
|
|
153
|
-
throw new Error('epic-operational-store-missing');
|
|
154
|
-
}
|
|
155
|
-
assertEpicOperationalState(document.operational);
|
|
156
|
-
assertOperationalContext(document, document.operational, context);
|
|
157
|
-
return mutate(document.operational, document);
|
|
158
|
-
}, { expectedRevision: context.expectedRevision });
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
if (error instanceof Error && error.message === 'git-lifecycle-runtime-revision-conflict') {
|
|
162
|
-
throw new Error('epic-operational-revision-conflict');
|
|
163
|
-
}
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
export function projectEpicOperationalState(document, identityHash) {
|
|
168
|
-
if (document.schemaVersion !== 2 || document.operational === undefined) {
|
|
169
|
-
throw new Error('epic-operational-store-missing');
|
|
170
|
-
}
|
|
171
|
-
assertEpicOperationalState(document.operational);
|
|
172
|
-
const state = document.operational;
|
|
173
|
-
const identity = state.identities[identityHash];
|
|
174
|
-
if (!identity)
|
|
175
|
-
throw new Error('epic-operational-identity-mismatch');
|
|
176
|
-
const run = record(state.yoloop.state.runControl);
|
|
177
|
-
const pending = state.yoloop.state.pendingContinuationAuthorities ?? [];
|
|
178
|
-
return Object.freeze({
|
|
179
|
-
schemaVersion: 'EpicOperationalPublicProjection/1.0',
|
|
180
|
-
repositoryFingerprint: state.repositoryFingerprint,
|
|
181
|
-
identityHash,
|
|
182
|
-
epicId: identity.epicId,
|
|
183
|
-
executionBranch: identity.executionBranch,
|
|
184
|
-
graphRevision: identity.graphRevision,
|
|
185
|
-
storeRevision: document.revision,
|
|
186
|
-
writerEpoch: state.writerEpoch,
|
|
187
|
-
runId: typeof run?.runId === 'string' ? run.runId : null,
|
|
188
|
-
runStatus: typeof run?.status === 'string' ? run.status : null,
|
|
189
|
-
runRevision: Number.isSafeInteger(run?.revision) ? Number(run.revision) : null,
|
|
190
|
-
snapshotHash: run?.latestSnapshotHash === null || hash(run?.latestSnapshotHash)
|
|
191
|
-
? run?.latestSnapshotHash : null,
|
|
192
|
-
continuationSessionHash: epicOperationalHash('p224-common-continuation-session-v1', state.yoloop.state.continuationSession ?? null),
|
|
193
|
-
pendingAuthorityHash: epicOperationalHash('p224-common-pending-authorities-v1', pending),
|
|
194
|
-
pendingAuthorityCount: pending.length,
|
|
195
|
-
lockActive: state.yoloop.lock !== null,
|
|
196
|
-
currentSessionActive: state.yoloop.state.currentSession !== null,
|
|
197
|
-
journalActive: state.activeJournal !== null,
|
|
198
|
-
reasonCodes: ['epic-operational-projection-bounded'],
|
|
199
|
-
publicSafe: true,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
export function classifyCrossEpicContinuationReconciliation(input) {
|
|
203
|
-
let projection;
|
|
204
|
-
try {
|
|
205
|
-
projection = projectEpicOperationalState(input.document, input.targetIdentityHash);
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
return { kind: 'blocked', reasonCodes: [publicOperationalReason(error)] };
|
|
209
|
-
}
|
|
210
|
-
const operational = input.document.operational;
|
|
211
|
-
const targetIdentity = operational.identities[input.targetIdentityHash];
|
|
212
|
-
const session = record(operational.yoloop.state.continuationSession);
|
|
213
|
-
if (!session)
|
|
214
|
-
return { kind: 'not-applicable', reasonCodes: ['yoloop-cross-epic-session-missing'] };
|
|
215
|
-
const sourceEpicId = typeof session.epicId === 'string' ? session.epicId : '';
|
|
216
|
-
const sourceGraphRevision = typeof session.graphFingerprint === 'string' ? session.graphFingerprint : '';
|
|
217
|
-
if (sourceEpicId === input.targetEpicId && sourceGraphRevision === input.targetGraphRevision) {
|
|
218
|
-
return { kind: 'not-applicable', reasonCodes: ['yoloop-cross-epic-target-unchanged'] };
|
|
219
|
-
}
|
|
220
|
-
const reasons = [];
|
|
221
|
-
const run = record(operational.yoloop.state.runControl);
|
|
222
|
-
const pending = operational.yoloop.state.pendingContinuationAuthorities ?? [];
|
|
223
|
-
if (!targetIdentity || targetIdentity.epicId !== input.targetEpicId
|
|
224
|
-
|| targetIdentity.graphRevision !== input.targetGraphRevision)
|
|
225
|
-
reasons.push('epic-operational-identity-mismatch');
|
|
226
|
-
if (!operational.yoloop.capabilities.includes('cross-epic-continuation-reconcile-v1'))
|
|
227
|
-
reasons.push('yoloop-cross-epic-capability-missing');
|
|
228
|
-
if (!run || !['success', 'exhausted'].includes(String(run.status)))
|
|
229
|
-
reasons.push('yoloop-cross-epic-predecessor-active');
|
|
230
|
-
if (operational.yoloop.lock !== null || operational.yoloop.state.currentSession !== null
|
|
231
|
-
|| input.legacyActivity !== 'inactive')
|
|
232
|
-
reasons.push('yoloop-cross-epic-lock-active');
|
|
233
|
-
if (operational.activeJournal !== null)
|
|
234
|
-
reasons.push('epic-operational-journal-conflict');
|
|
235
|
-
if (operational.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP)
|
|
236
|
-
reasons.push('yoloop-cross-epic-receipt-capacity');
|
|
237
|
-
if (pending.length > 1)
|
|
238
|
-
reasons.push('yoloop-cross-epic-authority-ambiguous');
|
|
239
|
-
if (pending.length === 1) {
|
|
240
|
-
const authority = record(pending[0]);
|
|
241
|
-
const certificate = record(authority?.certificate);
|
|
242
|
-
if (!authority || !certificate || authority.sessionId !== session.sessionId
|
|
243
|
-
|| certificate.sessionId !== session.sessionId)
|
|
244
|
-
reasons.push('yoloop-cross-epic-authority-ambiguous');
|
|
245
|
-
else if (authority.delegated !== false)
|
|
246
|
-
reasons.push('yoloop-cross-epic-authority-delegated');
|
|
247
|
-
}
|
|
248
|
-
if (!hash(sourceGraphRevision) || !hash(session.sessionId)
|
|
249
|
-
|| projection.runId === null || projection.runRevision === null)
|
|
250
|
-
reasons.push('yoloop-cross-epic-state-corrupt');
|
|
251
|
-
if (reasons.length > 0)
|
|
252
|
-
return { kind: 'blocked', reasonCodes: [...new Set(reasons)] };
|
|
253
|
-
return {
|
|
254
|
-
kind: 'eligible',
|
|
255
|
-
evidence: Object.freeze({
|
|
256
|
-
...projection,
|
|
257
|
-
schemaVersion: 'YoloopCrossEpicReconciliationEvidence/1.0',
|
|
258
|
-
sourceEpicId,
|
|
259
|
-
sourceGraphRevision,
|
|
260
|
-
targetEpicId: input.targetEpicId,
|
|
261
|
-
targetGraphRevision: input.targetGraphRevision,
|
|
262
|
-
decision: 'eligible',
|
|
263
|
-
}),
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
export function buildCrossEpicContinuationPayloadHash(payload) {
|
|
267
|
-
return epicOperationalHash('p224-cross-epic-continuation-reconcile-v1', payload);
|
|
268
|
-
}
|
|
269
|
-
export async function applyCrossEpicContinuationReconciliation(projectRoot, operation) {
|
|
270
|
-
validateCrossEpicOperation(operation);
|
|
271
|
-
let current = await readGitLifecycleRuntime(projectRoot, { required: true });
|
|
272
|
-
if (current.schemaVersion !== 2 || current.operational === undefined)
|
|
273
|
-
throw new Error('epic-operational-store-missing');
|
|
274
|
-
assertEpicOperationalState(current.operational);
|
|
275
|
-
let operational = current.operational;
|
|
276
|
-
const expectedReceipt = crossEpicReceipt(operation);
|
|
277
|
-
const replay = operational.reconciliationReceipts.find((receipt) => receipt.operationId === operation.operationId || receipt.idempotencyKey === operation.idempotencyKey);
|
|
278
|
-
if (replay) {
|
|
279
|
-
if (epicOperationalCanonicalJson(replay) !== epicOperationalCanonicalJson(expectedReceipt)
|
|
280
|
-
|| epicOperationalHash('p224-common-continuation-session-v1', operational.yoloop.state.continuationSession ?? null)
|
|
281
|
-
!== operation.nextSessionHash
|
|
282
|
-
|| (operational.yoloop.state.pendingContinuationAuthorities?.length ?? 0) !== 0) {
|
|
283
|
-
throw new Error('yoloop-cross-epic-replay-conflict');
|
|
284
|
-
}
|
|
285
|
-
return { applied: false, replay: true, receipt: replay };
|
|
286
|
-
}
|
|
287
|
-
if (operational.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP) {
|
|
288
|
-
throw new Error('yoloop-cross-epic-receipt-capacity');
|
|
289
|
-
}
|
|
290
|
-
const existingJournal = operational.activeJournal;
|
|
291
|
-
if (existingJournal && (existingJournal.operationId !== operation.operationId
|
|
292
|
-
|| existingJournal.idempotencyKey !== operation.idempotencyKey
|
|
293
|
-
|| existingJournal.payloadHash !== operation.payloadHash)) {
|
|
294
|
-
throw new Error('epic-operational-journal-conflict');
|
|
295
|
-
}
|
|
296
|
-
if (!existingJournal)
|
|
297
|
-
assertCrossEpicPreimage(current, operational, operation);
|
|
298
|
-
const backup = existingJournal
|
|
299
|
-
? verifyOperationalBackup(projectRoot, existingJournal.backupManifestRef, existingJournal.preimageDigest)
|
|
300
|
-
: persistOperationalBackup(projectRoot, operation.operationId, operational);
|
|
301
|
-
if (!existingJournal) {
|
|
302
|
-
const journal = crossEpicJournal(operation, backup.digest, backup.ref, 'prepared');
|
|
303
|
-
const result = await mutateEpicOperationalState(projectRoot, {
|
|
304
|
-
identityHash: operation.expectedIdentityHash,
|
|
305
|
-
expectedRevision: current.revision,
|
|
306
|
-
expectedWriterEpoch: operation.expectedWriterEpoch,
|
|
307
|
-
}, (state) => {
|
|
308
|
-
state.activeJournal = journal;
|
|
309
|
-
});
|
|
310
|
-
current = result.document;
|
|
311
|
-
operational = current.operational;
|
|
312
|
-
}
|
|
313
|
-
if (operational.activeJournal?.phase === 'prepared') {
|
|
314
|
-
assertCrossEpicPreimage(current, operational, operation, true);
|
|
315
|
-
const nextState = {
|
|
316
|
-
...operational.yoloop.state,
|
|
317
|
-
continuationSession: operation.nextSession,
|
|
318
|
-
pendingContinuationAuthorities: [],
|
|
319
|
-
};
|
|
320
|
-
const result = await mutateEpicOperationalState(projectRoot, {
|
|
321
|
-
identityHash: operation.expectedIdentityHash,
|
|
322
|
-
expectedRevision: current.revision,
|
|
323
|
-
expectedWriterEpoch: operation.expectedWriterEpoch,
|
|
324
|
-
}, (state) => {
|
|
325
|
-
state.yoloop = { ...state.yoloop, state: nextState };
|
|
326
|
-
state.activeJournal = {
|
|
327
|
-
...state.activeJournal, phase: 'authority-applied',
|
|
328
|
-
};
|
|
329
|
-
});
|
|
330
|
-
current = result.document;
|
|
331
|
-
operational = current.operational;
|
|
332
|
-
}
|
|
333
|
-
if (operational.activeJournal?.phase === 'authority-applied') {
|
|
334
|
-
const result = await mutateEpicOperationalState(projectRoot, {
|
|
335
|
-
identityHash: operation.expectedIdentityHash,
|
|
336
|
-
expectedRevision: current.revision,
|
|
337
|
-
expectedWriterEpoch: operation.expectedWriterEpoch,
|
|
338
|
-
}, (state) => {
|
|
339
|
-
state.activeJournal = {
|
|
340
|
-
...state.activeJournal, phase: 'projections-applied',
|
|
341
|
-
projectionStatuses: { trackedState: 'not-applicable', sqlite: 'not-applicable' },
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
current = result.document;
|
|
345
|
-
operational = current.operational;
|
|
346
|
-
}
|
|
347
|
-
if (operational.activeJournal?.phase !== 'projections-applied') {
|
|
348
|
-
throw new Error('epic-operational-journal-corrupt');
|
|
349
|
-
}
|
|
350
|
-
const result = await mutateEpicOperationalState(projectRoot, {
|
|
351
|
-
identityHash: operation.expectedIdentityHash,
|
|
352
|
-
expectedRevision: current.revision,
|
|
353
|
-
expectedWriterEpoch: operation.expectedWriterEpoch,
|
|
354
|
-
}, (state) => {
|
|
355
|
-
if (state.reconciliationReceipts.length >= EPIC_OPERATIONAL_RECEIPT_CAP) {
|
|
356
|
-
throw new Error('yoloop-cross-epic-receipt-capacity');
|
|
357
|
-
}
|
|
358
|
-
state.reconciliationReceipts = [
|
|
359
|
-
...state.reconciliationReceipts,
|
|
360
|
-
expectedReceipt,
|
|
361
|
-
];
|
|
362
|
-
state.activeJournal = null;
|
|
363
|
-
});
|
|
364
|
-
assertEpicOperationalState(result.document.operational);
|
|
365
|
-
return { applied: true, replay: false, receipt: expectedReceipt };
|
|
366
|
-
}
|
|
367
|
-
function assertCrossEpicPreimage(document, state, operation, allowPrepared = false) {
|
|
368
|
-
const run = record(state.yoloop.state.runControl);
|
|
369
|
-
const pending = state.yoloop.state.pendingContinuationAuthorities ?? [];
|
|
370
|
-
const identity = state.identities[operation.expectedIdentityHash];
|
|
371
|
-
const session = record(state.yoloop.state.continuationSession);
|
|
372
|
-
if ((document.revision !== operation.expectedCommonRevision && !allowPrepared)
|
|
373
|
-
|| state.writerEpoch !== operation.expectedWriterEpoch
|
|
374
|
-
|| !identity || identity.epicId !== operation.targetEpicId
|
|
375
|
-
|| identity.graphRevision !== operation.targetGraphRevision
|
|
376
|
-
|| !state.yoloop.capabilities.includes('cross-epic-continuation-reconcile-v1')
|
|
377
|
-
|| state.yoloop.lock !== null || state.yoloop.state.currentSession !== null
|
|
378
|
-
|| !run || run.runId !== operation.expectedRunId || run.status !== operation.expectedRunStatus
|
|
379
|
-
|| run.revision !== operation.expectedRunRevision
|
|
380
|
-
|| (run.latestSnapshotHash ?? null) !== operation.expectedSnapshotHash
|
|
381
|
-
|| !session || session.epicId !== operation.sourceEpicId
|
|
382
|
-
|| session.graphFingerprint !== operation.sourceGraphRevision
|
|
383
|
-
|| session.sessionId !== operation.sourceSessionId
|
|
384
|
-
|| epicOperationalHash('p224-common-continuation-session-v1', state.yoloop.state.continuationSession ?? null)
|
|
385
|
-
!== operation.expectedSessionHash
|
|
386
|
-
|| epicOperationalHash('p224-common-pending-authorities-v1', pending) !== operation.expectedPendingHash
|
|
387
|
-
|| pending.length !== operation.expectedPendingCount) {
|
|
388
|
-
throw new Error('yoloop-cross-epic-revision-conflict');
|
|
389
|
-
}
|
|
390
|
-
if (pending.length > 1 || pending.some((entry) => {
|
|
391
|
-
const authority = record(entry);
|
|
392
|
-
const certificate = record(authority?.certificate);
|
|
393
|
-
return !authority || authority.delegated !== false || !certificate
|
|
394
|
-
|| authority.sessionId !== operation.sourceSessionId
|
|
395
|
-
|| certificate.sessionId !== operation.sourceSessionId;
|
|
396
|
-
}))
|
|
397
|
-
throw new Error('yoloop-cross-epic-authority-ambiguous');
|
|
398
|
-
}
|
|
399
|
-
function crossEpicReceipt(operation) {
|
|
400
|
-
return Object.freeze({
|
|
401
|
-
schemaVersion: 'YoloopCrossEpicReconciliationReceipt/1.0',
|
|
402
|
-
operationId: operation.operationId,
|
|
403
|
-
idempotencyKey: operation.idempotencyKey,
|
|
404
|
-
payloadHash: operation.payloadHash,
|
|
405
|
-
sourceEpicId: operation.sourceEpicId,
|
|
406
|
-
sourceGraphRevision: operation.sourceGraphRevision,
|
|
407
|
-
targetEpicId: operation.targetEpicId,
|
|
408
|
-
targetGraphRevision: operation.targetGraphRevision,
|
|
409
|
-
runId: operation.expectedRunId,
|
|
410
|
-
previousSessionHash: operation.expectedSessionHash,
|
|
411
|
-
nextSessionHash: operation.nextSessionHash,
|
|
412
|
-
retiredAuthorityHash: operation.expectedPendingHash,
|
|
413
|
-
retiredAuthorityCount: operation.expectedPendingCount,
|
|
414
|
-
outcome: 'cross-epic-reconciled',
|
|
415
|
-
publicSafe: true,
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
function crossEpicJournal(operation, preimageDigest, backupManifestRef, phase) {
|
|
419
|
-
return {
|
|
420
|
-
schemaVersion: 'EpicOperationalJournal/1.0',
|
|
421
|
-
kind: 'cross-epic-reconcile',
|
|
422
|
-
operationId: operation.operationId,
|
|
423
|
-
idempotencyKey: operation.idempotencyKey,
|
|
424
|
-
payloadHash: operation.payloadHash,
|
|
425
|
-
identityHash: operation.expectedIdentityHash,
|
|
426
|
-
expectedRevision: operation.expectedCommonRevision,
|
|
427
|
-
expectedWriterEpoch: operation.expectedWriterEpoch,
|
|
428
|
-
preimageDigest,
|
|
429
|
-
backupManifestRef,
|
|
430
|
-
phase,
|
|
431
|
-
projectionStatuses: { trackedState: 'not-applicable', sqlite: 'not-applicable' },
|
|
432
|
-
reasonCodes: ['epic-operational-backup-verified'],
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
function validateCrossEpicOperation(operation) {
|
|
436
|
-
const { type: _type, payloadHash: _payloadHash, receipt: _receipt, ...payload } = operation;
|
|
437
|
-
const nextSession = record(operation.nextSession);
|
|
438
|
-
if (!safeOperationId(operation.operationId) || !safeOperationId(operation.idempotencyKey)
|
|
439
|
-
|| buildCrossEpicContinuationPayloadHash(payload) !== operation.payloadHash
|
|
440
|
-
|| Buffer.byteLength(epicOperationalCanonicalJson(payload), 'utf8') > EPIC_OPERATIONAL_PAYLOAD_LIMIT_BYTES
|
|
441
|
-
|| operation.expectedPendingCount < 0 || operation.expectedPendingCount > 1
|
|
442
|
-
|| !nextSession || nextSession.epicId !== operation.targetEpicId
|
|
443
|
-
|| nextSession.graphFingerprint !== operation.targetGraphRevision
|
|
444
|
-
|| !hash(nextSession.sessionId)
|
|
445
|
-
|| operation.nextSessionHash !== epicOperationalHash('p224-common-continuation-session-v1', operation.nextSession)
|
|
446
|
-
|| epicOperationalCanonicalJson(operation.receipt) !== epicOperationalCanonicalJson(crossEpicReceipt(operation))) {
|
|
447
|
-
throw new Error('yoloop-cross-epic-replay-conflict');
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
function persistOperationalBackup(projectRoot, operationId, operational) {
|
|
451
|
-
if (!safeOperationId(operationId))
|
|
452
|
-
throw new Error('epic-operational-backup-failed');
|
|
453
|
-
const canonicalRoot = realpathSync(resolve(projectRoot));
|
|
454
|
-
const rawCommon = execFileSync('git', ['rev-parse', '--git-common-dir'], { cwd: canonicalRoot, encoding: 'utf8' }).trim();
|
|
455
|
-
const commonDir = realpathSync(isAbsolute(rawCommon) ? resolve(rawCommon) : resolve(canonicalRoot, rawCommon));
|
|
456
|
-
const runtimeDir = join(commonDir, GIT_LIFECYCLE_RUNTIME_DIRECTORY);
|
|
457
|
-
const backupsDir = join(runtimeDir, 'backups');
|
|
458
|
-
assertPrivateDirectory(runtimeDir, commonDir);
|
|
459
|
-
if (!existsSync(backupsDir))
|
|
460
|
-
mkdirSync(backupsDir, { mode: 0o700 });
|
|
461
|
-
assertPrivateDirectory(backupsDir, runtimeDir);
|
|
462
|
-
const name = `${operationId}.operational.json`;
|
|
463
|
-
const path = join(backupsDir, name);
|
|
464
|
-
const raw = `${JSON.stringify(operational, null, 2)}\n`;
|
|
465
|
-
const digest = createHash('sha256').update(raw).digest('hex');
|
|
466
|
-
if (existsSync(path)) {
|
|
467
|
-
const info = lstatSync(path);
|
|
468
|
-
if (!info.isFile() || info.isSymbolicLink()
|
|
469
|
-
|| createHash('sha256').update(readFileSync(path)).digest('hex') !== digest) {
|
|
470
|
-
throw new Error('epic-operational-backup-failed');
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
const fd = openSync(path, 'wx', 0o600);
|
|
475
|
-
try {
|
|
476
|
-
writeFileSync(fd, raw);
|
|
477
|
-
fsyncSync(fd);
|
|
478
|
-
}
|
|
479
|
-
finally {
|
|
480
|
-
closeSync(fd);
|
|
481
|
-
}
|
|
482
|
-
const directoryFd = openSync(backupsDir, 'r');
|
|
483
|
-
try {
|
|
484
|
-
fsyncSync(directoryFd);
|
|
485
|
-
}
|
|
486
|
-
finally {
|
|
487
|
-
closeSync(directoryFd);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return { ref: `backups/${name}`, digest };
|
|
491
|
-
}
|
|
492
|
-
function verifyOperationalBackup(projectRoot, backupRef, expectedDigest) {
|
|
493
|
-
if (!/^backups\/[A-Za-z0-9._-]{1,240}$/.test(backupRef) || !hash(expectedDigest)) {
|
|
494
|
-
throw new Error('epic-operational-backup-failed');
|
|
495
|
-
}
|
|
496
|
-
const canonicalRoot = realpathSync(resolve(projectRoot));
|
|
497
|
-
const rawCommon = execFileSync('git', ['rev-parse', '--git-common-dir'], {
|
|
498
|
-
cwd: canonicalRoot,
|
|
499
|
-
encoding: 'utf8',
|
|
500
|
-
}).trim();
|
|
501
|
-
const commonDir = realpathSync(isAbsolute(rawCommon) ? resolve(rawCommon) : resolve(canonicalRoot, rawCommon));
|
|
502
|
-
const runtimeDir = join(commonDir, GIT_LIFECYCLE_RUNTIME_DIRECTORY);
|
|
503
|
-
const path = join(runtimeDir, backupRef);
|
|
504
|
-
assertPrivateDirectory(runtimeDir, commonDir);
|
|
505
|
-
const info = lstatSync(path);
|
|
506
|
-
if (!info.isFile() || info.isSymbolicLink()
|
|
507
|
-
|| createHash('sha256').update(readFileSync(path)).digest('hex') !== expectedDigest) {
|
|
508
|
-
throw new Error('epic-operational-backup-failed');
|
|
509
|
-
}
|
|
510
|
-
return { ref: backupRef, digest: expectedDigest };
|
|
511
|
-
}
|
|
512
|
-
function assertPrivateDirectory(path, parent) {
|
|
513
|
-
const canonicalParent = realpathSync(parent);
|
|
514
|
-
if (relative(canonicalParent, resolve(path)).startsWith('..'))
|
|
515
|
-
throw new Error('epic-operational-path-unsafe');
|
|
516
|
-
const info = lstatSync(path);
|
|
517
|
-
if (!info.isDirectory() || info.isSymbolicLink() || realpathSync(path) !== resolve(path)
|
|
518
|
-
|| (typeof process.getuid === 'function' && (info.uid !== process.getuid() || (info.mode & 0o077) !== 0))) {
|
|
519
|
-
throw new Error('epic-operational-path-unsafe');
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
function assertOperationalContext(document, state, context) {
|
|
523
|
-
if (context?.expectedRevision !== undefined && document.revision !== context.expectedRevision) {
|
|
524
|
-
throw new Error('epic-operational-revision-conflict');
|
|
525
|
-
}
|
|
526
|
-
if (context?.expectedWriterEpoch !== undefined && state.writerEpoch !== context.expectedWriterEpoch) {
|
|
527
|
-
throw new Error('epic-operational-epoch-conflict');
|
|
528
|
-
}
|
|
529
|
-
if (context?.identityHash !== undefined && !state.identities[context.identityHash]) {
|
|
530
|
-
throw new Error('epic-operational-identity-mismatch');
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
function assertEpicOperationalIdentity(value) {
|
|
534
|
-
const item = record(value);
|
|
535
|
-
if (!item)
|
|
536
|
-
throw new Error('epic-operational-identity-mismatch');
|
|
537
|
-
exact(item, [
|
|
538
|
-
'schemaVersion', 'epicId', 'executionBranch', 'graphRevision',
|
|
539
|
-
'originBindingDigest', 'repositoryFingerprint', 'identityHash',
|
|
540
|
-
]);
|
|
541
|
-
const { identityHash, ...base } = item;
|
|
542
|
-
if (item.schemaVersion !== 'EpicOperationalIdentity/1.0' || !ID.test(String(item.epicId))
|
|
543
|
-
|| !safeGitRef(item.executionBranch) || !hash(item.graphRevision)
|
|
544
|
-
|| !hash(item.originBindingDigest) || !hash(item.repositoryFingerprint)
|
|
545
|
-
|| identityHash !== epicOperationalHash('p224-epic-operational-identity-v1', base)) {
|
|
546
|
-
throw new Error('epic-operational-identity-mismatch');
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
function validProjectionBinding(value) {
|
|
550
|
-
const item = record(value);
|
|
551
|
-
return Boolean(item && hasExactKeys(item, ['status', 'sourceDigest', 'authorityRevision', 'reasonCodes'])
|
|
552
|
-
&& ['bound', 'unbound', 'unavailable'].includes(String(item.status))
|
|
553
|
-
&& (item.sourceDigest === null || hash(item.sourceDigest))
|
|
554
|
-
&& Number.isSafeInteger(item.authorityRevision) && Number(item.authorityRevision) >= 0
|
|
555
|
-
&& Array.isArray(item.reasonCodes) && item.reasonCodes.length <= 16
|
|
556
|
-
&& item.reasonCodes.every(safeReasonCode));
|
|
557
|
-
}
|
|
558
|
-
function validJournal(value) {
|
|
559
|
-
const item = record(value);
|
|
560
|
-
const statuses = record(item?.projectionStatuses);
|
|
561
|
-
return Boolean(item && hasExactKeys(item, [
|
|
562
|
-
'schemaVersion', 'kind', 'operationId', 'idempotencyKey', 'payloadHash',
|
|
563
|
-
'identityHash', 'expectedRevision', 'expectedWriterEpoch', 'preimageDigest',
|
|
564
|
-
'backupManifestRef', 'phase', 'projectionStatuses', 'reasonCodes',
|
|
565
|
-
]) && item.schemaVersion === 'EpicOperationalJournal/1.0'
|
|
566
|
-
&& ['backfill', 'cross-epic-reconcile', 'rollback'].includes(String(item.kind))
|
|
567
|
-
&& safeToken(item.operationId) && safeToken(item.idempotencyKey) && hash(item.payloadHash)
|
|
568
|
-
&& hash(item.identityHash) && Number.isSafeInteger(item.expectedRevision)
|
|
569
|
-
&& Number.isSafeInteger(item.expectedWriterEpoch) && hash(item.preimageDigest)
|
|
570
|
-
&& typeof item.backupManifestRef === 'string' && item.backupManifestRef.length <= 1_024
|
|
571
|
-
&& !isAbsolute(item.backupManifestRef) && !item.backupManifestRef.includes('..')
|
|
572
|
-
&& ['prepared', 'authority-applied', 'projections-applied', 'committed', 'aborted-before-authority'].includes(String(item.phase))
|
|
573
|
-
&& statuses && hasExactKeys(statuses, ['trackedState', 'sqlite'])
|
|
574
|
-
&& ['pending', 'applied', 'not-applicable'].includes(String(statuses.trackedState))
|
|
575
|
-
&& ['pending', 'applied', 'unavailable', 'not-applicable'].includes(String(statuses.sqlite))
|
|
576
|
-
&& Array.isArray(item.reasonCodes) && item.reasonCodes.length <= 16
|
|
577
|
-
&& item.reasonCodes.every(safeReasonCode));
|
|
578
|
-
}
|
|
579
|
-
function boundedReceipts(value, schema) {
|
|
580
|
-
return Array.isArray(value) && value.length <= EPIC_OPERATIONAL_RECEIPT_CAP
|
|
581
|
-
&& value.every((entry) => schema === 'EpicLifecycleBackfillReceipt/1.0'
|
|
582
|
-
? validBackfillReceipt(entry)
|
|
583
|
-
: schema === 'YoloopCrossEpicReconciliationReceipt/1.0'
|
|
584
|
-
? validReconciliationReceipt(entry)
|
|
585
|
-
: validRollbackReceipt(entry));
|
|
586
|
-
}
|
|
587
|
-
function validBackfillReceipt(value) {
|
|
588
|
-
const item = record(value);
|
|
589
|
-
return Boolean(item && hasExactKeys(item, [
|
|
590
|
-
'schemaVersion', 'operationId', 'idempotencyKey', 'payloadHash', 'planDigest',
|
|
591
|
-
'sourceInventoryDigest', 'preimageDigest', 'postimageDigest',
|
|
592
|
-
'authorityPostimageDigest', 'backupManifestDigest', 'identityHash',
|
|
593
|
-
'appliedRevision', 'writerEpoch', 'stateMigrationOperationId', 'outcome',
|
|
594
|
-
'reasonCodes', 'publicSafe',
|
|
595
|
-
]) && item.schemaVersion === 'EpicLifecycleBackfillReceipt/1.0'
|
|
596
|
-
&& safeOperationId(item.operationId) && safeOperationId(item.idempotencyKey)
|
|
597
|
-
&& item.operationId === item.idempotencyKey
|
|
598
|
-
&& [item.payloadHash, item.planDigest, item.sourceInventoryDigest, item.preimageDigest,
|
|
599
|
-
item.postimageDigest, item.authorityPostimageDigest, item.backupManifestDigest,
|
|
600
|
-
item.identityHash].every(hash)
|
|
601
|
-
&& Number.isSafeInteger(item.appliedRevision) && Number(item.appliedRevision) >= 1
|
|
602
|
-
&& Number.isSafeInteger(item.writerEpoch) && Number(item.writerEpoch) >= 1
|
|
603
|
-
&& (item.stateMigrationOperationId === null || safeOperationId(item.stateMigrationOperationId))
|
|
604
|
-
&& ['applied', 'already-applied'].includes(String(item.outcome))
|
|
605
|
-
&& validReasonCodes(item.reasonCodes) && item.publicSafe === true);
|
|
606
|
-
}
|
|
607
|
-
function validReconciliationReceipt(value) {
|
|
608
|
-
const item = record(value);
|
|
609
|
-
return Boolean(item && hasExactKeys(item, [
|
|
610
|
-
'schemaVersion', 'operationId', 'idempotencyKey', 'payloadHash', 'sourceEpicId',
|
|
611
|
-
'sourceGraphRevision', 'targetEpicId', 'targetGraphRevision', 'runId',
|
|
612
|
-
'previousSessionHash', 'nextSessionHash', 'retiredAuthorityHash',
|
|
613
|
-
'retiredAuthorityCount', 'outcome', 'publicSafe',
|
|
614
|
-
]) && item.schemaVersion === 'YoloopCrossEpicReconciliationReceipt/1.0'
|
|
615
|
-
&& safeOperationId(item.operationId) && safeOperationId(item.idempotencyKey)
|
|
616
|
-
&& hash(item.payloadHash) && ID.test(String(item.sourceEpicId)) && ID.test(String(item.targetEpicId))
|
|
617
|
-
&& hash(item.sourceGraphRevision) && hash(item.targetGraphRevision)
|
|
618
|
-
&& safeOperationId(item.runId) && hash(item.previousSessionHash) && hash(item.nextSessionHash)
|
|
619
|
-
&& hash(item.retiredAuthorityHash) && [0, 1].includes(Number(item.retiredAuthorityCount))
|
|
620
|
-
&& item.outcome === 'cross-epic-reconciled' && item.publicSafe === true);
|
|
621
|
-
}
|
|
622
|
-
function validRollbackReceipt(value) {
|
|
623
|
-
const item = record(value);
|
|
624
|
-
return Boolean(item && hasExactKeys(item, [
|
|
625
|
-
'schemaVersion', 'operationId', 'targetOperationId', 'expectedPostimageDigest',
|
|
626
|
-
'restoredPreimageDigest', 'reasonCodes', 'publicSafe',
|
|
627
|
-
]) && item.schemaVersion === 'EpicOperationalRollbackReceipt/1.0'
|
|
628
|
-
&& safeOperationId(item.operationId) && safeOperationId(item.targetOperationId)
|
|
629
|
-
&& hash(item.expectedPostimageDigest) && hash(item.restoredPreimageDigest)
|
|
630
|
-
&& validReasonCodes(item.reasonCodes) && item.publicSafe === true);
|
|
631
|
-
}
|
|
632
|
-
function validReasonCodes(value) {
|
|
633
|
-
return Array.isArray(value) && value.length <= 16 && value.every(safeReasonCode);
|
|
634
|
-
}
|
|
635
|
-
function sameStringSet(value, expected) {
|
|
636
|
-
return value.length === expected.length && expected.every((item) => value.includes(item));
|
|
637
|
-
}
|
|
638
|
-
function exact(item, keys) {
|
|
639
|
-
const allowed = new Set(keys);
|
|
640
|
-
if (Object.keys(item).some((key) => !allowed.has(key)))
|
|
641
|
-
throw new Error('epic-operational-store-corrupt');
|
|
642
|
-
}
|
|
643
|
-
function hasExactKeys(item, keys) {
|
|
644
|
-
const actual = Object.keys(item).sort();
|
|
645
|
-
const expected = [...keys].sort();
|
|
646
|
-
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
647
|
-
}
|
|
648
|
-
function record(value) {
|
|
649
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
650
|
-
? value : undefined;
|
|
651
|
-
}
|
|
652
|
-
function hash(value) {
|
|
653
|
-
return typeof value === 'string' && HASH.test(value);
|
|
654
|
-
}
|
|
655
|
-
function safeToken(value) {
|
|
656
|
-
return typeof value === 'string' && SAFE_TOKEN.test(value) && !value.includes('..');
|
|
657
|
-
}
|
|
658
|
-
function safeOperationId(value) {
|
|
659
|
-
return typeof value === 'string' && /^[A-Za-z0-9._:-]{1,128}$/.test(value);
|
|
660
|
-
}
|
|
661
|
-
function safeReasonCode(value) {
|
|
662
|
-
return typeof value === 'string' && /^[a-z0-9][a-z0-9-]{0,79}$/.test(value);
|
|
663
|
-
}
|
|
664
|
-
function safeGitRef(value) {
|
|
665
|
-
return typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$/.test(value)
|
|
666
|
-
&& !value.includes('..') && !value.includes('//') && !value.includes('@{')
|
|
667
|
-
&& !value.endsWith('/') && !value.endsWith('.');
|
|
668
|
-
}
|
|
669
|
-
function publicOperationalReason(error) {
|
|
670
|
-
const reason = error instanceof Error ? error.message : '';
|
|
671
|
-
return safeReasonCode(reason) ? reason : 'epic-operational-store-corrupt';
|
|
672
|
-
}
|
|
1
|
+
import{execFileSync as O}from"node:child_process";import{createHash as S}from"node:crypto";import{existsSync as B,fsyncSync as V,lstatSync as w,mkdirSync as Y,openSync as _,closeSync as J,readFileSync as N,realpathSync as E,writeFileSync as q}from"node:fs";import{isAbsolute as x,join as R,relative as Z,resolve as y}from"node:path";import{GIT_LIFECYCLE_RUNTIME_DIRECTORY as F,mutateGitLifecycleRuntime as U,readGitLifecycleRuntime as Q,readGitLifecycleRuntimeSync as X}from"./git-lifecycle-store.js";const ee=4*1024*1024,ie=1024*1024,C=64,te=2048,oe=/^[a-f0-9]{64}$/,A=/^P[1-9][0-9]*(?:\.[0-9]+)*$/,ne=/^[A-Za-z0-9._:/#\-[\]]{1,240}$/;function m(i){return i===null||typeof i!="object"?JSON.stringify(i):Array.isArray(i)?`[${i.map(m).join(",")}]`:`{${Object.entries(i).filter(([,e])=>e!==void 0).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${m(t)}`).join(",")}}`}function g(i,e){return S("sha256").update(`${i}\0${m(e)}`).digest("hex")}function re(i){const e=E(y(i)),t=O("git",["rev-parse","--git-common-dir"],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim(),o=x(t)?y(t):y(e,t),n=E(o);if(n!==o)throw new Error("epic-operational-path-unsafe");const r=w(n);if(!r.isDirectory()||r.isSymbolicLink())throw new Error("epic-operational-path-unsafe");return g("p224-epic-operational-repository-v1",{commonDir:n,dev:Number(r.dev),ino:Number(r.ino)})}function we(i,e){const t=re(i),o={schemaVersion:"EpicOperationalIdentity/1.0",epicId:e.epicId,executionBranch:e.executionBranch,graphRevision:e.graphRevision,originBindingDigest:e.originBindingDigest,repositoryFingerprint:t},n=Object.freeze({...o,identityHash:g("p224-epic-operational-identity-v1",o)});return W(n),n}function xe(i){const e={schemaVersion:"EpicOperationalState/1.0",repositoryFingerprint:i.identity.repositoryFingerprint,writerEpoch:1,capabilities:["epic-operational-state-v1","epic-lifecycle-backfill-v1"],identities:{[i.identity.identityHash]:i.identity},yoloop:{schemaVersion:"CommonYoloopAuthority/1.0",...i.yoloop},projectionBindings:{schemaVersion:"EpicOperationalProjectionBindings/1.0",trackedState:{status:"bound",sourceDigest:i.trackedStateDigest,authorityRevision:0,reasonCodes:["epic-operational-tracked-state-bound"]},sqlite:{status:i.sqliteStatus??"unbound",sourceDigest:null,authorityRevision:0,reasonCodes:i.sqliteReasonCodes??["epic-operational-projection-unbound"]}},activeJournal:null,backfillReceipts:[],reconciliationReceipts:[],rollbackReceipts:[]};return I(e),Object.freeze(e)}function I(i){const e=p(i);if(!e)throw new Error("epic-operational-store-corrupt");z(e,["schemaVersion","repositoryFingerprint","writerEpoch","capabilities","identities","yoloop","projectionBindings","activeJournal","backfillReceipts","reconciliationReceipts","rollbackReceipts"]);const t=p(e.identities),o=p(e.yoloop),n=p(e.projectionBindings);if(e.schemaVersion!=="EpicOperationalState/1.0"||!l(e.repositoryFingerprint)||!Number.isSafeInteger(e.writerEpoch)||Number(e.writerEpoch)<1||!Array.isArray(e.capabilities)||!ge(e.capabilities,["epic-operational-state-v1","epic-lifecycle-backfill-v1"])||!t||Object.keys(t).length>te||!o||!v(o,["schemaVersion","lock","state","capabilities"])||o.schemaVersion!=="CommonYoloopAuthority/1.0"||!p(o.state)||!(o.lock===null||p(o.lock))||!Array.isArray(o.capabilities)||o.capabilities.length>16||new Set(o.capabilities).size!==o.capabilities.length||o.capabilities.some(r=>!["rollover-session-v1","successor-transition-v1","continuation-replan-v1","cross-epic-continuation-reconcile-v1"].includes(String(r)))||!n||!v(n,["schemaVersion","trackedState","sqlite"])||n.schemaVersion!=="EpicOperationalProjectionBindings/1.0"||!T(n.trackedState)||!T(n.sqlite)||!(e.activeJournal===null||ue(e.activeJournal))||!P(e.backfillReceipts,"EpicLifecycleBackfillReceipt/1.0")||!P(e.reconciliationReceipts,"YoloopCrossEpicReconciliationReceipt/1.0")||!P(e.rollbackReceipts,"EpicOperationalRollbackReceipt/1.0"))throw new Error("epic-operational-store-corrupt");for(const[r,c]of Object.entries(t))if(W(c),r!==c.identityHash||c.repositoryFingerprint!==e.repositoryFingerprint)throw new Error("epic-operational-identity-mismatch");if(Buffer.byteLength(m(e),"utf8")>ee)throw new Error("epic-operational-source-oversized")}function Ce(i,e){const t=X(i,{required:!0});if(t.schemaVersion!==2||t.operational===void 0)throw new Error("epic-operational-store-missing");return I(t.operational),K(t,t.operational,e),{document:t,operational:t.operational}}async function k(i,e,t){try{return await U(i,o=>{if(o.schemaVersion!==2||o.operational===void 0)throw new Error("epic-operational-store-missing");return I(o.operational),K(o,o.operational,e),t(o.operational,o)},{expectedRevision:e.expectedRevision})}catch(o){throw o instanceof Error&&o.message==="git-lifecycle-runtime-revision-conflict"?new Error("epic-operational-revision-conflict"):o}}function se(i,e){if(i.schemaVersion!==2||i.operational===void 0)throw new Error("epic-operational-store-missing");I(i.operational);const t=i.operational,o=t.identities[e];if(!o)throw new Error("epic-operational-identity-mismatch");const n=p(t.yoloop.state.runControl),r=t.yoloop.state.pendingContinuationAuthorities??[];return Object.freeze({schemaVersion:"EpicOperationalPublicProjection/1.0",repositoryFingerprint:t.repositoryFingerprint,identityHash:e,epicId:o.epicId,executionBranch:o.executionBranch,graphRevision:o.graphRevision,storeRevision:i.revision,writerEpoch:t.writerEpoch,runId:typeof n?.runId=="string"?n.runId:null,runStatus:typeof n?.status=="string"?n.status:null,runRevision:Number.isSafeInteger(n?.revision)?Number(n.revision):null,snapshotHash:n?.latestSnapshotHash===null||l(n?.latestSnapshotHash)?n?.latestSnapshotHash:null,continuationSessionHash:g("p224-common-continuation-session-v1",t.yoloop.state.continuationSession??null),pendingAuthorityHash:g("p224-common-pending-authorities-v1",r),pendingAuthorityCount:r.length,lockActive:t.yoloop.lock!==null,currentSessionActive:t.yoloop.state.currentSession!==null,journalActive:t.activeJournal!==null,reasonCodes:["epic-operational-projection-bounded"],publicSafe:!0})}function ke(i){let e;try{e=se(i.document,i.targetIdentityHash)}catch(d){return{kind:"blocked",reasonCodes:[Ee(d)]}}const t=i.document.operational,o=t.identities[i.targetIdentityHash],n=p(t.yoloop.state.continuationSession);if(!n)return{kind:"not-applicable",reasonCodes:["yoloop-cross-epic-session-missing"]};const r=typeof n.epicId=="string"?n.epicId:"",c=typeof n.graphFingerprint=="string"?n.graphFingerprint:"";if(r===i.targetEpicId&&c===i.targetGraphRevision)return{kind:"not-applicable",reasonCodes:["yoloop-cross-epic-target-unchanged"]};const s=[],u=p(t.yoloop.state.runControl),a=t.yoloop.state.pendingContinuationAuthorities??[];if((!o||o.epicId!==i.targetEpicId||o.graphRevision!==i.targetGraphRevision)&&s.push("epic-operational-identity-mismatch"),t.yoloop.capabilities.includes("cross-epic-continuation-reconcile-v1")||s.push("yoloop-cross-epic-capability-missing"),(!u||!["success","exhausted"].includes(String(u.status)))&&s.push("yoloop-cross-epic-predecessor-active"),(t.yoloop.lock!==null||t.yoloop.state.currentSession!==null||i.legacyActivity!=="inactive")&&s.push("yoloop-cross-epic-lock-active"),t.activeJournal!==null&&s.push("epic-operational-journal-conflict"),t.reconciliationReceipts.length>=C&&s.push("yoloop-cross-epic-receipt-capacity"),a.length>1&&s.push("yoloop-cross-epic-authority-ambiguous"),a.length===1){const d=p(a[0]),h=p(d?.certificate);!d||!h||d.sessionId!==n.sessionId||h.sessionId!==n.sessionId?s.push("yoloop-cross-epic-authority-ambiguous"):d.delegated!==!1&&s.push("yoloop-cross-epic-authority-delegated")}return(!l(c)||!l(n.sessionId)||e.runId===null||e.runRevision===null)&&s.push("yoloop-cross-epic-state-corrupt"),s.length>0?{kind:"blocked",reasonCodes:[...new Set(s)]}:{kind:"eligible",evidence:Object.freeze({...e,schemaVersion:"YoloopCrossEpicReconciliationEvidence/1.0",sourceEpicId:r,sourceGraphRevision:c,targetEpicId:i.targetEpicId,targetGraphRevision:i.targetGraphRevision,decision:"eligible"})}}function ce(i){return g("p224-cross-epic-continuation-reconcile-v1",i)}async function He(i,e){pe(e);let t=await Q(i,{required:!0});if(t.schemaVersion!==2||t.operational===void 0)throw new Error("epic-operational-store-missing");I(t.operational);let o=t.operational;const n=L(e),r=o.reconciliationReceipts.find(a=>a.operationId===e.operationId||a.idempotencyKey===e.idempotencyKey);if(r){if(m(r)!==m(n)||g("p224-common-continuation-session-v1",o.yoloop.state.continuationSession??null)!==e.nextSessionHash||(o.yoloop.state.pendingContinuationAuthorities?.length??0)!==0)throw new Error("yoloop-cross-epic-replay-conflict");return{applied:!1,replay:!0,receipt:r}}if(o.reconciliationReceipts.length>=C)throw new Error("yoloop-cross-epic-receipt-capacity");const c=o.activeJournal;if(c&&(c.operationId!==e.operationId||c.idempotencyKey!==e.idempotencyKey||c.payloadHash!==e.payloadHash))throw new Error("epic-operational-journal-conflict");c||G(t,o,e);const s=c?de(i,c.backupManifestRef,c.preimageDigest):le(i,e.operationId,o);if(!c){const a=ae(e,s.digest,s.ref,"prepared");t=(await k(i,{identityHash:e.expectedIdentityHash,expectedRevision:t.revision,expectedWriterEpoch:e.expectedWriterEpoch},h=>{h.activeJournal=a})).document,o=t.operational}if(o.activeJournal?.phase==="prepared"){G(t,o,e,!0);const a={...o.yoloop.state,continuationSession:e.nextSession,pendingContinuationAuthorities:[]};t=(await k(i,{identityHash:e.expectedIdentityHash,expectedRevision:t.revision,expectedWriterEpoch:e.expectedWriterEpoch},h=>{h.yoloop={...h.yoloop,state:a},h.activeJournal={...h.activeJournal,phase:"authority-applied"}})).document,o=t.operational}if(o.activeJournal?.phase==="authority-applied"&&(t=(await k(i,{identityHash:e.expectedIdentityHash,expectedRevision:t.revision,expectedWriterEpoch:e.expectedWriterEpoch},d=>{d.activeJournal={...d.activeJournal,phase:"projections-applied",projectionStatuses:{trackedState:"not-applicable",sqlite:"not-applicable"}}})).document,o=t.operational),o.activeJournal?.phase!=="projections-applied")throw new Error("epic-operational-journal-corrupt");const u=await k(i,{identityHash:e.expectedIdentityHash,expectedRevision:t.revision,expectedWriterEpoch:e.expectedWriterEpoch},a=>{if(a.reconciliationReceipts.length>=C)throw new Error("yoloop-cross-epic-receipt-capacity");a.reconciliationReceipts=[...a.reconciliationReceipts,n],a.activeJournal=null});return I(u.document.operational),{applied:!0,replay:!1,receipt:n}}function G(i,e,t,o=!1){const n=p(e.yoloop.state.runControl),r=e.yoloop.state.pendingContinuationAuthorities??[],c=e.identities[t.expectedIdentityHash],s=p(e.yoloop.state.continuationSession);if(i.revision!==t.expectedCommonRevision&&!o||e.writerEpoch!==t.expectedWriterEpoch||!c||c.epicId!==t.targetEpicId||c.graphRevision!==t.targetGraphRevision||!e.yoloop.capabilities.includes("cross-epic-continuation-reconcile-v1")||e.yoloop.lock!==null||e.yoloop.state.currentSession!==null||!n||n.runId!==t.expectedRunId||n.status!==t.expectedRunStatus||n.revision!==t.expectedRunRevision||(n.latestSnapshotHash??null)!==t.expectedSnapshotHash||!s||s.epicId!==t.sourceEpicId||s.graphFingerprint!==t.sourceGraphRevision||s.sessionId!==t.sourceSessionId||g("p224-common-continuation-session-v1",e.yoloop.state.continuationSession??null)!==t.expectedSessionHash||g("p224-common-pending-authorities-v1",r)!==t.expectedPendingHash||r.length!==t.expectedPendingCount)throw new Error("yoloop-cross-epic-revision-conflict");if(r.length>1||r.some(u=>{const a=p(u),d=p(a?.certificate);return!a||a.delegated!==!1||!d||a.sessionId!==t.sourceSessionId||d.sessionId!==t.sourceSessionId}))throw new Error("yoloop-cross-epic-authority-ambiguous")}function L(i){return Object.freeze({schemaVersion:"YoloopCrossEpicReconciliationReceipt/1.0",operationId:i.operationId,idempotencyKey:i.idempotencyKey,payloadHash:i.payloadHash,sourceEpicId:i.sourceEpicId,sourceGraphRevision:i.sourceGraphRevision,targetEpicId:i.targetEpicId,targetGraphRevision:i.targetGraphRevision,runId:i.expectedRunId,previousSessionHash:i.expectedSessionHash,nextSessionHash:i.nextSessionHash,retiredAuthorityHash:i.expectedPendingHash,retiredAuthorityCount:i.expectedPendingCount,outcome:"cross-epic-reconciled",publicSafe:!0})}function ae(i,e,t,o){return{schemaVersion:"EpicOperationalJournal/1.0",kind:"cross-epic-reconcile",operationId:i.operationId,idempotencyKey:i.idempotencyKey,payloadHash:i.payloadHash,identityHash:i.expectedIdentityHash,expectedRevision:i.expectedCommonRevision,expectedWriterEpoch:i.expectedWriterEpoch,preimageDigest:e,backupManifestRef:t,phase:o,projectionStatuses:{trackedState:"not-applicable",sqlite:"not-applicable"},reasonCodes:["epic-operational-backup-verified"]}}function pe(i){const{type:e,payloadHash:t,receipt:o,...n}=i,r=p(i.nextSession);if(!f(i.operationId)||!f(i.idempotencyKey)||ce(n)!==i.payloadHash||Buffer.byteLength(m(n),"utf8")>ie||i.expectedPendingCount<0||i.expectedPendingCount>1||!r||r.epicId!==i.targetEpicId||r.graphFingerprint!==i.targetGraphRevision||!l(r.sessionId)||i.nextSessionHash!==g("p224-common-continuation-session-v1",i.nextSession)||m(i.receipt)!==m(L(i)))throw new Error("yoloop-cross-epic-replay-conflict")}function le(i,e,t){if(!f(e))throw new Error("epic-operational-backup-failed");const o=E(y(i)),n=O("git",["rev-parse","--git-common-dir"],{cwd:o,encoding:"utf8"}).trim(),r=E(x(n)?y(n):y(o,n)),c=R(r,F),s=R(c,"backups");D(c,r),B(s)||Y(s,{mode:448}),D(s,c);const u=`${e}.operational.json`,a=R(s,u),d=`${JSON.stringify(t,null,2)}
|
|
2
|
+
`,h=S("sha256").update(d).digest("hex");if(B(a)){const b=w(a);if(!b.isFile()||b.isSymbolicLink()||S("sha256").update(N(a)).digest("hex")!==h)throw new Error("epic-operational-backup-failed")}else{const b=_(a,"wx",384);try{q(b,d),V(b)}finally{J(b)}const j=_(s,"r");try{V(j)}finally{J(j)}}return{ref:`backups/${u}`,digest:h}}function de(i,e,t){if(!/^backups\/[A-Za-z0-9._-]{1,240}$/.test(e)||!l(t))throw new Error("epic-operational-backup-failed");const o=E(y(i)),n=O("git",["rev-parse","--git-common-dir"],{cwd:o,encoding:"utf8"}).trim(),r=E(x(n)?y(n):y(o,n)),c=R(r,F),s=R(c,e);D(c,r);const u=w(s);if(!u.isFile()||u.isSymbolicLink()||S("sha256").update(N(s)).digest("hex")!==t)throw new Error("epic-operational-backup-failed");return{ref:e,digest:t}}function D(i,e){const t=E(e);if(Z(t,y(i)).startsWith(".."))throw new Error("epic-operational-path-unsafe");const o=w(i);if(!o.isDirectory()||o.isSymbolicLink()||E(i)!==y(i)||typeof process.getuid=="function"&&(o.uid!==process.getuid()||(o.mode&63)!==0))throw new Error("epic-operational-path-unsafe")}function K(i,e,t){if(t?.expectedRevision!==void 0&&i.revision!==t.expectedRevision)throw new Error("epic-operational-revision-conflict");if(t?.expectedWriterEpoch!==void 0&&e.writerEpoch!==t.expectedWriterEpoch)throw new Error("epic-operational-epoch-conflict");if(t?.identityHash!==void 0&&!e.identities[t.identityHash])throw new Error("epic-operational-identity-mismatch")}function W(i){const e=p(i);if(!e)throw new Error("epic-operational-identity-mismatch");z(e,["schemaVersion","epicId","executionBranch","graphRevision","originBindingDigest","repositoryFingerprint","identityHash"]);const{identityHash:t,...o}=e;if(e.schemaVersion!=="EpicOperationalIdentity/1.0"||!A.test(String(e.epicId))||!me(e.executionBranch)||!l(e.graphRevision)||!l(e.originBindingDigest)||!l(e.repositoryFingerprint)||t!==g("p224-epic-operational-identity-v1",o))throw new Error("epic-operational-identity-mismatch")}function T(i){const e=p(i);return!!(e&&v(e,["status","sourceDigest","authorityRevision","reasonCodes"])&&["bound","unbound","unavailable"].includes(String(e.status))&&(e.sourceDigest===null||l(e.sourceDigest))&&Number.isSafeInteger(e.authorityRevision)&&Number(e.authorityRevision)>=0&&Array.isArray(e.reasonCodes)&&e.reasonCodes.length<=16&&e.reasonCodes.every(H))}function ue(i){const e=p(i),t=p(e?.projectionStatuses);return!!(e&&v(e,["schemaVersion","kind","operationId","idempotencyKey","payloadHash","identityHash","expectedRevision","expectedWriterEpoch","preimageDigest","backupManifestRef","phase","projectionStatuses","reasonCodes"])&&e.schemaVersion==="EpicOperationalJournal/1.0"&&["backfill","cross-epic-reconcile","rollback"].includes(String(e.kind))&&M(e.operationId)&&M(e.idempotencyKey)&&l(e.payloadHash)&&l(e.identityHash)&&Number.isSafeInteger(e.expectedRevision)&&Number.isSafeInteger(e.expectedWriterEpoch)&&l(e.preimageDigest)&&typeof e.backupManifestRef=="string"&&e.backupManifestRef.length<=1024&&!x(e.backupManifestRef)&&!e.backupManifestRef.includes("..")&&["prepared","authority-applied","projections-applied","committed","aborted-before-authority"].includes(String(e.phase))&&t&&v(t,["trackedState","sqlite"])&&["pending","applied","not-applicable"].includes(String(t.trackedState))&&["pending","applied","unavailable","not-applicable"].includes(String(t.sqlite))&&Array.isArray(e.reasonCodes)&&e.reasonCodes.length<=16&&e.reasonCodes.every(H))}function P(i,e){return Array.isArray(i)&&i.length<=C&&i.every(t=>e==="EpicLifecycleBackfillReceipt/1.0"?he(t):e==="YoloopCrossEpicReconciliationReceipt/1.0"?ye(t):fe(t))}function he(i){const e=p(i);return!!(e&&v(e,["schemaVersion","operationId","idempotencyKey","payloadHash","planDigest","sourceInventoryDigest","preimageDigest","postimageDigest","authorityPostimageDigest","backupManifestDigest","identityHash","appliedRevision","writerEpoch","stateMigrationOperationId","outcome","reasonCodes","publicSafe"])&&e.schemaVersion==="EpicLifecycleBackfillReceipt/1.0"&&f(e.operationId)&&f(e.idempotencyKey)&&e.operationId===e.idempotencyKey&&[e.payloadHash,e.planDigest,e.sourceInventoryDigest,e.preimageDigest,e.postimageDigest,e.authorityPostimageDigest,e.backupManifestDigest,e.identityHash].every(l)&&Number.isSafeInteger(e.appliedRevision)&&Number(e.appliedRevision)>=1&&Number.isSafeInteger(e.writerEpoch)&&Number(e.writerEpoch)>=1&&(e.stateMigrationOperationId===null||f(e.stateMigrationOperationId))&&["applied","already-applied"].includes(String(e.outcome))&&$(e.reasonCodes)&&e.publicSafe===!0)}function ye(i){const e=p(i);return!!(e&&v(e,["schemaVersion","operationId","idempotencyKey","payloadHash","sourceEpicId","sourceGraphRevision","targetEpicId","targetGraphRevision","runId","previousSessionHash","nextSessionHash","retiredAuthorityHash","retiredAuthorityCount","outcome","publicSafe"])&&e.schemaVersion==="YoloopCrossEpicReconciliationReceipt/1.0"&&f(e.operationId)&&f(e.idempotencyKey)&&l(e.payloadHash)&&A.test(String(e.sourceEpicId))&&A.test(String(e.targetEpicId))&&l(e.sourceGraphRevision)&&l(e.targetGraphRevision)&&f(e.runId)&&l(e.previousSessionHash)&&l(e.nextSessionHash)&&l(e.retiredAuthorityHash)&&[0,1].includes(Number(e.retiredAuthorityCount))&&e.outcome==="cross-epic-reconciled"&&e.publicSafe===!0)}function fe(i){const e=p(i);return!!(e&&v(e,["schemaVersion","operationId","targetOperationId","expectedPostimageDigest","restoredPreimageDigest","reasonCodes","publicSafe"])&&e.schemaVersion==="EpicOperationalRollbackReceipt/1.0"&&f(e.operationId)&&f(e.targetOperationId)&&l(e.expectedPostimageDigest)&&l(e.restoredPreimageDigest)&&$(e.reasonCodes)&&e.publicSafe===!0)}function $(i){return Array.isArray(i)&&i.length<=16&&i.every(H)}function ge(i,e){return i.length===e.length&&e.every(t=>i.includes(t))}function z(i,e){const t=new Set(e);if(Object.keys(i).some(o=>!t.has(o)))throw new Error("epic-operational-store-corrupt")}function v(i,e){const t=Object.keys(i).sort(),o=[...e].sort();return t.length===o.length&&t.every((n,r)=>n===o[r])}function p(i){return i&&typeof i=="object"&&!Array.isArray(i)?i:void 0}function l(i){return typeof i=="string"&&oe.test(i)}function M(i){return typeof i=="string"&&ne.test(i)&&!i.includes("..")}function f(i){return typeof i=="string"&&/^[A-Za-z0-9._:-]{1,128}$/.test(i)}function H(i){return typeof i=="string"&&/^[a-z0-9][a-z0-9-]{0,79}$/.test(i)}function me(i){return typeof i=="string"&&/^[A-Za-z0-9][A-Za-z0-9._/-]{0,239}$/.test(i)&&!i.includes("..")&&!i.includes("//")&&!i.includes("@{")&&!i.endsWith("/")&&!i.endsWith(".")}function Ee(i){const e=i instanceof Error?i.message:"";return H(e)?e:"epic-operational-store-corrupt"}export{te as EPIC_OPERATIONAL_IDENTITY_CAP,ie as EPIC_OPERATIONAL_PAYLOAD_LIMIT_BYTES,C as EPIC_OPERATIONAL_RECEIPT_CAP,ee as EPIC_OPERATIONAL_SOURCE_LIMIT_BYTES,He as applyCrossEpicContinuationReconciliation,I as assertEpicOperationalState,ce as buildCrossEpicContinuationPayloadHash,ke as classifyCrossEpicContinuationReconciliation,we as createEpicOperationalIdentity,xe as createInitialEpicOperationalState,re as deriveEpicOperationalRepositoryFingerprint,m as epicOperationalCanonicalJson,g as epicOperationalHash,k as mutateEpicOperationalState,se as projectEpicOperationalState,Ce as readEpicOperationalState};
|