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
2 changes: 1 addition & 1 deletion HANDOFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ git hooks (simple-git-hooks): pre-commit → lint-staged ; pre-push → typeche
**세션 5 (2026-05-12) — Devvit Web 공식 문서 정독 + 정합화 (PR `feat/devvit-web-conformance`)**:

- 📚 `developers.reddit.com`은 WebFetch 차단 → **Playwright로 비-게임 문서 58페이지 크롤** → `docs/devvit-reference.md`(440KB 스냅샷)에 저장. 발견사항/수정내역은 `docs/devvit-conformance-notes.md`.
- ✅ **`devvit build`가 실패했을 버그 수정**: `devvit.json`에 필수 `server` 블록 없음 + CJS 서버 번들 빌드 없음(`tsconfig`는 `noEmit`+ESM). → `vite.config.ts`(SSR 빌드, `format:'cjs'` → `dist/server/index.cjs`, `noExternal:true`, minify) + `devvit.json`에 `"server":{"entry":"dist/server/index.cjs"}` + `"scripts":{"dev":"vite build --watch","build":"vite build"}` + `"dev":{"subreddit":"vibe-mod-playtest"}` 추가. `vite` devDep 추가. `npm run build` = `tsc --noEmit && vite build`. `dist/`는 gitignore.
- ✅ **`devvit build`가 실패했을 버그 수정**: `devvit.json`에 필수 `server` 블록 없음 + CJS 서버 번들 빌드 없음(`tsconfig`는 `noEmit`+ESM). → `vite.config.ts`(SSR 빌드, `format:'cjs'` → `dist/server/index.cjs`, `noExternal:true`, minify) + `devvit.json`에 `"server":{"dir":"dist/server","entry":"index.cjs"}` + `"scripts":{"dev":"vite build --watch","build":"vite build"}` + `"dev":{"subreddit":"vibemod_playtest"}` 추가. `vite` devDep 추가. `npm run build` = `tsc --noEmit && vite build`. `dist/`는 gitignore.
- ✅ **deps 정리**: Devvit Web은 `@devvit/web` 하나 + submodule import만 사용 → `@devvit/reddit`/`@devvit/redis`를 `dependencies`에서 제거(transitive로 남음), `import {redis} from '@devvit/redis'` → `'@devvit/web/server'`. `TaskBody/TaskAck` 로컬 타입 → 진짜 `TaskRequest/TaskResponse`(`@devvit/web/server` 재익스포트). CLI devDep: `@devvit/cli` → `devvit`.
- ✅ **`context` 사용**: 서브레딧 이름은 `reddit.getCurrentSubreddit()`(API 호출) 대신 `context.subredditName`/`subredditId`(요청 컨텍스트, 호출 0). `devvit-helpers.ts`가 `context.*` 기반·동기. 호출부 `await`/`.catch` 제거. (테스트에 `fakeContext` 더블 추가.)
- ✅ **publish 순서 정정**: `devvit settings set`은 "최소 1개 설치" 후에만 → `npm run dev`/`devvit upload`를 *먼저*. (이전 HANDOFF Step 2가 순서 틀림 — 위 Step 1.5/2/3에서 정정.)
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
> and keeps **30-day rollback** on every action it ever takes. The LLM is used **only at rule-edit
> time** — zero AI calls per post or comment, and the LLM never sees Reddit content.

<!-- TODO before submission: add 3+ screenshots of the live app (compose form, dry-run preview, dashboard/audit log). -->
<!-- ![Compose a rule](docs/img/compose.png) -->
<!-- ![Dry-run preview](docs/img/dry-run.png) -->
<!-- ![Rules + audit log](docs/img/dashboard.png) -->
_Screenshots (compose form · dry-run preview · audit log) are added once the app is running in a test community — see [`docs/devvit-setup-guide.md`](./docs/devvit-setup-guide.md)._

---

