Skip to content

feat(harness): migrate graphs to package inventory [SAP-2955] - #750

Merged
ynadge merged 4 commits into
mainfrom
codex/sap-2955-package-inventory-replacement
Aug 31, 2026
Merged

feat(harness): migrate graphs to package inventory [SAP-2955]#750
ynadge merged 4 commits into
mainfrom
codex/sap-2955-package-inventory-replacement

Conversation

@ynadge

@ynadge ynadge commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces #731 with a clean SAP-2955 implementation on current main.

  • defines package inventory protocol 1 as the deterministic node contract: which agents exist, their stable canonical/provisional identities, and package-relative locations
  • migrates the V0 workspace graph through the inventory boundary, with fast provisional rendering and background source-identity enrichment
  • keeps filesystem navigation private in a boot-token-protected sidecar committed with the exact graph revision it belongs to
  • preserves current main's right-pane, group-aware Project map
  • retains the useful follow-up fixes from fix(harness): reach the graph fast path on a real install — take over #731 [SAP-2978] #741: settled-identity caching, pending-collision protection, enrichment after superseded builds, the public identity-issue type, and bounded navigation backoff

Package inventory intentionally carries no opaque staticSignals field. Package-wide data-flow and cross-agent relationship evidence are out of scope here and will use a separately versioned contract with deterministic provenance.

Integration notes

Verification

  • @sapiom/agent: 10 suites / 175 tests; typecheck, lint, CJS build, and ESM build
  • @sapiom/harness: 168 unit files / 2,758 tests with sandbox capabilities dropped for normal permission semantics; typecheck, lint, and production build
  • Harness performance suite: 3 files / 10 tests
  • focused graph integration after the latest main rebase: 10 files / 142 tests
  • Chromium mock UI: 479 / 479 tests; the zero-size Polsia opening journey also passed 10 / 10 under four workers
  • Chromium canvas: 10 / 10 tests
  • GitHub browser CI: 479 mock UI tests and 10 canvas tests on final head 9a8d8e8
  • terminology and provider-neutral copy checks
  • pnpm changeset status --since=origin/main
  • git diff --check
  • repository search confirms no staticSignals, PackageInventoryStaticSignals, or PackageInventoryJsonValue

Independent review

Two Claude Opus review rounds inspected the complete diff. Round one found two lifecycle regressions; both were fixed with regression coverage. Round two confirmed those fixes and the deliberate fingerprint/canonical-identity decisions, found the shared-unsettled-marker identity defect and dead state that were then fixed, withdrew its Zod concern after checking current main, and reported no blockers in its follow-up reviews of both the amended identity logic and final viewport hardening at 9a8d8e8.

Linear: SAP-2955

ynadge added 3 commits August 31, 2026 05:27
Define the identity-and-location-only package inventory contract and migrate the V0 workspace graph through revision-atomic inventory, navigation, and lifecycle boundaries.\n\nRetain settled-identity caching, pending-collision protection, superseded enrichment, and resolver backoff without shipping opaque relationship signals.\n\nRefs: SAP-2955\n\nCo-authored-by: G. David Witwer <gdavid.witwer@gmail.com>
Keep the newest usable partial projection and its revision-matched navigation visible while the next graph build runs. Exact-revision stale sidecars remain interactive unless a newer invalidation has arrived.

Refs: SAP-2955
Use location-backed keys when unsettled agents share a marker, keep the newest usable projection through refresh failures, and pin revision-matched stale navigation and groups in the browser.

Refs: SAP-2955
@ynadge
ynadge force-pushed the codex/sap-2955-package-inventory-replacement branch from b07931e to bc58b35 Compare August 31, 2026 05:32
@github-actions

Copy link
Copy Markdown
Contributor

Review — PR #750 (round 1)

Public-copy hygiene is clean: both changesets, the workspace-system-graph.md additions, and
every new JSDoc block describe roles and mechanics only — no company other than Sapiom, no
business arrangement, no internal hostname, incident, or private link. Fixture names
(acme-app, polsia, demo) are pre-existing. Nothing here describes an exploitable
vulnerability.

