Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .codex/review-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,21 @@ When both exist, report blockers first.

Do three passes:

1. **Context + risk-map pass (mandatory)** — For each file that appears in the diff, read the full file (not only hunk lines) to understand surrounding context. Do NOT read files or packages that have no changed lines in the diff. Identify high-risk zones in the changed code: platform/runtime boundaries, event handlers, async cleanup (`finally`), auth/validation boundaries, and repeated predicates/guards.
1. **Context + risk-map pass (mandatory)** — Start from diff hunks, then read surrounding or full touched files when needed to evaluate maintainability, coupling, naming, and extraction opportunities. Use this context to assess changed behavior, not to run unrelated file-wide audits.
2. **Blockers pass** — Scan for correctness bugs, security issues, API/schema contract breaks, missing migrations, data integrity risks, and missing tests for changed behavior. These are `🔴 Bug` comments.
3. **Maintainability pass** — Scan for code bloat, readability issues, naming problems, pattern violations, hardcoded values, and architecture drift in touched areas. These are `🟡 Issue`, `🔵 Nit`, or `💡 Suggestion` comments.

### Comment Gate

Before posting any comment, verify all three conditions:
Before posting any comment, verify all four conditions:

1. **Introduced by this diff** — The issue is introduced or materially worsened by the changes in this PR, not pre-existing.
2. **Materially impactful** — The issue affects correctness, security, readability, or maintainability in a meaningful way. Not a theoretical concern.
3. **Concrete fix direction** — You can suggest a specific fix or clear direction. If you can only say "this seems off" without a concrete suggestion, do not comment.
4. **Scope fit** — If the issue is mainly in pre-existing code, the PR must touch the same function/module and fixing it must directly simplify, de-risk, or de-duplicate the new/changed code.

If any check fails, skip the comment.
Every comment must be traceable to changed behavior in this PR and anchored to a right-side line present in `pr-diff.patch`. Prefer added/modified lines; use nearby unchanged hunk lines only when necessary to explain a directly related issue.

**Uncertainty guard:** If you are not certain an issue is real and cannot verify it from the diff and allowed context, do not label it `🔴 Bug`. Downgrade to `🟡 Issue` or `💡 Suggestion`, or skip it entirely.

Expand Down Expand Up @@ -141,8 +143,9 @@ Do not flag one-off numeric literals that are self-explanatory in context (e.g.,
- Type annotations for code that already type-checks.
- Things that are clearly intentional design choices backed by existing patterns.
- Pre-existing issues in unchanged code outside the diff.
- Code in files or packages that have no changed lines in the diff — even if you read them for context.
- Pre-existing issues in touched files when the PR does not introduce/worsen them.
- Adding documentation unless a public API is clearly undocumented.
- Repository-wide or file-wide audits not required by the changed behavior.

## Comment Format

Expand Down
35 changes: 18 additions & 17 deletions .github/workflows/codex-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ jobs:
}
);

// Build set of valid (path:line) pairs from diff patches
// Build set of valid (path:line) pairs from right-side diff hunk lines
// (added + context). This keeps comments bound to changed areas.
const validLines = new Set();
for (const file of files) {
// Skip binary/large/truncated files with no patch
Expand All @@ -89,20 +90,26 @@ jobs:
currentLine = parseInt(hunkMatch[1], 10);
continue;
}
// Deleted lines don't exist in the new file
if (line.startsWith('-')) continue;
// Context lines and added lines are valid targets
if (!line.startsWith('\\')) {
// Added lines are valid comment targets
if (line.startsWith('+')) {
validLines.add(`${file.filename}:${currentLine}`);
currentLine++;
continue;
}
// Deleted lines don't exist in the new file
if (line.startsWith('-')) continue;
// Ignore hunk metadata lines
if (line.startsWith('\\')) continue;
// Context lines on the right side are also valid targets
validLines.add(`${file.filename}:${currentLine}`);
currentLine++;
}
}

// Partition comments into valid (on diff lines) and overflow
// Partition comments into valid (on right-side diff lines) and dropped
const comments = Array.isArray(review.comments) ? review.comments : [];
const validComments = [];
const overflowComments = [];
const droppedComments = [];

for (const comment of comments) {
const key = `${comment.path}:${comment.line}`;
Expand All @@ -114,19 +121,13 @@ jobs:
side: 'RIGHT',
});
} else {
overflowComments.push(comment);
droppedComments.push(comment);
}
}

// Build review body: summary + any overflow comments
// Build review body from summary only.
// Intentionally do NOT publish out-of-diff comments.
let body = review.summary || 'Codex review complete.';
if (overflowComments.length > 0) {
body += '\n\n### Additional comments\n';
body += '_These comments target lines outside the diff and could not be posted inline._\n\n';
for (const c of overflowComments) {
body += `- **\`${c.path}:${c.line}\`** — ${c.body}\n`;
}
}

// Post the review
await github.rest.pulls.createReview({
Expand All @@ -138,4 +139,4 @@ jobs:
comments: validComments,
});

