Enhancement for /tree-of-knowledge/domain(tools:csharp-playground) — the C# playground (CsharpRunner) should track lesson completion per lesson (completed / not completed / in progress as appropriate), not only a generic "run lesson" event that does not distinguish which lessons the learner finished.
Problem
Today it appears completion/progress captures a broad run signal rather than per-lesson state aligned with Tree of Knowledge lessons.
Goals
Persist and surface each lesson’s completion state for this playground only.
Stay consistent with how other playgrounds will report lesson progress (sibling tickets below).
Integrate with lesson sidebar / ToK UX so learners see clear progress.
Acceptance (draft)
Model stores lesson id ↔ completion for C# playground runs (authenticated user; define offline/local fallback if applicable).
UI reflects done vs not done per lesson in context (sidebar, lesson list, or equivalent).
Migrate away from ambiguous generic run-only telemetry where this feature replaces it — document deltas.
Aligned reporting API or client contract shared with #44 and #45 (same epic).
Area
CsharpRunner · app-language-runner · Tree of Knowledge / domain tools outlet (csharp-playground).
## Summary
**Enhancement** for **`/tree-of-knowledge/domain`(tools:csharp-playground)** — the **C# playground** (`CsharpRunner`) should **track lesson completion per lesson** (completed / not completed / in progress as appropriate), not only a **generic "run lesson"** event that does not distinguish which lessons the learner finished.
## Problem
Today it appears completion/progress captures a **broad run** signal rather than **per-lesson** state aligned with Tree of Knowledge lessons.
## Goals
- Persist and surface **each lesson’s** completion state for this playground only.
- Stay consistent with how other playgrounds will report lesson progress (**sibling tickets** below).
- Integrate with **lesson sidebar / ToK** UX so learners see clear progress.
## Acceptance (draft)
- [ ] Model stores **lesson id ↔ completion** for C# playground runs (authenticated user; define offline/local fallback if applicable).
- [ ] UI reflects **done vs not done** per lesson in context (sidebar, lesson list, or equivalent).
- [ ] Migrate away from ambiguous **generic** run-only telemetry where this feature replaces it — document deltas.
- [ ] Aligned reporting API or client contract shared with **[#44](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/44)** and **[#45](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/45)** (same epic).
## Area
`CsharpRunner` · app-language-runner · Tree of Knowledge / domain `tools` outlet (`csharp-playground`).
## Sibling tickets (same enhancement pattern)
- **[#44 — Python playground](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/44)**
- **[#45 — JavaScript playground](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/45)**
Completion is no longer derived from generic run telemetry. There are now three distinct mechanisms:
Source of truth (durable backend record).UserLessonProgress is one Mongo doc per (UserId, LessonNumber) with attempt count, test results, and timing. Server-side, CodeExecutionWorkerService records an attempt after every submission and only marks completed=true when every graded (non-hint) test passes:
Offline / local fallback. The C# playground grades client-side (WASM .NET) and queues completion deltas in IndexedDB via ProgressSyncService for later server re-verification. Each queued attempt carries lessonNumber, attemptToken, language: "csharp", allTestsPassed, and the studentCode itself. See libraries/features/dev-tools-csharp-runner/src/lib/csharp-runner.service.ts:522–542 and libraries/platform/progress-sync/src/lib/csharp-playground.idb.ts:45–73.
UI integration.LessonSidebar consumes a completedLessonNumbers input and renders checkmark / lock / progress-count state declaratively:
Model stores lessonId ↔ completion for C# runs (with offline/local fallback)
SURPASSED
UserLessonProgress Mongo doc + IndexedDB offline queue + automatic server re-verification on reconnect. Goes beyond a flat "completed" bit — stores attempt count, test results, timing.
UI reflects done vs not done per lesson
IMPLEMENTED
LessonSidebar renders checkmark/lock/bullet + completedCount aggregate; LanguageRunner loads backend progress + pending offline passes and feeds the sidebar.
Migrate away from ambiguous generic run-only telemetry
PARTIALLY IMPLEMENTED
The completion source of truth has fully migrated to UserLessonProgress. Generic activity events (run-code-success, run-code-fail, run-lesson) still coexist for analytics, but no longer drive completion. No standalone migration doc, but the README + code comments capture the delta. Considered acceptable because telemetry and completion serve different purposes now.
Aligned reporting API/client contract shared with #44 / #45
IMPLEMENTED
Shared LanguageRunner, ProgressSyncPort, QueuedProgressInput, /Lessons/progress, /Lessons/progress/batch. C# / Python / JavaScript runners all share the same contract via the language-runner cluster + progress-sync platform lib.
The server-side ExecuteLessonCodeCommand is still annotated as [TrackableActivity("tools.csharp-playground", "run-lesson")] even though Python and JavaScript share the same /CSharpCodeRunner endpoint. So the durable completion path is correct cross-language, but the generic activity row is misattributed to csharp-playground for Python/JS submissions. That's a cross-cutting bug under #44 / #45's scope (telemetry attribution), not #43's. Worth a separate ticket if not already on the radar.
Sibling tickets
The same shared infrastructure (LanguageRunner + ProgressSyncPort + UserLessonProgress) covers Python (#44) and JavaScript (#45) by construction. They could be re-audited and closed under the same logic, modulo the telemetry attribution caveat above.
Audited the current state of per-lesson completion tracking for the C# playground. **Implemented — and partly surpassed.** Closing.
## Architecture as it stands today
The C# runner is a thin shell over a shared `LanguageRunner`:
```ts
// libraries/features/dev-tools-csharp-runner/src/lib/csharp-runner.ts:14–21
@Component({
selector: "app-csharp-runner",
imports: [LanguageRunner],
template: "<app-language-runner></app-language-runner>",
providers: [
{ provide: LANGUAGE_RUNNER_CONFIG, useValue: CSHARP_LANGUAGE_RUNNER_CONFIG },
{ provide: LANGUAGE_RUNTIME_ADAPTER, useExisting: CSharpRuntimeAdapter },
PROGRESS_SYNC_PORT_PROVIDER,
],
})
export class CsharpRunner {}
```
Completion is no longer derived from generic run telemetry. There are now three distinct mechanisms:
1. **Source of truth (durable backend record).** `UserLessonProgress` is one Mongo doc per `(UserId, LessonNumber)` with attempt count, test results, and timing. Server-side, `CodeExecutionWorkerService` records an attempt after every submission and only marks `completed=true` when every graded (non-hint) test passes:
```cs
// SpikerSoft.EventHandlers.CodeExecution/Services/CodeExecutionWorkerService.cs:196–218
var allPassed = response.Success && gradedResults.Count > 0 && gradedResults.All(t => t.Passed);
var outcome = await _lessonProgressService.RecordAttemptAsync(
request.UserId ?? "anonymous",
request.LessonId.Value, allPassed, response.TestResults,
response.ExecutionTimeMs, cancellationToken);
```
2. **Offline / local fallback.** The C# playground grades client-side (WASM .NET) and queues completion deltas in IndexedDB via `ProgressSyncService` for later server re-verification. Each queued attempt carries `lessonNumber`, `attemptToken`, `language: "csharp"`, `allTestsPassed`, and the `studentCode` itself. See `libraries/features/dev-tools-csharp-runner/src/lib/csharp-runner.service.ts:522–542` and `libraries/platform/progress-sync/src/lib/csharp-playground.idb.ts:45–73`.
3. **UI integration.** `LessonSidebar` consumes a `completedLessonNumbers` input and renders checkmark / lock / progress-count state declaratively:
```html
<!-- libraries/platform/language-runner/src/lib/language-runner.html:184–190 -->
<app-lesson-sidebar
[lessons]="lessonsSignal()"
[completedLessonNumbers]="completedLessonsSignal()"
[selectedLessonNumber]="selectedLessonNumber()"
[curriculumTitle]="curriculumTitle"
(lessonSelected)="onLessonSelected($event)">
</app-lesson-sidebar>
```
```ts
// libraries/shared/lesson-panes/src/lib/lesson-sidebar/lesson-sidebar.ts:31–53
protected readonly completedSet = computed(() => new Set(this.completedLessonNumbers()));
protected isCompleted(lesson: LessonCatalogEntry): boolean {
return this.completedSet().has(lesson.lessonNumber);
}
```
## Acceptance criteria — verdict by row
| AC | Verdict | Evidence |
|----|---------|----------|
| **Model stores `lessonId ↔ completion` for C# runs (with offline/local fallback)** | **SURPASSED** | `UserLessonProgress` Mongo doc + IndexedDB offline queue + automatic server re-verification on reconnect. Goes beyond a flat "completed" bit — stores attempt count, test results, timing. |
| **UI reflects done vs not done per lesson** | **IMPLEMENTED** | `LessonSidebar` renders checkmark/lock/bullet + `completedCount` aggregate; `LanguageRunner` loads backend progress + pending offline passes and feeds the sidebar. |
| **Migrate away from ambiguous generic run-only telemetry** | **PARTIALLY IMPLEMENTED** | The completion *source of truth* has fully migrated to `UserLessonProgress`. Generic activity events (`run-code-success`, `run-code-fail`, `run-lesson`) still coexist for analytics, but no longer drive completion. No standalone migration doc, but the README + code comments capture the delta. Considered acceptable because telemetry and completion serve different purposes now. |
| **Aligned reporting API/client contract shared with #44 / #45** | **IMPLEMENTED** | Shared `LanguageRunner`, `ProgressSyncPort`, `QueuedProgressInput`, `/Lessons/progress`, `/Lessons/progress/batch`. C# / Python / JavaScript runners all share the same contract via the language-runner cluster + progress-sync platform lib. |
## Known follow-up (not blocking #43)
The server-side `ExecuteLessonCodeCommand` is still annotated as `[TrackableActivity("tools.csharp-playground", "run-lesson")]` even though Python and JavaScript share the same `/CSharpCodeRunner` endpoint. So the *durable completion* path is correct cross-language, but the *generic activity row* is misattributed to `csharp-playground` for Python/JS submissions. That's a cross-cutting bug under #44 / #45's scope (telemetry attribution), not #43's. Worth a separate ticket if not already on the radar.
## Sibling tickets
The same shared infrastructure (`LanguageRunner` + `ProgressSyncPort` + `UserLessonProgress`) covers Python (#44) and JavaScript (#45) by construction. They could be re-audited and closed under the same logic, modulo the telemetry attribution caveat above.
Closing #43.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Enhancement for
/tree-of-knowledge/domain(tools:csharp-playground) — the C# playground (CsharpRunner) should track lesson completion per lesson (completed / not completed / in progress as appropriate), not only a generic "run lesson" event that does not distinguish which lessons the learner finished.Problem
Today it appears completion/progress captures a broad run signal rather than per-lesson state aligned with Tree of Knowledge lessons.
Goals
Acceptance (draft)
Area
CsharpRunner· app-language-runner · Tree of Knowledge / domaintoolsoutlet (csharp-playground).Sibling tickets (same enhancement pattern)
Audited the current state of per-lesson completion tracking for the C# playground. Implemented — and partly surpassed. Closing.
Architecture as it stands today
The C# runner is a thin shell over a shared
LanguageRunner:Completion is no longer derived from generic run telemetry. There are now three distinct mechanisms:
Source of truth (durable backend record).
UserLessonProgressis one Mongo doc per(UserId, LessonNumber)with attempt count, test results, and timing. Server-side,CodeExecutionWorkerServicerecords an attempt after every submission and only markscompleted=truewhen every graded (non-hint) test passes:Offline / local fallback. The C# playground grades client-side (WASM .NET) and queues completion deltas in IndexedDB via
ProgressSyncServicefor later server re-verification. Each queued attempt carrieslessonNumber,attemptToken,language: "csharp",allTestsPassed, and thestudentCodeitself. Seelibraries/features/dev-tools-csharp-runner/src/lib/csharp-runner.service.ts:522–542andlibraries/platform/progress-sync/src/lib/csharp-playground.idb.ts:45–73.UI integration.
LessonSidebarconsumes acompletedLessonNumbersinput and renders checkmark / lock / progress-count state declaratively:Acceptance criteria — verdict by row
lessonId ↔ completionfor C# runs (with offline/local fallback)UserLessonProgressMongo doc + IndexedDB offline queue + automatic server re-verification on reconnect. Goes beyond a flat "completed" bit — stores attempt count, test results, timing.LessonSidebarrenders checkmark/lock/bullet +completedCountaggregate;LanguageRunnerloads backend progress + pending offline passes and feeds the sidebar.UserLessonProgress. Generic activity events (run-code-success,run-code-fail,run-lesson) still coexist for analytics, but no longer drive completion. No standalone migration doc, but the README + code comments capture the delta. Considered acceptable because telemetry and completion serve different purposes now.LanguageRunner,ProgressSyncPort,QueuedProgressInput,/Lessons/progress,/Lessons/progress/batch. C# / Python / JavaScript runners all share the same contract via the language-runner cluster + progress-sync platform lib.Known follow-up (not blocking #43)
The server-side
ExecuteLessonCodeCommandis still annotated as[TrackableActivity("tools.csharp-playground", "run-lesson")]even though Python and JavaScript share the same/CSharpCodeRunnerendpoint. So the durable completion path is correct cross-language, but the generic activity row is misattributed tocsharp-playgroundfor Python/JS submissions. That's a cross-cutting bug under #44 / #45's scope (telemetry attribution), not #43's. Worth a separate ticket if not already on the radar.Sibling tickets
The same shared infrastructure (
LanguageRunner+ProgressSyncPort+UserLessonProgress) covers Python (#44) and JavaScript (#45) by construction. They could be re-audited and closed under the same logic, modulo the telemetry attribution caveat above.Closing #43.