Skip to content

fix(skills,#245): lesson provenance + timestamps, drop the unparseable CSS hue - #259

Merged
kevinthelago merged 1 commit into
mainfrom
245-skills-plan-polish
Jul 29, 2026
Merged

fix(skills,#245): lesson provenance + timestamps, drop the unparseable CSS hue#259
kevinthelago merged 1 commit into
mainfrom
245-skills-plan-polish

Conversation

@kevinthelago

@kevinthelago kevinthelago commented Jul 28, 2026

Copy link
Copy Markdown
Owner

The skills half of #245. Part of #234.

Scope changed after review. This PR originally carried the plan half too (P1 ahead colour, P2 button labels). Both targeted LivePlanBoard.tsx, which has been dead code since 2026-07-25 — see below. They have been removed rather than merged into a component nothing renders.

S1 — lessons lost their provenance and timestamps

The desktop ships the full 9-field Lesson; LessonVM kept 6, dropping provenance, createdAt, updatedAt (epoch ms on both sides — the ISO-string trap that broke security does not occur here). So the pending queue rendered in raw payload order with no capture age and no origin.

Now parsed, sorted newest-first (stable, so equal timestamps keep payload order), and rendered through a pure relativeAge(at, now) helper — now injected rather than read inside, so it stays testable. Age and origin ride in the lesson spec's existing third slot, so this needed no mobile.lessonItem.json change.

S2 — hue is CSS React Native cannot parse

Parsed and typed but never rendered, which was quietly load-bearing in our favour: parseSkillGroupsFile defaults to var(--accent) and authored values may be oklch(...) literals. Bind either to a color prop and you get a crash or a silently ignored style.

Taking the issue's first option: dropped from SkillGroupVM rather than left as a trap. The reasoning is recorded on the type and on the payload decoder, which passes the field through as a documented deliberate ignore — so whoever first wants group colour finds the warning before the bug.

Harness

skills moves from PENDING_DOMAINS into DECODERS: Layer A strict re-encode, plus Layer B asserting lesson provenance/timestamps survive the parse and that the skills_no_lessons variant yields null rather than an empty queue.

Why the plan half is not here

LivePlanBoard.tsx — the sole target of P1 and P2 — has had zero importers since 54c13fb (2026-07-25, #250), which rewrote app/(tabs)/plan.tsx from 103 lines to 18 and removed the segment strip that was its only mount point. The component file was left on disk. #245 was filed 2026-07-20, five days earlier, so it cites a surface that has not been live since.

The plan store domain was not merely unmounted — it was superseded. Live planning now runs on plan_state / plan_event frames: livePlan.tsLivePlanContext (mounted in app/_layout.tsx) → BlueprintStageBar, which uses a different status vocabulary (SectionRenderStatus: in-progress / complete / …), not StageStatus. So the ahead fix does not transfer; the live path needs its own audit against its own vocabulary.

Re-scoping tracked on #245, which stays open for the plan half.

Gate

  • npx tsc --noEmit clean.
  • 529 tests pass, including Layer A: skills payload round-trips through the mobile model.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RAPZC6xEyCrH9qSKUKLQdy

@github-actions

Copy link
Copy Markdown

📦 Preview build ready

Artifact: preview-pr-259-19a23a345d75c7fd549e247c364a53d65da5e5c1

⬇️ Download from Actions run

Built from commit 19a23a3

…e CSS hue

The skills half of #245. The plan half (P1 `ahead` colour, P2 button labels) is NOT here —
see below.

**S1 — lessons lost their provenance and timestamps.** Desktop ships the full 9-field
`Lesson`; `LessonVM` kept 6, dropping `provenance`, `createdAt`, `updatedAt` (epoch ms on both
sides — no ISO-string trap). The pending queue therefore rendered in raw payload order with no
capture age and no origin. Now parsed, sorted newest-first (stable, so equal timestamps keep
payload order), and surfaced via a pure `relativeAge(at, now)` helper. Age + origin ride in the
lesson spec's existing third slot, so no design-system spec change was needed.

**S2 — `hue` is CSS React Native cannot parse.** Parsed and typed but never rendered, which was
load-bearing in our favour: desktop values are `var(--accent)` by default and may be authored
`oklch(...)`. Taking the issue's first option, it is dropped from `SkillGroupVM` rather than
left as a trap for whoever first binds it to a `color` prop. The reason is recorded on the type
and on the payload decoder, which passes it through as a documented deliberate ignore.

**Harness:** `skills` moves from `PENDING_DOMAINS` into `DECODERS` — Layer A re-encode plus
Layer B asserting lesson provenance/timestamps survive and that `skills_no_lessons` parses as
null rather than an empty queue.

## Why the plan half was dropped

`LivePlanBoard.tsx` — the sole target of P1 and P2 — has been DEAD CODE since 54c13fb
(2026-07-25, #250), which rewrote `app/(tabs)/plan.tsx` and removed the segment strip that was
its only mount point. Nothing in the repo imports it. #245 was filed 2026-07-20, five days
earlier, so it cites a live surface that has not been live since.

The `plan` STORE domain was not merely unmounted, it was superseded: live planning now runs on
`plan_state`/`plan_event` FRAMES through `livePlan.ts` → `LivePlanContext` (mounted in
`app/_layout.tsx`) → `BlueprintStageBar`, which uses a different status vocabulary
(`SectionRenderStatus`), not `StageStatus`. So the `ahead` fix does not transfer — the live path
needs its own audit. Tracked back on #245.

Gates: `npx tsc --noEmit` clean; 529 tests pass.

Refs #245

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RAPZC6xEyCrH9qSKUKLQdy
@kevinthelago
kevinthelago force-pushed the 245-skills-plan-polish branch from 5bc0011 to 3c90de0 Compare July 28, 2026 21:52
@kevinthelago kevinthelago changed the title fix(skills,plan,#245): lesson provenance + timestamps, the missing ahead colour, honest plan labels fix(skills,#245): lesson provenance + timestamps, drop the unparseable CSS hue Jul 28, 2026
@github-actions

Copy link
Copy Markdown

📦 Preview build ready

Artifact: preview-pr-259-a1de71713bc18f43590c4e7dc98ff890bfd19315

⬇️ Download from Actions run

Built from commit a1de717

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