console.log(`Review posted: ${validComments.length} inline comments, ${overflowComments.length} overflow comments`);
console.log(`Review posted: ${validComments.length} inline comments, ${droppedComments.length} dropped out-of-diff comments`);
2 changes: 1 addition & 1 deletion apps/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"better-sqlite3": "^12.2.0",
"dkg.js": "^8.2.2",
"dotenv": "^16.3.1",
"mysql2": "^3.6.5",
"drizzle-orm": "^0.44.7",
"expo": "53.0.20",
"expo-blur": "~14.1.5",
Expand All @@ -77,6 +76,7 @@
"expo-three": "^8.0.0",
"expo-web-browser": "~14.2.0",
"js-sha256": "^0.11.1",
"mysql2": "^3.6.5",
"nodemailer": "^7.0.6",
"prompts": "^2.4.2",
"react": "19.0.0",
Expand Down
15 changes: 14 additions & 1 deletion apps/agent/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ const version = "1.0.0";
const { oauthPlugin, openapiSecurityScheme } = createOAuthPlugin({
storage: new SqliteOAuthStorageProvider(db),
issuerUrl: new URL(process.env.EXPO_PUBLIC_MCP_URL),
scopesSupported: ["mcp", "llm", "scope123", "blob"],
scopesSupported: [
"mcp",
"llm",
"scope123",
"blob",
"epcis.read",
"epcis.write",
],
loginPageUrl: new URL(process.env.EXPO_PUBLIC_APP_URL + "/login"),
schema: userCredentialsSchema,
async login(credentials) {
Expand Down Expand Up @@ -101,6 +108,12 @@ const app = createPluginServer({
oauthPlugin,
(_, __, api) => {
api.use("/mcp", authorized(["mcp"]));
api.use("/mcp", (req, res, next) => {
if (res.locals.auth) {
(req as any).auth = res.locals.auth;
}
next();
});
api.use("/llm", authorized(["llm"]));
api.use("/blob", authorized(["blob"]));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: dkgEssentialsPlugin now exposes /document-to-markdown, but this auth block does not guard that route, so unauthenticated clients can trigger file conversion and blob writes. Add an api.use('/document-to-markdown', authorized([...])) guard (or protect it via namespace middleware).

api.use("/change-password", authorized([]));
Expand Down
19 changes: 18 additions & 1 deletion apps/agent/tests/integration/setup/test-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createPluginServer, defaultPlugin } from "@dkg/plugins";
import { authorized, createOAuthPlugin } from "@dkg/plugin-oauth";
import dkgEssentialsPlugin from "@dkg/plugin-dkg-essentials";
import createFsBlobStorage from "@dkg/plugin-dkg-essentials/createFsBlobStorage";
import epcisPlugin, { applyEpcisHttpScopeGuards } from "@dkg/plugin-epcis";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Issue: This new import relies on @dkg/plugin-epcis, but apps/agent/package.json does not declare that dependency. Add it (likely as a dev dependency) so test resolution is not dependent on workspace hoisting behavior.

import {
createInMemoryBlobStorage,
createMockDkgClient,
Expand Down Expand Up @@ -84,7 +85,14 @@ export async function createTestServer(config: TestServerConfig = {}): Promise<{
const { oauthPlugin, openapiSecurityScheme } = createOAuthPlugin({
storage: testDatabase.oauthStorage,
issuerUrl: new URL(oauthUrls.issuerUrl),
scopesSupported: ["mcp", "llm", "scope123", "blob"],
scopesSupported: [
"mcp",
"llm",
"scope123",
"blob",
"epcis.read",
"epcis.write",
],
loginPageUrl: new URL(oauthUrls.loginPageUrl),
schema: userCredentialsSchema,
async login(credentials: { email: string; password: string }) {
Expand Down Expand Up @@ -149,7 +157,14 @@ export async function createTestServer(config: TestServerConfig = {}): Promise<{
oauthPlugin,
// Same authorization middleware as real app
(_, __, api) => {
applyEpcisHttpScopeGuards(api, authorized);
api.use("/mcp", authorized(["mcp"]));
api.use("/mcp", (req, res, next) => {
if (res.locals.auth) {
(req as any).auth = res.locals.auth;
}
next();
});
api.use("/llm", authorized(["llm"]));
api.use("/blob", authorized(["blob"]));
},
Expand All @@ -161,6 +176,7 @@ export async function createTestServer(config: TestServerConfig = {}): Promise<{
});
},
dkgEssentialsPlugin,
epcisPlugin,
// DKG Publisher Plugin for API contract testing
mockDkgPublisherPlugin, // Mock version - tests interfaces without database
// Test the namespace functionality
Expand Down Expand Up @@ -232,6 +248,7 @@ export async function startTestServer(config: TestServerConfig = {}): Promise<{

const actualPort = (server.address() as any)?.port || port;
const url = `http://localhost:${actualPort}`;
process.env.EXPO_PUBLIC_MCP_URL = url;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Issue: startTestServer mutates process.env.EXPO_PUBLIC_MCP_URL and never restores the previous value, which introduces cross-test global state coupling. Capture the prior env value and reset it in the returned cleanup().


console.log(`Test server running at ${url}`);

Expand Down
Loading
Loading