Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
be0670a
test: sweep committed docs/superpowers for retired-CLI claims
Sep 23, 2026
8fdd0c3
test: make mutation-test prove its guards
Sep 23, 2026
7a9c7fd
ci: pin Node 22 wherever the extension suite runs
Sep 23, 2026
1f55d56
test(extension): import the shipped ESM; retire .cjs twins
Sep 23, 2026
4e3d6c5
test(extension): execute service worker, storage and side panel under…
Sep 23, 2026
36fcdc3
fix(extension): crawl every page of assignments and fail loudly
Sep 23, 2026
e70b482
fix(extension): align evidence tiers, citations and severities with r…
Sep 23, 2026
c18e920
fix(extension): refuse empty pages/courses, validate model JSON, boun…
Sep 24, 2026
57e0506
fix(extension): gate SW messages to the panel; drop <all_urls>; read …
Sep 24, 2026
6695795
fix(extension): read every Modules item, refuse student-record pages,…
Sep 24, 2026
4f913f6
fix(extension): label results with the audited page; clear stale stat…
Sep 24, 2026
1924477
docs: make the extension's privacy and capability claims match the code
Sep 24, 2026
f50b6e7
release: v3.5.1.0 (extension 1.1.0)
Sep 24, 2026
ee79db2
Merge origin/main into fix/review-remediation; release as v3.6.0.0
Sep 24, 2026
36823ca
docs: disclose what the Consensus integration sends
Sep 24, 2026
36e5919
test: port useful cases from closed bot PRs #99 #100 #101
Sep 24, 2026
2e7ff46
test: re-anchor mutation 31d on the two-key storage sentence
Sep 24, 2026
2fa2f2e
fix(extension): Retry does not audit a different course than the one …
Sep 24, 2026
2896bf9
test: keep a retired CLI's name out of mutation 30n's comment
Sep 24, 2026
6a6ca78
fix(extension): one Retry guard, keyed on the course that failed
Sep 24, 2026
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.1.0",
"version": "3.6.0.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
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ jobs:
echo "plugin.json version does not match VERSION ($VER)" >&2; exit 1; }
grep -qF "## v$VER" CHANGELOG.md || {
echo "CHANGELOG.md has no entry for v$VER" >&2; exit 1; }
# test/test-extension.sh imports the shipped extension ES modules. extension/ has no
# package.json, so Node must detect ESM syntax in .js files itself (default only from
# 20.19 / 22.7). Pinned so the runner image's Node cannot change under the suite.
# Required here too: smoke-test.sh runs test-extension.sh.
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run test suite
run: ./test/smoke-test.sh
- name: Create GitHub Release
Expand Down
21 changes: 11 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ 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.
# Node 22 is pinned for two suites. test/test-extension.sh imports the shipped extension
# ES modules; extension/ has no package.json, so Node must detect ESM syntax in .js files
# itself (the default only from 20.19 / 22.7). test-rendered-landing.js drives Chrome over
# the global WebSocket, which node exposes unflagged only from 22.4. Declaring node here
# also keeps smoke-test's node-absent SKIP from silently hiding a failure.
- 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
Expand All @@ -54,6 +53,8 @@ jobs:
run: ./test/test-preamble-python.sh
- name: Setup behavioral tests
run: ./test/test-setup.sh
- name: Chrome extension unit tests
run: ./test/test-extension.sh

# Confirms the guards above actually fail when their bugs return. Slower
# (copies the repo per mutation), so it runs once rather than per matrix leg.
Expand All @@ -69,10 +70,10 @@ 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.
# Node 22 for the same two reasons as the test job, and a hard requirement here: fresh()
# copies extension/ and smoke runs test-extension.sh in every case, and the landing-page
# mutations are guarded only by the node suites, so without node they report NOT-GUARDED.
# The rendered-landing mutation additionally needs Chrome.
- uses: actions/setup-node@v4
with:
node-version: '22'
Expand Down
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,70 @@
# Changelog

## v3.6.0.0 (2026-09-24)

Adds an optional Consensus evidence engine to the skills and the Chrome extension, and hardens the extension after a code review of v3.5.0.0. The extension no longer asks for access to every website, refuses Canvas pages that show student records, reads every page of a course's assignments, and its privacy notes now say what is sent to Google and to Consensus. The extension is now version 1.1.0 and needs Chrome 116 or later.

To update: `cd` into your idstack clone, then `git pull && ./setup`. The Chrome Web Store updates the extension on its own once 1.1.0 is published there.

### Added — Consensus evidence engine (bring your own key)

- **`bin/idstack-consensus`** looks up a claim in the Consensus research database and caches every answer under `~/.idstack/cache/consensus/`, so a claim is looked up once. Its subcommands are `status`, `configure`, `query`, `verify`, and `sync`. It reads your own Consensus API key from `CONSENSUS_API_KEY`, `~/.idstack/profile.yaml`, or the project manifest. Without a key it works from `evidence/references.md` alone.
- **An evidence check before findings are written.** `/idstack:course-quality-review`, `/idstack:assessment-design`, and `/idstack:red-team` now pass their findings through `idstack-consensus verify` before writing the report or the manifest. It corrects four known neuromyths (learning styles, left- and right-brain learning, "10% of the brain", and Dale's cone retention percentages), checks each tier against `evidence/references.md`, and attaches literature matches when a key is set.
- **Every skill's preamble reports whether a Consensus key is configured**, and `bin/idstack-doctor` checks the CLI and its cache.
- **The Chrome extension takes an optional Consensus API key in Settings.** With a key, each finding's claim is looked up on Consensus after the audit, and a matched finding shows a Consensus badge and a link to the top paper. The neuromyth corrections apply with or without a key. PRIVACY.md says what is sent and where results are kept.

### Fixed — the extension could read every website

- **v3.5.0.0 injected its page reader into every site you visited.** Chrome's install prompt therefore said it could read your data on all websites, and the same match let the extension's background worker make logged-in requests to any site and read the replies. The reader is now injected only into the tab you are looking at, after you click the idstack toolbar icon. Canvas at `instructure.com` needs no click. A Canvas site on its own domain is requested for that site only, when you first click **Audit Entire Course** there, and Chrome asks you before granting it. The toolbar icon now opens the panel and no longer closes it.
- **The background worker took course-audit requests from any part of the extension, including the page reader it injected into every site, and fetched whatever course address it was given.** Web pages themselves could not message it. It now ignores messages that don't come from the extension's own pages. The course audit accepts only a bare `https` site address and a numeric course id before it requests anything with your Canvas login.

### Fixed — student-record pages, Modules pages, and Google Docs

- **Canvas pages that show student records were read like any other page.** Gradebook and SpeedGrader, grades, People (in a course or the whole account), groups, discussions, submissions, and Inbox are now refused, and the panel says why instead of auditing them. Other Canvas pages no longer fall back to reading the whole page body, and rubric text is now included. On a Canvas site outside `instructure.com`, only numeric `/courses/<n>` pages are read as Canvas; its other pages, such as the dashboard, are read like any web page.
- **A Modules page audited only its first item.** Every module item title is now read, including a course home shown in Modules view.
- **Google Docs were read from the editor's rendered page.** The extension now downloads the document's plain-text export with your Google login, waits at most 10 seconds, accepts only plain text, and says so when a document can't be exported. Published (`/d/e/`) documents are not fetched.

### Fixed — the course audit read at most 50 assignments and hid failures

- **Courses with more than 50 assignments were silently cut short.** The crawler now follows Canvas's pagination to the end, up to 10 pages (500 assignments), and says so when a course has more. A failed request or an unexpected response now stops the audit with an error. It used to become an empty assignment list, so the audit ran as if the course had no assessments.
- **The course prompt could cut an assignment off partway through.** It keeps its 20,000-character budget but fills it with whole assignments, and its header now states how many of the course's items are shown in full.

### Fixed — empty pages, malformed answers, slow requests, and the API key in the URL

- **An empty or unreadable page was still audited.** Pages with fewer than 10 words are refused before anything is sent, with the reason when the extension knows it (for example, a tab it cannot read). A course with no syllabus text and no assignments is refused too. Both checks also apply in demo mode.
- **A malformed answer from the model was saved and could crash the results view.** An answer must contain a list of findings before it is saved or shown.
- **A slow model request had no limit of its own.** It now stops after 25 seconds with a message that says so.
- **Your API key travelled in the request URL.** It is now sent in a request header.

### Fixed — the side panel labelled, kept, or retried the wrong result

- **A result could carry the wrong page's name.** A page audit is now labelled with the page that was sent, not whichever tab is active when the answer arrives, and a course audit is labelled with the course. A slow tab read can no longer overwrite a newer one.
- **After an error, the previous result stayed actionable.** The error card now clears it and hides **Add to Dossier** and **Export .md** until the next successful audit. **Retry** re-runs the audit you started, page or course, instead of always running a page audit. If the tab now shows a different course, or no course, Retry goes back to the ready screen instead of auditing it.
- **A thumbs-up or thumbs-down vote also removed Audit Another Page.** Now only the vote buttons go.
- **The copy buttons did not report a refused clipboard.** One said "Copied!" anyway and the other failed silently. Both now say "Copy failed".
- **The results view crashed on findings that were not a list, on empty entries, or on a tier that was not text.** It now handles all three.

### Fixed — evidence labels disagreed with `evidence/references.md`

- **The extension's tier scale put randomized trials in T2 and called T3 observational.** Its labels now match idstack's scale, T1 meta-analyses and RCTs through T5 expert opinion, and both prompts give the model that scale.
- **Demo findings and the course prompt cited studies idstack does not hold, or held them at the wrong tier.** Biggs (1996) is now `[Alignment-1] [T5]`, not `[Alignment-3]` at T2. The non-existent `[Cognitive-2]` is replaced by `[CogLoad-1]` and `[CogLoad-6]`, which exist. Carpenter (2022), Sweller (2011), and Wood (1976), none of which are in `evidence/references.md`, are replaced by studies that are. An unused list of evidence domains that cited unreferenced studies is removed.
- **The page prompt asked for a `suggestion` severity, which idstack does not use.** It now asks for `critical`, `warning`, or `info`.

### Fixed — the Chrome extension's privacy promises did not match what it does

- **The Settings panel promised that no student data is "ever collected, retained, or used for model training."** None of that held. On a Canvas page the extractor did not recognize, such as Gradebook, People, Inbox, or a discussion, it read the whole page. The panel also points you to a free Google AI Studio key, whose terms let Google use submitted content to improve its products and let human reviewers read it. The extractor now refuses those pages (see above), and the panel says what is sent, to whom, and what Google's free tier allows.
- **"Leave blank to use the built-in free demo tier" described a service that does not exist.** Without a key, every audit returns the same sample findings, whatever the page says. The panel, README, and PRIVACY.md now say so.
- **PRIVACY.md said the API key is stored with `chrome.storage.local`.** The code uses `chrome.storage.sync`, which Chrome copies to your Google account when sync is on. The policy now says that. It also discloses that the last 20 audit results are kept in `chrome.storage.local` and that the side panel loads its fonts from Google Fonts. It no longer claims that the extension never touches rosters or grades, or that its design is FERPA-compliant. The README's "Is my data safe?" answer now points to it for the extension.
- **Correction to the v3.5.0.0 notes.** They said the course crawler audits "syllabi, modules, assignments, discussions, quizzes". It requests the syllabus and assignments only. README repeated the claim, along with a course "dashboard" and a JSON export that do not exist, and is fixed. The v3.5.0.0 entry is left as released.

### For contributors

- **The mutation suite refuses a mutation that changes nothing.** Two cases whose anchors had gone stale kept reporting GUARDED because nothing checked that the mutation had edited anything. The suite now copies the repo once into a snapshot that its baseline smoke run vouches for, and stops on any mutation that leaves the copy unchanged. It grows from 36 cases to 108.
- **The retired-CLI sweep now covers `docs/superpowers/`.** `--exclude-dir=superpowers` matched that name at any depth and exempted every committed spec and plan.
- **The extension tests run the shipped code.** They used to load hand-copied `.cjs` twins, which are gone, and `test-extension.sh` fails if one comes back. `test/extension-harness.mjs` stubs the Chrome APIs and a small DOM, so the tests import the shipped modules and now also run the background worker, storage, and side panel. `test/test-evidence-labels.mjs` checks every citation and tier against `evidence/references.md`. `test/test-disclosures.mjs` checks the side panel, PRIVACY.md, and README against the shipped code.
- **The release workflow pins Node 22**, as the test and mutation jobs already did, and `test-extension.sh` stops with a clear message on a Node too old to import the modules (it needs 20.19+ or 22.7+). The extension suite also has its own CI step, so its full failure output shows.
- **`bin/package-extension.sh` names the zip from `extension/manifest.json`** and leaves the dev-only `icons/generate-icons.js` out of it. The side panel's version badge is tested against the manifest.

## 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
Expand Down
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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 # 362 assertions: install, SKILL.md freshness, frontmatter, version agreement,
./test/smoke-test.sh # 365 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 # 51 behavioral tests across the bin/ scripts; also proves the suite
Expand All @@ -39,7 +39,8 @@ Tests (run in CI on every push and PR — see `.github/workflows/test.yml`):
./test/test-version-classifier.sh # bin/lib/version-classify.sh unit tests
./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-extension.sh # Chrome extension unit tests; import the shipped extension/ modules via
# test/extension-harness.mjs (Node >= 20.19 or 22.7; never add .cjs twins)
./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)
Expand All @@ -48,9 +49,9 @@ python3 test/check-doc-accuracy.py . # Verifies documentation accuracy across
./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 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.
`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). 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 sixteen 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.
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. The test, mutation and release jobs pin Node 22 for the extension and rendered-landing tests.

No build step for users. No dependencies beyond bash (python3 recommended for full features). Skills are plain Markdown files.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Every suite below runs in CI (`.github/workflows/test.yml`) on every push and pu
| `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-extension.sh` | The Chrome extension. Imports the shipped `extension/` modules through `test/extension-harness.mjs` (Node 20.19+ or 22.7+) and fails if a hand-copied `.cjs` twin comes back |
| `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` |
Expand Down
Loading
Loading