feat(projects): render STATUS.md + DECISIONS.md as new tabs#23
feat(projects): render STATUS.md + DECISIONS.md as new tabs#23
Conversation
Adds two new tabs to the project page (Chats / Status / SOW / Decisions / Tasks / Artifacts) that render STATUS.md and DECISIONS.md from the 4-tier doc framework. - src/lib/types/oracle.ts: new `ProjectDoc` type — slimmer shape than Project (no frontmatter, no DoD parsing) since these sibling docs are pure body content. - src/lib/server/oracle-reader.ts: new `readProjectStatus(slug)` and `readProjectDecisions(slug)` exports, both delegating to a shared `readProjectDoc(slug, filename)` helper. Same security checks as `readProject()` (slug whitelist, path containment). Both return null when the file doesn't exist — most projects haven't adopted the 4-tier framework yet, so absence is the common case, not an error. - routes/projects/[slug]/+layout.server.ts: parallel-load project + status + decisions via Promise.all. Status and decisions are nullable. - routes/projects/[slug]/+layout.svelte: tabs reordered as Chats / Status / SOW / Decisions / Tasks / Artifacts. Status appears right after the default Chats tab because per the 4-tier framework it is the read-first doc. Tab bar gets `overflow-x-auto` so 6 tabs scroll cleanly on mobile (375px viewport). Active tab uses `scroll-margin-inline` so it auto-scrolls into view when activated. - routes/projects/[slug]/+page.svelte: new view branches for `status` and `decisions`. Each renders bodyHtml in a prose article with the Edit-in-GitHub link pattern matching the SOW tab. When the file doesn't exist, shows a friendly empty state with a one-tap "Create on GitHub" link that pre-fills the path. No test changes — Playwright suite already covers the layout shell; follow-up PR can add tab-specific assertions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 32 minutes and 29 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds support for reading and displaying per-project STATUS and DECISIONS markdown documents. New Changes
Sequence DiagramsequenceDiagram
participant Browser as Browser
participant LayoutServer as Layout.Server
participant OracleReader as Oracle Reader
participant FileSystem as File System
participant LayoutSvelte as Layout.Svelte
participant PageSvelte as Page.Svelte
Browser->>LayoutServer: Load route with [slug]
LayoutServer->>OracleReader: readProject(slug)
OracleReader->>FileSystem: Read project.md
FileSystem-->>OracleReader: File content
OracleReader-->>LayoutServer: Project data
LayoutServer->>OracleReader: readProjectStatus(slug)
OracleReader->>FileSystem: Read STATUS.md (if exists)
FileSystem-->>OracleReader: File content or null
OracleReader-->>LayoutServer: ProjectDoc | null
LayoutServer->>OracleReader: readProjectDecisions(slug)
OracleReader->>FileSystem: Read DECISIONS.md (if exists)
FileSystem-->>OracleReader: File content or null
OracleReader-->>LayoutServer: ProjectDoc | null
LayoutServer-->>Browser: Return { project, status, decisions }
Browser->>LayoutSvelte: Render with data
LayoutSvelte->>PageSvelte: Pass status & decisions via data prop
Browser->>PageSvelte: User selects status or decisions tab (?view=)
PageSvelte->>PageSvelte: Conditionally render content or empty state
PageSvelte-->>Browser: Display HTML or GitHub creation CTA
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/lib/server/oracle-reader.ts (1)
143-170: LGTM — security parity withreadProject, clean factoring.
readProjectDoccorrectly mirrorsreadProject's slug whitelist and path-containment defense, and the two thin wrappers keep the public API explicit. Body-only handling (no frontmatter/DoD) is the right call for STATUS.md / DECISIONS.md.Optional observability nit: the bare
catchcollapses ENOENT (expected, common case) with permission/render errors (unexpected), which can mask real misconfiguration in production. The existingreadProjecthas the same shape, so feel free to defer — but if you want to differentiate later, branching onerr.code === 'ENOENT'andconsole.warn-ing other failures is a low-cost upgrade.Also: CI flagged a Prettier warning on this file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/server/oracle-reader.ts` around lines 143 - 170, The bare catch in readProjectDoc masks ENOENT (expected) and other unexpected errors; update readProjectDoc to catch the error into a variable, check err.code === 'ENOENT' and return null for that case, but for other errors log a warning (e.g., console.warn or processLogger.warn) including the error and filePath before returning null; also run Prettier/format the file to resolve the CI formatting warning. Ensure the change is applied in the readProjectDoc function and keep the readProjectStatus and readProjectDecisions wrappers unchanged.src/routes/projects/[slug]/+layout.svelte (1)
17-28: Optional: tightenactiveTabmapping.The IIFE-of-ifs works but a small whitelist read directly off the param keeps tab state and tab list in sync. Purely a clarity nit — feel free to skip.
♻️ Suggested simplification
- const activeTab = $derived( - $page.url.pathname.endsWith('/tasks') - ? 'tasks' - : ((): string => { - const view = $page.url.searchParams.get('view'); - if (view === 'status') return 'status'; - if (view === 'decisions') return 'decisions'; - if (view === 'sow') return 'sow'; - if (view === 'artifacts') return 'artifacts'; - return 'chats'; - })() - ); + const VIEW_TABS = new Set(['status', 'decisions', 'sow', 'artifacts']); + const activeTab = $derived.by(() => { + if ($page.url.pathname.endsWith('/tasks')) return 'tasks'; + const view = $page.url.searchParams.get('view') ?? ''; + return VIEW_TABS.has(view) ? view : 'chats'; + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/projects/`[slug]/+layout.svelte around lines 17 - 28, Replace the IIFE-of-ifs used to compute activeTab with a simple whitelist lookup: read the view via $page.url.searchParams.get('view'), check it against an allowed set/array of tabs (e.g., ['status','decisions','sow','artifacts','chats']), and return the view if present or 'chats' as the default; keep the existing pathname check for '/tasks' (the activeTab derived store and $page usage remain unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/routes/projects/`[slug]/+page.svelte:
- Around line 22-30: Update the misleading comment to reflect that the code
intentionally uses GitHub's /new/ endpoint (not /edit/) for deterministic
new-file creation; reference the constants createStatusUrl and
createDecisionsUrl and change the prose to state that the
/new/<branch>/<dir>?filename=... form opens the new-file dialog with the
filename prefilled, so replace the mention of /edit/ with /new/ and clarify that
this is why /new/ is used here.
---
Nitpick comments:
In `@src/lib/server/oracle-reader.ts`:
- Around line 143-170: The bare catch in readProjectDoc masks ENOENT (expected)
and other unexpected errors; update readProjectDoc to catch the error into a
variable, check err.code === 'ENOENT' and return null for that case, but for
other errors log a warning (e.g., console.warn or processLogger.warn) including
the error and filePath before returning null; also run Prettier/format the file
to resolve the CI formatting warning. Ensure the change is applied in the
readProjectDoc function and keep the readProjectStatus and readProjectDecisions
wrappers unchanged.
In `@src/routes/projects/`[slug]/+layout.svelte:
- Around line 17-28: Replace the IIFE-of-ifs used to compute activeTab with a
simple whitelist lookup: read the view via $page.url.searchParams.get('view'),
check it against an allowed set/array of tabs (e.g.,
['status','decisions','sow','artifacts','chats']), and return the view if
present or 'chats' as the default; keep the existing pathname check for '/tasks'
(the activeTab derived store and $page usage remain unchanged).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ff068893-b2a5-43d2-b870-33f7d31c416a
📒 Files selected for processing (5)
src/lib/server/oracle-reader.tssrc/lib/types/oracle.tssrc/routes/projects/[slug]/+layout.server.tssrc/routes/projects/[slug]/+layout.sveltesrc/routes/projects/[slug]/+page.svelte
| // GitHub create-new-file URLs for empty-state CTAs. The same /edit/ path | ||
| // works whether the file exists or not — GitHub creates a new file when | ||
| // the path doesn't resolve. | ||
| const createStatusUrl = $derived( | ||
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=STATUS.md` | ||
| ); | ||
| const createDecisionsUrl = $derived( | ||
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=DECISIONS.md` | ||
| ); |
There was a problem hiding this comment.
Comment contradicts the actual URL — it says /edit/ but the code uses /new/.
The comment block states the same /edit/ path works for missing files, yet createStatusUrl / createDecisionsUrl use GitHub's dedicated /new/<branch>/<dir>?filename=... endpoint. The /new/ form is the right choice (it deterministically opens the new-file dialog with the filename prefilled), so just update the comment to match — the prose is otherwise misleading for future readers.
📝 Suggested comment fix
- // GitHub create-new-file URLs for empty-state CTAs. The same /edit/ path
- // works whether the file exists or not — GitHub creates a new file when
- // the path doesn't resolve.
+ // GitHub create-new-file URLs for empty-state CTAs. Uses the /new/<branch>/
+ // <dir>?filename=... endpoint, which opens GitHub's new-file editor with
+ // the directory + filename prefilled.📝 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.
| // GitHub create-new-file URLs for empty-state CTAs. The same /edit/ path | |
| // works whether the file exists or not — GitHub creates a new file when | |
| // the path doesn't resolve. | |
| const createStatusUrl = $derived( | |
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=STATUS.md` | |
| ); | |
| const createDecisionsUrl = $derived( | |
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=DECISIONS.md` | |
| ); | |
| // GitHub create-new-file URLs for empty-state CTAs. Uses the /new/<branch>/ | |
| // <dir>?filename=... endpoint, which opens GitHub's new-file editor with | |
| // the directory + filename prefilled. | |
| const createStatusUrl = $derived( | |
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=STATUS.md` | |
| ); | |
| const createDecisionsUrl = $derived( | |
| `https://github.com/Nkburdick/ORACLE/new/main/Projects/${fm.slug}?filename=DECISIONS.md` | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/routes/projects/`[slug]/+page.svelte around lines 22 - 30, Update the
misleading comment to reflect that the code intentionally uses GitHub's /new/
endpoint (not /edit/) for deterministic new-file creation; reference the
constants createStatusUrl and createDecisionsUrl and change the prose to state
that the /new/<branch>/<dir>?filename=... form opens the new-file dialog with
the filename prefilled, so replace the mention of /edit/ with /new/ and clarify
that this is why /new/ is used here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds two new tabs to each project page that render
STATUS.mdandDECISIONS.mdfrom the 4-tier doc framework. Tab order is nowChats / Status / SOW / Decisions / Tasks / Artifacts— Status sits right after the default Chats because the framework treats it as the read-first doc.Most projects don't have these files yet, so the tabs always render but show a friendly empty state with a one-tap "Create on GitHub" link when the file is missing. As of today's sweep, three projects have full content:
psi-odoo-v8-to-v19-upgrade,pai-orchestration,stridemind-ai.What changed
src/lib/types/oracle.ts— newProjectDoctype (slimmer thanProject— no frontmatter or DoD parsing, just rendered body).src/lib/server/oracle-reader.ts— newreadProjectStatus(slug)andreadProjectDecisions(slug)exports, both delegating to a sharedreadProjectDoc(slug, filename)helper. Same security posture asreadProject()(slug whitelist + path-inside-base check). Both returnnullwhen the file doesn't exist.routes/projects/[slug]/+layout.server.ts— parallel-loads project + status + decisions viaPromise.all. Status and decisions are nullable.routes/projects/[slug]/+layout.svelte— tabs reordered, plusoverflow-x-autoon the tab bar so 6 tabs scroll cleanly on mobile (~375px viewport). Active tab usesscroll-margin-inlineso it auto-scrolls into view.routes/projects/[slug]/+page.svelte— new view branches for?view=statusand?view=decisions. Each rendersbodyHtmlin aprosearticle matching the SOW pattern, with the Edit-in-GitHub link. Empty state shows when the file is missing.Test plan
bun run check— 0 errors (3 pre-existing warnings)bun run build— succeedsbun run test:unit— 80/80 pass/projects/pai-orchestration?view=statusshows the live STATUS.md (Right Now, Next 3 Actions, Active Sessions, etc.). Verified via QATester + screenshot./projects/pai-orchestration?view=decisionsshows the rendered DECISIONS.md with date headers + Decision/Rationale/What changed entries. Verified via QATester + screenshot./projects/oracle?view=statusshows the "No STATUS.md yet" friendly state with the copper "Create on GitHub" link. Verified via QATester + screenshot.Visual check
Screenshots at
/tmp/oracle-qa-20260425/s{1,2,3,4}*.png(local artifacts). Worth eyeballing before merge — the empty-state is the bit most likely to need polish.Notes
ORACLE_DATA_PATHcontinues to drive disk reads — no schema changes, no DB migrations.main.🤖 Generated with Claude Code
Summary by CodeRabbit