diff --git a/packages/builder/src/env.d.ts b/packages/builder/src/env.d.ts index 45298a4..3ddb9e5 100644 --- a/packages/builder/src/env.d.ts +++ b/packages/builder/src/env.d.ts @@ -5,12 +5,5 @@ declare namespace NodeJS { VECTOR_KOIOS_URL: string; VECTOR_SUBMIT_URL: string; VECTOR_EXPLORER_URL: string; - - // Safety Limits - VECTOR_SPEND_LIMIT_PER_TX: string; - VECTOR_SPEND_LIMIT_DAILY: string; - - // Audit Log - VECTOR_AUDIT_LOG_PATH: string; } } diff --git a/packages/builder/src/vector/gov-build.ts b/packages/builder/src/vector/gov-build.ts index 0865223..6c45d23 100644 --- a/packages/builder/src/vector/gov-build.ts +++ b/packages/builder/src/vector/gov-build.ts @@ -455,6 +455,7 @@ export async function buildCritique(lucid: LucidEvolution, p: { proposalRef: `${p.proposalTxHash}#${p.proposalOutputIndex}`, stakeLovelace: stakeLovelace.toString(), scriptAddress: critiqueSpendAddress, + storageUri: finalUri, ...(ipfsCid ? { ipfsCid, documentHash } : {}), }; } diff --git a/packages/builder/src/vector/registry-build.ts b/packages/builder/src/vector/registry-build.ts index bb3b946..4dd757a 100644 --- a/packages/builder/src/vector/registry-build.ts +++ b/packages/builder/src/vector/registry-build.ts @@ -300,7 +300,7 @@ export async function buildDeregisterAgent( // REGISTRY_POLICY_ID above) does NOT match the in-workspace Aiken source // under agent-infrastructure/contracts/agent-registry/ - that source // compiles to policy 5dd51189..., a different script entirely (confirmed - // by hashing both; provenance worth resolving before the governance + // by hashing both; provenance worth resolving before the self-improvement // family trusts that source as ground truth). Differential-tested // directly against the deployed bytecode instead: deregister enforces a // floor on the owner's returned change - a sole-input deregister of a diff --git a/packages/builder/src/vector/self-improvement.ts b/packages/builder/src/vector/self-improvement.ts index c87bef1..cb051cb 100644 --- a/packages/builder/src/vector/self-improvement.ts +++ b/packages/builder/src/vector/self-improvement.ts @@ -463,7 +463,6 @@ This transaction is valid until ${new Date(r.validToMs).toISOString()} (about 6 const r = await buildCritique(lucid, { agentDid, proposalTxHash, proposalOutputIndex, critiqueType, stakeApex, critiqueDocument, critiqueHash, storageUri, }); - const storageDisplay = r.ipfsCid ? `ipfs://${r.ipfsCid}` : storageUri; return { content: [{ type: "text" as const, @@ -472,7 +471,7 @@ This transaction is valid until ${new Date(r.validToMs).toISOString()} (about 6 **Type:** ${critiqueType} **Stake:** ${stakeApex} AP3X **Proposal:** ${proposalTxHash}#${proposalOutputIndex} -**Storage:** ${storageDisplay} +**Storage:** ${r.storageUri} ${r.ipfsCid ? `**IPFS CID:** ${r.ipfsCid}\n**Hash (auto-computed):** ${r.documentHash}\n` : ''} **Script Address:** ${r.scriptAddress} **Estimated Fee:** ${r.feeAda} AP3X diff --git a/packages/builder/test/boundary.test.ts b/packages/builder/test/boundary.test.ts index b559ecb..57042fe 100644 --- a/packages/builder/test/boundary.test.ts +++ b/packages/builder/test/boundary.test.ts @@ -101,3 +101,32 @@ describe('builder custody boundary', () => { } }); }); + +// A second, narrower mechanical guard, same style as the custody boundary +// above but for a different invariant: gov-build.ts's buildProposalSpend +// exposes a 4th, TEST-ONLY `opts?: { localUPLCEval?: boolean }` parameter +// (documented in-source as an escape hatch used only by gov-build.test.ts's +// native-eval-attempt test - see gov-build.ts). Every production caller, +// including self-improvement.ts's vector_build_self_improvement_proposal_spend +// handler, must omit it and get gov-build.ts's own default +// (`opts?.localUPLCEval ?? false` - provider-side eval, parity with the +// deployed module). Nothing in the hermetic or smoke suites would notice a +// handler mutation that passed `{ localUPLCEval: true }` as a 4th argument: +// gov-build.test.ts tests buildProposalSpend directly (not through the tool +// handler), and the smoke suite only inspects tool schemas/descriptions, not +// handler bodies. This test closes that gap the same mechanical way the +// custody boundary above closes the mnemonic gap: by asserting on the +// compiled source text itself. +describe('self-improvement.ts never touches the test-only eval escape hatch', () => { + test('the test-only eval escape hatch may never be passed from a tool handler', () => { + const content = readFileSync(join(SRC_ROOT, 'vector/self-improvement.ts'), 'utf-8'); + assert.doesNotMatch( + content, + /localUPLCEval/, + 'self-improvement.ts must never reference localUPLCEval - the test-only eval escape hatch may never be passed from a tool handler; ' + + 'production callers always get gov-build.ts\'s own default (provider-side eval, parity with the deployed module). ' + + 'If buildProposalSpend is now being called with a 4th argument from a tool handler, remove it - that argument exists only for ' + + 'gov-build.test.ts\'s native-eval-attempt test.', + ); + }); +}); diff --git a/packages/builder/test/integration/rate-limit-gating.test.ts b/packages/builder/test/integration/rate-limit-gating.test.ts index 256e6e1..1a7d9b9 100644 --- a/packages/builder/test/integration/rate-limit-gating.test.ts +++ b/packages/builder/test/integration/rate-limit-gating.test.ts @@ -10,9 +10,14 @@ import type { RawServerHandle } from '../setup.ts'; // unit-tests RateLimiter/limiterFor in isolation; test:smoke only ever calls // listTools(). Neither exercises a single tool handler, so neither would // notice checkRateLimit/rateLimiter falling out of scope in agent-network.ts -// or self-improvement.ts. agent-network.ts is typed now, so tsc catches a -// scoping regression there; self-improvement.ts (typed in the -// self-improvement-keyless PR) is the remaining compiler-blind case. See +// or self-improvement.ts. Both files are typed now (self-improvement.ts as of +// the self-improvement-keyless PR) - no compiler-blind tool-registration file +// remains in the builder - but tsc alone still can't catch this class of bug: +// a handler that silently drops the `if (rateLimited) return rateLimited;` +// early-return typechecks fine (the function still returns the right shape), +// it just never gets called at runtime unless a real request exercises it. +// This test's timing-based hermeticity rationale stands on its own for that +// reason, independent of which files happen to be typed. See // .superpowers/sdd/pr5-integration-tests-report.md for the mutation-testing // evidence. // diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 2cb7122..5c075b9 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -143,6 +143,7 @@ export interface VectorBuildStakeResult extends VectorUnsignedBuildResult { scriptAddress: string; ipfsCid?: string; documentHash?: string; + storageUri?: string; // set by buildCritique only - buildEndorse has no document/URI concept } export interface VectorBuildProposalLockResult extends VectorUnsignedBuildResult {