@markjaquith/agency 2.71.18 → 2.71.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.71.18",
3
+ "version": "2.71.19",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -77,6 +77,7 @@
77
77
  "benchmark:finish": "bun scripts/benchmark-finish.ts",
78
78
  "benchmark:epic": "bun scripts/benchmark-epic.ts",
79
79
  "benchmark:phase": "bun scripts/benchmark-phase.ts",
80
+ "benchmark:graph": "bun scripts/benchmark-graph.ts",
80
81
  "benchmark:push": "bun scripts/benchmark-push.ts",
81
82
  "benchmark:archive": "bun scripts/benchmark-archive.ts",
82
83
  "benchmark:sync": "bun scripts/benchmark-sync.ts",
@@ -6,7 +6,10 @@ import { dirname, join } from "node:path"
6
6
  import { AgencyGraph } from "../graph-schema"
7
7
  import { cleanupTempDir, createTempDir, runTestEffect } from "../test-utils"
8
8
  import { GraphService } from "./GraphService"
9
- import { VersionControlService } from "./VersionControlService"
9
+ import {
10
+ VersionControlService,
11
+ type VersionControlBackend,
12
+ } from "./VersionControlService"
10
13
 
11
14
  const write = async (root: string, path: string, content: string) => {
12
15
  const fullPath = join(root, path)
@@ -364,6 +367,15 @@ status: open
364
367
 
365
368
  const detailed = await getGraph(root, {
366
369
  include: ["bodies", "workspace", "git", "pr"],
370
+ backend: {
371
+ kind: "git",
372
+ inspectRepository: () => Effect.succeed(null),
373
+ listWorkspaces: () => Effect.succeed([]),
374
+ resolveRevision: () => Effect.succeed(null),
375
+ workspaceHead: () => Effect.succeed(null),
376
+ workspaceDirty: () => Effect.succeed(null),
377
+ remoteUrl: () => Effect.succeed(null),
378
+ } as unknown as VersionControlBackend,
367
379
  })
368
380
  expect(detailed.includes).toEqual(["bodies", "git", "pr", "workspace"])
369
381
  expect(detailed.workbase.root).toBe(root)
@@ -386,4 +398,51 @@ status: open
386
398
  expect(execution?.pr).toEqual({ url: null, state: "none" })
387
399
  expect(Schema.decodeUnknownSync(AgencyGraph)(detailed)).toEqual(detailed)
388
400
  })
401
+
402
+ test("reuses repository metadata and revision lookups for git details", async () => {
403
+ const root = await createWorkbase()
404
+ roots.push(root)
405
+ const calls = {
406
+ listWorkspaces: 0,
407
+ remoteUrl: 0,
408
+ resolveRevision: new Map<string, number>(),
409
+ }
410
+ const backend = {
411
+ kind: "git",
412
+ inspectRepository: () => Effect.succeed(null),
413
+ listWorkspaces: () =>
414
+ Effect.sync(() => {
415
+ calls.listWorkspaces += 1
416
+ return []
417
+ }),
418
+ remoteUrl: () =>
419
+ Effect.sync(() => {
420
+ calls.remoteUrl += 1
421
+ return null
422
+ }),
423
+ resolveRevision: (_path: string, revision: string) =>
424
+ Effect.sync(() => {
425
+ calls.resolveRevision.set(
426
+ revision,
427
+ (calls.resolveRevision.get(revision) ?? 0) + 1,
428
+ )
429
+ return revision
430
+ }),
431
+ workspaceHead: () => Effect.succeed(null),
432
+ workspaceDirty: () => Effect.succeed(null),
433
+ } as unknown as VersionControlBackend
434
+
435
+ await getGraph(root, { include: ["git"], backend })
436
+
437
+ expect(calls.listWorkspaces).toBe(0)
438
+ expect(calls.remoteUrl).toBe(1)
439
+ expect(calls.resolveRevision).toEqual(
440
+ new Map([
441
+ ["feat/prepare", 1],
442
+ ["feat/implement", 1],
443
+ ["feat/verify", 1],
444
+ ["main", 1],
445
+ ]),
446
+ )
447
+ })
389
448
  })