Expand Down
8 changes: 3 additions & 5 deletions devvit.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"$schema": "https://developers.reddit.com/schema/config-file.v1.json",
"name": "vibe-mod",
"version": "0.1.0",
"server": { "entry": "dist/server/index.cjs" },
"server": { "dir": "dist/server", "entry": "index.cjs" },
"marketingAssets": { "icon": "assets/icon.png" },
"permissions": {
"reddit": { "enable": true, "scope": "moderator" },
Expand All @@ -15,8 +14,7 @@
"type": "string",
"label": "Developer OpenAI API Key (sk-…)",
"isSecret": true,
"defaultValue": "",
"helpText": "Devvit constraint: secrets must be global. This key is shared across all subreddit installs of vibe-mod (per-install BYOK override available via subredditOpenaiApiKey below). Daily quota per sub is enforced to bound cost."
"helpText": "Devvit constraint: secrets must be global. This key is shared across all subreddit installs of vibe-mod (per-install BYOK override available via subredditOpenaiApiKey below). Daily quota per sub is enforced to bound cost. Set it after the first upload: `npx devvit settings set openaiApiKey`."
},
"openaiModel": {
"type": "select",
Expand Down Expand Up @@ -125,6 +123,6 @@
"build": "vite build"
},
"dev": {
"subreddit": "vibe-mod-playtest"
"subreddit": "vibemod_playtest"
}
}
2 changes: 1 addition & 1 deletion docs/devvit-conformance-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fixed. Anything still open is at the bottom.
- **`devvit.json` was missing the required `server` block.** A Devvit Web app must declare `post` and/or `server`; vibe-mod is server-only → it needs `"server": { "entry": "<cjs bundle path>" }`. Added `"server": { "entry": "dist/server/index.cjs" }`.
- **No build step produced a CommonJS server bundle.** `tsconfig` had `noEmit: true` and ESM output; the Devvit Web runtime requires **CJS** server output. Added `vite.config.ts` (SSR build, `format: 'cjs'`, `entryFileNames: 'index.cjs'`, `target: 'node22'`, `ssr.noExternal: true` so `@devvit/web`/`hono`/`zod` are bundled in, only Node builtins external, `minify: 'esbuild'` → ~2.1 MB). Added `vite` as a devDep.
- **`devvit.json` had no `scripts`.** `devvit playtest` runs `scripts.dev`, `devvit upload` runs `scripts.build`. Added `"scripts": { "dev": "vite build --watch", "build": "vite build" }`. `package.json`'s `build` is now `tsc --noEmit && vite build`.
- Added `"dev": { "subreddit": "vibe-mod-playtest" }` so `devvit playtest` doesn't need a generated sub (overridable via `DEVVIT_SUBREDDIT`).
- Added `"dev": { "subreddit": "vibemod_playtest" }` so `devvit playtest` doesn't need a generated sub (overridable via `DEVVIT_SUBREDDIT`).
- `dist/` is git-ignored (build artifact).

### 2. Dependency / import hygiene (the canonical Devvit Web shape)
Expand Down
6 changes: 3 additions & 3 deletions docs/devvit-setup-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,12 @@ npm run dev # = `devvit playtest` → Ctrl+C to end

What `devvit playtest` does: installs the app on your test subreddit, re-installs a new version every
time you save a code change, and streams logs to the terminal. It picks the subreddit from, in order:
`DEVVIT_SUBREDDIT` env var → `dev.subreddit` in `devvit.json` (currently `"vibe-mod-playtest"`) → the
`DEVVIT_SUBREDDIT` env var → `dev.subreddit` in `devvit.json` (currently `"vibemod_playtest"`) → the
playtest sub stored for your app → otherwise it auto-creates one.