1. @sapiom/agent gains a 5-export public contract with no consumer outside this repo

packages/agent/src/index.ts:87-94 exports PACKAGE_INVENTORY_PROTOCOL,
packageInventorySchema, PackageInventory, PackageInventoryAgent,
PackageInventoryIdentityIssue, PackageInventoryVersion as a minor bump. The PR body
states no backend/cloud ingestion is introduced, and the only in-repo consumer is
@sapiom/harness (core/system-graph-inventory.ts, core/system-graph.ts) — a sibling in
the same monorepo that does not need the authoring SDK's published surface to reach it.

What this commits us to: protocol 1's validation rules ship as contract, not just its
shape. packageInventorySchema rejects a bundle inventory that is degraded, rejects any
complete inventory containing a provisional identity, and rejects non-canonical path
spellings rather than normalizing them (package-inventory.ts:282-302). A stranger who
adopts packageInventorySchema for their own build tooling can be broken by any loosening,
so protocol 1 is effectively frozen before a second producer exists to validate the design.
Either state in the changeset that this is load-bearing for the cloud build (and what
protocol 2 will do), or keep it internal to the harness until a real producer lands —
@sapiom/harness already has src/shared/ for exactly this kind of cross-boundary type.

2. Viewport store: unbounded growth, and pan/zoom lost on any membership change

web/src/components/SystemGraphCanvas.tsx:46-58 keys the viewport store on
JSON.stringify([workspaceKey, groups.map(({id, nodeIds, isUngrouped}) => ...)]).
createSystemGraphViewportStore (web/src/lib/system-graph-viewport.ts:226-237) is a
module-level Map with no eviction and no retain() — it was previously bounded by the
number of workspaces. Now every distinct layout adds a permanent entry whose key embeds
every node id in the graph. In a desktop session left open for days across agent
adds/removes/renames and identity enrichment, that Map only grows.

The same over-specification costs behavior: adding one agent changes nodeIds, so the key
changes, so the layout effect refits and silently discards a pan/zoom the user set on a
large map. The stated goal — never restore flat coordinates onto the grouped layout — needs
only a coarse discriminator (e.g. groups === undefined, group count, or a hash), not the
full membership. Also, viewportLayoutKey is declared to return WorkspaceKey while
returning a JSON blob; that type lie is what makes the mismatch invisible at the call sites.

3. workspaceRelativeLocalKey is now dead, still exported, and had its behavior changed

src/shared/system-graph.ts:66 has zero production callers after this PR (both former
callers — core/system-graph.ts and web/src/lib/system-graph-navigation.ts — dropped it,
and core/system-graph-inventory.ts dropped its re-export). It still ships in dist, and
the PR changed its source === scope fallback from the directory basename to "root"
(shared/system-graph.ts:93). Changing the semantics of a function nothing calls, while the
live local: key is built inline in system-graph-inventory.ts:473-475, leaves two
divergent implementations of the same rule and one of them untestable against real use.
Delete it, or make the inventory call it so the "root" fallback has exactly one home.

4. Freshness test asserts a transient state — CI flake risk

src/server/system-graph-freshness.test.ts:160-166: the vi.waitFor requires
sourceRefresh.state === "degraded" after the source edit. degraded is the intermediate
window between the invalidated rebuild and enrichment settling; the terminal state is
ready. If a 150 ms poll misses that window (fast machine, warm esbuild), the condition can
never become true again and the test fails at the 8 s timeout rather than flaking loudly.
Assert the invariant that holds — a revision bump plus the expected edge — and let the state
settle, or drive enrichment deterministically.


Verdict: No confidentiality or semver-correctness blockers; the harness changes are
internal and the minor levels fit. Finding 1 is the one worth deciding before merge, since
an npm export cannot be walked back — the rest are cleanups.

@ynadge
ynadge merged commit 917c930 into main Aug 31, 2026
12 of 13 checks passed
@ynadge
ynadge deleted the codex/sap-2955-package-inventory-replacement branch August 31, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant