Skip to content

Commit 8ef4f00

Browse files
authored
Merge pull request #58 from savvides/worktree-fix-audit-v3.3.0.0
v3.3.0.1 — post-ship docs pass, plus the bare-command bug it found
2 parents 5071975 + 0b4ba51 commit 8ef4f00

36 files changed

Lines changed: 414 additions & 138 deletions

File tree

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "idstack",
3-
"version": "3.3.0.0",
3+
"version": "3.3.0.1",
44
"description": "Evidence-based instructional design pipeline. 11 skills backed by peer-reviewed research across 11 domains. Analyze, design, build, review, and export courses with every recommendation citing its evidence tier.",
55
"author": {
66
"name": "Philippos Savvides",

AGENTS.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ Manifest write rules:
7474
- Use `bin/idstack-manifest-merge` for the write path: section-scoped, atomic (tempfile +
7575
rename), preserves foreign sections, validates against the canonical schema in
7676
`templates/manifest-schema.md`. Inline full-manifest edit is the deprecated fallback.
77+
- **The one documented exception:** `needs-analysis` and `course-import` keep the
78+
Read-modify-Write path because each writes several co-owned sections in one pass, which
79+
whole-section merge cannot express. Both state why inline. Don't "fix" them to use the
80+
merge tool, and don't copy their pattern into a single-section writer.
81+
- Running standalone, call `bin/idstack-migrate --init` before merging. On a missing
82+
manifest plain `idstack-migrate` is a no-op, so the merge that follows exits 4 and the
83+
results are silently never persisted.
7784

7885
Report write rules:
7986

@@ -99,12 +106,34 @@ Every skill template follows this pattern:
99106
2. **`{{PREAMBLE}}`** placeholder (replaced by `templates/preamble.md` during generation)
100107
3. **Workflow** (Markdown defining the conversational flow, decision trees, outputs)
101108
4. **`{{MANIFEST_SCHEMA}}`** placeholder (replaced by `templates/manifest-schema.md`)
102-
5. **Timeline logging** (logs session data to `.idstack/timeline.jsonl` on completion)
109+
5. **`{{IDSTACK_RESOLVE}}`** placeholder (replaced by `templates/snippets/idstack-resolve.sh`).
110+
Unlike the other two it appears many times per template — once at the top of every
111+
bash block that calls `$_IDSTACK/bin/...`. Bash blocks run in separate shells, so
112+
`_IDSTACK` must be re-derived in each. The snippet is the single definition of that
113+
resolution order: `CLAUDE_PLUGIN_ROOT`, `IDSTACK_HOME`, the Codex symlinks, then the
114+
Claude Code marketplace cache. `templates/manifest-schema.md` is spliced verbatim and
115+
writes the resolution out longhand; smoke-test keeps the two in lockstep.
116+
6. **Timeline logging** (logs session data to `.idstack/timeline.jsonl` on completion)
103117

104118
The shared preamble includes: update check, manifest check, preferences check, designer
105119
profile check, and context recovery (reads timeline + learnings for welcome-back messages
106120
and pipeline guidance).
107121

122+
Python embedded in the preamble must parse on Python 3.9 — the version macOS ships.
123+
`test/test-preamble-python.sh` runs every embedded block on 3.9 and 3.12; a syntax error
124+
there dies silently at runtime, which is how context recovery stayed broken for several
125+
releases.
126+
127+
### Shared shell libraries
128+
129+
Logic used by more than one script — or that deserves a unit test — lives in `bin/lib/`
130+
and is sourced by its callers rather than inlined:
131+
132+
- `bin/lib/version-classify.sh` — version comparison, shared by `setup` and `bin/idstack-doctor`
133+
- `bin/lib/plugin-status.sh` — parses `claude plugin list` output into idstack's own entry
134+
135+
Test the shipped file, never a copy.
136+
108137
### Course memory
109138

110139
Skills log session data to `.idstack/timeline.jsonl` (what skills ran, scores,
@@ -139,15 +168,39 @@ takes precedence when tiers conflict.
139168

140169
```bash
141170
./setup # Install for the detected CLI(s)
142-
./test/smoke-test.sh # Verify installation and generated artifacts
171+
./setup --local # Install at project scope instead of user scope
172+
./setup --codex / --no-codex # Force or skip the Codex bundle
173+
./setup --keep-legacy # Leave pre-v2.0.1.0 installs in place
143174
bin/idstack-gen-skills # Regenerate skill files for all targets
144175
bin/idstack-gen-skills --target codex # Regenerate Codex flavor only
145176
bin/idstack-gen-skills --dry-run # Check if generated files are up to date
146177
bin/idstack-doctor # Diagnose installs across CLIs
147178
bin/idstack-status # Course health dashboard (run in a project dir)
148179
bin/idstack-status --readiness # Pre-export readiness check only
180+
bin/idstack-migrate # Migrate .idstack/project.json to the latest schema
181+
bin/idstack-migrate --init # Also create a canonical manifest when none exists
182+
bin/idstack-manifest-merge --section <s> --payload <f> # Canonical manifest write path
183+
bin/idstack-slugify "<project name>" # Derive the <course-slug> for .idstack/exports/
149184
```
150185

186+
Tests — all eight run in CI on every push and PR (`.github/workflows/test.yml`,
187+
ubuntu on Python 3.9 + 3.12, macOS on 3.12):
188+
189+
```bash
190+
./test/smoke-test.sh # Install, SKILL.md freshness, frontmatter, version agreement,
191+
# canonical section names, namespacing, resolve-snippet lockstep
192+
./test/integration-test.sh # End-to-end; proves the suite leaves the working tree untouched
193+
./test/test-setup.sh # ./setup behavior: flags, scope, legacy cleanup, failure handling
194+
./test/test-manifest-merge.sh # bin/idstack-manifest-merge unit tests
195+
./test/test-version-classifier.sh # bin/lib/version-classify.sh unit tests
196+
./test/test-plugin-status.sh # bin/lib/plugin-status.sh unit tests
197+
./test/test-preamble-python.sh # Runs the preamble's embedded python on 3.9 and 3.12
198+
./test/mutation-test.sh # Reintroduces each fixed defect, asserts its guarding test fails
199+
```
200+
201+
Python 3.9 is the oldest interpreter in the field (macOS system python3) and the
202+
leg that catches modern-only syntax reaching the preamble's embedded scripts.
203+
151204
No build step for users. No dependencies beyond bash (python3 recommended for full
152205
features). Skills are plain Markdown files.
153206

CHANGELOG.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
# Changelog
22

3+
## v3.3.0.1 (2026-08-05)
4+
5+
To get this fix: `cd` into your idstack clone, then `git pull && ./setup`. Restart Claude Code afterward — plugins load at session start.
6+
7+
### Fixed — skills no longer suggest commands that don't exist
8+
9+
- **Welcome-back and next-step messages named unrunnable commands.** v3.3.0.0 banned bare `/skill` references because they resolve in neither CLI, and fixed them in `bin/idstack-status`. The guard enforcing it matched only backticked refs, so three plain-prose examples in the preamble's context-recovery section survived — and the preamble is spliced into all 22 skill files. The model copied their shape and told users things like "Based on your progress, /assessment-design is the natural next step." Typing that does nothing. Landing inside the context-recovery message v3.3.0.0 had just repaired made it the first thing a user saw once welcome-back messages started working again. Now namespaced; on Codex the existing translation rule renders them as `$<skill>`.
10+
11+
### For contributors
12+
13+
- The smoke-test guard now matches a bare `/skill` in any command position, not only inside backticks, and scans skill bodies with frontmatter still exempt (`description:` is picker prose, not a command). Suite count unchanged at 371.
14+
- `test/mutation-test.sh` gained a case that reintroduces an unbackticked bare reference into the preamble and asserts smoke-test fails: 14 mutations, 14 guarded.
15+
316
## v3.3.0.0 (2026-08-04)
417

18+
To get these fixes: `cd` into your idstack clone, then `git pull && ./setup`. Restart Claude Code afterward — plugins load at session start.
19+
520
### Fixed — course memory, pipeline orchestration, re-run detection
621

722
A full audit of the toolchain surfaced a set of bugs that broke user-visible behavior, some since their features shipped:
@@ -19,14 +34,14 @@ A full audit of the toolchain surfaced a set of bugs that broke user-visible beh
1934
- course-quality-review, course-export, and learning-objectives now write their manifest sections through `bin/idstack-manifest-merge` (atomic, section-scoped, preserves every other section). needs-analysis and course-import keep the Read-modify-Write path — both write several co-owned sections in one pass, which whole-section merge cannot express — and each now documents why.
2035
- `bin/idstack-migrate --init` creates a canonical manifest with every section at its default. Three skills told the user to run `idstack-migrate` to create one when running standalone; it was a no-op on a missing file, so the merge that followed died with exit 4 and standalone results were silently never persisted. The skeleton comes from running the existing migration chain over a minimal seed, so there is no second definition of "canonical".
2136
- learning-objectives reports gained the required "Top recommendations" section; `[Alignment-1]` is now correctly cited as T5.
22-
- Logic that was duplicated or inlined and therefore untestable now lives in `bin/lib/` and is sourced by its callers: `version-classify.sh` (shared by `setup` and `bin/idstack-doctor`) and `plugin-status.sh` (the `claude plugin list` parser). Their unit tests exercise the shipped code rather than a copy — the version classifier had drifted across three PRs while a mirrored test passed green.
2337

24-
### Infrastructure
38+
### For contributors
2539

2640
The audit found the test suite had never run automatically, and that some of it was not testing what it appeared to test. Both are fixed:
2741

28-
- **CI.** New GitHub Actions `test.yml` runs all seven suites on push and PR (ubuntu + macos, Python 3.9 + 3.12 — 3.9 is the leg that catches the context-recovery class of bug). `release.yml` refuses to publish unless the tag, `VERSION`, `plugin.json`, and `CHANGELOG.md` agree and the smoke test passes.
29-
- **`./setup` is now tested** — 16 behavioral tests covering flag parsing, scope selection, all three legacy-cleanup shapes, and failure handling, run against a repo copy with a fake `$HOME` and a stub `claude`. It previously had no coverage at all while the smoke test spent 14 assertions on landing-page CSS.
42+
- Logic that was duplicated or inlined and therefore untestable now lives in `bin/lib/` and is sourced by its callers: `version-classify.sh` (shared by `setup` and `bin/idstack-doctor`) and `plugin-status.sh` (the `claude plugin list` parser). Their unit tests exercise the shipped code rather than a copy — the version classifier had drifted across three PRs while a mirrored test passed green.
43+
- **CI.** New GitHub Actions `test.yml` runs all eight suites on push and PR — seven in a matrix job (ubuntu + macos, Python 3.9 + 3.12 — 3.9 is the leg that catches the context-recovery class of bug), plus the mutation suite in its own job. `release.yml` refuses to publish unless the tag, `VERSION`, `plugin.json`, and `CHANGELOG.md` agree and the smoke test passes.
44+
- **`./setup` is now tested** — 17 behavioral tests covering flag parsing, scope selection, all three legacy-cleanup shapes, and failure handling, run against a repo copy with a fake `$HOME` and a stub `claude`. It previously had no coverage at all while the smoke test spent 14 assertions on landing-page CSS.
3045
- **A mutation suite proves the guards work.** `test/mutation-test.sh` reintroduces each of the 13 defects fixed here into a throwaway copy and asserts the guarding test fails. This is what was missing: the version-classifier suite passed green while testing a local copy of the classifier rather than the shipped code, and `gen-skills` counted a placeholder-less template as neither generated nor failed.
3146
- smoke-test grew from 272 to 371 assertions (version agreement, canonical section names, `/idstack:` namespacing, resolve-snippet lockstep, v1.1 migration, `bash -n` on every script) and prints failure diagnostics instead of a bare FAIL; integration-test proves it leaves the working tree untouched.
3247

CLAUDE.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## What is idstack
66

7-
An open source set of skills for evidence-based instructional design. Runs in Claude Code and OpenAI Codex CLI (Gemini CLI on the v2.6 roadmap). Each skill is a SKILL.md file that defines a conversational workflow backed by evidence from peer-reviewed research across 11 domains.
7+
An open source set of skills for evidence-based instructional design. Runs in Claude Code and OpenAI Codex CLI (Gemini CLI is on the roadmap, not yet scheduled). Each skill is a SKILL.md file that defines a conversational workflow backed by evidence from peer-reviewed research across 11 domains.
88

99
## Commands
1010

@@ -13,16 +13,36 @@ An open source set of skills for evidence-based instructional design. Runs in Cl
1313
./setup --local # Install at project scope (./.claude/) instead of user scope
1414
./setup --codex # Force-install the Codex bundle even if codex isn't on PATH
1515
./setup --no-codex # Skip the Codex install
16-
./test/smoke-test.sh # Verify installation, SKILL.md files, YAML frontmatter, migrations
16+
./setup --keep-legacy # Leave pre-v2.0.1.0 installs in place instead of removing them
1717
bin/idstack-gen-skills # Regenerate skill files for all targets (claude + codex)
1818
bin/idstack-gen-skills --target claude # Regenerate Claude flavor only (skills/<name>/SKILL.md)
1919
bin/idstack-gen-skills --target codex # Regenerate Codex flavor only (dist/codex/skills/idstack-<name>/)
2020
bin/idstack-gen-skills --dry-run # Check if generated files are up to date
2121
bin/idstack-doctor # Diagnose plugin install (presence, manifest version, legacy-install conflicts)
2222
bin/idstack-status # Course health dashboard (run in a project dir)
2323
bin/idstack-status --readiness # Pre-export readiness check only
24+
bin/idstack-migrate # Migrate an existing .idstack/project.json to the latest schema
25+
bin/idstack-migrate --init # Also create a canonical manifest when none exists (standalone skill runs)
26+
bin/idstack-manifest-merge --section <s> --payload <f> # Canonical manifest write path (atomic, section-scoped)
27+
bin/idstack-slugify "<project name>" # Derive the <course-slug> used for .idstack/exports/
2428
```
2529

30+
Tests (all eight run in CI on every push and PR — see `.github/workflows/test.yml`):
31+
32+
```bash
33+
./test/smoke-test.sh # 371 assertions: install, SKILL.md freshness, frontmatter, version agreement,
34+
# canonical section names, /idstack: namespacing, resolve-snippet lockstep, bash -n
35+
./test/integration-test.sh # End-to-end run; proves the suite leaves the working tree untouched
36+
./test/test-setup.sh # 17 behavioral tests for ./setup (flags, scope, legacy cleanup, failure handling)
37+
./test/test-manifest-merge.sh # bin/idstack-manifest-merge unit tests
38+
./test/test-version-classifier.sh # bin/lib/version-classify.sh unit tests
39+
./test/test-plugin-status.sh # bin/lib/plugin-status.sh unit tests
40+
./test/test-preamble-python.sh # Runs the preamble's embedded python on 3.9 and 3.12
41+
./test/mutation-test.sh # Reintroduces each fixed defect and asserts its guarding test fails
42+
```
43+
44+
CI matrix: ubuntu (Python 3.9 + 3.12) and macOS (3.12). 3.9 is the leg that catches modern-only Python syntax reaching the preamble's embedded scripts — it is what macOS ships. `mutation-test.sh` runs once, pinned to 3.9.
45+
2646
No build step for users. No dependencies beyond bash (python3 recommended for full features). Skills are plain Markdown files.
2747

2848
## Architecture
@@ -70,6 +90,8 @@ Rules for writing the manifest:
7090
- Own your section only. Read full manifest, modify only your skill's section, preserve everything else.
7191
- Update the `updated` timestamp on every write.
7292
- Use `bin/idstack-manifest-merge` for the write path: it's section-scoped, atomic (tempfile + rename), preserves foreign sections, and validates against the canonical schema in `templates/manifest-schema.md`. Inline full-manifest `Edit` is the deprecated fallback only.
93+
- **The one documented exception:** `needs-analysis` and `course-import` keep the Read-modify-Write path because each writes several co-owned sections in one pass, which whole-section merge cannot express. Both state why inline. Don't "fix" them to use the merge tool, and don't copy their pattern into a single-section writer.
94+
- Running standalone, call `bin/idstack-migrate --init` before merging. On a missing manifest plain `idstack-migrate` is a no-op, so the merge that follows exits 4 and the results are silently never persisted.
7395

7496
Rules for writing the report:
7597

@@ -86,10 +108,22 @@ Every skill template follows this pattern:
86108
2. **`{{PREAMBLE}}`** placeholder (replaced by `templates/preamble.md` during generation)
87109
3. **Workflow** (Markdown defining the conversational flow, decision trees, outputs)
88110
4. **`{{MANIFEST_SCHEMA}}`** placeholder (replaced by `templates/manifest-schema.md`)
89-
5. **Timeline logging** (logs session data to `.idstack/timeline.jsonl` on completion)
111+
5. **`{{IDSTACK_RESOLVE}}`** placeholder (replaced by `templates/snippets/idstack-resolve.sh`). Unlike the other two, this one appears many times per template — once at the top of every bash block that calls `$_IDSTACK/bin/...`. Bash blocks run in separate shells, so `_IDSTACK` must be re-derived in each; the snippet is the single definition of that resolution order (`CLAUDE_PLUGIN_ROOT`, `IDSTACK_HOME`, the Codex symlinks, then the Claude Code marketplace cache). `templates/manifest-schema.md` is spliced verbatim and so writes the resolution out longhand — smoke-test keeps the two in lockstep.
112+
6. **Timeline logging** (logs session data to `.idstack/timeline.jsonl` on completion)
90113

91114
The shared preamble includes: interaction conventions (defines `AskUserQuestion` / `Agent` / `Skill` as portable concept names so the same body runs in both CLIs), update check, manifest check, preferences check, designer profile check, and context recovery (reads timeline + learnings for welcome-back messages and pipeline guidance).
92115

116+
Python embedded in the preamble must parse on Python 3.9 — the version macOS ships. `test/test-preamble-python.sh` runs every embedded block on 3.9 and 3.12; a syntax error there dies silently at runtime, which is how context recovery stayed broken for several releases.
117+
118+
### Shared shell libraries
119+
120+
Logic used by more than one script — or that deserves a unit test — lives in `bin/lib/` and is sourced by its callers rather than inlined:
121+
122+
- `bin/lib/version-classify.sh` — version comparison, shared by `setup` and `bin/idstack-doctor`
123+
- `bin/lib/plugin-status.sh` — parses `claude plugin list` output into idstack's own entry
124+
125+
Test the shipped file, never a copy. The version classifier drifted across three PRs while a mirrored copy in its test passed green.
126+
93127
### Course memory
94128

95129
Skills log session data to `.idstack/timeline.jsonl` (what skills ran, scores, dimensions) and `.idstack/learnings.jsonl` (project-specific discoveries). The context recovery preamble reads these on session start to provide continuity across conversations.

0 commit comments

Comments
 (0)