Skip to content

feat(projects): render STATUS.md + DECISIONS.md as new tabs#23

Merged
Nkburdick merged 2 commits intomainfrom
feat/project-status-decisions-tabs
Apr 25, 2026
Merged

feat(projects): render STATUS.md + DECISIONS.md as new tabs#23
Nkburdick merged 2 commits intomainfrom
feat/project-status-decisions-tabs

Conversation

@Nkburdick
Copy link
Copy Markdown
Owner

@Nkburdick Nkburdick commented Apr 25, 2026

Summary

Adds two new tabs to each project page that render STATUS.md and DECISIONS.md from the 4-tier doc framework. Tab order is now Chats / 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 — new ProjectDoc type (slimmer than Project — no frontmatter or DoD parsing, just rendered body).
  • src/lib/server/oracle-reader.ts — new readProjectStatus(slug) and readProjectDecisions(slug) exports, both delegating to a shared readProjectDoc(slug, filename) helper. Same security posture as readProject() (slug whitelist + path-inside-base check). Both return null when the file doesn't exist.
  • routes/projects/[slug]/+layout.server.ts — parallel-loads project + status + decisions via Promise.all. Status and decisions are nullable.
  • routes/projects/[slug]/+layout.svelte — tabs reordered, plus overflow-x-auto on the tab bar so 6 tabs scroll cleanly on mobile (~375px viewport). Active tab uses scroll-margin-inline so it auto-scrolls into view.
  • routes/projects/[slug]/+page.svelte — new view branches for ?view=status and ?view=decisions. Each renders bodyHtml in a prose article 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 — succeeds
  • bun run test:unit — 80/80 pass
  • Status tab renders content/projects/pai-orchestration?view=status shows the live STATUS.md (Right Now, Next 3 Actions, Active Sessions, etc.). Verified via QATester + screenshot.
  • Decisions tab renders content/projects/pai-orchestration?view=decisions shows the rendered DECISIONS.md with date headers + Decision/Rationale/What changed entries. Verified via QATester + screenshot.
  • Empty state for missing file/projects/oracle?view=status shows the "No STATUS.md yet" friendly state with the copper "Create on GitHub" link. Verified via QATester + screenshot.
  • Mobile tab overflow scrolls horizontally — at 375px viewport, all 6 tabs render and the bar scrolls horizontally; active tab is not clipped. 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

  • No test additions in this PR — existing Playwright suite still covers the layout shell. A follow-up PR can add tab-specific assertions if we want regression coverage for the new views.
  • ORACLE_DATA_PATH continues to drive disk reads — no schema changes, no DB migrations.
  • Pre-existing service-worker registration warning + Pennyworth-not-configured chat banner are unrelated and already on main.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Status and Decisions tabs to project pages for viewing and managing project documentation
    • Implemented empty states with GitHub integration for creating missing Status or Decisions documents
    • Enhanced tab navigation with horizontal scrolling support for improved mobile experience

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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@Nkburdick has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 29 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e71597e6-aef6-4b86-91a8-b661febb41fe

📥 Commits

Reviewing files that changed from the base of the PR and between 7029f63 and a0c58a5.

📒 Files selected for processing (3)
  • src/lib/server/oracle-reader.ts
  • src/routes/projects/[slug]/+layout.server.ts
  • src/routes/projects/[slug]/+page.svelte
📝 Walkthrough

Walkthrough

Adds support for reading and displaying per-project STATUS and DECISIONS markdown documents. New ProjectDoc type and oracle-reader functions enable safe file reading and HTML rendering. Route loaders fetch this data concurrently, and UI components render new tabs with content display and GitHub file creation shortcuts.

Changes

Cohort / File(s) Summary
Type Definitions
src/lib/types/oracle.ts
Introduces ProjectDoc interface for generic rendered document content with bodyMarkdown, bodyHtml, filePath, and githubEditUrl fields.
Server-side Data Reading
src/lib/server/oracle-reader.ts
Implements readProjectStatus and readProjectDecisions exported functions using internal readProjectDoc helper, which validates slugs, ensures path containment, reads markdown files, and renders to HTML.
Data Loading
src/routes/projects/[slug]/+layout.server.ts
Updates load function to concurrently fetch project, status, and decisions using Promise.all, preserving existing 404 behavior while exposing all three values to child routes.
Tab Navigation & Mobile UI
src/routes/projects/[slug]/+layout.svelte
Expands tab list to include status and decisions tabs, updates view parameter mapping, reorders tabs with artifacts last, and adds horizontal scrolling with improved alignment for mobile.
Tab Content & Empty States
src/routes/projects/[slug]/+page.svelte
Adds conditional rendering for status and decisions views, displaying rendered HTML when available, or empty states with GitHub file creation links targeting Projects/${slug}/STATUS.md and Projects/${slug}/DECISIONS.md paths.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit hops through folder deep
STATUS and DECISIONS now to keep
Tabs expand with thoughtful care
Mobile scrolls without a snare
New documents dance on every page!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding two new tabs (STATUS.md and DECISIONS.md) to project pages, which aligns with the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/project-status-decisions-tabs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/lib/server/oracle-reader.ts (1)

143-170: LGTM — security parity with readProject, clean factoring.

readProjectDoc correctly mirrors readProject'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 catch collapses ENOENT (expected, common case) with permission/render errors (unexpected), which can mask real misconfiguration in production. The existing readProject has the same shape, so feel free to defer — but if you want to differentiate later, branching on err.code === 'ENOENT' and console.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: tighten activeTab mapping.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 691c15f and 7029f63.

📒 Files selected for processing (5)
  • src/lib/server/oracle-reader.ts
  • src/lib/types/oracle.ts
  • src/routes/projects/[slug]/+layout.server.ts
  • src/routes/projects/[slug]/+layout.svelte
  • src/routes/projects/[slug]/+page.svelte

Comment on lines +22 to +30
// 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`
);
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

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.

Suggested change
// 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>
@Nkburdick Nkburdick merged commit 47a7dbd into main Apr 25, 2026
2 checks passed
@Nkburdick Nkburdick deleted the feat/project-status-decisions-tabs branch April 25, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant