# Foundry artifact identity fix — handoff

## Root cause

Foundry 1.7.1 with solc 0.8.24 emits a `PartnershipBondV3Safe` artifact AST
for `contracts/bonds/PartnershipBondV3Safe.sol` only. Its runtime
`immutableReferences` also contains ID `17935`, inherited from
`contracts/signatures/EIP712BaseV3.sol` as `_VERSION_HASH`.

The package helper treated any artifact carrying an `ast` as complete and
skipped `out/build-info` augmentation. `_immutable_names` then compared eight
runtime reference IDs with the seven declarations in the child AST and failed
closed with:

```text
PartnershipBondV3Safe immutable runtime references cannot be bound to reviewed declarations
```

## Remediation

`_attach_build_info_asts` now always validates the local artifact AST and
combines it with only the missing, ID-matched declarations from build-info.
It rejects:

- local AST declarations absent from runtime references;
- local/build-info ID-to-name disagreement;
- unavailable inherited IDs; and
- non-unique declaration-name bindings.

The synthesized AST contains exactly the runtime-reference declarations and is
preferred by `_immutable_names`, preserving the existing exact set-equality
fail-closed gate.

## Real-artifact observations and validation

- `PartnershipBondV3Safe` immutable reference IDs:
  `17935, 3237, 3240, 3243, 3247, 3249, 3251, 3253`.
- Build-info binds `17935` to `EIP712BaseV3._VERSION_HASH`.
- The patched runtime builder writes `keccak256("3.0.0")` at every location
  for ID `17935`.
- Focused Python regression suites pass: 17 tests total.
- Direct generation with `v3-src/out` succeeds and writes an unsigned,
  non-broadcast seven-transaction package to
  `/tmp/vaultfire-real-package-after.json`.

```text
{"written": "/tmp/vaultfire-real-package-after.json", "unsigned": true, "broadcast": false, "deployments": 7}
packageSha256=59a305ea2ca9816e5c5c474dc5173f3bbcfec7863c2e2f508f5b5f558d9e7f99
boundName17935=_VERSION_HASH
allVersionWordsMatch=True
```

## Handoff constraints

The direct check used synthetic public-only finalization-candidate manifest and
intake data in `/tmp`; it did not sign, broadcast, deploy, contact RPC, or
access a wallet. The checked-in template remains non-deployable and no release
authorization is implied.
