@agent-relay/factory 0.1.58 → 0.1.60
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/README.md +13 -0
- package/dist/cli/fleet.d.ts +7 -1
- package/dist/cli/fleet.d.ts.map +1 -1
- package/dist/cli/fleet.js +126 -10
- package/dist/cli/fleet.js.map +1 -1
- package/dist/config/schema.d.ts +777 -273
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +37 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/delivery/ticket-dispatch.d.ts +15 -0
- package/dist/delivery/ticket-dispatch.d.ts.map +1 -0
- package/dist/delivery/ticket-dispatch.js +20 -0
- package/dist/delivery/ticket-dispatch.js.map +1 -0
- package/dist/git/agent-worktree.d.ts.map +1 -1
- package/dist/git/agent-worktree.js +4 -0
- package/dist/git/agent-worktree.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/issue-key-match.d.ts +6 -0
- package/dist/issue-key-match.d.ts.map +1 -1
- package/dist/issue-key-match.js +14 -0
- package/dist/issue-key-match.js.map +1 -1
- package/dist/mount/relayfile-github-connection-write.d.ts.map +1 -1
- package/dist/mount/relayfile-github-connection-write.js +6 -3
- package/dist/mount/relayfile-github-connection-write.js.map +1 -1
- package/dist/orchestrator/batch-tracker.d.ts +2 -1
- package/dist/orchestrator/batch-tracker.d.ts.map +1 -1
- package/dist/orchestrator/batch-tracker.js.map +1 -1
- package/dist/orchestrator/factory.d.ts.map +1 -1
- package/dist/orchestrator/factory.js +1473 -99
- package/dist/orchestrator/factory.js.map +1 -1
- package/dist/ports/fleet.d.ts +4 -0
- package/dist/ports/fleet.d.ts.map +1 -1
- package/dist/ports/mount.d.ts +2 -0
- package/dist/ports/mount.d.ts.map +1 -1
- package/dist/ports/state.d.ts +45 -1
- package/dist/ports/state.d.ts.map +1 -1
- package/dist/state/file-state-store.d.ts +6 -1
- package/dist/state/file-state-store.d.ts.map +1 -1
- package/dist/state/file-state-store.js +119 -2
- package/dist/state/file-state-store.js.map +1 -1
- package/dist/state/in-memory-state-store.d.ts +6 -1
- package/dist/state/in-memory-state-store.d.ts.map +1 -1
- package/dist/state/in-memory-state-store.js +62 -0
- package/dist/state/in-memory-state-store.js.map +1 -1
- package/dist/triage/schema.d.ts +36 -0
- package/dist/triage/schema.d.ts.map +1 -1
- package/dist/triage/schema.js +2 -0
- package/dist/triage/schema.js.map +1 -1
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version-info.d.ts +21 -0
- package/dist/version-info.d.ts.map +1 -0
- package/dist/version-info.js +146 -0
- package/dist/version-info.js.map +1 -0
- package/dist/writeback/github.d.ts.map +1 -1
- package/dist/writeback/github.js +12 -0
- package/dist/writeback/github.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,6 +143,19 @@ After init, add the `factory` label to an open issue and run a dry run below.
|
|
|
143
143
|
| `factory featuremap check [--manifest <path>] [--base <ref>]` | Validate the repository feature/test manifest and optionally report advisory drift for unchanged entries whose locations changed. |
|
|
144
144
|
| `factory intake notion <manifest>` | Normalize ready specs from a read-only Notion mount into GitHub lifecycle issues or exact-path fleet work. Honors `--dry-run`. |
|
|
145
145
|
|
|
146
|
+
`factory status` includes `inFlightDispatches`, grouped by issue with agent
|
|
147
|
+
names and the provider-claim state (`pending`, `verified`, or `degraded`). This
|
|
148
|
+
view is read from Factory's local in-flight registry, so it remains available
|
|
149
|
+
when GitHub lifecycle writeback is the degraded subsystem.
|
|
150
|
+
|
|
151
|
+
Dispatch lifecycle writes are claim-critical. Factory applies the
|
|
152
|
+
`factory:in-progress` label/state before the dispatch comment, confirms the
|
|
153
|
+
GitHub label by provider read-back, and retries either write three times. An
|
|
154
|
+
exhausted write is logged at error level as dead-lettered, recorded as a
|
|
155
|
+
degraded claim in the registry, and fails the dispatch instead of reporting a
|
|
156
|
+
clean dispatch with missing GitHub state. Durable lifecycle recovery repeats
|
|
157
|
+
the same label-and-comment claim without respawning acknowledged agents.
|
|
158
|
+
|
|
146
159
|
Global options work anywhere in the args: `--config <path>`, `--dry-run`,
|
|
147
160
|
`--backend <internal|relay>`, and `--agent-exit-timeout <ms>`. The internal
|
|
148
161
|
backend reuses a relay broker that's already running for your workspace, and
|
package/dist/cli/fleet.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ensureCloudSession, type CloudSession } from '@agent-relay/cloud';
|
|
2
2
|
import { type LocalClonePathOptions } from '../config/local-clone-paths.js';
|
|
3
|
-
import { createFactory, createFleet, ensureRelayBroker, reapFactoryEnvironmentsOnce, type Capability, type Factory, type FactoryEventReporter, type FactoryConfig, type FleetBackend, type FleetClient, type GhRunner, type FactoryStateResolution, type LocalMountOptions, type MountClient, type ProbeCloser, type RelayfileCloudMountClientConfig, type ResolvedFactoryWorkspace } from '../index.js';
|
|
3
|
+
import { createFactory, createFleet, ensureRelayBroker, reapFactoryEnvironmentsOnce, type Capability, type Factory, type FactoryEventReporter, type FactoryConfig, type FleetBackend, type FleetClient, type GhRunner, type FactoryStateResolution, type Logger, type LocalMountOptions, type MountClient, type ProbeCloser, type RelayfileCloudMountClientConfig, type ResolvedFactoryWorkspace } from '../index.js';
|
|
4
4
|
import { type CheckFeatureMapOptions, type FeatureMapCheckReport } from '../featuremap/index.js';
|
|
5
5
|
import type { FactoryIntegrationProvider } from '../ports/index.js';
|
|
6
|
+
import { type FactoryVersionInfo, type FactoryVersionInfoOptions } from '../version-info.js';
|
|
6
7
|
import { type NotionIntakeClaimStore, type NotionContractPublisher } from '../intake/index.js';
|
|
7
8
|
interface FleetCliDeps {
|
|
8
9
|
fleet?: FleetClient;
|
|
@@ -40,6 +41,10 @@ interface FleetCliDeps {
|
|
|
40
41
|
notionClaims?: NotionIntakeClaimStore;
|
|
41
42
|
/** Hermetic verification-environment sweep for CLI tests and alternate runtimes. */
|
|
42
43
|
reapEnvironments?: typeof reapFactoryEnvironmentsOnce;
|
|
44
|
+
/** Hermetic package/registry metadata for CLI tests and alternate runtimes. */
|
|
45
|
+
versionInfo?: (options?: FactoryVersionInfoOptions) => Promise<FactoryVersionInfo>;
|
|
46
|
+
/** Override the six-hour daemon drift check cadence in tests. */
|
|
47
|
+
versionCheckIntervalMs?: number;
|
|
43
48
|
}
|
|
44
49
|
interface GlobalOptions {
|
|
45
50
|
backend: FleetBackend;
|
|
@@ -109,6 +114,7 @@ export declare function parseGlobalOptions(argv: string[]): {
|
|
|
109
114
|
globals: GlobalOptions;
|
|
110
115
|
args: string[];
|
|
111
116
|
};
|
|
117
|
+
export declare function reportFactoryVersionDrift(provider: FleetCliDeps['versionInfo'], logger: Logger): Promise<void>;
|
|
112
118
|
export declare function formatLogArgs(args: unknown[]): string;
|
|
113
119
|
export declare function resolveBrokerConnectionPath(startCwd?: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
114
120
|
export type GithubIssueSelector = {
|
package/dist/cli/fleet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../src/cli/fleet.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAG1E,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AAEnG,OAAO,EAKL,aAAa,EACb,WAAW,EAEX,iBAAiB,EAgBjB,2BAA2B,
|
|
1
|
+
{"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../src/cli/fleet.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/fleet.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
import readline from 'node:readline/promises';
|
|
6
6
|
import { ensureCloudSession } from '@agent-relay/cloud';
|
|
7
7
|
import { stringifyLogValue } from '../logging.js';
|
|
8
8
|
import { resolveLocalFactoryConfig } from '../config/local-clone-paths.js';
|
|
9
9
|
import { initializeFactory } from './init.js';
|
|
10
|
-
import { FileStateStore, RelayfileCloudMountClient, checkFactoryLoopLiveness, closeProbePr, createFactory, createFleet, createFactoryCloudEventV1, ensureRelayBroker, FactoryCloudReporter, FileFactoryCloudEventOutbox, defaultGhRunner, explicitLinkedIssueKey, githubIssuePathParts, githubWatchStatePath, isInFactoryScope, parseGithubFactoryIssue, publishFactoryMountHealth, parseOwnedBrokerAgentExitTimeoutMs, parseStandaloneBabysitTarget, readStandalonePullRequest, readLinearIssueWithCanonicalFallback, reapFactoryOrphansOnce, reapFactoryEnvironmentsOnce, readFactoryLoopHeartbeat, resolveFactoryStates, stateResolutionFromIds, standaloneBabysitterAgentName, renderAgentTask, resolveFactoryWorkspace, } from '../index.js';
|
|
10
|
+
import { FileStateStore, RelayfileCloudMountClient, checkFactoryLoopLiveness, closeProbePr, createFactory, createFleet, createFactoryCloudEventV1, ensureRelayBroker, FactoryCloudReporter, FileFactoryCloudEventOutbox, defaultGhRunner, explicitLinkedIssueKey, githubIssuePathParts, githubWatchStatePath, isInFactoryScope, parseGithubFactoryIssue, publishFactoryMountHealth, parseOwnedBrokerAgentExitTimeoutMs, parseStandaloneBabysitTarget, readStandalonePullRequest, readLinearIssueWithCanonicalFallback, reapFactoryOrphansOnce, reapFactoryEnvironmentsOnce, readFactoryLoopHeartbeat, readFactoryInFlightRegistry, resolveFactoryStates, stateResolutionFromIds, standaloneBabysitterAgentName, renderAgentTask, resolveFactoryWorkspace, } from '../index.js';
|
|
11
11
|
import { resolveTestGuidance } from '../dispatch/test-guidance.js';
|
|
12
12
|
import { FakeFleetClient, FakeMountClient } from '../testing/index.js';
|
|
13
13
|
import { GitAgentWorktreeManager } from '../git/agent-worktree.js';
|
|
@@ -17,6 +17,7 @@ import { ensureFactoryIntegrations, inspectFactoryIntegration, openIntegrationUr
|
|
|
17
17
|
import { checkMountStaleness } from '../mount/relayfile-binary.js';
|
|
18
18
|
import { MountAuthScopeError } from '../mount/mount-auth-error.js';
|
|
19
19
|
import { resolveRelayWorkspaceKey } from '../fleet/relay-workspace-key.js';
|
|
20
|
+
import { isMeaningfullyBehind, readFactoryVersionInfo, } from '../version-info.js';
|
|
20
21
|
import { GhCliIssuePublisher, RelayChannelNotionClaimStore, RelayChannelNotionContractPublisher, loadNotionIntakeManifest, runNotionIntake, } from '../intake/index.js';
|
|
21
22
|
const autoDetectedIssueSources = new WeakSet();
|
|
22
23
|
export async function runFleetCli(argv, deps = {}) {
|
|
@@ -491,6 +492,18 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
491
492
|
if (command.action === 'start') {
|
|
492
493
|
const waiter = createStopSignalWaiter();
|
|
493
494
|
let stoppedBySignal = false;
|
|
495
|
+
const logger = streamLogger(mountStderr);
|
|
496
|
+
const installedVersion = await safeFactoryVersionInfo(deps.versionInfo, false);
|
|
497
|
+
logger.info?.('[factory] daemon starting', installedVersion);
|
|
498
|
+
// The registry lookup is deliberately detached from the startup path.
|
|
499
|
+
// stop() only joins the bounded in-flight lookup during shutdown.
|
|
500
|
+
const versionMonitor = deps.versionInfo || !hasInjectedFactoryRuntime(deps)
|
|
501
|
+
? startFactoryVersionMonitor({
|
|
502
|
+
versionInfo: deps.versionInfo,
|
|
503
|
+
intervalMs: deps.versionCheckIntervalMs,
|
|
504
|
+
logger,
|
|
505
|
+
})
|
|
506
|
+
: { stop: async () => { } };
|
|
494
507
|
const flushAndResolve = async (code) => {
|
|
495
508
|
try {
|
|
496
509
|
await (deps.flushDaemonOutput ?? flushProcessOutput)();
|
|
@@ -558,6 +571,7 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
558
571
|
return typeof code === 'number' ? code : 0;
|
|
559
572
|
}
|
|
560
573
|
finally {
|
|
574
|
+
await versionMonitor.stop();
|
|
561
575
|
removeSignalHandlers();
|
|
562
576
|
if (!stoppedBySignal) {
|
|
563
577
|
await factory.stop();
|
|
@@ -570,7 +584,8 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
570
584
|
return 0;
|
|
571
585
|
}
|
|
572
586
|
if (command.action === 'status') {
|
|
573
|
-
|
|
587
|
+
const versionInfo = await safeFactoryVersionInfo(deps.versionInfo, Boolean(deps.versionInfo) || !hasInjectedFactoryRuntime(deps));
|
|
588
|
+
writeJson(out, await factoryStatusWithMountHealth(factory, mount, config.loop.heartbeatPath, config.loop.registryPath, config.loop.heartbeatStaleMs, versionInfo));
|
|
574
589
|
return 0;
|
|
575
590
|
}
|
|
576
591
|
if (command.action === 'loop-status') {
|
|
@@ -595,7 +610,7 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
595
610
|
const reports = await factory.runLoop({ dryRun: globals.dryRun });
|
|
596
611
|
writeJson(out, {
|
|
597
612
|
reports,
|
|
598
|
-
status: await factoryStatusWithMountHealth(factory, mount, config.loop.heartbeatPath, config.loop.heartbeatStaleMs),
|
|
613
|
+
status: await factoryStatusWithMountHealth(factory, mount, config.loop.heartbeatPath, config.loop.registryPath, config.loop.heartbeatStaleMs),
|
|
599
614
|
});
|
|
600
615
|
}
|
|
601
616
|
finally {
|
|
@@ -809,9 +824,18 @@ async function ensureMountPath(mountFn, workspaceId, path, mountOpts, stderr, lo
|
|
|
809
824
|
function resolveIntegrationsMountRoot(mount) {
|
|
810
825
|
return mount.getLocalMountRoot?.() ?? resolve(process.cwd(), '.integrations');
|
|
811
826
|
}
|
|
812
|
-
async function factoryStatusWithMountHealth(factory, mount, heartbeatPath, heartbeatStaleMs) {
|
|
827
|
+
async function factoryStatusWithMountHealth(factory, mount, heartbeatPath, registryPath, heartbeatStaleMs, versionInfo) {
|
|
813
828
|
const status = factory.status();
|
|
814
|
-
const heartbeat = await
|
|
829
|
+
const [heartbeat, registry] = await Promise.all([
|
|
830
|
+
readFactoryLoopHeartbeat(heartbeatPath),
|
|
831
|
+
readFactoryInFlightRegistry(registryPath),
|
|
832
|
+
]);
|
|
833
|
+
const registryDispatches = registry?.heartbeatPath && registry.heartbeatPath !== heartbeatPath
|
|
834
|
+
? []
|
|
835
|
+
: inFlightDispatchesFromRegistry(registry);
|
|
836
|
+
const observableStatus = registryDispatches.length > 0
|
|
837
|
+
? { ...status, inFlightDispatches: registryDispatches }
|
|
838
|
+
: status;
|
|
815
839
|
const liveness = checkFactoryLoopLiveness(heartbeat, { staleMs: heartbeatStaleMs });
|
|
816
840
|
const eventListener = liveness.ok
|
|
817
841
|
? heartbeat?.eventListener ?? {
|
|
@@ -824,9 +848,10 @@ async function factoryStatusWithMountHealth(factory, mount, heartbeatPath, heart
|
|
|
824
848
|
};
|
|
825
849
|
const health = mount.getLocalMountHealth?.();
|
|
826
850
|
if (!health)
|
|
827
|
-
return { ...
|
|
851
|
+
return { ...observableStatus, ...versionInfo, eventListener };
|
|
828
852
|
return {
|
|
829
|
-
...
|
|
853
|
+
...observableStatus,
|
|
854
|
+
...versionInfo,
|
|
830
855
|
eventListener,
|
|
831
856
|
localMountDegraded: health.degraded,
|
|
832
857
|
...(health.reason ? { localMountDegradedReason: health.reason } : {}),
|
|
@@ -839,6 +864,82 @@ async function factoryStatusWithMountHealth(factory, mount, heartbeatPath, heart
|
|
|
839
864
|
},
|
|
840
865
|
};
|
|
841
866
|
}
|
|
867
|
+
function inFlightDispatchesFromRegistry(registry) {
|
|
868
|
+
if (!registry)
|
|
869
|
+
return [];
|
|
870
|
+
const grouped = new Map();
|
|
871
|
+
const claimPriority = { verified: 0, pending: 1, degraded: 2 };
|
|
872
|
+
for (const agent of registry.agents) {
|
|
873
|
+
if (!agent.issue)
|
|
874
|
+
continue;
|
|
875
|
+
const key = `${agent.issue.key}\u0000${agent.issue.uuid}\u0000${agent.issue.path}`;
|
|
876
|
+
const claim = agent.dispatchClaim ?? {
|
|
877
|
+
state: 'pending',
|
|
878
|
+
updatedAtMs: registry.updatedAtMs,
|
|
879
|
+
};
|
|
880
|
+
const existing = grouped.get(key);
|
|
881
|
+
const entry = existing ?? {
|
|
882
|
+
issue: { ...agent.issue },
|
|
883
|
+
agents: [],
|
|
884
|
+
claim: { ...claim },
|
|
885
|
+
};
|
|
886
|
+
if (claimPriority[claim.state] > claimPriority[entry.claim.state]) {
|
|
887
|
+
entry.claim = { ...claim };
|
|
888
|
+
}
|
|
889
|
+
if (!entry.agents.some((candidate) => candidate.name === agent.name)) {
|
|
890
|
+
entry.agents.push({
|
|
891
|
+
name: agent.name,
|
|
892
|
+
...(agent.role ? { role: agent.role } : {}),
|
|
893
|
+
...(agent.sessionRef ? { sessionRef: agent.sessionRef } : {}),
|
|
894
|
+
...(agent.invocationId ? { invocationId: agent.invocationId } : {}),
|
|
895
|
+
...(agent.node ? { node: agent.node } : {}),
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
grouped.set(key, entry);
|
|
899
|
+
}
|
|
900
|
+
return [...grouped.values()]
|
|
901
|
+
.map((entry) => ({
|
|
902
|
+
...entry,
|
|
903
|
+
agents: entry.agents.sort((left, right) => left.name.localeCompare(right.name)),
|
|
904
|
+
}))
|
|
905
|
+
.sort((left, right) => left.issue.key.localeCompare(right.issue.key));
|
|
906
|
+
}
|
|
907
|
+
const DEFAULT_VERSION_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1_000;
|
|
908
|
+
async function safeFactoryVersionInfo(provider, includeRegistry) {
|
|
909
|
+
try {
|
|
910
|
+
return await (provider ?? readFactoryVersionInfo)({ includeRegistry });
|
|
911
|
+
}
|
|
912
|
+
catch {
|
|
913
|
+
return { version: 'unknown' };
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
export async function reportFactoryVersionDrift(provider, logger) {
|
|
917
|
+
const info = await safeFactoryVersionInfo(provider, true);
|
|
918
|
+
if (!isMeaningfullyBehind(info) || !info.latestVersion)
|
|
919
|
+
return;
|
|
920
|
+
logger.warn?.(`[factory] version drift detected: running ${info.version}; published latest ${info.latestVersion}`, {
|
|
921
|
+
...(info.versionsBehind !== undefined ? { versionsBehind: info.versionsBehind } : {}),
|
|
922
|
+
...(info.installedAt ? { installedAt: info.installedAt } : {}),
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
function startFactoryVersionMonitor(input) {
|
|
926
|
+
let pending;
|
|
927
|
+
const check = () => {
|
|
928
|
+
if (pending)
|
|
929
|
+
return;
|
|
930
|
+
pending = reportFactoryVersionDrift(input.versionInfo, input.logger)
|
|
931
|
+
.finally(() => { pending = undefined; });
|
|
932
|
+
};
|
|
933
|
+
check();
|
|
934
|
+
const interval = setInterval(check, input.intervalMs ?? DEFAULT_VERSION_CHECK_INTERVAL_MS);
|
|
935
|
+
interval.unref?.();
|
|
936
|
+
return {
|
|
937
|
+
stop: async () => {
|
|
938
|
+
clearInterval(interval);
|
|
939
|
+
await pending;
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
}
|
|
842
943
|
function writeMountRefreshSummary(refreshedStaleMounts, stderr, debug) {
|
|
843
944
|
if (debug) {
|
|
844
945
|
for (const mount of [...refreshedStaleMounts].sort((left, right) => left.path.localeCompare(right.path))) {
|
|
@@ -1291,7 +1392,7 @@ async function buildMount(loaded, deps, observability = {}) {
|
|
|
1291
1392
|
if (deps.mount)
|
|
1292
1393
|
return deps.mount;
|
|
1293
1394
|
if (hasExplicitFixtureFiles(loaded))
|
|
1294
|
-
return new
|
|
1395
|
+
return new FixtureMountClient(loaded.fixtureFiles);
|
|
1295
1396
|
let mount;
|
|
1296
1397
|
mount = await (deps.cloudMountFromConfig ?? RelayfileCloudMountClient.fromConfig)({
|
|
1297
1398
|
workspaceId: loaded.config.workspaceId,
|
|
@@ -1303,6 +1404,21 @@ async function buildMount(loaded, deps, observability = {}) {
|
|
|
1303
1404
|
return mount;
|
|
1304
1405
|
}
|
|
1305
1406
|
const hasExplicitFixtureFiles = (loaded) => loaded?.fixtureFiles !== undefined;
|
|
1407
|
+
/** Hermetic fixture runs have no server event feed, so expose the in-memory
|
|
1408
|
+
* tree shape as their change watermark. The discovery tree cache only
|
|
1409
|
+
* remembers which paths exist under a prefix, not their content, so the
|
|
1410
|
+
* watermark must track path membership only. Hashing full file entries
|
|
1411
|
+
* (as an earlier version of this did) also hashes each file's `revision`,
|
|
1412
|
+
* which FakeMountClient.writeFile bumps on every write — so any factory
|
|
1413
|
+
* writeback mid-sweep (a draft, a confirmed write) would change the digest
|
|
1414
|
+
* with no fixture change-event to explain the gap, and the checkpoint
|
|
1415
|
+
* would never persist across a sweep that actually dispatched anything. */
|
|
1416
|
+
class FixtureMountClient extends FakeMountClient {
|
|
1417
|
+
async getEventHighWatermark() {
|
|
1418
|
+
const paths = [...this.files.keys()].sort();
|
|
1419
|
+
return `fixture:${createHash('sha256').update(JSON.stringify(paths)).digest('hex')}`;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1306
1422
|
async function isAllowedFactoryDraft(path, content, opts, mount, config) {
|
|
1307
1423
|
if (!opts?.guarded)
|
|
1308
1424
|
return false;
|
|
@@ -1481,7 +1597,7 @@ async function findGithubIssueThroughConnection(mount, selector, config, issueAr
|
|
|
1481
1597
|
return found[0].issue;
|
|
1482
1598
|
}
|
|
1483
1599
|
async function issueProjectionStatus(factory, mount, config) {
|
|
1484
|
-
const status = await factoryStatusWithMountHealth(factory, mount, config.loop.heartbeatPath, config.loop.heartbeatStaleMs);
|
|
1600
|
+
const status = await factoryStatusWithMountHealth(factory, mount, config.loop.heartbeatPath, config.loop.registryPath, config.loop.heartbeatStaleMs);
|
|
1485
1601
|
const githubConnection = mount.integrationConnections
|
|
1486
1602
|
? await mount.integrationConnections.getStatus('github')
|
|
1487
1603
|
: undefined;
|