⚠️ `devvit.json` currently has `"dev": { "subreddit": "vibe-mod-playtest" }`. If you don't actually
⚠️ `devvit.json` currently has `"dev": { "subreddit": "vibemod_playtest" }`. If you don't actually
moderate a sub by that name, either:
- create `r/vibe-mod-playtest` (private, you as mod, keep < 200 subscribers), **or**
- create `r/vibemod_playtest` (private, you as mod, keep < 200 subscribers), **or**
- change that value to your test sub's name, **or**
- delete the `dev.subreddit` field and let `devvit upload`/`playtest` auto-create one (recommended if
you don't care about the name).
Expand Down
24 changes: 14 additions & 10 deletions scripts/devvit-doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
// HARD checks (exit 1 on failure): devvit.json is well-formed; every external
// host the server code fetch()es is declared under permissions.http.domains;
// node satisfies package.json engines.
// SOFT checks (warn only): Devvit CLI logged in; .devvit-app-id present; the
// other tooling files exist. Soft because they depend on the human having done
// the wizard / `devvit login`.
// SOFT checks (warn only): Devvit CLI logged in; the other tooling files exist.
// Soft because they depend on the human having run `devvit login`.

import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
Expand All @@ -36,7 +35,7 @@ console.log('devvit.json');
let devvit: {
$schema?: string;
name?: string;
version?: string;
server?: { dir?: string; entry?: string };
permissions?: { http?: { enable?: boolean; domains?: string[] }; reddit?: unknown; redis?: unknown };
menu?: { items?: Array<{ endpoint?: string }> };
forms?: Record<string, string>;
Expand All @@ -54,8 +53,17 @@ if (devvit) {
else warn('$schema not set — add "https://developers.reddit.com/schema/config-file.v1.json" for editor validation');
if (devvit.name) ok(`name: ${devvit.name}`);
else fail('name missing');
if (devvit.version) ok(`version: ${devvit.version}`);
else fail('version missing');
// NB: the official devvit.json schema does NOT allow a top-level `version`
// (versioning is managed by `devvit upload --bump` / package.json) — having
// one makes `devvit upload` fail validation, so flag it.
if ((devvit as Record<string, unknown>).version !== undefined)
fail(
'remove the top-level "version" field from devvit.json — not allowed by the schema (managed by `devvit upload`)',
);
if (devvit.server?.entry && devvit.server.entry.includes('/'))
fail(
`server.entry must be a bare filename within server.dir (default dist/server), not a path — got "${devvit.server.entry}"`,
);
Comment on lines +63 to +66
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 💤 Low value

🧩 Analysis chain

🌐 Web query:

What is the default value for server.dir in Devvit config schema when not specified?

💡 Result:

There is no server.dir property in the Devvit config schema (devvit.json). The server object only has an entry property, which specifies the server bundle filename and has a default value of "src/server/index.js" [1][2][3]. The dir property is used in the post object for web view configuration (e.g., "public" or "dist/client"), but not under server [1][4].

Citations:


🏁 Script executed:

cat -n scripts/devvit-doctor.ts | sed -n '60,70p'

Repository: Two-Weeks-Team/vibe-mod

Length of output: 596


에러 메시지의 부정확한 정보 수정 필요

에러 메시지가 존재하지 않는 server.dir 속성을 언급하고 있습니다. Devvit 공식 문서에 따르면 server 객체에는 dir 속성이 없으며, server.entry의 기본값은 dist/server가 아니라 src/server/index.js입니다. "within server.dir (default dist/server)" 부분을 삭제하고 더 정확한 메시지로 수정하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/devvit-doctor.ts` around lines 63 - 66, The error message referencing
a non-existent server.dir and wrong default should be corrected: update the
check that uses devvit.server?.entry and the fail(...) call so the message no
longer mentions "server.dir (default dist/server)" and instead states the
correct default location (src/server/index.js) or simply asks for a bare
filename; ensure the fail call for devvit.server.entry includes the actual value
via "${devvit.server.entry}" and references devvit.server.entry in the message.

if (devvit.permissions) ok('permissions block present');
else fail('permissions block missing');
}
Expand Down Expand Up @@ -146,10 +154,6 @@ try {
} catch {
warn('not logged in — run `npx devvit login` before `npm run dev` / `upload` / `publish`');
}
if (exists('.devvit-app-id')) ok('.devvit-app-id present');
else
warn('.devvit-app-id not found — run the Devvit "Mod Tool" wizard at https://developers.reddit.com/new (creates it)');

// ── tooling files (soft) ──────────────────────────────────────────────────────
console.log('\nTooling');
for (const f of [
Expand Down
7 changes: 5 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// vite.config.ts — builds the Devvit Web *server* bundle.
// vibe-mod is server-only (no webview/post), so there's just one build target:
// the Node server endpoints in src/server/index.ts, compiled to a single
// CommonJS file at dist/server/index.cjs (the path `devvit.json`'s `server.entry`
// points at). The Devvit Web runtime requires CJS — ESM server output is not
// CommonJS file at dist/server/index.cjs. devvit.json declares this as
// `server: { dir: "dist/server", entry: "index.cjs" }` — `entry` is the filename
// *within* `dir`, not a path from the project root, so it must stay in sync with
// `outDir` + `entryFileNames` below. The Devvit Web runtime requires CJS — ESM
// server output is not
Comment on lines +4 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Line 8의 불완전한 문장

주석 Line 8이 "The Devvit Web runtime requires CJS — ESM server output is not"로 끝나면서 문장이 중간에 끊긴 것처럼 보입니다. "is not supported" 같은 식으로 완성되어야 할 것 같습니다.

📝 제안된 수정
-// server output is not
+// server output is not supported.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// CommonJS file at dist/server/index.cjs. devvit.json declares this as
// `server: { dir: "dist/server", entry: "index.cjs" }` — `entry` is the filename
// *within* `dir`, not a path from the project root, so it must stay in sync with
// `outDir` + `entryFileNames` below. The Devvit Web runtime requires CJS — ESM
// server output is not
// CommonJS file at dist/server/index.cjs. devvit.json declares this as
// `server: { dir: "dist/server", entry: "index.cjs" }` — `entry` is the filename
// *within* `dir`, not a path from the project root, so it must stay in sync with
// `outDir` + `entryFileNames` below. The Devvit Web runtime requires CJS — ESM
// server output is not supported.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vite.config.ts` around lines 4 - 8, The comment ending with "The Devvit Web
runtime requires CJS — ESM server output is not" is truncated; update that
comment in vite.config.ts (the block referencing "dist/server/index.cjs",
devvit.json's server.entry, outDir and entryFileNames) to complete the sentence
(e.g., "is not supported") or rephrase to a full sentence clarifying that the
Devvit Web runtime requires CommonJS and does not support ESM server output.

// supported. Run via `vite build` (= devvit.json `scripts.build`, used by
// `devvit upload`) or `vite build --watch` (= `scripts.dev`, used by `devvit playtest`).
//
Expand Down