Skip to content

feat: first-run auto-init, overall progress, completion celebration (#13, #15, #16)#20

Closed
abhiksark wants to merge 3 commits into
mainfrom
feature/first-run-and-progress
Closed

feat: first-run auto-init, overall progress, completion celebration (#13, #15, #16)#20
abhiksark wants to merge 3 commits into
mainfrom
feature/first-run-and-progress

Conversation

@abhiksark

@abhiksark abhiksark commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Closes #13
Closes #15
Closes #16

Part of the 0.3.0 adoption roadmap — First-run UX & motivation. Built test-first (TDD).

#13 — auto-init a workspace when launching with none

Running pylings (or watch/start/topics) in a directory without a
workspace previously raised ManifestError and exited 2 — the #1 beginner
trap. It now auto-creates ./pylings-workspace (reusing an existing one) and
launches into it.

  • New ensure_workspace(root) -> (path, created) in core/curriculum.py.
  • cli.main() resolves the root via it before run_tui, printing where the
    workspace is when it differs from the requested root.

#15 — overall curriculum progress in the TUI

The progress bar showed only the current topic. It now also shows overall
progress across all exercises.

  • Pure format_progress(...) in widgets/progress.py; TrackScreen._render_state
    passes overall counts (len(state.completed) vs len(manifest.exercises)).

#16 — celebrate finishing the whole curriculum

A per-topic message existed, but finishing everything had no payoff. Completing
the final pending exercise now shows a celebration (count, shareable line,
GitHub-star nudge). Gated on next_pending(manifest.exercises, completed) is None
so it fires only at true 100%; non-blocking.

Tests

TDD throughout — 7 new tests (ensure_workspace x3, headless CLI auto-init x1,
format_progress x2, celebration_message x1). Full suite 125 → 132 passed,
verified on Python 3.9 and 3.13.

Summary by CodeRabbit

  • New Features
    • Workspace auto-initialization: Running commands in an empty directory now automatically creates a workspace.
    • Enhanced progress tracking: Progress bars now display both topic-specific and overall curriculum completion percentages.
    • Curriculum completion celebration: Special congratulatory message displays upon finishing all topics.

abhiksark added 3 commits June 3, 2026 01:43
Closes #13.

Running `pylings` (or watch/start/topics) in a directory without a
workspace previously raised ManifestError and exited 2 — the #1 beginner
trap. Now it auto-creates a workspace at ./pylings-workspace (reusing one
if already present) and launches into it.

- New ensure_workspace(root) in core/curriculum.py: returns root if it is
  already a workspace, reuses a prior ./pylings-workspace, else inits one.
  Returns (path, created_new).
- cli.main() resolves the root via ensure_workspace before run_tui and
  prints where the workspace is when it differs from the requested root.
- Tests: unit coverage for ensure_workspace (3 cases) and a headless CLI
  integration test (start with a bad topic returns before the TUI).
Closes #15.

The progress bar previously showed only the current topic's completion.
It now also shows overall progress across all exercises, so learners can
see how far they are in the whole curriculum.

- New pure format_progress(...) in widgets/progress.py renders both the
  topic and overall bars/counts; ProgressBar.update_progress delegates to it.
- TrackScreen._render_state passes overall counts (len(state.completed) vs
  len(manifest.exercises)).
- Tests cover the formatter incl. the zero-total edge case.
Closes #16.

A per-topic completion message already existed, but finishing the entire
curriculum had no payoff. When the final pending exercise across all topics
is completed, the track screen now shows a celebration (count, a shareable
line, and a GitHub-star nudge) instead of the plain topic-complete message.

- celebration_message(total) builds the text; shown via OutputPanel.show_final.
- Gated on next_pending(manifest.exercises, completed) is None, so it fires
  only at true 100%. Non-blocking: F4/Ctrl+Q still work.
- Unit test covers the message content.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR implements three features from the 0.3.0 adoption roadmap: auto-initializing a workspace on first run (preventing ManifestError), enhancing the progress bar to show both per-topic and overall curriculum completion, and displaying a celebration message when all exercises are finished.

Changes

Adoption & Motivation UX

Layer / File(s) Summary
Workspace auto-initialization on startup
pylings/core/curriculum.py, pylings/cli.py, tests/unit/test_workspace_autoinit.py, tests/integration/test_cli_autoinit.py
New DEFAULT_WORKSPACE_DIRNAME constant and ensure_workspace(root) function detect or create a workspace at root or root/pylings-workspace, returning the resolved path and a creation flag. The CLI's TUI commands (watch, start, topics) call this function before loading the manifest, eliminating the ManifestError on first run and printing a message when a workspace is created or reused.
Progress widget enhancement for dual-level display
pylings/widgets/progress.py, tests/unit/test_progress.py
ProgressBar.update_progress signature now accepts both per-topic and overall progress counts. New format_progress and _bar helpers compose a single-line display with topic-specific and overall sections, each with a progress bar and percentage.
Overall completion tracking and celebration
pylings/screens/track.py, tests/unit/test_celebration.py
New celebration_message(total) function generates a celebratory completion message. The track screen now passes overall progress metrics to the progress widget and branches on whether any pending exercise remains: shows the curriculum celebration message at 100% or the per-topic completion message otherwise.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

A workspace blooms without a call,
Progress bars now track it all—
Complete the course and celebrate,
292 exercises sealed with fate! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main features implemented: first-run auto-init, overall progress, and completion celebration, with clear issue references.
Description check ✅ Passed The description comprehensively covers all three linked issues, explains the implementation approach, and documents the test additions with specific counts and Python version verification.
Linked Issues check ✅ Passed The PR successfully implements all requirements from #13 (auto-init workspace), #15 (overall progress display), and #16 (completion celebration) with corresponding code changes and tests.
Out of Scope Changes check ✅ Passed All code changes directly support the three linked objectives: workspace auto-initialization, overall progress tracking, and completion celebrations. No unrelated changes detected.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/first-run-and-progress

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In `@pylings/cli.py`:
- Around line 293-299: ensure_workspace can raise WorkspaceError but the current
code only converts ManifestError, so wrap the call to ensure_workspace in a
try/except that catches WorkspaceError (import WorkspaceError from
pylings.core.curriculum) and handle it gracefully: print a concise,
user-friendly message including the error details (e.g., "Workspace appears
invalid: {e}. Try removing or repairing the pylings-workspace or run init") and
exit with a non-zero status (sys.exit(1)); keep the existing created/root logic
for successful returns.

In `@pylings/core/curriculum.py`:
- Around line 96-100: The current call to init_workspace(default) can raise
WorkspaceError when root/pylings-workspace exists and is non-empty but lacks
info.toml; wrap the init_workspace(default) call in a try/except for
WorkspaceError and, if the directory already exists and contains files (i.e.,
default.exists() and any(default.iterdir())), treat it as an existing workspace
by returning default.resolve(), False; otherwise re-raise the WorkspaceError.
Reference init_workspace and WorkspaceError so you can locate and update the
error handling around that call.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6d11b1b-5340-42ec-8299-2fce47491bda

📥 Commits

Reviewing files that changed from the base of the PR and between a6567e3 and cb7ea86.

📒 Files selected for processing (8)
  • pylings/cli.py
  • pylings/core/curriculum.py
  • pylings/screens/track.py
  • pylings/widgets/progress.py
  • tests/integration/test_cli_autoinit.py
  • tests/unit/test_celebration.py
  • tests/unit/test_progress.py
  • tests/unit/test_workspace_autoinit.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
tests/{unit,integration,tui}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Tests should be organized in tests/unit/, tests/integration/, and tests/tui/ directories with fixtures in tests/fixtures/

Files:

  • tests/unit/test_celebration.py
  • tests/unit/test_workspace_autoinit.py
  • tests/unit/test_progress.py
  • tests/integration/test_cli_autoinit.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use Python 3.11+ idioms and 4-space indentation
Prefer small, typed functions where practical

Files:

  • tests/unit/test_celebration.py
  • pylings/screens/track.py
  • tests/unit/test_workspace_autoinit.py
  • pylings/widgets/progress.py
  • pylings/cli.py
  • pylings/core/curriculum.py
  • tests/unit/test_progress.py
  • tests/integration/test_cli_autoinit.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Name test files as test_<behavior>.py and test functions as test_<expected_behavior>
Use pytest for all tests; async tests are supported by pytest-asyncio in auto mode

Files:

  • tests/unit/test_celebration.py
  • tests/unit/test_workspace_autoinit.py
  • tests/unit/test_progress.py
  • tests/integration/test_cli_autoinit.py
tests/unit/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add unit tests for core behavior in tests/unit/

Files:

  • tests/unit/test_celebration.py
  • tests/unit/test_workspace_autoinit.py
  • tests/unit/test_progress.py
pylings/{screens,widgets}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Textual screens and widgets should live in pylings/screens/ and pylings/widgets/

Files:

  • pylings/screens/track.py
  • pylings/widgets/progress.py
pylings/{cli.py,__main__.py}

📄 CodeRabbit inference engine (AGENTS.md)

CLI entry points should be in pylings/cli.py and pylings/__main__.py

Files:

  • pylings/cli.py
pylings/core/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Core exercise loading, workspace setup, state, reset, solutions, and runner logic should live in pylings/core/

Files:

  • pylings/core/curriculum.py
tests/integration/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add integration tests for CLI/workspace flows in tests/integration/

Files:

  • tests/integration/test_cli_autoinit.py
🧠 Learnings (6)
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: Applies to tests/integration/**/*.py : Add integration tests for CLI/workspace flows in `tests/integration/`

Applied to files:

  • tests/unit/test_workspace_autoinit.py
  • pylings/cli.py
  • tests/integration/test_cli_autoinit.py
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: Applies to tests/unit/**/*.py : Add unit tests for core behavior in `tests/unit/`

Applied to files:

  • tests/unit/test_workspace_autoinit.py
  • tests/unit/test_progress.py
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: Applies to pylings/core/**/*.py : Core exercise loading, workspace setup, state, reset, solutions, and runner logic should live in `pylings/core/`

Applied to files:

  • tests/unit/test_workspace_autoinit.py
  • pylings/core/curriculum.py
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: Applies to pylings/{cli.py,__main__.py} : CLI entry points should be in `pylings/cli.py` and `pylings/__main__.py`

Applied to files:

  • pylings/cli.py
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: When changing curriculum, update `exercises/`, `checks/`, `solutions/`, and `info.toml`, then run relevant pytest files plus `pylings --root tests/fixtures/passing_curriculum verify`

Applied to files:

  • pylings/cli.py
  • pylings/core/curriculum.py
📚 Learning: 2026-05-25T15:06:34.945Z
Learnt from: CR
Repo: abhiksark/pylings PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-25T15:06:34.945Z
Learning: Applies to tests/tui/**/*.py : Add TUI tests for Textual interactions in `tests/tui/`

Applied to files:

  • tests/unit/test_progress.py
🪛 Ruff (0.15.15)
tests/integration/test_cli_autoinit.py

[error] 9-9: subprocess call: check for execution of untrusted input

(S603)

🔇 Additional comments (6)
tests/unit/test_workspace_autoinit.py (1)

8-31: LGTM!

tests/integration/test_cli_autoinit.py (1)

8-21: LGTM!

pylings/widgets/progress.py (1)

6-39: LGTM!

tests/unit/test_progress.py (1)

6-22: LGTM!

pylings/screens/track.py (1)

22-30: LGTM!

Also applies to: 193-202

tests/unit/test_celebration.py (1)

6-10: LGTM!

Comment thread pylings/cli.py
Comment on lines +293 to +299
from pylings.core.curriculum import ensure_workspace
root, created = ensure_workspace(args.root)
if root != args.root.expanduser().resolve():
if created:
print(f"No pylings workspace found here; created one at {root}")
else:
print(f"Using existing pylings workspace at {root}")

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

Handle WorkspaceError from ensure_workspace gracefully.

ensure_workspace can raise WorkspaceError (e.g., a non-empty ./pylings-workspace without info.toml). The handler at lines 316-322 only converts ManifestError, so this surfaces as a raw traceback — the opposite of the friendly first-run behavior this PR targets.

🛡️ Proposed fix
         if args.command in (None, "watch", "start", "topics"):
-            from pylings.core.curriculum import ensure_workspace
-            root, created = ensure_workspace(args.root)
+            from pylings.core.curriculum import WorkspaceError, ensure_workspace
+            try:
+                root, created = ensure_workspace(args.root)
+            except WorkspaceError as e:
+                sys.stderr.write(f"pylings: {e}\n")
+                return 2
             if root != args.root.expanduser().resolve():
🤖 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 `@pylings/cli.py` around lines 293 - 299, ensure_workspace can raise
WorkspaceError but the current code only converts ManifestError, so wrap the
call to ensure_workspace in a try/except that catches WorkspaceError (import
WorkspaceError from pylings.core.curriculum) and handle it gracefully: print a
concise, user-friendly message including the error details (e.g., "Workspace
appears invalid: {e}. Try removing or repairing the pylings-workspace or run
init") and exit with a non-zero status (sys.exit(1)); keep the existing
created/root logic for successful returns.

Comment on lines +96 to +100
default = root / DEFAULT_WORKSPACE_DIRNAME
if (default / "info.toml").exists():
return default.resolve(), False

return init_workspace(default), True

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

Edge case: non-empty pylings-workspace without info.toml raises WorkspaceError.

If root/pylings-workspace already exists and is non-empty but lacks info.toml (e.g., a partial/aborted init or a user-created dir), init_workspace(default) raises WorkspaceError ("already exists and is not empty"). This propagates uncaught through cli.main (which only handles ManifestError), reintroducing a crash on the very first-run path this PR aims to smooth. See the related comment in cli.py.

🤖 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 `@pylings/core/curriculum.py` around lines 96 - 100, The current call to
init_workspace(default) can raise WorkspaceError when root/pylings-workspace
exists and is non-empty but lacks info.toml; wrap the init_workspace(default)
call in a try/except for WorkspaceError and, if the directory already exists and
contains files (i.e., default.exists() and any(default.iterdir())), treat it as
an existing workspace by returning default.resolve(), False; otherwise re-raise
the WorkspaceError. Reference init_workspace and WorkspaceError so you can
locate and update the error handling around that call.

@abhiksark

Copy link
Copy Markdown
Owner Author

Superseded by the v0.4.0 pylingspythonlings rename on main — this branch is built on the old package layout and conflicts wholesale. Re-implementing the still-novel features (#14 welcome, #15 overall progress, #16 celebration) fresh on top of v0.4.0. #13 is dropped (main's zero-config first-run already covers it). Carrying forward the two code-review fixes (uncaught WorkspaceError; >100% progress clamp).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant