feat(engineering): compile OpenAI's Spinning Up in Deep RL into a knowledge-base plugin - #993
Conversation
…wledge-base plugin Runs engineering/book-to-skill end to end on its first real source: OpenAI's Spinning Up in Deep RL (MIT, (c) 2018 OpenAI; primarily developed by Joshua Achiam). Cloned openai/spinningup and compiled its docs/ reStructuredText tree (38 files, ~37k words, ~49K tokens) through the full pipeline -- extract --mode technical, analysis, 20 chapter files, glossary/patterns/cheatsheet, master SKILL.md, validator, plugin emitter. The compiled skill passes book_skill_validator.py in --strict mode with every file inside budget: a 2,101-token resident core (cap 4,000) plus 20 on-demand chapters averaging ~1,256 tokens each. Chapter structure follows the source's own toctree rather than a heading scan: user documentation (ch01-06), Introduction to RL Parts 1-3 (ch07-09), the researcher essay / key papers / exercises / benchmarks (ch10-13), one chapter per algorithm in lineage order (ch14-19: VPG to TRPO to PPO, DDPG to TD3 and SAC), and the logger/MPI/ExperimentGrid utilities (ch20). Rights basis is open-license, not fair use -- the emitter's Step-11 gate refuses a shareable package without one. Upstream's MIT notice is reproduced in full in the plugin's LICENSE beside this package's own, and README.md names the source, the author and the source's frozen version; a sidecar JSON is not a license notice. Also fixes a defect the emitter only reveals at its final step: skill_plugin_emitter.py wrote its whole `source` provenance block into plugin.json, on a stale inline claim that `source`/`attribution` were approved extension fields. Claude Code rejects an entire manifest on any unrecognized key (issue #954) and scripts/check_plugin_json.py hard-fails such a manifest, so every package the emitter produced failed the blocking CI gate on commit. _plugin_manifest() now emits spec fields only and a new _authoring_notes() writes .claude-plugin/authoring-notes.json. Recorded as deviation 26 in engineering/book-to-skill/README.md; the printed marketplace.json snippet is unchanged, since `source` is a valid key there. Counters: skills 386 -> 387, agents 116 -> 117, commands 146 -> 147, plugins 97 -> 98. Tools and references unchanged -- a compiled knowledge base ships notes, not scripts. All blocking CI gates verified locally: compileall, check_plugin_json --all, check_skill_names, check_paths, check_frontmatter, check_dual_publish, check_model_freshness, smoke_scripts (692/692), derive_counters --check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
dev's derive_counters.py gained two additional check targets that main's copy did not have -- mkdocs.yml's site_description and .codex-plugin/plugin.json's description / shortDescription / longDescription. Both carried the pre-linkedin, pre-skill-doctor numbers (380 skills / 706 tools / 823 refs / 114 agents / 138 commands / 96 plugins), so the blocking gate fails on this branch the moment dev's checker meets main's tree. Synced all six to the derived values: 387 skills, 723 tools, 838 references, 117 agents, 147 commands, 98 plugins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
ReviewReviewed the PR-specific content (the rest of the diff is inherited
|
…decar Addresses the review observation on #993: the compiled skill's authoring-notes.json carried only a `source` block (how it was built) even though its content is derived from an external MIT-licensed work, where the rest of the repo uses an `attribution` block for that. check_plugin_json.py's NOTES_ALLOWED permits both keys, so the two coexist. Adds `attribution` to engineering/spinning-up-deep-rl following the shape used by book-to-skill and skillopt-sleep: derived_from, upstream_docs, upstream_path, original_author, original_license, original_copyright, derivation_note. The emitter is deliberately NOT changed to synthesise this. It knows only `--source-note` free text and a rights basis -- not an upstream URL, author or licence -- and a half-filled attribution block is worse than none. Instead Step 11 of conversion_workflow.md now says attribution is added by hand whenever `--rights` is anything but internal-docs, names the field shape, and restates that the actual obligation is the LICENSE notice and README credit -- authoring-notes.json is metadata Claude Code never reads, and a sidecar JSON file is not a licence notice. Gates re-run clean: check_plugin_json --all, check_paths, check_frontmatter, check_dual_publish, check_model_freshness, smoke_scripts (692/692), derive_counters --check, book_skill_validator --strict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
|
Thanks — I verified the
I deliberately did not change the emitter to synthesise this, which I think is the more interesting half of your point. That section also now restates the thing worth not losing: the actual licence obligation is met by the notice in Gates re-run clean on the new head: Generated by Claude Code |
dev moved: PR #994 landed engineering/deep-learning-book, which collides with this branch on every headline-counter and registry surface. Conflicts resolved in four files, keeping both sides' content: - .claude-plugin/marketplace.json -- both plugin entries kept; the registry now carries spinning-up-deep-rl and deep-learning-book. 99 plugins. - CHANGELOG.md -- both Unreleased sections kept. - CLAUDE.md, README.md -- dev's prose taken as the newer baseline, then this branch's engineering-row entry restored and every counter re-derived rather than hand-picked from either side. Counters re-derived from the merged tree with derive_counters.py, which is the ground truth, and trued up across all five surfaces: 388 skills, 99 plugins, 727 tools, 842 references, 118 agents, 150 commands. Both changelog/CLAUDE.md delta lines are restated: each side was written against its own base and both claimed 386 -> 387, which is no longer true of either now that they land together. This branch's entry is now stated as the delta on top of deep-learning-book. Gates re-run on the resolved merge: no conflict markers left in the tree, compileall, check_plugin_json --all, check_skill_names, check_paths, check_frontmatter, check_dual_publish, check_model_freshness, smoke_scripts (696/696), derive_counters --check, book_skill_validator --strict, and a JSON/YAML parse of every file touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
ReviewReviewed the diff, the new OverallStrong PR. The compiled knowledge base is technically accurate and clearly traces back to the real Spinning Up docs — I spot-checked The new Issue: counters inconsistency in the docsThe per-feature changelog counters don't match the root totals in the same diff:
So the actual delta this PR contributes is Minor
Not concerns
|
Addresses both review findings on #993. 1. The changelog/CLAUDE.md counter line said "commands 147 → 150", which asserts this plugin adds three commands. It adds exactly one (commands/cs-spinning-up-deep-rl.md). 147 was this branch's pre-merge baseline; after merging dev the delta had to be restated against dev's baseline, and that one figure was carried over unchanged while the others were updated. Verified empirically rather than by arithmetic: derive_counters on an origin/dev worktree reports 149 commands, and the raw command-file count goes 281 → 282 across the merge. Corrected to 149 → 150. The other three figures on that line (skills 387 → 388, agents 117 → 118, plugins 98 → 99) were already right. 2. README POWERFUL-tier row had a stray space before a comma: "calculator) , **spinning-up-deep-rl**". Removed. Neither affected derive_counters --check, which reads the tree rather than the prose -- which is exactly why a wrong delta in prose can survive a green gate, and why it was worth fixing in a repo this strict about counters being trustworthy. Gates re-run: compileall, check_plugin_json --all, check_paths, check_frontmatter, check_dual_publish, check_model_freshness, smoke_scripts (696/696), derive_counters --check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
|
Both findings verified and fixed in The commands delta was wrong, and your diagnosis of why was right. I checked it empirically rather than by arithmetic: The cause was the merge with #994. Before it, this branch's baseline was 147; after, the delta had to be restated against dev's. I updated skills, agents and plugins but carried Worth naming the general point: The README spacing nit is fixed too — Gates re-run on the new head: Generated by Claude Code |
ReviewThis is a well-executed PR — the compiled Two things worth cleaning up before/after merge: 1. Stale docstring in The module docstring wasn't updated to match this PR's own fix:
Low severity (docstring only, doesn't affect behavior), but since this PR's whole point in that file is "the manifest must not carry 2. Token-count discrepancy between CHANGELOG.md and CLAUDE.md The PR states the resident core is 2,101 tokens in the PR description and in Other notes (no action needed):
Nice work overall — the emitter fix alone makes this a valuable PR independent of the new skill content. |
…core figure Addresses both review findings on #993. 1. skill_plugin_emitter.py's module docstring still described the behaviour this PR removed. Its tree diagram called plugin.json the "manifest with ./skills/<slug> + attribution", and the rights-gate paragraph said cleared_for_distribution is recorded "in the manifest" -- both now the sidecar. A docstring asserting the opposite of its own file's point is worse than no docstring, and it is what `--help` readers see. Also corrects a third instance the review did not name but which shares the root cause: the tree diagram never listed authoring-notes.json at all, even though this PR's fix is what creates it. Added, with the issue #954 reason stated inline so the next reader knows why provenance cannot live in the manifest. 2. CLAUDE.md said the resident core is 2,066 tokens while, twelve lines later, the same file said 2,101. 2,066 predates the worked invocation block added for write-a-skill checklist item 5; token_budget_estimator.py reports 2,101 on the current tree, matching CHANGELOG.md and the PR body. CLAUDE.md was the only stale copy and it contradicted itself. Docstring-only and prose-only; no behaviour change. Verified the emitter still emits both plugin.json and authoring-notes.json via --sample after the edit. Gates: compileall, check_plugin_json --all, check_paths, check_frontmatter, check_dual_publish, check_model_freshness, smoke_scripts (696/696), derive_counters --check, book_skill_validator --strict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UySnyf5upm4y8xhYA3w6yw
|
Both verified and fixed in 1. Docstring drift — correct, and there was a third instance. You named the tree-diagram line and the rights-gate paragraph; both now point at the sidecar. The diagram also never listed 2. Token count — 2,101 is right, and CLAUDE.md was contradicting itself. Both changes are docstring/prose only, no behaviour change. I re-ran Gates green on the new head: Noting a pattern across the three review rounds, since it's the useful takeaway: every finding has been prose drifting from a verified tree — a half-rebased delta, a stale docstring, a pre-fix token count. None were caught by CI, because every gate here reads the tree and none read the prose. That's a real blind spot in an otherwise strict setup, though widening it is out of scope for this PR. Generated by Claude Code |
ReviewWent through the emitter fix, the manifest/authoring-notes schema, and spot-checked the compiled The core fix (
|
Summary
Runs
engineering/book-to-skillend to end on its first real source: OpenAI's Spinning Up in Deep RL (MIT, © 2018 OpenAI; primarily developed by Joshua Achiam). Newengineering/spinning-up-deep-rl/plugin — 20 chapters, a glossary, a patterns file and a decision cheatsheet behind a 2,101-token resident core.The full pipeline, not a hand-write. Cloned
openai/spinningupand compiled itsdocs/reStructuredText tree (38 files, ~37k words, ~49K tokens) through every step:extract_document.py --mode technical→ analysis → 20 chapter files → supporting files → masterSKILL.md→book_skill_validator.py→skill_plugin_emitter.py. The validator passes clean in--strictmode and every file is inside budget (core 2,101 / 4,000 tokens; 20 chapters averaging ~1,256 tokens, loaded on demand).Structure follows the source's own
toctree, not a heading scan: user documentation (ch01–06), Introduction to RL Parts 1–3 (ch07–09), the researcher essay / key papers / exercises / benchmarks (ch10–13), one chapter per algorithm in lineage order (ch14–19: VPG → TRPO → PPO, DDPG → TD3 → SAC), and the logger / MPI / ExperimentGrid utilities (ch20).Rights basis is
open-license, not fair use. The emitter's Step-11 gate refuses a shareable package without one; MIT permits derivative distribution. Upstream's notice is reproduced in full in the plugin'sLICENSEbeside this package's own, andREADME.mdnames the source, the author and the source's frozen version — a sidecar JSON is not a license notice.authoring-notes.jsoncarries both asourceblock (how it was built) and anattributionblock (whose work it came from).The cheatsheet carries the judgment a glossary cannot — the under-5-minute debug-turnaround target, the 3-seed minimum (10+ to be thorough), benchmark network defaults that differ by algorithm family ((64,32)/tanh on-policy vs (256,256)/relu off-policy), and Spinning Up's own parity disclosure: DDPG/TD3/SAC are research-grade, VPG/TRPO/PPO are not and the docs say to use OpenAI Baselines for those.
Also fixed: the emitter produced manifests this repo's CI rejects
skill_plugin_emitter.pywrote its wholesourceprovenance block intoplugin.json, on a stale inline claim thatsource/attributionwere "approved extension fields." Claude Code rejects an entire manifest on any unrecognized key (#954) andscripts/check_plugin_json.pyhard-fails such a manifest, pointing at.claude-plugin/authoring-notes.jsoninstead — so every package the emitter produced failed the blocking CI gate the moment it was committed. A defect at the very last step of the pipeline, which is why it had gone unnoticed._plugin_manifest()now emits spec fields only; a new_authoring_notes()writes the sidecar. Recorded as deviation 26 inengineering/book-to-skill/README.md. The printedmarketplace.jsonsnippet is unchanged —sourceis a valid key there, which is how it leaked into the manifest originally.Step 11 of
conversion_workflow.mdnow also states thatattributionis added by hand whenever--rightsis anything butinternal-docs. The emitter knows only--source-notefree text and a rights basis, not an upstream URL/author/licence, and a half-filled attribution block would look authoritative while being wrong.Counters
This branch sits on top of
deep-learning-book(#994), which merged intodevfirst. Derived totals on the merged tree: 388 skills · 99 plugins · 727 tools · 842 references · 118 agents · 150 commands.This plugin's own delta against dev's baseline: skills 387 → 388, agents 117 → 118, commands 149 → 150, plugins 98 → 99. Tools and references unchanged, because a compiled knowledge base ships notes, not scripts.
All five counter surfaces (
README.md,CLAUDE.md,marketplace.json,mkdocs.yml,.codex-plugin/plugin.json) were re-derived from the tree rather than hand-picked from either side of the merge.Checklist
dev(notmain)SKILL.mdwith valid YAML frontmatter (name,description,license) — name + description only, deliberately. book-to-skill hard rule 5 is that generated frontmatter carries those two keys and nothing else, so a compiled skill never widens its own authority;book_skill_validator.pyerrors onallowed-toolsand ondisable-model-invocation: false. The package'splugin.jsonandLICENSEcarry the licensing.--helpwithout errors — no new scripts; the edited emitter passes--helpand--sampleend to endType of Change
plugin.jsonprovenance defect inbook-to-skill's emitter)Testing
Every blocking CI gate re-run locally on the current head (
8b0fca4), all green:compileall(all 17 domains + scripts)check_plugin_json.py --allcheck_skill_names.py --allcheck_paths.py --all(G1)check_frontmatter.py --all(G10)check_dual_publish.py(G4)check_model_freshness.py --all(G7)smoke_scripts.py(G8)derive_counters.py --check(G3)Skill-specific:
book_skill_validator.py <skill> --strict→ passed all checks (frontmatter, safety, budget, index families)token_budget_estimator.py --skill-dir→ every file under cap; core 2,101/4,000skill_plugin_emitter.py --sample→ emits bothplugin.jsonandauthoring-notes.json; the sample manifest passescheck_plugin_json.pywrite-a-skill checklist: 4/6. Item 5 (concrete examples) was a real gap and is fixed — the resident core now carries a worked invocation block. The two remaining are structural to a compiled knowledge base rather than defects: item 2 ("under 100 lines") conflicts with the format's mandatory 20-row Chapter Index plus Topic Index, where the binding budget is the validator's 4,000-token cap and trimming to 100 lines would delete exactly the navigation
budget.over_capexists to protect; item 3 ("no time-sensitive info") fires on "January 2020", which is a provenance pin on a frozen source, not a staleness claim — removing it would make the skill less honest about its coverage.Merge history
This branch was originally cut from
mainwhiledevtrailed it, which made the diff carry unrelated already-merged commits.devhas since caught up and been merged in again to resolve a conflict from #994 (deep-learning-book), which collided on every counter and registry surface. The diff is now clean — 40 files, down from 136 — and reflects only this change.Conflicts were resolved keeping both sides' content: both plugin entries in
marketplace.json, bothUnreleasedsections inCHANGELOG.md, and dev's prose inCLAUDE.md/README.mdwith this branch's engineering-row entry restored on top.Review fixes
Four findings across three review rounds, each verified against the tree before acting:
authoring-notes.jsoncarried only asourceblock, though the content derives from an external MIT workattributionblock (40fa752). The emitter was deliberately not taught to synthesise one — it knows no upstream URL/author/licence, and a half-filled block would look authoritative while being wrong; Step 11 now documents hand-adding it147 → 150, implying three new commands147was the pre-merge baseline; the line was half-rebased. Verified viaderive_counterson anorigin/devworktree (149) and the raw file count (281 → 282). Corrected to149 → 150(3301348)3301348)skill_plugin_emitter.py's docstring still described the behaviour this PR removed;CLAUDE.mdsaid the core is 2,066 tokensauthoring-notes.json, the file this fix creates.2,066predated the added invocation block;CLAUDE.mdcontradicted itself twelve lines apart (8b0fca4)The pattern is the useful takeaway: every finding was prose drifting from a verified tree — a half-rebased delta, a stale docstring, a pre-fix measurement. None were caught by CI, because every gate here reads the tree and none read the prose. That's a real blind spot in an otherwise strict setup; widening it is out of scope for this PR.