feat(workspaces): connectWorkspace use case with audit
Manifest-first: connectWorkspace declared mutates:true with the workspace-connected audit event, requiredCores gains audit. Workspace entity gains gitUrl + persisted status enum (created/connecting/ready/ error). Input takes name + git URL + PAT; the output schema is the credential-free workspace entity, so the PAT can never round-trip. Audit emission asserted with RecordingAuditLog; binders wire the use case through wireUseCase with the __audited brand, and web-next bindAll now binds core-audit (payload+stdout sinks in production, stdout in dev-seed) so boot conformance passes in both modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@repo/auth": "workspace:*",
|
||||
"@repo/core-api": "workspace:*",
|
||||
"@repo/core-audit": "workspace:*",
|
||||
"@repo/core-cms": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@repo/core-trpc": "workspace:^",
|
||||
|
||||
@@ -8,6 +8,18 @@ vi.mock("@repo/auth/di/bind-production", () => ({
|
||||
bindProductionAuth: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/auth/di/bind-dev-seed", () => ({ bindDevSeedAuth: vi.fn() }));
|
||||
vi.mock("@repo/workspaces/di/bind-production", () => ({
|
||||
bindProductionWorkspaces: vi.fn(),
|
||||
}));
|
||||
vi.mock("@repo/workspaces/di/bind-dev-seed", () => ({
|
||||
bindDevSeedWorkspaces: vi.fn(),
|
||||
}));
|
||||
// bindAudit enforces AUDIT_PSEUDONYM_SALT under NODE_ENV=production — mocked
|
||||
// here so dispatcher-routing tests stay focused on routing. The real audit
|
||||
// binding is exercised by bind-production.smoke.test.ts (dev-seed path).
|
||||
vi.mock("@repo/core-audit/di", () => ({
|
||||
bindAudit: vi.fn(() => ({ auditLog: { record: vi.fn() } })),
|
||||
}));
|
||||
vi.mock("@repo/core-shared/instrumentation", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("@repo/core-shared/instrumentation")>();
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
type IJobQueue,
|
||||
} from "@repo/core-shared/jobs";
|
||||
import { NoopRateLimit } from "@repo/core-shared/rate-limit";
|
||||
import { bindAudit } from "@repo/core-audit/di";
|
||||
import { bindProductionAuth } from "@repo/auth/di/bind-production";
|
||||
import { bindDevSeedAuth } from "@repo/auth/di/bind-dev-seed";
|
||||
import { bindProductionWorkspaces } from "@repo/workspaces/di/bind-production";
|
||||
@@ -85,11 +86,19 @@ export async function bindAllProduction(): Promise<void> {
|
||||
const { queue } = await resolveJobsProduction();
|
||||
const resolvedConfig = await config;
|
||||
|
||||
// Audit trail (core-audit): Payload hot store + stdout JSON for the log
|
||||
// shipper. Entries are trace-id enriched from the active OTel span.
|
||||
const { auditLog } = bindAudit(sharedContainer, {
|
||||
payloadConfig: resolvedConfig,
|
||||
sinks: ["payload", "stdout"],
|
||||
});
|
||||
|
||||
const ctx: BindProductionContext = {
|
||||
config: resolvedConfig,
|
||||
tracer,
|
||||
logger,
|
||||
queue,
|
||||
auditLog,
|
||||
rateLimit: new NoopRateLimit(),
|
||||
};
|
||||
|
||||
@@ -106,10 +115,16 @@ export async function bindAllDevSeed(): Promise<void> {
|
||||
const { tracer, logger } = resolveInstrumentation(); // Rule 0
|
||||
const { queue } = resolveJobsDevSeed();
|
||||
|
||||
// Dev-seed audit trail: stdout JSON only (no Payload booted). Keeps the
|
||||
// __audited brand path identical to production so the boot conformance
|
||||
// assertion exercises the same wiring.
|
||||
const { auditLog } = bindAudit(sharedContainer, { sinks: ["stdout"] });
|
||||
|
||||
const ctx: BindContext = {
|
||||
tracer,
|
||||
logger,
|
||||
queue,
|
||||
auditLog,
|
||||
rateLimit: new NoopRateLimit(),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user