@@ -674,6 +674,40 @@ export class GraphService extends Effect.Service<GraphService>()(
674
674
  }
675
675
  const dependents = (id: string) =>
676
676
  [...(reverseDependencies.get(id) ?? [])].sort()
677
+ const repositoryWorkspaces = new Map<
678
+ string,
679
+ ReturnType<VersionControlBackend["listWorkspaces"]>
680
+ >()
681
+ const listWorkspaces = (path: string) => {
682
+ if (!backend) return Effect.succeed([])
683
+ const cached = repositoryWorkspaces.get(path)
684
+ if (cached) return cached
685
+ const workspaces = backend
686
+ .listWorkspaces(path)
687
+ .pipe(Effect.catchAll(() => Effect.succeed([])))
688
+ repositoryWorkspaces.set(path, workspaces)
689
+ return workspaces
690
+ }
691
+ const repositoryRemotes = new Map<string, string | null>()
692
+ const remoteUrl = (path: string, remote: string) =>
693
+ Effect.gen(function* () {
694
+ if (!backend) return null
695
+ const key = `${path}\u0000${remote}`
696
+ if (repositoryRemotes.has(key)) return repositoryRemotes.get(key)!
697
+ const url = yield* backend.remoteUrl(path, remote)
698
+ repositoryRemotes.set(key, url)
699
+ return url
700
+ })
701
+ const resolvedRevisions = new Map<string, string | null>()
702
+ const resolveRevision = (path: string, revision: string) =>
703
+ Effect.gen(function* () {
704
+ if (!backend) return null
705
+ const key = `${path}\u0000${revision}`
706
+ if (resolvedRevisions.has(key)) return resolvedRevisions.get(key)!
707
+ const resolved = yield* backend.resolveRevision(path, revision)
708
+ resolvedRevisions.set(key, resolved)
709
+ return resolved
710
+ })
677
711
 
678
712
  const documentDetails = <T extends Record<string, unknown>>(
679
713
  document: Document<T>,
@@ -697,11 +731,7 @@ export class GraphService extends Effect.Service<GraphService>()(
697
731
  Effect.gen(function* () {
698
732
  if (!backend) return undefined
699
733
  const inspection = yield* backend.inspectRepository(path)
700
- const workspaces = inspection
701
- ? yield* backend
702
- .listWorkspaces(path)
703
- .pipe(Effect.catchAll(() => Effect.succeed([])))
704
- : []
734
+ const workspaces = inspection ? yield* listWorkspaces(path) : []
705
735
  const primary = workspaces.find(
706
736
  (workspace) => workspace.path === path,
707
737
  )
@@ -734,15 +764,12 @@ export class GraphService extends Effect.Service<GraphService>()(
734
764
  }
735
765
  if (include.has("git")) {
736
766
  if (!backend) return result
737
- const remote = yield* backend.remoteUrl(
738
- repositoryPath,
739
- "origin",
740
- )
767
+ const remote = yield* remoteUrl(repositoryPath, "origin")
741
768
  const canonicalCheckoutPath = materialized
742
769
  ? yield* fs.realPath(checkoutPath)
743
770
  : checkoutPath
744
771
  const actualBranch = materialized
745
- ? yield* backend.listWorkspaces(repositoryPath).pipe(
772
+ ? yield* listWorkspaces(repositoryPath).pipe(
746
773
  Effect.map(
747
774
  (workspaces) =>
748
775
  workspaces
@@ -783,11 +810,11 @@ export class GraphService extends Effect.Service<GraphService>()(
783
810
  : {
784
811
  branch: data.branch,
785
812
  base: data.base,
786
- branchCommit: yield* backend.resolveRevision(
813
+ branchCommit: yield* resolveRevision(
787
814
  repositoryPath,
788
815
  data.branch,
789
816
  ),
790
- baseCommit: yield* backend.resolveRevision(
817
+ baseCommit: yield* resolveRevision(
791
818
  repositoryPath,
792
819
  data.base,
793
820
  ),