Valisthea, this is the right way to respond to a technical review.
You reproduced the claims independently, distinguished the actual consequence from the original wording, identified an additional non-canonical Boolean defect, and published a concrete correction plan rather than defending the existing implementation.
I also rechecked current main after your reply.
Commit f1b041f already corrects the published selector, the active CeremonyHelper verification link, and the previously absent observed code hashes, and adds the first selector-recomputation and portable-subset gates.
That is a meaningful immediate response.
There is, however, one layer still missing from point 3.
You are correct that an immutable CeremonyHelper correction forces a new address and therefore a new helper-suite identity.
But this is not only a helper redeployment problem.
It is a migration of the complete semantic dependency graph:
Covenant source
→ compiler identity
→ helper interface
→ helper implementation
→ helper deployment
→ target descriptor
→ generated Covenant bytecode
→ existing helper sessions
→ verifier identity
That changes the order in which the work should be frozen.
1. Point 3 requires four separate identities
The target descriptor should not be the first object and cannot be the only object.
Some information is known before deployment; other information exists only after deployment.
I would separate the system into four immutable records.
A. Helper Interface Specification
Chain-independent and frozen before implementation deployment:
- suite schema version;
- helper family;
- canonical signatures;
- selectors;
- argument types;
- return types;
- state mutability;
- call mode;
- revert contract;
- state-transition semantics;
- evidence object semantics;
- security classification;
- interface hash.
This should be the typed source from which Rust dispatch, Solidity interfaces, JSON output, ABI expectations, and tests are generated.
B. Helper Suite Build Receipt
Frozen before deployment:
- exact source-tree hash;
- Solidity compiler version;
- compiler settings;
- optimizer settings;
- metadata policy;
- creation bytecode hash;
- runtime bytecode hash;
- init-code hash;
- ABI hash;
- storage-layout hash;
- test manifest;
- suite composition.
This produces the expected identity of the code.
C. Chain Deployment Receipt
Created after deployment, separately for every helper:
- chain ID;
- chain fingerprint;
- helper identity;
- deployment mechanism;
- factory;
- salt;
- init-code hash;
- deployer;
- transaction hash;
- block number;
- block hash;
- resulting address;
- observed
EXTCODEHASH;
- expected runtime hash;
- equality result.
D. Target Descriptor
The target descriptor should then reference:
- the Helper Interface Specification;
- the Helper Suite Build Receipt;
- the relevant Chain Deployment Receipts;
- the qualified chain environment.
The Covenant artifact receipt can finally reference the target-descriptor hash.
The corrected order is therefore:
freeze interface
→ build candidate
→ freeze expected bytecode identity
→ deploy once
→ record exact chain identity
→ construct target descriptor
→ compile target-bound Covenant artifact
In the current plan, Sprint 3 redeploys the helper suite before Sprint 4 defines the target descriptor and build receipt. That risks freezing the deployment before the object that gives the deployment meaning is complete.
Your “freeze once, properly” conclusion is correct.
The practical consequence is that the normative interface and expected build identity must be frozen before the one deployment.
2. Treat the correction as a migration, not only a new address
The current architecture explicitly states that helper addresses are embedded into generated bytecode and remain there for the life of the Covenant contract.
Therefore, deploying a corrected helper does not correct any previously generated contract.
For every legacy Covenant artifact, the old address remains permanent.
The helper also stores sessions internally, so existing sessions do not automatically move into a corrected helper.
The correction therefore needs an affected-artifact and session policy:
- which compiler versions emitted the old address;
- which generated contracts reference it;
- which sessions exist in the old helper;
- whether any session can be completed safely;
- whether migration is possible;
- whether the old path must be abandoned;
- how the verifier reports legacy artifacts;
- how the compiler rejects superseded suites;
- whether the playground can still reproduce historical builds without presenting them as current.
Required suite states should include:
CURRENT;
SUPERSEDED;
LEGACY_REPRODUCTION_ONLY;
UNSAFE_DO_NOT_USE;
RETIRED.
A new helper version fixes future compilations.
It does not retroactively repair the old dependency graph.
Also, a new suite version does not necessarily require redeploying every unchanged immutable helper.
The suite can be a versioned composition that references:
- a new CeremonyHelper receipt;
- existing unchanged FHE/PQ/ZK helper receipts;
- their exact previously frozen code identities.
That avoids unnecessary redeployment while still producing a new aggregate suite identity.
3. Reconcile the deployment history before the next deployment
The current public sources describe three different deployment stories.
Your reply states:
- three helpers were deployed by CREATE2;
- CeremonyHelper was deployed by an ordinary transaction.
The current helpers/script/Deploy.s.sol deploys all four contracts using CREATE2 syntax, including:
SALT_CEREMONY = keccak256("covenant-v0.9.1-ceremony")
The helper README also describes all four helpers through deterministic CREATE2 salts.
The registry then states:
All 4 CREATE2 addresses match Sprint 30 predictions exactly
but the Sprint 30/V0.9.0 predicted CeremonyHelper address is:
0x6cAB...
while the active V0.9.1 CeremonyHelper is:
0x627f...
These statements may refer to different historical deployments, but they cannot currently be reconstructed from one canonical receipt.
Before the next suite is deployed, record the deployment identity separately for each helper.
A single global:
deployed_at_block
is not sufficient where the active suite combines contracts from different transactions, versions, salts, or deployment mechanisms.
4. Do not mutate the V0.9.0 manifest under the same identity
The selector and verification-link corrections are correct.
The identity problem is that the file remains:
helper-addresses-v0.9.0.json
with:
"version": "0.9.0"
while it now contains:
- the active V0.9.1 CeremonyHelper;
- corrected selector information;
- new code hashes;
- a superseded V0.9.0 helper;
- mutable status text.
This means two materially different documents have existed under one semantic identity.
The historical V0.9.0 manifest should remain immutable.
The clean recovery is:
- preserve the original V0.9.0 manifest hash;
- record that its selector and verification entry were incorrect;
- publish a detached correction/supersession receipt;
- publish a new manifest identity for the actual active suite;
- make the compiler and verifier bind the exact manifest hash, not only a compatible version expression such as
^0.9.0.
The correction history is part of the evidence.
It should not disappear when the current JSON becomes accurate.
5. Distinguish observed code hash from expected code identity
Populating code_hashes is useful, but:
code_hashes_read_at: "sepolia latest, 2026-07-30"
is not yet a reproducible verification receipt.
An observed EXTCODEHASH answers:
What code was present at this address when the query was made?
It does not answer:
Is this the exact runtime code produced by the accepted source and compiler configuration?
That requires:
expected runtime hash from the build receipt
observed EXTCODEHASH at an exact block
The receipt should include the exact block number and block hash rather than latest.
Otherwise the system can authenticate the identity of an unknown deployment without proving that it is the intended deployment.
6. The current smoke test is helper-level, not compiler-to-helper end-to-end
The registry records:
CeremonyHelper.amnesiaSetup(42, 3, 2)
as the first-call method and describes the result as an end-to-end smoke test.
The current compiler path, however, emits:
amnesiaSetup(uint256)
A direct cast call to the three-argument overload proves that the helper accepts that ABI.
It does not prove that a generated Covenant contract emits the correct selector, argument layout, call mode, return decoder, and local state transition.
The actual end-to-end gate should be:
- compile a frozen Covenant ceremony fixture;
- confirm the generated bytecode embeds the expected helper address;
- deploy the generated Covenant contract;
- call its public
setup();
- trace the generated call into the helper;
- verify the exact selector and calldata;
- verify the returned session ID;
- verify both helper state and Covenant-local state;
- bind the complete run to the compiler, suite, target, and deployment receipts.
The direct cast test should remain, but should be named a helper deployment smoke test.
7. The selector gate is not yet exhaustive
The new selector tests close the immediate regression, but the current manifest test does not actually enumerate every selector in the manifest.
The manifest contains approximately twenty selector entries.
The test-side MANIFEST array contains six.
The direct dispatch-to-manifest comparison contains five mappings.
The current test therefore proves that the listed subset is correct.
It does not prove that:
- every manifest selector was checked;
- no extra untested selector exists;
- no compiler dispatch entry is absent from the manifest;
- no selector collision exists;
- argument and return shapes agree;
- mutability and call mode agree.
The typed interface registry you propose is the real fix.
Its generated acceptance should require:
- exact entry-count equality;
- no unconsumed manifest entries;
- no unrepresented dispatch entries;
- no selector collisions;
- ABI equality against the compiled Solidity interface;
- return-shape equality;
- call-mode and mutability equality.
No hand-maintained parallel list should decide what “every selector” means.
8. Keep destruction and its evidence atomic
The proposed strict Boolean plus separate bytes32 view removes the dynamic-ABI/Boolean mismatch.
I would still change the return contract.
The cleanest V0.9-compatible interface is:
amnesiaDestroy(uint256 sessionId) returns (bytes32 commitment)
Failure is already expressed by revert.
On success, the generated Covenant contract should:
- require exactly 32 bytes of returndata;
- decode the commitment;
- store it locally;
- emit it in the generated Covenant event;
- advance the local phase only after successful decoding.
A second view such as:
getDestructionCommitment(uint256)
can remain as a recovery and inspection surface.
It should not be the only way the generated Covenant artifact receives the evidence produced by its own state transition.
A valid alternative is:
returns (bool ok, bytes32 commitment)
with exact 64-byte decoding and a strict canonical ok == 1.
But the Boolean carries little information when every failure already reverts.
The important invariant is:
destruction state transition
+
commitment creation
+
commitment consumption
+
local phase transition
occur in one atomic execution path.
9. Bind the commitment to the ceremony policy, not only the submitted values
The present commitment binds:
sharesSubmitted;
sessionId.
It does not explicitly bind:
- commitment schema/domain version;
- chain ID;
- helper address or suite identity;
- ceremony contract;
- guardian count;
- threshold;
- submitted guardian identities.
After the shares and guardian list are deleted, the retained commitment should still identify the complete policy and participation object whose values were destroyed.
At minimum, its canonical preimage should bind:
- a domain separator;
- schema version;
- chain;
- helper;
- ceremony;
- session;
- guardian count;
- threshold;
- identity-set commitment;
- share-set commitment.
This still does not become proof of cryptographic erasure.
It becomes a well-scoped commitment to the exact ceremony state that was destroyed.
10. Define the guardian trust boundary explicitly
Replacing tx.origin is necessary, but the replacement needs a precise claim.
The generated Covenant contract currently enforces distinct callers.
The language does not yet support a pre-registered guardian-address set.
Therefore, the current guarantee is:
threshold distinct submitter identities participated
It is not:
threshold authorized members of a predefined guardian set participated
If the generated contract passes an identity argument to the helper, the helper is accepting an identity attested by that ceremony contract.
It is not independently authenticating the human or account behind that identity.
The helper should still enforce its local invariants:
- nonzero identity;
- one share per identity;
- submitted-share count not exceeding
guardiansCount;
- threshold within bounds;
- no finalization below threshold.
But the documentation should preserve the distinction between:
- contract-attested submitter identity;
- independently verified guardian membership.
11. Refuse the current destroy path immediately
The current source now documents that the assert is vacuous and that the helper’s reverts are the real protection.
That is honest and useful.
But the path remains synthesized and callable until Sprint 2.
At the same time, the public README still describes:
- a real four-phase ceremony lifecycle;
- an externally verified
setup → destroy milestone.
The project is testnet-only, so this is not a live-value emergency.
It is still better to make refusal the first containment action:
- compiler error for helper-bound
destroy;
- playground refusal;
- CLI refusal;
- capability state
REFUSED_ABI_MISMATCH;
- legacy milestone explicitly marked as historical and semantically limited.
A comment explaining a non-functional guard should not coexist for days or weeks with a public path that still emits that guard.
12. Make the portable-subset gate exhaustive through the capability registry
Promoting the portable invariant early is correct.
The current gate is still fixture-based.
Once the capability registry exists, it should generate the portable test matrix:
- every construct classified
IMPLEMENTED_PORTABLE must contribute a fixture;
- adding a portable construct without a fixture must fail CI;
- every portable fixture must compile byte-identically across all target descriptors;
- every target-bound construct must provide a negative control showing that target identity does affect its dependency receipt.
This prevents the invariant from silently becoming incomplete as the language grows.
Revised closure order
I would now use this order:
-
Refuse the known-wrong destroy path everywhere.
-
Freeze the historical V0.9.0 and V0.9.1 identities and publish the correction/supersession relationship.
-
Define the typed Helper Interface Specification.
-
Finalize the corrected CeremonyHelper ABI and the generated compiler ABI together.
-
Build the candidate helper suite and freeze the expected build receipt.
-
Run exhaustive interface, ABI, state-machine, and negative-boundary tests.
-
Deploy the immutable candidate once.
-
Produce per-helper Chain Deployment Receipts.
-
Construct the Target Descriptor from the frozen specification, expected build identity, and observed deployment identity.
-
Run the true compiler-to-helper end-to-end acceptance.
-
Publish the legacy-artifact and session migration/supersession matrix.
-
Enable constructor CHAINID and exact EXTCODEHASH gates.
-
Enable the strict deployable profile and generated capability registry.
-
Run the Orbit qualification matrix against the frozen descriptor.
-
Audit the portable profile, then the helper bridge, then the real cryptographic implementations.
The acceptance result I would hold the correction to is not merely:
new helper deployed
It is:
- no legacy artifact is misidentified as current or safe;
- no new artifact can compile against an incomplete suite identity;
- every deployed helper is bound to its expected build;
- the actual compiler-generated route is exercised end to end;
- the destruction commitment is consumed atomically;
- the helper and generated contract enforce compatible local state machines;
- the verifier can distinguish every historical and current dependency graph.
Your response and the immediate commit are strong.
The remaining work is to make the correction itself the first artifact governed by the identity system it is introducing.