chore(builder): delete dead env declarations, pin the eval escape hatch, close residue

Opus review round on the self-improvement-keyless capstone commit, 2
Important + 3 Minor, all residue (no custody defects):

- env.d.ts: delete the three dead VECTOR_SPEND_LIMIT_PER_TX/_DAILY/
  VECTOR_AUDIT_LOG_PATH ambient declarations - safety.ts was their only
  reader and it's deleted. The capstone custody PR should not leave
  live-looking spend-limit declarations behind.
- boundary.test.ts: new mechanical pin asserting self-improvement.ts never
  references localUPLCEval - the test-only eval escape hatch on
  buildProposalSpend may never be passed from a tool handler. Proved
  load-bearing: applied the mutation (a 4th { localUPLCEval: true } arg),
  watched it fail by name, reverted, confirmed a byte-identical diff against
  the mutation (git diff --stat showed only the unrelated storageUri
  simplification below), re-ran green.
- rate-limit-gating.test.ts: reworded the now-false "self-improvement.ts is
  the remaining compiler-blind case" comment - both tool-registration files
  are typed now; the timing-based hermeticity rationale stands on its own.
- types.ts + gov-build.ts + self-improvement.ts: added optional storageUri
  to VectorBuildStakeResult, set by buildCritique only; the critique tool
  handler now renders r.storageUri directly instead of reconstructing
  ipfs://${ipfsCid}, removing the duplicated URI-format knowledge. Result
  types are not wire schemas - zero snapshot drift confirmed.
- registry-build.ts:303: "governance family" -> "self-improvement family".

Gate: typecheck 0 (3 packages) - unit 392/392 (391 + the new eval-escape-hatch
pin) - smoke 27/27, 24 tools, zero snapshot drift - auth 12/12 - boundary
5/5 (4 custody + the new pin). Custody grep re-confirmed empty.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
FilipBl4gojevic
2026-07-30 17:00:02 +02:00
parent 88068816e6
commit daa3fd6a43
7 changed files with 41 additions and 13 deletions

View File

@@ -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;
}
}

View File

@@ -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 } : {}),
};
}

View File

@@ -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

View File

@@ -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

View File

@@ -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.',
);
});
});

View File

@@ -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.
//

View File

@@ -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 {