Compare commits

..
Author SHA1 Message Date
fred c56483eb1b test(#1017): wire in four CI-fit shell suites, drop their signed exclusions
ci/woodpecker/pr/ci Pipeline failed
check-test-enumeration.sh signed four suites as 'likely CI-fit; #1017 burndown'.
Measured all four: each passes standing alone, and each still passes with tmux
removed from PATH entirely (test-start-agent-session.sh writes its own tmux shim
into a fake bin dir, so it never needed the real binary).

Red-first: removing the four exclusion lines makes the guard report exactly four
UNENUMERATED failures. Appending the four to test:framework-shell returns it to
OK, with in-population enumerated going 32 -> 36 and signed exclusions 19 -> 15.

Refs #1017
2026-08-16 01:14:03 -05:00
7 changed files with 1 additions and 117 deletions
-1
View File
@@ -33,7 +33,6 @@ export default tseslint.config(
'packages/db/vitest.config.ts',
'packages/storage/vitest.config.ts',
'packages/mosaic/vitest.config.ts',
'packages/mosaic/vitest.setup.ts',
'packages/mosaic/__tests__/*.ts',
'tools/federation-harness/*.ts',
],
@@ -32,10 +32,6 @@ packages/mosaic/framework/tools/tmux/test-send-message-socket.sh | requires a re
packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh | requires real tmux-pane fixtures on a throwaway socket; CI image ships no tmux; #1017 burndown (same condition as its sibling)
# --- single-suite directories: unmeasured in CI ---
packages/mosaic/framework/tools/fleet/test-start-agent-session.sh | unmeasured in CI image; stubs tmux via a fake bin dir, likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/glpi/test-list-http-status.sh | unmeasured in CI image; stub-based (#807 regression harness), likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/orchestrator/test-board-roll.sh | unmeasured in CI image; file-fixture based, likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/woodpecker/test-ci-wait-exit-matrix.sh | unmeasured in CI image; drives ci-wait.sh against a stub pipeline-status.sh, likely CI-fit; #1017 burndown
# --- naming-boundary files the strict test-*.sh prefix cannot even name ---
# (#1017: three independent censuses handled the microtest file three different
+1 -1
View File
@@ -25,7 +25,7 @@
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
"test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/promotion_binding_unittest.py && python3 src/lease-broker/promotion_trigger_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/receipt_observer_client_unittest.py && python3 src/lease-broker/invariant_r_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/_scripts/test-mosaic-init-rce.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh"
"test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/promotion_binding_unittest.py && python3 src/lease-broker/promotion_trigger_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/receipt_observer_client_unittest.py && python3 src/lease-broker/invariant_r_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/_scripts/test-mosaic-init-rce.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh && bash framework/tools/fleet/test-start-agent-session.sh && bash framework/tools/glpi/test-list-http-status.sh && bash framework/tools/orchestrator/test-board-roll.sh && bash framework/tools/woodpecker/test-ci-wait-exit-matrix.sh"
},
"dependencies": {
"@mosaicstack/brain": "workspace:*",
@@ -1,64 +0,0 @@
import { describe, expect, test } from 'vitest';
import { isHostLeaseVariable, scrubHostLeaseEnv } from './host-lease-env.js';
describe('host lease environment scrubbing', () => {
test('removes every lease variable and reports what it removed', () => {
const environment = {
MOSAIC_LEASE_GENERATION_FILE: '/run/user/1001/mosaic-lease/generation-abc.state',
MOSAIC_LEASE_SESSION_ID: 'a'.repeat(64),
MOSAIC_LEASE_BROKER_SOCKET: '/run/user/1001/mosaic-lease/broker.sock',
MOSAIC_LEASE_RUNTIME: 'claude',
MOSAIC_RUNTIME_GENERATION: '388',
PATH: '/usr/bin',
MOSAIC_AGENT_NAME: 'fred',
} as NodeJS.ProcessEnv;
expect(scrubHostLeaseEnv(environment)).toEqual([
'MOSAIC_LEASE_BROKER_SOCKET',
'MOSAIC_LEASE_GENERATION_FILE',
'MOSAIC_LEASE_RUNTIME',
'MOSAIC_LEASE_SESSION_ID',
'MOSAIC_RUNTIME_GENERATION',
]);
expect(environment).toEqual({ PATH: '/usr/bin', MOSAIC_AGENT_NAME: 'fred' });
});
test('a lease variable added later is scrubbed without being listed anywhere', () => {
// The prefix rule is the point: this is the case a hand-maintained list would miss.
const environment = { MOSAIC_LEASE_SOMETHING_NEW: 'x' } as NodeJS.ProcessEnv;
expect(scrubHostLeaseEnv(environment)).toEqual(['MOSAIC_LEASE_SOMETHING_NEW']);
expect(environment).toEqual({});
});
test('leaves unrelated variables alone', () => {
const environment = {
MOSAIC_AGENT_NAME: 'fred',
MOSAIC_HOME: '/home/fred/.mosaic',
HOME: '/home/fred',
} as NodeJS.ProcessEnv;
expect(scrubHostLeaseEnv(environment)).toEqual([]);
expect(environment).toEqual({
MOSAIC_AGENT_NAME: 'fred',
MOSAIC_HOME: '/home/fred/.mosaic',
HOME: '/home/fred',
});
});
test('classifies by prefix, not by an exact list', () => {
expect(isHostLeaseVariable('MOSAIC_LEASE_ANYTHING')).toBe(true);
expect(isHostLeaseVariable('MOSAIC_RUNTIME_GENERATION')).toBe(true);
expect(isHostLeaseVariable('MOSAIC_RUNTIME')).toBe(false);
expect(isHostLeaseVariable('LEASE_MOSAIC_X')).toBe(false);
});
// Wiring check. On a clean checkout or in CI these variables are unset, so this
// passes whether or not vitest.setup.ts is registered -- it is worth little there and
// is not claimed to be. Its value is inside a Mosaic-managed agent seat, where the
// variables ARE set and this is the assertion that catches the setup file being
// dropped from vitest.config.ts. That is the environment the leak was found in.
test('the suite does not run with the host lease identity in scope', () => {
expect(Object.keys(process.env).filter(isHostLeaseVariable)).toEqual([]);
});
});
@@ -1,41 +0,0 @@
/**
* Remove the host's live lease identity from an environment before tests run.
*
* The lease specs start their own broker on a private socket and then spawn the real
* hook scripts against it, building each child's environment as `{ ...process.env, <the
* few vars this case cares about> }`. That spread is the problem: when the suite runs
* inside a Mosaic-managed agent seat, `process.env` already carries that seat's real
* lease identity, and the parts the spread does not override survive into the child.
*
* `MOSAIC_LEASE_GENERATION_FILE` is the one that bites. `read_runtime_generation()`
* prefers that file over `MOSAIC_RUNTIME_GENERATION`, so a case that carefully sets
* `MOSAIC_RUNTIME_GENERATION: '1'` is silently overruled by the host's generation
* counter -- which on a long-lived seat is in the hundreds. The revoke client reads it,
* sends it, and the test broker advances the session to that generation. Every later
* `authorize` in the case sends generation 1, is now behind, and is denied with
* `STALE_GENERATION` instead of the `MUTATOR_UNVERIFIED` the case asserts. The gate
* still denies, so this is not a hole in the product -- but it turns four acceptance
* tests red for a reason that has nothing to do with the code under test.
*
* It only reproduces inside a managed seat. On a clean checkout or in CI these vars are
* unset, the suite is green, and the leak is invisible -- which is why it survived.
*
* Scrubbing by prefix rather than by an explicit list is deliberate: any lease variable
* added later leaks by exactly the same route, and a list would have to be remembered.
*/
const HOST_LEASE_PREFIX = 'MOSAIC_LEASE_';
const HOST_LEASE_EXTRA = ['MOSAIC_RUNTIME_GENERATION'];
export function isHostLeaseVariable(name: string): boolean {
return name.startsWith(HOST_LEASE_PREFIX) || HOST_LEASE_EXTRA.includes(name);
}
/** Deletes the host lease variables from `environment`; returns the names removed. */
export function scrubHostLeaseEnv(environment: NodeJS.ProcessEnv): string[] {
const removed = Object.keys(environment).filter(isHostLeaseVariable);
for (const name of removed) {
delete environment[name];
}
return removed.sort();
}
-1
View File
@@ -5,7 +5,6 @@ export default defineConfig({
globals: true,
environment: 'node',
testTimeout: 30_000,
setupFiles: ['./vitest.setup.ts'],
coverage: {
provider: 'v8',
include: ['src/commands/skill.ts', 'src/lease-broker/broker-test-client.ts'],
-5
View File
@@ -1,5 +0,0 @@
import { scrubHostLeaseEnv } from './src/test-support/host-lease-env.js';
// Runs before every spec file in this package. See src/test-support/host-lease-env.ts
// for why the host's lease identity must not reach a spawned hook process.
scrubHostLeaseEnv(process.env);