Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "idstack",
"version": "3.5.0.0",
"version": "3.5.1.0",
"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.",
"author": {
"name": "Philippos Savvides",
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# smoke-test skips the Chrome-extension and landing-page suites when node is
# absent. Declaring it here keeps that skip from silently hiding a failure on a
# runner image that stops preinstalling node.
- uses: actions/setup-node@v4
with:
# 22, not 20: test-rendered-landing.js drives Chrome over the global WebSocket,
# which node exposes unflagged only from 22.4. On 20 it fails with
# "WebSocket is not defined" and the rendered suite never runs.
node-version: '22'
- name: Smoke test
run: ./test/smoke-test.sh
- name: Integration test
Expand Down Expand Up @@ -60,5 +69,12 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.9'
# Mutations 23-32 are guarded only by the node landing-page suites. Without node,
# smoke-test skips them, those mutations report NOT-GUARDED and this job fails. node is
# a hard requirement here, not a hedge against a silent skip. 22 for the same WebSocket
# reason as the test job; mutation 32 additionally needs Chrome.
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Mutation test
run: ./test/mutation-test.sh
75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,80 @@
# Changelog

## v3.5.1.0 (2026-08-29)

Fixes three defects on idstack.org that only show up on a phone, and repairs the test harness that
was supposed to be catching them. No skill behavior changes.

To update: `cd` into your idstack clone, then `git pull && ./setup`.

### Fixed — every mutation in the suite was passing vacuously

- **`test/mutation-test.sh` reported a perfect score while proving nothing.** Its `fresh()` helper
copies the repo into a throwaway directory per mutation, and `extension/` was not on the copy
list. So `test/test-extension.sh` failed on every copy before any mutation was applied, and
because `expect_fail` only checks that smoke-test exits non-zero, all 36 mutations reported
GUARDED whether or not their guard worked. The suite that exists to prove the other tests work
had been unable to fail since the extension landed.

`extension` is now copied, and a null-mutation control runs first: it aborts the whole suite if
an unmutated copy is already red, because that is the state in which every GUARDED below it is
meaningless. Verified by removing the fix again and watching the control fire.

### Fixed — idstack.org on a phone

- **Every content section sat under the notch.** The responsive pass added `viewport-fit=cover`,
which extends the page into the display cutout, then gave the safe-area inset to the nav, hero
and footer but not to `.section` — which wraps the evidence, pipeline, output, install and
what's-new regions. On a notched phone in landscape the install command ran under the cutout.
The four containers now share one rule, so a fifth cannot be added without the gutter.
- **Touch targets were below the 44px floor, inconsistently.** The page carried three ad-hoc
minimums (36px, 40px, 42px), and the footer controls applied theirs only below 480px, leaving
every width above that — tablets and desktop included — at 36.6px. One `--tap-min` token now
applies at every width.
- **A viewport clip was hiding overflow rather than preventing it.** `overflow-x: clip` on
`html`/`body` suppressed horizontal scrolling, which also meant a real overflow became content
the reader could not reach instead of a visible bug. The grid tracks that actually contain long
install commands now size with `minmax(0, …)`, and the clip is gone: measured clean at 11
viewport widths from 320px up.

### Added — the landing page is now tested by rendering it

- **`test/test-rendered-landing.js`** loads `docs/index.html` in headless Chrome and asserts what
the page does at 11 widths: no sideways scroll, every control at least 44px, the pipeline and
output grids collapsing at their breakpoints, the nav sticky and all its links reachable. It
adds no npm dependency (Chrome over CDP with node's built-in `fetch` and `WebSocket`) and skips
loudly when no browser is present. It does require node 22.4+, which is where the global
`WebSocket` becomes available; CI pins node 22 for exactly this reason.

The existing suite checks CSS as text, which can only forbid the spellings someone thought of.
Six kinds of edit shipped a broken page past it — a selector list, an `:is()` wrapper, an
`@container` wrapper, a print-only media query, a media query nested inside a desktop one, and a
`<style>` block inside an HTML comment. All six fail the rendered suite. Both run: the text one
is fast and names the exact rule, the rendered one cannot be talked around.

### Fixed — the responsive guard had holes in it

- Assertions accepted the regressions they existed to catch: `(clip|hidden)` allowed the
`overflow-x: hidden` that breaks the sticky nav, `grid-template-columns: 1fr` matched the
`1fr 1fr` it forbids, a `--tap-min` existence check passed while a later rule shipped 30px
targets, and a `@media` regex spanned block boundaries so a rule could move to another
breakpoint unnoticed. Each is now pinned and carries a mutation.
- Guards no longer fail correct changes: adding a fifth container to the shared gutter rule, or
reformatting `minmax(0, 1fr)`, used to fail a page that renders identically.

### Changed

- **CI declares its node dependency.** Both jobs install node rather than relying on the runner
image. Without it the landing-page suites skip, which in the mutation job means nine mutations
silently report NOT-GUARDED.
- **Landing page type simplified.** 12 of 19 fluid `font-size: clamp()` declarations varied by at
most 1.12px across their whole range — a fixed value written in three terms — and are now fixed
values. The 7 that move meaningfully stay fluid. `DESIGN.md` records the rule and the 44px
target figure.
- **Documentation corrected** where it disagreed with the code: the smoke-test assertion count,
the suite tables in `CLAUDE.md` and `CONTRIBUTING.md`, and the claim that every suite sources
`test/test-helper.sh` (the two node suites cannot).

## v3.5.0.0 (2026-08-16)

Adds the native Chrome Side Panel extension for Canvas LMS and web course audits, the Course Dossier compiled Markdown export, and resolves open issues in learnings log management and test coverage.
Expand Down
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ bin/package-extension.sh # Package Chrome extension into build
Tests (run in CI on every push and PR — see `.github/workflows/test.yml`):

```bash
./test/smoke-test.sh # 355 assertions: install, SKILL.md freshness, frontmatter, version agreement,
./test/smoke-test.sh # 362 assertions: install, SKILL.md freshness, frontmatter, version agreement,
# canonical section names, /idstack: namespacing, resolve-snippet lockstep, bash -n,
# Claude-Code-only invariant (no dist/, no AGENTS.md, no retired-CLI references)
./test/integration-test.sh # 48 behavioral tests across the bin/ scripts; also proves the suite
./test/integration-test.sh # 51 behavioral tests across the bin/ scripts; also proves the suite
# leaves the working tree untouched
./test/test-setup.sh # 17 behavioral tests for ./setup (flags, scope, legacy cleanup, failure handling)
./test/test-doctor.sh # 13 behavioral tests for bin/idstack-doctor's PROBLEM/WARNING branches
Expand All @@ -40,12 +40,15 @@ Tests (run in CI on every push and PR — see `.github/workflows/test.yml`):
./test/test-plugin-status.sh # bin/lib/plugin-status.sh unit tests
./test/test-preamble-python.sh # Runs the preamble's embedded python on 3.9 and 3.12
./test/test-extension.sh # Chrome Extension packaging and static analysis tests
./test/test-responsive-landing.js # Responsive/mobile invariants for docs/index.html, as CSS text (node; via smoke-test)
./test/test-rendered-landing.js # Renders docs/index.html in headless Chrome and asserts the outcome:
# no sideways scroll, 44px touch targets, column counts (node + Chrome)
python3 test/check-evidence-cards.py . # Verifies landing page evidence cards match evidence/references.md
python3 test/check-doc-accuracy.py . # Verifies documentation accuracy across version strings, binaries, flags, and links
./test/mutation-test.sh # Reintroduces each fixed defect and asserts its guarding test fails
```

`test/test-helper.sh` is not a suite — it is sourced by all of them and owns the shared `PASS`/`FAIL`/`TOTAL` counters and the `check()` assertion. Do not add a local counter block to a suite; smoke-test fails on one, and a mutation proves that guard works.
`test/test-helper.sh` is not a suite — it is sourced by every **bash** suite that runs assertions itself and owns the shared `PASS`/`FAIL`/`TOTAL` counters and the `check()` assertion (`test-extension.sh` is the exception: it delegates to node and asserts nothing of its own). Do not add a local counter block to a bash suite; smoke-test fails on one, and a mutation proves that guard works. The node suites (`test-extension.sh`'s eight unit tests, `test/test-responsive-landing.js`, and `test/test-rendered-landing.js`) cannot source it; they accumulate their own problems and report a count, which is the same contract in another language.

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.

Expand Down
7 changes: 5 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ bin/idstack-gen-skills --dry-run # Generated files up to date? Run this first.
./test/smoke-test.sh # Verify the install
```

Ten suites run in CI (`.github/workflows/test.yml`) on every push and pull request. Run whichever ones your change touches locally; CI runs them all:
Every suite below runs in CI (`.github/workflows/test.yml`) on every push and pull request. Run whichever ones your change touches locally; CI runs them all:

| Suite | Covers |
|-------|--------|
Expand All @@ -140,6 +140,9 @@ Ten suites run in CI (`.github/workflows/test.yml`) on every push and pull reque
| `test/test-version-classifier.sh` | `bin/lib/version-classify.sh` |
| `test/test-plugin-status.sh` | `bin/lib/plugin-status.sh` |
| `test/test-preamble-python.sh` | The preamble's embedded Python, on 3.9 and 3.12 |
| `test/test-extension.sh` | Chrome extension packaging and static analysis, plus eight node unit suites under `test/test-*.js` |
| `test/test-rendered-landing.js` | Renders `docs/index.html` in headless Chrome across 11 widths and asserts the rendered outcome: no horizontal scroll, 44px touch targets, breakpoint column counts, sticky nav. Catches regressions the text suite cannot see (selector lists, `@container`, nested or print-only media queries, a `<style>` inside an HTML comment). Skips loudly with no browser |
| `test/test-responsive-landing.js` | Responsive and mobile-ergonomics invariants for `docs/index.html` — fluid tokens, notch-safe gutters, breakpoint-scoped rules, touch targets. Runs on node, via `smoke-test.sh` |
| `python3 test/check-evidence-cards.py .` | Verifies landing page evidence card study counts and tier ranges against `evidence/references.md` |
| `python3 test/check-doc-accuracy.py .` | Validates version agreement, manifest schema version, binary/flag references, link targets, and surface accuracy across docs |
| `test/mutation-test.sh` | Reintroduces each known defect into a throwaway copy and asserts the guarding test fails. Add a mutation here whenever you fix a bug — it is what proves your new test would have caught it |
Expand All @@ -148,7 +151,7 @@ The CI matrix is ubuntu on Python 3.9 and 3.12, plus macOS on 3.12 for BSD `grep

### Writing a new assertion

`test/test-helper.sh` is sourced by every suite and owns the counters and the assertion:
`test/test-helper.sh` is sourced by every bash suite that runs assertions itself and owns the counters and the assertion (`test-extension.sh` delegates to node and asserts nothing of its own; the node suites cannot source it and accumulate their own problems instead):

```bash
. "$(dirname "$0")/test-helper.sh"
Expand Down
17 changes: 17 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ This file is the source of truth for idstack's visual system. Any visual or UI d
| `small` | 0.80 | 14 | Captions, mono citations, badges |

- **Line heights:** 1.65 body · 1.20 display · 1.40 UI.
- **When to use fluid `clamp()` type (landing page):** only where the size genuinely moves —
a total min→max span of roughly 1.5px or more. Below that the clamp is a three-term
declaration encoding a sub-pixel step, so use a fixed rem value instead. Seven declarations
in `docs/index.html` clear the bar (`.brand`, `.nav-links a`, `.section-header h2`,
`.hero h1`, `.hero .lede`, `.install-track-header h3`, `.install-block code`); the rest are
fixed. The layout tokens `--pad-x` / `--pad-y` stay fluid regardless: 16→32px (fluid across a
457–914px viewport) and 44→88px (800–1600px).

## Color

Expand Down Expand Up @@ -104,6 +111,14 @@ Restrained. Two-color annotation set (rust + prussian blue) for primary marks; t
| `3xl` | 64 | Major page section spacing |
| `4xl` | 96 | Hero spacing |

- **Minimum interactive target (landing page only):** 44px, as `--tap-min` in `docs/index.html`.
44px is where Apple's HIG and WCAG 2.5.5 Target Size (Enhanced) both land; citing it is the source
of the number, not a claim that the page conforms to that AAA criterion. Applies to buttons and
form inputs at every viewport width, not only in stacked mobile layouts. Text links are not sized
to it — measured at 375px they run 18px in prose, 29-32px for nav, footer, `.brand` and
`.cta-secondary`, and 35px for `.skip-link`. They sit in running text where a 44px box would break
the line rhythm, and all clear WCAG 2.5.8 (AA) at 24px or fall under its inline-target exception.

## Layout

- **Approach:** Hybrid. Grid-disciplined for marketing landing; single-column prose for reports.
Expand Down Expand Up @@ -160,3 +175,5 @@ No medium / long durations. No scroll-driven animations. No parallax. No entranc
| 2026-05-13 | Background ivory `#faf8f3` (replaces parchment `#fbfaf6`) | Cleaner publication feel. Parchment-warm read as "old book"; ivory reads as "good paper." |
| 2026-05-13 | Add prussian blue `#1d4a5e` as secondary accent | Two-color annotation set (red + blue) mirrors how academic editors marked manuscripts. |
| 2026-06-12 | Landing reverted to original dark/indigo aesthetic, separate from the report system (owner request) | Reports retain scholarly ivory "Proof" identity. Landing uses indigo→purple gradient accents — a scoped exception to the no-gradient anti-pattern, which still binds reports. |
| 2026-08-29 | Fluid `clamp()` type reserved for sizes that span ≥1.5px | The responsive pass put `clamp()` on 19 landing-page font sizes; 12 of them varied by at most 1.12px across their whole range, i.e. a fixed value written in three terms. Those 12 reverted to fixed rem. Cost: type on phones is up to 1.1px larger than the clamp gave; accepted, it reads slightly better. |
| 2026-08-29 | Minimum interactive target fixed at 44px (`--tap-min`) | The landing page had grown three ad-hoc floors (36/40/42px), and the footer controls applied theirs only below 480px, leaving every width above 480px (desktop included) at 36.6px. One token, applied at every width. Cost: slightly taller controls on desktop; accepted in a product that ships a WCAG review skill. |
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# idstack

> **Status: beta (v3.5.0.0).** Skills work end-to-end and ship behind a versioned plugin, but expect rough edges and breaking changes between minor versions while we shake out the rest of the workflow with real instructional designers. See [CHANGELOG.md](CHANGELOG.md) for what's new in the current release. [Tell us what's broken or missing.](https://forms.gle/6LDgDD1M6WWyYvME8)
> **Status: beta (v3.5.1.0).** Skills work end-to-end and ship behind a versioned plugin, but expect rough edges and breaking changes between minor versions while we shake out the rest of the workflow with real instructional designers. See [CHANGELOG.md](CHANGELOG.md) for what's new in the current release. [Tell us what's broken or missing.](https://forms.gle/6LDgDD1M6WWyYvME8)

Decades of research say elaborated feedback improves learning. Cognitive load theory has been replicated for 30 years. Constructive alignment measurably raises grades. The evidence is strong, but most course design tools don't make it easy to apply.

Expand Down
7 changes: 6 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ What's coming next for idstack. Priorities are shaped by user feedback. [Tell us

## Just shipped

### Mobile fixes for idstack.org & test-harness repair (v3.5.1.0)
- **idstack.org is usable on a phone.** Every content section sat under the display cutout, buttons and inputs fell below the 44px touch floor at every width above 480px, and a viewport clip was hiding horizontal overflow instead of preventing it. Measured clean at 11 widths from 320px up. No skill behavior changed.
- **The mutation suite could not fail.** `test/mutation-test.sh` never copied `extension/` into its throwaway repos, so `test/test-extension.sh` broke before any mutation was applied and all 36 mutations reported GUARDED whether their guard worked or not. A null-mutation control now aborts the run when an unmutated copy is already red.
- **The landing page is tested by rendering it.** `test/test-rendered-landing.js` loads the page in headless Chrome and asserts what it does at 11 widths, not what its CSS says. Six kinds of edit had shipped a broken page past the text-only suite; all six fail this one.

### Chrome Extension, Course Dossier & Robustness (v3.5.0.0)
- **Native Chrome Side Panel extension for Canvas LMS & Google Docs.** Officially published on the [Chrome Web Store](https://chromewebstore.google.com/detail/eclnhfehloplcnidkkopphamllnlhinm). Audits course content and assignments with cognitive demand classification (Bloom's Revised Taxonomy) and empirical evidence citations directly in the browser.
- **Automated background Canvas course crawler.** Audits entire Canvas courses in the background via active session cookies without requiring developer API keys.
Expand Down Expand Up @@ -36,7 +41,7 @@ What's coming next for idstack. Priorities are shaped by user feedback. [Tell us
- **Standalone runs persist.** `bin/idstack-migrate --init` creates a canonical manifest, so a skill run outside the pipeline has something to write into instead of silently discarding its results.

### Test infrastructure and CI (v3.3.0.0–v3.3.0.4, for contributors)
- The suite had never run automatically. GitHub Actions runs every suite on each push and pull request, across ubuntu (Python 3.9 + 3.12) and macOS — eight suites at v3.3.0.0, ten today.
- The suite had never run automatically. GitHub Actions runs every suite on each push and pull request, across ubuntu (Python 3.9 + 3.12) and macOS — eight suites at v3.3.0.0, and more since; `CONTRIBUTING.md` lists the current set.
- `./setup` — the primary deliverable — went from zero coverage to 17 behavioral tests.
- `bin/idstack-doctor` and `bin/idstack-status --readiness` gained their first execution coverage in v3.3.0.4. Both are what a user reaches for when something has already gone wrong, and neither had any.
- `test/mutation-test.sh` reintroduces each fixed defect and asserts its guarding test fails, which is how a test that only appeared to test something gets caught. Every mutation in it is guarded; the suite prints the current count when you run it.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.5.0.0
3.5.1.0
Loading
Loading