Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR adds a local-only “Trust Agents” workflow orchestration subsystem to the API (in-memory artifacts, verification, and release-gating), introduces a browser-local artifact verification page in the web app, and tightens repository hygiene checks around private diligence artifacts and environment files.
Changes:
- Add API workflow orchestration types/service/store/policy plus new routes and end-to-end tests for workflow + readiness audit flows.
- Add a new web UI route to locally fingerprint artifacts, generate a local receipt JSON, and verify integrity without uploading files.
- Add repo hygiene tooling (audit script + pre-commit checks) and documentation updates for compliance/readiness boundaries.
Reviewed changes
Copilot reviewed 51 out of 53 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/index.ts | Adjust SDK verify() return shape to explicit CombinedResult fields. |
| scripts/tsrepo-private-artifact-audit.sh | New repo audit script for private diligence artifact paths/content markers. |
| packages/core/src/zkp/zkp.test.ts | Import ordering/formatting tweaks. |
| packages/core/src/zkp/index.ts | Formatting tweak (blank line). |
| packages/core/src/verifiers.ts | Remove unused OCRData import. |
| packages/core/src/risk/risk.test.ts | Formatting tweaks (blank lines). |
| packages/core/src/risk/patterns.ts | Formatting tweaks (blank lines). |
| packages/core/src/risk/layout.ts | Formatting tweaks (blank lines). |
| packages/core/src/risk/index.ts | Formatting tweaks (blank lines). |
| packages/core/src/risk/forensics.ts | Formatting tweaks (blank lines). |
| packages/core/src/mocks.ts | Prefix unused parameters with _ to satisfy linting. |
| packages/core/src/headless.test.ts | Remove unused types import / formatting. |
| packages/core/src/attom/normalize.ts | Formatting tweaks (blank line). |
| packages/core/src/attom/crossCheck.ts | Remove unused imports; prefix unused arg in mock client. |
| packages/core/src/attom/crossCheck.test.ts | Formatting tweaks (blank line). |
| package-lock.json | Bump versions and update Next dependency lock entries. |
| docs/verification-lifecycle.md | Add boundary note and link to Trust Agents workflow doc. |
| docs/compliance/trust-agents-workflow.md | New documentation for local Trust Agents + TrustSignal workflow boundaries. |
| apps/web/src/utils/extraction.test.ts | Formatting tweak (remove leading blank line, add spacing). |
| apps/web/src/types/index.ts | Narrow details from any to unknown; formatting. |
| apps/web/src/contexts/OperatorContext.tsx | Formatting tweak (blank line). |
| apps/web/src/components/ui/Select.tsx | Formatting tweak (blank line). |
| apps/web/src/components/FileDropzone.tsx | Remove Promise.withResolvers polyfill; tighten OCR input typing. |
| apps/web/src/app/verify-artifact/page.tsx | New route page wrapper for artifact verification UI. |
| apps/web/src/app/verify-artifact/ArtifactVerifyClient.tsx | New client UI for local artifact fingerprinting + receipt generation + verification. |
| apps/web/package.json | Bump Next dependency version. |
| apps/watcher/src/index.js | Import ordering and regex formatting; eslint directive added. |
| apps/watcher/package.json | Add no-op build script. |
| apps/api/test/rate-limit.test.ts | Minor refactor: const API keys + import ordering. |
| apps/api/src/workflow/types.ts | New zod schemas + TypeScript types for workflow subsystem. |
| apps/api/src/workflow/store.ts | New in-memory workflow store abstraction. |
| apps/api/src/workflow/service.ts | New workflow service with built-in agents + readiness audit orchestration. |
| apps/api/src/workflow/policy.ts | Classification inheritance + release target policy rules. |
| apps/api/src/workflow/events.ts | Event sink seam (noop by default). |
| apps/api/src/workflow/errors.ts | WorkflowError typed codes + helper guard. |
| apps/api/src/workflow.test.ts | New API-level tests for workflow routes + auth/scope behavior. |
| apps/api/src/workflow.service.test.ts | New unit tests for WorkflowService behaviors. |
| apps/api/src/v2-integration.test.ts | Import ordering/formatting. |
| apps/api/src/services/registryAdapters.ts | Adjust details field when endpoint validation fails. |
| apps/api/src/services/compliance.ts | Branding text updates + safer PDFParser error typing. |
| apps/api/src/services/attomClient.ts | Improve ATTOM response typing; refactor mapping to reduce any. |
| apps/api/src/server.ts | Add workflow endpoints; improve DB failure redaction; rename metrics prefix; various refactors. |
| apps/api/src/security-hardening.test.ts | Import ordering/formatting. |
| apps/api/src/registryLoader.ts | Require TRUST_REGISTRY_PUBLIC_KEY in production; allow env override of public key. |
| apps/api/src/registryLoader.test.ts | Strengthen typing and mock handling for fs.readFile. |
| apps/api/src/registry-adapters.test.ts | Ensure Prisma cleanup/disconnect in tests. |
| apps/api/src/receiptPdf.ts | Rename PDF title to “TrustSignal Receipt”. |
| apps/api/src/readiness-workflow.test.ts | New API test coverage for readiness audit workflow behavior. |
| apps/api/src/lib/v2ReceiptMapper.ts | Add stronger typing for v2 verify response mapping. |
| apps/api/src/health-endpoints.test.ts | New test ensuring DB init errors are redacted from responses/logs. |
| .gitignore | Expand ignore rules and add private diligence artifact patterns. |
| .githooks/pre-commit | Block private diligence artifacts and content markers at commit time. |
| .eslintrc.cjs | Add more ignore patterns (bench/demo/action dist, etc.). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| a.click(); | ||
| URL.revokeObjectURL(url); |
There was a problem hiding this comment.
downloadJson revokes the object URL immediately after calling a.click(). In some browsers this can cancel or intermittently break the download. Consider revoking the URL after the click has had a chance to start (e.g., in a setTimeout(..., 0)/requestAnimationFrame, or by awaiting a microtask) and/or removing the anchor node after use.
| a.click(); | |
| URL.revokeObjectURL(url); | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(() => { | |
| URL.revokeObjectURL(url); | |
| a.remove(); | |
| }, 0); |
| if (!file) return; | ||
| try { | ||
| const text = await file.text(); | ||
| const parsed = JSON.parse(text) as ReceiptLike; | ||
| const fp = extractReceiptFingerprint(parsed); | ||
| if (!fp) { | ||
| setReceiptError('That receipt file does not look like a TrustSignal receipt (missing fingerprint).'); | ||
| return; | ||
| } | ||
| setReceipt(parsed); | ||
| setReceiptFingerprint(fp); | ||
| } catch { |
There was a problem hiding this comment.
When a receipt is dropped, the handler doesn’t clear the previous receiptFingerprint/receipt state before parsing the new file. If the new file is invalid, the old fingerprint remains set, so the UI can still show “Receipt loaded” and allow verification against stale data. Clear receiptFingerprint (and receipt) at the start of onDropReceipt and also when parsing fails / fingerprint is missing.
| if (!file) return; | |
| try { | |
| const text = await file.text(); | |
| const parsed = JSON.parse(text) as ReceiptLike; | |
| const fp = extractReceiptFingerprint(parsed); | |
| if (!fp) { | |
| setReceiptError('That receipt file does not look like a TrustSignal receipt (missing fingerprint).'); | |
| return; | |
| } | |
| setReceipt(parsed); | |
| setReceiptFingerprint(fp); | |
| } catch { | |
| if (!file) return; | |
| // Clear any previous receipt state before processing a new file | |
| setReceipt(null); | |
| setReceiptFingerprint(null); | |
| try { | |
| const text = await file.text(); | |
| const parsed = JSON.parse(text) as ReceiptLike; | |
| const fp = extractReceiptFingerprint(parsed); | |
| if (!fp) { | |
| setReceipt(null); | |
| setReceiptFingerprint(null); | |
| setReceiptError('That receipt file does not look like a TrustSignal receipt (missing fingerprint).'); | |
| return; | |
| } | |
| setReceipt(parsed); | |
| setReceiptFingerprint(fp); | |
| } catch { | |
| setReceipt(null); | |
| setReceiptFingerprint(null); |
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| ROOT="${1:-/Users/christopher/Projects/TSREPO}" |
There was a problem hiding this comment.
The default ROOT points to a specific developer machine path (/Users/christopher/...). This will produce confusing results for other users and CI. Consider defaulting to the current working directory (or requiring an explicit argument) and printing a short usage message when the path doesn’t exist.
| echo "- HEAD path matches:" | ||
| if [[ -n "$head_paths" ]]; then | ||
| printf ' - %s\n' $head_paths | ||
| else | ||
| echo " - none" | ||
| fi | ||
|
|
||
| echo "- History path matches:" | ||
| if [[ -n "$history_paths" ]]; then | ||
| printf ' - %s\n' $history_paths | ||
| else |
There was a problem hiding this comment.
printf ' - %s\n' $head_paths (and the similar history_paths line) relies on unquoted variable expansion, so paths will be word-split and break if any matched path contains spaces or glob characters. Prefer printing line-by-line (e.g., printf ' - %s\n' "$head_paths" via a while IFS= read -r loop) to preserve exact paths.
| blocked_globs=( | ||
| "*.pem" | ||
| "*.key" | ||
| "credentials*.json" | ||
| "*token*" | ||
| "*.env" | ||
| ) | ||
|
|
||
| blocked_repo_regex='(^docs/compliance/kpmg-|^docs/evidence/|(^|/)audit-output/|(^|/)kpmg-[^/]+)' | ||
| blocked_content_regex='(/tmp/kpmg-|kpmg-readiness-)' | ||
|
|
There was a problem hiding this comment.
This hook blocks *.env but would still allow staging of .env.* files (e.g. .env.ci, .env.secrets) if someone creates them, and the .gitignore change in this PR also narrows .env.* coverage. Consider expanding the hook checks to also block .env.* patterns (and/or **/.env.*) to reduce the chance of committing secret env variants.
| lat: (record.location?.latitude as number | undefined) || (record.geo?.latitude as number | undefined) || null, | ||
| lon: (record.location?.longitude as number | undefined) || (record.geo?.longitude as number | undefined) || null |
There was a problem hiding this comment.
lat/lon mapping uses || null, which will incorrectly treat valid numeric 0 values as missing (because 0 is falsy). Use nullish coalescing (??) or an explicit typeof === 'number' check so 0 is preserved.
| lat: (record.location?.latitude as number | undefined) || (record.geo?.latitude as number | undefined) || null, | |
| lon: (record.location?.longitude as number | undefined) || (record.geo?.longitude as number | undefined) || null | |
| lat: (record.location?.latitude as number | undefined) ?? (record.geo?.latitude as number | undefined) ?? null, | |
| lon: (record.location?.longitude as number | undefined) ?? (record.geo?.longitude as number | undefined) ?? null |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b5ecec2e76
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const [registryRaw, signatureRaw, publicKeyFileRaw] = await Promise.all([ | ||
| readFile(registryPath, 'utf-8'), | ||
| readFile(signaturePath, 'utf-8'), | ||
| readFile(publicKeyPath, 'utf-8') |
There was a problem hiding this comment.
Avoid reading fallback key file when env key is set
loadRegistry now requires TRUST_REGISTRY_PUBLIC_KEY in production, but this Promise.all still unconditionally reads registry.public.jwk. In an env-only production deployment (the documented mode), a missing file will throw before the env override is used, causing startup failure despite correct secret configuration. Make the public-key file read conditional when no env override is provided.
Useful? React with 👍 / 👎.
| const workflow = this.createWorkflow(request.createdBy); | ||
| const sourceArtifacts = request.sourceArtifacts.map((sourceArtifact) => | ||
| this.createArtifact({ |
There was a problem hiding this comment.
Validate source refs before creating workflow artifacts
This method persists a workflow and source artifacts before it validates findings[].evidenceSourceRefs; if a bad ref is encountered later, it throws unknown_source_ref but leaves partial state in memory. Under repeated invalid requests (especially with large content payloads), these orphan artifacts can accumulate and consume process memory even though the API returns 400. Validate source-ref consistency before persisting, or add rollback on failure.
Useful? React with 👍 / 👎.
| **/.env | ||
| **/.env.* | ||
| **/.env.local | ||
| **/.env.development | ||
| **/.env.development.local | ||
| **/.env.test |
There was a problem hiding this comment.
Restore wildcard ignore for unlisted .env variants
Replacing wildcard env ignores with a fixed allowlist leaves common secret files like .env.qa, .env.preview, or nested */.env.<custom> unignored. Those files are now eligible to be staged, and the pre-commit glob *.env does not catch them either, which weakens the repository’s secret-leak guardrails. Reintroduce **/.env.* (while keeping !.env.example) to preserve broad protection.
Useful? React with 👍 / 👎.
Summary
AI Disclosure (optional)
Review Checklist