From 18057e6cc8b4fb45799b099464c60f1ef47f9317 Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 12:31:39 -0700 Subject: [PATCH 01/10] feat: add the ASD-STE100 writing standard and bin/idstack-ste-check Co-Authored-By: Claude Opus 5.5 --- bin/idstack-ste-check | 563 ++++++++++++++++++++++++++ skills/accessibility-review/SKILL.md | 137 +++++++ skills/assessment-design/SKILL.md | 137 +++++++ skills/course-builder/SKILL.md | 137 +++++++ skills/course-export/SKILL.md | 137 +++++++ skills/course-import/SKILL.md | 137 +++++++ skills/course-quality-review/SKILL.md | 137 +++++++ skills/learn/SKILL.md | 137 +++++++ skills/learning-objectives/SKILL.md | 137 +++++++ skills/needs-analysis/SKILL.md | 137 +++++++ skills/pipeline/SKILL.md | 137 +++++++ skills/red-team/SKILL.md | 137 +++++++ templates/preamble.md | 137 +++++++ test/fixtures/ste/clean-cli.txt | 16 + test/fixtures/ste/clean-module.md | 36 ++ test/fixtures/ste/clean-report.html | 78 ++++ test/mutation-test.sh | 31 ++ test/smoke-test.sh | 8 +- test/test-ste-check.py | 293 ++++++++++++++ 19 files changed, 2668 insertions(+), 1 deletion(-) create mode 100755 bin/idstack-ste-check create mode 100644 test/fixtures/ste/clean-cli.txt create mode 100644 test/fixtures/ste/clean-module.md create mode 100644 test/fixtures/ste/clean-report.html create mode 100644 test/test-ste-check.py diff --git a/bin/idstack-ste-check b/bin/idstack-ste-check new file mode 100755 index 0000000..6b1bdd8 --- /dev/null +++ b/bin/idstack-ste-check @@ -0,0 +1,563 @@ +#!/usr/bin/env python3 +"""Find the ASD-STE100 writing-rule problems that a script can find in idstack output. + +Usage: + idstack-ste-check [--format auto|html|markdown|text] [--word-list PATH] PATH... + +PATH is a file, a directory (all .html, .htm and .md files below it), or "-" for +standard input. The word list comes from the text between the ste-core markers in +templates/preamble.md. + +Rules that this script examines: + sentence-length more than 20 words in a numbered step, more than 25 words in other text + paragraph-length more than 6 sentences in a paragraph + semicolon a semicolon in text + contraction a contraction, for example "don't" + verb-form "has been", "have been" or "had been", also with "not" or an adverb + word a word from the word list + html an HTML element that has no end tag (the text after it is not examined) + +Text that the script does not examine: code, quotations, citations, URLs, placeholders, +HTML script and style, Markdown block quotations and fenced code. Each code span, +quotation, citation, URL and placeholder counts as one word (ASD-STE100 rule 8.6). + +Exit codes: 0 no problems, 1 problems, 2 an error in the command or the word list. +If the script cannot read a file, it examines the other files. Then the exit code is 2 +if it finds no problems. +This script finds only some rule problems. ASD does not endorse idstack. +""" + +import argparse +import html +import os +import re +import sys +from html.parser import HTMLParser + +HERE = os.path.dirname(os.path.realpath(__file__)) +DEFAULT_WORD_LIST = os.path.join(HERE, '..', 'templates', 'preamble.md') +BEGIN_MARKER = '' +END_MARKER = '' + +PROCEDURAL_MAX = 20 +DESCRIPTIVE_MAX = 25 +PARAGRAPH_MAX = 6 + +# One private-use character stands in for each exempt span, so that the span +# counts as one word and is not examined. +SLOT = '\ue000' + +CHECK_SUFFIXES = ('.html', '.htm', '.md') + +CONTRACTION_RES = [ + re.compile(r"\b[A-Za-z]+n['\u2019]t\b", re.I), + re.compile(r"\b[A-Za-z]+['\u2019](?:re|ve|ll|d|m)\b", re.I), + re.compile(r"\b(?!(?-i:IT)['\u2019]s)(?:it|that|there|what|let|here|who|where|how)['\u2019]s\b", re.I), +] +VERB_FORM_RE = re.compile( + r'\b(?:has|have|had|having)(?:\s+(?:not|never|already|also|just|always))?\s+been\b', re.I) + +# Words that end in a period but do not end a sentence. +ABBREVIATIONS = ['e.g.', 'i.e.', 'etc.', 'vs.', 'et al.', 'No.', 'Dr.', 'Mr.', 'Mrs.', + 'Ms.', 'St.', 'Fig.', 'approx.', 'U.S.', 'a.m.', 'p.m.'] +SENTENCE_SPLIT_RE = re.compile('(?<=[.!?])["\'\u201d\u2019)\\]]*\\s+') + +YEAR_RE = re.compile(r'\b(?:1[89]|20)\d\d[a-z]?\b') +# Lowercase words that an author-year citation can contain. +CITATION_WORDS = {'e.g.', 'see', 'also', 'cf.', 'et', 'al.', 'and', 'p.', 'pp.', + 'van', 'von', 'de', 'der', 'den', 'du', 'da', 'di', 'la', 'le'} +PAREN_RE = re.compile(r'\(([^()]*)\)') +EXEMPT_BRACKET_RE = re.compile( + r"\[(?:[A-Z][A-Za-z]*-(?:\d+|N)|[A-Z0-9][A-Z0-9 _'/&.,:-]*|[ x])\]") +INLINE_CODE_RE = re.compile(r'`[^`\n]*`') +# Punctuation at the end of a URL or an identifier stays outside the span, so +# that it still ends a sentence and a semicolon after the span is still found. +SPAN_END = r'(?=[.,;:!?)\]"\'\u2019\u201d]*(?:\s|$))' +URL_RE = re.compile(r'\b(?:https?|ftp|chrome)://\S+?' + SPAN_END + r'|\bwww\.\S+?' + SPAN_END) +TEMPLATE_RE = re.compile(r'\{\{[^{}]*\}\}|\$\{[^{}]*\}') +QUOTE_RE = re.compile('"[^"\\n]{1,300}"|\u201c[^\u201d\\n]{1,300}\u201d') +MD_IMAGE_RE = re.compile(r'!\[([^\]]*)\]\([^)]*\)') +MD_LINK_RE = re.compile(r'\[([^\]]+)\]\([^)]*\)') +IDENTIFIER_RE = re.compile(r'(? 1: + return re.compile(r'\b' + r'\s+'.join(re.escape(p) for p in parts) + r'\b', flags) + forms = [re.escape(word) + '(?:s|es|d|ed|ing)?'] + if word.endswith('e'): + forms.append(re.escape(word[:-1]) + '(?:ing|ed)') + if word.endswith('y') and len(word) > 2 and word[-2] not in 'aeiou': + forms.append(re.escape(word[:-1]) + '(?:ies|ied)') + if re.search(r'[^aeiou][aeiou][^aeiouwxy]$', word): + forms.append(re.escape(word + word[-1]) + '(?:ing|ed)') + forms += {'begin': ['began', 'begun'], 'choose': ['chose', 'chosen']}.get(word.lower(), []) + return re.compile(r'\b(?:' + '|'.join(forms) + r')\b', flags) + + +def load_word_list(path): + try: + with open(path, encoding='utf-8') as handle: + text = handle.read() + except OSError as exc: + raise DataError('cannot read the word list %s: %s' % (path, exc)) + start = text.find(BEGIN_MARKER) + end = text.find(END_MARKER) + if start < 0 or end < start: + raise DataError('the ste-core markers are missing from %s' % path) + words = [] + for line in text[start:end].splitlines(): + match = re.match(r'^\|\s*"([^"]+)"\s*\|([^|]*)\|([^|]*)\|\s*$', line) + if not match: + continue + word = match.group(1).strip() + use = match.group(2).strip().strip('"') + case_sensitive = 'lowercase only' in match.group(3) + words.append((word, use, word_pattern(word, case_sensitive))) + if not words: + raise DataError('the word list in %s has no rows' % path) + return words + + +# --------------------------------------------------------------------------- +# Text preparation +# --------------------------------------------------------------------------- + +def citation_or_text(match): + """Replace a parenthesis that holds only author-year citations, for example + (Mayer, 2019) or (e.g., Paas & van Gog, 2006, p. 3; Mayer, 2021).""" + for part in match.group(1).split(';'): + if not YEAR_RE.search(part): + return match.group(0) + for token in part.replace(',', ' ').split(): + if token[0].islower() and token not in CITATION_WORDS: + return match.group(0) + return SLOT + + +def replace_spans(text): + """Replace the spans that STE counts as one word. Return text and inner sentences.""" + text = INLINE_CODE_RE.sub(SLOT, text) + text = URL_RE.sub(SLOT, text) + text = TEMPLATE_RE.sub(SLOT, text) + text = QUOTE_RE.sub(SLOT, text) + text = EXEMPT_BRACKET_RE.sub(SLOT, text) + text = PAREN_RE.sub(citation_or_text, text) + inner = [] + while True: + found = PAREN_RE.search(text) + if not found: + break + if found.group(1).strip(' ' + SLOT): + inner.append(found.group(1)) + text = text[:found.start()] + SLOT + text[found.end():] + text = IDENTIFIER_RE.sub(SLOT, text) + inner = [IDENTIFIER_RE.sub(SLOT, part) for part in inner] + return text, inner + + +def split_sentences(text): + protected = text + for abbreviation in ABBREVIATIONS: + protected = protected.replace(abbreviation, abbreviation.replace('.', '\ue001')) + parts = SENTENCE_SPLIT_RE.split(protected) + return [part.replace('\ue001', '.').strip() for part in parts if part.strip()] + + +def count_words(sentence): + return len([token for token in sentence.split() if WORD_TOKEN_RE.search(token)]) + + +def excerpt(text): + text = ' '.join(text.replace(SLOT, '[...]').split()) + return text if len(text) <= 70 else text[:67] + '...' + + +def check_unit(unit, words): + problems = [] + text, inner = replace_spans(unit.text) + for part in [text] + inner: + if ';' in part: + problems.append(Problem(unit.line, 'semicolon', + 'write two sentences: "%s"' % excerpt(part))) + for pattern in CONTRACTION_RES: + for match in pattern.finditer(part): + problems.append(Problem(unit.line, 'contraction', + 'write the full words for "%s"' % match.group(0))) + for match in VERB_FORM_RE.finditer(part): + problems.append(Problem(unit.line, 'verb-form', + 'use a simple tense, not "%s"' % match.group(0))) + for word, use, pattern in words: + for match in pattern.finditer(part): + advice = use if use.startswith('(') else 'use "%s"' % use + problems.append(Problem(unit.line, 'word', + '"%s": %s' % (match.group(0), advice))) + if unit.kind == 'label': + return problems + limit = PROCEDURAL_MAX if unit.kind == 'procedural' else DESCRIPTIVE_MAX + sentences = split_sentences(text) + for sentence in sentences + [s for part in inner for s in split_sentences(part)]: + count = count_words(sentence) + if count > limit: + problems.append(Problem(unit.line, 'sentence-length', + '%d words (maximum %d): "%s"' % (count, limit, excerpt(sentence)))) + if unit.paragraph and unit.kind == 'descriptive' and len(sentences) > PARAGRAPH_MAX: + problems.append(Problem(unit.line, 'paragraph-length', + '%d sentences (maximum %d): "%s"' % (len(sentences), PARAGRAPH_MAX, excerpt(text)))) + return problems + + +# --------------------------------------------------------------------------- +# HTML +# --------------------------------------------------------------------------- + +BLOCK_TAGS = {'address', 'article', 'aside', 'body', 'button', 'caption', 'dd', 'details', + 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'html', 'label', + 'legend', 'li', 'main', 'nav', 'ol', 'option', 'p', 'section', 'select', + 'summary', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'ul'} +LABEL_TAGS = {'button', 'caption', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'label', + 'legend', 'option', 'summary', 'th', 'title'} +SKIP_TAGS = {'blockquote', 'pre', 'script', 'style', 'svg', 'template', 'textarea'} +ONE_WORD_TAGS = {'cite', 'code', 'kbd', 'q', 'samp', 'var'} +VOID_TAGS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', + 'source', 'track', 'wbr'} + + +class UnitParser(HTMLParser): + def __init__(self): + HTMLParser.__init__(self, convert_charrefs=True) + self.units = [] + self.stack = [] + self.skip_tag = None + self.skip_depth = 0 + self.skip_line = None + self.buffer = [] + self.buffer_line = None + + def flush(self): + text = ''.join(self.buffer) + if text.strip(' \t\r\n' + SLOT): + block = 'p' + for tag in reversed(self.stack): + if tag in BLOCK_TAGS: + block = tag + break + if block in LABEL_TAGS: + kind = 'label' + elif block in ('li', 'p') and 'li' in self.stack and 'ol' in self.stack: + kind = 'procedural' + else: + kind = 'descriptive' + self.units.append(Unit(self.buffer_line, kind, ' '.join(text.split()), True)) + self.buffer = [] + self.buffer_line = None + + def add(self, text): + if self.buffer_line is None and text.strip(): + self.buffer_line = self.getpos()[0] + self.buffer.append(text) + + def end_inline_skip(self, tag): + # An inline span that is not closed ends at the next block tag. + if self.skip_tag and tag in BLOCK_TAGS and self.skip_tag not in BLOCK_TAGS | SKIP_TAGS: + self.skip_tag = None + + def handle_starttag(self, tag, attrs): + self.end_inline_skip(tag) + if self.skip_tag: + if tag == self.skip_tag: + self.skip_depth += 1 + return + quoted = ('data-ste', 'quoted') in attrs + if tag in BLOCK_TAGS: + self.flush() + if tag in VOID_TAGS: + if tag == 'br': + self.flush() + self.add(' ') + return + if tag in SKIP_TAGS or (quoted and tag in BLOCK_TAGS): + self.skip_tag, self.skip_depth = tag, 1 + self.skip_line = self.getpos()[0] + return + if tag in ONE_WORD_TAGS or quoted: + self.add(' ' + SLOT + ' ') + self.skip_tag, self.skip_depth = tag, 1 + self.skip_line = self.getpos()[0] + return + self.stack.append(tag) + + def handle_startendtag(self, tag, attrs): + if tag == 'br' and not self.skip_tag: + self.flush() + if not self.skip_tag: + self.add(' ') + + def handle_endtag(self, tag): + self.end_inline_skip(tag) + if self.skip_tag: + if tag == self.skip_tag: + self.skip_depth -= 1 + if self.skip_depth == 0: + self.skip_tag = None + return + if tag in BLOCK_TAGS: + self.flush() + if tag in self.stack: + while self.stack: + if self.stack.pop() == tag: + break + + def handle_data(self, data): + if not self.skip_tag: + self.add(data) + + +# --------------------------------------------------------------------------- +# Markdown and plain text +# --------------------------------------------------------------------------- + +LIST_ITEM_RE = re.compile(r'^(\s*)([-*+]|\d+[.)])\s+(.*)$') +TABLE_SEPARATOR_RE = re.compile(r'^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$') +RULE_RE = re.compile(r'^\s*([-*_])(\s*\1){2,}\s*$') + + +def markdown_inline(text): + text = MD_IMAGE_RE.sub(r'\1', text) + text = MD_LINK_RE.sub(r'\1', text) + text = re.sub(r'<(code|kbd|samp|q|cite|var)\b[^>]*>.*?', ' ' + SLOT + ' ', text) + text = re.sub(r'<[^>\n]+>', ' ', text) + text = re.sub(r'(\*\*|__|\*)', '', text) + text = re.sub(r'(?' in line: + in_comment = False + continue + fence = re.match(r'(`{3,}|~{3,})', stripped) + if fence: + close() + current = None + in_fence = fence.group(1) + continue + if stripped.startswith('' not in stripped + continue + if not stripped or RULE_RE.match(line) or stripped.startswith('>'): + close() + current = None + continue + if stripped.startswith('#'): + close() + current = None + units.append(Unit(number, 'label', markdown_inline(stripped.lstrip('#')), False)) + continue + following = lines[index].strip() if index < len(lines) else '' + header = '|' in following and TABLE_SEPARATOR_RE.match(following) + if stripped.startswith('|') or ('|' in stripped and (was_table or header)): + in_table = True + close() + current = None + if TABLE_SEPARATOR_RE.match(stripped): + continue + for cell in stripped.strip('|').split('|'): + if cell.strip(): + units.append(Unit(number, 'label' if header else 'descriptive', + markdown_inline(cell.strip()), False)) + continue + item = LIST_ITEM_RE.match(line) + if item: + close() + kind = 'procedural' if item.group(2)[0].isdigit() else 'descriptive' + current = {'line': number, 'kind': kind, 'text': item.group(3), 'paragraph': True} + elif current is None: + current = {'line': number, 'kind': 'descriptive', 'text': stripped, 'paragraph': True} + else: + current['text'] += ' ' + stripped + if line.endswith(' ') or line.endswith('\\'): + # A hard line break ends the unit, as
does in HTML. + current['text'] = current['text'].rstrip(' \\') + close() + current = {'line': number + 1, 'kind': current['kind'], 'text': '', 'paragraph': True} + close() + return units + + +def text_units(text): + units = [] + for number, line in enumerate(text.splitlines(), 1): + stripped = html.unescape(line.strip()) + if not stripped: + continue + kind = 'procedural' if re.match(r'^\d+[.)]\s', stripped) else 'descriptive' + units.append(Unit(number, kind, stripped, False)) + return units + + +# --------------------------------------------------------------------------- +# Driver +# --------------------------------------------------------------------------- + +def detect_format(path, requested): + if requested != 'auto': + return requested + lower = path.lower() + if lower.endswith('.html') or lower.endswith('.htm'): + return 'html' + if lower.endswith('.md') or lower.endswith('.markdown'): + return 'markdown' + return 'text' + + +def check_text(text, fmt, words): + problems = [] + if fmt == 'html': + parser = UnitParser() + parser.feed(text) + parser.close() + parser.flush() + units = parser.units + if parser.skip_tag: + problems.append(Problem(parser.skip_line, 'html', '<%s> has no end tag. Add the end tag. The checker ' + 'did not examine the text after it.' % parser.skip_tag)) + elif fmt == 'markdown': + units = markdown_units(text) + else: + units = text_units(text) + for unit in units: + problems.extend(check_unit(unit, words)) + return problems + + +def expand(paths): + files = [] + for path in paths: + if path != '-' and os.path.isdir(path): + for root, dirs, names in os.walk(path): + dirs.sort() + for name in sorted(names): + if name.lower().endswith(CHECK_SUFFIXES): + files.append(os.path.join(root, name)) + else: + files.append(path) + return files + + +def main(argv=None): + parser = argparse.ArgumentParser( + prog='idstack-ste-check', + description='Find ASD-STE100 writing-rule problems in idstack output.') + parser.add_argument('paths', nargs='+', metavar='PATH', + help='a file, a directory, or "-" for standard input') + parser.add_argument('--format', default='auto', + choices=['auto', 'html', 'markdown', 'text'], + help='the input format (default: from the file name, text for "-")') + parser.add_argument('--word-list', default=DEFAULT_WORD_LIST, + help='the file with the ste-core word list (default: templates/preamble.md)') + args = parser.parse_args(argv) + + try: + words = load_word_list(args.word_list) + except DataError as exc: + print('idstack-ste-check: error: %s' % exc, file=sys.stderr) + return 2 + + total = 0 + unread = 0 + files = expand(args.paths) + for path in files: + try: + if path == '-': + text = sys.stdin.read() + else: + with open(path, encoding='utf-8-sig') as handle: + text = handle.read() + except (OSError, UnicodeDecodeError) as exc: + print('idstack-ste-check: error: cannot read %s: %s' % (path, exc), file=sys.stderr) + unread += 1 + continue + name = 'stdin' if path == '-' else path + for problem in check_text(text, detect_format(path, args.format), words): + print('%s:%s: %s: %s' % (name, problem.line, problem.rule, problem.message)) + total += 1 + read = len(files) - unread + note = '. The checker did not read %d file(s).' % unread if unread else '' + if total: + print('idstack-ste-check: %d problem(s) in %d file(s)%s' % (total, read, note)) + return 1 + if unread: + if read: + print('idstack-ste-check: no problems in %d file(s)%s' % (read, note)) + return 2 + print('idstack-ste-check: no problems in %d file(s)' % read) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/skills/accessibility-review/SKILL.md b/skills/accessibility-review/SKILL.md index ed7165f..3702641 100644 --- a/skills/accessibility-review/SKILL.md +++ b/skills/accessibility-review/SKILL.md @@ -42,6 +42,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/assessment-design/SKILL.md b/skills/assessment-design/SKILL.md index 6e9e16a..50df89d 100644 --- a/skills/assessment-design/SKILL.md +++ b/skills/assessment-design/SKILL.md @@ -41,6 +41,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/course-builder/SKILL.md b/skills/course-builder/SKILL.md index 2e7e279..6246f57 100644 --- a/skills/course-builder/SKILL.md +++ b/skills/course-builder/SKILL.md @@ -42,6 +42,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/course-export/SKILL.md b/skills/course-export/SKILL.md index 9b5a228..7c58684 100644 --- a/skills/course-export/SKILL.md +++ b/skills/course-export/SKILL.md @@ -42,6 +42,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/course-import/SKILL.md b/skills/course-import/SKILL.md index dc39d92..23bc18d 100644 --- a/skills/course-import/SKILL.md +++ b/skills/course-import/SKILL.md @@ -42,6 +42,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/course-quality-review/SKILL.md b/skills/course-quality-review/SKILL.md index 3da1008..06b8072 100644 --- a/skills/course-quality-review/SKILL.md +++ b/skills/course-quality-review/SKILL.md @@ -42,6 +42,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/learn/SKILL.md b/skills/learn/SKILL.md index 926e917..ea2f22d 100644 --- a/skills/learn/SKILL.md +++ b/skills/learn/SKILL.md @@ -37,6 +37,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/learning-objectives/SKILL.md b/skills/learning-objectives/SKILL.md index c9b862e..69811ba 100644 --- a/skills/learning-objectives/SKILL.md +++ b/skills/learning-objectives/SKILL.md @@ -41,6 +41,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/needs-analysis/SKILL.md b/skills/needs-analysis/SKILL.md index d7e1897..ade560d 100644 --- a/skills/needs-analysis/SKILL.md +++ b/skills/needs-analysis/SKILL.md @@ -41,6 +41,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/pipeline/SKILL.md b/skills/pipeline/SKILL.md index 56185db..5c1c8ec 100644 --- a/skills/pipeline/SKILL.md +++ b/skills/pipeline/SKILL.md @@ -41,6 +41,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/skills/red-team/SKILL.md b/skills/red-team/SKILL.md index 125e3ec..d3c6688 100644 --- a/skills/red-team/SKILL.md +++ b/skills/red-team/SKILL.md @@ -44,6 +44,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/templates/preamble.md b/templates/preamble.md index 50f79b0..abc1ade 100644 --- a/templates/preamble.md +++ b/templates/preamble.md @@ -21,6 +21,143 @@ lean on: These are **directives to the model**, not magic words — interpret them as the protocol above. +## Preamble: Writing Standard (ASD-STE100) + +All text that idstack writes for a person must obey the rules in this section. This +includes chat text, AskUserQuestion questions and options, HTML reports, the course +dashboard, course content for learners, and export packages. `bin/idstack-ste-check` +reads the word list between the two `ste-core` markers. `extension/shared/ste-rules.js` +has a copy of the text between the markers. If you change this text, change the copy at +the same time. + +The rules also apply to the text that idstack writes in `.idstack/project.json` and in +learnings, for example finding text and recommendations. Other skills use this text in +their output. The checker does not read these files. Do not change text that comes from +the course. Do not change JSON keys or the values that a script or a skill reads. + + +Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | | + + +How to use the standard: + +1. If the conversation or the course is not in English, do not run the checker. +2. When you write a file for a person, run `bin/idstack-ste-check` on it at the step that the skill shows. +3. If the checker shows problems, write each sentence that it shows again. Then write the file again and run the checker again. +4. Run the checker a maximum of three times. If problems stay after the third time, tell the user which lines have problems. +5. If the checker does not run, continue the skill. Tell the user one time that the check did not run. + +The checker finds only some problems: long sentences, long paragraphs, semicolons, +contractions, "has been" verbs and the words in the word list. Apply all of the rules +when you write. The verbosity and experience settings change how much you write. They do +not change these rules. + +Identify text from a different source for the checker: +- In HTML, put a short quotation in `` and a long quotation in `
`. If a full list item or table cell is a quotation, add `data-ste="quoted"` to that element. +- In Markdown, put a long quotation on lines that start with `> `. Put a short quotation in quotation marks. +- The name of a standard or a principle can have a word from the word list. Identify that name as a short quotation. Example: `3.3.3 Error Suggestion`. +- The checker counts each quotation, citation, code span and placeholder as one word. + +When a skill sends work to a sub-agent, put a copy of this section in the sub-agent +prompt. The sub-agent cannot see this preamble. + +Example sentences in a skill show the content of a message. Write them in STE when you +use them. + ## Preamble: Update Check ```bash diff --git a/test/fixtures/ste/clean-cli.txt b/test/fixtures/ste/clean-cli.txt new file mode 100644 index 0000000..4d1cc14 --- /dev/null +++ b/test/fixtures/ste/clean-cli.txt @@ -0,0 +1,16 @@ +============================== + idstack course status +============================== +Project: Bob's Advanced Course +Skills completed: + [x] /idstack:needs-analysis (2x) + [ ] /idstack:learning-objectives +Quality trend: 65 -> 85 +Recommended next: /idstack:learning-objectives +Reports (open in a browser for the human view): + .idstack/exports/bobs-advanced-course/index.html <-- course dashboard, start here +─── Course Readiness ─── + Quality Review: READY Score: 80/100 (READY threshold: 70) + Verdict: NOT-READY (quality score 69 < 70) + Fix: claude plugin enable idstack@idstack + claude plugin install idstack@idstack --scope user diff --git a/test/fixtures/ste/clean-module.md b/test/fixtures/ste/clean-module.md new file mode 100644 index 0000000..730fe81 --- /dev/null +++ b/test/fixtures/ste/clean-module.md @@ -0,0 +1,36 @@ +--- +title: Module 3 +--- + +# Module 3: Bias in Data Sets + +## Overview + +This module shows how bias gets into a data set. You will compare two samples and find the differences. The module takes about 3 hours. + +**Learning objectives.** At the end of this module, you can: + +- Identify three sources of sampling bias in a data set. +- Evaluate a data set against the course checklist (Module 2). + +## Activity: Compare two samples + +1. Open the two data files in the module folder. +2. Count the rows in each file. Write the two numbers in your notes. +3. Answer the questions in the worksheet before the live session on May 15. + +> **Academic integrity.** [INSTITUTION ACADEMIC INTEGRITY STATEMENT]. Students should read it; it is the institution's text. + +| Criterion | Excellent (4) | Proficient (3) | Satisfactory (2) | Unsatisfactory (1) | +|---|---|---|---|---| +| Sources of bias | Names three sources and gives an example of each. | Names three sources. | Names two sources. | Names one source or none. | +| Use of evidence | Uses data from both samples, for example row counts and means. | Uses data from one sample. | Uses general statements. | Gives no data. | + +Contact [INSTRUCTOR NAME] at [INSTRUCTOR EMAIL] if you have a question. See the [course site](https://canvas.example.edu/courses/123) for the schedule. + +```bash +# Code is not examined; this line has a semicolon; and it's long and it should not matter at all here. +python3 analyze.py data.csv +``` + + diff --git a/test/fixtures/ste/clean-report.html b/test/fixtures/ste/clean-report.html new file mode 100644 index 0000000..aa5bc35 --- /dev/null +++ b/test/fixtures/ste/clean-report.html @@ -0,0 +1,78 @@ + + + + + Course Quality Review — Data Ethics 101 + + + +
+

idstack · course-quality-review

+

Course Quality Review

+

run 3f2a9c1 · idstack v3.6.0.0

+
+
+

Summary

+

The course has a quality score of 62/100. Three of 8 ILOs have no assessment. The QM review found two critical problems in Module 4. idstack recommends that you add a rubric to the Module 4 project first.

+

Quality 62/100 · Accessibility 71/100 · Confidence 4/5

+
+
+

Findings

+
+

critical T1 Finding alignment-1: Module 4 has no rubric

+
+
What we saw
+
The Module 4 project asks learners to analyze a data set (module-04-project.md), but the course has no rubric for it.
+
What the evidence says
+
Rubrics with clear criteria improve self-assessment and performance (Panadero & Jonsson, 2013; Brookhart, 2018). [Assessment-8] [T1]
+
Why it matters
+
Without a rubric, learners do not know the criteria for a good result. Feedback is then less useful to them.
+
Recommendation
+
idstack recommends that you add a four-level analytic rubric. You can run /idstack:assessment-design to write one.
+
+
+
+

warning Finding cogload-2: Long video segments

+
+
What we saw
+
The course syllabus says: Each lecture video runs about 45 minutes; watch them all before the quiz.
+
What the evidence says
+
Short segments decrease extraneous cognitive load for novice learners, according to Mayer et al. (2019). [CogLoad-4] [CogLoad-19] [T1]
+
Recommendation
+
You can cut each video into parts of 6 to 10 minutes. WCAG 2.1 success criterion 1.2.2 also applies to these videos.
+
+
+
+

Finding access-3: Faculty text

+
+
What we saw
+
Students should utilize the discussion board; posts are graded weekly, and late posts won't count.
+
+
+
+
+

Top recommendations

+
    +
  1. Add a rubric to the Module 4 project. [Assessment-8] [T1]
  2. +
  3. Cut the lecture videos into short segments. [CogLoad-4] [T1]
  4. +
+
+
+

Limitations

+

This review used only the manifest and the files in .idstack/course-content/. It did not examine the LMS gradebook.

+
Quoted policy text from the institution; it is not idstack text, so the checker skips it.
+
+
+

Next steps

+
    +
  1. Run /idstack:assessment-design and write the Module 4 rubric.
  2. +
  3. Run this skill again after you change the course.
  4. +
+
+ + + + diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 935ae4e..6bc212e 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -1550,6 +1550,37 @@ open(p, 'w').write(s) PY expect_fail "a selector list shrinks the touch target past the text guard" "$WORK/r/test/smoke-test.sh" "$WORK/r" +# 43a-43c. The ASD-STE100 checker. Skills trust its exit code to end their rewrite +# loop, so a checker that stops finding a problem lets non-STE text ship silently. +# 43a. The word list in the preamble loses its rows -> test-ste-check must fail. +# An empty list must stop the checker, not turn the word rule off. +fresh +python3 - "$WORK/r/templates/preamble.md" <<'PY' +import re, sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +new = re.sub(r'(?m)^\| "[^"]+" \|.*\n', '', s) +assert new != s, 'no word-list rows found' +open(p, 'w', encoding='utf-8').write(new) +PY +expect_fail "the checker runs with an empty word list" python3 "$WORK/r/test/test-ste-check.py" + +# 43b. The checker stops finding semicolons -> test-ste-check must fail. +fresh +python3 - "$WORK/r/bin/idstack-ste-check" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = " if ';' in part:\n" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, " if '\\x00' in part:\n", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the checker misses a semicolon" python3 "$WORK/r/test/test-ste-check.py" + +# 43c. The description limit drifts from 25 to 26 words -> test-ste-check must fail. +fresh +sed -i.bak 's/^DESCRIPTIVE_MAX = 25$/DESCRIPTIVE_MAX = 26/' "$WORK/r/bin/idstack-ste-check" +expect_fail "the checker accepts a 26-word description" python3 "$WORK/r/test/test-ste-check.py" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/smoke-test.sh b/test/smoke-test.sh index ada06d2..b452dc4 100755 --- a/test/smoke-test.sh +++ b/test/smoke-test.sh @@ -50,7 +50,7 @@ done check "evidence/references.md exists" "[ -f '$IDSTACK_DIR/evidence/references.md' ]" # Check bin scripts exist and are executable -for script in idstack-migrate idstack-timeline-log idstack-learnings-log idstack-learnings-search idstack-learnings-delete idstack-learnings-promote idstack-status idstack-gen-skills idstack-doctor idstack-slugify idstack-update-check idstack-consensus; do +for script in idstack-migrate idstack-timeline-log idstack-learnings-log idstack-learnings-search idstack-learnings-delete idstack-learnings-promote idstack-status idstack-gen-skills idstack-doctor idstack-slugify idstack-update-check idstack-consensus idstack-ste-check; do check "bin/$script exists" "[ -f '$IDSTACK_DIR/bin/$script' ]" check "bin/$script is executable" "[ -x '$IDSTACK_DIR/bin/$script' ]" done @@ -311,6 +311,12 @@ if command -v python3 &>/dev/null; then check "consensus cli unit tests pass" "python3 '$IDSTACK_DIR/test/test-consensus-cli.py'" fi +# The ASD-STE100 checker. Skills run it on each file they write, so a false +# positive costs the user three rewrite passes on every run. +if command -v python3 &>/dev/null; then + check "ste checker unit tests pass" "python3 '$IDSTACK_DIR/test/test-ste-check.py'" +fi + # ./setup is what a new user runs first; it is exercised against a repo copy # with a fake $HOME and a stub `claude`, never the real install. if [ -x "$IDSTACK_DIR/test/test-setup.sh" ]; then diff --git a/test/test-ste-check.py b/test/test-ste-check.py new file mode 100644 index 0000000..404d919 --- /dev/null +++ b/test/test-ste-check.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +"""Unit tests for bin/idstack-ste-check. + +The clean fixtures in test/fixtures/ste/ hold the hard cases of real idstack output: +chained citations, "et al.", WCAG numbers, version strings, file names, placeholders, +HTML entities, quoted faculty text and code. A false positive on these makes each +skill loop three times and then report problems that do not exist. +""" + +import os +import shutil +import subprocess +import sys +import tempfile +import unittest + +REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +CHECKER = os.path.join(REPO_ROOT, 'bin', 'idstack-ste-check') +FIXTURES = os.path.join(REPO_ROOT, 'test', 'fixtures', 'ste') +PREAMBLE = os.path.join(REPO_ROOT, 'templates', 'preamble.md') + + +def run_checker(args, stdin=None): + return subprocess.run([sys.executable, CHECKER] + args, input=stdin, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, cwd=REPO_ROOT) + + +class SteCheckTest(unittest.TestCase): + + def setUp(self): + self.work = tempfile.mkdtemp(prefix='idstack-test-ste-') + + def tearDown(self): + shutil.rmtree(self.work) + + def write(self, name, text): + path = os.path.join(self.work, name) + with open(path, 'w', encoding='utf-8') as handle: + handle.write(text) + return path + + def check_file(self, name, text): + return run_checker([self.write(name, text)]) + + def assert_problem(self, proc, rule): + self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr) + self.assertIn(': %s: ' % rule, proc.stdout) + + def assert_clean(self, proc): + self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr) + + # --- clean input ----------------------------------------------------- + + def test_clean_fixtures_pass(self): + for name in sorted(os.listdir(FIXTURES)): + with self.subTest(fixture=name): + self.assert_clean(run_checker([os.path.join(FIXTURES, name)])) + + def test_writing_standard_section_passes(self): + with open(PREAMBLE, encoding='utf-8') as handle: + text = handle.read() + start = text.index('## Preamble: Writing Standard (ASD-STE100)') + end = text.index('## Preamble:', start + 10) + self.assert_clean(run_checker(['--format', 'markdown', '-'], stdin=text[start:end])) + + def test_directory_checks_html_and_markdown_only(self): + self.write('a.md', 'Do not utilize this.\n') + self.write('b.html', '

Do not utilize this.

\n') + self.write('c.css', '.x { a: b; }\n') + proc = run_checker([self.work]) + self.assertEqual(proc.returncode, 1) + self.assertIn('a.md:1: word:', proc.stdout) + self.assertIn('b.html:1: word:', proc.stdout) + self.assertNotIn('c.css', proc.stdout) + + # --- sentence and paragraph length ------------------------------------- + + def test_description_of_25_words_passes_and_26_fails(self): + words25 = ' '.join(['word'] * 24) + ' end.' + self.assert_clean(self.check_file('a.html', '

%s

' % words25)) + self.assert_problem(self.check_file('b.html', '

more %s

' % words25), 'sentence-length') + + def test_numbered_step_of_20_words_passes_and_21_fails(self): + words20 = ' '.join(['word'] * 19) + ' end.' + self.assert_clean(self.check_file('a.html', '
  1. %s
' % words20)) + self.assert_problem(self.check_file('b.html', '
  1. more %s
' % words20), + 'sentence-length') + self.assert_problem(self.check_file('c.md', '1. more %s\n' % words20), 'sentence-length') + self.assert_problem(self.check_file('d.html', '
  1. Open it.

    more %s

' % words20), + 'sentence-length') + + def test_bullet_item_uses_the_description_limit(self): + words25 = ' '.join(['word'] * 24) + ' end.' + self.assert_clean(self.check_file('a.md', '- %s\n' % words25)) + + def test_citations_and_codes_count_as_one_word(self): + sentence = ' '.join(['word'] * 22) + ' [CogLoad-4] [CogLoad-19] [T1].' + self.assert_clean(self.check_file('a.html', '

%s

' % sentence)) + + def test_text_in_parentheses_is_a_separate_sentence(self): + inner = ' '.join(['word'] * 26) + self.assert_problem(self.check_file('a.md', 'Short host sentence (%s).\n' % inner), + 'sentence-length') + self.assert_clean(self.check_file('b.md', 'Short host sentence (with a short note).\n')) + two = ' '.join(['word'] * 14) + '. ' + ' '.join(['word'] * 13) + '.' + self.assert_clean(self.check_file('c.md', 'Short host sentence (%s).\n' % two)) + + def test_paragraph_of_6_sentences_passes_and_7_fails(self): + six = ' '.join(['This is a sentence.'] * 6) + self.assert_clean(self.check_file('a.html', '

%s

' % six)) + self.assert_problem(self.check_file('b.html', '

%s It has seven.

' % six), + 'paragraph-length') + + def test_sentence_that_starts_in_lowercase_is_a_new_sentence(self): + first = ' '.join(['word'] * 14) + ' end.' + second = "idstack recommends that you add a rubric to each module 'now.' Then stop." + self.assert_clean(self.check_file('a.html', '

%s %s

' % (first, second))) + seven = ' '.join(['idstack found a problem.'] * 7) + self.assert_problem(self.check_file('b.html', '

%s

' % seven), 'paragraph-length') + + def test_problems_in_parentheses_are_found(self): + self.assert_problem(self.check_file('a.md', 'Add a rubric (you should do it now).\n'), 'word') + self.assert_problem(self.check_file('b.md', 'Add a rubric (first this; then that).\n'), 'semicolon') + self.assert_problem(self.check_file('c.md', "Add a rubric (it's late).\n"), 'contraction') + # A year does not make a parenthesis a citation. + self.assert_problem(self.check_file('d.md', 'Submit it (by October 15, 2026; late work loses points).\n'), + 'semicolon') + self.assert_clean(self.check_file('e.md', 'It works (e.g., Paas & van Gog, 2006, p. 3; Mayer, 2021).\n')) + + def test_punctuation_after_a_url_or_identifier_ends_the_sentence(self): + second = ('Three of the eight learning objectives in this course have no matching ' + 'assessment in any of the modules.') + self.assert_clean(self.check_file( + 'a.html', '

The quality score of the course is 62/100. %s

' % second)) + seven = ' '.join(['The score is 62/100.'] * 7) + self.assert_problem(self.check_file('b.html', '

%s

' % seven), 'paragraph-length') + for text in ['The score is 62/100; the target is 70/100.', + 'Open data/x.csv; then save it.', + 'See https://example.edu/syllabus; it has the dates.']: + with self.subTest(text=text): + self.assert_problem(run_checker(['-'], stdin=text + '\n'), 'semicolon') + + def test_line_breaks_end_a_sentence(self): + lines = ['Course: Data Ethics 101', 'Instructor: [NAME]', 'Term: Fall 2026', + 'Prerequisite: STAT 200 or the permission of the instructor', + 'Format: Online, with two live sessions on Zoom'] + self.assert_clean(self.check_file('a.html', '

' + '
'.join(lines) + '

')) + self.assert_clean(self.check_file('b.html', '

' + '
'.join(lines) + '

')) + self.assert_clean(self.check_file('c.md', ' \n'.join(lines) + '\n')) + self.assert_clean(self.check_file('d.md', '\\\n'.join(lines) + '\n')) + step = ' '.join(['word'] * 20) + ' \n ' + ' '.join(['word'] * 21) + '\n' + self.assert_problem(self.check_file('e.md', '1. ' + step), 'sentence-length') + + def test_abbreviations_and_versions_do_not_split_or_merge_sentences(self): + text = '

Mayer et al. (2019) and Sweller et al. found this in v3.6.0.0 and WCAG 1.4.3.

' + self.assert_clean(self.check_file('a.html', text)) + + # --- punctuation, contractions, verb forms ---------------------------- + + def test_semicolon_in_text_fails(self): + self.assert_problem(self.check_file('a.md', 'Write the rubric; then test it.\n'), 'semicolon') + + def test_semicolon_in_code_entity_or_citation_passes(self): + self.assert_clean(self.check_file('a.html', '

Run a; b now.

')) + self.assert_clean(self.check_file('b.html', '

Text & more text.

')) + self.assert_clean(self.check_file('c.md', 'Text & more text.\n')) + self.assert_clean(self.check_file('d.md', 'It works (Pashler et al., 2008; Mayer, 2021).\n')) + + def test_contractions_fail_and_possessives_pass(self): + self.assert_problem(self.check_file('a.md', "Learners don't read it.\n"), 'contraction') + self.assert_problem(self.check_file('b.md', "It's late.\n"), 'contraction') + self.assert_clean(self.check_file('c.md', "Bloom's taxonomy has six levels.\n")) + self.assert_clean(self.check_file('d.md', "Campus IT's help desk supports the LMS.\n")) + + def test_perfect_tense_with_been_fails(self): + self.assert_problem(self.check_file('a.md', 'The rubric has been changed.\n'), 'verb-form') + self.assert_problem(self.check_file('b.md', 'The rubric has not been changed.\n'), 'verb-form') + self.assert_problem(self.check_file('c.md', 'The rubrics have already been changed.\n'), 'verb-form') + + # --- word list -------------------------------------------------------- + + def test_word_list_words_and_forms_fail(self): + for text in ['Utilize the rubric.', 'The rubric is ensured.', 'It is ensuring that.', + 'Do this prior to the quiz.', 'Use a rubric, e.g. this one.', + 'You should add a rubric.', 'It verifies the score.', + 'Start at the beginning.', 'The module began in week 2.', + 'The instructor chose a quiz.']: + with self.subTest(text=text): + self.assert_problem(self.check_file('a.md', text + '\n'), 'word') + + def test_may_is_lowercase_only(self): + self.assert_problem(self.check_file('a.md', 'You may add a rubric.\n'), 'word') + self.assert_clean(self.check_file('b.md', 'The session is on May 15.\n')) + + def test_markdown_emphasis_does_not_hide_words(self): + for text in ['You *should* add a rubric.', 'You **should** add a rubric.', + 'You _should_ add a rubric.']: + with self.subTest(text=text): + self.assert_problem(self.check_file('a.md', text + '\n'), 'word') + self.assert_clean(self.check_file('b.md', 'Set report_path and $_IDSTACK now.\n')) + self.assert_clean(self.check_file('c.md', 'Use $_IDSTACK or should_ now.\n')) + + def test_technical_terms_are_not_in_the_word_list(self): + text = ('Learners analyze, evaluate and create artifacts. The review found a critical ' + 'finding about the evidence and the assessment.\n') + self.assert_clean(self.check_file('a.md', text)) + + # --- quoted text ------------------------------------------------------ + + def test_quoted_text_is_not_examined(self): + self.assert_clean(self.check_file('a.md', 'The syllabus says "you should utilize it; do not wait".\n')) + self.assert_clean(self.check_file('b.html', '

It says you should utilize it; do not wait.

')) + self.assert_clean(self.check_file('c.html', '
You should utilize it; do not wait.
')) + self.assert_clean(self.check_file('d.html', '
  • You should utilize it; wait.
')) + self.assert_clean(self.check_file('e.md', '> You should utilize it; do not wait.\n')) + + def test_text_after_an_open_element_is_examined(self): + bad = '

You should utilize it.

' + self.assert_problem(self.check_file('a.html', '

It says wait.

' + bad), 'word') + self.assert_problem(self.check_file('b.html', '

Run ./setup.

' + bad), 'word') + self.assert_problem(self.check_file('c.html', '

Text.

' + bad), + 'word') + self.assert_problem(self.check_file('d.html', '
Quoted text.' + bad), 'html') + + def test_long_fence_ends_only_at_a_long_fence(self): + text = '````markdown\n```bash\nrm a; rm b\n```\nYou should not see this.\n````\n\nText.\n' + self.assert_clean(self.check_file('a.md', text)) + self.assert_problem(self.check_file('b.md', text + 'You should see this.\n'), 'word') + + def test_markdown_inline_code_and_quotation_are_not_examined(self): + self.assert_clean(self.check_file('a.md', 'Run a; b in the terminal.\n')) + self.assert_clean(self.check_file('b.md', 'The syllabus says you should utilize it; do not wait.\n')) + + def test_markdown_table_without_leading_pipe_is_cells(self): + text = ('Week | Module | Topics\n--- | --- | ---\n' + '1 | Module 1: Introduction to data | Variables and data types\n' + '2 | Module 2: Methods | Loops and functions\n' + '3 | Module 3: Analysis | Data frames and plots\n') + self.assert_clean(self.check_file('a.md', text)) + long = ' '.join(['word'] * 13) + ' A | B ' + ' '.join(['word'] * 13) + '.\n' + self.assert_problem(self.check_file('b.md', '| W | M |\n|---|---|\n| 1 | a |\n\n' + long), 'sentence-length') + self.assert_problem(self.check_file('c.md', long + '---\n'), 'sentence-length') + + def test_bracket_notes_with_words_are_examined(self): + self.assert_problem(self.check_file('a.md', 'Text. [Note: this is a neuromyth; use dual coding.]\n'), + 'semicolon') + + # --- input modes and errors ------------------------------------------- + + def test_stdin_uses_text_mode(self): + proc = run_checker(['-'], stdin=" PROBLEM: it doesn't work.\n") + self.assert_problem(proc, 'contraction') + self.assertIn('stdin:1:', proc.stdout) + + def test_missing_markers_give_exit_2(self): + bad = self.write('preamble.md', '# no markers here\n') + proc = run_checker(['--word-list', bad, '-'], stdin='text\n') + self.assertEqual(proc.returncode, 2) + self.assertIn('markers are missing', proc.stderr) + + def test_empty_word_list_gives_exit_2(self): + bad = self.write('preamble.md', '\nNo table.\n\n') + proc = run_checker(['--word-list', bad, '-'], stdin='text\n') + self.assertEqual(proc.returncode, 2) + self.assertIn('has no rows', proc.stderr) + + def test_missing_file_gives_exit_2(self): + proc = run_checker([os.path.join(self.work, 'nothing.md')]) + self.assertEqual(proc.returncode, 2) + self.assertNotIn('no problems', proc.stdout) + + def test_unreadable_file_does_not_stop_the_other_files(self): + with open(os.path.join(self.work, 'a.md'), 'wb') as handle: + handle.write(b'Caf\xe9 text.\n') + self.write('b.md', 'You should do this.\n') + proc = run_checker([self.work]) + self.assert_problem(proc, 'word') + self.assertIn('b.md:1: word:', proc.stdout) + self.assertIn('did not read 1 file(s)', proc.stdout) + + def test_byte_order_mark_before_front_matter_passes(self): + path = os.path.join(self.work, 'bom.md') + with open(path, 'wb') as handle: + handle.write(b'\xef\xbb\xbf---\ntitle: You should utilize this; ok\n---\n\nThe text is good.\n') + self.assert_clean(run_checker([path])) + + def test_help_exits_0(self): + self.assertEqual(run_checker(['--help']).returncode, 0) + + +if __name__ == '__main__': + unittest.main() From 7e61012cb9f2f2ffebf703f8761bcace577199cf Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 12:52:14 -0700 Subject: [PATCH 02/10] feat(skills): run the STE checker on each file that a skill writes Co-Authored-By: Claude Haiku 4.5 --- README.md | 2 +- skills/accessibility-review/SKILL.md | 30 +++++++++- skills/accessibility-review/SKILL.md.tmpl | 11 ++++ skills/assessment-design/SKILL.md | 30 +++++++++- skills/assessment-design/SKILL.md.tmpl | 11 +++- skills/course-builder/SKILL.md | 60 +++++++++++++++++++- skills/course-builder/SKILL.md.tmpl | 24 +++++++- skills/course-export/SKILL.md | 64 +++++++++++++++++++++- skills/course-export/SKILL.md.tmpl | 28 ++++++++++ skills/course-import/SKILL.md | 28 +++++++++- skills/course-import/SKILL.md.tmpl | 9 +++ skills/course-quality-review/SKILL.md | 30 +++++++++- skills/course-quality-review/SKILL.md.tmpl | 11 ++++ skills/learn/SKILL.md | 26 +++++++++ skills/learn/SKILL.md.tmpl | 9 +++ skills/learning-objectives/SKILL.md | 28 +++++++++- skills/learning-objectives/SKILL.md.tmpl | 9 +++ skills/needs-analysis/SKILL.md | 28 +++++++++- skills/needs-analysis/SKILL.md.tmpl | 9 +++ skills/pipeline/SKILL.md | 33 ++++++++++- skills/pipeline/SKILL.md.tmpl | 16 +++++- skills/red-team/SKILL.md | 56 +++++++++++++++++++ skills/red-team/SKILL.md.tmpl | 22 ++++++++ templates/index.html.tmpl | 10 +++- templates/manifest-schema.md | 2 +- templates/report-format.md | 12 ++-- templates/report.html.tmpl | 4 +- test/mutation-test.sh | 51 +++++++++++++++++ test/smoke-test.sh | 15 +++++ 29 files changed, 639 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 965126b..ae95266 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ When you run `/idstack:course-import`, it creates the manifest with your course ### How idstack reports back to you idstack functions as a design partner rather than an automated content generator. When a skill finishes, it produces two artifacts: -- **Branded HTML report** at `.idstack/exports//.html` — the human view. Open it in any browser. Every finding follows the same structure: *what we saw* in your course, *what the evidence says* (with a citation tag like `[Assessment-8] [T1]`), *why it matters* for learners, and *what to consider* changing. Suggestions, not directives: the designer owns the course, while idstack provides the analytical review. +- **Branded HTML report** at `.idstack/exports//.html` — the human view. Open it in any browser. Every finding follows the same structure: *what we saw* in your course, *what the evidence says* (with a citation tag like `[Assessment-8] [T1]`), *why it matters* for learners, and a *recommendation* for what to change. Recommendations, not orders: the designer owns the course, while idstack provides the analytical review. - **Manifest section** at `.idstack/project.json` — the system view. Same findings in JSON so other skills can read and act on them. The visual contract lives in [`templates/report.html.tmpl`](templates/report.html.tmpl) (the HTML skeleton) and [`templates/assets/idstack.css`](templates/assets/idstack.css) (the branded stylesheet). The content contract is in [`templates/report-format.md`](templates/report-format.md). Re-running a skill overwrites its report; the timeline at `.idstack/timeline.jsonl` carries the run history. diff --git a/skills/accessibility-review/SKILL.md b/skills/accessibility-review/SKILL.md index 3702641..b3d4bef 100644 --- a/skills/accessibility-review/SKILL.md +++ b/skills/accessibility-review/SKILL.md @@ -506,6 +506,8 @@ If you have access to the **Agent tool**, dispatch the WCAG audit and UDL review 2. **UDL 3.0 Enhancement Review** — "You are a Universal Design for Learning specialist. Given this course data: [paste course info from Step 1]. Review against UDL 3.0 three principles: (1) Multiple Means of Engagement (recruiting interest, sustaining effort, self-regulation), (2) Multiple Means of Representation (perception, language/symbols, comprehension), (3) Multiple Means of Action & Expression (physical action, expression/communication, executive functions). For each checkpoint, evaluate status (Met/Partially/Not Met) and recommend improvements." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. + **After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Must Fix" and UDL gaps as "Should Improve". **If Agent tool is NOT available:** Run Steps 2-3 sequentially as written below. @@ -742,6 +744,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** review reads structural metadata, not the actual learner-facing content text; alt-text quality is checked for presence not for descriptive accuracy; UDL recommendations are generated from manifest signals, not from observed learner use. - **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for adversarial persona testing (the persona dimension will simulate learners who depend on the accommodations being audited here). +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest @@ -867,7 +895,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/accessibility-review/SKILL.md.tmpl b/skills/accessibility-review/SKILL.md.tmpl index d2aa89a..878d46a 100644 --- a/skills/accessibility-review/SKILL.md.tmpl +++ b/skills/accessibility-review/SKILL.md.tmpl @@ -96,6 +96,8 @@ If you have access to the **Agent tool**, dispatch the WCAG audit and UDL review 2. **UDL 3.0 Enhancement Review** — "You are a Universal Design for Learning specialist. Given this course data: [paste course info from Step 1]. Review against UDL 3.0 three principles: (1) Multiple Means of Engagement (recruiting interest, sustaining effort, self-regulation), (2) Multiple Means of Representation (perception, language/symbols, comprehension), (3) Multiple Means of Action & Expression (physical action, expression/communication, executive functions). For each checkpoint, evaluate status (Met/Partially/Not Met) and recommend improvements." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. + **After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Must Fix" and UDL gaps as "Should Improve". **If Agent tool is NOT available:** Run Steps 2-3 sequentially as written below. @@ -315,6 +317,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** review reads structural metadata, not the actual learner-facing content text; alt-text quality is checked for presence not for descriptive accuracy; UDL recommendations are generated from manifest signals, not from observed learner use. - **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for adversarial persona testing (the persona dimension will simulate learners who depend on the accommodations being audited here). +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest diff --git a/skills/assessment-design/SKILL.md b/skills/assessment-design/SKILL.md index 50df89d..a000bd1 100644 --- a/skills/assessment-design/SKILL.md +++ b/skills/assessment-design/SKILL.md @@ -603,7 +603,7 @@ ambiguous [Alignment-12] [T2]. 3. **Cross-walk.** Build a `learning_objectives.alignment_matrix.gaps[]` entries for each Bloom-mismatch, untested ILO, or orphaned criterion. Use the canonical `gaps[]` shape: `{ilo, type, description, severity}`. 4. **Recommendations.** Surface the top 1-3 fixes — usually rubric criterion edits, not new assessments. If the user wants to act on them, propose specific edits and apply via `Edit`. Track applied/deferred fixes in a new optional `assessments.audit_notes` array. -When done, write the manifest using `bin/idstack-manifest-merge` (see "Write Manifest" below). Skip the rest of this skill (Steps 1-4 of the design workflow are for design-new mode only). +When done, write the report and do its writing standard check (see the "Generate Report" section). Then write the manifest with `bin/idstack-manifest-merge` (see the "Write Manifest" section). Do not do the remaining steps of this skill (Steps 1-4 of the design workflow are for design-new mode only). --- @@ -944,6 +944,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ In Mode 3 (audit-existing), every finding should reference the audited rubric criterion or assessment item and explicitly state the gap, not propose a wholesale redesign. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest @@ -1118,7 +1144,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/assessment-design/SKILL.md.tmpl b/skills/assessment-design/SKILL.md.tmpl index 0dbde10..6c2ea29 100644 --- a/skills/assessment-design/SKILL.md.tmpl +++ b/skills/assessment-design/SKILL.md.tmpl @@ -193,7 +193,7 @@ ambiguous [Alignment-12] [T2]. 3. **Cross-walk.** Build a `learning_objectives.alignment_matrix.gaps[]` entries for each Bloom-mismatch, untested ILO, or orphaned criterion. Use the canonical `gaps[]` shape: `{ilo, type, description, severity}`. 4. **Recommendations.** Surface the top 1-3 fixes — usually rubric criterion edits, not new assessments. If the user wants to act on them, propose specific edits and apply via `Edit`. Track applied/deferred fixes in a new optional `assessments.audit_notes` array. -When done, write the manifest using `bin/idstack-manifest-merge` (see "Write Manifest" below). Skip the rest of this skill (Steps 1-4 of the design workflow are for design-new mode only). +When done, write the report and do its writing standard check (see the "Generate Report" section). Then write the manifest with `bin/idstack-manifest-merge` (see the "Write Manifest" section). Do not do the remaining steps of this skill (Steps 1-4 of the design workflow are for design-new mode only). --- @@ -500,6 +500,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ In Mode 3 (audit-existing), every finding should reference the audited rubric criterion or assessment item and explicitly state the gap, not propose a wholesale redesign. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest diff --git a/skills/course-builder/SKILL.md b/skills/course-builder/SKILL.md index 6246f57..09cd311 100644 --- a/skills/course-builder/SKILL.md +++ b/skills/course-builder/SKILL.md @@ -598,7 +598,7 @@ In gap-fill mode, **skip Steps 2–7 entirely** (instructor info, syllabus, modu 3. **Generate only what's missing.** For each confirmed target, follow the relevant sub-step from Steps 4–6 below (e.g., generate one rubric, one module page, one quiz set) — but skip the full-course iteration. 4. **Record outputs.** Update `course_content.generated_files` (additive) and `course_content.recommended_generation_targets` (the list, with status `generated | deferred | declined`). -When done, write the manifest via `bin/idstack-manifest-merge` (see Write Manifest below) and skip directly to the final summary. +When done, do the writing standard check at the end of Step 6. In its command, replace `".idstack/course-content"` with the paths of the files that you generated. Put quotation marks around each path, for example `".idstack/course-content/rubrics/rubric-05-discussion.md" ".idstack/course-content/modules/module-05-vision.md"`. If you write the build report (Step 7), also do its check. Then write the manifest through `bin/idstack-manifest-merge` (see Write Manifest) and skip directly to the final summary. Save the chosen mode under `course_content.mode` (`"build-new"` or `"gap-fill"`). @@ -953,6 +953,32 @@ Write files with consistent naming: Confirm each file as it is written. If any write fails, report the error and continue with remaining files. +**Writing standard check (course content).** Run this command on the course content folder. Do the check only on the files that you wrote in this run. If the folder has other files, replace `".idstack/course-content"` with the paths of the files that you wrote. Put each path in quotation marks. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/course-content"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. Show the user each sentence that you changed, before and after the change. + ### Step 7: Generate Build Report Before updating the manifest, generate an HTML build report so the designer can see what was generated and why each design choice was made (or, in gap-fill mode, what was skipped and why). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. @@ -998,6 +1024,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** content adaptation is structural, not voice/style; cognitive-load estimates are heuristic; placeholders are detected by template, not by reading the surrounding paragraph. - **Next steps:** Review and edit the generated files to add your voice and institutional details. Then run `/idstack:course-quality-review` to audit the complete course against QM standards and CoI presence. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 8: Update Manifest Save the `course_content` section to `.idstack/project.json` via `bin/idstack-manifest-merge`. The merge tool replaces only the named section, preserves every other section verbatim, validates JSON, and atomically updates the top-level `updated` timestamp. @@ -1115,11 +1167,15 @@ user, run an adversarial self-review if the **Agent tool** is available. - **Alignment Validator** — "You are an independent alignment reviewer. Read these generated course files: [list file paths in .idstack/course-content/]. Also read the project manifest at .idstack/project.json (specifically the learning_objectives and assessments sections). Verify: (1) Every ILO from the manifest has at least one module that teaches it, (2) Every assessment aligns to a stated ILO at the correct Bloom's level, (3) Module sequencing respects prerequisite chains, (4) No content exceeds cognitive load guidelines (>7 new concepts per module). Report issues found and fixes applied. Be specific with file names and line numbers." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in the agent prompt. The agent cannot see the preamble. Its changes go into the course files. + **After the agent returns:** - If issues were found that can be auto-fixed (e.g., missing ILO reference, wrong Bloom's verb), fix them in the generated files. - Add a "Review: N issues found, M fixed" line to the output summary. - If critical issues remain that require user input, list them in the summary. +If you or the agent changed course files in this step, do the writing standard check at the end of Step 6 again. Show the user each sentence that you changed, before and after the change. + **If Agent tool is NOT available:** Skip this step. Add a note to the output: "Tip: Run `/idstack:course-quality-review` next for a full alignment audit." @@ -1138,7 +1194,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/course-builder/SKILL.md.tmpl b/skills/course-builder/SKILL.md.tmpl index bed3591..6b96c0a 100644 --- a/skills/course-builder/SKILL.md.tmpl +++ b/skills/course-builder/SKILL.md.tmpl @@ -188,7 +188,7 @@ In gap-fill mode, **skip Steps 2–7 entirely** (instructor info, syllabus, modu 3. **Generate only what's missing.** For each confirmed target, follow the relevant sub-step from Steps 4–6 below (e.g., generate one rubric, one module page, one quiz set) — but skip the full-course iteration. 4. **Record outputs.** Update `course_content.generated_files` (additive) and `course_content.recommended_generation_targets` (the list, with status `generated | deferred | declined`). -When done, write the manifest via `bin/idstack-manifest-merge` (see Write Manifest below) and skip directly to the final summary. +When done, do the writing standard check at the end of Step 6. In its command, replace `".idstack/course-content"` with the paths of the files that you generated. Put quotation marks around each path, for example `".idstack/course-content/rubrics/rubric-05-discussion.md" ".idstack/course-content/modules/module-05-vision.md"`. If you write the build report (Step 7), also do its check. Then write the manifest through `bin/idstack-manifest-merge` (see Write Manifest) and skip directly to the final summary. Save the chosen mode under `course_content.mode` (`"build-new"` or `"gap-fill"`). @@ -543,6 +543,15 @@ Write files with consistent naming: Confirm each file as it is written. If any write fails, report the error and continue with remaining files. +**Writing standard check (course content).** Run this command on the course content folder. Do the check only on the files that you wrote in this run. If the folder has other files, replace `".idstack/course-content"` with the paths of the files that you wrote. Put each path in quotation marks. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/course-content"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. Show the user each sentence that you changed, before and after the change. + ### Step 7: Generate Build Report Before updating the manifest, generate an HTML build report so the designer can see what was generated and why each design choice was made (or, in gap-fill mode, what was skipped and why). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. @@ -571,6 +580,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** content adaptation is structural, not voice/style; cognitive-load estimates are heuristic; placeholders are detected by template, not by reading the surrounding paragraph. - **Next steps:** Review and edit the generated files to add your voice and institutional details. Then run `/idstack:course-quality-review` to audit the complete course against QM standards and CoI presence. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 8: Update Manifest Save the `course_content` section to `.idstack/project.json` via `bin/idstack-manifest-merge`. The merge tool replaces only the named section, preserves every other section verbatim, validates JSON, and atomically updates the top-level `updated` timestamp. @@ -671,11 +689,15 @@ user, run an adversarial self-review if the **Agent tool** is available. - **Alignment Validator** — "You are an independent alignment reviewer. Read these generated course files: [list file paths in .idstack/course-content/]. Also read the project manifest at .idstack/project.json (specifically the learning_objectives and assessments sections). Verify: (1) Every ILO from the manifest has at least one module that teaches it, (2) Every assessment aligns to a stated ILO at the correct Bloom's level, (3) Module sequencing respects prerequisite chains, (4) No content exceeds cognitive load guidelines (>7 new concepts per module). Report issues found and fixes applied. Be specific with file names and line numbers." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in the agent prompt. The agent cannot see the preamble. Its changes go into the course files. + **After the agent returns:** - If issues were found that can be auto-fixed (e.g., missing ILO reference, wrong Bloom's verb), fix them in the generated files. - Add a "Review: N issues found, M fixed" line to the output summary. - If critical issues remain that require user input, list them in the summary. +If you or the agent changed course files in this step, do the writing standard check at the end of Step 6 again. Show the user each sentence that you changed, before and after the change. + **If Agent tool is NOT available:** Skip this step. Add a note to the output: "Tip: Run `/idstack:course-quality-review` next for a full alignment audit." diff --git a/skills/course-export/SKILL.md b/skills/course-export/SKILL.md index 7c58684..471cd41 100644 --- a/skills/course-export/SKILL.md +++ b/skills/course-export/SKILL.md @@ -575,6 +575,42 @@ zip the whole folder to hand to a stakeholder. --- +## Writing Standard Check (course content) + +If the course content is not in English, do not run this check. Go to "Export Format Selection". If the course content is in English, run the ASD-STE100 checker on it before you make a package or send content to Canvas. This check only shows problems. Do not change the course files. The designer wrote this text or gave approval to it. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/course-content"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +1. If the output is `STE_CHECK_UNAVAILABLE`, tell the user one time that the check did not run. Continue the export. +2. If the checker shows no problems, continue the export. +3. If the checker shows problems, show them to the user. Then use AskUserQuestion with two alternatives: "Continue the export" and "Stop the export to correct the content". +4. If the user selects "Stop the export to correct the content", stop this skill. Tell the user to correct the files in `.idstack/course-content/` and then run `/idstack:course-export` again. +5. If the checker shows an error, tell the user which files the checker did not read. Then continue with the steps above for the problems that it shows. + +The package files (HTML pages, XML files and quiz text) get no file check. When you write text for the package, for example quiz questions, quiz feedback or item titles, obey the "Writing Standard (ASD-STE100)" section of the preamble. + +--- + ## Export Format Selection Ask the user how they want to export. Use AskUserQuestion: @@ -1310,6 +1346,32 @@ After the export completes (any path), write the HTML report at `$_REPORT_PATH` - **Limitations:** SCORM/IMSCC packages contain static HTML; interactive elements aren't generated. Quiz questions are auto-generated from assessment specs and need designer review. Tokens are never written to disk. - **Next steps:** Verify that the LMS import preserved everything correctly. Pay particular attention to quiz questions, assignment rubrics, discussion prompts, and module sequencing. If the readiness verdict was "with warnings," address the flagged findings before publishing. +**Writing standard check.** In this command, replace `` with the path of `course-export.html` in the folder from the "Course export folder:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Manifest Write @@ -1442,7 +1504,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/course-export/SKILL.md.tmpl b/skills/course-export/SKILL.md.tmpl index 0d550d6..d40e16b 100644 --- a/skills/course-export/SKILL.md.tmpl +++ b/skills/course-export/SKILL.md.tmpl @@ -131,6 +131,25 @@ zip the whole folder to hand to a stakeholder. --- +## Writing Standard Check (course content) + +If the course content is not in English, do not run this check. Go to "Export Format Selection". If the course content is in English, run the ASD-STE100 checker on it before you make a package or send content to Canvas. This check only shows problems. Do not change the course files. The designer wrote this text or gave approval to it. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/course-content"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +1. If the output is `STE_CHECK_UNAVAILABLE`, tell the user one time that the check did not run. Continue the export. +2. If the checker shows no problems, continue the export. +3. If the checker shows problems, show them to the user. Then use AskUserQuestion with two alternatives: "Continue the export" and "Stop the export to correct the content". +4. If the user selects "Stop the export to correct the content", stop this skill. Tell the user to correct the files in `.idstack/course-content/` and then run `/idstack:course-export` again. +5. If the checker shows an error, tell the user which files the checker did not read. Then continue with the steps above for the problems that it shows. + +The package files (HTML pages, XML files and quiz text) get no file check. When you write text for the package, for example quiz questions, quiz feedback or item titles, obey the "Writing Standard (ASD-STE100)" section of the preamble. + +--- + ## Export Format Selection Ask the user how they want to export. Use AskUserQuestion: @@ -866,6 +885,15 @@ After the export completes (any path), write the HTML report at `$_REPORT_PATH` - **Limitations:** SCORM/IMSCC packages contain static HTML; interactive elements aren't generated. Quiz questions are auto-generated from assessment specs and need designer review. Tokens are never written to disk. - **Next steps:** Verify that the LMS import preserved everything correctly. Pay particular attention to quiz questions, assignment rubrics, discussion prompts, and module sequencing. If the readiness verdict was "with warnings," address the flagged findings before publishing. +**Writing standard check.** In this command, replace `` with the path of `course-export.html` in the folder from the "Course export folder:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Manifest Write diff --git a/skills/course-import/SKILL.md b/skills/course-import/SKILL.md index 23bc18d..8a0b14c 100644 --- a/skills/course-import/SKILL.md +++ b/skills/course-import/SKILL.md @@ -1165,6 +1165,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ 2. Run `/idstack:learning-objectives` to verify the Bloom's inference and check bidirectional alignment. 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that can't be extracted from the cartridge. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Step 6: Write Manifest @@ -1262,7 +1288,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/course-import/SKILL.md.tmpl b/skills/course-import/SKILL.md.tmpl index 3faa4c6..08a15d2 100644 --- a/skills/course-import/SKILL.md.tmpl +++ b/skills/course-import/SKILL.md.tmpl @@ -738,6 +738,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ 2. Run `/idstack:learning-objectives` to verify the Bloom's inference and check bidirectional alignment. 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that can't be extracted from the cartridge. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Step 6: Write Manifest diff --git a/skills/course-quality-review/SKILL.md b/skills/course-quality-review/SKILL.md index 06b8072..c3fe955 100644 --- a/skills/course-quality-review/SKILL.md +++ b/skills/course-quality-review/SKILL.md @@ -573,6 +573,8 @@ as parallel subagents after gathering course information (Mode 1/2/3 above). 3. **Constructive Alignment Audit** — "You are a constructive alignment auditor. Given this course data: [paste objectives, assessments, activities]. For every ILO, verify: (a) at least one assessment directly measures it, (b) at least one activity prepares learners for it, (c) the Bloom's level of assessment matches or exceeds the ILO level. Report gaps, orphaned activities, and level mismatches." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. + **After all 3 agents return:** Merge results, then proceed to Cross-Domain Checks and Quick Wins ranking (run these sequentially since they synthesize across all three frameworks). **If Agent tool is NOT available:** Run the three frameworks sequentially as written below. @@ -1285,6 +1287,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** review reads structural metadata, not the actual learner-facing content text; cognitive-load assessments are heuristic; CoI presence is inferred from manifest signals, not surveyed. - **Next steps:** 1–3 specific next actions referencing other idstack skills. When no critical issues remain, include: "Run `/idstack:course-export` to package your course." +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + After writing both the manifest and the quality report, confirm to the user: "Your quality review has been saved to `.idstack/project.json` and a shareable HTML @@ -1316,7 +1344,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/course-quality-review/SKILL.md.tmpl b/skills/course-quality-review/SKILL.md.tmpl index 6109032..127be2d 100644 --- a/skills/course-quality-review/SKILL.md.tmpl +++ b/skills/course-quality-review/SKILL.md.tmpl @@ -163,6 +163,8 @@ as parallel subagents after gathering course information (Mode 1/2/3 above). 3. **Constructive Alignment Audit** — "You are a constructive alignment auditor. Given this course data: [paste objectives, assessments, activities]. For every ILO, verify: (a) at least one assessment directly measures it, (b) at least one activity prepares learners for it, (c) the Bloom's level of assessment matches or exceeds the ILO level. Report gaps, orphaned activities, and level mismatches." +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. + **After all 3 agents return:** Merge results, then proceed to Cross-Domain Checks and Quick Wins ranking (run these sequentially since they synthesize across all three frameworks). **If Agent tool is NOT available:** Run the three frameworks sequentially as written below. @@ -824,6 +826,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Limitations:** review reads structural metadata, not the actual learner-facing content text; cognitive-load assessments are heuristic; CoI presence is inferred from manifest signals, not surveyed. - **Next steps:** 1–3 specific next actions referencing other idstack skills. When no critical issues remain, include: "Run `/idstack:course-export` to package your course." +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + After writing both the manifest and the quality report, confirm to the user: "Your quality review has been saved to `.idstack/project.json` and a shareable HTML diff --git a/skills/learn/SKILL.md b/skills/learn/SKILL.md index ea2f22d..b19c54a 100644 --- a/skills/learn/SKILL.md +++ b/skills/learn/SKILL.md @@ -603,6 +603,32 @@ done Format as markdown with sections grouped by type (operational, pedagogical, etc.) and write to `.idstack/learnings-export.md`. +**Writing standard check.** Run this command on the export file. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/learnings-export.md"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ## Workflow 1. Parse the user's input to determine which command they want diff --git a/skills/learn/SKILL.md.tmpl b/skills/learn/SKILL.md.tmpl index 0bdccab..993cc6b 100644 --- a/skills/learn/SKILL.md.tmpl +++ b/skills/learn/SKILL.md.tmpl @@ -91,6 +91,15 @@ Export all learnings to a markdown file. Format as markdown with sections grouped by type (operational, pedagogical, etc.) and write to `.idstack/learnings-export.md`. +**Writing standard check.** Run this command on the export file. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ".idstack/learnings-export.md"; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ## Workflow 1. Parse the user's input to determine which command they want diff --git a/skills/learning-objectives/SKILL.md b/skills/learning-objectives/SKILL.md index 69811ba..ae96831 100644 --- a/skills/learning-objectives/SKILL.md +++ b/skills/learning-objectives/SKILL.md @@ -768,6 +768,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ Every finding in the HTML must correspond to an entry in `learning_objectives.alignment_matrix.gaps[]` or `learning_objectives.expertise_reversal_flags[]` so downstream skills can read them programmatically. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest @@ -870,7 +896,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/learning-objectives/SKILL.md.tmpl b/skills/learning-objectives/SKILL.md.tmpl index da7ed2b..f729095 100644 --- a/skills/learning-objectives/SKILL.md.tmpl +++ b/skills/learning-objectives/SKILL.md.tmpl @@ -341,6 +341,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ Every finding in the HTML must correspond to an entry in `learning_objectives.alignment_matrix.gaps[]` or `learning_objectives.expertise_reversal_flags[]` so downstream skills can read them programmatically. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + --- ## Write Manifest diff --git a/skills/needs-analysis/SKILL.md b/skills/needs-analysis/SKILL.md index ade560d..1aeb782 100644 --- a/skills/needs-analysis/SKILL.md +++ b/skills/needs-analysis/SKILL.md @@ -773,6 +773,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ Every finding in the HTML must correspond to a finding the manifest's `needs_analysis` section can carry, so downstream skills and `bin/idstack-status` can refer to them programmatically. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 7: Write Manifest Create or update the project manifest. Use the Write tool to write `.idstack/project.json`. @@ -820,7 +846,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/skills/needs-analysis/SKILL.md.tmpl b/skills/needs-analysis/SKILL.md.tmpl index 8198cb9..65347c8 100644 --- a/skills/needs-analysis/SKILL.md.tmpl +++ b/skills/needs-analysis/SKILL.md.tmpl @@ -346,6 +346,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ Every finding in the HTML must correspond to a finding the manifest's `needs_analysis` section can carry, so downstream skills and `bin/idstack-status` can refer to them programmatically. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 7: Write Manifest Create or update the project manifest. Use the Write tool to write `.idstack/project.json`. diff --git a/skills/pipeline/SKILL.md b/skills/pipeline/SKILL.md index 5c1c8ec..23bdd1f 100644 --- a/skills/pipeline/SKILL.md +++ b/skills/pipeline/SKILL.md @@ -602,15 +602,44 @@ Also read `.idstack/project.json` for project_name, scores (`quality_review.over - **`{{project_name}}`:** from the manifest top-level. - **`{{pipeline_run_status}}`:** e.g., "complete · 8 of 8 skills" or "partial · 4 of 8 skills". -- **Cross-cutting summary** (replaces `{{cross_cutting_summary_2_to_3_paragraphs}}`): 2–3 paragraphs of cross-cutting synthesis. Designed to be read by a designer (or stakeholder) who hasn't yet opened the per-skill reports. Lead with the verdict, follow with the themes that recur across multiple skills, end with where to start. +- **Cross-cutting summary** (replaces `{{cross_cutting_verdict_paragraph}}`, `{{cross_cutting_themes_paragraph}}` and `{{cross_cutting_start_paragraph}}`): two or three paragraphs of cross-cutting synthesis for a designer or stakeholder who did not open the per-skill reports. Put each paragraph in a different `

`, with a maximum of six sentences. + - The first paragraph gives the verdict. + - The second paragraph gives the themes that occur in more than one skill. + - The third paragraph tells where to start. It is optional. If you do not use it, remove its `

`. - **Readiness scoreboard** — populate the four `

` cards (Quality, Accessibility, Red-team confidence, Overall verdict). Verdict values: `ready` (green border) / `issues` (amber) / `blocked` (red) / `pending` (grey, when skill hasn't run yet). Thresholds match `bin/idstack-status --readiness`. - **Pipeline status table** — one row per pipeline skill. `status-done` if the per-skill HTML report file exists, `status-pending` otherwise. `signal` column carries a 1-line skill-specific summary (e.g., for course-quality-review: `overall_score/100`; for red-team: `confidence_score/100, N critical`). Link the report cell to the relative HTML filename (e.g., `needs-analysis.html`). Add an optional course-import row when that report is present. - **Top cross-cutting issues** (`
    ` items): 3–5 highest-impact findings that appear in or affect multiple per-skill reports. Each item shows the source skill(s) and the evidence tier as a `` element. -- **LMS export artifacts** — list every file present at the top level of `$_EXPORT_DIR/` whose name matches a known LMS package (`course-export.imscc`, `scorm-export.zip`). If none, write: `

    No LMS packages produced yet. Run /idstack:course-export to package this course.

    `. +- **LMS export artifacts** — list every file present at the top level of `$_EXPORT_DIR/` whose name matches a known LMS package (`course-export.imscc`, `scorm-export.zip`). If none, write: `

    This folder has no LMS packages. To make them, run /idstack:course-export.

    `. - **Where to start** — one paragraph. The single change (or 2–3 changes) that would address the largest number of cross-cutting concerns. Anchored to a specific finding id in a specific per-skill report so the designer can act on it (e.g., "Address `assess-1` in assessment-design.html first…"). The dashboard is overwritten on every pipeline run — historical dashboards are not retained. The timeline at `.idstack/timeline.jsonl` carries the run history. +**Writing standard check.** In this command, replace `` with the path from the "Dashboard:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 5: Pipeline Complete When all remaining skills have been executed: diff --git a/skills/pipeline/SKILL.md.tmpl b/skills/pipeline/SKILL.md.tmpl index 46797dc..50ea25c 100644 --- a/skills/pipeline/SKILL.md.tmpl +++ b/skills/pipeline/SKILL.md.tmpl @@ -175,15 +175,27 @@ Also read `.idstack/project.json` for project_name, scores (`quality_review.over - **`{{project_name}}`:** from the manifest top-level. - **`{{pipeline_run_status}}`:** e.g., "complete · 8 of 8 skills" or "partial · 4 of 8 skills". -- **Cross-cutting summary** (replaces `{{cross_cutting_summary_2_to_3_paragraphs}}`): 2–3 paragraphs of cross-cutting synthesis. Designed to be read by a designer (or stakeholder) who hasn't yet opened the per-skill reports. Lead with the verdict, follow with the themes that recur across multiple skills, end with where to start. +- **Cross-cutting summary** (replaces `{{cross_cutting_verdict_paragraph}}`, `{{cross_cutting_themes_paragraph}}` and `{{cross_cutting_start_paragraph}}`): two or three paragraphs of cross-cutting synthesis for a designer or stakeholder who did not open the per-skill reports. Put each paragraph in a different `

    `, with a maximum of six sentences. + - The first paragraph gives the verdict. + - The second paragraph gives the themes that occur in more than one skill. + - The third paragraph tells where to start. It is optional. If you do not use it, remove its `

    `. - **Readiness scoreboard** — populate the four `

    ` cards (Quality, Accessibility, Red-team confidence, Overall verdict). Verdict values: `ready` (green border) / `issues` (amber) / `blocked` (red) / `pending` (grey, when skill hasn't run yet). Thresholds match `bin/idstack-status --readiness`. - **Pipeline status table** — one row per pipeline skill. `status-done` if the per-skill HTML report file exists, `status-pending` otherwise. `signal` column carries a 1-line skill-specific summary (e.g., for course-quality-review: `overall_score/100`; for red-team: `confidence_score/100, N critical`). Link the report cell to the relative HTML filename (e.g., `needs-analysis.html`). Add an optional course-import row when that report is present. - **Top cross-cutting issues** (`
      ` items): 3–5 highest-impact findings that appear in or affect multiple per-skill reports. Each item shows the source skill(s) and the evidence tier as a `` element. -- **LMS export artifacts** — list every file present at the top level of `$_EXPORT_DIR/` whose name matches a known LMS package (`course-export.imscc`, `scorm-export.zip`). If none, write: `

      No LMS packages produced yet. Run /idstack:course-export to package this course.

      `. +- **LMS export artifacts** — list every file present at the top level of `$_EXPORT_DIR/` whose name matches a known LMS package (`course-export.imscc`, `scorm-export.zip`). If none, write: `

      This folder has no LMS packages. To make them, run /idstack:course-export.

      `. - **Where to start** — one paragraph. The single change (or 2–3 changes) that would address the largest number of cross-cutting concerns. Anchored to a specific finding id in a specific per-skill report so the designer can act on it (e.g., "Address `assess-1` in assessment-design.html first…"). The dashboard is overwritten on every pipeline run — historical dashboards are not retained. The timeline at `.idstack/timeline.jsonl` carries the run history. +**Writing standard check.** In this command, replace `` with the path from the "Dashboard:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Do the check a maximum of three times. + ### Step 5: Pipeline Complete When all remaining skills have been executed: diff --git a/skills/red-team/SKILL.md b/skills/red-team/SKILL.md index d3c6688..25f58d4 100644 --- a/skills/red-team/SKILL.md +++ b/skills/red-team/SKILL.md @@ -498,6 +498,8 @@ Use the **Agent tool** with `subagent_type=general-purpose`. The prompt is the f - `{{MANIFEST_INFO}}` → either `Manifest at .idstack/project.json — read it directly.` or, in standalone mode, the captured course information from Step 1 - `{{COURSE_FILES_HINT}}` → if the manifest has `course_content.content_dir`, set this to that path; otherwise `Look under ./course/ or ./modules/ for generated course files.` +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble at the end of the prompt. The orchestrator cannot see the preamble. + Then call Agent. Block on its return. @@ -535,6 +537,8 @@ If you have access to the **Agent tool**, dispatch the 5 dimensions in parallel If you do NOT have Agent access, run the dimensions sequentially using the same briefs. +The "Writing Standard (ASD-STE100)" section is at the end of this prompt. Put a copy of it in each sub-agent prompt. The sub-agents cannot see it. + --- ## Dimension Briefs @@ -797,6 +801,32 @@ Write the HTML report at the path printed above (`.idstack/exports/ Each finding **must** have a stable id of the form `-` (e.g., `alignment-1`, `cogload-3`) so the parent can reference findings when applying fixes. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section at the end of this prompt. Do the check a maximum of three times. If these steps tell you to tell the user something, put it in your executive summary. + ## Return value After writing the report, return ONLY a short executive summary (≤200 words) to the parent: @@ -843,6 +873,32 @@ Do not spawn additional sub-agents for fixes. The parent has the relevant contex If the user pushes back on any specific fix, mark it deferred and continue. +**Writing standard check.** Run this command one time for each course file that you edited in this step. Replace `` with the path of the file. Do not run it on `.idstack/project.json`. + +```bash +# Resolve the idstack install dir. Re-derived at the top of every bash block — +# blocks run in separate shells, so a value derived in an earlier block is not +# available here. Priority: explicit env overrides, then the Claude Code +# marketplace cache. Empty if none found; guard "$_IDSTACK/bin/..." calls +# accordingly. +_IDSTACK="" +# Marketplace cache holds one dir per installed version. Sort the basenames by +# numeric version fields, not lexically — plain sort ranks 3.9.0.0 above +# 3.10.0.0 and would pick a stale install once the minor hits double digits. +_idstack_cache_root="$HOME/.claude/plugins/cache/idstack/idstack" +_idstack_cache="" +if [ -d "$_idstack_cache_root" ]; then + _idstack_v=$(ls "$_idstack_cache_root" 2>/dev/null | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1) + [ -n "$_idstack_v" ] && _idstack_cache="$_idstack_cache_root/$_idstack_v" +fi +for _p in "${CLAUDE_PLUGIN_ROOT:-}" "${IDSTACK_HOME:-}" "$_idstack_cache"; do + if [ -n "$_p" ] && [ -d "$_p" ]; then _IDSTACK="${_p%/}"; break; fi +done +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Change only the sentences that you wrote or changed in this step. Do not change the other text in the file. Do the check a maximum of three times. If the checker shows problems only in sentences that you did not write or change, the check of that file is complete. Tell the user one time that these lines had problems before your changes. + ### Step 6: Update manifest Save results to `.idstack/project.json` via `bin/idstack-manifest-merge`, which replaces only diff --git a/skills/red-team/SKILL.md.tmpl b/skills/red-team/SKILL.md.tmpl index 00de60d..30f709a 100644 --- a/skills/red-team/SKILL.md.tmpl +++ b/skills/red-team/SKILL.md.tmpl @@ -88,6 +88,8 @@ Use the **Agent tool** with `subagent_type=general-purpose`. The prompt is the f - `{{MANIFEST_INFO}}` → either `Manifest at .idstack/project.json — read it directly.` or, in standalone mode, the captured course information from Step 1 - `{{COURSE_FILES_HINT}}` → if the manifest has `course_content.content_dir`, set this to that path; otherwise `Look under ./course/ or ./modules/ for generated course files.` +Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble at the end of the prompt. The orchestrator cannot see the preamble. + Then call Agent. Block on its return. @@ -125,6 +127,8 @@ If you have access to the **Agent tool**, dispatch the 5 dimensions in parallel If you do NOT have Agent access, run the dimensions sequentially using the same briefs. +The "Writing Standard (ASD-STE100)" section is at the end of this prompt. Put a copy of it in each sub-agent prompt. The sub-agents cannot see it. + --- ## Dimension Briefs @@ -353,6 +357,15 @@ Write the HTML report at the path printed above (`.idstack/exports/ Each finding **must** have a stable id of the form `-` (e.g., `alignment-1`, `cogload-3`) so the parent can reference findings when applying fixes. +**Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section at the end of this prompt. Do the check a maximum of three times. If these steps tell you to tell the user something, put it in your executive summary. + ## Return value After writing the report, return ONLY a short executive summary (≤200 words) to the parent: @@ -399,6 +412,15 @@ Do not spawn additional sub-agents for fixes. The parent has the relevant contex If the user pushes back on any specific fix, mark it deferred and continue. +**Writing standard check.** Run this command one time for each course file that you edited in this step. Replace `` with the path of the file. Do not run it on `.idstack/project.json`. + +```bash +{{IDSTACK_RESOLVE}} +if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ""; else echo "STE_CHECK_UNAVAILABLE"; fi +``` + +For the result, do the steps in "How to use the standard" in the "Writing Standard (ASD-STE100)" section of the preamble. Change only the sentences that you wrote or changed in this step. Do not change the other text in the file. Do the check a maximum of three times. If the checker shows problems only in sentences that you did not write or change, the check of that file is complete. Tell the user one time that these lines had problems before your changes. + ### Step 6: Update manifest Save results to `.idstack/project.json` via `bin/idstack-manifest-merge`, which replaces only diff --git a/templates/index.html.tmpl b/templates/index.html.tmpl index cc66972..253904b 100644 --- a/templates/index.html.tmpl +++ b/templates/index.html.tmpl @@ -37,7 +37,11 @@

      Across your course

      -

      {{cross_cutting_summary_2_to_3_paragraphs}}

      + +

      {{cross_cutting_verdict_paragraph}}

      +

      {{cross_cutting_themes_paragraph}}

      +

      {{cross_cutting_start_paragraph}}

      @@ -140,7 +144,7 @@

      Top cross-cutting issues

        -
      1. {{issue_title}} — appears in {{skill_list}}. [{{domain}}-{{n}}] [{{tier}}]
        {{one_line_why}}
      2. +
      3. {{issue_title}} — found in {{skill_list}}.
        {{one_line_why}} [{{domain}}-{{n}}] [{{tier}}]
      @@ -148,7 +152,7 @@

      LMS export artifacts

      • course-export.imscc — IMS Common Cartridge (Canvas, Blackboard, Moodle, D2L)
      • diff --git a/templates/manifest-schema.md b/templates/manifest-schema.md index 244fb29..4cde237 100644 --- a/templates/manifest-schema.md +++ b/templates/manifest-schema.md @@ -11,7 +11,7 @@ Every skill that produces findings emits **both**: - a **JSON section** in this manifest (system state — read by other skills, the pipeline orchestrator, and `bin/idstack-status`), and - an **HTML report** at `.idstack/exports//.html` (the human view — read by the instructional designer). -The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → suggestion, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) +The HTML report follows the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md` (observation → evidence → why-it-matters → recommendation, with severity and evidence tier on every finding). The skill writes the report's relative path back into its own section's `report_path` field so other skills can find it. (`bin/idstack-status` discovers reports independently by globbing `.idstack/exports//*.html`, so the dashboard survives a stale `report_path`.) `` is derived from the top-level `project_name` field via `bin/idstack-slugify` (rule: NFKD-fold, lowercase, kebab-case, ASCII-safe; empty input → `untitled-course`). The slug is computed deterministically — skills don't cache it in the manifest. All exports for a course — per-skill HTML reports, the pipeline dashboard at `index.html`, and LMS packages (`course-export.imscc`, `scorm-export.zip`) — live under the same `.idstack/exports//` folder so the deliverable is self-describing when zipped, emailed, or handed off. diff --git a/templates/report-format.md b/templates/report-format.md index 0b62253..31ee237 100644 --- a/templates/report-format.md +++ b/templates/report-format.md @@ -14,11 +14,11 @@ Re-runs overwrite the report. The timeline at `.idstack/timeline.jsonl` carries ### Voice and stance -idstack is a collaborator, not a builder. Every report should read like evidence-based feedback to a designer who already has work to defend. Three rules: +idstack works with the designer. It does not make the course. Each report gives evidence-based feedback to the designer. The designer makes the decisions about the course. Write all report text in ASD-STE100 Simplified Technical English, as the "Writing Standard (ASD-STE100)" section of `templates/preamble.md` tells you. Obey these three rules: -- **Observation, then evidence, then suggestion.** Never recommend without first showing what was seen and what the research says about it. -- **Suggest, don't direct.** "Consider…" / "Evidence suggests…" / "You may want to…" — not "You must…" / "Add this." The designer owns the course; idstack offers the read. -- **Cite every recommendation.** Every finding carries `[Domain-N] [Tier]`. If you can't cite it, it doesn't belong in a finding — move it to a "Limitations" or "Notes" section. +- **Problem, then evidence, then recommendation.** First show the problem that idstack found. Then show the evidence from the research. Then give the recommendation. +- **Recommend, do not give orders.** In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". In a numbered list of steps, write each step as a command. Do not use "consider", "may", "should" or "suggest". In the HTML, the label of the recommendation is "Recommendation". +- **Cite each recommendation.** Each finding has a `[Domain-N] [Tier]` citation. If you cannot cite a recommendation, do not put it in a finding. Put it in a "Limitations" or "Notes" section. ### Canonical content (what each placeholder must carry) @@ -33,8 +33,8 @@ Skills emit the HTML structure from `templates/report.html.tmpl`. The data each - `observation_html` — concrete observation grounded in the manifest or the imported course ("Module 4 has no rubric." / "Of 8 ILOs, 3 have no matching assessment."). - `evidence_html` — 1–2 sentences from the literature synthesis, plain language, followed by `[Domain-N] [Tier]`. - `why_it_matters_html` — bridge: explicitly connect observation → evidence → learner-outcome consequence. Without this, the citation reads as decoration. - - `suggestion_html` — collaborative recommendation. Phrased as a suggestion, not a directive. May reference another skill ("Run /idstack:learning-objectives to address the bidirectional gap.") or a concrete change. -4. **Top recommendations** — 3–5 highest-impact moves, ordered. Each carries an evidence citation and points to the skill/finding that would address it. `bin/idstack-status` and `/idstack:pipeline` may surface this section as a digest. + - `suggestion_html` — the recommendation, with the label "Recommendation". Write it as "idstack recommends that you ..." or as "You can ...". It can name a skill to run ("You can run /idstack:learning-objectives to correct the bidirectional gap.") or a change to the course. +4. **Top recommendations** — 3–5 highest-impact moves, ordered. Each carries an evidence citation and points to the skill/finding that would address it. Write each item as one command, with no period at the end of `{{action}}`. In a second sentence, tell why the change is necessary. Put the citation at the end of the item. `bin/idstack-status` and `/idstack:pipeline` may surface this section as a digest. 5. **Optional skill-specific sections** — e.g., needs-analysis adds "Training justification" + "Expertise-fit read"; accessibility-review may add a WCAG/UDL split. Use `
        ` with a clear `

        ` heading. 6. **Limitations** — what this report didn't analyze, what's a proxy, what would change the read if available. 7. **Next steps** — one paragraph. What the designer might do next inside idstack — a specific skill to run, a specific finding to address first, or "rerun this skill after addressing X." Avoid generic advice. diff --git a/templates/report.html.tmpl b/templates/report.html.tmpl index 02aa88e..465182e 100644 --- a/templates/report.html.tmpl +++ b/templates/report.html.tmpl @@ -67,7 +67,7 @@
        {{evidence_html}} [{{domain}}-{{n}}] [{{tier}}]
        Why it matters
        {{why_it_matters_html}}
        -
        Consider
        +
        Recommendation
        {{suggestion_html}}
        @@ -79,7 +79,7 @@

        Top recommendations

          -
        1. {{action}} [{{domain}}-{{n}}] [{{tier}}] — {{one_line_why}}
        2. +
        3. {{action}}. {{one_line_why}} [{{domain}}-{{n}}] [{{tier}}]
        diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 6bc212e..1a4666f 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -1581,6 +1581,57 @@ fresh sed -i.bak 's/^DESCRIPTIVE_MAX = 25$/DESCRIPTIVE_MAX = 26/' "$WORK/r/bin/idstack-ste-check" expect_fail "the checker accepts a 26-word description" python3 "$WORK/r/test/test-ste-check.py" +# 48a-48b. The writing standard reaches a report only through the skill steps and +# the report template. A skill without the check step ships text that no check +# has seen, and a bad template label goes into every report. +# 48a. A skill loses its writing standard check step -> smoke-test must fail. +# learn has one check step only, so removing it leaves no other call to match. +fresh +python3 - "$WORK/r/skills/learn/SKILL.md.tmpl" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = ('**Writing standard check.** Run this command on the export file.\n\n' + '```bash\n{{IDSTACK_RESOLVE}}\n' + 'if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ' + '".idstack/learnings-export.md"; else echo "STE_CHECK_UNAVAILABLE"; fi\n```\n\n' + 'For the result, do the steps in "How to use the standard" in the "Writing Standard ' + '(ASD-STE100)" section of the preamble. Do the check a maximum of three times.\n\n') +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, '', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +regen +expect_fail "a skill drops its writing standard check" "$WORK/r/test/smoke-test.sh" "$WORK/r" + +# 48b. The report template labels a recommendation "Consider" again -> smoke-test +# must fail. The template is not spliced into a skill, so no regen. +fresh +python3 - "$WORK/r/templates/report.html.tmpl" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "
        Recommendation
        \n" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "
        Consider
        \n", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the report template labels a recommendation Consider" "$WORK/r/test/smoke-test.sh" "$WORK/r" + +# 48c. course-builder loses its course-content check step but keeps the report check -> +# smoke-test must fail. A guard that only asks "is the call present" passes here. +fresh +python3 - "$WORK/r/skills/course-builder/SKILL.md.tmpl" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = ('```bash\n{{IDSTACK_RESOLVE}}\n' + 'if [ -x "$_IDSTACK/bin/idstack-ste-check" ]; then "$_IDSTACK/bin/idstack-ste-check" ' + '".idstack/course-content"; else echo "STE_CHECK_UNAVAILABLE"; fi\n```\n') +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, '', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +regen +expect_fail "course-builder drops its course-content check step" "$WORK/r/test/smoke-test.sh" "$WORK/r" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/smoke-test.sh b/test/smoke-test.sh index b452dc4..c5e86a1 100755 --- a/test/smoke-test.sh +++ b/test/smoke-test.sh @@ -316,6 +316,21 @@ fi if command -v python3 &>/dev/null; then check "ste checker unit tests pass" "python3 '$IDSTACK_DIR/test/test-ste-check.py'" fi +# Each skill runs the checker on the files it writes for a person. The match is +# the bash call itself, not the prose around it, so a step that loses its +# command fails here even when the text that names the check stays. +for skill in $SKILLS; do + case "$skill" in course-builder|course-export|red-team) want=2 ;; *) want=1 ;; esac + # grep -c exits 1 on zero matches; without "|| true", set -e ends the run with no FAIL line. + got=$(grep -cF '"$_IDSTACK/bin/idstack-ste-check"' "$IDSTACK_DIR/skills/$skill/SKILL.md.tmpl" || true) + check "$skill SKILL.md.tmpl runs the ste checker at all $want check steps" "[ '$got' -eq $want ]" +done +# Every report and the course dashboard copy the fixed labels in these two +# templates, so one bad label puts a problem in every report. +if command -v python3 &>/dev/null; then + check "report and dashboard templates pass the ste checker" \ + "'$IDSTACK_DIR/bin/idstack-ste-check' --format html '$IDSTACK_DIR/templates/report.html.tmpl' '$IDSTACK_DIR/templates/index.html.tmpl'" +fi # ./setup is what a new user runs first; it is exercised against a repo copy # with a fake $HOME and a stub `claude`, never the real install. From 47e74e229aba9beed28f74f907bcc88fd25d04ab Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 13:13:34 -0700 Subject: [PATCH 03/10] feat(skills): rewrite fixed skill text in ASD-STE100 Co-Authored-By: Claude Haiku 4.5 --- README.md | 2 +- skills/accessibility-review/SKILL.md | 68 ++++++------ skills/accessibility-review/SKILL.md.tmpl | 52 ++++----- skills/assessment-design/SKILL.md | 68 ++++++------ skills/assessment-design/SKILL.md.tmpl | 52 ++++----- skills/course-builder/SKILL.md | 109 +++++++++---------- skills/course-builder/SKILL.md.tmpl | 93 ++++++++-------- skills/course-export/SKILL.md | 92 ++++++++-------- skills/course-export/SKILL.md.tmpl | 76 ++++++------- skills/course-import/SKILL.md | 120 ++++++++++----------- skills/course-import/SKILL.md.tmpl | 104 +++++++++--------- skills/course-quality-review/SKILL.md | 48 ++++----- skills/course-quality-review/SKILL.md.tmpl | 32 +++--- skills/learn/SKILL.md | 18 ++-- skills/learn/SKILL.md.tmpl | 4 +- skills/learning-objectives/SKILL.md | 98 ++++++++--------- skills/learning-objectives/SKILL.md.tmpl | 82 +++++++------- skills/needs-analysis/SKILL.md | 100 ++++++++--------- skills/needs-analysis/SKILL.md.tmpl | 84 +++++++-------- skills/pipeline/SKILL.md | 34 +++--- skills/pipeline/SKILL.md.tmpl | 20 ++-- skills/red-team/SKILL.md | 44 ++++---- skills/red-team/SKILL.md.tmpl | 30 +++--- templates/manifest-schema.md | 2 +- templates/preamble.md | 14 +-- test/mutation-test.sh | 17 ++- test/smoke-test.sh | 13 +++ 27 files changed, 753 insertions(+), 723 deletions(-) diff --git a/README.md b/README.md index ae95266..be3ce6b 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ idstack turns Claude Code into an evidence-based instructional design team. Each | `/idstack:course-builder` | **Content Generator** | Generates complete course content from the manifest: syllabus, module pages, assignment descriptions, and rubric documents. Content follows cognitive load principles. Includes adversarial spec review (auto-validates alignment). For imported courses, switches to gap-fill mode: generates only the artifacts upstream skills flagged as missing, instead of regenerating what already exists in the cartridge. Writes `.idstack/exports//course-builder.html`. | | `/idstack:course-export` | **LMS Publisher** | Exports to any LMS. Generates IMS Common Cartridge (.imscc), SCORM 1.2 packages, or pushes directly to Canvas via API. Shows readiness dashboard before export. The output IS the course. | | `/idstack:course-quality-review` | **Quality Auditor** | Full QM-aligned audit plus Community of Inquiry presence layer. 8 structural standards, 3 presence dimensions (teaching, social, cognitive), constructive alignment audit. Parallel sub-agents on Claude Code for speed. Writes `.idstack/exports//course-quality-review.html`. | -| `/idstack:accessibility-review` | **Accessibility Reviewer** | WCAG 2.1 AA compliance audit plus Universal Design for Learning (UDL 3.0) enhancement review. Two-tier output: "Must Fix" for accessibility violations, "Should Improve" for UDL recommendations. Parallel sub-agents for WCAG and UDL. Writes `.idstack/exports//accessibility-review.html`. | +| `/idstack:accessibility-review` | **Accessibility Reviewer** | WCAG 2.1 AA compliance audit plus Universal Design for Learning (UDL 3.0) enhancement review. Two-tier output: "Necessary changes" for accessibility violations, "Recommended changes" for UDL recommendations. Parallel sub-agents for WCAG and UDL. Writes `.idstack/exports//accessibility-review.html`. | | `/idstack:red-team` | **Adversarial Auditor** | Assumes your course is broken and tries to prove it. Runs in a clean-context sub-agent so the audit can't inherit build-bias from the parent. Five dimensions in parallel: alignment stress test, evidence verification, cognitive load analysis, learner persona simulation, prerequisite chain integrity. Writes `.idstack/exports//red-team.html` and returns to the parent for a triage-and-fix loop (Critical / Critical+High / All / Skip). Produces a confidence score. | | `/idstack:pipeline` | **Orchestrator** | Chains the 8-skill linear pipeline automatically. Auto-skips completed skills. Shows pipeline status. Pause anytime, resume later. Produces `.idstack/exports//index.html`, a branded course dashboard linking to every per-skill report, with readiness scores, top recurring issues, evidence themes, and where to start. Open in any browser. | | `/idstack:learn` | **Memory Manager** | Search, list, delete, promote, and export project learnings. Supports cross-project intelligence. | diff --git a/skills/accessibility-review/SKILL.md b/skills/accessibility-review/SKILL.md index b3d4bef..e7f7b8a 100644 --- a/skills/accessibility-review/SKILL.md +++ b/skills/accessibility-review/SKILL.md @@ -2,8 +2,8 @@ name: accessibility-review description: | WCAG 2.1 AA compliance audit plus Universal Design for Learning (UDL 3.0) - enhancement review for course designs. Two-tier output: "Must Fix" for - accessibility violations and "Should Improve" for UDL recommendations. + enhancement review for course designs. Two-tier output: "Necessary changes" for + accessibility violations and "Recommended changes" for UDL recommendations. Works standalone or reads from the idstack project manifest. (idstack) allowed-tools: - Bash @@ -206,7 +206,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -287,8 +287,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -314,8 +314,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -421,15 +421,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `accessibility_review` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Accessibility Review — WCAG + UDL Two-Tier Audit @@ -439,9 +439,9 @@ inclusive (UDL Guidelines 3.0). Your two-layer approach: 1. **WCAG Compliance** — Does the course meet accessibility standards? These are - "Must Fix" items with legal and institutional implications. + "Necessary changes" items with legal and institutional implications. 2. **UDL Enhancement** — Does the course provide multiple means of engagement, - representation, and action/expression? These are "Should Improve" items backed + representation, and action/expression? These are "Recommended changes" items backed by evidence that improve learning for ALL learners, not just those with disabilities. A course can be technically accessible (screen readers work, captions exist) and still @@ -484,10 +484,10 @@ The shared preamble above already ran the manifest existence check **Without manifest:** Ask the user via AskUserQuestion (one question at a time): -1. "Describe your course at a high level. What subject, how many modules, what's the +1. "Describe your course at a high level. What is the subject, how many modules does it have, and what is the target audience?" -2. "What types of assessments do you use? (quizzes, essays, projects, discussions, - presentations, etc.)" +2. "What types of assessments do you use? (for example, quizzes, essays, projects, discussions, or + presentations)" 3. "What media formats are in your course? (text, video, audio, images, interactive elements, simulations)" 4. "Are there any timed activities or assessments?" @@ -508,13 +508,13 @@ If you have access to the **Agent tool**, dispatch the WCAG audit and UDL review Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. -**After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Must Fix" and UDL gaps as "Should Improve". +**After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Necessary changes" and UDL gaps as "Recommended changes". **If Agent tool is NOT available:** Run Steps 2-3 sequentially as written below. --- -### Step 2: WCAG 2.1 AA Compliance Audit (Tier 1: Must Fix) +### Step 2: WCAG 2.1 AA Compliance Audit (Tier 1: Necessary changes) Review the course design against these WCAG-derived accessibility requirements. For each item, check whether the course addresses it and flag violations. @@ -634,7 +634,7 @@ For each violation found, provide: - Specific remediation with an example - Evidence citation -### Step 3: UDL Guidelines 3.0 Enhancement Review (Tier 2: Should Improve) +### Step 3: UDL Guidelines 3.0 Enhancement Review (Tier 2: Recommended changes) Review the course design against the three UDL principles. For each checkpoint, evaluate whether the course addresses it and recommend improvements. @@ -695,13 +695,13 @@ Calculate the accessibility score (0-100): **Combined Score:** - 80+ "Strong accessibility" — meets compliance and supports diverse learners -- 60-79 "Needs improvement" — basic compliance but gaps in inclusivity -- 40-59 "Significant gaps" — multiple compliance issues and limited UDL coverage -- <40 "Major accessibility barriers" — course needs substantial redesign +- 60-79 "Some accessibility problems" — basic compliance but gaps in inclusivity +- 40-59 "Important accessibility problems" — multiple compliance issues and limited UDL coverage +- <40 "Many accessibility barriers" — course needs substantial redesign ### Step 5: Generate Report -Generate an HTML report so the designer has a single document covering both compliance (WCAG, the Must Fix layer) and inclusion (UDL, the Should Improve layer). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. +Generate an HTML report so the designer has a single document covering both compliance (WCAG, the Necessary changes layer) and inclusion (UDL, the Recommended changes layer). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. ```bash # Resolve the idstack install dir. Re-derived at the top of every bash block — @@ -736,13 +736,13 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Accessibility Review" - **`{{skill_name}}`:** `accessibility-review` -- **Summary:** 2–3 sentences. Lead with the overall score, the number of Must Fix items at WCAG Level A, and the biggest single barrier the designer should know about. Include the optional one-line scoreboard: "Overall XX/100 · WCAG XX/100 · UDL XX/100". +- **Summary:** 2–3 sentences. Lead with the overall score, the number of necessary changes at WCAG Level A, and the biggest single barrier the designer should know about. Include the optional one-line scoreboard: "Overall XX/100 · WCAG XX/100 · UDL XX/100". - **Findings** organized into two clearly-labeled subgroups inside `
        `: - - `

        Tier 1 — Must Fix (WCAG 2.1 AA)

        ` — one `
        ` per violation, stable ids `wcag-1`, `wcag-2`, etc. Order: Level A first (the floor), then AA; within level, by impact. - - `

        Tier 2 — Should Improve (UDL 3.0)

        ` — one `
        ` per UDL recommendation, stable ids `udl-1`, `udl-2`, etc. Group by principle (Engagement, Representation, Action & Expression). Phrase suggestions as enhancement, not compliance — UDL is enhancement. + - `

        Tier 1 — Necessary changes (WCAG 2.1 AA)

        ` — one `
        ` per violation, stable ids `wcag-1`, `wcag-2`, etc. Order: Level A first (the floor), then AA; within level, by impact. + - `

        Tier 2 — Recommended changes (UDL 3.0)

        ` — one `
        ` per UDL recommendation, stable ids `udl-1`, `udl-2`, etc. Group by principle (Engagement, Representation, Action & Expression). Write recommendations as enhancement, not compliance — UDL is enhancement. - **Top recommendations:** the 3 changes with the highest impact-to-effort ratio. Cite each. -- **Limitations:** review reads structural metadata, not the actual learner-facing content text; alt-text quality is checked for presence not for descriptive accuracy; UDL recommendations are generated from manifest signals, not from observed learner use. -- **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for adversarial persona testing (the persona dimension will simulate learners who depend on the accommodations being audited here). +- **Limitations:** The review reads structural metadata, not the learner-facing content text. The review checks that alt text is present, not that it is accurate. The UDL recommendations come from manifest signals, not from observed learner use. +- **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for an adversarial persona test (the persona dimension simulates learners who depend on the accommodations that this review audited). **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -865,19 +865,19 @@ verbatim. After writing the manifest, confirm to the user: -"Your accessibility review is saved. Two artifacts: +"I saved your accessibility review in two files: - **Read this:** `.idstack/exports//accessibility-review.html` — the report - with WCAG violations (Must Fix), UDL recommendations (Should Improve), evidence-backed - findings on every item, and the 3 highest-impact quick wins. Open it in any browser; - the folder is self-contained. + with WCAG violations (Necessary changes), UDL recommendations (Recommended changes), evidence-backed + findings on every item, and the 3 highest-impact quick wins. Open it in a web browser. + The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Score:** Overall XX/100 · WCAG XX/100 · UDL XX/100. [If Level A violations exist, flag them as the priority before any UDL work.] -**Next step:** Run `/idstack:red-team` for adversarial persona testing — the persona -dimension will stress-test the accommodations from this review against learners who +**Next step:** Run `/idstack:red-team` for an adversarial persona test. The persona +dimension tests the accommodations from this review against learners who depend on them." --- @@ -991,7 +991,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/accessibility-review/SKILL.md.tmpl b/skills/accessibility-review/SKILL.md.tmpl index 878d46a..7f06a69 100644 --- a/skills/accessibility-review/SKILL.md.tmpl +++ b/skills/accessibility-review/SKILL.md.tmpl @@ -2,8 +2,8 @@ name: accessibility-review description: | WCAG 2.1 AA compliance audit plus Universal Design for Learning (UDL 3.0) - enhancement review for course designs. Two-tier output: "Must Fix" for - accessibility violations and "Should Improve" for UDL recommendations. + enhancement review for course designs. Two-tier output: "Necessary changes" for + accessibility violations and "Recommended changes" for UDL recommendations. Works standalone or reads from the idstack project manifest. (idstack) allowed-tools: - Bash @@ -19,7 +19,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `accessibility_review` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Accessibility Review — WCAG + UDL Two-Tier Audit @@ -29,9 +29,9 @@ inclusive (UDL Guidelines 3.0). Your two-layer approach: 1. **WCAG Compliance** — Does the course meet accessibility standards? These are - "Must Fix" items with legal and institutional implications. + "Necessary changes" items with legal and institutional implications. 2. **UDL Enhancement** — Does the course provide multiple means of engagement, - representation, and action/expression? These are "Should Improve" items backed + representation, and action/expression? These are "Recommended changes" items backed by evidence that improve learning for ALL learners, not just those with disabilities. A course can be technically accessible (screen readers work, captions exist) and still @@ -74,10 +74,10 @@ The shared preamble above already ran the manifest existence check **Without manifest:** Ask the user via AskUserQuestion (one question at a time): -1. "Describe your course at a high level. What subject, how many modules, what's the +1. "Describe your course at a high level. What is the subject, how many modules does it have, and what is the target audience?" -2. "What types of assessments do you use? (quizzes, essays, projects, discussions, - presentations, etc.)" +2. "What types of assessments do you use? (for example, quizzes, essays, projects, discussions, or + presentations)" 3. "What media formats are in your course? (text, video, audio, images, interactive elements, simulations)" 4. "Are there any timed activities or assessments?" @@ -98,13 +98,13 @@ If you have access to the **Agent tool**, dispatch the WCAG audit and UDL review Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in each agent prompt. The agents cannot see the preamble. -**After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Must Fix" and UDL gaps as "Should Improve". +**After both agents return:** Merge results into the unified report format (Step 4), with WCAG violations as "Necessary changes" and UDL gaps as "Recommended changes". **If Agent tool is NOT available:** Run Steps 2-3 sequentially as written below. --- -### Step 2: WCAG 2.1 AA Compliance Audit (Tier 1: Must Fix) +### Step 2: WCAG 2.1 AA Compliance Audit (Tier 1: Necessary changes) Review the course design against these WCAG-derived accessibility requirements. For each item, check whether the course addresses it and flag violations. @@ -224,7 +224,7 @@ For each violation found, provide: - Specific remediation with an example - Evidence citation -### Step 3: UDL Guidelines 3.0 Enhancement Review (Tier 2: Should Improve) +### Step 3: UDL Guidelines 3.0 Enhancement Review (Tier 2: Recommended changes) Review the course design against the three UDL principles. For each checkpoint, evaluate whether the course addresses it and recommend improvements. @@ -285,13 +285,13 @@ Calculate the accessibility score (0-100): **Combined Score:** - 80+ "Strong accessibility" — meets compliance and supports diverse learners -- 60-79 "Needs improvement" — basic compliance but gaps in inclusivity -- 40-59 "Significant gaps" — multiple compliance issues and limited UDL coverage -- <40 "Major accessibility barriers" — course needs substantial redesign +- 60-79 "Some accessibility problems" — basic compliance but gaps in inclusivity +- 40-59 "Important accessibility problems" — multiple compliance issues and limited UDL coverage +- <40 "Many accessibility barriers" — course needs substantial redesign ### Step 5: Generate Report -Generate an HTML report so the designer has a single document covering both compliance (WCAG, the Must Fix layer) and inclusion (UDL, the Should Improve layer). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. +Generate an HTML report so the designer has a single document covering both compliance (WCAG, the Necessary changes layer) and inclusion (UDL, the Recommended changes layer). The report follows the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md`. ```bash {{IDSTACK_RESOLVE}} @@ -309,13 +309,13 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Accessibility Review" - **`{{skill_name}}`:** `accessibility-review` -- **Summary:** 2–3 sentences. Lead with the overall score, the number of Must Fix items at WCAG Level A, and the biggest single barrier the designer should know about. Include the optional one-line scoreboard: "Overall XX/100 · WCAG XX/100 · UDL XX/100". +- **Summary:** 2–3 sentences. Lead with the overall score, the number of necessary changes at WCAG Level A, and the biggest single barrier the designer should know about. Include the optional one-line scoreboard: "Overall XX/100 · WCAG XX/100 · UDL XX/100". - **Findings** organized into two clearly-labeled subgroups inside `
        `: - - `

        Tier 1 — Must Fix (WCAG 2.1 AA)

        ` — one `
        ` per violation, stable ids `wcag-1`, `wcag-2`, etc. Order: Level A first (the floor), then AA; within level, by impact. - - `

        Tier 2 — Should Improve (UDL 3.0)

        ` — one `
        ` per UDL recommendation, stable ids `udl-1`, `udl-2`, etc. Group by principle (Engagement, Representation, Action & Expression). Phrase suggestions as enhancement, not compliance — UDL is enhancement. + - `

        Tier 1 — Necessary changes (WCAG 2.1 AA)

        ` — one `
        ` per violation, stable ids `wcag-1`, `wcag-2`, etc. Order: Level A first (the floor), then AA; within level, by impact. + - `

        Tier 2 — Recommended changes (UDL 3.0)

        ` — one `
        ` per UDL recommendation, stable ids `udl-1`, `udl-2`, etc. Group by principle (Engagement, Representation, Action & Expression). Write recommendations as enhancement, not compliance — UDL is enhancement. - **Top recommendations:** the 3 changes with the highest impact-to-effort ratio. Cite each. -- **Limitations:** review reads structural metadata, not the actual learner-facing content text; alt-text quality is checked for presence not for descriptive accuracy; UDL recommendations are generated from manifest signals, not from observed learner use. -- **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for adversarial persona testing (the persona dimension will simulate learners who depend on the accommodations being audited here). +- **Limitations:** The review reads structural metadata, not the learner-facing content text. The review checks that alt text is present, not that it is accurate. The UDL recommendations come from manifest signals, not from observed learner use. +- **Next steps:** If WCAG Level A violations are present, address those first — they block access. Then run `/idstack:red-team` for an adversarial persona test (the persona dimension simulates learners who depend on the accommodations that this review audited). **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -387,19 +387,19 @@ verbatim. After writing the manifest, confirm to the user: -"Your accessibility review is saved. Two artifacts: +"I saved your accessibility review in two files: - **Read this:** `.idstack/exports//accessibility-review.html` — the report - with WCAG violations (Must Fix), UDL recommendations (Should Improve), evidence-backed - findings on every item, and the 3 highest-impact quick wins. Open it in any browser; - the folder is self-contained. + with WCAG violations (Necessary changes), UDL recommendations (Recommended changes), evidence-backed + findings on every item, and the 3 highest-impact quick wins. Open it in a web browser. + The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Score:** Overall XX/100 · WCAG XX/100 · UDL XX/100. [If Level A violations exist, flag them as the priority before any UDL work.] -**Next step:** Run `/idstack:red-team` for adversarial persona testing — the persona -dimension will stress-test the accommodations from this review against learners who +**Next step:** Run `/idstack:red-team` for an adversarial persona test. The persona +dimension tests the accommodations from this review against learners who depend on them." --- diff --git a/skills/assessment-design/SKILL.md b/skills/assessment-design/SKILL.md index a000bd1..8d8b928 100644 --- a/skills/assessment-design/SKILL.md +++ b/skills/assessment-design/SKILL.md @@ -2,7 +2,7 @@ name: assessment-design description: | Evidence-based assessment design with rubrics, feedback strategies, and formative - checkpoints. Aligns each assessment to learning objectives using Bloom's taxonomy. + checkpoints. Aligns each assessment to learning objectives with Bloom's taxonomy. Applies Nicol's 7 principles of good feedback practice. Reads from /learning-objectives manifest and extends it with assessment specs. (idstack) allowed-tools: @@ -205,7 +205,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -286,8 +286,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -313,8 +313,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -420,15 +420,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `assessments` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Assessment Design — Rubrics, Feedback Strategies & Formative Checkpoints @@ -522,9 +522,9 @@ The shared preamble above already ran the manifest existence check (`MANIFEST_EXISTS` / `NO_MANIFEST`) and this skill's re-run question. **If NO_MANIFEST:** -- Say: "I see you haven't run `/idstack:learning-objectives` yet. Running it first gives me - your ILOs with Bloom's classifications, which helps me recommend assessment types - that actually measure your stated outcomes. Want to continue anyway, or run +- Say: "You did not run `/idstack:learning-objectives`. That skill gives me + your ILOs with Bloom's classifications. With them, I can recommend assessment types + that measure your stated outcomes. Do you want to continue without it, or run `/idstack:learning-objectives` first?" - If the user wants to continue, proceed without manifest context. You can still design assessments; you just won't have the upstream alignment data. The manifest @@ -550,11 +550,11 @@ Summarize what you have: | ILO-1 | [text] | [dimension] | [level] | | ... | ... | ... | ... | -I'll use these Bloom's classifications to recommend assessment types that align with +I will use these Bloom's classifications to recommend assessment types that align with each objective's cognitive level." If `needs_analysis.learner_profile` is also available, note the prior knowledge level: -"Your learners are [level]. I'll factor this into feedback strategy recommendations." +"Your learners are [level]. I will use this level in the feedback strategy recommendations." Proceed directly to the Assessment Design Workflow using manifest data. @@ -565,7 +565,7 @@ Proceed directly to the Assessment Design Workflow using manifest data. Ask the user: **"What are the key learning objectives for this course? For each one, tell me what -learners should be able to DO after completing it. I'll classify them and design +learners must be able to DO after they complete it. I will classify them and design assessments to match."** For each objective provided, classify on both Bloom's dimensions (knowledge and @@ -581,7 +581,7 @@ ambiguous [Alignment-12] [T2]. **Announce the chosen mode to the user as the first sentence:** -> "Mode 3: audit-existing. The imported course already contains [N] assessments and [M] rubrics — I'll audit them against your ILOs rather than designing new ones from scratch. If you want to add new assessments, say 'design more' at any point." +> "Mode 3: audit-existing. The imported course contains [N] assessments and [M] rubrics. I will audit them against your ILOs. I will not design new assessments. To add new assessments, say 'design more'." **What audit-existing does (and doesn't do):** @@ -631,9 +631,9 @@ Use this alignment table: "**ILO-X:** [objective text] - Bloom's level: [process] / [knowledge dimension] - Recommended assessment types: [list from table above] -- My suggestion: [specific recommendation with rationale] +- idstack recommends: [specific recommendation with rationale] -Does this assessment type work for your context, or would you prefer a different +Does this assessment type work for your context, or do you want a different format?" Use one AskUserQuestion per assessment to confirm or adjust. @@ -641,14 +641,14 @@ Use one AskUserQuestion per assessment to confirm or adjust. **Flag misalignments.** If the user requests an assessment type that does not match the ILO's cognitive level, flag it directly: -"You've asked for multiple-choice for ILO-X, which targets '[evaluate].' Multiple-choice -primarily measures recognition and recall (remember level). This creates a constructive -alignment gap — you won't know if students can actually evaluate because you're -measuring whether they can recognize [Alignment-1] [T5]. +"You asked for multiple-choice for ILO-X, which targets '[evaluate]'. Multiple-choice +primarily measures recognition and recall (remember level). This causes a constructive +alignment gap. You cannot know if students can evaluate, because the test measures +only if they can recognize [Alignment-1] [T5]. -Consider instead: [aligned alternatives]. Want to adjust, or keep multiple-choice -with the understanding that it measures a lower cognitive level than the objective -states?" +idstack recommends: [aligned alternatives]. Do you want to change the format, or keep +multiple-choice? Multiple-choice measures a lower cognitive level than the objective +states." Do not silently accept misaligned choices. Present the evidence, let the user decide, and record their decision. @@ -825,7 +825,7 @@ After completing the full workflow, present a consolidated summary. |-----------|--------|--------|----------| | Practice quiz on Module 3 concepts | A-1 Midterm | auto-graded, elaborated | immediate | | Draft outline peer review | A-2 Final project | peer, structured | delayed | -| Self-assessment checklist | A-1 Midterm | self-check against rubric | student-initiated | +| Self-assessment checklist | A-1 Midterm | self-check against rubric | student-led | ### Alignment Verification | ILO | Bloom's Level | Assessment | Assessment Level | Status | @@ -836,8 +836,8 @@ After completing the full workflow, present a consolidated summary. ``` Flag any remaining alignment issues. If the user accepted a misalignment in Step 1, -note it here: "ILO-3 / A-3: User accepted misalignment (quiz for evaluate-level ILO). -Consider adding a formative peer review checkpoint to partially address the gap." +note it here: "ILO-3 / A-3: The user accepted the misalignment (quiz for evaluate-level ILO). +idstack recommends a formative peer review checkpoint to make the gap smaller." --- @@ -939,8 +939,8 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Assessment plan

        ` and an HTML `` (columns: ID, Assessment, Type, Format, Aligned ILOs, Feedback, Points). - **Finding ids:** `assess-1`, `rubric-1`, `feedback-1`, etc. Findings come from alignment mismatches, correctness-only feedback at apply+ Bloom's levels, missing rubrics, missing formative checkpoints before high-stakes summatives, and fewer than 5 of Nicol's 7 feedback principles. - **Optional skill-specific section** (Mode 3 only, after Top recommendations, before Limitations): `
        ` with `

        Mode 3 audit notes

        ` listing the existing assessments audited, the Bloom's level each rubric criterion targets, where the gap is, and which gaps the designer chose to act on vs. defer. -- **Limitations:** report reads rubric criteria as documented in the manifest, not as enacted in instructor grading; `feedback_quality_score` is a heuristic, not a validated instrument; Mode 3 doesn't propose new assessments. -- **Next steps:** Run `/idstack:course-builder` to generate the full course content including assessment documents, rubric handouts, and assignment instructions. (For imported courses in gap-fill mode, course-builder will only generate the artifacts flagged as missing here.) +- **Limitations:** The report reads rubric criteria as the manifest records them, not as instructors apply them to student work. `feedback_quality_score` is a heuristic, not a validated instrument. Mode 3 does not recommend new assessments. +- **Next steps:** Run `/idstack:course-builder` to generate the full course content, with assessment documents, rubric handouts, and assignment instructions. (For imported courses in gap-fill mode, course-builder will only generate the missing artifacts that this report found.) In Mode 3 (audit-existing), every finding should reference the audited rubric criterion or assessment item and explicitly state the gap, not propose a wholesale redesign. @@ -1119,14 +1119,14 @@ Map each ILO to its aligned assessment(s): Write the manifest, then confirm to the user: -"Your assessment designs are saved. Two artifacts: +"I saved your assessment designs in two files: - **Read this:** `.idstack/exports//assessment-design.html` — the assessment plan, the feedback-quality score with evidence-backed findings, and per-rubric - alignment notes. Open it in any browser; the folder is self-contained. + alignment notes. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). -**Next step:** Run `/idstack:course-builder` to generate the full course content including +**Next step:** Run `/idstack:course-builder` to generate the full course content, with assessment documents, rubric handouts, and assignment instructions." --- @@ -1240,7 +1240,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/assessment-design/SKILL.md.tmpl b/skills/assessment-design/SKILL.md.tmpl index 6c2ea29..1761a61 100644 --- a/skills/assessment-design/SKILL.md.tmpl +++ b/skills/assessment-design/SKILL.md.tmpl @@ -2,7 +2,7 @@ name: assessment-design description: | Evidence-based assessment design with rubrics, feedback strategies, and formative - checkpoints. Aligns each assessment to learning objectives using Bloom's taxonomy. + checkpoints. Aligns each assessment to learning objectives with Bloom's taxonomy. Applies Nicol's 7 principles of good feedback practice. Reads from /learning-objectives manifest and extends it with assessment specs. (idstack) allowed-tools: @@ -18,7 +18,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `assessments` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Assessment Design — Rubrics, Feedback Strategies & Formative Checkpoints @@ -112,9 +112,9 @@ The shared preamble above already ran the manifest existence check (`MANIFEST_EXISTS` / `NO_MANIFEST`) and this skill's re-run question. **If NO_MANIFEST:** -- Say: "I see you haven't run `/idstack:learning-objectives` yet. Running it first gives me - your ILOs with Bloom's classifications, which helps me recommend assessment types - that actually measure your stated outcomes. Want to continue anyway, or run +- Say: "You did not run `/idstack:learning-objectives`. That skill gives me + your ILOs with Bloom's classifications. With them, I can recommend assessment types + that measure your stated outcomes. Do you want to continue without it, or run `/idstack:learning-objectives` first?" - If the user wants to continue, proceed without manifest context. You can still design assessments; you just won't have the upstream alignment data. The manifest @@ -140,11 +140,11 @@ Summarize what you have: | ILO-1 | [text] | [dimension] | [level] | | ... | ... | ... | ... | -I'll use these Bloom's classifications to recommend assessment types that align with +I will use these Bloom's classifications to recommend assessment types that align with each objective's cognitive level." If `needs_analysis.learner_profile` is also available, note the prior knowledge level: -"Your learners are [level]. I'll factor this into feedback strategy recommendations." +"Your learners are [level]. I will use this level in the feedback strategy recommendations." Proceed directly to the Assessment Design Workflow using manifest data. @@ -155,7 +155,7 @@ Proceed directly to the Assessment Design Workflow using manifest data. Ask the user: **"What are the key learning objectives for this course? For each one, tell me what -learners should be able to DO after completing it. I'll classify them and design +learners must be able to DO after they complete it. I will classify them and design assessments to match."** For each objective provided, classify on both Bloom's dimensions (knowledge and @@ -171,7 +171,7 @@ ambiguous [Alignment-12] [T2]. **Announce the chosen mode to the user as the first sentence:** -> "Mode 3: audit-existing. The imported course already contains [N] assessments and [M] rubrics — I'll audit them against your ILOs rather than designing new ones from scratch. If you want to add new assessments, say 'design more' at any point." +> "Mode 3: audit-existing. The imported course contains [N] assessments and [M] rubrics. I will audit them against your ILOs. I will not design new assessments. To add new assessments, say 'design more'." **What audit-existing does (and doesn't do):** @@ -221,9 +221,9 @@ Use this alignment table: "**ILO-X:** [objective text] - Bloom's level: [process] / [knowledge dimension] - Recommended assessment types: [list from table above] -- My suggestion: [specific recommendation with rationale] +- idstack recommends: [specific recommendation with rationale] -Does this assessment type work for your context, or would you prefer a different +Does this assessment type work for your context, or do you want a different format?" Use one AskUserQuestion per assessment to confirm or adjust. @@ -231,14 +231,14 @@ Use one AskUserQuestion per assessment to confirm or adjust. **Flag misalignments.** If the user requests an assessment type that does not match the ILO's cognitive level, flag it directly: -"You've asked for multiple-choice for ILO-X, which targets '[evaluate].' Multiple-choice -primarily measures recognition and recall (remember level). This creates a constructive -alignment gap — you won't know if students can actually evaluate because you're -measuring whether they can recognize [Alignment-1] [T5]. +"You asked for multiple-choice for ILO-X, which targets '[evaluate]'. Multiple-choice +primarily measures recognition and recall (remember level). This causes a constructive +alignment gap. You cannot know if students can evaluate, because the test measures +only if they can recognize [Alignment-1] [T5]. -Consider instead: [aligned alternatives]. Want to adjust, or keep multiple-choice -with the understanding that it measures a lower cognitive level than the objective -states?" +idstack recommends: [aligned alternatives]. Do you want to change the format, or keep +multiple-choice? Multiple-choice measures a lower cognitive level than the objective +states." Do not silently accept misaligned choices. Present the evidence, let the user decide, and record their decision. @@ -415,7 +415,7 @@ After completing the full workflow, present a consolidated summary. |-----------|--------|--------|----------| | Practice quiz on Module 3 concepts | A-1 Midterm | auto-graded, elaborated | immediate | | Draft outline peer review | A-2 Final project | peer, structured | delayed | -| Self-assessment checklist | A-1 Midterm | self-check against rubric | student-initiated | +| Self-assessment checklist | A-1 Midterm | self-check against rubric | student-led | ### Alignment Verification | ILO | Bloom's Level | Assessment | Assessment Level | Status | @@ -426,8 +426,8 @@ After completing the full workflow, present a consolidated summary. ``` Flag any remaining alignment issues. If the user accepted a misalignment in Step 1, -note it here: "ILO-3 / A-3: User accepted misalignment (quiz for evaluate-level ILO). -Consider adding a formative peer review checkpoint to partially address the gap." +note it here: "ILO-3 / A-3: The user accepted the misalignment (quiz for evaluate-level ILO). +idstack recommends a formative peer review checkpoint to make the gap smaller." --- @@ -495,8 +495,8 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Assessment plan

        ` and an HTML `
        ` (columns: ID, Assessment, Type, Format, Aligned ILOs, Feedback, Points). - **Finding ids:** `assess-1`, `rubric-1`, `feedback-1`, etc. Findings come from alignment mismatches, correctness-only feedback at apply+ Bloom's levels, missing rubrics, missing formative checkpoints before high-stakes summatives, and fewer than 5 of Nicol's 7 feedback principles. - **Optional skill-specific section** (Mode 3 only, after Top recommendations, before Limitations): `
        ` with `

        Mode 3 audit notes

        ` listing the existing assessments audited, the Bloom's level each rubric criterion targets, where the gap is, and which gaps the designer chose to act on vs. defer. -- **Limitations:** report reads rubric criteria as documented in the manifest, not as enacted in instructor grading; `feedback_quality_score` is a heuristic, not a validated instrument; Mode 3 doesn't propose new assessments. -- **Next steps:** Run `/idstack:course-builder` to generate the full course content including assessment documents, rubric handouts, and assignment instructions. (For imported courses in gap-fill mode, course-builder will only generate the artifacts flagged as missing here.) +- **Limitations:** The report reads rubric criteria as the manifest records them, not as instructors apply them to student work. `feedback_quality_score` is a heuristic, not a validated instrument. Mode 3 does not recommend new assessments. +- **Next steps:** Run `/idstack:course-builder` to generate the full course content, with assessment documents, rubric handouts, and assignment instructions. (For imported courses in gap-fill mode, course-builder will only generate the missing artifacts that this report found.) In Mode 3 (audit-existing), every finding should reference the audited rubric criterion or assessment item and explicitly state the gap, not propose a wholesale redesign. @@ -624,14 +624,14 @@ Map each ILO to its aligned assessment(s): Write the manifest, then confirm to the user: -"Your assessment designs are saved. Two artifacts: +"I saved your assessment designs in two files: - **Read this:** `.idstack/exports//assessment-design.html` — the assessment plan, the feedback-quality score with evidence-backed findings, and per-rubric - alignment notes. Open it in any browser; the folder is self-contained. + alignment notes. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). -**Next step:** Run `/idstack:course-builder` to generate the full course content including +**Next step:** Run `/idstack:course-builder` to generate the full course content, with assessment documents, rubric handouts, and assignment instructions." --- diff --git a/skills/course-builder/SKILL.md b/skills/course-builder/SKILL.md index 09cd311..1aa66b2 100644 --- a/skills/course-builder/SKILL.md +++ b/skills/course-builder/SKILL.md @@ -4,7 +4,7 @@ description: | Generate complete course content from the idstack manifest. Produces syllabus, module pages, assignment descriptions, and rubric documents. Content follows cognitive load principles and adapts to learner expertise level. Reads from - the full pipeline (needs, objectives, assessments) for richest output. (idstack) + the full pipeline (needs, objectives, assessments) for the best output. (idstack) allowed-tools: - Bash - Read @@ -206,7 +206,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -287,8 +287,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -314,8 +314,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -421,15 +421,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `course_content` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Builder — Evidence-Based Content Generation @@ -530,7 +530,7 @@ The shared preamble above already ran the manifest existence check - `context` — at least `modality` and `timeline` **If NO_MANIFEST:** -- Say: "I need a project manifest with learning objectives to generate course content. +- Say: "idstack must have a project manifest with learning objectives to generate course content. Run `/idstack:needs-analysis` followed by `/idstack:learning-objectives` to build the foundation. If you have objectives ready, I can create a minimal manifest to work from — just tell me your learning objectives, course modality, and timeline." @@ -540,10 +540,10 @@ The shared preamble above already ran the manifest existence check **Nudge for assessment design:** If the manifest exists but has no `assessments` section (or it is empty), say: -"I notice you haven't run `/idstack:assessment-design` yet. I can generate basic assessment -documents from the alignment matrix in your objectives, but running `/idstack:assessment-design` -first would give me richer assessment data — rubric criteria, feedback strategies, and -assessment type recommendations. Want to continue with what I have, or run +"You did not run `/idstack:assessment-design`. I can generate basic assessment +documents from the alignment matrix in your objectives. But `/idstack:assessment-design` +gives me more assessment data: rubric criteria, feedback strategies, and +assessment type recommendations. Do you want to continue with the data that I have, or run `/idstack:assessment-design` first?" --- @@ -588,13 +588,13 @@ Decide which mode this skill is operating in **before** running the content gene - **Build-new mode** — anything else (no manifest, no import_metadata, manual source, or zero modules in course_content). **Announce the chosen mode to the user as the first sentence:** -- "Mode: build-new. I'll generate the syllabus, module pages, and assessment documents from scratch." -- "Mode: gap-fill (cartridge import detected). The course already exists; I'll generate ONLY the artifacts that upstream skills flagged as missing — not a fresh syllabus or modules. Say 'rebuild' if you want a full regeneration anyway." +- "Mode: build-new. I will generate a new syllabus, new module pages, and new assessment documents." +- "Mode: gap-fill (idstack found a cartridge import). The course is available. I will generate ONLY the missing artifacts that upstream skills found. I will not generate a new syllabus or new modules. To generate all of the course again, say 'rebuild'." In gap-fill mode, **skip Steps 2–7 entirely** (instructor info, syllabus, module pages, assessments, rubrics, content review). Instead: 1. **Identify gaps.** Read `red_team_audit.top_actions` and `quality_review.recommendations` from the manifest (if those skills already ran) plus any explicit user requests. Each "missing artifact" finding becomes a generation target. -2. **Confirm with user.** Show the list of generation targets ("I'll generate: discussion rubric for Module 5, vision/mission framework module, formative practice quiz set"). Ask via `AskUserQuestion` which to generate (all / specific / skip). +2. **Confirm with user.** Show the list of generation targets ("I will generate: discussion rubric for Module 5, vision/mission framework module, formative practice quiz set"). Ask via `AskUserQuestion` which to generate (all / specific / skip). 3. **Generate only what's missing.** For each confirmed target, follow the relevant sub-step from Steps 4–6 below (e.g., generate one rubric, one module page, one quiz set) — but skip the full-course iteration. 4. **Record outputs.** Update `course_content.generated_files` (additive) and `course_content.recommended_generation_targets` (the list, with status `generated | deferred | declined`). @@ -642,7 +642,7 @@ Wait for user confirmation before proceeding. ### Step 2: Gather Instructor Information -"I need a few details that are not in the manifest:" +"Some information is not in the manifest. Give me this information:" Ask using AskUserQuestion: - Instructor name and title @@ -667,7 +667,7 @@ instructor and student — it must be clear, complete, and welcoming. ## Course Information - **Term:** [from context.timeline or placeholder] - **Modality:** [from context.modality] -- **Meeting times:** [placeholder or from context] +- **Class times:** [placeholder or from context] - **Location:** [placeholder or from context] ## Instructor Information @@ -681,7 +681,7 @@ administrators. Should answer: what is this course about, why does it matter, what will you be able to do after completing it.] ## Learning Objectives -By the end of this course, you will be able to: +When you complete this course, you can: 1. [ILO-1, rewritten in student-friendly language] 2. [ILO-2, rewritten in student-friendly language] ... @@ -705,24 +705,24 @@ By the end of this course, you will be able to: [Adapted to modality. Online: participation in discussions. F2F: attendance.] ### Late Work -[Institution policy or reasonable default: deduction per day, grace period, +[Institution policy as a Markdown block quotation, or a reasonable default: deduction per day, grace period, communication expectations.] ### Academic Integrity -[Standard academic integrity statement. Note: include specific guidance on +[Institution academic integrity statement as a Markdown block quotation. Note: include specific guidance on AI tool use if relevant to the discipline.] ### Accessibility -[ADA/accessibility statement. Point to institution disability services.] +[Institution ADA/accessibility statement as a Markdown block quotation. Point to institution disability services.] ### Technology Requirements [From context.available_tech. Include LMS, required software, hardware.] -## Required Materials -[From user input or manifest. If none specified, state "No required textbook. -All materials provided through the course site."] +## Course Materials +[From user input or manifest. If none specified, state "A textbook is not necessary for this course. +All of the materials are on the course site."] -## Getting Help +## Student Support - **Academic support:** [tutoring, writing center, library] - **Technical support:** [help desk, LMS support] - **Wellness resources:** [counseling, health services] @@ -732,9 +732,11 @@ All materials provided through the course site."] - Write the course description for students, not accreditation reviewers. It should make students want to take the course. - Rewrite ILOs in student-friendly language. "Evaluate research proposals for - ethical compliance using APA guidelines" becomes "You will learn to evaluate - whether a research study meets ethical standards, using APA's guidelines as - your framework." + ethical compliance using APA guidelines" becomes "You will learn to use the APA + guidelines to evaluate if a research study meets ethical standards." +- Institutional statements (academic integrity, accessibility, late work) are + institution text. Put the institution's own words in a Markdown block quotation + (lines that start with `> `). The writing check skips block quotations. - The schedule should show a clear progression. If spaced practice is built in, note where earlier topics resurface [CogLoad-6] [T1]. @@ -756,7 +758,7 @@ generation step and the most evidence-sensitive. Why does it matter? How does it connect to what came before and what comes next?] ## Module Objectives -By the end of this module, you will be able to: +When you complete this module, you can: - [Subset of course ILOs that map to this module] - [Module-specific enabling objectives if needed] @@ -772,14 +774,13 @@ lectures or for a student to understand the structure of the content.] ## Learning Activities ### Activity 1: [Title] -**Type:** [individual/group/discussion/lab/practice] -**Estimated time:** [minutes] -**Instructions:** -[Detailed, step-by-step instructions. A student should be able to complete -this activity from these instructions alone.] +- **Type:** [individual/group/discussion/lab/practice] +- **Estimated time:** [minutes] +- **Instructions:** [Detailed, step-by-step instructions. A student should be able to complete + this activity from these instructions alone.] **Purpose:** This activity helps you practice [cognitive operation] at the -[Bloom's level] level, preparing you for [related assessment]. +[Bloom's level] level. It prepares you for [related assessment]. ### Activity 2: [Title] ... @@ -797,11 +798,11 @@ Should connect to the module's ILOs at the appropriate cognitive level.] - [Specific guidance on what "substantive" means for this discussion] ## Connections to Assessment -- [Assessment name] addresses objectives from this module. See the assignment - description for details. +- [Assessment name] measures objectives from this module. For more information, read the + assignment description. - [If formative assessment exists: quiz, check-in, or practice activity] -## Summary and Looking Ahead +## Summary and Next Steps [2-3 sentences synthesizing key takeaways. Preview of next module and how it builds on this one. If spaced practice is planned, note what from this module will resurface later.] @@ -885,12 +886,12 @@ produce, how to produce it, and what "good" looks like.] ### Submission - **Format:** [file type, naming convention] -- **Submit via:** [LMS, email, in-class — placeholder if unknown] +- **Submit through:** [LMS, email, in-class — placeholder if unknown] - **Due:** [date placeholder] ## Rubric -| Criterion | Excellent (A) | Proficient (B) | Developing (C) | Beginning (D/F) | -|-----------|---------------|-----------------|-----------------|------------------| +| Criterion | Excellent (A) | Proficient (B) | Satisfactory (C) | Unsatisfactory (D/F) | +|-----------|---------------|-----------------|------------------|----------------------| | [criterion 1] | [description] | [description] | [description] | [description] | | [criterion 2] | [description] | [description] | [description] | [description] | ... @@ -898,11 +899,11 @@ produce, how to produce it, and what "good" looks like.] **Points:** [total points or weight] ## Feedback -You will receive feedback within [X] days of submission. Feedback will address -[what aspects — see rubric criteria]. [If peer review is part of the process, +You will get feedback in [X] days or less after you submit your work. The feedback +will tell you about [what aspects — see rubric criteria]. [If peer review is part of the process, describe it here.] -## Tips for Success +## Recommendations - [Practical advice derived from the rubric — what distinguishes excellent from proficient work] - [Common pitfalls to avoid] @@ -925,8 +926,8 @@ that can be imported into an LMS or printed for grading: **Total Points:** [points] -| Criterion | Weight | Excellent | Proficient | Developing | Beginning | -|-----------|--------|-----------|------------|------------|-----------| +| Criterion | Weight | Excellent | Proficient | Satisfactory | Unsatisfactory | +|-----------|--------|-----------|------------|--------------|----------------| | ... | ... | ... | ... | ... | ... | ``` @@ -1021,7 +1022,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Generated artifacts

        ` and an HTML `
        ` (columns: File, Purpose, Notes). - **Finding ids:** `cogload-1`, `multimedia-1`, `placeholder-1`, etc. Findings come from cognitive-load decisions made during generation, multimedia-principle applications and tradeoffs, places where placeholders were inserted, and expertise-reversal flags from the learner profile that shaped the build. In gap-fill mode, every finding should reference the upstream flag (e.g., `red-team:alignment-3`) that triggered the generation. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Placeholders used

        ` listing the placeholder fields the designer must fill in before the course is ready for learners. -- **Limitations:** content adaptation is structural, not voice/style; cognitive-load estimates are heuristic; placeholders are detected by template, not by reading the surrounding paragraph. +- **Limitations:** The content adaptation changes the structure, not the voice or style. The cognitive-load estimates are heuristic. The placeholder list comes from the template, not from the text around each placeholder. - **Next steps:** Review and edit the generated files to add your voice and institutional details. Then run `/idstack:course-quality-review` to audit the complete course against QM standards and CoI presence. **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -1117,12 +1118,12 @@ The `placeholders_used` array lists any fields where placeholder text was used b After writing the manifest, confirm: -"Your course content is generated. Three artifacts: +"I generated your course content. You have three outputs: - **Read this:** `.idstack/exports//course-builder.html` — the build report with evidence-backed design choices, cognitive-load notes, and a placeholders list. - Open it in any browser; the folder is self-contained. -- Course content: `.idstack/course-content/` (the actual files — review and add + Open it in a web browser. The folder has all of the files for the report. +- Course content: `.idstack/course-content/` (the course files — review them and add your voice and institutional details). - System state: `.idstack/project.json` (the manifest — for downstream skills). @@ -1177,7 +1178,7 @@ Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in the If you or the agent changed course files in this step, do the writing standard check at the end of Step 6 again. Show the user each sentence that you changed, before and after the change. **If Agent tool is NOT available:** Skip this step. Add a note to the output: -"Tip: Run `/idstack:course-quality-review` next for a full alignment audit." +"Note: For a full alignment audit, run `/idstack:course-quality-review` next." --- @@ -1290,7 +1291,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/course-builder/SKILL.md.tmpl b/skills/course-builder/SKILL.md.tmpl index 6b96c0a..dd61bd1 100644 --- a/skills/course-builder/SKILL.md.tmpl +++ b/skills/course-builder/SKILL.md.tmpl @@ -4,7 +4,7 @@ description: | Generate complete course content from the idstack manifest. Produces syllabus, module pages, assignment descriptions, and rubric documents. Content follows cognitive load principles and adapts to learner expertise level. Reads from - the full pipeline (needs, objectives, assessments) for richest output. (idstack) + the full pipeline (needs, objectives, assessments) for the best output. (idstack) allowed-tools: - Bash - Read @@ -19,7 +19,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `course_content` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Builder — Evidence-Based Content Generation @@ -120,7 +120,7 @@ The shared preamble above already ran the manifest existence check - `context` — at least `modality` and `timeline` **If NO_MANIFEST:** -- Say: "I need a project manifest with learning objectives to generate course content. +- Say: "idstack must have a project manifest with learning objectives to generate course content. Run `/idstack:needs-analysis` followed by `/idstack:learning-objectives` to build the foundation. If you have objectives ready, I can create a minimal manifest to work from — just tell me your learning objectives, course modality, and timeline." @@ -130,10 +130,10 @@ The shared preamble above already ran the manifest existence check **Nudge for assessment design:** If the manifest exists but has no `assessments` section (or it is empty), say: -"I notice you haven't run `/idstack:assessment-design` yet. I can generate basic assessment -documents from the alignment matrix in your objectives, but running `/idstack:assessment-design` -first would give me richer assessment data — rubric criteria, feedback strategies, and -assessment type recommendations. Want to continue with what I have, or run +"You did not run `/idstack:assessment-design`. I can generate basic assessment +documents from the alignment matrix in your objectives. But `/idstack:assessment-design` +gives me more assessment data: rubric criteria, feedback strategies, and +assessment type recommendations. Do you want to continue with the data that I have, or run `/idstack:assessment-design` first?" --- @@ -178,13 +178,13 @@ Decide which mode this skill is operating in **before** running the content gene - **Build-new mode** — anything else (no manifest, no import_metadata, manual source, or zero modules in course_content). **Announce the chosen mode to the user as the first sentence:** -- "Mode: build-new. I'll generate the syllabus, module pages, and assessment documents from scratch." -- "Mode: gap-fill (cartridge import detected). The course already exists; I'll generate ONLY the artifacts that upstream skills flagged as missing — not a fresh syllabus or modules. Say 'rebuild' if you want a full regeneration anyway." +- "Mode: build-new. I will generate a new syllabus, new module pages, and new assessment documents." +- "Mode: gap-fill (idstack found a cartridge import). The course is available. I will generate ONLY the missing artifacts that upstream skills found. I will not generate a new syllabus or new modules. To generate all of the course again, say 'rebuild'." In gap-fill mode, **skip Steps 2–7 entirely** (instructor info, syllabus, module pages, assessments, rubrics, content review). Instead: 1. **Identify gaps.** Read `red_team_audit.top_actions` and `quality_review.recommendations` from the manifest (if those skills already ran) plus any explicit user requests. Each "missing artifact" finding becomes a generation target. -2. **Confirm with user.** Show the list of generation targets ("I'll generate: discussion rubric for Module 5, vision/mission framework module, formative practice quiz set"). Ask via `AskUserQuestion` which to generate (all / specific / skip). +2. **Confirm with user.** Show the list of generation targets ("I will generate: discussion rubric for Module 5, vision/mission framework module, formative practice quiz set"). Ask via `AskUserQuestion` which to generate (all / specific / skip). 3. **Generate only what's missing.** For each confirmed target, follow the relevant sub-step from Steps 4–6 below (e.g., generate one rubric, one module page, one quiz set) — but skip the full-course iteration. 4. **Record outputs.** Update `course_content.generated_files` (additive) and `course_content.recommended_generation_targets` (the list, with status `generated | deferred | declined`). @@ -232,7 +232,7 @@ Wait for user confirmation before proceeding. ### Step 2: Gather Instructor Information -"I need a few details that are not in the manifest:" +"Some information is not in the manifest. Give me this information:" Ask using AskUserQuestion: - Instructor name and title @@ -257,7 +257,7 @@ instructor and student — it must be clear, complete, and welcoming. ## Course Information - **Term:** [from context.timeline or placeholder] - **Modality:** [from context.modality] -- **Meeting times:** [placeholder or from context] +- **Class times:** [placeholder or from context] - **Location:** [placeholder or from context] ## Instructor Information @@ -271,7 +271,7 @@ administrators. Should answer: what is this course about, why does it matter, what will you be able to do after completing it.] ## Learning Objectives -By the end of this course, you will be able to: +When you complete this course, you can: 1. [ILO-1, rewritten in student-friendly language] 2. [ILO-2, rewritten in student-friendly language] ... @@ -295,24 +295,24 @@ By the end of this course, you will be able to: [Adapted to modality. Online: participation in discussions. F2F: attendance.] ### Late Work -[Institution policy or reasonable default: deduction per day, grace period, +[Institution policy as a Markdown block quotation, or a reasonable default: deduction per day, grace period, communication expectations.] ### Academic Integrity -[Standard academic integrity statement. Note: include specific guidance on +[Institution academic integrity statement as a Markdown block quotation. Note: include specific guidance on AI tool use if relevant to the discipline.] ### Accessibility -[ADA/accessibility statement. Point to institution disability services.] +[Institution ADA/accessibility statement as a Markdown block quotation. Point to institution disability services.] ### Technology Requirements [From context.available_tech. Include LMS, required software, hardware.] -## Required Materials -[From user input or manifest. If none specified, state "No required textbook. -All materials provided through the course site."] +## Course Materials +[From user input or manifest. If none specified, state "A textbook is not necessary for this course. +All of the materials are on the course site."] -## Getting Help +## Student Support - **Academic support:** [tutoring, writing center, library] - **Technical support:** [help desk, LMS support] - **Wellness resources:** [counseling, health services] @@ -322,9 +322,11 @@ All materials provided through the course site."] - Write the course description for students, not accreditation reviewers. It should make students want to take the course. - Rewrite ILOs in student-friendly language. "Evaluate research proposals for - ethical compliance using APA guidelines" becomes "You will learn to evaluate - whether a research study meets ethical standards, using APA's guidelines as - your framework." + ethical compliance using APA guidelines" becomes "You will learn to use the APA + guidelines to evaluate if a research study meets ethical standards." +- Institutional statements (academic integrity, accessibility, late work) are + institution text. Put the institution's own words in a Markdown block quotation + (lines that start with `> `). The writing check skips block quotations. - The schedule should show a clear progression. If spaced practice is built in, note where earlier topics resurface [CogLoad-6] [T1]. @@ -346,7 +348,7 @@ generation step and the most evidence-sensitive. Why does it matter? How does it connect to what came before and what comes next?] ## Module Objectives -By the end of this module, you will be able to: +When you complete this module, you can: - [Subset of course ILOs that map to this module] - [Module-specific enabling objectives if needed] @@ -362,14 +364,13 @@ lectures or for a student to understand the structure of the content.] ## Learning Activities ### Activity 1: [Title] -**Type:** [individual/group/discussion/lab/practice] -**Estimated time:** [minutes] -**Instructions:** -[Detailed, step-by-step instructions. A student should be able to complete -this activity from these instructions alone.] +- **Type:** [individual/group/discussion/lab/practice] +- **Estimated time:** [minutes] +- **Instructions:** [Detailed, step-by-step instructions. A student should be able to complete + this activity from these instructions alone.] **Purpose:** This activity helps you practice [cognitive operation] at the -[Bloom's level] level, preparing you for [related assessment]. +[Bloom's level] level. It prepares you for [related assessment]. ### Activity 2: [Title] ... @@ -387,11 +388,11 @@ Should connect to the module's ILOs at the appropriate cognitive level.] - [Specific guidance on what "substantive" means for this discussion] ## Connections to Assessment -- [Assessment name] addresses objectives from this module. See the assignment - description for details. +- [Assessment name] measures objectives from this module. For more information, read the + assignment description. - [If formative assessment exists: quiz, check-in, or practice activity] -## Summary and Looking Ahead +## Summary and Next Steps [2-3 sentences synthesizing key takeaways. Preview of next module and how it builds on this one. If spaced practice is planned, note what from this module will resurface later.] @@ -475,12 +476,12 @@ produce, how to produce it, and what "good" looks like.] ### Submission - **Format:** [file type, naming convention] -- **Submit via:** [LMS, email, in-class — placeholder if unknown] +- **Submit through:** [LMS, email, in-class — placeholder if unknown] - **Due:** [date placeholder] ## Rubric -| Criterion | Excellent (A) | Proficient (B) | Developing (C) | Beginning (D/F) | -|-----------|---------------|-----------------|-----------------|------------------| +| Criterion | Excellent (A) | Proficient (B) | Satisfactory (C) | Unsatisfactory (D/F) | +|-----------|---------------|-----------------|------------------|----------------------| | [criterion 1] | [description] | [description] | [description] | [description] | | [criterion 2] | [description] | [description] | [description] | [description] | ... @@ -488,11 +489,11 @@ produce, how to produce it, and what "good" looks like.] **Points:** [total points or weight] ## Feedback -You will receive feedback within [X] days of submission. Feedback will address -[what aspects — see rubric criteria]. [If peer review is part of the process, +You will get feedback in [X] days or less after you submit your work. The feedback +will tell you about [what aspects — see rubric criteria]. [If peer review is part of the process, describe it here.] -## Tips for Success +## Recommendations - [Practical advice derived from the rubric — what distinguishes excellent from proficient work] - [Common pitfalls to avoid] @@ -515,8 +516,8 @@ that can be imported into an LMS or printed for grading: **Total Points:** [points] -| Criterion | Weight | Excellent | Proficient | Developing | Beginning | -|-----------|--------|-----------|------------|------------|-----------| +| Criterion | Weight | Excellent | Proficient | Satisfactory | Unsatisfactory | +|-----------|--------|-----------|------------|--------------|----------------| | ... | ... | ... | ... | ... | ... | ``` @@ -577,7 +578,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Generated artifacts

        ` and an HTML `
        ` (columns: File, Purpose, Notes). - **Finding ids:** `cogload-1`, `multimedia-1`, `placeholder-1`, etc. Findings come from cognitive-load decisions made during generation, multimedia-principle applications and tradeoffs, places where placeholders were inserted, and expertise-reversal flags from the learner profile that shaped the build. In gap-fill mode, every finding should reference the upstream flag (e.g., `red-team:alignment-3`) that triggered the generation. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Placeholders used

        ` listing the placeholder fields the designer must fill in before the course is ready for learners. -- **Limitations:** content adaptation is structural, not voice/style; cognitive-load estimates are heuristic; placeholders are detected by template, not by reading the surrounding paragraph. +- **Limitations:** The content adaptation changes the structure, not the voice or style. The cognitive-load estimates are heuristic. The placeholder list comes from the template, not from the text around each placeholder. - **Next steps:** Review and edit the generated files to add your voice and institutional details. Then run `/idstack:course-quality-review` to audit the complete course against QM standards and CoI presence. **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -639,12 +640,12 @@ The `placeholders_used` array lists any fields where placeholder text was used b After writing the manifest, confirm: -"Your course content is generated. Three artifacts: +"I generated your course content. You have three outputs: - **Read this:** `.idstack/exports//course-builder.html` — the build report with evidence-backed design choices, cognitive-load notes, and a placeholders list. - Open it in any browser; the folder is self-contained. -- Course content: `.idstack/course-content/` (the actual files — review and add + Open it in a web browser. The folder has all of the files for the report. +- Course content: `.idstack/course-content/` (the course files — review them and add your voice and institutional details). - System state: `.idstack/project.json` (the manifest — for downstream skills). @@ -699,7 +700,7 @@ Put a copy of the "Writing Standard (ASD-STE100)" section of the preamble in the If you or the agent changed course files in this step, do the writing standard check at the end of Step 6 again. Show the user each sentence that you changed, before and after the change. **If Agent tool is NOT available:** Skip this step. Add a note to the output: -"Tip: Run `/idstack:course-quality-review` next for a full alignment audit." +"Note: For a full alignment audit, run `/idstack:course-quality-review` next." --- diff --git a/skills/course-export/SKILL.md b/skills/course-export/SKILL.md index 471cd41..499dfa3 100644 --- a/skills/course-export/SKILL.md +++ b/skills/course-export/SKILL.md @@ -3,7 +3,7 @@ name: course-export description: | Export course content to any LMS. Generates IMS Common Cartridge files (.imscc) compatible with Canvas, Blackboard, Moodle, and D2L, or pushes directly to - Canvas via REST API. Reads from /course-builder output and the idstack manifest. + Canvas through the REST API. Reads from /course-builder output and the idstack manifest. The output IS the course. (idstack) allowed-tools: - Bash @@ -206,7 +206,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -287,8 +287,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -314,8 +314,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -421,15 +421,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `export_metadata` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" ## Pre-Export Readiness Check @@ -459,8 +459,8 @@ done Show the readiness table to the user. If the verdict is: - **READY TO EXPORT:** Proceed normally. -- **ISSUES:** Show the issues and ask: "There are unresolved issues. Continue with export anyway?" -- **INCOMPLETE:** Show what's missing and ask: "Some review skills haven't run yet. Continue with export anyway, or run the missing skills first?" +- **NOT-READY:** Show the problems and ask: "The readiness check found problems. Do you want to export the course, or correct the problems first?" +- **INCOMPLETE:** Show what's missing and ask: "Some review skills did not run. Do you want to export the course, or run those skills first?" This is advisory — the user can always choose to export regardless. @@ -522,7 +522,7 @@ fi ``` **If NO_MANIFEST or NO_CONTENT:** -Tell the user: "I need generated course content to export. Run `/idstack:course-builder` +Tell the user: "idstack must have generated course content to export. Run `/idstack:course-builder` first to generate your syllabus, modules, and assessments. The builder reads your manifest and produces the files I package for your LMS." @@ -856,8 +856,8 @@ it is visible to both instructors and students after import. significantly larger learning gains [Assessment-8] [T1] - Quiz question generation is best-effort. The instructional designer should review and edit questions in the LMS after import. Flag this clearly: - "Quiz questions are auto-generated from your assessment specs. Review each - question in your LMS before publishing to students." + "idstack generated the quiz questions from your assessment specs. Review each + question in your LMS before you publish the quiz to students." - Support these question types: multiple choice, true/false, short answer (essay type in QTI) @@ -931,8 +931,8 @@ Contents: - {Q} discussion topics (.xml) - Syllabus (.html) -Quiz questions are auto-generated from your assessment specs. Review each -question in your LMS before publishing to students. +idstack generated the quiz questions from your assessment specs. Review each +question in your LMS before you publish the quiz to students. To import into your LMS: - **Canvas:** Settings > Import Course Content > Common Cartridge 1.x @@ -949,19 +949,19 @@ To import into your LMS: Ask the user for Canvas connection details. Use AskUserQuestion: -"I need three things to push your course to Canvas: +"idstack must have three things to push your course to Canvas: 1. **Canvas URL** — Your institution's Canvas address - (e.g., `https://canvas.university.edu`) + (for example, `https://canvas.university.edu`) 2. **Access token** — Generate one in Canvas: Account > Settings > scroll to 'Approved Integrations' > New Access Token 3. **Course ID** — The number in the URL when you open the course - (e.g., `https://canvas.university.edu/courses/12345` > course ID is `12345`) + (for example, `https://canvas.university.edu/courses/12345` > course ID is `12345`) Use an existing empty course shell, or create a new course first in Canvas. -Your token is used for this session only and is NEVER saved to any file." +idstack uses your token for this session only. It does NOT save the token to a file." ### B2. Validate Connection @@ -978,9 +978,9 @@ echo "$BODY" | head -5 Handle errors: - HTTP 401: "Token rejected. Make sure you copied the full token. In Canvas: Account > Settings > New Access Token." -- HTTP 403: "Access denied. Your token may not have the right permissions for - this course. You need at least Teacher or Designer role." -- Network error: "Can't reach Canvas at that URL. Check the address and make +- HTTP 403: "Access denied. Your token does not have the necessary permissions for + this course. You must have the Teacher role, the Designer role or a higher role." +- Network error: "idstack cannot connect to Canvas at that URL. Check the address and make sure it includes `https://`." Verify course access: @@ -992,8 +992,8 @@ curl -s -w "\n%{http_code}" \ - HTTP 404: "Course not found. Check the course ID. You can find it in the URL when you open the course in Canvas." -- HTTP 403: "You don't have access to this course. Ask your Canvas admin for - Teacher or Designer role." +- HTTP 403: "You do not have access to this course. Get the Teacher or Designer + role from your Canvas admin." **SECURITY RULE: The token variable is used ONLY in curl commands within this section. NEVER write the token to the manifest, to any file, or to conversation @@ -1126,7 +1126,7 @@ Each API call is wrapped in error checking. Handle these cases: - **HTTP 422:** Validation error. Report the error message from Canvas. Common causes: duplicate page titles, missing required fields. - **HTTP 429:** Rate limited. Wait 10 seconds, retry once. If still 429: - "Canvas is rate-limiting requests. Waiting 30 seconds before continuing." + "Canvas limits the request rate. idstack waits 30 seconds and then continues." Wait 30 seconds and retry. If still failing, log the item as failed and continue with the rest. - **Timeout / network error:** Log the item as failed, continue with the rest. @@ -1150,8 +1150,8 @@ URL: {Canvas URL}/courses/{course_id} | Assignments | {P} | {0} | {0} | | Discussions | {Q} | {0} | {0} | -Assignments and discussions are unpublished. Review them in Canvas before -publishing to students. +Assignments and discussions are not published. Review them in Canvas before +you publish them to students. Open your course: {Canvas URL}/courses/{course_id} ``` @@ -1311,17 +1311,17 @@ Total files: [count] - **Canvas:** Settings > Import Course Content > SCORM package - **Moodle:** Add Activity > SCORM package > Upload - **Blackboard:** Content > Build Content > SCORM package -- **Corporate LMS (Cornerstone, SAP SuccessFactors, etc.):** +- **Corporate LMS (for example, Cornerstone or SAP SuccessFactors):** Upload through your content management interface ### Limitations -- This SCORM package contains static HTML content. Interactive elements - (drag-and-drop, branching scenarios) are not generated. -- SCORM API tracking (completion, score reporting) is not included. - The LMS will mark the SCO as complete when the learner opens it. -- For richer interactivity, author in Articulate Rise or Storyline and - use idstack's /idstack:course-quality-review and /idstack:red-team on the exported package. +- This SCORM package contains static HTML content. idstack does not generate + interactive elements (drag-and-drop, branching scenarios). +- The package does not include SCORM API tracking (completion, score reports). + The LMS marks the SCO as completed when the learner opens it. +- For more interactivity, create the course in Articulate Rise or Storyline. Then + use /idstack:course-quality-review and /idstack:red-team on the exported package. ``` ### C8. Cleanup @@ -1339,12 +1339,12 @@ After the export completes (any path), write the HTML report at `$_REPORT_PATH` - **`{{skill_title}}`:** "Course Export Report" - **`{{skill_name}}`:** `course-export` - **`{{mode}}`:** include `format: imscc|canvas-api|scorm` in the header `meta` line. -- **Summary:** 2–3 sentences. What was exported, where it landed, and the readiness verdict (clean / with warnings) at the time of export. Include the optional one-line scoreboard: "Modules N · Pages M · Assignments P · Quizzes Q · Discussions R". +- **Summary:** 2–3 sentences. What was exported, where it landed, and the readiness verdict (READY TO EXPORT / NOT-READY / INCOMPLETE) at the time of export. Include the optional one-line scoreboard: "Modules N · Pages M · Assignments P · Quizzes Q · Discussions R". - **Skill-specific section before Findings** — add a `
        ` with `

        Export contents

        ` and an HTML `
        ` (Item type, Count, Notes). For Canvas API: include Created / Failed / Skipped columns instead. - **Finding ids:** `export-1`, `readiness-1`, `qti-1`, etc. Findings come from auto-generated content the designer should review before publishing (quiz questions, rubric formatting, missing placeholders), readiness-check items at time of export, and items the API call failed on. If everything is clean, the Findings section can simply contain an `info` finding noting the clean state. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Failed items

        ` only when the Canvas API path produced failed items. -- **Limitations:** SCORM/IMSCC packages contain static HTML; interactive elements aren't generated. Quiz questions are auto-generated from assessment specs and need designer review. Tokens are never written to disk. -- **Next steps:** Verify that the LMS import preserved everything correctly. Pay particular attention to quiz questions, assignment rubrics, discussion prompts, and module sequencing. If the readiness verdict was "with warnings," address the flagged findings before publishing. +- **Limitations:** SCORM and IMSCC packages contain static HTML only. They do not include interactive elements. The quiz questions come from the assessment specs, and the designer must review them. The export does not write tokens to disk. +- **Next steps:** Make sure that the LMS import kept all of the content correctly. Look carefully at quiz questions, assignment rubrics, discussion prompts, and the module sequence. If the readiness verdict was NOT-READY, correct the flagged findings before you publish the course. **Writing standard check.** In this command, replace `` with the path of `course-export.html` in the folder from the "Course export folder:" line. Then run the command. @@ -1480,14 +1480,14 @@ sections from the manifest (if they exist). The `verdict` is: Run the merge, then confirm: -"Your export metadata has been saved to `.idstack/project.json`. +"I saved your export metadata to `.idstack/project.json`. -Your course has been exported. Verify that the LMS import preserved -everything correctly. Pay particular attention to: -- Quiz questions (auto-generated, may need editing) -- Assignment rubrics (verify formatting survived the transfer) -- Discussion prompts (check that instructions are clear) -- Module sequencing (verify order matches your intended flow)" +I exported your course. Make sure that the LMS import kept +all of the content correctly. Look carefully at: +- Quiz questions (idstack generated them. If necessary, edit them.) +- Assignment rubrics (make sure that the format did not change in the transfer) +- Discussion prompts (make sure that the instructions are clear) +- Module sequence (make sure that the module sequence is correct)" --- @@ -1600,7 +1600,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/course-export/SKILL.md.tmpl b/skills/course-export/SKILL.md.tmpl index d40e16b..eb119bc 100644 --- a/skills/course-export/SKILL.md.tmpl +++ b/skills/course-export/SKILL.md.tmpl @@ -3,7 +3,7 @@ name: course-export description: | Export course content to any LMS. Generates IMS Common Cartridge files (.imscc) compatible with Canvas, Blackboard, Moodle, and D2L, or pushes directly to - Canvas via REST API. Reads from /course-builder output and the idstack manifest. + Canvas through the REST API. Reads from /course-builder output and the idstack manifest. The output IS the course. (idstack) allowed-tools: - Bash @@ -19,7 +19,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `export_metadata` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" ## Pre-Export Readiness Check @@ -32,8 +32,8 @@ Before starting the export workflow, run the readiness dashboard: Show the readiness table to the user. If the verdict is: - **READY TO EXPORT:** Proceed normally. -- **ISSUES:** Show the issues and ask: "There are unresolved issues. Continue with export anyway?" -- **INCOMPLETE:** Show what's missing and ask: "Some review skills haven't run yet. Continue with export anyway, or run the missing skills first?" +- **NOT-READY:** Show the problems and ask: "The readiness check found problems. Do you want to export the course, or correct the problems first?" +- **INCOMPLETE:** Show what's missing and ask: "Some review skills did not run. Do you want to export the course, or run those skills first?" This is advisory — the user can always choose to export regardless. @@ -95,7 +95,7 @@ fi ``` **If NO_MANIFEST or NO_CONTENT:** -Tell the user: "I need generated course content to export. Run `/idstack:course-builder` +Tell the user: "idstack must have generated course content to export. Run `/idstack:course-builder` first to generate your syllabus, modules, and assessments. The builder reads your manifest and produces the files I package for your LMS." @@ -395,8 +395,8 @@ it is visible to both instructors and students after import. significantly larger learning gains [Assessment-8] [T1] - Quiz question generation is best-effort. The instructional designer should review and edit questions in the LMS after import. Flag this clearly: - "Quiz questions are auto-generated from your assessment specs. Review each - question in your LMS before publishing to students." + "idstack generated the quiz questions from your assessment specs. Review each + question in your LMS before you publish the quiz to students." - Support these question types: multiple choice, true/false, short answer (essay type in QTI) @@ -470,8 +470,8 @@ Contents: - {Q} discussion topics (.xml) - Syllabus (.html) -Quiz questions are auto-generated from your assessment specs. Review each -question in your LMS before publishing to students. +idstack generated the quiz questions from your assessment specs. Review each +question in your LMS before you publish the quiz to students. To import into your LMS: - **Canvas:** Settings > Import Course Content > Common Cartridge 1.x @@ -488,19 +488,19 @@ To import into your LMS: Ask the user for Canvas connection details. Use AskUserQuestion: -"I need three things to push your course to Canvas: +"idstack must have three things to push your course to Canvas: 1. **Canvas URL** — Your institution's Canvas address - (e.g., `https://canvas.university.edu`) + (for example, `https://canvas.university.edu`) 2. **Access token** — Generate one in Canvas: Account > Settings > scroll to 'Approved Integrations' > New Access Token 3. **Course ID** — The number in the URL when you open the course - (e.g., `https://canvas.university.edu/courses/12345` > course ID is `12345`) + (for example, `https://canvas.university.edu/courses/12345` > course ID is `12345`) Use an existing empty course shell, or create a new course first in Canvas. -Your token is used for this session only and is NEVER saved to any file." +idstack uses your token for this session only. It does NOT save the token to a file." ### B2. Validate Connection @@ -517,9 +517,9 @@ echo "$BODY" | head -5 Handle errors: - HTTP 401: "Token rejected. Make sure you copied the full token. In Canvas: Account > Settings > New Access Token." -- HTTP 403: "Access denied. Your token may not have the right permissions for - this course. You need at least Teacher or Designer role." -- Network error: "Can't reach Canvas at that URL. Check the address and make +- HTTP 403: "Access denied. Your token does not have the necessary permissions for + this course. You must have the Teacher role, the Designer role or a higher role." +- Network error: "idstack cannot connect to Canvas at that URL. Check the address and make sure it includes `https://`." Verify course access: @@ -531,8 +531,8 @@ curl -s -w "\n%{http_code}" \ - HTTP 404: "Course not found. Check the course ID. You can find it in the URL when you open the course in Canvas." -- HTTP 403: "You don't have access to this course. Ask your Canvas admin for - Teacher or Designer role." +- HTTP 403: "You do not have access to this course. Get the Teacher or Designer + role from your Canvas admin." **SECURITY RULE: The token variable is used ONLY in curl commands within this section. NEVER write the token to the manifest, to any file, or to conversation @@ -665,7 +665,7 @@ Each API call is wrapped in error checking. Handle these cases: - **HTTP 422:** Validation error. Report the error message from Canvas. Common causes: duplicate page titles, missing required fields. - **HTTP 429:** Rate limited. Wait 10 seconds, retry once. If still 429: - "Canvas is rate-limiting requests. Waiting 30 seconds before continuing." + "Canvas limits the request rate. idstack waits 30 seconds and then continues." Wait 30 seconds and retry. If still failing, log the item as failed and continue with the rest. - **Timeout / network error:** Log the item as failed, continue with the rest. @@ -689,8 +689,8 @@ URL: {Canvas URL}/courses/{course_id} | Assignments | {P} | {0} | {0} | | Discussions | {Q} | {0} | {0} | -Assignments and discussions are unpublished. Review them in Canvas before -publishing to students. +Assignments and discussions are not published. Review them in Canvas before +you publish them to students. Open your course: {Canvas URL}/courses/{course_id} ``` @@ -850,17 +850,17 @@ Total files: [count] - **Canvas:** Settings > Import Course Content > SCORM package - **Moodle:** Add Activity > SCORM package > Upload - **Blackboard:** Content > Build Content > SCORM package -- **Corporate LMS (Cornerstone, SAP SuccessFactors, etc.):** +- **Corporate LMS (for example, Cornerstone or SAP SuccessFactors):** Upload through your content management interface ### Limitations -- This SCORM package contains static HTML content. Interactive elements - (drag-and-drop, branching scenarios) are not generated. -- SCORM API tracking (completion, score reporting) is not included. - The LMS will mark the SCO as complete when the learner opens it. -- For richer interactivity, author in Articulate Rise or Storyline and - use idstack's /idstack:course-quality-review and /idstack:red-team on the exported package. +- This SCORM package contains static HTML content. idstack does not generate + interactive elements (drag-and-drop, branching scenarios). +- The package does not include SCORM API tracking (completion, score reports). + The LMS marks the SCO as completed when the learner opens it. +- For more interactivity, create the course in Articulate Rise or Storyline. Then + use /idstack:course-quality-review and /idstack:red-team on the exported package. ``` ### C8. Cleanup @@ -878,12 +878,12 @@ After the export completes (any path), write the HTML report at `$_REPORT_PATH` - **`{{skill_title}}`:** "Course Export Report" - **`{{skill_name}}`:** `course-export` - **`{{mode}}`:** include `format: imscc|canvas-api|scorm` in the header `meta` line. -- **Summary:** 2–3 sentences. What was exported, where it landed, and the readiness verdict (clean / with warnings) at the time of export. Include the optional one-line scoreboard: "Modules N · Pages M · Assignments P · Quizzes Q · Discussions R". +- **Summary:** 2–3 sentences. What was exported, where it landed, and the readiness verdict (READY TO EXPORT / NOT-READY / INCOMPLETE) at the time of export. Include the optional one-line scoreboard: "Modules N · Pages M · Assignments P · Quizzes Q · Discussions R". - **Skill-specific section before Findings** — add a `
        ` with `

        Export contents

        ` and an HTML `
        ` (Item type, Count, Notes). For Canvas API: include Created / Failed / Skipped columns instead. - **Finding ids:** `export-1`, `readiness-1`, `qti-1`, etc. Findings come from auto-generated content the designer should review before publishing (quiz questions, rubric formatting, missing placeholders), readiness-check items at time of export, and items the API call failed on. If everything is clean, the Findings section can simply contain an `info` finding noting the clean state. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Failed items

        ` only when the Canvas API path produced failed items. -- **Limitations:** SCORM/IMSCC packages contain static HTML; interactive elements aren't generated. Quiz questions are auto-generated from assessment specs and need designer review. Tokens are never written to disk. -- **Next steps:** Verify that the LMS import preserved everything correctly. Pay particular attention to quiz questions, assignment rubrics, discussion prompts, and module sequencing. If the readiness verdict was "with warnings," address the flagged findings before publishing. +- **Limitations:** SCORM and IMSCC packages contain static HTML only. They do not include interactive elements. The quiz questions come from the assessment specs, and the designer must review them. The export does not write tokens to disk. +- **Next steps:** Make sure that the LMS import kept all of the content correctly. Look carefully at quiz questions, assignment rubrics, discussion prompts, and the module sequence. If the readiness verdict was NOT-READY, correct the flagged findings before you publish the course. **Writing standard check.** In this command, replace `` with the path of `course-export.html` in the folder from the "Course export folder:" line. Then run the command. @@ -985,14 +985,14 @@ sections from the manifest (if they exist). The `verdict` is: Run the merge, then confirm: -"Your export metadata has been saved to `.idstack/project.json`. +"I saved your export metadata to `.idstack/project.json`. -Your course has been exported. Verify that the LMS import preserved -everything correctly. Pay particular attention to: -- Quiz questions (auto-generated, may need editing) -- Assignment rubrics (verify formatting survived the transfer) -- Discussion prompts (check that instructions are clear) -- Module sequencing (verify order matches your intended flow)" +I exported your course. Make sure that the LMS import kept +all of the content correctly. Look carefully at: +- Quiz questions (idstack generated them. If necessary, edit them.) +- Assignment rubrics (make sure that the format did not change in the transfer) +- Discussion prompts (make sure that the instructions are clear) +- Module sequence (make sure that the module sequence is correct)" --- diff --git a/skills/course-import/SKILL.md b/skills/course-import/SKILL.md index 8a0b14c..8440acc 100644 --- a/skills/course-import/SKILL.md +++ b/skills/course-import/SKILL.md @@ -206,7 +206,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -287,8 +287,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -314,8 +314,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -421,15 +421,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `import_metadata` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Import — Universal LMS Course Import @@ -508,8 +508,8 @@ Options: ### A1. Get the file path -Ask: "Where is your .imscc file? Provide the file path (drag and drop the file -into this window to paste the path)." +Ask: "Where is your .imscc file? Give the file path. To paste the path, drag the file +into this window." ### A2. Validate and extract @@ -524,8 +524,8 @@ fi ``` If FILE_NOT_FOUND: "File not found at that path. Check the path and try again." -If not a ZIP: "This doesn't look like a Common Cartridge file. It should be a -.imscc file exported from your LMS." +If not a ZIP: "This file does not look like a Common Cartridge file. Use the +.imscc file that your LMS exports." Extract the cartridge. **Use `mktemp -d` with no other flags** — `-t` on macOS treats the argument as a literal prefix instead of substituting the `XXXXXX`, producing a broken @@ -550,8 +550,8 @@ else fi ``` -If no imsmanifest.xml found: "This ZIP doesn't contain an IMS manifest. Is this -a Common Cartridge export? Try re-exporting from your LMS." +If no imsmanifest.xml found: "This ZIP does not contain an IMS manifest. Is this +a Common Cartridge export? Export the course from your LMS again." Read the manifest XML: @@ -619,11 +619,11 @@ Continue to Step 2 (Quality Flags). ### B1. Get the documents -Ask: "Paste your course documents below. This could be a syllabus, module outline, -assignment list, or course description. The more detail you provide, the better I -can map your course structure. +Ask: "Paste your course documents below. For example, paste a syllabus, a module outline, +an assignment list, or a course description. If you give more information, I can +map your course structure better. -Paste the content and I'll extract the structure." +Paste the content, and I will identify the structure." ### B2. Extract structure @@ -671,11 +671,11 @@ Continue to Step 2 (Quality Flags). ### D1. Get the file path -Ask: "Where is your PDF or document file? Provide the file path (drag and drop the -file into this window to paste the path). +Ask: "Where is your PDF or document file? Give the file path. To paste the path, drag the +file into this window. This works with PDFs exported from Articulate Rise, Storyline, Adobe Captivate, -or any authoring tool. Also works with Word documents, course packets, and syllabus PDFs." +or a different authoring tool. It also works with Word documents, course packets, and syllabus PDFs." ### D2. Read the file @@ -687,8 +687,8 @@ If the file does not exist, ask the user to check the path. If the PDF is large (more than 10 pages), read in chunks using the `pages` parameter: - First pass: pages "1-10" - If more content exists: pages "11-20", etc. -- Maximum 50 pages total. If the PDF is longer, note: "Reading first 50 pages. - If important content is after page 50, let me know which pages to focus on." +- Maximum 50 pages total. If the PDF is longer, note: "idstack reads the first 50 pages only. + If important content is after page 50, tell me which pages to read." ### D3. Extract structure @@ -725,15 +725,15 @@ Continue to Step 2 (Quality Flags). Ask: "I need two things to connect to Canvas: 1. **Canvas URL** — Your institution's Canvas address - (e.g., `https://canvas.university.edu`) + (for example, `https://canvas.university.edu`) 2. **Access token** — Generate one in Canvas: Account → Settings → scroll to 'Approved Integrations' → New Access Token 3. **Course ID** — The number in the URL when you open the course - (e.g., `https://canvas.university.edu/courses/12345` → course ID is `12345`) + (for example, `https://canvas.university.edu/courses/12345` → course ID is `12345`) -Your token is used for this session only and is NEVER saved to any file." +idstack uses your token for this session only. It does NOT save the token to a file." ### C2. Validate connection @@ -750,8 +750,8 @@ echo "$BODY" | head -5 Handle errors: - HTTP 401: "Token rejected. Make sure you copied the full token. In Canvas: Account → Settings → New Access Token." -- HTTP 403: "Access denied. Your token may not have the right permissions." -- Network error: "Can't reach Canvas at that URL. Check the address." +- HTTP 403: "Access denied. Your token does not have the necessary permissions." +- Network error: "idstack cannot connect to Canvas at that URL. Check the address." ### C3. Fetch course data @@ -809,9 +809,9 @@ it for up to 10 pages (500 items max per endpoint). After 500 items, stop and no **Error handling for each call:** - 404: Skip this endpoint, note what's missing -- 429: Wait 10 seconds, retry once. If still 429: "Canvas is rate-limiting. - Wait a minute and try `/idstack:course-import` again." -- Timeout: "Canvas didn't respond for [endpoint]. Continuing with what we have." +- 429: Wait 10 seconds, retry once. If still 429: "Canvas limits the request rate. + Wait one minute and run `/idstack:course-import` again." +- Timeout: "Canvas did not send a response for [endpoint]. idstack continues with the data that it has." ### C4. Map API response to course structure @@ -832,8 +832,8 @@ Continue to Step 2 (Quality Flags). ### E1. Get the file path -Ask: "Where is your SCORM package (.zip)? Provide the file path (drag and drop the -file into this window to paste the path). +Ask: "Where is your SCORM package (.zip)? Give the file path. To paste the path, drag the +file into this window. This works with SCORM 1.2 and SCORM 2004 packages from Articulate Rise, Storyline, Adobe Captivate, Lectora, iSpring, or any SCORM-compliant authoring tool." @@ -850,8 +850,8 @@ fi ``` If FILE_NOT_FOUND: "File not found at that path. Check the path and try again." -If not a ZIP: "This doesn't look like a SCORM package. It should be a .zip file -exported from your authoring tool." +If not a ZIP: "This file does not look like a SCORM package. Use the .zip file +that your authoring tool exports." Extract the package: @@ -873,16 +873,16 @@ else fi ``` -If NO_SCORM_MANIFEST: "No imsmanifest.xml found in this ZIP. This may not be a valid -SCORM package. Try exporting again from your authoring tool, or use Path D (PDF import) -instead." +If NO_SCORM_MANIFEST: "This ZIP does not contain imsmanifest.xml. Without this file, it is not a valid +SCORM package. Export the package from your authoring tool again, or use Path D (PDF import) +for this file." ### E4. Detect SCORM version From the manifest XML, check namespaces and schema references: - If `adlcp_rootv1p2` or `adlcp:scormtype` (lowercase) → SCORM 1.2 - If `adlcp_v1p3` or `adlcp:scormType` (camelCase) → SCORM 2004 -- Note the version for the user: "Detected SCORM [version] package." +- Note the version for the user: "idstack found a SCORM [version] package." ### E5. Parse manifest structure @@ -987,19 +987,19 @@ flags problems visible in the structural data alone. - Check for accessibility info. Flag if none: "⚠ No accessibility information found for course materials" - Check for learner support resources. Flag if none: - "⚠ No learner support section detected (tutoring, office hours, tech support)" + "⚠ idstack found no learner support section (tutoring, office hours, tech support)" **Alignment flags:** - If objectives exist AND assessments exist but no clear mapping between them: - "⚠ Objectives and assessments found but no alignment mapping detected" + "⚠ The course has objectives and assessments, but idstack found no alignment matrix between them" - If all assessments are the same type (e.g., all quizzes): - "⚠ All {X} assessments are {type} — consider varied assessment for different + "⚠ All {X} assessments are {type}. idstack recommends different assessment types for different Bloom's levels [Assessment-10] [T1]" **Assessment feedback flags:** - If quizzes are auto-graded with no indication of elaborated feedback: - "⚠ Auto-graded assessments detected. Elaborated feedback (explaining WHY) produces - larger learning gains than correctness-only feedback [Assessment-8] [T1]" + "⚠ idstack found auto-graded assessments. Elaborated feedback (that explains WHY) increases + learning more than correctness-only feedback [Assessment-8] [T1]" Present the flags: @@ -1013,7 +1013,7 @@ These are quick observations from the course structure, not a full review. Run /idstack:course-quality-review for an evidence-based audit with specific recommendations. ``` -If zero flags: "No obvious structural issues detected during import. Run +If zero flags: "idstack found no structural problems in the import. Run /idstack:course-quality-review for a deeper analysis." --- @@ -1052,15 +1052,15 @@ Present for user review: ``` ## Inferred Task Analysis -I've mapped your {N} modules to task analysis entries. Please review and adjust: +I mapped your {N} modules to task analysis entries. Review and adjust them: | ID | Task | Frequency | Criticality | Difficulty | |----|------|-----------|-------------|------------| | T-1 | [performance statement] | [est.] | [est.] | [est.] | ... -These estimates are based on module structure. Edit any that don't match your -actual course context. +These estimates come from the module structure. Edit each estimate that is not correct for your +course context. ``` Ask the user to confirm or edit via AskUserQuestion. @@ -1092,7 +1092,7 @@ For each objective: 4. **Confidence level:** - High: verb maps clearly to one Bloom's level - Ambiguous: verb could map to multiple levels (e.g., "analyze", "demonstrate") - Mark as "verify with /idstack:learning-objectives" [Alignment-12] [T2] + Mark as "check with /idstack:learning-objectives" [Alignment-12] [T2] 5. **Set alignment_status to "imported-unverified"** — the user should run /idstack:learning-objectives to verify and check bidirectional alignment @@ -1107,11 +1107,11 @@ Present for review: | ID | Objective | Knowledge | Process | Status | |----|-----------|-----------|---------|--------| | ILO-1 | [text] | [dim] | [proc] | high confidence | -| ILO-2 | [text] | [dim] | [proc] | ambiguous — verify | +| ILO-2 | [text] | [dim] | [proc] | ambiguous — check | ... -All classifications are marked "imported-unverified." Run /idstack:learning-objectives -to verify Bloom's levels and check alignment with activities and assessments. +All classifications have the status "imported-unverified." Run /idstack:learning-objectives +to check the Bloom's levels and the alignment with activities and assessments. ``` --- @@ -1154,16 +1154,16 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Course Import Report" - **`{{skill_name}}`:** `course-import` - **`{{mode}}`:** include `source: cartridge|paste|canvas-api|pdf|scorm` and `source LMS: canvas|blackboard|moodle|d2l|rise|storyline|unknown` in the header `meta` line. -- **Summary:** 2–3 sentences. What came in (N modules, M objectives, P assessments), how many quality flags were raised, and the single most important thing the designer should know — e.g., "Import is structurally clean but objectives are sparse." or "Cartridge schema is valid but 8/12 modules are missing rubrics." +- **Summary:** 2–3 sentences. What came in (N modules, M objectives, P assessments), how many quality flags were raised, and the single most important thing the designer should know — e.g., "Import is structurally clean but objectives are sparse." or "Cartridge schema is valid but 8/12 modules have no rubric." - **Skill-specific sections before Findings**: - `
        ` with `

        Imported

        ` and an HTML `
        ` (Item, Count). Rows: Modules, Objectives, Assessments, Activities/discussions, Pages, Rubrics. - **Finding ids:** `import-1`, `quality-1`, `bloom-1`, etc. Findings come from import quality flags, missing rubrics, missing objectives, alignment-already-broken-on-arrival, and Bloom's-inference low-confidence classifications. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Bloom's classification

        ` and an HTML `
        ` (ILO ID, Statement (truncated), Bloom's level, Confidence). Note any low-confidence classifications the designer should verify. -- **Limitations:** structural metadata only — interactive elements (Storyline, flashcards) don't render in PDF; Canvas API auth tokens are never written to disk; SCORM packages with non-standard manifest extensions may have been parsed loosely. +- **Limitations:** The import reads structural metadata only. Interactive elements (Storyline, flashcards) do not show in a PDF. The import does not write Canvas API auth tokens to disk. The import can miss data in SCORM packages that have non-standard manifest extensions. - **Next steps** (use an `
          `): 1. Run `/idstack:course-quality-review` for the full evidence-based audit. - 2. Run `/idstack:learning-objectives` to verify the Bloom's inference and check bidirectional alignment. - 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that can't be extracted from the cartridge. + 2. Run `/idstack:learning-objectives` to check the Bloom's inference and the bidirectional alignment. + 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that idstack cannot get from the cartridge. **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -1261,16 +1261,16 @@ Write the manifest, then confirm: **Two artifacts:** - **Read this:** `.idstack/exports//course-import.html` — the import report with evidence-backed quality flags, the Bloom's classification table, and recommended - next steps tied to specific findings. Open it in any browser; the folder is self-contained. + next steps tied to specific findings. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Recommended next steps:** 1. `/idstack:course-quality-review` — Full evidence-based audit with QM standards and CoI presence analysis -2. `/idstack:learning-objectives` — Verify Bloom's classifications and check +2. `/idstack:learning-objectives` — Check the Bloom's classifications and the bidirectional alignment (objectives ↔ activities ↔ assessments) 3. `/idstack:needs-analysis` — Add organizational context and learner profile data - that can't be extracted from the course structure alone + that idstack cannot find in the course structure alone ``` --- @@ -1384,7 +1384,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/course-import/SKILL.md.tmpl b/skills/course-import/SKILL.md.tmpl index 08a15d2..28c232d 100644 --- a/skills/course-import/SKILL.md.tmpl +++ b/skills/course-import/SKILL.md.tmpl @@ -19,7 +19,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `import_metadata` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Import — Universal LMS Course Import @@ -98,8 +98,8 @@ Options: ### A1. Get the file path -Ask: "Where is your .imscc file? Provide the file path (drag and drop the file -into this window to paste the path)." +Ask: "Where is your .imscc file? Give the file path. To paste the path, drag the file +into this window." ### A2. Validate and extract @@ -114,8 +114,8 @@ fi ``` If FILE_NOT_FOUND: "File not found at that path. Check the path and try again." -If not a ZIP: "This doesn't look like a Common Cartridge file. It should be a -.imscc file exported from your LMS." +If not a ZIP: "This file does not look like a Common Cartridge file. Use the +.imscc file that your LMS exports." Extract the cartridge. **Use `mktemp -d` with no other flags** — `-t` on macOS treats the argument as a literal prefix instead of substituting the `XXXXXX`, producing a broken @@ -140,8 +140,8 @@ else fi ``` -If no imsmanifest.xml found: "This ZIP doesn't contain an IMS manifest. Is this -a Common Cartridge export? Try re-exporting from your LMS." +If no imsmanifest.xml found: "This ZIP does not contain an IMS manifest. Is this +a Common Cartridge export? Export the course from your LMS again." Read the manifest XML: @@ -209,11 +209,11 @@ Continue to Step 2 (Quality Flags). ### B1. Get the documents -Ask: "Paste your course documents below. This could be a syllabus, module outline, -assignment list, or course description. The more detail you provide, the better I -can map your course structure. +Ask: "Paste your course documents below. For example, paste a syllabus, a module outline, +an assignment list, or a course description. If you give more information, I can +map your course structure better. -Paste the content and I'll extract the structure." +Paste the content, and I will identify the structure." ### B2. Extract structure @@ -261,11 +261,11 @@ Continue to Step 2 (Quality Flags). ### D1. Get the file path -Ask: "Where is your PDF or document file? Provide the file path (drag and drop the -file into this window to paste the path). +Ask: "Where is your PDF or document file? Give the file path. To paste the path, drag the +file into this window. This works with PDFs exported from Articulate Rise, Storyline, Adobe Captivate, -or any authoring tool. Also works with Word documents, course packets, and syllabus PDFs." +or a different authoring tool. It also works with Word documents, course packets, and syllabus PDFs." ### D2. Read the file @@ -277,8 +277,8 @@ If the file does not exist, ask the user to check the path. If the PDF is large (more than 10 pages), read in chunks using the `pages` parameter: - First pass: pages "1-10" - If more content exists: pages "11-20", etc. -- Maximum 50 pages total. If the PDF is longer, note: "Reading first 50 pages. - If important content is after page 50, let me know which pages to focus on." +- Maximum 50 pages total. If the PDF is longer, note: "idstack reads the first 50 pages only. + If important content is after page 50, tell me which pages to read." ### D3. Extract structure @@ -315,15 +315,15 @@ Continue to Step 2 (Quality Flags). Ask: "I need two things to connect to Canvas: 1. **Canvas URL** — Your institution's Canvas address - (e.g., `https://canvas.university.edu`) + (for example, `https://canvas.university.edu`) 2. **Access token** — Generate one in Canvas: Account → Settings → scroll to 'Approved Integrations' → New Access Token 3. **Course ID** — The number in the URL when you open the course - (e.g., `https://canvas.university.edu/courses/12345` → course ID is `12345`) + (for example, `https://canvas.university.edu/courses/12345` → course ID is `12345`) -Your token is used for this session only and is NEVER saved to any file." +idstack uses your token for this session only. It does NOT save the token to a file." ### C2. Validate connection @@ -340,8 +340,8 @@ echo "$BODY" | head -5 Handle errors: - HTTP 401: "Token rejected. Make sure you copied the full token. In Canvas: Account → Settings → New Access Token." -- HTTP 403: "Access denied. Your token may not have the right permissions." -- Network error: "Can't reach Canvas at that URL. Check the address." +- HTTP 403: "Access denied. Your token does not have the necessary permissions." +- Network error: "idstack cannot connect to Canvas at that URL. Check the address." ### C3. Fetch course data @@ -399,9 +399,9 @@ it for up to 10 pages (500 items max per endpoint). After 500 items, stop and no **Error handling for each call:** - 404: Skip this endpoint, note what's missing -- 429: Wait 10 seconds, retry once. If still 429: "Canvas is rate-limiting. - Wait a minute and try `/idstack:course-import` again." -- Timeout: "Canvas didn't respond for [endpoint]. Continuing with what we have." +- 429: Wait 10 seconds, retry once. If still 429: "Canvas limits the request rate. + Wait one minute and run `/idstack:course-import` again." +- Timeout: "Canvas did not send a response for [endpoint]. idstack continues with the data that it has." ### C4. Map API response to course structure @@ -422,8 +422,8 @@ Continue to Step 2 (Quality Flags). ### E1. Get the file path -Ask: "Where is your SCORM package (.zip)? Provide the file path (drag and drop the -file into this window to paste the path). +Ask: "Where is your SCORM package (.zip)? Give the file path. To paste the path, drag the +file into this window. This works with SCORM 1.2 and SCORM 2004 packages from Articulate Rise, Storyline, Adobe Captivate, Lectora, iSpring, or any SCORM-compliant authoring tool." @@ -440,8 +440,8 @@ fi ``` If FILE_NOT_FOUND: "File not found at that path. Check the path and try again." -If not a ZIP: "This doesn't look like a SCORM package. It should be a .zip file -exported from your authoring tool." +If not a ZIP: "This file does not look like a SCORM package. Use the .zip file +that your authoring tool exports." Extract the package: @@ -463,16 +463,16 @@ else fi ``` -If NO_SCORM_MANIFEST: "No imsmanifest.xml found in this ZIP. This may not be a valid -SCORM package. Try exporting again from your authoring tool, or use Path D (PDF import) -instead." +If NO_SCORM_MANIFEST: "This ZIP does not contain imsmanifest.xml. Without this file, it is not a valid +SCORM package. Export the package from your authoring tool again, or use Path D (PDF import) +for this file." ### E4. Detect SCORM version From the manifest XML, check namespaces and schema references: - If `adlcp_rootv1p2` or `adlcp:scormtype` (lowercase) → SCORM 1.2 - If `adlcp_v1p3` or `adlcp:scormType` (camelCase) → SCORM 2004 -- Note the version for the user: "Detected SCORM [version] package." +- Note the version for the user: "idstack found a SCORM [version] package." ### E5. Parse manifest structure @@ -577,19 +577,19 @@ flags problems visible in the structural data alone. - Check for accessibility info. Flag if none: "⚠ No accessibility information found for course materials" - Check for learner support resources. Flag if none: - "⚠ No learner support section detected (tutoring, office hours, tech support)" + "⚠ idstack found no learner support section (tutoring, office hours, tech support)" **Alignment flags:** - If objectives exist AND assessments exist but no clear mapping between them: - "⚠ Objectives and assessments found but no alignment mapping detected" + "⚠ The course has objectives and assessments, but idstack found no alignment matrix between them" - If all assessments are the same type (e.g., all quizzes): - "⚠ All {X} assessments are {type} — consider varied assessment for different + "⚠ All {X} assessments are {type}. idstack recommends different assessment types for different Bloom's levels [Assessment-10] [T1]" **Assessment feedback flags:** - If quizzes are auto-graded with no indication of elaborated feedback: - "⚠ Auto-graded assessments detected. Elaborated feedback (explaining WHY) produces - larger learning gains than correctness-only feedback [Assessment-8] [T1]" + "⚠ idstack found auto-graded assessments. Elaborated feedback (that explains WHY) increases + learning more than correctness-only feedback [Assessment-8] [T1]" Present the flags: @@ -603,7 +603,7 @@ These are quick observations from the course structure, not a full review. Run /idstack:course-quality-review for an evidence-based audit with specific recommendations. ``` -If zero flags: "No obvious structural issues detected during import. Run +If zero flags: "idstack found no structural problems in the import. Run /idstack:course-quality-review for a deeper analysis." --- @@ -642,15 +642,15 @@ Present for user review: ``` ## Inferred Task Analysis -I've mapped your {N} modules to task analysis entries. Please review and adjust: +I mapped your {N} modules to task analysis entries. Review and adjust them: | ID | Task | Frequency | Criticality | Difficulty | |----|------|-----------|-------------|------------| | T-1 | [performance statement] | [est.] | [est.] | [est.] | ... -These estimates are based on module structure. Edit any that don't match your -actual course context. +These estimates come from the module structure. Edit each estimate that is not correct for your +course context. ``` Ask the user to confirm or edit via AskUserQuestion. @@ -682,7 +682,7 @@ For each objective: 4. **Confidence level:** - High: verb maps clearly to one Bloom's level - Ambiguous: verb could map to multiple levels (e.g., "analyze", "demonstrate") - Mark as "verify with /idstack:learning-objectives" [Alignment-12] [T2] + Mark as "check with /idstack:learning-objectives" [Alignment-12] [T2] 5. **Set alignment_status to "imported-unverified"** — the user should run /idstack:learning-objectives to verify and check bidirectional alignment @@ -697,11 +697,11 @@ Present for review: | ID | Objective | Knowledge | Process | Status | |----|-----------|-----------|---------|--------| | ILO-1 | [text] | [dim] | [proc] | high confidence | -| ILO-2 | [text] | [dim] | [proc] | ambiguous — verify | +| ILO-2 | [text] | [dim] | [proc] | ambiguous — check | ... -All classifications are marked "imported-unverified." Run /idstack:learning-objectives -to verify Bloom's levels and check alignment with activities and assessments. +All classifications have the status "imported-unverified." Run /idstack:learning-objectives +to check the Bloom's levels and the alignment with activities and assessments. ``` --- @@ -727,16 +727,16 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Course Import Report" - **`{{skill_name}}`:** `course-import` - **`{{mode}}`:** include `source: cartridge|paste|canvas-api|pdf|scorm` and `source LMS: canvas|blackboard|moodle|d2l|rise|storyline|unknown` in the header `meta` line. -- **Summary:** 2–3 sentences. What came in (N modules, M objectives, P assessments), how many quality flags were raised, and the single most important thing the designer should know — e.g., "Import is structurally clean but objectives are sparse." or "Cartridge schema is valid but 8/12 modules are missing rubrics." +- **Summary:** 2–3 sentences. What came in (N modules, M objectives, P assessments), how many quality flags were raised, and the single most important thing the designer should know — e.g., "Import is structurally clean but objectives are sparse." or "Cartridge schema is valid but 8/12 modules have no rubric." - **Skill-specific sections before Findings**: - `
          ` with `

          Imported

          ` and an HTML `
        ` (Item, Count). Rows: Modules, Objectives, Assessments, Activities/discussions, Pages, Rubrics. - **Finding ids:** `import-1`, `quality-1`, `bloom-1`, etc. Findings come from import quality flags, missing rubrics, missing objectives, alignment-already-broken-on-arrival, and Bloom's-inference low-confidence classifications. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Bloom's classification

        ` and an HTML `
        ` (ILO ID, Statement (truncated), Bloom's level, Confidence). Note any low-confidence classifications the designer should verify. -- **Limitations:** structural metadata only — interactive elements (Storyline, flashcards) don't render in PDF; Canvas API auth tokens are never written to disk; SCORM packages with non-standard manifest extensions may have been parsed loosely. +- **Limitations:** The import reads structural metadata only. Interactive elements (Storyline, flashcards) do not show in a PDF. The import does not write Canvas API auth tokens to disk. The import can miss data in SCORM packages that have non-standard manifest extensions. - **Next steps** (use an `
          `): 1. Run `/idstack:course-quality-review` for the full evidence-based audit. - 2. Run `/idstack:learning-objectives` to verify the Bloom's inference and check bidirectional alignment. - 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that can't be extracted from the cartridge. + 2. Run `/idstack:learning-objectives` to check the Bloom's inference and the bidirectional alignment. + 3. Run `/idstack:needs-analysis` (in audit-existing mode) to add the organizational context and learner profile that idstack cannot get from the cartridge. **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -817,16 +817,16 @@ Write the manifest, then confirm: **Two artifacts:** - **Read this:** `.idstack/exports//course-import.html` — the import report with evidence-backed quality flags, the Bloom's classification table, and recommended - next steps tied to specific findings. Open it in any browser; the folder is self-contained. + next steps tied to specific findings. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Recommended next steps:** 1. `/idstack:course-quality-review` — Full evidence-based audit with QM standards and CoI presence analysis -2. `/idstack:learning-objectives` — Verify Bloom's classifications and check +2. `/idstack:learning-objectives` — Check the Bloom's classifications and the bidirectional alignment (objectives ↔ activities ↔ assessments) 3. `/idstack:needs-analysis` — Add organizational context and learner profile data - that can't be extracted from the course structure alone + that idstack cannot find in the course structure alone ``` --- diff --git a/skills/course-quality-review/SKILL.md b/skills/course-quality-review/SKILL.md index c3fe955..ecc5d43 100644 --- a/skills/course-quality-review/SKILL.md +++ b/skills/course-quality-review/SKILL.md @@ -206,7 +206,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -287,8 +287,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -314,8 +314,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -421,15 +421,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `quality_review` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Quality Review — QM-Aligned Audit with CoI Presence Layer @@ -517,8 +517,8 @@ with substantive data (not just empty defaults). This is the richest review. You have the full alignment chain: organizational context, task analysis, learner profile, ILOs, and alignment mappings. -Tell the user: "I have your needs analysis and [X] learning objectives. I'll use -these for a deep alignment audit, checking the full chain from organizational need +Tell the user: "I have your needs analysis and [X] learning objectives. I will use +them for a deep alignment audit. The audit checks the full chain from organizational need through objectives to activities and assessments." Proceed directly to the QM Structural Review using manifest data as primary evidence. @@ -529,8 +529,8 @@ Proceed directly to the QM Structural Review using manifest data as primary evid Review what is available, and flag what is missing. -Tell the user: "I have [populated sections] but not [missing sections]. I'll review -what I can and flag gaps. For a complete audit, consider running [missing skill] +Tell the user: "I have [populated sections] but not [missing sections]. I will review +the available data and show the gaps. For a full audit, run [missing skill] first." Common gaps and their impact: @@ -818,7 +818,7 @@ Ask the user targeted questions: complete to practice that skill?" 2. "How is each objective assessed? What does the learner produce or demonstrate?" -3. "Are there any objectives that you teach but don't formally assess?" +3. "Do you teach objectives that you do not formally assess?" Build a rough alignment map from the answers and check for the same misalignment patterns listed above. @@ -1018,7 +1018,7 @@ Then present the detailed findings: (Scores summed and scaled: raw X/30 -> XX/25) ### Constructive Alignment Audit (XX/15) -[findings or "Full alignment verified across all ILOs"] +[findings or "All ILOs are fully aligned"] ### Cross-Domain Evidence Checks (XX/20) | Check | Flags | Severity | Fix | @@ -1057,8 +1057,8 @@ When recommending fixes, point users to the appropriate idstack skill: - Misaligned or weak ILOs: "Run `/idstack:learning-objectives` to realign ILO-3 with its assessment." -- Missing learner profile: "Run `/idstack:needs-analysis` to establish the learner - profile that is currently missing." +- Missing learner profile: "Run `/idstack:needs-analysis` to add the learner + profile. The course does not have a learner profile." - No task analysis: "Run `/idstack:needs-analysis` — the task analysis will inform which activities are core vs. reference." - Weak alignment chain: "Run `/idstack:learning-objectives` to rebuild the alignment @@ -1231,7 +1231,7 @@ scores. The preamble's context recovery already reads this file, but the score t display should also appear in the completion message. - If 1 prior score exists: show delta. "Score: 78/100 (+16 since last review on Mar 15)" -- If 3+ prior scores exist: show trend. "Trending up: 62 -> 72 -> 78 across 3 reviews." +- If 3+ prior scores exist: show trend. "The score went up: 62 -> 72 -> 78 in 3 reviews." - If no prior scores exist: just show the current score. The manifest stores the current overall_score. The timeline stores historical scores. @@ -1284,7 +1284,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - `

          Cross-domain evidence checks

          ` — cognitive load, multimedia, feedback quality, expertise reversal findings. Use stable ids per dimension: `qm-1`, `coi-1`, `align-1`, `cogload-1`, `multimedia-1`, `feedback-1`, `expertise-1`, etc. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
          ` with `

          Top 3 quick wins

          ` and an HTML `
        ` (#, Finding, Impact, Skill to Run). -- **Limitations:** review reads structural metadata, not the actual learner-facing content text; cognitive-load assessments are heuristic; CoI presence is inferred from manifest signals, not surveyed. +- **Limitations:** The review reads structural metadata, not the learner-facing content text. The cognitive-load assessments are heuristic. The CoI presence scores come from manifest signals, not from a survey. - **Next steps:** 1–3 specific next actions referencing other idstack skills. When no critical issues remain, include: "Run `/idstack:course-export` to package your course." **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -1315,11 +1315,11 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa After writing both the manifest and the quality report, confirm to the user: -"Your quality review has been saved to `.idstack/project.json` and a shareable HTML -report generated at `.idstack/exports//course-quality-review.html`. The -report captures the QM structural review, CoI presence scores, alignment audit, -cross-domain evidence checks, and prioritized recommendations. Open it in any browser; -the folder is self-contained (CSS bundled). +"I saved your quality review to `.idstack/project.json`. I also wrote a shareable HTML +report to `.idstack/exports//course-quality-review.html`. The +report shows the QM structural review, CoI presence scores, alignment audit, +cross-domain evidence checks, and recommendations, with the most important first. Open it in a web browser. +The folder also contains the CSS file. **Score: XX/100** [If previous: "Previous: X/100 (DATE). Delta: +/-Z."] @@ -1440,7 +1440,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/course-quality-review/SKILL.md.tmpl b/skills/course-quality-review/SKILL.md.tmpl index 127be2d..22f1164 100644 --- a/skills/course-quality-review/SKILL.md.tmpl +++ b/skills/course-quality-review/SKILL.md.tmpl @@ -19,7 +19,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `quality_review` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Course Quality Review — QM-Aligned Audit with CoI Presence Layer @@ -107,8 +107,8 @@ with substantive data (not just empty defaults). This is the richest review. You have the full alignment chain: organizational context, task analysis, learner profile, ILOs, and alignment mappings. -Tell the user: "I have your needs analysis and [X] learning objectives. I'll use -these for a deep alignment audit, checking the full chain from organizational need +Tell the user: "I have your needs analysis and [X] learning objectives. I will use +them for a deep alignment audit. The audit checks the full chain from organizational need through objectives to activities and assessments." Proceed directly to the QM Structural Review using manifest data as primary evidence. @@ -119,8 +119,8 @@ Proceed directly to the QM Structural Review using manifest data as primary evid Review what is available, and flag what is missing. -Tell the user: "I have [populated sections] but not [missing sections]. I'll review -what I can and flag gaps. For a complete audit, consider running [missing skill] +Tell the user: "I have [populated sections] but not [missing sections]. I will review +the available data and show the gaps. For a full audit, run [missing skill] first." Common gaps and their impact: @@ -408,7 +408,7 @@ Ask the user targeted questions: complete to practice that skill?" 2. "How is each objective assessed? What does the learner produce or demonstrate?" -3. "Are there any objectives that you teach but don't formally assess?" +3. "Do you teach objectives that you do not formally assess?" Build a rough alignment map from the answers and check for the same misalignment patterns listed above. @@ -608,7 +608,7 @@ Then present the detailed findings: (Scores summed and scaled: raw X/30 -> XX/25) ### Constructive Alignment Audit (XX/15) -[findings or "Full alignment verified across all ILOs"] +[findings or "All ILOs are fully aligned"] ### Cross-Domain Evidence Checks (XX/20) | Check | Flags | Severity | Fix | @@ -647,8 +647,8 @@ When recommending fixes, point users to the appropriate idstack skill: - Misaligned or weak ILOs: "Run `/idstack:learning-objectives` to realign ILO-3 with its assessment." -- Missing learner profile: "Run `/idstack:needs-analysis` to establish the learner - profile that is currently missing." +- Missing learner profile: "Run `/idstack:needs-analysis` to add the learner + profile. The course does not have a learner profile." - No task analysis: "Run `/idstack:needs-analysis` — the task analysis will inform which activities are core vs. reference." - Weak alignment chain: "Run `/idstack:learning-objectives` to rebuild the alignment @@ -787,7 +787,7 @@ scores. The preamble's context recovery already reads this file, but the score t display should also appear in the completion message. - If 1 prior score exists: show delta. "Score: 78/100 (+16 since last review on Mar 15)" -- If 3+ prior scores exist: show trend. "Trending up: 62 -> 72 -> 78 across 3 reviews." +- If 3+ prior scores exist: show trend. "The score went up: 62 -> 72 -> 78 in 3 reviews." - If no prior scores exist: just show the current score. The manifest stores the current overall_score. The timeline stores historical scores. @@ -823,7 +823,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - `

        Cross-domain evidence checks

        ` — cognitive load, multimedia, feedback quality, expertise reversal findings. Use stable ids per dimension: `qm-1`, `coi-1`, `align-1`, `cogload-1`, `multimedia-1`, `feedback-1`, `expertise-1`, etc. - **Optional skill-specific section** (after Top recommendations, before Limitations): `
        ` with `

        Top 3 quick wins

        ` and an HTML `
        ` (#, Finding, Impact, Skill to Run). -- **Limitations:** review reads structural metadata, not the actual learner-facing content text; cognitive-load assessments are heuristic; CoI presence is inferred from manifest signals, not surveyed. +- **Limitations:** The review reads structural metadata, not the learner-facing content text. The cognitive-load assessments are heuristic. The CoI presence scores come from manifest signals, not from a survey. - **Next steps:** 1–3 specific next actions referencing other idstack skills. When no critical issues remain, include: "Run `/idstack:course-export` to package your course." **Writing standard check.** In this command, replace `` with the path from the "Report path:" line. Then run the command. @@ -837,11 +837,11 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa After writing both the manifest and the quality report, confirm to the user: -"Your quality review has been saved to `.idstack/project.json` and a shareable HTML -report generated at `.idstack/exports//course-quality-review.html`. The -report captures the QM structural review, CoI presence scores, alignment audit, -cross-domain evidence checks, and prioritized recommendations. Open it in any browser; -the folder is self-contained (CSS bundled). +"I saved your quality review to `.idstack/project.json`. I also wrote a shareable HTML +report to `.idstack/exports//course-quality-review.html`. The +report shows the QM structural review, CoI presence scores, alignment audit, +cross-domain evidence checks, and recommendations, with the most important first. Open it in a web browser. +The folder also contains the CSS file. **Score: XX/100** [If previous: "Previous: X/100 (DATE). Delta: +/-Z."] diff --git a/skills/learn/SKILL.md b/skills/learn/SKILL.md index b19c54a..0a650c0 100644 --- a/skills/learn/SKILL.md +++ b/skills/learn/SKILL.md @@ -2,7 +2,7 @@ name: learn description: | Manage project learnings. Search, list, delete, promote, and export what idstack - has learned across sessions. Use when asked to "what have we learned", "show learnings", + learned across sessions. Use when asked to "what have we learned", "show learnings", "prune stale learnings", or "export learnings". (idstack) allowed-tools: - Bash @@ -201,7 +201,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -282,8 +282,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -309,8 +309,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -416,10 +416,10 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- @@ -468,7 +468,7 @@ Format the output as a readable table: Key | Type | Insight | Confidence ─────────────────┼──────────────┼──────────────────────────────────┼─────────── canvas-rubrics | operational | Uses HTML format for rubrics | 8/10 -bloom-verbs | pedagogical | Avoid "understand" as ILO verb | 9/10 +bloom-verbs | pedagogical | Do not use "understand" in ILOs | 9/10 ``` ### search diff --git a/skills/learn/SKILL.md.tmpl b/skills/learn/SKILL.md.tmpl index 993cc6b..3a2cc59 100644 --- a/skills/learn/SKILL.md.tmpl +++ b/skills/learn/SKILL.md.tmpl @@ -2,7 +2,7 @@ name: learn description: | Manage project learnings. Search, list, delete, promote, and export what idstack - has learned across sessions. Use when asked to "what have we learned", "show learnings", + learned across sessions. Use when asked to "what have we learned", "show learnings", "prune stale learnings", or "export learnings". (idstack) allowed-tools: - Bash @@ -41,7 +41,7 @@ Format the output as a readable table: Key | Type | Insight | Confidence ─────────────────┼──────────────┼──────────────────────────────────┼─────────── canvas-rubrics | operational | Uses HTML format for rubrics | 8/10 -bloom-verbs | pedagogical | Avoid "understand" as ILO verb | 9/10 +bloom-verbs | pedagogical | Do not use "understand" in ILOs | 9/10 ``` ### search diff --git a/skills/learning-objectives/SKILL.md b/skills/learning-objectives/SKILL.md index ae96831..bdfb385 100644 --- a/skills/learning-objectives/SKILL.md +++ b/skills/learning-objectives/SKILL.md @@ -2,7 +2,7 @@ name: learning-objectives description: | Evidence-based learning objective development with revised Bloom's taxonomy - classification and bidirectional alignment checking. Reads from /needs-analysis + classification and a bidirectional alignment check. Reads from /needs-analysis manifest and extends it with ILOs, alignment matrix, and expertise reversal flags. (idstack) allowed-tools: @@ -205,7 +205,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -286,8 +286,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -313,8 +313,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -420,15 +420,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `learning_objectives` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Learning Objectives — Revised Bloom's Taxonomy & Constructive Alignment @@ -486,9 +486,9 @@ The shared preamble above already ran the manifest existence check (`MANIFEST_EXISTS` / `NO_MANIFEST`) and this skill's re-run question. **If NO_MANIFEST:** -- Say: "I notice you haven't run `/idstack:needs-analysis` yet. Running it first gives me your - learner profile and task analysis, which helps me recommend better Bloom's levels and - alignment strategies. Want to continue anyway, or run `/idstack:needs-analysis` first?" +- Say: "You did not run `/idstack:needs-analysis`. That skill gives me your + learner profile and task analysis. With them, I can recommend better Bloom's levels and + alignment strategies. Do you want to continue without it, or run `/idstack:needs-analysis` first?" - If the user wants to continue, proceed without manifest context. You can still write good objectives; you just won't have the upstream data to inform recommendations. @@ -499,8 +499,8 @@ The shared preamble above already ran the manifest existence check If the manifest exists and has `needs_analysis` data, use it to inform your guidance. **Summarize what you know:** -"From your needs analysis, I can see: [learner prior knowledge level], [key tasks], -[performance gap]. I'll use this to guide objective development." +"Your needs analysis gives: [learner prior knowledge level], [key tasks], +[performance gap]. I will use this data to develop the objectives." **Use upstream data:** - `needs_analysis.task_analysis.job_tasks` — Suggest which objectives are needed based on @@ -527,8 +527,8 @@ using AskUserQuestion. Do not batch multiple questions. Ask the user: -**"What do you want learners to be able to DO after completing this course? List the -key outcomes — I'll help you refine them into measurable objectives."** +**"What do you want learners to be able to DO after they complete this course? List the +key outcomes. I will help you change them into measurable objectives."** For each outcome the user provides: @@ -578,17 +578,17 @@ identify, describe, compare, apply, design, develop, assess, interpret, create. When you encounter one of these: -"The verb '[verb]' can operate at different cognitive levels depending on context. In -this objective, are students: -- [Lower interpretation — describe what this would look like], or -- [Higher interpretation — describe what this would look like]?" +"The verb '[verb]' can be at different cognitive levels. The context sets the level. In +this objective, do students: +- [Lower interpretation — describe what this looks like], or +- [Higher interpretation — describe what this looks like]?" **Example:** -"The verb 'analyze' in 'Analyze patient data to identify trends' could mean: +"The verb 'analyze' in 'Analyze patient data to identify trends' can mean: - **Apply level:** Follow a prescribed analysis procedure step by step, or -- **Analyze level:** Independently break down the data, identify patterns, and draw - connections that aren't explicitly taught. -Which is closer to what you intend?" +- **Analyze level:** Independently break down the data, identify patterns, and find + connections that the course does not teach explicitly. +Which level do you want?" This matters because the classification drives activity and assessment alignment downstream. Getting it wrong here cascades [Alignment-12] [T2]. @@ -603,11 +603,11 @@ After all objectives are drafted and classified, review the set as a whole. If the objectives follow a strict low-to-high Bloom's sequence (remember -> understand -> apply -> analyze -> evaluate -> create), flag it: -"Your objectives follow a strict low-to-high Bloom's sequence. Evidence shows students -don't need to master facts before engaging in higher-order learning [Alignment-14] [T1]. -Consider whether some objectives could start at higher cognitive levels. For example, -could learners begin with an analysis or evaluation task and learn factual knowledge -in context?" +"Your objectives follow a strict low-to-high Bloom's sequence. Evidence shows that students +can start higher-order learning before they master facts [Alignment-14] [T1]. +You can start some objectives at higher cognitive levels. For example, learners +can start with an analysis or evaluation task and learn factual knowledge +in context." **Cross-reference with learner profile (if available from manifest):** @@ -623,8 +623,8 @@ in context?" objectives (remember, understand) may add extraneous cognitive load for learners who already have this knowledge [CogLoad-19] [T1]. Recommend starting at apply or higher. -- **Mixed audience:** Flag that a single sequence won't serve everyone. Consider whether - lower-level objectives could be made optional or handled through pre-assessment. +- **Mixed audience:** Flag that one sequence does not serve all learners. Recommend that + lower-level objectives become optional, or that a pre-assessment covers them. Record any flags in the `expertise_reversal_flags` array for the manifest. @@ -640,15 +640,15 @@ both a learning activity AND an assessment, and all three target the same cognit For each ILO, ask: -**"What learning activity will help students achieve ILO-X: [objective text]?"** +**"What learning activity will help students meet ILO-X: [objective text]?"** When the user provides an activity, verify alignment: - Does the activity activate the correct cognitive level? - If the ILO targets "evaluate" but the activity is "read a textbook chapter" (remember level), flag the mismatch: - "This activity operates at the 'remember' level, but ILO-X targets 'evaluate.' Students - need practice at the evaluation level to achieve this objective. Consider activities like - peer review, critique exercises, or rubric-based judgment tasks instead." + "This activity is at the 'remember' level, but ILO-X targets 'evaluate'. Students + must practice at the evaluation level to meet this objective. idstack recommends a different + activity, for example peer review, a critique exercise, or a rubric-based judgment task." - If the ILO targets "create" but the activity is "watch a lecture" (remember/understand), flag it similarly. @@ -659,14 +659,14 @@ describes. Passive activities cannot prepare students for active objectives. For each ILO, ask: -**"How will you assess whether students achieved ILO-X: [objective text]?"** +**"How will you assess if students met ILO-X: [objective text]?"** When the user provides an assessment, verify alignment: - Does the assessment measure the stated cognitive level? - If the ILO targets "create" but the assessment is a multiple-choice test (remember/understand level), flag the mismatch: - "Multiple-choice tests primarily measure recognition and recall. ILO-X targets 'create.' - Consider assessments where students actually produce something: a project, design, + "Multiple-choice tests primarily measure recognition and recall. ILO-X targets 'create'. + idstack recommends an assessment where students make a product: a project, design, portfolio, or prototype." - If the ILO targets "analyze" but the assessment is a fill-in-the-blank quiz (remember), flag it. @@ -679,17 +679,17 @@ stated in the objective. After both passes are complete, identify gaps: **ILOs with no mapped activity:** -"ILO-X has no learning activity. Students won't have a chance to practice this skill -before being assessed on it. This is a critical alignment gap." +"ILO-X has no learning activity. Students cannot practice this skill +before the assessment. This is a critical alignment gap." **ILOs with no mapped assessment:** -"ILO-X has no assessment. You won't know if students achieved this objective. Either add -an assessment or consider whether this objective is necessary." +"ILO-X has no assessment. You cannot know if students met this objective. Add +an assessment. If the objective is not necessary, remove it." **Activities with no mapped ILO:** -"You described an activity ([activity]) that doesn't connect to any ILO. Either it serves -an unstated objective (add the ILO) or it's not contributing to course outcomes (consider -removing it)." +"You described an activity ([activity]) that does not connect to an ILO. If it is for +an unstated objective, add the ILO. If it does not help the course outcomes, you can +remove it." Present gaps prominently. These are the primary findings from the alignment check. @@ -763,7 +763,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Alignment table

        ` and an HTML `
        ` (columns: ID, Objective, Knowledge, Process, Activity, Assessment, Alignment). Alignment values: `aligned` / `MISMATCH` / `GAP`. - **Finding ids:** `align-1`, `bloom-1`, `expertise-1`, etc. Findings come from bidirectional alignment gaps, Bloom's-level mismatches, expertise-reversal flags, and ambiguous verbs that were clarified. - **Top recommendations:** the 3-5 highest-impact alignment fixes, ordered by priority; cite each ([Domain-N] [TN]) and reference the finding id it addresses. -- **Limitations:** alignment is read from manifest descriptions, not from the actual rubric criteria; expertise-reversal flags are inferred from the learner profile without a learner survey. +- **Limitations:** The alignment data comes from the manifest descriptions, not from the rubric criteria. The expertise-reversal flags come from the learner profile, without a learner survey. - **Next steps:** Run `/idstack:assessment-design` to design assessments aligned to these objectives with evidence-based rubrics and feedback strategies. Every finding in the HTML must correspond to an entry in `learning_objectives.alignment_matrix.gaps[]` or `learning_objectives.expertise_reversal_flags[]` so downstream skills can read them programmatically. @@ -871,11 +871,11 @@ the bash block above — i.e., `.idstack/exports//learning-objectiv Write the manifest, then confirm to the user: -"Your learning objectives are saved. Two artifacts: +"I saved your learning objectives in two files: - **Read this:** `.idstack/exports//learning-objectives.html` — the alignment table, evidence-backed findings on gaps and mismatches, and a Bloom's-level expertise - read. Open it in any browser; the folder is self-contained. + read. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Next step:** Run `/idstack:assessment-design` to design assessments aligned to your objectives @@ -992,7 +992,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/learning-objectives/SKILL.md.tmpl b/skills/learning-objectives/SKILL.md.tmpl index f729095..2949c53 100644 --- a/skills/learning-objectives/SKILL.md.tmpl +++ b/skills/learning-objectives/SKILL.md.tmpl @@ -2,7 +2,7 @@ name: learning-objectives description: | Evidence-based learning objective development with revised Bloom's taxonomy - classification and bidirectional alignment checking. Reads from /needs-analysis + classification and a bidirectional alignment check. Reads from /needs-analysis manifest and extends it with ILOs, alignment matrix, and expertise reversal flags. (idstack) allowed-tools: @@ -18,7 +18,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `learning_objectives` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Learning Objectives — Revised Bloom's Taxonomy & Constructive Alignment @@ -76,9 +76,9 @@ The shared preamble above already ran the manifest existence check (`MANIFEST_EXISTS` / `NO_MANIFEST`) and this skill's re-run question. **If NO_MANIFEST:** -- Say: "I notice you haven't run `/idstack:needs-analysis` yet. Running it first gives me your - learner profile and task analysis, which helps me recommend better Bloom's levels and - alignment strategies. Want to continue anyway, or run `/idstack:needs-analysis` first?" +- Say: "You did not run `/idstack:needs-analysis`. That skill gives me your + learner profile and task analysis. With them, I can recommend better Bloom's levels and + alignment strategies. Do you want to continue without it, or run `/idstack:needs-analysis` first?" - If the user wants to continue, proceed without manifest context. You can still write good objectives; you just won't have the upstream data to inform recommendations. @@ -89,8 +89,8 @@ The shared preamble above already ran the manifest existence check If the manifest exists and has `needs_analysis` data, use it to inform your guidance. **Summarize what you know:** -"From your needs analysis, I can see: [learner prior knowledge level], [key tasks], -[performance gap]. I'll use this to guide objective development." +"Your needs analysis gives: [learner prior knowledge level], [key tasks], +[performance gap]. I will use this data to develop the objectives." **Use upstream data:** - `needs_analysis.task_analysis.job_tasks` — Suggest which objectives are needed based on @@ -117,8 +117,8 @@ using AskUserQuestion. Do not batch multiple questions. Ask the user: -**"What do you want learners to be able to DO after completing this course? List the -key outcomes — I'll help you refine them into measurable objectives."** +**"What do you want learners to be able to DO after they complete this course? List the +key outcomes. I will help you change them into measurable objectives."** For each outcome the user provides: @@ -168,17 +168,17 @@ identify, describe, compare, apply, design, develop, assess, interpret, create. When you encounter one of these: -"The verb '[verb]' can operate at different cognitive levels depending on context. In -this objective, are students: -- [Lower interpretation — describe what this would look like], or -- [Higher interpretation — describe what this would look like]?" +"The verb '[verb]' can be at different cognitive levels. The context sets the level. In +this objective, do students: +- [Lower interpretation — describe what this looks like], or +- [Higher interpretation — describe what this looks like]?" **Example:** -"The verb 'analyze' in 'Analyze patient data to identify trends' could mean: +"The verb 'analyze' in 'Analyze patient data to identify trends' can mean: - **Apply level:** Follow a prescribed analysis procedure step by step, or -- **Analyze level:** Independently break down the data, identify patterns, and draw - connections that aren't explicitly taught. -Which is closer to what you intend?" +- **Analyze level:** Independently break down the data, identify patterns, and find + connections that the course does not teach explicitly. +Which level do you want?" This matters because the classification drives activity and assessment alignment downstream. Getting it wrong here cascades [Alignment-12] [T2]. @@ -193,11 +193,11 @@ After all objectives are drafted and classified, review the set as a whole. If the objectives follow a strict low-to-high Bloom's sequence (remember -> understand -> apply -> analyze -> evaluate -> create), flag it: -"Your objectives follow a strict low-to-high Bloom's sequence. Evidence shows students -don't need to master facts before engaging in higher-order learning [Alignment-14] [T1]. -Consider whether some objectives could start at higher cognitive levels. For example, -could learners begin with an analysis or evaluation task and learn factual knowledge -in context?" +"Your objectives follow a strict low-to-high Bloom's sequence. Evidence shows that students +can start higher-order learning before they master facts [Alignment-14] [T1]. +You can start some objectives at higher cognitive levels. For example, learners +can start with an analysis or evaluation task and learn factual knowledge +in context." **Cross-reference with learner profile (if available from manifest):** @@ -213,8 +213,8 @@ in context?" objectives (remember, understand) may add extraneous cognitive load for learners who already have this knowledge [CogLoad-19] [T1]. Recommend starting at apply or higher. -- **Mixed audience:** Flag that a single sequence won't serve everyone. Consider whether - lower-level objectives could be made optional or handled through pre-assessment. +- **Mixed audience:** Flag that one sequence does not serve all learners. Recommend that + lower-level objectives become optional, or that a pre-assessment covers them. Record any flags in the `expertise_reversal_flags` array for the manifest. @@ -230,15 +230,15 @@ both a learning activity AND an assessment, and all three target the same cognit For each ILO, ask: -**"What learning activity will help students achieve ILO-X: [objective text]?"** +**"What learning activity will help students meet ILO-X: [objective text]?"** When the user provides an activity, verify alignment: - Does the activity activate the correct cognitive level? - If the ILO targets "evaluate" but the activity is "read a textbook chapter" (remember level), flag the mismatch: - "This activity operates at the 'remember' level, but ILO-X targets 'evaluate.' Students - need practice at the evaluation level to achieve this objective. Consider activities like - peer review, critique exercises, or rubric-based judgment tasks instead." + "This activity is at the 'remember' level, but ILO-X targets 'evaluate'. Students + must practice at the evaluation level to meet this objective. idstack recommends a different + activity, for example peer review, a critique exercise, or a rubric-based judgment task." - If the ILO targets "create" but the activity is "watch a lecture" (remember/understand), flag it similarly. @@ -249,14 +249,14 @@ describes. Passive activities cannot prepare students for active objectives. For each ILO, ask: -**"How will you assess whether students achieved ILO-X: [objective text]?"** +**"How will you assess if students met ILO-X: [objective text]?"** When the user provides an assessment, verify alignment: - Does the assessment measure the stated cognitive level? - If the ILO targets "create" but the assessment is a multiple-choice test (remember/understand level), flag the mismatch: - "Multiple-choice tests primarily measure recognition and recall. ILO-X targets 'create.' - Consider assessments where students actually produce something: a project, design, + "Multiple-choice tests primarily measure recognition and recall. ILO-X targets 'create'. + idstack recommends an assessment where students make a product: a project, design, portfolio, or prototype." - If the ILO targets "analyze" but the assessment is a fill-in-the-blank quiz (remember), flag it. @@ -269,17 +269,17 @@ stated in the objective. After both passes are complete, identify gaps: **ILOs with no mapped activity:** -"ILO-X has no learning activity. Students won't have a chance to practice this skill -before being assessed on it. This is a critical alignment gap." +"ILO-X has no learning activity. Students cannot practice this skill +before the assessment. This is a critical alignment gap." **ILOs with no mapped assessment:** -"ILO-X has no assessment. You won't know if students achieved this objective. Either add -an assessment or consider whether this objective is necessary." +"ILO-X has no assessment. You cannot know if students met this objective. Add +an assessment. If the objective is not necessary, remove it." **Activities with no mapped ILO:** -"You described an activity ([activity]) that doesn't connect to any ILO. Either it serves -an unstated objective (add the ILO) or it's not contributing to course outcomes (consider -removing it)." +"You described an activity ([activity]) that does not connect to an ILO. If it is for +an unstated objective, add the ILO. If it does not help the course outcomes, you can +remove it." Present gaps prominently. These are the primary findings from the alignment check. @@ -336,7 +336,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Alignment table

        ` and an HTML `
        ` (columns: ID, Objective, Knowledge, Process, Activity, Assessment, Alignment). Alignment values: `aligned` / `MISMATCH` / `GAP`. - **Finding ids:** `align-1`, `bloom-1`, `expertise-1`, etc. Findings come from bidirectional alignment gaps, Bloom's-level mismatches, expertise-reversal flags, and ambiguous verbs that were clarified. - **Top recommendations:** the 3-5 highest-impact alignment fixes, ordered by priority; cite each ([Domain-N] [TN]) and reference the finding id it addresses. -- **Limitations:** alignment is read from manifest descriptions, not from the actual rubric criteria; expertise-reversal flags are inferred from the learner profile without a learner survey. +- **Limitations:** The alignment data comes from the manifest descriptions, not from the rubric criteria. The expertise-reversal flags come from the learner profile, without a learner survey. - **Next steps:** Run `/idstack:assessment-design` to design assessments aligned to these objectives with evidence-based rubrics and feedback strategies. Every finding in the HTML must correspond to an entry in `learning_objectives.alignment_matrix.gaps[]` or `learning_objectives.expertise_reversal_flags[]` so downstream skills can read them programmatically. @@ -410,11 +410,11 @@ the bash block above — i.e., `.idstack/exports//learning-objectiv Write the manifest, then confirm to the user: -"Your learning objectives are saved. Two artifacts: +"I saved your learning objectives in two files: - **Read this:** `.idstack/exports//learning-objectives.html` — the alignment table, evidence-backed findings on gaps and mismatches, and a Bloom's-level expertise - read. Open it in any browser; the folder is self-contained. + read. Open it in a web browser. The folder has all of the files for the report. - System state: `.idstack/project.json` (the manifest — for downstream skills). **Next step:** Run `/idstack:assessment-design` to design assessments aligned to your objectives diff --git a/skills/needs-analysis/SKILL.md b/skills/needs-analysis/SKILL.md index 1aeb782..ca6d168 100644 --- a/skills/needs-analysis/SKILL.md +++ b/skills/needs-analysis/SKILL.md @@ -2,8 +2,8 @@ name: needs-analysis description: | Evidence-based three-level needs assessment for instructional design. - Guides you through organizational, task, and learner analysis before - building a course. Creates a shared project manifest that downstream + Guides you through the organizational, task, and learner analysis before + you design a course. Creates a shared project manifest that downstream skills (/learning-objectives, /course-quality-review) read and extend. (idstack) allowed-tools: - Bash @@ -205,7 +205,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -286,8 +286,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -313,8 +313,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -420,15 +420,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `needs_analysis` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Needs Analysis — Three-Level Assessment Protocol @@ -488,8 +488,8 @@ Before gathering context, decide which mode this skill is operating in. Read **Announce the chosen mode to the user as the first sentence of the conversation.** Examples: -- "Mode: design-new (no import detected). I'll walk you through fresh needs analysis." -- "Mode: audit-existing (cartridge import from Canvas). The course already exists; I'll skip the 'is training justified?' gate and assess design-fit instead." +- "Mode: design-new (idstack found no import). I will help you do a new needs analysis." +- "Mode: audit-existing (cartridge import from Canvas). The course is available. I will not use the 'is training justified?' gate. I will assess design-fit." If the user says they meant a different mode (e.g., they imported but want to redesign from scratch), accept and switch. The mode determines the rest of the workflow. @@ -503,8 +503,8 @@ Before diving into the three levels, establish the project context. **In design-new mode**, ask the user: -"What course or training program are we designing? Give me the basics: title, subject -area, and who requested it." +"What course or training program do you want to design? Tell me the title, the subject +area and the person who requested it." Then establish the delivery context. Ask about: - **Modality:** Online, face-to-face, hybrid, or hyflex? @@ -530,11 +530,11 @@ Store these in the `context` section of the manifest. **In audit-existing mode, the "is training justified?" question is moot.** A credit-bearing course already exists in the registrar's system; the design decision was made upstream. Skip the decision-gate logic below and instead ask one design-fit question: -> "Given the course as imported (from `import_metadata.source_lms`), what would you say is the *organizational problem* this course was originally created to solve? (e.g., 'undergraduates lack synthesis skills before entering capstone'). Be specific — this anchors the rest of the design audit." +> "The course came from `import_metadata.source_lms`. What *organizational problem* did your institution create this course to solve? For example: 'Undergraduates do not have synthesis skills when they start the capstone.' Give a clear answer. The rest of the design audit uses this answer." Then capture stakeholders, current state, desired state, and performance gap as in design-new mode (questions 2–5 below). Set `needs_analysis.training_justification` to: ```json -{"justified": true, "confidence": 10, "rationale": "Existing credit-bearing course; design audit only — training-fit decision is upstream of this skill.", "alternatives_considered": []} +{"justified": true, "confidence": 10, "rationale": "This course is in use for credit. This skill does a design audit only. The decision about training occurred before this skill.", "alternatives_considered": []} ``` Then proceed to Step 3. @@ -557,9 +557,9 @@ Ask these questions (one at a time): 2. **"Who are the stakeholders? Who requested this, who approves it, who will be affected by it?"** -3. **"What is the current state? How are people performing right now?"** +3. **"What is the current state? How well do people do the task now?"** -4. **"What is the desired state? What should performance look like after this +4. **"What is the desired state? What performance do you want after this intervention?"** 5. **"What is the gap between current and desired state?"** @@ -572,14 +572,14 @@ Ask these questions (one at a time): After gathering answers, make a judgment: - If the gap is **knowledge or skill**: Training is likely justified. Proceed. -- If the gap is **motivation**: Training alone won't fix this. Flag it. Consider - incentive redesign, performance support, or management intervention. Training - may be part of the solution but not the whole solution [T3]. +- If the gap is **motivation**: Training alone is not the solution to this problem. Flag it. Tell the user that + incentive redesign, performance support, or management intervention can help. Training + can be part of the solution, but not all of the solution [T3]. - If the gap is **environmental** (bad tools, unclear processes, insufficient resources): - Training is NOT the right intervention. Say so directly. "Based on what you've - described, the performance gap is caused by [environmental factor], not a lack of - knowledge or skills. Training won't fix this. Consider [alternative intervention] - instead." [Needs-8] [T3] + Training is NOT the right intervention. Say so directly. "From what you + described, [environmental factor] causes the performance gap. The cause is not + knowledge or skills. Training is not the solution to this problem. idstack recommends + [alternative intervention]." [Needs-8] [T3] Populate the `training_justification` object: - `justified`: true or false (you CAN recommend against training) @@ -589,7 +589,7 @@ Populate the `training_justification` object: **If training is NOT justified:** Present the finding clearly. Ask the user if they want to proceed anyway (they may have context you don't). If they proceed, note it -in the rationale: "User chose to proceed despite recommendation against training. +in the rationale: "The user continued after idstack recommended against training. Reason: [user's reason]." --- @@ -600,8 +600,8 @@ Reason: [user's reason]." Ask: -1. **"What are the key tasks or activities that learners need to perform after - completing this course?"** +1. **"What are the key tasks or activities that learners must do after + they complete this course?"** Push for observable, measurable performance. "Understand ethics" is not a task. "Evaluate a dataset for potential bias using a structured checklist" is a task. @@ -615,7 +615,7 @@ Ask: 3. **"What prerequisite knowledge or skills do learners need before they can learn these tasks?"** -4. **"What tools, resources, or systems do learners use to perform these tasks?"** +4. **"What tools, resources, or systems do learners use to do these tasks?"** Assign task IDs: T-1, T-2, T-3, etc. @@ -648,7 +648,7 @@ Ask: [CogLoad-19] [T1]. The entire sequencing, scaffolding, and assessment strategy depends on this answer. -2. **"What motivates your learners? Why would they engage with this course?"** +2. **"What motivates your learners? Why do they do the work in this course?"** Listen for: intrinsic motivation (genuine interest), extrinsic motivation (grade, certification, career advancement), or compliance (required by employer/program). @@ -663,19 +663,19 @@ Ask: If the user mentions "learning styles," "VARK," "visual learners," "auditory learners," or similar: respond with this exact framing: -"I appreciate you thinking about learner differences. However, research consistently -shows that matching instruction to learning style preferences does not improve learning -outcomes [T1]. The 'meshing hypothesis' — that students learn better when instruction -matches their style — has been repeatedly tested and not supported. +"It is good that you think about learner differences. But many studies show that +instruction that matches learning style preferences does not make learning outcomes +better [T1]. Researchers tested the 'meshing hypothesis' — that students learn better when +instruction matches their style — many times. The results do not support it. -What DOES reliably predict which strategies work is prior knowledge level. Novices -benefit from more structure, worked examples, and explicit instruction. Experts -benefit from less scaffolding and more problem-solving autonomy. Let's focus on -prior knowledge instead." +Prior knowledge level is the factor that predicts which strategies work. Novices +learn more with more structure, worked examples, and explicit instruction. Experts +learn more with less scaffolding and more problem-solving autonomy. Thus, this +analysis uses prior knowledge level." The `learning_preferences_note` field in the manifest is always populated with: -"Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge -is the primary differentiator." +"idstack does NOT use learning styles to differentiate instruction because the evidence does +not support them. Prior knowledge is the primary differentiator." --- @@ -696,7 +696,7 @@ After completing all three levels, present a structured summary: - Performance Gap: [knowledge/skill/motivation/environment] - Training Justified: [Yes/No] (Confidence: X/10) - Rationale: [one paragraph] -- Alternatives Considered: [list] +- Alternatives Evaluated: [list] ### Level 2: Task Analysis [task table] @@ -766,9 +766,9 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Project context

        ` and a `
        ` listing modality, timeline, class size, institution type, and available tech. - **Finding ids:** `needs-1`, `needs-2`, etc. Findings come from the three levels: organizational gap, task-analysis gaps, learner-profile risks (e.g., expertise mismatch with the planned design). - **Optional skill-specific sections** (after Top recommendations, before Limitations): - - `
        ` with `

        Training justification

        ` — show `Justified: Yes/No`, `Confidence: X/10`, the rationale paragraph, and alternatives considered (or "n/a — imported credit-bearing course" in audit-existing mode). + - `
        ` with `

        Training justification

        ` — show `Justified: Yes/No`, `Confidence: X/10`, the rationale paragraph, and alternatives evaluated (or "n/a — imported credit-bearing course" in audit-existing mode). - `
        ` with `

        Expertise-fit read

        ` — which instructional strategies are appropriate given the learner profile. Cite the expertise-reversal evidence so the read isn't opinion: `[CogLoad-4] [T1]` for novices; `[CogLoad-19] [T1]` for advanced; `[Learner-16] [T1]` for mixed. -- **Limitations:** imported-course mode skips the training-decision gate; learner profile draws on the registrar/syllabus, not a learner survey; task analysis is from job-task lists, not observed performance. +- **Limitations:** In imported-course mode, idstack skips the training-decision gate. The learner profile comes from the registrar or syllabus data, not from a learner survey. The task analysis comes from job-task lists, not from observed performance. - **Next steps:** Run `/idstack:learning-objectives` to develop ILOs grounded in this analysis. The objectives skill reads your task analysis and learner profile to recommend appropriate Bloom's levels and alignment strategies. Every finding in the HTML must correspond to a finding the manifest's `needs_analysis` section can carry, so downstream skills and `bin/idstack-status` can refer to them programmatically. @@ -819,17 +819,17 @@ Create or update the project manifest. Use the Write tool to write `.idstack/pro Write the manifest, then confirm to the user: -"Your needs analysis is saved. Two artifacts: +"I saved your needs analysis in two files: - **Read this:** `.idstack/exports//needs-analysis.html` — the branded HTML report with evidence-backed findings, the training-justification read, and a - learner-profile expertise check. Open it in any browser. The folder is self-contained - (CSS is bundled), so you can zip or email it. + learner-profile expertise check. Open it in a web browser. The folder also contains + the CSS file. You can zip or email the folder. - System state: `.idstack/project.json` (the manifest — for downstream skills). -**Next step:** Run `/idstack:learning-objectives` to develop learning objectives based on -this analysis. The objectives skill will read your task analysis and learner profile -to recommend appropriate Bloom's levels and alignment strategies." +**Next step:** Run `/idstack:learning-objectives` to develop learning objectives from +this analysis. The objectives skill reads your task analysis and learner profile +to recommend Bloom's levels and alignment strategies." --- @@ -942,7 +942,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/skills/needs-analysis/SKILL.md.tmpl b/skills/needs-analysis/SKILL.md.tmpl index 65347c8..9291a7e 100644 --- a/skills/needs-analysis/SKILL.md.tmpl +++ b/skills/needs-analysis/SKILL.md.tmpl @@ -2,8 +2,8 @@ name: needs-analysis description: | Evidence-based three-level needs assessment for instructional design. - Guides you through organizational, task, and learner analysis before - building a course. Creates a shared project manifest that downstream + Guides you through the organizational, task, and learner analysis before + you design a course. Creates a shared project manifest that downstream skills (/learning-objectives, /course-quality-review) read and extend. (idstack) allowed-tools: - Bash @@ -18,7 +18,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `needs_analysis` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Needs Analysis — Three-Level Assessment Protocol @@ -78,8 +78,8 @@ Before gathering context, decide which mode this skill is operating in. Read **Announce the chosen mode to the user as the first sentence of the conversation.** Examples: -- "Mode: design-new (no import detected). I'll walk you through fresh needs analysis." -- "Mode: audit-existing (cartridge import from Canvas). The course already exists; I'll skip the 'is training justified?' gate and assess design-fit instead." +- "Mode: design-new (idstack found no import). I will help you do a new needs analysis." +- "Mode: audit-existing (cartridge import from Canvas). The course is available. I will not use the 'is training justified?' gate. I will assess design-fit." If the user says they meant a different mode (e.g., they imported but want to redesign from scratch), accept and switch. The mode determines the rest of the workflow. @@ -93,8 +93,8 @@ Before diving into the three levels, establish the project context. **In design-new mode**, ask the user: -"What course or training program are we designing? Give me the basics: title, subject -area, and who requested it." +"What course or training program do you want to design? Tell me the title, the subject +area and the person who requested it." Then establish the delivery context. Ask about: - **Modality:** Online, face-to-face, hybrid, or hyflex? @@ -120,11 +120,11 @@ Store these in the `context` section of the manifest. **In audit-existing mode, the "is training justified?" question is moot.** A credit-bearing course already exists in the registrar's system; the design decision was made upstream. Skip the decision-gate logic below and instead ask one design-fit question: -> "Given the course as imported (from `import_metadata.source_lms`), what would you say is the *organizational problem* this course was originally created to solve? (e.g., 'undergraduates lack synthesis skills before entering capstone'). Be specific — this anchors the rest of the design audit." +> "The course came from `import_metadata.source_lms`. What *organizational problem* did your institution create this course to solve? For example: 'Undergraduates do not have synthesis skills when they start the capstone.' Give a clear answer. The rest of the design audit uses this answer." Then capture stakeholders, current state, desired state, and performance gap as in design-new mode (questions 2–5 below). Set `needs_analysis.training_justification` to: ```json -{"justified": true, "confidence": 10, "rationale": "Existing credit-bearing course; design audit only — training-fit decision is upstream of this skill.", "alternatives_considered": []} +{"justified": true, "confidence": 10, "rationale": "This course is in use for credit. This skill does a design audit only. The decision about training occurred before this skill.", "alternatives_considered": []} ``` Then proceed to Step 3. @@ -147,9 +147,9 @@ Ask these questions (one at a time): 2. **"Who are the stakeholders? Who requested this, who approves it, who will be affected by it?"** -3. **"What is the current state? How are people performing right now?"** +3. **"What is the current state? How well do people do the task now?"** -4. **"What is the desired state? What should performance look like after this +4. **"What is the desired state? What performance do you want after this intervention?"** 5. **"What is the gap between current and desired state?"** @@ -162,14 +162,14 @@ Ask these questions (one at a time): After gathering answers, make a judgment: - If the gap is **knowledge or skill**: Training is likely justified. Proceed. -- If the gap is **motivation**: Training alone won't fix this. Flag it. Consider - incentive redesign, performance support, or management intervention. Training - may be part of the solution but not the whole solution [T3]. +- If the gap is **motivation**: Training alone is not the solution to this problem. Flag it. Tell the user that + incentive redesign, performance support, or management intervention can help. Training + can be part of the solution, but not all of the solution [T3]. - If the gap is **environmental** (bad tools, unclear processes, insufficient resources): - Training is NOT the right intervention. Say so directly. "Based on what you've - described, the performance gap is caused by [environmental factor], not a lack of - knowledge or skills. Training won't fix this. Consider [alternative intervention] - instead." [Needs-8] [T3] + Training is NOT the right intervention. Say so directly. "From what you + described, [environmental factor] causes the performance gap. The cause is not + knowledge or skills. Training is not the solution to this problem. idstack recommends + [alternative intervention]." [Needs-8] [T3] Populate the `training_justification` object: - `justified`: true or false (you CAN recommend against training) @@ -179,7 +179,7 @@ Populate the `training_justification` object: **If training is NOT justified:** Present the finding clearly. Ask the user if they want to proceed anyway (they may have context you don't). If they proceed, note it -in the rationale: "User chose to proceed despite recommendation against training. +in the rationale: "The user continued after idstack recommended against training. Reason: [user's reason]." --- @@ -190,8 +190,8 @@ Reason: [user's reason]." Ask: -1. **"What are the key tasks or activities that learners need to perform after - completing this course?"** +1. **"What are the key tasks or activities that learners must do after + they complete this course?"** Push for observable, measurable performance. "Understand ethics" is not a task. "Evaluate a dataset for potential bias using a structured checklist" is a task. @@ -205,7 +205,7 @@ Ask: 3. **"What prerequisite knowledge or skills do learners need before they can learn these tasks?"** -4. **"What tools, resources, or systems do learners use to perform these tasks?"** +4. **"What tools, resources, or systems do learners use to do these tasks?"** Assign task IDs: T-1, T-2, T-3, etc. @@ -238,7 +238,7 @@ Ask: [CogLoad-19] [T1]. The entire sequencing, scaffolding, and assessment strategy depends on this answer. -2. **"What motivates your learners? Why would they engage with this course?"** +2. **"What motivates your learners? Why do they do the work in this course?"** Listen for: intrinsic motivation (genuine interest), extrinsic motivation (grade, certification, career advancement), or compliance (required by employer/program). @@ -253,19 +253,19 @@ Ask: If the user mentions "learning styles," "VARK," "visual learners," "auditory learners," or similar: respond with this exact framing: -"I appreciate you thinking about learner differences. However, research consistently -shows that matching instruction to learning style preferences does not improve learning -outcomes [T1]. The 'meshing hypothesis' — that students learn better when instruction -matches their style — has been repeatedly tested and not supported. +"It is good that you think about learner differences. But many studies show that +instruction that matches learning style preferences does not make learning outcomes +better [T1]. Researchers tested the 'meshing hypothesis' — that students learn better when +instruction matches their style — many times. The results do not support it. -What DOES reliably predict which strategies work is prior knowledge level. Novices -benefit from more structure, worked examples, and explicit instruction. Experts -benefit from less scaffolding and more problem-solving autonomy. Let's focus on -prior knowledge instead." +Prior knowledge level is the factor that predicts which strategies work. Novices +learn more with more structure, worked examples, and explicit instruction. Experts +learn more with less scaffolding and more problem-solving autonomy. Thus, this +analysis uses prior knowledge level." The `learning_preferences_note` field in the manifest is always populated with: -"Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge -is the primary differentiator." +"idstack does NOT use learning styles to differentiate instruction because the evidence does +not support them. Prior knowledge is the primary differentiator." --- @@ -286,7 +286,7 @@ After completing all three levels, present a structured summary: - Performance Gap: [knowledge/skill/motivation/environment] - Training Justified: [Yes/No] (Confidence: X/10) - Rationale: [one paragraph] -- Alternatives Considered: [list] +- Alternatives Evaluated: [list] ### Level 2: Task Analysis [task table] @@ -339,9 +339,9 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **Skill-specific section before Findings** — add a `
        ` with `

        Project context

        ` and a `
        ` listing modality, timeline, class size, institution type, and available tech. - **Finding ids:** `needs-1`, `needs-2`, etc. Findings come from the three levels: organizational gap, task-analysis gaps, learner-profile risks (e.g., expertise mismatch with the planned design). - **Optional skill-specific sections** (after Top recommendations, before Limitations): - - `
        ` with `

        Training justification

        ` — show `Justified: Yes/No`, `Confidence: X/10`, the rationale paragraph, and alternatives considered (or "n/a — imported credit-bearing course" in audit-existing mode). + - `
        ` with `

        Training justification

        ` — show `Justified: Yes/No`, `Confidence: X/10`, the rationale paragraph, and alternatives evaluated (or "n/a — imported credit-bearing course" in audit-existing mode). - `
        ` with `

        Expertise-fit read

        ` — which instructional strategies are appropriate given the learner profile. Cite the expertise-reversal evidence so the read isn't opinion: `[CogLoad-4] [T1]` for novices; `[CogLoad-19] [T1]` for advanced; `[Learner-16] [T1]` for mixed. -- **Limitations:** imported-course mode skips the training-decision gate; learner profile draws on the registrar/syllabus, not a learner survey; task analysis is from job-task lists, not observed performance. +- **Limitations:** In imported-course mode, idstack skips the training-decision gate. The learner profile comes from the registrar or syllabus data, not from a learner survey. The task analysis comes from job-task lists, not from observed performance. - **Next steps:** Run `/idstack:learning-objectives` to develop ILOs grounded in this analysis. The objectives skill reads your task analysis and learner profile to recommend appropriate Bloom's levels and alignment strategies. Every finding in the HTML must correspond to a finding the manifest's `needs_analysis` section can carry, so downstream skills and `bin/idstack-status` can refer to them programmatically. @@ -375,17 +375,17 @@ Create or update the project manifest. Use the Write tool to write `.idstack/pro Write the manifest, then confirm to the user: -"Your needs analysis is saved. Two artifacts: +"I saved your needs analysis in two files: - **Read this:** `.idstack/exports//needs-analysis.html` — the branded HTML report with evidence-backed findings, the training-justification read, and a - learner-profile expertise check. Open it in any browser. The folder is self-contained - (CSS is bundled), so you can zip or email it. + learner-profile expertise check. Open it in a web browser. The folder also contains + the CSS file. You can zip or email the folder. - System state: `.idstack/project.json` (the manifest — for downstream skills). -**Next step:** Run `/idstack:learning-objectives` to develop learning objectives based on -this analysis. The objectives skill will read your task analysis and learner profile -to recommend appropriate Bloom's levels and alignment strategies." +**Next step:** Run `/idstack:learning-objectives` to develop learning objectives from +this analysis. The objectives skill reads your task analysis and learner profile +to recommend Bloom's levels and alignment strategies." --- diff --git a/skills/pipeline/SKILL.md b/skills/pipeline/SKILL.md index 23bdd1f..0ece2af 100644 --- a/skills/pipeline/SKILL.md +++ b/skills/pipeline/SKILL.md @@ -1,8 +1,8 @@ --- name: pipeline description: | - Pipeline orchestrator for idstack. Chains skills from /needs-analysis through - /course-export in evidence-based order, auto-skipping completed skills. + Pipeline orchestrator for idstack. Runs the skills from /needs-analysis to + /course-export in the evidence-based sequence. It skips the skills that are complete. Handles fresh starts (no manifest) and resumption (partial pipeline). (idstack) allowed-tools: - Bash @@ -205,7 +205,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -286,8 +286,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -313,8 +313,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -420,10 +420,10 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- @@ -495,7 +495,7 @@ starting point. **Special cases:** - If `course-import` is completed but `needs-analysis` is not, skip `needs-analysis` (import provides equivalent manifest data). -- If ALL skills are completed, tell the user via AskUserQuestion: "All pipeline skills have been completed. What would you like to do?" Options: +- If ALL skills are completed, tell the user via AskUserQuestion: "All pipeline skills are completed. What do you want to do?" Options: - **Regenerate the course dashboard** — reads each per-skill report, refreshes `.idstack/exports//index.html` with the latest cross-cutting view (no skills re-run). Recommended after editing per-skill outputs by hand. - **Re-run a specific skill** — e.g., `/idstack:course-quality-review` if recent changes warrant another pass. - **Exit** — leave everything as-is. @@ -509,7 +509,7 @@ Show the user a status table before starting: Pipeline Status: [done] /idstack:needs-analysis [done] /idstack:learning-objectives - [next] /idstack:assessment-design <-- starting here + [next] /idstack:assessment-design <-- the run starts here [ ] /idstack:course-builder [ ] /idstack:course-quality-review [ ] /idstack:accessibility-review @@ -527,7 +527,7 @@ ask which one using AskUserQuestion with the skill list as options. For each skill from the starting point onward: -1. Announce: "Starting /idstack:..." +1. Announce: "/idstack: starts." 2. Invoke the skill using the `Skill` tool with the **plugin-namespaced** skill name (e.g., `skill: "idstack:needs-analysis"`). Plugin skills are addressed as `plugin:skill` — a bare `"needs-analysis"` does not resolve in Claude Code. @@ -537,14 +537,14 @@ For each skill from the starting point onward: **If the `Skill` invocation fails** (the tool is unavailable, or the child skill cannot be resolved), degrade gracefully rather than silently skipping the stage: print exactly what -the user should type to run it themselves (e.g., "Type `/idstack:needs-analysis` to run the -next stage, then resume by typing `/idstack:pipeline` again") and STOP. The pipeline picks +the user should type to run it themselves (for example, "Type `/idstack:needs-analysis` to run the +next stage. Then type `/idstack:pipeline` again to continue the pipeline") and STOP. The pipeline picks up on the next `/idstack:pipeline` invocation by re-reading the timeline and continuing from the new starting point. Generate the course dashboard (Step 4) before stopping so the partial-run dashboard is up to date. **Between skills**, briefly announce the transition: -"[skill-name] complete. Course dashboard refreshed. Moving to /idstack:..." +"[skill-name] is completed. I updated the course dashboard. The next skill is /idstack:." ### Step 4: Generate Course Dashboard @@ -643,8 +643,8 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa ### Step 5: Pipeline Complete When all remaining skills have been executed: -- Announce: "Pipeline complete. Your course has been through all 8 stages." -- Confirm the dashboard path: "Course dashboard at `.idstack/exports//index.html` — open it in any browser for the cross-cutting view; the per-skill HTML reports in the same folder carry the full detail. Zip the folder to hand it to a stakeholder." +- Announce: "The pipeline is completed. Your course went through all 8 stages." +- Confirm the dashboard path: "The course dashboard is at `.idstack/exports//index.html`. Open it in a web browser to see the cross-cutting view. The per-skill HTML reports in the same folder give the full findings. To give the reports to a stakeholder, zip the folder." - If `/idstack:course-quality-review` produced a score, show it. - Remind the user they can re-run any skill individually if needed, and that re-running `/idstack:pipeline` regenerates the course dashboard. - Log the pipeline run so "the pipeline was run" is recoverable from the timeline: diff --git a/skills/pipeline/SKILL.md.tmpl b/skills/pipeline/SKILL.md.tmpl index 50ea25c..3f05405 100644 --- a/skills/pipeline/SKILL.md.tmpl +++ b/skills/pipeline/SKILL.md.tmpl @@ -1,8 +1,8 @@ --- name: pipeline description: | - Pipeline orchestrator for idstack. Chains skills from /needs-analysis through - /course-export in evidence-based order, auto-skipping completed skills. + Pipeline orchestrator for idstack. Runs the skills from /needs-analysis to + /course-export in the evidence-based sequence. It skips the skills that are complete. Handles fresh starts (no manifest) and resumption (partial pipeline). (idstack) allowed-tools: - Bash @@ -85,7 +85,7 @@ starting point. **Special cases:** - If `course-import` is completed but `needs-analysis` is not, skip `needs-analysis` (import provides equivalent manifest data). -- If ALL skills are completed, tell the user via AskUserQuestion: "All pipeline skills have been completed. What would you like to do?" Options: +- If ALL skills are completed, tell the user via AskUserQuestion: "All pipeline skills are completed. What do you want to do?" Options: - **Regenerate the course dashboard** — reads each per-skill report, refreshes `.idstack/exports//index.html` with the latest cross-cutting view (no skills re-run). Recommended after editing per-skill outputs by hand. - **Re-run a specific skill** — e.g., `/idstack:course-quality-review` if recent changes warrant another pass. - **Exit** — leave everything as-is. @@ -99,7 +99,7 @@ Show the user a status table before starting: Pipeline Status: [done] /idstack:needs-analysis [done] /idstack:learning-objectives - [next] /idstack:assessment-design <-- starting here + [next] /idstack:assessment-design <-- the run starts here [ ] /idstack:course-builder [ ] /idstack:course-quality-review [ ] /idstack:accessibility-review @@ -117,7 +117,7 @@ ask which one using AskUserQuestion with the skill list as options. For each skill from the starting point onward: -1. Announce: "Starting /idstack:..." +1. Announce: "/idstack: starts." 2. Invoke the skill using the `Skill` tool with the **plugin-namespaced** skill name (e.g., `skill: "idstack:needs-analysis"`). Plugin skills are addressed as `plugin:skill` — a bare `"needs-analysis"` does not resolve in Claude Code. @@ -127,14 +127,14 @@ For each skill from the starting point onward: **If the `Skill` invocation fails** (the tool is unavailable, or the child skill cannot be resolved), degrade gracefully rather than silently skipping the stage: print exactly what -the user should type to run it themselves (e.g., "Type `/idstack:needs-analysis` to run the -next stage, then resume by typing `/idstack:pipeline` again") and STOP. The pipeline picks +the user should type to run it themselves (for example, "Type `/idstack:needs-analysis` to run the +next stage. Then type `/idstack:pipeline` again to continue the pipeline") and STOP. The pipeline picks up on the next `/idstack:pipeline` invocation by re-reading the timeline and continuing from the new starting point. Generate the course dashboard (Step 4) before stopping so the partial-run dashboard is up to date. **Between skills**, briefly announce the transition: -"[skill-name] complete. Course dashboard refreshed. Moving to /idstack:..." +"[skill-name] is completed. I updated the course dashboard. The next skill is /idstack:." ### Step 4: Generate Course Dashboard @@ -199,8 +199,8 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa ### Step 5: Pipeline Complete When all remaining skills have been executed: -- Announce: "Pipeline complete. Your course has been through all 8 stages." -- Confirm the dashboard path: "Course dashboard at `.idstack/exports//index.html` — open it in any browser for the cross-cutting view; the per-skill HTML reports in the same folder carry the full detail. Zip the folder to hand it to a stakeholder." +- Announce: "The pipeline is completed. Your course went through all 8 stages." +- Confirm the dashboard path: "The course dashboard is at `.idstack/exports//index.html`. Open it in a web browser to see the cross-cutting view. The per-skill HTML reports in the same folder give the full findings. To give the reports to a stakeholder, zip the folder." - If `/idstack:course-quality-review` produced a score, show it. - Remind the user they can re-run any skill individually if needed, and that re-running `/idstack:pipeline` regenerates the course dashboard. - Log the pipeline run so "the pipeline was run" is recoverable from the timeline: diff --git a/skills/red-team/SKILL.md b/skills/red-team/SKILL.md index 25f58d4..953fb1a 100644 --- a/skills/red-team/SKILL.md +++ b/skills/red-team/SKILL.md @@ -4,7 +4,7 @@ description: | Adversarial course design audit across 5 dimensions: alignment stress test, evidence verification, cognitive load analysis, learner persona simulation, and prerequisite chain integrity. Produces a confidence score (0-100). - Runs in a clean-context sub-agent so synthesis is unbiased by build history. + Runs in a clean-context sub-agent. Thus the build history does not change the synthesis. Works standalone or reads from the idstack project manifest. (idstack) allowed-tools: - Bash @@ -208,7 +208,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -289,8 +289,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -316,8 +316,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -423,15 +423,15 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- **Skill-specific manifest check:** If the manifest `red_team_audit` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Red Team — Adversarial Course Design Audit @@ -479,7 +479,7 @@ The preamble above already ran the manifest check. Now confirm scope. **Ask one focus question** via AskUserQuestion: -> "Any specific angle to red-team, or a full sweep?" +> "Do you want a full sweep, or a red-team audit with one focus?" Options: - **Full sweep (recommended)** — all 5 dimensions at equal depth @@ -581,7 +581,7 @@ Check every evidence citation in the manifest or course design for accuracy. [Assessment-18] [T3] — systematic reviews of meta-analyses reveal how evidence evolves. - Only flag contradictions from clearly relevant papers. Ignore tangential matches. - Check for retractions of cited papers. -- If WebSearch is unavailable, set `mode: limited` in the report and note: "currency verification requires internet." +- If WebSearch is unavailable, set `mode: limited` in the report and note: "the currency check must have internet access." ### Dimension 3 — Cognitive Load Analysis @@ -693,9 +693,9 @@ Severity weights reflect that structural misalignment and cognitive overload are Contextualize: - 80+ "High confidence" — minor issues only -- 60-79 "Moderate, needs work" — several design gaps -- 40-59 "Low confidence, significant gaps" — multiple problem dimensions -- <40 "Course needs redesign" — structural issues across most dimensions +- 60-79 "Moderate confidence, changes necessary" — several design gaps +- 40-59 "Low confidence, important gaps" — multiple problem dimensions +- <40 "New design necessary" — structural issues across most dimensions --- @@ -792,7 +792,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Red Team Audit" - **`{{skill_name}}`:** `red-team` - **`{{mode}}`:** `full` or `limited` (include the optional mode segment in the header `meta` line; you may also append `· focus: {{FOCUS}}`). -- **Summary:** 2–3 sentences. Lead with the confidence score (0–100) and the band ("High confidence" / "Moderate, needs work" / "Low confidence, significant gaps" / "Course needs redesign"). Include the optional one-line scoreboard: "Confidence XX/100 · Critical N · Warning N · Info N". +- **Summary:** 2–3 sentences. Lead with the confidence score (0–100) and the band ("High confidence" / "Moderate confidence, changes necessary" / "Low confidence, important gaps" / "New design necessary"). Include the optional one-line scoreboard: "Confidence XX/100 · Critical N · Warning N · Info N". - **Findings** ordered by severity (`sev-critical` → `sev-warning` → `sev-info`) inside a single `
        `. Stable ids of the form `-` (e.g., `alignment-1`, `cogload-3`) so the parent can reference findings when applying fixes. Each `
        ` should include the affected module/objective/assessment in the "What we saw" `
        `. - **Skill-specific section before Findings** — add a `
        ` with `

        Per-dimension summary

        ` and an HTML `
        ` with a row for each dimension (Alignment, Evidence, Cognitive Load, Personas, Prerequisites) showing the per-dimension `pass | warning | critical` score and a one-line summary. - **Top recommendations:** the 3 changes that would most improve the score. Reference finding ids. @@ -830,7 +830,7 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa ## Return value After writing the report, return ONLY a short executive summary (≤200 words) to the parent: -- Confidence score and band ("Moderate, needs work") +- Confidence score and band ("Moderate confidence, changes necessary") - Severity counts - Top 1 critical finding (one line) - Path: the value of `$_REPORT_PATH` (e.g., `.idstack/exports//red-team.html`) @@ -844,19 +844,19 @@ Do NOT return the full report inline. The parent will read the file. After the orchestrator returns: 1. Read the HTML report file at the path the orchestrator returned (e.g., `.idstack/exports//red-team.html`). HTML is fine to Read — extract the content sections by tag. 2. Show the user the executive summary in your own words: confidence score, severity counts, top critical finding, and the report path. -3. Mention: "Full HTML report at the path above — open it in any browser for the complete finding list." +3. Mention: "The full HTML report is at the path above. Open it in a web browser to see all of the findings." ### Step 4: Triage — choose fix scope Ask one AskUserQuestion: -> "Which findings would you like to address?" +> "Which findings do you want to correct?" Options: - **Critical only (recommended)** — highest-impact fixes, smallest scope - **Critical + Warning** — broader cleanup -- **All findings** — including Info; can be a lot -- **Skip — review report manually** — no fixes now; user will read the file themselves +- **All findings** — Info findings also. This can be many changes. +- **Skip — review report manually** — no changes. You read the report. If the user chooses **Skip**, jump straight to Step 6. @@ -952,7 +952,7 @@ Each finding object: `{"id": "alignment-1", "description": "...", "module": "Mod `fixes_applied[]` — each item: `{"id": "alignment-1", "description": "Optional one-line summary of the change applied"}`. -`fixes_deferred[]` — each item: `{"id": "alignment-3", "reason": "One-line reason — e.g., requires re-running /idstack:assessment-design"}`. +`fixes_deferred[]` — each item: `{"id": "alignment-3", "reason": "One-line reason — for example, the fix needs a new run of /idstack:assessment-design"}`. The merge tool exits non-zero (and prints a diagnostic on stderr) if the payload is malformed, the manifest is corrupt, or the section name is misspelled — never silently overwriting. If @@ -989,7 +989,7 @@ canonical schema for reference is in `templates/manifest-schema.md`. ### Step 7: Final summary to user Two sentences: -- "Confidence: X/100. Applied N fixes (M deferred). Report at `.idstack/exports//red-team.html`." +- "Confidence: X/100. I made N changes and deferred M changes. The report is at `.idstack/exports//red-team.html`." - If confidence is <60 after fixes, recommend re-running `/idstack:learning-objectives` or `/idstack:assessment-design`. If 60+, recommend `/idstack:course-export`. If the user wants to verify fixes hold, they can re-run `/idstack:red-team` — that's deliberately manual to avoid token costs of automatic re-verification. diff --git a/skills/red-team/SKILL.md.tmpl b/skills/red-team/SKILL.md.tmpl index 30f709a..c04a82e 100644 --- a/skills/red-team/SKILL.md.tmpl +++ b/skills/red-team/SKILL.md.tmpl @@ -4,7 +4,7 @@ description: | Adversarial course design audit across 5 dimensions: alignment stress test, evidence verification, cognitive load analysis, learner persona simulation, and prerequisite chain integrity. Produces a confidence score (0-100). - Runs in a clean-context sub-agent so synthesis is unbiased by build history. + Runs in a clean-context sub-agent. Thus the build history does not change the synthesis. Works standalone or reads from the idstack project manifest. (idstack) allowed-tools: - Bash @@ -21,7 +21,7 @@ allowed-tools: {{PREAMBLE}} **Skill-specific manifest check:** If the manifest `red_team_audit` section already has data, -ask the user: "I see you've already run this skill. Want to update the results or start fresh?" +ask the user: "You ran this skill previously. Do you want to update the results or start again?" # Red Team — Adversarial Course Design Audit @@ -69,7 +69,7 @@ The preamble above already ran the manifest check. Now confirm scope. **Ask one focus question** via AskUserQuestion: -> "Any specific angle to red-team, or a full sweep?" +> "Do you want a full sweep, or a red-team audit with one focus?" Options: - **Full sweep (recommended)** — all 5 dimensions at equal depth @@ -171,7 +171,7 @@ Check every evidence citation in the manifest or course design for accuracy. [Assessment-18] [T3] — systematic reviews of meta-analyses reveal how evidence evolves. - Only flag contradictions from clearly relevant papers. Ignore tangential matches. - Check for retractions of cited papers. -- If WebSearch is unavailable, set `mode: limited` in the report and note: "currency verification requires internet." +- If WebSearch is unavailable, set `mode: limited` in the report and note: "the currency check must have internet access." ### Dimension 3 — Cognitive Load Analysis @@ -283,9 +283,9 @@ Severity weights reflect that structural misalignment and cognitive overload are Contextualize: - 80+ "High confidence" — minor issues only -- 60-79 "Moderate, needs work" — several design gaps -- 40-59 "Low confidence, significant gaps" — multiple problem dimensions -- <40 "Course needs redesign" — structural issues across most dimensions +- 60-79 "Moderate confidence, changes necessary" — several design gaps +- 40-59 "Low confidence, important gaps" — multiple problem dimensions +- <40 "New design necessary" — structural issues across most dimensions --- @@ -348,7 +348,7 @@ Write the HTML report at the path printed above (`.idstack/exports/ - **`{{skill_title}}`:** "Red Team Audit" - **`{{skill_name}}`:** `red-team` - **`{{mode}}`:** `full` or `limited` (include the optional mode segment in the header `meta` line; you may also append `· focus: {{FOCUS}}`). -- **Summary:** 2–3 sentences. Lead with the confidence score (0–100) and the band ("High confidence" / "Moderate, needs work" / "Low confidence, significant gaps" / "Course needs redesign"). Include the optional one-line scoreboard: "Confidence XX/100 · Critical N · Warning N · Info N". +- **Summary:** 2–3 sentences. Lead with the confidence score (0–100) and the band ("High confidence" / "Moderate confidence, changes necessary" / "Low confidence, important gaps" / "New design necessary"). Include the optional one-line scoreboard: "Confidence XX/100 · Critical N · Warning N · Info N". - **Findings** ordered by severity (`sev-critical` → `sev-warning` → `sev-info`) inside a single `
        `. Stable ids of the form `-` (e.g., `alignment-1`, `cogload-3`) so the parent can reference findings when applying fixes. Each `
        ` should include the affected module/objective/assessment in the "What we saw" `
        `. - **Skill-specific section before Findings** — add a `
        ` with `

        Per-dimension summary

        ` and an HTML `
        ` with a row for each dimension (Alignment, Evidence, Cognitive Load, Personas, Prerequisites) showing the per-dimension `pass | warning | critical` score and a one-line summary. - **Top recommendations:** the 3 changes that would most improve the score. Reference finding ids. @@ -369,7 +369,7 @@ For the result, do the steps in "How to use the standard" in the "Writing Standa ## Return value After writing the report, return ONLY a short executive summary (≤200 words) to the parent: -- Confidence score and band ("Moderate, needs work") +- Confidence score and band ("Moderate confidence, changes necessary") - Severity counts - Top 1 critical finding (one line) - Path: the value of `$_REPORT_PATH` (e.g., `.idstack/exports//red-team.html`) @@ -383,19 +383,19 @@ Do NOT return the full report inline. The parent will read the file. After the orchestrator returns: 1. Read the HTML report file at the path the orchestrator returned (e.g., `.idstack/exports//red-team.html`). HTML is fine to Read — extract the content sections by tag. 2. Show the user the executive summary in your own words: confidence score, severity counts, top critical finding, and the report path. -3. Mention: "Full HTML report at the path above — open it in any browser for the complete finding list." +3. Mention: "The full HTML report is at the path above. Open it in a web browser to see all of the findings." ### Step 4: Triage — choose fix scope Ask one AskUserQuestion: -> "Which findings would you like to address?" +> "Which findings do you want to correct?" Options: - **Critical only (recommended)** — highest-impact fixes, smallest scope - **Critical + Warning** — broader cleanup -- **All findings** — including Info; can be a lot -- **Skip — review report manually** — no fixes now; user will read the file themselves +- **All findings** — Info findings also. This can be many changes. +- **Skip — review report manually** — no changes. You read the report. If the user chooses **Skip**, jump straight to Step 6. @@ -457,7 +457,7 @@ Each finding object: `{"id": "alignment-1", "description": "...", "module": "Mod `fixes_applied[]` — each item: `{"id": "alignment-1", "description": "Optional one-line summary of the change applied"}`. -`fixes_deferred[]` — each item: `{"id": "alignment-3", "reason": "One-line reason — e.g., requires re-running /idstack:assessment-design"}`. +`fixes_deferred[]` — each item: `{"id": "alignment-3", "reason": "One-line reason — for example, the fix needs a new run of /idstack:assessment-design"}`. The merge tool exits non-zero (and prints a diagnostic on stderr) if the payload is malformed, the manifest is corrupt, or the section name is misspelled — never silently overwriting. If @@ -477,7 +477,7 @@ canonical schema for reference is in `templates/manifest-schema.md`. ### Step 7: Final summary to user Two sentences: -- "Confidence: X/100. Applied N fixes (M deferred). Report at `.idstack/exports//red-team.html`." +- "Confidence: X/100. I made N changes and deferred M changes. The report is at `.idstack/exports//red-team.html`." - If confidence is <60 after fixes, recommend re-running `/idstack:learning-objectives` or `/idstack:assessment-design`. If 60+, recommend `/idstack:course-export`. If the user wants to verify fixes hold, they can re-run `/idstack:red-team` — that's deliberately manual to avoid token costs of automatic re-verification. diff --git a/templates/manifest-schema.md b/templates/manifest-schema.md index 4cde237..31af0f7 100644 --- a/templates/manifest-schema.md +++ b/templates/manifest-schema.md @@ -107,7 +107,7 @@ The merge tool replaces only the named top-level section, preserves every other "motivation_factors": [], "demographics": "", "access_constraints": [], - "learning_preferences_note": "Learning styles are NOT used as a differentiation basis per evidence. Prior knowledge is the primary differentiator." + "learning_preferences_note": "idstack does NOT use learning styles to differentiate instruction because the evidence does not support them. Prior knowledge is the primary differentiator." }, "training_justification": { "justified": true, diff --git a/templates/preamble.md b/templates/preamble.md index abc1ade..a67d5ed 100644 --- a/templates/preamble.md +++ b/templates/preamble.md @@ -185,7 +185,7 @@ _UPD=$("$_IDSTACK/bin/idstack-update-check" 2>/dev/null || true) [ -n "$_UPD" ] && echo "$_UPD" ``` -If the output contains `UPDATE_AVAILABLE`: tell the user "A newer version of idstack is available. Run `cd $_IDSTACK && git pull && ./setup` to update. (The `./setup` step is required — it cleans up legacy symlinks.)" Then continue normally. +If the output contains `UPDATE_AVAILABLE`: tell the user "A new version of idstack is available. To update, run `cd $_IDSTACK && git pull && ./setup`. Do not skip the `./setup` step. It removes symlinks from earlier installs." Then continue normally. ## Preamble: Project Manifest @@ -266,8 +266,8 @@ instructional design concepts but explain idstack-specific patterns. **If EXPERIENCE:expert:** Be concise. Skip basic explanations. Focus on evidence tiers, edge cases, and advanced considerations. Trust the user's domain knowledge. **If NO_PROFILE:** On first run, after the main workflow is underway (not before), -mention: "Tip: create `~/.idstack/profile.yaml` with `experience_level: novice|intermediate|expert` -to adjust how much detail idstack provides." +mention: "Note: To change how much information idstack gives, create `~/.idstack/profile.yaml` +with `experience_level: novice|intermediate|expert`." ## Preamble: Evidence Engine & Consensus QA @@ -293,8 +293,8 @@ _CONSENSUS_KEY=$("$_IDSTACK/bin/idstack-consensus" status 2>/dev/null | grep -q **If CONFIGURED:** Live literature grounding via Consensus API is active. Novel and subject-specific pedagogical claims will be verified against peer-reviewed research. **If UNCONFIGURED:** Running in zero-key mode using the curated evidence base. On first -run, after the main workflow is underway (not before), mention: "Tip: Set CONSENSUS_API_KEY -to enable live literature verification via Consensus." +run, after the main workflow is underway (not before), mention: "Note: Set CONSENSUS_API_KEY +to let idstack use Consensus to check its claims against published research." ## Preamble: Context Recovery @@ -400,9 +400,9 @@ mention it as a recurring pattern with its evidence citation. Example: "Welcome back. Last session you ran /idstack:course-import." **If SUGGESTED_NEXT is shown:** Mention the suggested next skill naturally. -Example: "Based on your progress, /idstack:assessment-design is the natural next step." +Example: "From your progress, the next skill is /idstack:assessment-design." **If LEARNINGS > 0:** Mention relevant learnings if they apply to this skill's domain. -Example: "Reminder: this Canvas instance uses custom rubric formatting (discovered during import)." +Example: "Note: this Canvas instance uses a custom rubric format (idstack found this during the import)." --- diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 1a4666f..2ed68e1 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -149,7 +149,7 @@ expect_fail "bare /skill reference regression" "$WORK/r/test/smoke-test.sh" "$WO # "/assessment-design is the natural next step" — a command that does nothing. The # preamble is spliced into all 22 skill files, so this reached every user. fresh -sed -i.bak 's|/idstack:assessment-design is the natural next step|/assessment-design is the natural next step|' \ +sed -i.bak 's|the next skill is /idstack:assessment-design|the next skill is /assessment-design|' \ "$WORK/r/templates/preamble.md" regen expect_fail "unbackticked bare /skill in preamble regression" "$WORK/r/test/smoke-test.sh" "$WORK/r" @@ -1632,6 +1632,21 @@ PY regen expect_fail "course-builder drops its course-content check step" "$WORK/r/test/smoke-test.sh" "$WORK/r" +# 49a. The two learning_preferences_note copies drift apart -> smoke-test must fail. +# The schema default and the needs-analysis step carry the same fixed STE sentence. +# Changing one copy without the other makes the manifest and the skill disagree. +fresh +python3 - "$WORK/r/skills/needs-analysis/SKILL.md.tmpl" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "because the evidence does\nnot support them." +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "because the evidence does\nnot support it.", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +regen +expect_fail "the learning_preferences_note copies drift apart" "$WORK/r/test/smoke-test.sh" "$WORK/r" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/smoke-test.sh b/test/smoke-test.sh index c5e86a1..ea6f723 100755 --- a/test/smoke-test.sh +++ b/test/smoke-test.sh @@ -179,6 +179,19 @@ for skill in needs-analysis course-import; do check "$skill SKILL.md.tmpl justifies its Write-tool fallback" "grep -q 'Write-tool fallback' '$IDSTACK_DIR/skills/$skill/SKILL.md.tmpl'" done +# The learning_preferences_note text has two copies: the schema default and the +# needs-analysis step that fills it. Both are fixed STE text and must keep the +# same words, or the manifest says one thing and the skill writes another. +NOTE_SYNC_PY='import re, sys +d = sys.argv[1] +schema = open(d + "/templates/manifest-schema.md", encoding="utf-8").read() +skill = open(d + "/skills/needs-analysis/SKILL.md.tmpl", encoding="utf-8").read() +a = re.search(r"\x22learning_preferences_note\x22: \x22([^\x22]+)\x22", schema) +b = re.search(r"always populated with:\s*\x22([^\x22]+)\x22", skill) +same = a and b and " ".join(a.group(1).split()) == " ".join(b.group(1).split()) +sys.exit(0 if same else 1)' +check "learning_preferences_note has the same words in manifest-schema.md and needs-analysis" "python3 -c '$NOTE_SYNC_PY' '$IDSTACK_DIR'" + # Canonical manifest section names only — these five non-canonical tokens once # shipped in re-run checks and prose, making re-run detection dead in 5 skills. for skill in $SKILLS; do From 719ba073ba98edf69c19f6703383ac8f4045c9d5 Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 13:35:59 -0700 Subject: [PATCH 04/10] feat(cli): rewrite setup, doctor and status messages in ASD-STE100 Co-Authored-By: Claude Haiku 4.5 --- README.md | 15 +++++----- bin/idstack-doctor | 42 +++++++++++++-------------- bin/idstack-status | 25 ++++++++-------- setup | 61 +++++++++++++++++++++------------------- test/integration-test.sh | 10 +++---- test/mutation-test.sh | 51 +++++++++++++++++++++++++++++---- test/test-doctor.sh | 40 ++++++++++++++++++++++++++ test/test-setup.sh | 19 +++++++++++-- test/test-status.sh | 33 +++++++++++++++++++--- 9 files changed, 211 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index be3ce6b..9ef1fe5 100644 --- a/README.md +++ b/README.md @@ -152,19 +152,20 @@ Setup is idempotent — re-run it any time. You should see: ``` regenerated skill files -Installing idstack (user)... +Setup installs idstack (scope: user)... source: /path/to/idstack -idstack installed (Claude Code) — scope: user. +Setup installed idstack in Claude Code (scope: user). - If Claude Code is already running, restart it (plugins load at session start). + If Claude Code is open, close it and open it again. + Claude Code reads plugins only when a session starts. - Usage: /idstack: + To use a skill, run /idstack: - Have an existing course? + If you have a course, import it and examine it: /idstack:course-import -> /idstack:course-quality-review - Starting fresh? + To start a new course: /idstack:needs-analysis Run the full pipeline: @@ -174,7 +175,7 @@ idstack installed (Claude Code) — scope: user. course-builder, course-quality-review, accessibility-review, red-team, course-export, course-import, pipeline, learn - More info: https://idstack.org + More information: https://idstack.org ``` ### Chrome Extension (No-Terminal Mode) diff --git a/bin/idstack-doctor b/bin/idstack-doctor index fae2af1..b5a3aa5 100755 --- a/bin/idstack-doctor +++ b/bin/idstack-doctor @@ -57,7 +57,7 @@ if [ -f "$plugin_manifest" ]; then version=$(grep -o '"version": *"[^"]*"' "$plugin_manifest" | head -n1 | sed 's/.*"\([^"]*\)"$/\1/') fi if [ -n "$version" ]; then - echo " plugin.json present (version $version)" + echo " found plugin.json (version $version)" else echo " PROBLEM: could not parse version from $plugin_manifest" note_problem @@ -67,11 +67,11 @@ else note_problem fi if [ -f "$market_manifest" ]; then - echo " marketplace.json present" + echo " found marketplace.json" else echo " PROBLEM: missing $market_manifest" - echo " Claude Code installs plugins through a marketplace manifest;" - echo " without it, ./setup cannot register the plugin." + echo " Claude Code installs plugins through a marketplace manifest." + echo " Without this file, ./setup cannot install the plugin." note_problem fi echo "" @@ -86,9 +86,9 @@ for skill in $SKILLS; do fi done if [ "$missing_skills" = "0" ]; then - echo " all 11 SKILL.md files present" + echo " found all 11 SKILL.md files" else - echo " PROBLEM: $missing_skills SKILL.md file(s) missing under skills/" + echo " PROBLEM: $missing_skills SKILL.md file(s) missing in skills/" note_problem fi echo "" @@ -103,7 +103,7 @@ if command -v claude >/dev/null 2>&1; then # -A4 window read a neighboring plugin's status line and called a disabled # idstack enabled. Pinned by test/test-plugin-status.sh. if echo "$plugin_list" | plugin_is_enabled "idstack@idstack"; then - echo " idstack@idstack installed and enabled" + echo " idstack@idstack is installed and enabled." else echo " PROBLEM: idstack@idstack is installed but not enabled." echo " Fix: claude plugin enable idstack@idstack" @@ -111,19 +111,19 @@ if command -v claude >/dev/null 2>&1; then fi else echo " PROBLEM: idstack@idstack is not installed." - echo " Fix: run ./setup from the repo, or manually:" + echo " Fix: run ./setup from the repo. You can also install idstack manually:" echo " claude plugin marketplace add \"$IDSTACK_DIR\"" echo " claude plugin install idstack@idstack" note_problem fi else - echo " WARNING: 'claude' not found on PATH — cannot verify the install." - echo " Install Claude Code, then run ./setup from the repo." + echo " WARNING: 'claude' not found on PATH. Doctor cannot examine the Claude Code install." + echo " Install Claude Code. Then run ./setup from the repo." fi echo "" # 4. Legacy / vestigial install conflicts. -echo "Legacy install conflicts:" +echo "Legacy installs:" legacy_problem=0 # Vestigial bare symlink from the pre-marketplace setup method. Harmless on @@ -132,8 +132,8 @@ legacy_problem=0 # checks only ./.claude/plugins, the default checks only $HOME/.claude/plugins. vestigial="$LEGACY_BASE/plugins/idstack" if [ -L "$vestigial" ]; then - echo " NOTE: vestigial symlink at $vestigial (pre-marketplace install method)." - echo " Harmless — ./setup removes it. Or: rm \"$vestigial\"" + echo " NOTE: legacy plugin symlink at $vestigial (pre-marketplace install method)." + echo " This symlink is safe. ./setup removes it. You can also run: rm \"$vestigial\"" fi # Pre-v2.0.1.0 dispatcher install — the failure mode that surfaced as @@ -158,13 +158,13 @@ elif [ -d "$LEGACY_DIR" ]; then fi if [ -n "$signature" ]; then echo " PROBLEM: pre-v2.0.1.0 install at $LEGACY_DIR ($signature)" - echo " This shadows /idstack: namespacing — slash" - echo " commands resolve to a broken dispatcher." - echo " Fix: rm -rf \"$LEGACY_DIR\" (then restart Claude Code)" + echo " This install overrides the /idstack: namespace." + echo " Slash commands go to a defective dispatcher." + echo " Fix: rm -rf \"$LEGACY_DIR\" (then start Claude Code again)" legacy_problem=1 else - echo " WARNING: directory at $LEGACY_DIR is not a recognized idstack install." - echo " Not auto-removed. Inspect and remove manually if unwanted." + echo " WARNING: directory at $LEGACY_DIR is not a known idstack install." + echo " ./setup does not remove it. Examine it. If you do not want it, remove it manually." fi fi # Pre-v2 individual skill symlinks pointing into idstack. @@ -180,7 +180,7 @@ for skill in $SKILLS; do fi done if [ "$legacy_problem" = "0" ]; then - echo " none detected" + echo " no problems found" else note_problem fi @@ -195,7 +195,7 @@ elif [ ! -x "$IDSTACK_DIR/bin/idstack-consensus" ]; then echo " PROBLEM: bin/idstack-consensus is not executable" note_problem else - echo " idstack-consensus present and executable" + echo " found idstack-consensus (executable)" consensus_status="" if command -v python3 >/dev/null 2>&1; then consensus_status=$(python3 "$IDSTACK_DIR/bin/idstack-consensus" status 2>/dev/null || true) @@ -220,7 +220,7 @@ except Exception: fi echo " cached queries: ${cached_count:-0}" else - echo " Consensus status: unavailable" + echo " Consensus status: not available" fi fi echo "" diff --git a/bin/idstack-status b/bin/idstack-status index 08b4226..01071b4 100755 --- a/bin/idstack-status +++ b/bin/idstack-status @@ -42,10 +42,11 @@ fi if [ ! -f "$TIMELINE" ]; then if [ "$READINESS_ONLY" -eq 1 ]; then - echo "No timeline data. Run pipeline skills first." + echo "No timeline data. Run the pipeline skills first." else - echo "No course data yet." - echo "Run /idstack:needs-analysis to start a new course, or /idstack:course-import to bring in an existing one." + echo "There is no course data." + echo "To start a new course, run /idstack:needs-analysis." + echo "To import a course that you have, run /idstack:course-import." fi exit 0 fi @@ -54,8 +55,8 @@ if ! command -v python3 &>/dev/null; then echo "Project: $PROJECT_NAME" echo "" TOTAL=$(wc -l < "$TIMELINE" 2>/dev/null | tr -d ' ') - echo "Timeline: $TOTAL events" - echo "(Install python3 for score trends and dimension analysis)" + echo "Timeline: $TOTAL entries" + echo "(To see score trends and dimension analysis, install python3.)" exit 0 fi @@ -75,7 +76,7 @@ for line in lines: pass if not events: - print('No valid timeline entries.') + print('The timeline has no entries that idstack can read.') sys.exit(0) print('Project: ' + os.environ.get('PROJECT_NAME', 'unknown')) @@ -140,15 +141,15 @@ if not next_skill and 'needs-analysis' not in completed and 'course-import' not next_skill = 'needs-analysis' if next_skill: - print(f'Suggested next: /idstack:{next_skill}') + print(f'Recommended next: /idstack:{next_skill}') print() -" || echo "Error reading timeline." +" || echo "Error: idstack cannot read the timeline." fi # Learnings count if [ -f "$LEARNINGS" ] && [ "$READINESS_ONLY" -eq 0 ]; then LEARN_COUNT=$(wc -l < "$LEARNINGS" 2>/dev/null | tr -d ' ') - [ "$LEARN_COUNT" -gt 0 ] 2>/dev/null && echo "Learnings: $LEARN_COUNT discoveries stored" + [ "$LEARN_COUNT" -gt 0 ] 2>/dev/null && echo "Learnings: $LEARN_COUNT entries" fi # Reports — list every per-skill HTML report present under the course's export @@ -174,7 +175,7 @@ if [ -d ".idstack/exports" ] && [ "$READINESS_ONLY" -eq 0 ]; then REPORT_COUNT=$(find "$COURSE_DIR" -maxdepth 1 -name '*.html' -type f 2>/dev/null | wc -l | tr -d ' ') if [ "$REPORT_COUNT" -gt 0 ] 2>/dev/null; then echo "" - echo "Reports (open in a browser for the human view):" + echo "Reports (open these files in a browser):" if [ -f "$COURSE_DIR/index.html" ]; then echo " $COURSE_DIR/index.html <-- course dashboard, start here" fi @@ -204,7 +205,7 @@ READY_RED_CRITICAL_MAX = $READY_RED_CRITICAL_MAX if not os.path.exists(timeline_file): if readiness_only: - print('No timeline data. Run pipeline skills first.') + print('No timeline data. Run the pipeline skills first.') sys.exit(0) # Read timeline for completed skills @@ -290,5 +291,5 @@ def verdict(): print(f' Verdict: {verdict()}') print() -" || echo " (readiness computation failed)" +" || echo " (Error: idstack cannot calculate the readiness.)" fi diff --git a/setup b/setup index ee60855..14aedd6 100755 --- a/setup +++ b/setup @@ -26,14 +26,14 @@ for arg in "$@"; do ;; --keep-legacy) KEEP_LEGACY=1 ;; -h|--help) - echo "Usage: ./setup [--local] [--keep-legacy]" - echo " --local install at project scope (./.claude/) instead of user scope" - echo " --keep-legacy keep legacy installs in place — the pre-v2.0.1.0 dispatcher" - echo " dir, the v2.0 dispatcher symlink, and pre-v2 per-skill" - echo " symlinks under \$HOME/.claude/skills/ (default: remove)" + echo "Command: ./setup [--local] [--keep-legacy]" + echo " --local Installs idstack at project scope (./.claude/), not at user scope." + echo " --keep-legacy Keeps the legacy installs in \$HOME/.claude/skills/. These are the" + echo " pre-v2.0.1.0 dispatcher directory, the v2.0 dispatcher symlink and" + echo " the pre-v2 per-skill symlinks. By default, setup removes them." exit 0 ;; - *) echo "ERROR: unknown argument: $arg" >&2; echo "Run ./setup --help for usage." >&2; exit 2 ;; + *) echo "ERROR: unknown argument: $arg" >&2; echo "Run ./setup --help for more information." >&2; exit 2 ;; esac done @@ -42,7 +42,7 @@ done "$IDSTACK_DIR/bin/idstack-gen-skills" >/dev/null echo " regenerated skill files" -echo "Installing idstack ($SCOPE)..." +echo "Setup installs idstack (scope: $CLAUDE_SCOPE)..." echo " source: $IDSTACK_DIR" echo "" @@ -50,12 +50,12 @@ echo "" # not silently install something Claude Code can't discover). if [ ! -f "$IDSTACK_DIR/.claude-plugin/plugin.json" ]; then echo "ERROR: missing .claude-plugin/plugin.json at $IDSTACK_DIR" >&2 - echo " This setup script expects a plugin layout. Aborting." >&2 + echo " A plugin layout must have this file. Setup stopped." >&2 exit 1 fi if [ ! -f "$IDSTACK_DIR/.claude-plugin/marketplace.json" ]; then echo "ERROR: missing .claude-plugin/marketplace.json at $IDSTACK_DIR" >&2 - echo " Claude Code installs plugins through a marketplace manifest. Aborting." >&2 + echo " Claude Code installs plugins through a marketplace manifest. Setup stopped." >&2 exit 1 fi @@ -70,8 +70,8 @@ LEGACY_SKILLS_LINK="$LEGACY_SKILLS_DIR/idstack" if [ -L "$LEGACY_SKILLS_LINK" ]; then legacy_target=$(readlink "$LEGACY_SKILLS_LINK" 2>/dev/null || true) if [ "$KEEP_LEGACY" = "1" ]; then - echo " KEEPING legacy symlink: $LEGACY_SKILLS_LINK (--keep-legacy)" - echo " NOTE: this shadows the plugin namespace; /idstack: may fail." + echo " KEPT legacy symlink: $LEGACY_SKILLS_LINK (--keep-legacy)" + echo " NOTE: This symlink overrides the plugin namespace. It can cause errors in /idstack: commands." else rm "$LEGACY_SKILLS_LINK" echo " removed legacy symlink: $LEGACY_SKILLS_LINK${legacy_target:+ (was -> $legacy_target)}" @@ -98,15 +98,15 @@ elif [ -d "$LEGACY_SKILLS_LINK" ] && [ "$LEGACY_SKILLS_LINK" != "$IDSTACK_DIR" ] fi if [ "$legacy_is_dispatcher" = "1" ] || [ "$legacy_is_old_version" = "1" ]; then if [ "$KEEP_LEGACY" = "1" ]; then - echo " KEEPING legacy install dir: $LEGACY_SKILLS_LINK (--keep-legacy)" - echo " NOTE: this will shadow the plugin namespace; /idstack: may fail." + echo " KEPT legacy install directory: $LEGACY_SKILLS_LINK (--keep-legacy)" + echo " NOTE: This directory overrides the plugin namespace. It can cause errors in /idstack: commands." else rm -rf "$LEGACY_SKILLS_LINK" - echo " removed legacy install dir: $LEGACY_SKILLS_LINK (pre-v2.0.1.0 dispatcher)" + echo " removed legacy install directory: $LEGACY_SKILLS_LINK (pre-v2.0.1.0 dispatcher)" fi else - echo " WARNING: directory at $LEGACY_SKILLS_LINK is not a recognized idstack install." - echo " Leaving it in place. Remove manually if it shouldn't be there:" + echo " WARNING: directory at $LEGACY_SKILLS_LINK is not a known idstack install." + echo " Setup did not remove it. If you do not want it, remove it manually:" echo " rm -rf $LEGACY_SKILLS_LINK" fi fi @@ -119,10 +119,10 @@ for skill in $LEGACY_SKILLS; do link_target=$(readlink "$legacy" 2>/dev/null || true) if echo "$link_target" | grep -q "idstack"; then if [ "$KEEP_LEGACY" = "1" ]; then - echo " KEEPING legacy symlink: $legacy (--keep-legacy)" + echo " KEPT legacy symlink: $legacy (--keep-legacy)" else rm "$legacy" - echo " cleaned up legacy: $legacy" + echo " removed legacy symlink: $legacy" fi fi fi @@ -141,7 +141,7 @@ else fi if [ -L "$vestigial" ]; then rm "$vestigial" - echo " removed vestigial symlink: $vestigial (pre-marketplace install method)" + echo " removed legacy plugin symlink: $vestigial (pre-marketplace install method)" fi # Claude Code install — register this repo as a single-plugin marketplace, then @@ -154,36 +154,39 @@ if command -v claude >/dev/null 2>&1; then # message at all. if ! claude plugin marketplace add "$IDSTACK_DIR"; then echo "" >&2 - echo "ERROR: 'claude plugin marketplace add' failed. Register manually, then re-run ./setup:" >&2 + echo "ERROR: The 'claude plugin marketplace add' command stopped with an error." >&2 + echo "Run this command manually. Then run ./setup again:" >&2 echo " claude plugin marketplace add \"$IDSTACK_DIR\"" >&2 exit 1 fi claude plugin marketplace update idstack >/dev/null 2>&1 || true if ! claude plugin install idstack@idstack --scope "$CLAUDE_SCOPE"; then echo "" >&2 - echo "ERROR: 'claude plugin install' failed. Install manually:" >&2 + echo "ERROR: The 'claude plugin install' command stopped with an error." >&2 + echo "Install idstack manually:" >&2 echo " claude plugin install idstack@idstack --scope $CLAUDE_SCOPE" >&2 exit 1 fi claude plugin update idstack@idstack --scope "$CLAUDE_SCOPE" >/dev/null 2>&1 || true echo "" - echo "idstack installed (Claude Code) — scope: $CLAUDE_SCOPE." + echo "Setup installed idstack in Claude Code (scope: $CLAUDE_SCOPE)." else - echo " WARNING: 'claude' not found on PATH — skipped Claude Code install." - echo " Install Claude Code, then re-run ./setup — or register it manually:" + echo " WARNING: 'claude' not found on PATH. Setup did not install idstack in Claude Code." + echo " Install Claude Code. Then run ./setup again. You can also install idstack manually:" echo " claude plugin marketplace add \"$IDSTACK_DIR\"" echo " claude plugin install idstack@idstack --scope $CLAUDE_SCOPE" fi echo "" -echo " If Claude Code is already running, restart it (plugins load at session start)." +echo " If Claude Code is open, close it and open it again." +echo " Claude Code reads plugins only when a session starts." echo "" -echo " Usage: /idstack:" +echo " To use a skill, run /idstack:" echo "" -echo " Have an existing course?" +echo " If you have a course, import it and examine it:" echo " /idstack:course-import -> /idstack:course-quality-review" echo "" -echo " Starting fresh?" +echo " To start a new course:" echo " /idstack:needs-analysis" echo "" echo " Run the full pipeline:" @@ -193,4 +196,4 @@ echo " All skills: needs-analysis, learning-objectives, assessment-design," echo " course-builder, course-quality-review, accessibility-review," echo " red-team, course-export, course-import, pipeline, learn" echo "" -echo " More info: https://idstack.org" +echo " More information: https://idstack.org" diff --git a/test/integration-test.sh b/test/integration-test.sh index 40a4703..ae5e77d 100755 --- a/test/integration-test.sh +++ b/test/integration-test.sh @@ -161,7 +161,7 @@ echo "" echo "## idstack-status" check "no timeline shows empty state message" \ - "rm -f .idstack/timeline.jsonl && $IDSTACK_DIR/bin/idstack-status | grep -q 'No course data yet'" + "rm -f .idstack/timeline.jsonl && $IDSTACK_DIR/bin/idstack-status | grep -q 'There is no course data'" # Rebuild timeline for status tests $IDSTACK_DIR/bin/idstack-timeline-log '{"skill":"needs-analysis","event":"completed","training_justified":true}' @@ -174,7 +174,7 @@ check "shows quality trend" \ "$IDSTACK_DIR/bin/idstack-status | grep -q 'Quality trend: 65'" check "suggests next skill" \ - "$IDSTACK_DIR/bin/idstack-status | grep -q 'Suggested next'" + "$IDSTACK_DIR/bin/idstack-status | grep -q 'Recommended next'" # Quote-injection regression: a project name with an apostrophe must render, # not blank the dashboard with a Python SyntaxError. @@ -185,15 +185,15 @@ EOF check "apostrophe in project name renders" \ "'$IDSTACK_DIR/bin/idstack-status' | grep -qF \"Project: Bob's Advanced Course\"" -check "apostrophe in project name: no 'Error reading timeline'" \ - "! '$IDSTACK_DIR/bin/idstack-status' | grep -q 'Error reading timeline'" +check "apostrophe in project name: no 'cannot read the timeline' error" \ + "! '$IDSTACK_DIR/bin/idstack-status' | grep -q 'idstack cannot read the timeline'" # course-import is an alternative pipeline entry: with only course-import # completed, the suggestion must be learning-objectives, not nothing. mkdir -p importcase && ( cd importcase && \ "$IDSTACK_DIR/bin/idstack-timeline-log" '{"skill":"course-import","event":"completed"}' ) check "course-import alone suggests learning-objectives" \ - "( cd importcase && '$IDSTACK_DIR/bin/idstack-status' | grep -q 'Suggested next: /idstack:learning-objectives' )" + "( cd importcase && '$IDSTACK_DIR/bin/idstack-status' | grep -q 'Recommended next: /idstack:learning-objectives' )" # Everything idstack-status prints is text a user may type back. It must carry # the /idstack: prefix for the same reason skill templates do — a bare /skill diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 2ed68e1..80b2bc7 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -257,12 +257,12 @@ python3 - "$WORK/r/setup" <<'PY' import sys p = sys.argv[1]; s = open(p).read() s = s.replace(''' if [ "$KEEP_LEGACY" = "1" ]; then - echo " KEEPING legacy symlink: $legacy (--keep-legacy)" + echo " KEPT legacy symlink: $legacy (--keep-legacy)" else rm "$legacy" - echo " cleaned up legacy: $legacy" + echo " removed legacy symlink: $legacy" fi''', ''' rm "$legacy" - echo " cleaned up legacy: $legacy"''') + echo " removed legacy symlink: $legacy"''') open(p,'w').write(s) PY expect_fail "--keep-legacy ignored by per-skill loop" "$WORK/r/test/test-setup.sh" "$WORK/r" @@ -279,12 +279,12 @@ else fi if [ -L "$vestigial" ]; then rm "$vestigial" - echo " removed vestigial symlink: $vestigial (pre-marketplace install method)" + echo " removed legacy plugin symlink: $vestigial (pre-marketplace install method)" fi''', '''for plugins_base in "$HOME/.claude/plugins" "$(pwd)/.claude/plugins"; do vestigial="$plugins_base/idstack" if [ -L "$vestigial" ]; then rm "$vestigial" - echo " removed vestigial symlink: $vestigial" + echo " removed legacy plugin symlink: $vestigial" fi done''') open(p,'w').write(s) @@ -1647,6 +1647,47 @@ PY regen expect_fail "the learning_preferences_note copies drift apart" "$WORK/r/test/smoke-test.sh" "$WORK/r" +# 44a-44c. The CLI text checks. setup, doctor and status print text that a person +# reads. Each case puts back one non-STE message that no older pin matches, so +# only the writing-standard check of the suite can catch it. +# 44a. A semicolon returns to a doctor PROBLEM explanation -> test-doctor must fail. +fresh +python3 - "$WORK/r/bin/idstack-doctor" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = 'echo " Claude Code installs plugins through a marketplace manifest."' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, 'echo " Claude Code installs plugins through a marketplace manifest;"', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a semicolon in a doctor PROBLEM explanation" "$WORK/r/test/test-doctor.sh" + +# 44b. "should" returns to the --readiness text -> test-status must fail. +# course-export reads this output. No pin matches the empty-state line, so only +# the --readiness text check sees it. +fresh +python3 - "$WORK/r/bin/idstack-status" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = 'echo "No timeline data. Run the pipeline skills first."' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, 'echo "No timeline data. You should run the pipeline skills first."', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a word-list word in the --readiness text" "$WORK/r/test/test-status.sh" + +# 44c. A contraction returns to the closing text of setup -> test-setup must fail. +fresh +python3 - "$WORK/r/setup" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = 'echo " Claude Code reads plugins only when a session starts."' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, 'echo " Claude Code doesn\'t read plugins until a session starts."', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a contraction in the setup closing text" "$WORK/r/test/test-setup.sh" "$WORK/r" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/test-doctor.sh b/test/test-doctor.sh index 1abdc16..2e15629 100755 --- a/test/test-doctor.sh +++ b/test/test-doctor.sh @@ -36,6 +36,10 @@ mkdir -p "$MOCK_IDSTACK_DIR/bin" cp -R "$REPO_DIR/bin/." "$MOCK_IDSTACK_DIR/bin/" DOCTOR_CMD="$MOCK_IDSTACK_DIR/bin/idstack-doctor" +# The writing-standard check runs bin/idstack-ste-check from the real repo +# (the mock repo has no templates/ word list), with the python3 found here. +PYTHON="$(command -v python3)" + # Pinned PATH: only what doctor and its libs actually invoke, plus whatever # mock we drop in. `claude` is absent unless a test installs it. MOCK_BIN="$TEST_ROOT/bin" @@ -166,6 +170,42 @@ mkdir -p "$HOME/.claude/skills/idstack" echo "someone else's thing" > "$HOME/.claude/skills/idstack/README.md" check "unrecognized dir warns rather than claiming ownership" "'$DOCTOR_CMD'" 0 "WARNING: directory at" +# --- writing standard (ASD-STE100) --- +# A person reads every line that doctor prints. Send a healthy run and three +# problem runs, which print all of the explanation lines, through the checker. +STE_OUT="$TEST_ROOT/ste-out" +mkdir -p "$STE_OUT" +setup_happy_path +mock_claude_output "idstack@idstack +status: enabled" +"$DOCTOR_CMD" > "$STE_OUT/healthy.txt" 2>&1 || true + +setup_happy_path +mock_claude_output "idstack@idstack +status: disabled" +rm "$MOCK_IDSTACK_DIR/.claude-plugin/plugin.json" "$MOCK_IDSTACK_DIR/.claude-plugin/marketplace.json" +rm "$MOCK_IDSTACK_DIR/skills/learn/SKILL.md" +"$DOCTOR_CMD" > "$STE_OUT/manifests.txt" 2>&1 || true + +setup_happy_path +mock_claude_output "some-other-plugin +status: enabled" +echo 'not json at all' > "$MOCK_IDSTACK_DIR/.claude-plugin/plugin.json" +mkdir -p "$HOME/.claude/skills/idstack" +echo "someone else's thing" > "$HOME/.claude/skills/idstack/README.md" +"$DOCTOR_CMD" > "$STE_OUT/not-installed.txt" 2>&1 || true + +setup_happy_path +remove_mock_claude +mkdir -p "$HOME/.claude/skills/idstack" "$HOME/.claude/plugins" +echo "2.0.0" > "$HOME/.claude/skills/idstack/VERSION" +ln -s "$TEST_ROOT/dummy-idstack/some-skill" "$HOME/.claude/skills/needs-analysis" +ln -s "$TEST_ROOT/dummy" "$HOME/.claude/plugins/idstack" +"$DOCTOR_CMD" > "$STE_OUT/legacy.txt" 2>&1 || true + +check "doctor output obeys the writing standard (healthy and problem runs)" \ + "'$PYTHON' '$REPO_DIR/bin/idstack-ste-check' --format text '$STE_OUT'/*.txt" + echo "" echo " $PASS/$TOTAL passed" [ "$FAIL" = "0" ] || exit 1 diff --git a/test/test-setup.sh b/test/test-setup.sh index 4d01eec..c463236 100755 --- a/test/test-setup.sh +++ b/test/test-setup.sh @@ -37,13 +37,18 @@ EOF return 0 } -# run_setup — runs setup with the fake HOME and stub PATH. +# run_setup — runs setup with the fake HOME and stub PATH. Each +# run's output is also added to $WORK/ste-runs.txt for the writing-standard +# check at the end of this suite. run_setup() { + local _rc=0 ( export HOME="$WORK/env/home" \ PATH="$WORK/env/bin:$PATH" \ CLAUDE_STUB_LOG="$WORK/env/claude.log" \ CLAUDE_STUB_EXIT="${STUB_EXIT:-0}" - cd "$WORK/env/idstack" && ./setup "$@" ) >"$WORK/env/out.log" 2>&1 + cd "$WORK/env/idstack" && ./setup "$@" ) >"$WORK/env/out.log" 2>&1 || _rc=$? + cat "$WORK/env/out.log" >> "$WORK/ste-runs.txt" + return "$_rc" } echo "test-setup" @@ -68,6 +73,10 @@ setup_env check "--help exits 0 and documents --keep-legacy" \ "raw_setup --help 2>/dev/null | grep -q -- '--keep-legacy'" +setup_env +check "--help text obeys the writing standard (ASD-STE100)" \ + "( set -o pipefail; raw_setup --help 2>&1 | python3 '$SRC/bin/idstack-ste-check' --format text - )" + # --- scope selection --- setup_env run_setup @@ -169,6 +178,12 @@ run_setup check "setup regenerates stale generated files" \ "'$WORK/env/idstack/bin/idstack-gen-skills' --dry-run" +# --- writing standard (ASD-STE100) --- +# Every run_setup above: a normal install, --local, --keep-legacy, legacy +# cleanup, an unknown directory and a failed 'claude' call. +check "setup output obeys the writing standard in every run of this suite" \ + "python3 '$SRC/bin/idstack-ste-check' --format text '$WORK/ste-runs.txt'" + echo "" echo "Results: $PASS/$TOTAL passed, $FAIL failed" [ "$FAIL" -eq 0 ] && exit 0 || exit 1 diff --git a/test/test-status.sh b/test/test-status.sh index 7e5fb51..be00b93 100755 --- a/test/test-status.sh +++ b/test/test-status.sh @@ -31,12 +31,24 @@ WORK=$(mktemp -d) trap 'rm -rf "$WORK"' EXIT cd "$WORK" +# A person reads the dashboard, and course-export reads --readiness. The +# outputs of each state go to $STE_OUT, and the ASD-STE100 checker reads them +# at the end of this suite. +STE_CHECK="$REPO_ROOT/bin/idstack-ste-check" +STE_OUT="$WORK/ste-out" +mkdir -p "$STE_OUT" +ste_capture() { # + "$STATUS" > "$STE_OUT/dashboard-$1.txt" 2>&1 + "$STATUS" --readiness > "$STE_OUT/readiness-$1.txt" 2>&1 +} + echo "idstack-status tests" echo "" # --- dashboard --- check "no timeline shows the empty state" \ - "$STATUS | grep -q 'No course data yet'" + "$STATUS | grep -q 'There is no course data'" +ste_capture empty mkdir -p .idstack printf '%s\n' '{"project_name": "Test Course 123"}' > .idstack/project.json @@ -59,15 +71,15 @@ check "completed skills render as namespaced checkboxes" \ check "quality trend shows the progression" \ "$STATUS | grep -q 'Quality trend: 65 -> 85'" -check "next step is suggested, namespaced" \ - "$STATUS | grep -q 'Suggested next: /idstack:learning-objectives'" +check "next step is recommended, namespaced" \ + "$STATUS | grep -q 'Recommended next: /idstack:learning-objectives'" cat > .idstack/learnings.jsonl <<'JSON' {"skill":"needs-analysis","type":"operational","insight":"Learned X"} {"skill":"needs-analysis","type":"pattern","insight":"Learned Y"} JSON check "learnings are counted" \ - "$STATUS | grep -q 'Learnings: 2 discoveries stored'" + "$STATUS | grep -q 'Learnings: 2 entries'" mkdir -p .idstack/exports/test-course-123 touch .idstack/exports/test-course-123/index.html @@ -78,6 +90,7 @@ check "dashboard index.html is listed" \ "$STATUS | grep -q 'test-course-123/index.html'" check "per-skill report is listed" \ "$STATUS | grep -q 'test-course-123/other-report.html'" +ste_capture partial echo "" @@ -111,6 +124,7 @@ JSON write_manifest 80 0 90 check "all thresholds met gives READY TO EXPORT" \ "$STATUS --readiness | grep -q 'READY TO EXPORT'" +ste_capture ready # --- threshold boundaries, one at a time --- # Each case holds the other two dimensions passing, so the verdict can only @@ -159,6 +173,17 @@ write_manifest 80 0 95 '"wcag_violations": [{"level": "AA", "criterion": "1.4.3" check "a Level-AA violation alone does not block export" \ "$STATUS --readiness | grep -q 'READY TO EXPORT'" +# --- writing standard (ASD-STE100) --- +# NOT-READY with all four reasons, so the reason list is in the text too. +write_manifest 60 2 70 '"wcag_violations": [{"level": "A", "criterion": "1.1.1"}, {"level": "A", "criterion": "2.1.1"}]' +ste_capture not-ready +printf 'not json\n' > .idstack/timeline.jsonl +ste_capture unreadable-timeline +check "the dashboard text obeys the writing standard in each state" \ + "python3 '$STE_CHECK' --format text '$STE_OUT'/dashboard-*.txt" +check "the --readiness text obeys the writing standard in each state" \ + "python3 '$STE_CHECK' --format text '$STE_OUT'/readiness-*.txt" + echo "" echo "idstack-status: $PASS/$TOTAL passed, $FAIL failed" [ "$FAIL" -eq 0 ] && exit 0 || exit 1 From b53cc1b10b7f5575dd0e75edd658f477460839fb Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 13:56:55 -0700 Subject: [PATCH 05/10] feat(cli): rewrite consensus and helper script messages in ASD-STE100 Co-Authored-By: Claude Haiku 4.5 --- bin/idstack-consensus | 114 ++++++++++++++------------- bin/idstack-gen-skills | 37 ++++----- bin/idstack-learnings-delete | 10 +-- bin/idstack-learnings-promote | 10 +-- bin/idstack-manifest-merge | 30 +++---- bin/idstack-migrate | 30 +++---- bin/idstack-slugify | 4 +- bin/package-extension.sh | 12 +-- extension/shared/consensus-client.js | 50 ++++++------ test/mutation-test.sh | 43 ++++++++++ test/test-consensus-cli.py | 103 +++++++++++++++++++++++- test/test-consensus-client.mjs | 4 +- 12 files changed, 298 insertions(+), 149 deletions(-) diff --git a/bin/idstack-consensus b/bin/idstack-consensus index 92b748b..0848c68 100755 --- a/bin/idstack-consensus +++ b/bin/idstack-consensus @@ -123,16 +123,16 @@ def cmd_configure(args): if not key: if sys.stdin.isatty(): try: - key = input("Enter Consensus API key: ").strip() + key = input("Consensus API key: ").strip() except (EOFError, KeyboardInterrupt): - print("\nAborted.", file=sys.stderr) + print("\nidstack did not save a key.", file=sys.stderr) return 1 else: line = sys.stdin.readline() if line: key = line.strip() if not key: - print("Error: No API key provided.", file=sys.stderr) + print("Error: You did not give an API key.", file=sys.stderr) return 1 profile_path = os.path.expanduser("~/.idstack/profile.yaml") @@ -163,7 +163,7 @@ def cmd_configure(args): with open(profile_path, "w", encoding="utf-8") as f: f.writelines(new_lines) - print("Consensus API key saved to " + profile_path) + print("idstack saved the Consensus API key to " + profile_path) return 0 @@ -188,7 +188,7 @@ def cmd_query(args): "api_key_configured": False, "consensus_meter": None, "top_papers": [], - "note": "No Consensus API key configured; use local references or set CONSENSUS_API_KEY.", + "note": "idstack found no Consensus API key. Use the local references, or set CONSENSUS_API_KEY.", } print(json.dumps(fallback, indent=2)) return 0 @@ -366,67 +366,69 @@ def auto_correct_neuromyth(finding, myth_type): if myth_type == "learning_styles": finding["recommendation"] = ( - "Provide multimodal presentation and dual coding (combining complementary visual representations " - "and verbal/textual explanations for all learners) rather than attempting to segregate " - "or adapt materials to putative learning styles." + "idstack recommends that you use multimodal presentation and dual coding for all learners, " + "not different materials for each learning style. Dual coding shows related visuals and " + "verbal or text explanations together." ) corr_ev = ( - "[Auto-Corrected Neuromyth] The 'learning styles' (meshing hypothesis) lacks empirical support " - "(Pashler et al., 2008; Rogowsky et al., 2015). Empirical evidence supports multimodal " - "presentation and dual coding (Mayer, 2021; Paivio, 1986) for all learners." + "[Auto-Corrected Neuromyth] Research evidence does not show that 'learning styles' (the meshing " + "hypothesis) help learners (Pashler et al., 2008; Rogowsky et al., 2015). Research evidence shows " + "that multimodal presentation and dual coding help all learners (Mayer, 2021; Paivio, 1986)." ) if finding.get("evidence"): finding["evidence"] = finding["evidence"] + " " + corr_ev else: finding["evidence"] = corr_ev - obs_note = " [Note: Differentiating by sensory learning styles is a recognized neuromyth; multimodal presentation should be applied instead.]" + obs_note = " [Note: Sensory learning styles are a known neuromyth. idstack recommends that you use multimodal presentation for all learners.]" finding["observation"] = (finding.get("observation", "") + obs_note).strip() elif myth_type == "hemisphere_learning": finding["recommendation"] = ( - "Provide multimodal presentation and integrated cognitive activities rather than " - "attempting to cater to left-brain or right-brain learners." + "idstack recommends that you use multimodal presentation and integrated cognitive tasks for all " + "learners, not different tasks for left-brain or right-brain learners." ) corr_ev = ( - "[Auto-Corrected Neuromyth] Hemispheric specialization for learning is an empirically " - "unsupported neuromyth. Cognitive architecture operates through integrated networks; " - "multimodal dual coding benefits all learners." + "[Auto-Corrected Neuromyth] Hemispheric specialization for learning is a neuromyth with no " + "research evidence. Cognitive architecture operates through integrated networks. " + "Multimodal dual coding helps all learners." ) if finding.get("evidence"): finding["evidence"] = finding["evidence"] + " " + corr_ev else: finding["evidence"] = corr_ev - obs_note = " [Note: Left/right brain learning categorization is a neuromyth; integrated multimodal tasks should be used.]" + obs_note = " [Note: Left-brain and right-brain learner types are a neuromyth. idstack recommends that you use integrated multimodal tasks.]" finding["observation"] = (finding.get("observation", "") + obs_note).strip() elif myth_type == "ten_percent_brain": finding["recommendation"] = ( - "Design instructional activities that optimize working memory capacity and cognitive load " - "rather than relying on 10% brain capacity claims." + "idstack recommends that you design learning tasks for working memory capacity and cognitive " + "load, not for claims about 10% brain capacity." ) corr_ev = ( - "[Auto-Corrected Neuromyth] The 10% brain usage claim is a debunked neuromyth. Instructional " - "design should be guided by Cognitive Load Theory (Sweller, 1994) and working memory limits." + "[Auto-Corrected Neuromyth] The 10% brain claim is a neuromyth. Research shows that the claim " + "is incorrect. Instructional design must agree with Cognitive Load Theory (Sweller, 1994) and " + "working memory limits." ) if finding.get("evidence"): finding["evidence"] = finding["evidence"] + " " + corr_ev else: finding["evidence"] = corr_ev - obs_note = " [Note: The 10% brain claim is a recognized neuromyth; Cognitive Load Theory should guide design.]" + obs_note = " [Note: The 10% brain claim is a known neuromyth. idstack recommends that you use Cognitive Load Theory for the design.]" finding["observation"] = (finding.get("observation", "") + obs_note).strip() elif myth_type == "dales_cone_percentages": finding["recommendation"] = ( - "Structure learning through active retrieval and generative processing rather than " - "bogus retention percentage hierarchies." + "idstack recommends that you design learning tasks with active retrieval and generative " + "processing, not with incorrect retention percentage hierarchies." ) corr_ev = ( - "[Auto-Corrected Neuromyth] Numerical retention claims ('we remember 10% of what we read') " - "are fabricated percentages falsely attributed to Dale's Cone of Experience. Retention " - "depends on retrieval practice and cognitive processing depth (Agarwal, 2019)." + "[Auto-Corrected Neuromyth] Retention percentage claims ('we remember 10% of what we read') " + "use numbers that have no research source. Some texts incorrectly connect these numbers to " + "Dale's Cone of Experience. Retention changes with retrieval practice and the depth of " + "cognitive processing (Agarwal, 2019)." ) if finding.get("evidence"): finding["evidence"] = finding["evidence"] + " " + corr_ev else: finding["evidence"] = corr_ev - obs_note = " [Note: Retention percentage pyramids are fabricated; active retrieval principles should be used.]" + obs_note = " [Note: Retention percentage pyramids are incorrect. idstack recommends that you use active retrieval.]" finding["observation"] = (finding.get("observation", "") + obs_note).strip() @@ -455,7 +457,7 @@ def calibrate_tier(finding, study_design=None): finding["tier"] = "T2" finding["tier_calibrated"] = True finding["original_tier"] = "T1" - cal_note = " [Tier calibrated: downgraded from T1 to T2 because study design is controlled trial, not meta-analysis/RCT]" + cal_note = " [Tier calibrated: idstack lowered the tier from T1 to T2 because the study design is a controlled trial, not a meta-analysis or an RCT.]" finding["evidence"] = (finding.get("evidence", "") + cal_note).strip() return @@ -464,7 +466,7 @@ def calibrate_tier(finding, study_design=None): finding["tier"] = "T3" finding["tier_calibrated"] = True finding["original_tier"] = tier - cal_note = " [Tier calibrated: downgraded from " + tier + " to T3 because study design is systematic review without meta-analysis]" + cal_note = " [Tier calibrated: idstack lowered the tier from " + tier + " to T3 because the study design is a systematic review without a meta-analysis.]" finding["evidence"] = (finding.get("evidence", "") + cal_note).strip() return @@ -473,7 +475,7 @@ def calibrate_tier(finding, study_design=None): finding["tier"] = "T4" finding["tier_calibrated"] = True finding["original_tier"] = tier - cal_note = " [Tier calibrated: downgraded from " + tier + " to T4 because study design is observational/qualitative without control group]" + cal_note = " [Tier calibrated: idstack lowered the tier from " + tier + " to T4 because the study design is observational or qualitative, with no control group.]" finding["evidence"] = (finding.get("evidence", "") + cal_note).strip() return @@ -481,21 +483,21 @@ def calibrate_tier(finding, study_design=None): finding["tier"] = "T4" finding["tier_calibrated"] = True finding["original_tier"] = tier - cal_note = " [Tier calibrated: downgraded from " + tier + " to T4: lacks meta-analysis or controlled trial evidence]" + cal_note = " [Tier calibrated: idstack lowered the tier from " + tier + " to T4 because the evidence has no meta-analysis or controlled trial.]" finding["evidence"] = (finding.get("evidence", "") + cal_note).strip() def cmd_verify(args): """Run evidence QA gate and auto-correction on findings payload.""" if not os.path.isfile(args.findings): - print("Error: Input findings file not found: " + args.findings, file=sys.stderr) + print("Error: idstack cannot find the findings file: " + args.findings, file=sys.stderr) return 1 try: with open(args.findings, "r", encoding="utf-8") as f: data = json.load(f) except Exception as e: - print("Error reading findings file: " + str(e), file=sys.stderr) + print("Error: idstack cannot read the findings file: " + str(e), file=sys.stderr) return 1 if isinstance(data, dict): @@ -505,7 +507,7 @@ def cmd_verify(args): findings = data is_dict = False else: - print("Error: Expected JSON object or array in findings file", file=sys.stderr) + print("Error: The findings file must contain a JSON object or a JSON array.", file=sys.stderr) return 1 if not isinstance(findings, list): @@ -615,7 +617,7 @@ def cmd_verify(args): json.dump(out_payload, f, indent=2) print( - "Evidence QA: %d findings verified (%d cached, %d auto-corrected)" + "Evidence QA: idstack found evidence for %d findings (%d cached, %d auto-corrected)" % (total_verified, cached_count, auto_corrected_count) ) return 0 @@ -936,7 +938,7 @@ def cmd_sync(args): if not target_domain: valid_names = [d["short_name"] for d in RESEARCH_DOMAINS] print( - "Error: Unknown domain '%s'. Known domains are: %s" + "Error: Unknown domain '%s'. The known domains are: %s" % (domain_filter, ", ".join(valid_names)), file=sys.stderr, ) @@ -954,7 +956,7 @@ def cmd_sync(args): d = domains_to_sync[0] print("Target Domain: Domain %d: %s (%s)" % (d["id"], d["name"], d["short_name"])) else: - print("Scanning %d research domains for recent meta-analyses (2024–2026)..." % len(domains_to_sync)) + print("idstack examines %d research domains for recent meta-analyses (2024–2026)." % len(domains_to_sync)) print("") for domain in domains_to_sync: @@ -990,15 +992,15 @@ def cmd_sync(args): compat_ok, compat_msg = check_evidence_cards_compatibility() print("") if compat_ok: - print("Compatibility: test/check-evidence-cards.py verified (0 mismatches).") + print("Compatibility: test/check-evidence-cards.py found 0 mismatches.") else: - print("Compatibility warning: test/check-evidence-cards.py detected issues:") + print("Compatibility warning: test/check-evidence-cards.py found problems:") print(compat_msg) if dry_run: - print("Dry run complete. No changes made to evidence/references.md.") + print("Dry run: idstack did not change evidence/references.md.") else: - print("Notice: Non-dry-run flag acknowledged. Automated references.md update skipped to maintain card count verification.") + print("Notice: idstack does not change references.md automatically. An automatic change can make the evidence cards not agree with references.md. Add the candidate citations manually.") return 0 if compat_ok else 1 @@ -1007,27 +1009,27 @@ def main(): parser = argparse.ArgumentParser(description="idstack Consensus API client & cache") subparsers = parser.add_subparsers(dest="command") - status_parser = subparsers.add_parser("status", help="Report cache and configuration status") + status_parser = subparsers.add_parser("status", help="Show the cache and configuration status") status_parser.set_defaults(func=cmd_status) - configure_parser = subparsers.add_parser("configure", help="Configure Consensus API key in profile.yaml") - configure_parser.add_argument("--key", help="Consensus API key to save") + configure_parser = subparsers.add_parser("configure", help="Save a Consensus API key in profile.yaml") + configure_parser.add_argument("--key", help="The Consensus API key to save") configure_parser.set_defaults(func=cmd_configure) - query_parser = subparsers.add_parser("query", help="Query evidence for a pedagogical claim") - query_parser.add_argument("--claim", required=True, help="Pedagogical claim to query") + query_parser = subparsers.add_parser("query", help="Find evidence for an instructional design claim") + query_parser.add_argument("--claim", required=True, help="The claim to find evidence for") query_parser.set_defaults(func=cmd_query) - verify_parser = subparsers.add_parser("verify", help="Pre-response evidence QA gate & auto-correction") - verify_parser.add_argument("--findings", required=True, help="Path to findings JSON file") - verify_parser.add_argument("--output", required=True, help="Path to output verified findings JSON file") + verify_parser = subparsers.add_parser("verify", help="Do the evidence QA check and correct known problems in findings") + verify_parser.add_argument("--findings", required=True, help="The path to the findings JSON file") + verify_parser.add_argument("--output", required=True, help="The path for the output JSON file") verify_parser.set_defaults(func=cmd_verify) - sync_parser = subparsers.add_parser("sync", help="On-demand literature sync across research domains") - sync_parser.add_argument("--domain", help="Target specific research domain (e.g. Assessment, Models)") - sync_parser.add_argument("--dry-run", dest="dry_run", action="store_true", default=True, help="Simulate sync without modifying references.md (default)") - sync_parser.add_argument("--no-dry-run", dest="dry_run", action="store_false", help="Apply candidate citations to references.md") - sync_parser.add_argument("--apply", dest="dry_run", action="store_false", help="Apply candidate citations to references.md") + sync_parser = subparsers.add_parser("sync", help="Find candidate citations for the research domains") + sync_parser.add_argument("--domain", help="Use only this research domain (for example, Assessment or Models)") + sync_parser.add_argument("--dry-run", dest="dry_run", action="store_true", default=True, help="Show the citations, but do not change references.md (default)") + sync_parser.add_argument("--no-dry-run", dest="dry_run", action="store_false", help="Show the citations. idstack does not change references.md. Add the citations manually.") + sync_parser.add_argument("--apply", dest="dry_run", action="store_false", help="Show the citations. idstack does not change references.md. Add the citations manually.") sync_parser.set_defaults(func=cmd_sync) args = parser.parse_args() diff --git a/bin/idstack-gen-skills b/bin/idstack-gen-skills index 7d61ea2..9cf16dd 100755 --- a/bin/idstack-gen-skills +++ b/bin/idstack-gen-skills @@ -1,16 +1,17 @@ #!/usr/bin/env bash -# Generate the Claude Code SKILL.md files from their .tmpl templates. +# Make the Claude Code SKILL.md files from their .tmpl templates. # # Usage: # idstack-gen-skills [--dry-run] # -# Writes skills//SKILL.md — the layout Claude Code's plugin loader reads. +# The script writes skills//SKILL.md. The Claude Code plugin loader reads +# this layout. # -# Source of truth is skills//SKILL.md.tmpl with {{PREAMBLE}}, -# {{MANIFEST_SCHEMA}}, and {{IDSTACK_RESOLVE}} placeholders. {{IDSTACK_RESOLVE}} -# splices the canonical $_IDSTACK resolution (templates/snippets/idstack-resolve.sh) -# into each bash block that needs it — blocks run in separate shells and must be -# self-contained. +# The source file is skills//SKILL.md.tmpl. It contains the {{PREAMBLE}}, +# {{MANIFEST_SCHEMA}} and {{IDSTACK_RESOLVE}} placeholders. {{IDSTACK_RESOLVE}} +# puts the canonical $_IDSTACK resolution (templates/snippets/idstack-resolve.sh) +# into each bash block that uses it. Each block runs in a different shell. +# Thus each block must contain all of its code. set -e @@ -30,22 +31,22 @@ while [ $# -gt 0 ]; do # Pattern-based range — robust to header edits. sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; - *) echo "Unknown argument: $1" >&2; exit 2 ;; + *) echo "ERROR: unknown argument: $1" >&2; exit 2 ;; esac done if [ ! -f "$PREAMBLE" ]; then - echo "ERROR: templates/preamble.md not found at $PREAMBLE" >&2 + echo "ERROR: templates/preamble.md is missing ($PREAMBLE)" >&2 exit 1 fi if [ ! -f "$MANIFEST_SCHEMA" ]; then - echo "ERROR: templates/manifest-schema.md not found at $MANIFEST_SCHEMA" >&2 + echo "ERROR: templates/manifest-schema.md is missing ($MANIFEST_SCHEMA)" >&2 exit 1 fi if [ ! -f "$RESOLVE_SNIPPET" ]; then - echo "ERROR: templates/snippets/idstack-resolve.sh not found at $RESOLVE_SNIPPET" >&2 + echo "ERROR: templates/snippets/idstack-resolve.sh is missing ($RESOLVE_SNIPPET)" >&2 exit 1 fi @@ -61,7 +62,7 @@ REPORT_FORMAT="$IDSTACK_DIR/templates/report-format.md" for f in "$REPORT_HTML_TMPL" "$INDEX_HTML_TMPL" "$REPORT_CSS" "$REPORT_FORMAT"; do if [ ! -f "$f" ]; then - echo "ERROR: report asset not found at ${f#$IDSTACK_DIR/}" >&2 + echo "ERROR: the report asset ${f#$IDSTACK_DIR/} is missing" >&2 exit 1 fi done @@ -129,13 +130,13 @@ render_template() { local current current=$(cat "$output") if [ "$result" != "$current" ]; then - echo " STALE: ${output#$IDSTACK_DIR/} (would be updated)" + echo " STALE: ${output#$IDSTACK_DIR/} (a run without --dry-run updates it)" ERRORS=$((ERRORS + 1)) else echo " OK: ${output#$IDSTACK_DIR/} (up to date)" fi else - echo " MISSING: ${output#$IDSTACK_DIR/} (would be created)" + echo " MISSING: ${output#$IDSTACK_DIR/} (a run without --dry-run makes it)" ERRORS=$((ERRORS + 1)) fi else @@ -154,19 +155,19 @@ for tmpl in "$IDSTACK_DIR"/skills/*/SKILL.md.tmpl; do done if [ "$GENERATED" -eq 0 ] && [ "$ERRORS" -eq 0 ]; then - echo "No .tmpl files found." + echo "There are no .tmpl files." exit 1 fi if [ "$DRY_RUN" -eq 1 ]; then echo "" - echo "$GENERATED templates checked. $ERRORS stale or failed." + echo "The script examined $GENERATED templates. $ERRORS have problems." if [ "$ERRORS" -gt 0 ]; then exit 1; fi else echo "" - echo "$GENERATED skill files generated." + echo "The script wrote $GENERATED skill files." if [ "$ERRORS" -gt 0 ]; then - echo "ERROR: $ERRORS template(s) failed to render." >&2 + echo "ERROR: $ERRORS template(s) have errors. The script did not write their SKILL.md files." >&2 exit 1 fi fi diff --git a/bin/idstack-learnings-delete b/bin/idstack-learnings-delete index b9f15a1..032f88f 100755 --- a/bin/idstack-learnings-delete +++ b/bin/idstack-learnings-delete @@ -9,10 +9,10 @@ KEY="${1:-}" [ -z "$KEY" ] && { echo "Usage: idstack-learnings-delete " >&2; exit 1; } LEARNINGS=".idstack/learnings.jsonl" -[ -f "$LEARNINGS" ] || { echo "No learnings file found." >&2; exit 1; } +[ -f "$LEARNINGS" ] || { echo "There is no learnings file." >&2; exit 1; } if ! command -v python3 &>/dev/null; then - echo "python3 required for delete." >&2 + echo "python3 is necessary for this command." >&2 exit 1 fi @@ -34,7 +34,7 @@ for i, line in enumerate(lines): pass if found_idx == -1: - print(f'No learning found with key: {key}', file=sys.stderr) + print(f'idstack found no learning with the key: {key}', file=sys.stderr) sys.exit(1) # Remove that line @@ -52,8 +52,8 @@ except Exception as exc: os.unlink(tmp_path) except OSError: pass - print(f'Failed to write learnings file {learnings_file}: {exc}', file=sys.stderr) + print(f'idstack cannot write the learnings file {learnings_file}: {exc}', file=sys.stderr) sys.exit(1) -print(f'Deleted learning: {key}') +print(f'idstack removed the learning: {key}') " "$KEY" diff --git a/bin/idstack-learnings-promote b/bin/idstack-learnings-promote index 25d8b58..0995ce6 100755 --- a/bin/idstack-learnings-promote +++ b/bin/idstack-learnings-promote @@ -2,7 +2,7 @@ # Promote a local learning to the global store. # Usage: idstack-learnings-promote # Copies the matching learning from .idstack/learnings.jsonl to ~/.idstack/global/learnings.jsonl -# Tags it with the source project. Exit 0 always. +# Tags it with the source project. Exits 0 on success, 1 on error. set -e @@ -13,10 +13,10 @@ LOCAL_LEARNINGS=".idstack/learnings.jsonl" GLOBAL_DIR="$HOME/.idstack/global" GLOBAL_LEARNINGS="$GLOBAL_DIR/learnings.jsonl" -[ -f "$LOCAL_LEARNINGS" ] || { echo "No local learnings found." >&2; exit 1; } +[ -f "$LOCAL_LEARNINGS" ] || { echo "There are no local learnings." >&2; exit 1; } if ! command -v python3 &>/dev/null; then - echo "python3 required for promote." >&2 + echo "python3 is necessary for this command." >&2 exit 1 fi @@ -39,7 +39,7 @@ for line in open(local_file): pass if not match: - print(f'No learning found with key: {key}', file=sys.stderr) + print(f'idstack found no learning with the key: {key}', file=sys.stderr) sys.exit(1) # Tag with source project @@ -58,5 +58,5 @@ os.makedirs(global_dir, exist_ok=True) with open(global_file, 'a') as f: f.write(json.dumps(match) + '\n') -print(f'Promoted \"{key}\" to global learnings (source: {project_name})') +print(f'idstack added \"{key}\" to the global learnings (source: {project_name})') " "$KEY" diff --git a/bin/idstack-manifest-merge b/bin/idstack-manifest-merge index 8470c97..32117af 100755 --- a/bin/idstack-manifest-merge +++ b/bin/idstack-manifest-merge @@ -60,21 +60,21 @@ ALLOWED_SECTIONS = { def parse_args(): parser = argparse.ArgumentParser( - description="Atomically replace one section of an idstack manifest.", + description="Replace one section of an idstack manifest with an atomic write.", formatter_class=argparse.RawDescriptionHelpFormatter, ) - parser.add_argument("--section", required=True, help="Section name to replace.") + parser.add_argument("--section", required=True, help="The name of the section to replace.") parser.add_argument( "--payload", required=True, - help='Path to JSON payload file, or "-" for stdin.', + help='The path to the JSON payload file. Use "-" for stdin.', ) parser.add_argument( "--manifest", default=".idstack/project.json", - help="Path to manifest. Default: .idstack/project.json", + help="The path to the manifest. Default: .idstack/project.json", ) - parser.add_argument("--quiet", action="store_true", help="Suppress progress output.") + parser.add_argument("--quiet", action="store_true", help="Show only error messages.") return parser.parse_args() @@ -88,7 +88,7 @@ def load_payload(payload_arg): try: text = sys.stdin.read() except Exception as exc: - die(6, f"failed to read payload from stdin: {exc}") + die(6, f"cannot read the payload from stdin: {exc}") else: if not os.path.isfile(payload_arg): die(5, f"payload file not found: {payload_arg}") @@ -96,13 +96,13 @@ def load_payload(payload_arg): with open(payload_arg, "r", encoding="utf-8") as handle: text = handle.read() except Exception as exc: - die(6, f"failed to read payload file {payload_arg!r}: {exc}") + die(6, f"cannot read the payload file {payload_arg!r}: {exc}") try: data = json.loads(text) except json.JSONDecodeError as exc: - die(1, f"payload is not valid JSON: {exc}") + die(1, f"the payload has a JSON error: {exc}") if not isinstance(data, dict): - die(1, f"payload root must be a JSON object: got {type(data).__name__}") + die(1, f"the payload root must be a JSON object, not {type(data).__name__}") return data @@ -113,13 +113,13 @@ def load_manifest(manifest_path): with open(manifest_path, "r", encoding="utf-8") as handle: text = handle.read() except Exception as exc: - die(6, f"failed to read manifest {manifest_path!r}: {exc}") + die(6, f"cannot read the manifest {manifest_path!r}: {exc}") try: data = json.loads(text) except json.JSONDecodeError as exc: - die(2, f"manifest is not valid JSON ({manifest_path}): {exc}") + die(2, f"the manifest has a JSON error ({manifest_path}): {exc}") if not isinstance(data, dict): - die(2, f"manifest root must be a JSON object ({manifest_path}): got {type(data).__name__}") + die(2, f"the manifest root must be a JSON object, not {type(data).__name__} ({manifest_path})") return data @@ -138,7 +138,7 @@ def write_atomic(manifest_path, data): os.unlink(tmp_path) except OSError: pass - die(6, f"failed to write manifest {manifest_path!r}: {exc}") + die(6, f"cannot write the manifest {manifest_path!r}: {exc}") def main(): @@ -146,7 +146,7 @@ def main(): if args.section not in ALLOWED_SECTIONS: allowed = ", ".join(sorted(ALLOWED_SECTIONS)) - die(3, f"unknown section {args.section!r}. Allowed: {allowed}") + die(3, f"unknown section {args.section!r}. The known sections are: {allowed}") payload = load_payload(args.payload) manifest = load_manifest(args.manifest) @@ -157,7 +157,7 @@ def main(): write_atomic(args.manifest, manifest) if not args.quiet: - print(f"merged section {args.section!r} into {args.manifest}") + print(f"idstack replaced the section {args.section!r} in {args.manifest}") if __name__ == "__main__": diff --git a/bin/idstack-migrate b/bin/idstack-migrate index 5df7fb0..2c46b89 100755 --- a/bin/idstack-migrate +++ b/bin/idstack-migrate @@ -1,23 +1,23 @@ #!/usr/bin/env bash # Migrate .idstack/project.json to the latest schema version. -# Reads the manifest, applies any needed migrations, writes it back, and -# outputs the (possibly updated) JSON to stdout. -# Prints "MIGRATED " to stderr when a migration is applied. -# If the file does not exist, prints nothing and exits 0. -# If python3 is unavailable, falls back to cat (graceful degradation). +# The script reads the manifest, applies the necessary migrations and writes +# the manifest again. It also writes the JSON to stdout, changed or not. +# When it applies a migration, it writes "MIGRATED " to stderr. +# If there is no file, the script writes no output and exits 0. +# If python3 is not available, the script writes the file to stdout with cat. # # Usage: # idstack-migrate [--init] [path] # -# --init create a canonical manifest with every section at its default -# when none exists (still migrates in place when one does). Skills -# running standalone use this so a first run has something for -# bin/idstack-manifest-merge to merge into — that tool replaces one -# section and errors (exit 4) when the manifest is absent. +# --init If there is no manifest, make a canonical manifest with each +# section at its default value. If there is a manifest, migrate it. +# Skills that run without the pipeline use this flag. Then the first +# run has a manifest for bin/idstack-manifest-merge. That tool +# replaces one section, and it exits 4 when there is no manifest. # -# The --init skeleton is produced by seeding a minimal v1.0 document and -# running the normal migration chain over it, so there is exactly one -# definition of "every section at its default" — the migrations themselves. +# To make the --init manifest, the script writes a small v1.0 document and +# applies the usual migrations to it. Thus the migrations are the only +# definition of "each section at its default value". set -e @@ -31,7 +31,7 @@ for arg in "$@"; do -h|--help) sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; - -*) echo "idstack-migrate: unknown option: $arg" >&2; exit 2 ;; + -*) echo "idstack-migrate: unknown flag: $arg" >&2; exit 2 ;; *) [ -z "$MANIFEST" ] && MANIFEST="$arg" ;; esac done @@ -41,7 +41,7 @@ MANIFEST="${MANIFEST:-.idstack/project.json}" # section, so this only has to carry the fields migrations do not default. if [ ! -f "$MANIFEST" ] && [ "$INIT" = "1" ]; then if ! command -v python3 &>/dev/null; then - echo "idstack-migrate: --init requires python3" >&2 + echo "idstack-migrate: python3 is necessary for --init" >&2 exit 3 fi mkdir -p "$(dirname "$MANIFEST")" diff --git a/bin/idstack-slugify b/bin/idstack-slugify index e07c0de..7b130a2 100755 --- a/bin/idstack-slugify +++ b/bin/idstack-slugify @@ -27,7 +27,7 @@ set -e if ! command -v python3 >/dev/null 2>&1; then - echo "idstack-slugify: python3 is required" >&2 + echo "idstack-slugify: python3 is necessary" >&2 exit 2 fi @@ -36,7 +36,7 @@ if [ $# -gt 0 ] && [ "$1" != "-" ]; then INPUT="$1" else if [ -t 0 ]; then - echo "idstack-slugify: no input given (pass an argument or pipe via stdin)" >&2 + echo "idstack-slugify: there is no input. Give an argument, or send the text to stdin." >&2 exit 3 fi INPUT=$(cat) diff --git a/bin/package-extension.sh b/bin/package-extension.sh index 408ebf2..0fdf39e 100755 --- a/bin/package-extension.sh +++ b/bin/package-extension.sh @@ -11,24 +11,24 @@ BUILD_DIR="$REPO_ROOT/build" ZIP_NAME="idstack-chrome-extension-v$(node -p 'require(process.argv[1]).version' "$EXT_DIR/manifest.json").zip" ZIP_PATH="$BUILD_DIR/$ZIP_NAME" -echo "==> Verifying extension test suite..." +echo "==> The script runs the extension tests." "$REPO_ROOT/test/test-extension.sh" -echo "==> Creating build directory: $BUILD_DIR" +echo "==> The script makes the directory $BUILD_DIR." mkdir -p "$BUILD_DIR" rm -f "$ZIP_PATH" -echo "==> Packaging extension from $EXT_DIR..." +echo "==> The script puts the extension from $EXT_DIR into a zip file." # Zip contents directly so manifest.json is at root of archive # icons/generate-icons.js is a dev-time script that drew the PNGs; it is not part of the extension. (cd "$EXT_DIR" && zip -r "$ZIP_PATH" . -x "*.DS_Store" "*__MACOSX*" "*.git*" "icons/generate-icons.js") echo "" -echo "✅ Extension packaged successfully!" +echo "✅ The script made the extension zip file." echo "📦 Archive path: $ZIP_PATH" echo "📏 Archive size: $(du -h "$ZIP_PATH" | cut -f1)" echo "" -echo "To publish to the Chrome Web Store:" +echo "To publish the extension in the Chrome Web Store, do these steps:" echo "1. Go to https://chrome.google.com/webstore/devconsole" echo "2. Click 'New Item'" -echo "3. Upload: $ZIP_PATH" +echo "3. Upload $ZIP_PATH." diff --git a/extension/shared/consensus-client.js b/extension/shared/consensus-client.js index b68536f..c002c9e 100644 --- a/extension/shared/consensus-client.js +++ b/extension/shared/consensus-client.js @@ -72,47 +72,49 @@ export function autoCorrectNeuromyth(finding, mythType) { if (mythType === 'learning_styles') { finding.recommendation = - 'Provide multimodal presentation and dual coding (combining complementary visual representations ' + - 'and verbal/textual explanations for all learners) rather than attempting to segregate ' + - 'or adapt materials to putative learning styles.'; + 'idstack recommends that you use multimodal presentation and dual coding for all learners, ' + + 'not different materials for each learning style. Dual coding shows related visuals and ' + + 'verbal or text explanations together.'; const corrEv = - "[Auto-Corrected Neuromyth] The 'learning styles' (meshing hypothesis) lacks empirical support " + - '(Pashler et al., 2008; Rogowsky et al., 2015). Empirical evidence supports multimodal ' + - 'presentation and dual coding (Mayer, 2021; Paivio, 1986) for all learners.'; + "[Auto-Corrected Neuromyth] Research evidence does not show that 'learning styles' (the meshing " + + 'hypothesis) help learners (Pashler et al., 2008; Rogowsky et al., 2015). Research evidence shows ' + + 'that multimodal presentation and dual coding help all learners (Mayer, 2021; Paivio, 1986).'; finding.evidence = finding.evidence ? `${finding.evidence} ${corrEv}` : corrEv; - const obsNote = ' [Note: Differentiating by sensory learning styles is a recognized neuromyth; multimodal presentation should be applied instead.]'; + const obsNote = ' [Note: Sensory learning styles are a known neuromyth. idstack recommends that you use multimodal presentation for all learners.]'; finding.observation = `${finding.observation || ''}${obsNote}`.trim(); } else if (mythType === 'hemisphere_learning') { finding.recommendation = - 'Provide multimodal presentation and integrated cognitive activities rather than ' + - 'attempting to cater to left-brain or right-brain learners.'; + 'idstack recommends that you use multimodal presentation and integrated cognitive tasks for all ' + + 'learners, not different tasks for left-brain or right-brain learners.'; const corrEv = - '[Auto-Corrected Neuromyth] Hemispheric specialization for learning is an empirically ' + - 'unsupported neuromyth. Cognitive architecture operates through integrated networks; ' + - 'multimodal dual coding benefits all learners.'; + '[Auto-Corrected Neuromyth] Hemispheric specialization for learning is a neuromyth with no ' + + 'research evidence. Cognitive architecture operates through integrated networks. ' + + 'Multimodal dual coding helps all learners.'; finding.evidence = finding.evidence ? `${finding.evidence} ${corrEv}` : corrEv; - const obsNote = ' [Note: Left/right brain learning categorization is a neuromyth; integrated multimodal tasks should be used.]'; + const obsNote = ' [Note: Left-brain and right-brain learner types are a neuromyth. idstack recommends that you use integrated multimodal tasks.]'; finding.observation = `${finding.observation || ''}${obsNote}`.trim(); } else if (mythType === 'ten_percent_brain') { finding.recommendation = - 'Design instructional activities that optimize working memory capacity and cognitive load ' + - 'rather than relying on 10% brain capacity claims.'; + 'idstack recommends that you design learning tasks for working memory capacity and cognitive ' + + 'load, not for claims about 10% brain capacity.'; const corrEv = - '[Auto-Corrected Neuromyth] The 10% brain usage claim is a debunked neuromyth. Instructional ' + - 'design should be guided by Cognitive Load Theory (Sweller, 1994) and working memory limits.'; + '[Auto-Corrected Neuromyth] The 10% brain claim is a neuromyth. Research shows that the claim ' + + 'is incorrect. Instructional design must agree with Cognitive Load Theory (Sweller, 1994) and ' + + 'working memory limits.'; finding.evidence = finding.evidence ? `${finding.evidence} ${corrEv}` : corrEv; - const obsNote = ' [Note: The 10% brain claim is a recognized neuromyth; Cognitive Load Theory should guide design.]'; + const obsNote = ' [Note: The 10% brain claim is a known neuromyth. idstack recommends that you use Cognitive Load Theory for the design.]'; finding.observation = `${finding.observation || ''}${obsNote}`.trim(); } else if (mythType === 'dales_cone_percentages') { finding.recommendation = - 'Structure learning through active retrieval and generative processing rather than ' + - 'bogus retention percentage hierarchies.'; + 'idstack recommends that you design learning tasks with active retrieval and generative ' + + 'processing, not with incorrect retention percentage hierarchies.'; const corrEv = - "[Auto-Corrected Neuromyth] Numerical retention claims ('we remember 10% of what we read') " + - "are fabricated percentages falsely attributed to Dale's Cone of Experience. Retention " + - 'depends on retrieval practice and cognitive processing depth (Agarwal, 2019).'; + "[Auto-Corrected Neuromyth] Retention percentage claims ('we remember 10% of what we read') " + + 'use numbers that have no research source. Some texts incorrectly connect these numbers to ' + + "Dale's Cone of Experience. Retention changes with retrieval practice and the depth of " + + 'cognitive processing (Agarwal, 2019).'; finding.evidence = finding.evidence ? `${finding.evidence} ${corrEv}` : corrEv; - const obsNote = ' [Note: Retention percentage pyramids are fabricated; active retrieval principles should be used.]'; + const obsNote = ' [Note: Retention percentage pyramids are incorrect. idstack recommends that you use active retrieval.]'; finding.observation = `${finding.observation || ''}${obsNote}`.trim(); } return finding; diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 80b2bc7..a84ca97 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -1688,6 +1688,49 @@ open(p, 'w', encoding='utf-8').write(s) PY expect_fail "a contraction in the setup closing text" "$WORK/r/test/test-setup.sh" "$WORK/r" +# 45a-45c. The text that `idstack-consensus verify` writes into findings. Skills copy +# it into HTML reports and the manifest, so it must obey the writing standard, and +# the extension's copy of the neuromyth text must stay the same as the CLI's copy. +# 45a. A semicolon returns to a neuromyth note -> test-consensus-cli must fail. +# The mutation changes both copies the same way, so only the STE guard can fail. +fresh +python3 - "$WORK/r/bin/idstack-consensus" "$WORK/r/extension/shared/consensus-client.js" <<'PY' +import sys +for p in sys.argv[1:]: + s = open(p, encoding='utf-8').read() + old = "Sensory learning styles are a known neuromyth. idstack recommends" + assert s.count(old) == 1, 'anchor not unique in %s: %d' % (p, s.count(old)) + s = s.replace(old, "Sensory learning styles are a known neuromyth; idstack recommends", 1) + open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a neuromyth note has a semicolon again" python3 "$WORK/r/test/test-consensus-cli.py" + +# 45b. "should" returns to a tier-calibration note -> test-consensus-cli must fail. +# This note has no copy in the extension. +fresh +python3 - "$WORK/r/bin/idstack-consensus" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = 'to T4 because the evidence has no meta-analysis or controlled trial.]"' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, 'to T4 because the evidence should have a meta-analysis or controlled trial.]"', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a tier-calibration note says should again" python3 "$WORK/r/test/test-consensus-cli.py" + +# 45c. The extension's neuromyth text drifts from the CLI's -> test-consensus-cli must fail. +# Before this guard, nothing kept the two copies the same. +fresh +python3 - "$WORK/r/extension/shared/consensus-client.js" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "'Multimodal dual coding helps all learners.';" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "'Multimodal dual coding helps every learner.';", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the extension neuromyth text drifts from the CLI text" python3 "$WORK/r/test/test-consensus-cli.py" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/test-consensus-cli.py b/test/test-consensus-cli.py index fb8862a..3cfd77c 100644 --- a/test/test-consensus-cli.py +++ b/test/test-consensus-cli.py @@ -2,6 +2,8 @@ """Tests for bin/idstack-consensus client and caching engine.""" import hashlib +import importlib.machinery +import importlib.util import json import os import shutil @@ -9,9 +11,13 @@ import sys import tempfile import unittest +from pathlib import Path REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) CLI_PATH = os.path.join(REPO_ROOT, "bin", "idstack-consensus") +STE_CHECK_PATH = os.path.join(REPO_ROOT, "bin", "idstack-ste-check") +JS_CLIENT_PATH = os.path.join(REPO_ROOT, "extension", "shared", "consensus-client.js") +MYTH_TYPES = ["learning_styles", "hemisphere_learning", "ten_percent_brain", "dales_cone_percentages"] class TestConsensusCLI(unittest.TestCase): @@ -396,7 +402,7 @@ def test_sync_default_is_dry_run(self): def test_sync_evidence_cards_compatibility(self): proc = self.run_cli(["sync", "--dry-run"]) self.assertEqual(proc.returncode, 0) - self.assertIn("verified", proc.stdout.lower()) + self.assertIn("check-evidence-cards.py found 0 mismatches", proc.stdout) def test_sync_uses_cached_papers_when_available(self): os.makedirs(self.cache_dir, exist_ok=True) @@ -426,6 +432,101 @@ def test_sync_uses_cached_papers_when_available(self): self.assertIn("Custom Cached Formative Feedback Review", proc.stdout) self.assertIn("CachedAuthor", proc.stdout) + # The text that verify writes goes into HTML reports and the manifest, so it + # must obey the writing standard. The input fields are empty or in STE, and + # the trigger is in the recommendation, which verify replaces. Thus a problem + # that the checker finds comes from idstack's own text. + def run_verify(self, findings): + input_file = os.path.join(self.test_dir, "ste-in.json") + output_file = os.path.join(self.test_dir, "ste-out.json") + with open(input_file, "w", encoding="utf-8") as f: + json.dump({"findings": findings}, f) + proc = self.run_cli(["verify", "--findings", input_file, "--output", output_file]) + self.assertEqual(proc.returncode, 0, proc.stderr) + with open(output_file, "r", encoding="utf-8") as f: + return json.load(f)["findings"] + + def assert_ste_clean(self, text): + proc = subprocess.run( + [sys.executable, STE_CHECK_PATH, "-"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + input=text, + cwd=REPO_ROOT, + ) + self.assertEqual(proc.returncode, 0, "idstack-ste-check found problems:\n" + proc.stdout + proc.stderr) + + def test_verify_neuromyth_text_obeys_ste(self): + triggers = { + "learning_styles": "Cater to visual learners.", + "hemisphere_learning": "Give tasks to right-brain learners.", + "ten_percent_brain": "Learners use 10% of their brain.", + "dales_cone_percentages": "Learners remember 10% of what they read.", + } + findings = [ + {"severity": "warning", "tier": "T1", "citation": "[Novel-Claim]", + "observation": "", "evidence": "", "recommendation": triggers[t]} + for t in MYTH_TYPES + ] + texts = [] + for finding in self.run_verify(findings): + self.assertTrue(finding.get("auto_corrected")) + self.assertIn("[Auto-Corrected Neuromyth]", finding["evidence"]) + self.assertIn("[Note:", finding["observation"]) + texts += [finding["recommendation"], finding["evidence"], finding["observation"]] + self.assertEqual(len(texts), 12) + self.assert_ste_clean("\n\n".join(texts) + "\n") + + def test_verify_tier_calibration_text_obeys_ste(self): + evidence = [ + "A quasi-experimental study with a comparison group.", + "A systematic review of peer grading.", + "An observational survey of 45 students.", + "A study of 30 students in one class.", + ] + findings = [ + {"severity": "warning", "tier": "T1", "citation": "Zzyzx (2021)", + "observation": "", "evidence": e, "recommendation": "Add a rubric."} + for e in evidence + ] + result = self.run_verify(findings) + self.assertEqual([f["tier"] for f in result], ["T2", "T3", "T4", "T4"]) + for finding in result: + self.assertIn("[Tier calibrated:", finding["evidence"]) + self.assert_ste_clean("\n\n".join(f["evidence"] for f in result) + "\n") + + # The extension has a copy of the neuromyth text. The two copies must stay + # the same, word for word. + @unittest.skipUnless(shutil.which("node"), "node is not installed") + def test_neuromyth_text_matches_extension_copy(self): + script = ( + "import { autoCorrectNeuromyth } from %s;\n" + "const out = {};\n" + "for (const t of %s) {\n" + " out[t] = autoCorrectNeuromyth({ observation: '', evidence: '', recommendation: '' }, t);\n" + "}\n" + "console.log(JSON.stringify(out));\n" + ) % (json.dumps(Path(JS_CLIENT_PATH).as_uri()), json.dumps(MYTH_TYPES)) + proc = subprocess.run( + ["node", "--input-type=module", "-e", script], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + self.assertEqual(proc.returncode, 0, proc.stderr) + js = json.loads(proc.stdout) + + loader = importlib.machinery.SourceFileLoader("idstack_consensus", CLI_PATH) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + for myth_type in MYTH_TYPES: + finding = {"observation": "", "evidence": "", "recommendation": ""} + module.auto_correct_neuromyth(finding, myth_type) + for key in ("recommendation", "evidence", "observation"): + self.assertEqual(js[myth_type][key], finding[key], "%s %s" % (myth_type, key)) + if __name__ == "__main__": unittest.main() diff --git a/test/test-consensus-client.mjs b/test/test-consensus-client.mjs index d2fbea7..7110e89 100644 --- a/test/test-consensus-client.mjs +++ b/test/test-consensus-client.mjs @@ -71,7 +71,7 @@ async function runTests() { 'Evidence must cite empirical literature debunking learning styles' ); assert.ok( - corrected.observation.includes('recognized neuromyth'), + corrected.observation.includes('known neuromyth'), 'Observation must note the debunked status' ); console.log(' ✔ Test 2: Neuromyth auto-correction passed'); @@ -85,7 +85,7 @@ async function runTests() { evidence: 'Right-brain learners need creative tasks.' }; const correctedHemisphere = autoCorrectNeuromyth(hemisphereFinding, 'hemisphere_learning'); - assert.ok(correctedHemisphere.recommendation.includes('integrated cognitive activities')); + assert.ok(correctedHemisphere.recommendation.includes('integrated cognitive tasks')); assert.ok(correctedHemisphere.evidence.includes('Hemispheric specialization')); const tenPercentFinding = { From 331edb611c2621afcd601c90183eb9bc2af575a7 Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 14:18:25 -0700 Subject: [PATCH 06/10] feat(extension): give the model prompts the ASD-STE100 rules Co-Authored-By: Claude Opus 5.5 --- extension/shared/prompts.js | 33 +++++++----- extension/shared/ste-rules.js | 99 +++++++++++++++++++++++++++++++++++ test/mutation-test.sh | 27 ++++++++++ test/test-ste-rules.mjs | 36 +++++++++++++ 4 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 extension/shared/ste-rules.js create mode 100644 test/test-ste-rules.mjs diff --git a/extension/shared/prompts.js b/extension/shared/prompts.js index 8d142e7..6f015e2 100644 --- a/extension/shared/prompts.js +++ b/extension/shared/prompts.js @@ -1,6 +1,11 @@ import { TIER_METADATA } from './evidence-base.js'; +import { STE_RULES } from './ste-rules.js'; const TIER_SCALE = Object.entries(TIER_METADATA).map(([tier, m]) => ` ${tier}: ${m.description}`).join('\n'); +// The side panel shows the model's text to people, so the model gets the +// writing rules that the skills obey. +const WRITING_STANDARD = `Obey the rules that follow when you write keyTakeaway, observation, evidence, recommendation and improvedDraft. Do not change JSON keys, severity and tier values, citations or quotations from the course. +${STE_RULES}`; export function buildAuditPrompt({ title, pageType, content = '' }) { return `You are idstack, an evidence-based instructional design co-pilot. @@ -27,23 +32,25 @@ You MUST respond strictly with valid JSON conforming to this schema: "summary": { "bloomsLevel": "Remember | Understand | Apply | Analyze | Evaluate | Create", "alignmentScore": "Strong | Moderate | Weak", - "keyTakeaway": "1-2 sentence executive summary of findings" + "keyTakeaway": "Summary of the findings in one or two sentences" }, "findings": [ { "severity": "critical | warning | info", "tier": "T1 | T2 | T3 | T4 | T5", "citation": "[Domain-Code] Short Citation", - "observation": "What is present in the current material", - "evidence": "What peer-reviewed research indicates", - "recommendation": "Specific actionable suggestion" + "observation": "What is in the current material", + "evidence": "What peer-reviewed research shows", + "recommendation": "One clear change that idstack recommends" } ], "improvedDraft": { - "title": "Improved Rubric / Learning Objective / Assignment Prompt", - "content": "Full markdown text ready for the instructor to copy-paste into Canvas" + "title": "Recommended Rubric / Learning Objective / Assignment Prompt", + "content": "Full markdown text that the instructor can paste into Canvas" } -}`; +} + +${WRITING_STANDARD}`; } export function buildCourseAuditPrompt(courseData = {}) { @@ -81,25 +88,27 @@ ${TIER_SCALE} Respond with ONLY a valid JSON object matching this schema: { "summary": { - "bloomsLevel": "Overall Cognitive Demand (e.g. Apply / Analyze)", + "bloomsLevel": "Bloom's level of the course (for example Apply / Analyze)", "alignmentScore": "High (90%) | Moderate (70%) | Low (40%)", - "keyTakeaway": "1-2 sentence executive summary of course-wide curriculum alignment." + "keyTakeaway": "Summary of the alignment of the full course in one or two sentences." }, "findings": [ { "severity": "critical" | "warning" | "info", "tier": "T1" | "T2" | "T3" | "T4" | "T5", "citation": "[Domain-ID] Citation Name", - "observation": "What was identified across the course syllabus and assignments.", + "observation": "What idstack found in the course syllabus and assignments.", "evidence": "Author (Year) [Tier description]: Empirical finding.", - "recommendation": "Concrete actionable curriculum fix." + "recommendation": "One clear change to the curriculum that idstack recommends." } ], "improvedDraft": { "title": "Course Alignment & Scaffolding Matrix", "content": "Markdown formatted course roadmap and revised assessment scaffolding." } -}`; +} + +${WRITING_STANDARD}`; } export { TIER_METADATA }; diff --git a/extension/shared/ste-rules.js b/extension/shared/ste-rules.js new file mode 100644 index 0000000..5d10a3a --- /dev/null +++ b/extension/shared/ste-rules.js @@ -0,0 +1,99 @@ +// A copy of the ASD-STE100 rules between the ste-core markers in +// templates/preamble.md. Both model prompts carry it. If you change the +// preamble text, change this copy at the same time: test/test-ste-rules.mjs +// fails when the two differ. +export const STE_RULES = `Writing standard: ASD-STE100 Simplified Technical English (STE) + +This summary uses the ASD-STE100 writing rules. ASD does not endorse idstack. This summary does not replace the ASD-STE100 standard. + +Use these rules only for text in English. If the text is not in English, do not use these rules. + +Words: +- Use one word for one meaning. Use the same term for the same item in all of the text. +- Use short words that are easy to understand. Do not use the words in the first column of the word list. +- Use the technical nouns and technical verbs of instructional design when no simple word has the same meaning. Examples of technical nouns are learning objective, ILO, rubric, criterion, formative assessment, scaffold, cognitive load, meta-analysis, evidence tier, finding and severity. +- The Bloom's taxonomy verbs are technical verbs. Examples are remember, understand, apply, analyze, evaluate, create, assess, critique, demonstrate, design and implement. Use them in learning objectives and in text about Bloom's levels. +- Software verbs are technical verbs. Examples are run, click, open, save, install and update. +- Do not use a technical noun as a verb. +- Do not use contractions. Write "do not", not "don't". +- Do not use Latin abbreviations. Write "for example", not "e.g.". + +Verbs: +- Use the active voice. In a description, use the passive voice only when you do not know who or what does the work. +- Use only the simple tenses. Write "writes", "wrote" or "will write". Do not write "has written", "had written" or "is writing". +- Do not use a verb that ends in "-ing". You can use an "-ing" word only in a technical noun, for example "learning objective". + +Sentences: +- Write one topic in each sentence. +- An instruction has a maximum of 20 words. A description has a maximum of 25 words. +- Do not remove words, for example "the", "a" and "is", to make a sentence shorter. +- A noun cluster has a maximum of three nouns. +- Do not use semicolons. Write two sentences. +- Use a vertical list for text that has many parts. + +Instructions and descriptions: +- Write an instruction as a command. Write one instruction in each sentence. Give a number to each step. +- If the reader must know a condition first, write the condition first. Example: "If the course has no rubric, add a rubric." +- Do not write commands in a description. A paragraph has one topic and a maximum of six sentences. + +Recommendations: +- First show the problem that idstack found. Then show the evidence. Then give the recommendation. +- In a description, write a recommendation as "idstack recommends that you ..." or as "You can ...". +- In a numbered list of steps, write each step as a command. +- Do not use "consider", "may", "should" or "suggest". + +Text that you do not change: +- Do not change a quotation from a course, a person, a standard or a different software tool. Put each quotation in quotation marks. A new version of a text is not a quotation. Write it with these rules. +- Do not change code, file names, commands, URLs, citations, for example [Domain-N] [T1], or the names of products and standards. + +Word list. Do not use the word in the first column. Use the word in the second column. + +| Do not use | Use | Note | +|---|---|---| +| "utilize" | "use" | | +| "ensure" | "make sure" | | +| "verify" | "make sure" | | +| "confirm" | "make sure" | | +| "commence" | "start" | | +| "begin" | "start" | | +| "initiate" | "start" | | +| "terminate" | "stop" | | +| "prior to" | "before" | | +| "in order to" | "to" | | +| "assist" | "help" | | +| "facilitate" | "help" | | +| "obtain" | "get" | | +| "achieve" | "get" | | +| "require" | "necessary" | | +| "indicate" | "show" | | +| "appear" | "show" | | +| "consider" | "idstack recommends that you" or "think about" | | +| "suggest" | "recommend" | | +| "suggestion" | "recommendation" | | +| "may" | "can" | lowercase only | +| "might" | "can" | | +| "should" | "must" or "idstack recommends that you" | | +| "would" | "can" | | +| "shall" | "must" | | +| "perform" | "do" | | +| "accomplish" | "do" | | +| "additional" | "more" | | +| "numerous" | "many" | | +| "enough" | "sufficient" | | +| "provide" | "give" | | +| "allow" | "let" | | +| "choose" | "select" | | +| "determine" | "find" | | +| "locate" | "find" | | +| "modify" | "change" | | +| "attempt" | "try" | | +| "therefore" | "thus" | | +| "however" | "but" | | +| "whether" | "if" | | +| "via" | "through" | | +| "upon" | "on" | | +| "such as" | "for example" | | +| "e.g." | "for example" | | +| "i.e." | "that is" | | +| "etc." | (write the full list) | | +| "please" | (remove the word) | |`; diff --git a/test/mutation-test.sh b/test/mutation-test.sh index a84ca97..8a7c096 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -1731,6 +1731,33 @@ open(p, 'w', encoding='utf-8').write(s) PY expect_fail "the extension neuromyth text drifts from the CLI text" python3 "$WORK/r/test/test-consensus-cli.py" +# 47a-47b. The extension's model prompts carry a copy of the preamble's +# ASD-STE100 rules. test-ste-rules.mjs compares the copy with the ste-core block +# and checks that both prompts carry it. +# 47a. The extension copy drifts by one word -> test-extension must fail. +fresh +python3 - "$WORK/r/extension/shared/ste-rules.js" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = '| "utilize" | "use" | |' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, '| "utilize" | "employ" | |', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the extension copy of the STE rules drifts" "$WORK/r/test/test-extension.sh" + +# 47b. The course prompt drops the STE rules -> test-extension must fail. +fresh +python3 - "$WORK/r/extension/shared/prompts.js" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = 'revised assessment scaffolding."\n }\n}\n\n${WRITING_STANDARD}`;' +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, 'revised assessment scaffolding."\n }\n}`;', 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "course prompt drops the STE rules" "$WORK/r/test/test-extension.sh" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/test-ste-rules.mjs b/test/test-ste-rules.mjs new file mode 100644 index 0000000..0da7f28 --- /dev/null +++ b/test/test-ste-rules.mjs @@ -0,0 +1,36 @@ +// The extension's model prompts carry a copy of the ASD-STE100 writing rules +// that templates/preamble.md gives every skill, between the ste-core markers. +// The copy must stay word for word the same as the preamble text, and both +// prompts must carry it. Expected text is parsed from the preamble, never +// restated here. +import assert from 'node:assert'; +import fs from 'node:fs'; +import { STE_RULES } from '../extension/shared/ste-rules.js'; +import { buildAuditPrompt, buildCourseAuditPrompt } from '../extension/shared/prompts.js'; + +const preamble = fs.readFileSync(new URL('../templates/preamble.md', import.meta.url), 'utf8'); +const BEGIN = ''; +const END = ''; +assert.strictEqual(preamble.split(BEGIN).length, 2, `templates/preamble.md must contain ${BEGIN} once`); +assert.strictEqual(preamble.split(END).length, 2, `templates/preamble.md must contain ${END} once`); +assert.ok(preamble.indexOf(BEGIN) < preamble.indexOf(END), `${BEGIN} must come before ${END}`); +const norm = (s) => s.replace(/\s+/g, ' ').trim(); +const canonical = norm(preamble.slice(preamble.indexOf(BEGIN) + BEGIN.length, preamble.indexOf(END))); +// An empty block would make the comparison below vacuous. +assert.ok(canonical, 'no text between the ste-core markers in templates/preamble.md'); + +// Collect every disagreement so one run names them all. +const problems = new Set(); +const check = (ok, msg) => { if (!ok) problems.add(msg); }; + +const copy = norm(STE_RULES); +let at = 0; +while (at < canonical.length && canonical[at] === copy[at]) at++; +check(copy === canonical, `extension/shared/ste-rules.js differs from the ste-core block in templates/preamble.md at "${canonical.slice(at, at + 40)}" (copy: "${copy.slice(at, at + 40)}")`); + +for (const [where, prompt] of [['page prompt', buildAuditPrompt({ title: 't' })], ['course prompt', buildCourseAuditPrompt({})]]) { + check(prompt.includes(STE_RULES), `${where} does not contain STE_RULES`); +} + +assert.ok(problems.size === 0, `Extension writing rules disagree with templates/preamble.md:\n ${[...problems].join('\n ')}`); +console.log('✅ STE rules tests passed.'); From bc90a8935a7eda2e73cd7515851db9fd8674571b Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 14:40:35 -0700 Subject: [PATCH 07/10] feat(extension): rewrite the extension text in ASD-STE100 Co-Authored-By: Claude Haiku 4.5 --- PRIVACY.md | 4 +- README.md | 4 +- extension/background/canvas-crawler.js | 8 +- extension/background/parser-helper.js | 64 ++++++------- extension/background/service-worker.js | 10 +- extension/content/extractor.js | 4 +- extension/manifest.json | 4 +- extension/shared/dossier-compiler.js | 28 +++--- extension/sidepanel/index.html | 26 ++--- extension/sidepanel/renderer-helper.js | 16 ++-- extension/sidepanel/sidepanel.js | 28 +++--- test/mutation-test.sh | 69 ++++++++++++-- test/print-extension-output.mjs | 125 +++++++++++++++++++++++++ test/smoke-test.sh | 11 +++ test/test-crawler.mjs | 8 +- test/test-disclosures.mjs | 13 ++- test/test-dossier-compiler.mjs | 4 +- test/test-renderer-helper.mjs | 18 ++-- test/test-service-worker.mjs | 4 +- test/test-sidepanel-state.mjs | 14 +-- 20 files changed, 332 insertions(+), 130 deletions(-) create mode 100644 test/print-extension-output.mjs diff --git a/PRIVACY.md b/PRIVACY.md index c494262..d57ff30 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -41,13 +41,13 @@ Both run only when you invoke that skill and confirm the target course. Your tok The extension is built to audit course materials, not student records. Here is what it reads, where that text goes, and what it keeps. -- **What it reads.** The extension reads a tab only when it has access: after you click the idstack toolbar icon on that tab, on Canvas pages at `instructure.com`, or on a Canvas site you allowed when Chrome asked. While the side panel is open it reads the title and main text of such a tab; that text stays in the panel until you click **Audit Page with Evidence**. On a Google Doc it downloads the document's plain text using your Google login. **Audit Entire Course** requests the course syllabus and assignments from your Canvas instance's API using your existing Canvas login. The extension will not read Canvas grades, Gradebook, SpeedGrader, People, groups, discussions, submissions, or Inbox pages. Other pages can still contain student names or work, so audit course materials rather than student work. +- **What it reads.** The extension reads a tab only when it has access: after you click the idstack toolbar icon on that tab, on Canvas pages at `instructure.com`, or on a Canvas site you allowed when Chrome asked. While the side panel is open it reads the title and main text of such a tab; that text stays in the panel until you click **Audit Page with Evidence**. On a Google Doc it downloads the document's plain text using your Google login. **Audit Full Course** requests the course syllabus and assignments from your Canvas instance's API using your existing Canvas login. The extension will not read Canvas grades, Gradebook, SpeedGrader, People, groups, discussions, submissions, or Inbox pages. Other pages can still contain student names or work, so audit course materials rather than student work. - **Demo mode.** With no Google API key saved, the page's text is not sent anywhere: every audit returns the same sample findings, whatever the page says. If a Consensus key is saved, those sample findings are still checked against Consensus. - **Where the text goes with a key.** With your own Google AI Studio API key saved, the page title and text (or the syllabus and assignments) are sent directly from your browser to Google's Generative Language API (`generativelanguage.googleapis.com`) under your key and your agreement with Google. No idstack server is involved. - **Google's free tier.** If your key's Google Cloud project has no active Cloud Billing account, Google's terms let Google use what you submit, and the responses, to improve its products, and human reviewers may read them. The same terms ask you not to submit personal information. On billed projects, and for users in the European Economic Area, Switzerland, and the United Kingdom, Google says it does not use prompts or responses to improve its products. See [Google's terms for this API](https://ai.google.dev/gemini-api/terms). - **Consensus, with a Consensus key.** If you also save a Consensus key in Settings, then after each audit the claim of every finding (its evidence text, or failing that its observation, recommendation, or citation) is sent from your browser to Consensus (`api.consensus.app`) under your key. Findings describe the audited page and can quote it. Answers are cached in `chrome.storage.local`, and a claim already in the cache is not sent again. Without a Consensus key, nothing goes to Consensus. - **API keys.** Your Google API key and your Consensus key are saved in `chrome.storage.sync`. If Chrome sync is on, Chrome copies them to your Google account and to other browsers where you are signed in. The extension sends each key only to its own service, with each audit. To remove one, clear its field in Settings and save. -- **Audit history and dossier.** The last 20 audit results (page address, title, page type, and the full audit result) are kept in `chrome.storage.local`, as is the Course Dossier you build. Both stay on this device. **Clear Dossier** empties the dossier; removing the extension deletes both. +- **Audit history and dossier.** The last 20 audit results (page address, title, page type, and the full audit result) are kept in `chrome.storage.local`, as is the Course Dossier you build. Both stay on this device. **Remove All from Dossier** empties the dossier; removing the extension deletes both. - **Fonts.** Opening the side panel loads its fonts from Google Fonts (`fonts.googleapis.com`, `fonts.gstatic.com`), so Google receives your IP address and browser details; no page or course content is sent. - **No tracking.** The extension contains no analytics, tracking scripts, or telemetry. diff --git a/README.md b/README.md index 9ef1fe5..b19b84a 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,12 @@ If developing or modifying the extension locally: #### Full Course Audit (Canvas LMS) -When viewing any Canvas course homepage or modules list (`/courses/:id`), idstack automatically detects the course environment and presents an **"Audit Entire Course"** button: +When viewing any Canvas course homepage or modules list (`/courses/:id`), idstack automatically detects the course environment and presents an **"Audit Full Course"** button: - **Zero Developer Tokens Required:** Reads the course syllabus and assignment descriptions through Canvas's API using your active browser session. No Canvas API keys, LMS admin setup, or command line required. - **Course-level findings:** Reviews the syllabus and assignments together for alignment gaps, cognitive demand across Bloom's levels, and prioritized fixes, shown in the same results view as a page audit. - **Export Ready:** Add the course audit to your Course Dossier or export it as Markdown to share with instructional design teams and faculty stakeholders. -On Canvas hosted outside `instructure.com` (for example, a university's own domain), Chrome asks once, for that site only, when you first click **"Audit Entire Course"**. +On Canvas hosted outside `instructure.com` (for example, a university's own domain), Chrome asks once, for that site only, when you first click **"Audit Full Course"**. #### Multi-Page Course Dossier & Markdown Export diff --git a/extension/background/canvas-crawler.js b/extension/background/canvas-crawler.js index 6425b78..bd325ea 100644 --- a/extension/background/canvas-crawler.js +++ b/extension/background/canvas-crawler.js @@ -18,14 +18,14 @@ const MAX_ASSIGNMENT_PAGES = 10; export async function crawlCanvasCourse(origin, courseId, fetchImpl = fetch) { if (!origin || !courseId) { - throw new Error('Canvas origin and courseId are required for course crawling.'); + throw new Error('A course audit must have a Canvas origin and a courseId.'); } // 1. Fetch Course details & syllabus const courseUrl = `${origin}/api/v1/courses/${courseId}?include[]=syllabus_body`; const courseRes = await fetchImpl(courseUrl, { credentials: 'include' }); if (!courseRes.ok) { - throw new Error(`Failed to fetch Canvas course info (${courseRes.status})`); + throw new Error(`idstack cannot get the Canvas course information (${courseRes.status}).`); } const courseJson = await courseRes.json(); @@ -36,11 +36,11 @@ export async function crawlCanvasCourse(origin, courseId, fetchImpl = fetch) { let assignmentsUrl = `${origin}/api/v1/courses/${courseId}/assignments?per_page=50`; for (let page = 1; assignmentsUrl; page++) { if (page > MAX_ASSIGNMENT_PAGES) { - throw new Error(`This course has more than ${assignments.length} assignments, more than the full-course audit can read. Try auditing individual assignment pages instead.`); + throw new Error(`This course has more than ${assignments.length} assignments. The full-course audit cannot read that many. Audit the assignment pages one at a time.`); } const assignRes = await fetchImpl(assignmentsUrl, { credentials: 'include' }); if (!assignRes.ok) { - throw new Error(`Failed to fetch Canvas assignments (${assignRes.status})`); + throw new Error(`idstack cannot get the Canvas assignments (${assignRes.status}).`); } const assignJson = await assignRes.json(); assignments.push(...assignJson.map((a) => ({ diff --git a/extension/background/parser-helper.js b/extension/background/parser-helper.js index dbf8c91..5fa83b8 100644 --- a/extension/background/parser-helper.js +++ b/extension/background/parser-helper.js @@ -21,52 +21,52 @@ export function getDemoAuditResult(payload = {}) { summary: { bloomsLevel: 'Analyze (Level 4)', alignmentScore: 'Moderate (78%)', - keyTakeaway: `[Demo Mode] Sample audit for "${title}" (${pageType}). Entering an API key in Settings unlocks live audits on any page.` + keyTakeaway: `[Demo Mode] This is a sample audit for "${title}" (${pageType}). If you save a Google AI Studio API key in Settings, idstack audits the text of each page.` }, findings: [ { severity: 'warning', tier: 'T1', citation: '[Assessment-8] Formative Feedback Matrix', - observation: 'Assessment rubric lacks descriptive performance criteria for intermediate mastery levels.', - evidence: 'Wisniewski et al. (2020) [T1 meta-analysis, d=0.48]: Elaborated feedback and rubric transparency significantly boost student self-regulation and achievement.', - recommendation: 'Add concrete performance descriptors and milestone criteria for each grade band instead of generic labels.' + observation: 'The assessment rubric does not give performance criteria for the middle levels of mastery.', + evidence: 'Wisniewski et al. (2020) [T1 meta-analysis, d=0.48]: Elaborated feedback and clear rubrics increase student self-regulation and achievement.', + recommendation: 'idstack recommends that you replace the general labels of each grade band with clear performance descriptors and milestone criteria.' }, { severity: 'critical', tier: 'T5', citation: '[Alignment-1] Direct Constructive Alignment', - observation: 'Learning objectives target analytical synthesis, but evaluation instruments only test lower-order recall.', - evidence: 'Biggs (1996) [T5]: Constructive misalignment between stated objectives and assessment formats leads to superficial learning strategies.', - recommendation: 'Incorporate authentic problem-solving prompts and case analysis rather than purely multiple-choice recall questions.' + observation: 'Learning objectives are at the analysis and synthesis levels, but the assessments measure only lower-order recall.', + evidence: 'Biggs (1996) [T5]: If the assessments do not align with the learning objectives, students use surface learning strategies.', + recommendation: 'idstack recommends that you add authentic problem-solving prompts and case analysis, not only multiple-choice recall questions.' }, { severity: 'info', tier: 'T1', citation: '[CogLoad-1] Cognitive Load & Chunking', - observation: 'Task instructions present multiple complex requirements in a single unsegmented block.', - evidence: 'Costley et al. (2023) [T1]: Segmenting complex instructional tasks into structured sequential phases reduces extraneous cognitive load.', - recommendation: 'Format multi-step assignment guidelines into sequenced checklists or distinct milestone stages.' + observation: 'The task instructions give many complex conditions in one long paragraph that has no segments.', + evidence: 'Costley et al. (2023) [T1]: Segmentation of complex tasks into a sequence of steps decreases extraneous cognitive load.', + recommendation: 'idstack recommends that you put multi-step assignment instructions into a numbered checklist or into milestone steps.' } ], improvedDraft: { - title: 'Improved Rubric Draft (idstack Recommended)', - content: `### Revised Task & Assessment Rubric for: ${title} + title: 'Recommended Rubric Draft', + content: `### Recommended Task & Assessment Rubric for: ${title} -> **Note:** This is a sample evidence-based draft generated in demo mode. Entering an API key in Settings unlocks live audits on any page. +> **Note:** This is a sample draft from demo mode. If you save a Google AI Studio API key in Settings, idstack audits the text of each page. -#### Analytical Task Prompt -Analyze the core case scenario and formulate a structured recommendation addressing: -1. Primary contributing factors identified in the evidence base. -2. Direct trade-offs between proposed intervention strategies. -3. A measurable evaluation plan for validating outcomes. +#### Analysis Task Prompt +Analyze the case scenario. Then write a recommendation that includes these parts: +1. The primary causes that the evidence shows. +2. The trade-offs between the intervention strategies that you recommend. +3. A measurable evaluation plan for the outcomes. #### Evaluation Rubric (Elaborated Criteria) | Criterion | Exemplary (Proficient) | Developing | Novice | | :--- | :--- | :--- | :--- | -| **Evidence Application [T1]** | Synthesizes 3+ relevant empirical sources with clear justification. | References 1-2 sources with partial justification. | Makes claims without empirical citations. | -| **Analytical Rigor [T2]** | Rigorously evaluates trade-offs and alternative hypotheses. | Identifies trade-offs but lacks systematic comparison. | Describes facts without evaluating trade-offs. | -| **Actionable Strategy [T1]** | Proposes concrete, measurable milestones with validation metrics. | Proposes general steps without measurable metrics. | Vague or non-actionable suggestions. |` +| **Evidence Application [T1]** | Synthesizes three or more related research sources and gives a clear justification. | Uses one or two sources and gives some justification. | Makes claims without research citations. | +| **Analysis Quality [T2]** | Evaluates all trade-offs and alternative hypotheses carefully. | Identifies trade-offs but does not compare them carefully. | Gives facts but does not evaluate trade-offs. | +| **Action Plan [T1]** | Gives clear, measurable milestones and a metric for each milestone. | Gives general steps without measurable metrics. | Gives recommendations that are not clear or not possible to use. |` } }; } @@ -77,32 +77,32 @@ export function getDemoCourseAuditResult(payload = {}) { summary: { bloomsLevel: 'Analyze & Evaluate (Levels 4-5)', alignmentScore: 'Moderate (74%)', - keyTakeaway: `[Course-Level Demo] Full-course audit for "${title}". Alignment gaps identified between week 1-4 recall quizzes and week 12 analytical capstone.` + keyTakeaway: `[Course-Level Demo] This is a full-course audit for "${title}". The audit found alignment problems between the recall quizzes in weeks 1-4 and the analysis capstone in week 12.` }, findings: [ { severity: 'critical', tier: 'T5', citation: '[Alignment-1] Direct Constructive Alignment', - observation: 'Modules 1-6 assess solely lower-order factual recall, while the final course project demands high-order synthesis without intermediate scaffolding.', - evidence: 'Biggs (1996) [T5]: Constructive alignment requires every assessment to engage the cognitive level its learning outcome names.', - recommendation: 'Introduce mid-semester milestone case studies in Module 4 to bridge the gap between quizzes and the final capstone.' + observation: 'Modules 1-6 assess only lower-order factual recall. The last course project assesses higher-order synthesis, and no scaffold prepares students for it.', + evidence: 'Biggs (1996) [T5]: In constructive alignment, each assessment must measure the cognitive level that its learning outcome names.', + recommendation: 'idstack recommends that you add mid-semester milestone case studies in Module 4 to connect the quizzes and the capstone.' }, { severity: 'warning', tier: 'T1', citation: '[CogLoad-6] Cognitive Load & Spaced Practice', - observation: 'Major assignment deadlines are clustered in Week 14-15 with no spaced formative checkpoints.', - evidence: 'Chen et al. (2018) [T1]: Distributing practice across time produces stronger long-term retention than massing the same content into a single session.', - recommendation: 'Redistribute submission checkpoints into 3 progressive deliverables across weeks 6, 10, and 14.' + observation: 'The deadlines for the primary assignments are together in weeks 14-15. The course has no spaced formative checkpoints.', + evidence: 'Chen et al. (2018) [T1]: Distributed practice gives better long-term retention than massed practice of the same content in one session.', + recommendation: 'idstack recommends that you divide the submissions into 3 deliverables in weeks 6, 10 and 14. Each deliverable adds to the one before it.' }, { severity: 'info', tier: 'T1', citation: '[Assessment-8] Formative Rubric Transparency', - observation: 'Syllabus grading policy lacks explicit performance criteria for collaborative group deliverables.', - evidence: 'Wisniewski et al. (2020) [T1 meta-analysis]: Pre-distribution of analytic rubrics with milestone criteria boosts student self-regulation and achievement.', - recommendation: 'Publish the multi-tier analytic grading rubric during the initial module launch.' + observation: 'The grading policy in the syllabus does not give clear performance criteria for collaborative group deliverables.', + evidence: 'Wisniewski et al. (2020) [T1 meta-analysis]: If students get analytic rubrics with milestone criteria before the task, their self-regulation and achievement increase.', + recommendation: 'idstack recommends that you publish the multi-level analytic grading rubric when the first module opens.' } ], improvedDraft: { @@ -114,7 +114,7 @@ export function getDemoCourseAuditResult(payload = {}) { | **Weeks 1-3** | Foundational Cell Structure | Spaced Knowledge Check (10 pts) | Module 1 Synthesis Quiz | | **Weeks 4-7** | Enzyme Kinetics & Modeling | Case Problem Milestone 1 [T2] | Lab Protocol Analysis | | **Weeks 8-11** | Experimental Troubleshooting | Peer Review Protocol [T1] | Milestone 2 Experimental Draft | -| **Weeks 12-15**| Autonomous Investigation | Scaffolded Capstone Consult | Final Research Capstone |` +| **Weeks 12-15**| Autonomous Investigation | Capstone Check with Scaffold | Last Research Capstone |` } }; } diff --git a/extension/background/service-worker.js b/extension/background/service-worker.js index 58050d5..8925363 100644 --- a/extension/background/service-worker.js +++ b/extension/background/service-worker.js @@ -42,7 +42,7 @@ async function callLlmApi(apiKey, prompt) { }).catch((err) => { // Chrome < 124 rejects a timed-out fetch with AbortError, so check the signal, not err.name. throw signal.aborted - ? new Error(`The AI service did not respond within ${LLM_TIMEOUT_MS / 1000} seconds. Please retry the audit.`) + ? new Error(`The AI model did not send a result in ${LLM_TIMEOUT_MS / 1000} seconds. Try the audit again.`) : err; }); @@ -53,14 +53,14 @@ async function callLlmApi(apiKey, prompt) { const data = await response.json(); const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text; - if (!rawText) throw new Error('Empty response from AI model.'); + if (!rawText) throw new Error('The AI model sent an empty response.'); const result = cleanJsonResponse(rawText); // The renderer and dossier compiler read every finding and call string methods // on tier and severity; refuse a shape they cannot handle before it is saved. const optionalString = (v) => v == null || typeof v === 'string'; if (!Array.isArray(result?.findings) || !result.findings.every((f) => f && typeof f === 'object' && optionalString(f.tier) && optionalString(f.severity))) { - throw new Error('The AI response did not match the audit format. Please retry the audit.'); + throw new Error('The AI response is not in the audit format. Try the audit again.'); } return result; } @@ -76,7 +76,7 @@ if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.onMessage) const wordCount = String(request.payload?.content || '').split(/\s+/).filter(Boolean).length; if (wordCount < MIN_AUDIT_WORDS) { // The extractor and side panel explain pages they could not or would not read. - throw new Error(request.payload?.emptyReason || `This page has too little readable text to audit (${wordCount} words; at least ${MIN_AUDIT_WORDS} needed). Browser pages, PDF viewers, and image-only pages expose no text to idstack.`); + throw new Error(request.payload?.emptyReason || `This page does not have sufficient text to audit. It has ${wordCount} words, and an audit must have ${MIN_AUDIT_WORDS} words or more. Browser pages, PDF viewers and image-only pages do not give text to idstack.`); } const settings = await getSettings(); @@ -117,7 +117,7 @@ if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.onMessage) let target = null; try { target = new URL(origin); } catch (e) { /* not a URL */ } if (!target || target.protocol !== 'https:' || target.origin !== origin || !/^\d+$/.test(courseId)) { - throw new Error('Invalid Canvas course target.'); + throw new Error('The Canvas course URL is not correct.'); } const courseData = await crawlCanvasCourse(origin, courseId); if (!courseData.syllabus.trim() && courseData.assignments.length === 0) { diff --git a/extension/content/extractor.js b/extension/content/extractor.js index ca50fff..e43122d 100644 --- a/extension/content/extractor.js +++ b/extension/content/extractor.js @@ -27,7 +27,7 @@ function extractContentFromDOM(document, url = (typeof window !== 'undefined' && pageType: 'Canvas Student Records', content: '', wordCount: 0, - emptyReason: 'idstack does not read Canvas pages that show student records (grades, People, Inbox, discussions, groups, submissions). Consider auditing an assignment, page, syllabus, rubric, or Modules view instead.' + emptyReason: 'idstack does not read Canvas pages that show student records (grades, People, Inbox, discussions, groups and submissions). You can audit an assignment, a page, a syllabus, a rubric or the Modules view.' }; } @@ -88,7 +88,7 @@ async function extractPageContent() { return { ...data, content: text.slice(0, 15000), wordCount: text.split(/\s+/).filter(Boolean).length }; } catch (e) { // Offline, timed out, refused, or not plain text. - return { ...data, content: '', wordCount: 0, emptyReason: 'idstack could not read the text of this Google Doc. If downloading is turned off for it, consider asking the owner to allow downloads.' }; + return { ...data, content: '', wordCount: 0, emptyReason: 'idstack cannot read the text of this Google Doc. If downloads are off for this document, the owner of the document can turn them on.' }; } } diff --git a/extension/manifest.json b/extension/manifest.json index 7949678..58e9286 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -3,7 +3,7 @@ "name": "idstack — Evidence-Based Course Design", "version": "1.1.0", "minimum_chrome_version": "116", - "description": "Evidence-based instructional design co-pilot for Canvas LMS, Google Docs, and course web pages.", + "description": "idstack audits course pages in Canvas LMS, Google Docs and on the web against instructional design evidence.", "homepage_url": "https://idstack.org", "icons": { "16": "icons/icon-16.png", @@ -11,7 +11,7 @@ "128": "icons/icon-128.png" }, "action": { - "default_title": "Open idstack Course Co-Pilot" + "default_title": "Open the idstack Course Co-Pilot" }, "side_panel": { "default_path": "sidepanel/index.html" diff --git a/extension/shared/dossier-compiler.js b/extension/shared/dossier-compiler.js index eec4742..dda444b 100644 --- a/extension/shared/dossier-compiler.js +++ b/extension/shared/dossier-compiler.js @@ -14,39 +14,39 @@ export function compileSingleAuditToMarkdown(item) { md += `## Executive Summary\n\n`; md += `- **Bloom's Demand:** ${result.summary?.bloomsLevel || 'N/A'}\n`; - md += `- **Constructive Alignment:** ${result.summary?.alignmentScore || 'N/A'}\n`; - md += `- **Key Takeaway:** ${result.summary?.keyTakeaway || 'No summary provided.'}\n\n`; + md += `- **Alignment Score:** ${result.summary?.alignmentScore || 'N/A'}\n`; + md += `- **Key Takeaway:** ${result.summary?.keyTakeaway || 'The audit has no summary.'}\n\n`; if (Array.isArray(result.findings) && result.findings.length > 0) { - md += `## Evidence-Based Findings & Recommendations\n\n`; + md += `## Findings, Evidence & Recommendations\n\n`; result.findings.forEach((f, idx) => { md += `### ${idx + 1}. [${f.tier || 'T1'}] ${f.citation || 'Citation'}\n\n`; md += `- **Severity:** \`${(f.severity || 'info').toUpperCase()}\`\n`; md += `- **Observation:** ${f.observation || ''}\n`; - md += `- **Empirical Evidence:** ${f.evidence || ''}\n`; - md += `- **Actionable Recommendation:** ${f.recommendation || ''}\n\n`; + md += `- **Evidence:** ${f.evidence || ''}\n`; + md += `- **Recommendation:** ${f.recommendation || ''}\n\n`; }); } if (result.improvedDraft && result.improvedDraft.content) { - md += `## ${result.improvedDraft.title || 'Improved Draft & Alignment Matrix'}\n\n`; + md += `## ${result.improvedDraft.title || 'Recommended Draft & Alignment Matrix'}\n\n`; md += `${result.improvedDraft.content}\n\n`; } - md += `---\n*Generated by [idstack](https://github.com/savvides/idstack) — Evidence-Based Course Design.*`; + md += `---\n*Made with [idstack](https://github.com/savvides/idstack) — Evidence-Based Course Design.*`; return md; } export function compileDossierToMarkdown(dossierItems, courseTitle = 'Canvas Course') { if (!Array.isArray(dossierItems) || dossierItems.length === 0) { - return `# idstack Course Audit Dossier: ${courseTitle}\n\n*No audit materials in dossier.*`; + return `# idstack Course Audit Dossier: ${courseTitle}\n\n*The dossier has no audits.*`; } const timestamp = new Date().toLocaleString(); let md = `# idstack Course Audit Dossier: ${courseTitle}\n\n`; md += `> **Course:** ${courseTitle} \n`; md += `> **Total Audited Materials:** ${dossierItems.length} components \n`; - md += `> **Compiled Date:** ${timestamp} \n`; + md += `> **Date:** ${timestamp} \n`; md += `> **Engine:** idstack Evidence-Based Instructional Design Co-Pilot\n\n`; md += `## Table of Audited Materials\n\n`; @@ -64,9 +64,9 @@ export function compileDossierToMarkdown(dossierItems, courseTitle = 'Canvas Cou const r = item.result || {}; md += `### Summary\n`; - md += `- **Cognitive Demand:** ${r.summary?.bloomsLevel || 'N/A'}\n`; - md += `- **Alignment Rating:** ${r.summary?.alignmentScore || 'N/A'}\n`; - md += `- **Takeaway:** ${r.summary?.keyTakeaway || 'N/A'}\n\n`; + md += `- **Bloom's Demand:** ${r.summary?.bloomsLevel || 'N/A'}\n`; + md += `- **Alignment Score:** ${r.summary?.alignmentScore || 'N/A'}\n`; + md += `- **Key Takeaway:** ${r.summary?.keyTakeaway || 'N/A'}\n\n`; if (Array.isArray(r.findings) && r.findings.length > 0) { md += `### Findings & Evidence\n\n`; @@ -79,13 +79,13 @@ export function compileDossierToMarkdown(dossierItems, courseTitle = 'Canvas Cou } if (r.improvedDraft && r.improvedDraft.content) { - md += `### ${r.improvedDraft.title || 'Revised Draft'}\n\n`; + md += `### ${r.improvedDraft.title || 'Recommended Draft'}\n\n`; md += `${r.improvedDraft.content}\n\n`; } md += `---\n\n`; }); - md += `*Generated by [idstack](https://github.com/savvides/idstack) — Evidence-Based Course Design.*`; + md += `*Made with [idstack](https://github.com/savvides/idstack) — Evidence-Based Course Design.*`; return md; } diff --git a/extension/sidepanel/index.html b/extension/sidepanel/index.html index 4c75369..fceb3e6 100644 --- a/extension/sidepanel/index.html +++ b/extension/sidepanel/index.html @@ -30,16 +30,16 @@
        - Detecting page... -

        Loading page title...

        + Page type... +

        idstack reads the page...

        -

        Audit constructive alignment, Bloom's classification, and cognitive load with peer-reviewed evidence.

        +

        idstack audits constructive alignment, Bloom's levels and cognitive load against peer-reviewed evidence.

        @@ -49,12 +49,12 @@

        Loading page title...

        -

        Analyzing course structure...

        +

        idstack examines the page...

        @@ -75,13 +75,13 @@

        Course Audit Dossier

        -

        Collected audits for compiled course-level markdown export.

        +

        The dossier keeps the audits that you add. You can export all of them as one Markdown file for the course.

        - +
        - +
        @@ -98,19 +98,21 @@

        Settings & Privacy

        Get your free Google AI Studio key → -

        Without a key, idstack runs in demo mode: every audit shows the same sample findings, whatever the page says, and the page's text is not sent anywhere.

        +

        Without a key, idstack runs in demo mode. Each audit then shows the same sample findings for all pages. In demo mode, idstack does not send the page text out of your browser.

        Get your Consensus API key → -

        Checks each finding against the research literature on Consensus. With a key saved, each finding's claim is sent to Consensus under your key.

        +

        idstack checks each finding against the research on Consensus. If you save a key, idstack sends the claim of each finding to Consensus with your key.


        Privacy & Student Data

        -

        With a key saved, each audit sends the page's title and text (for a full-course audit, the syllabus and assignments) from your browser to Google's API. On Google's free tier, Google may use that content to improve its products, and human reviewers may read it. idstack will not read Canvas grades, Gradebook, SpeedGrader, People, groups, discussions, submissions, or Inbox pages. Other pages can still name students, so audit course materials, not student work. With a Consensus key saved, each finding's claim, which can quote the page, is also sent to Consensus.

        +

        If you save a Google AI Studio key, each audit sends the page title and text from your browser to the Google API. For a full-course audit, idstack sends the syllabus and the assignments to the Google API. On Google's free tier, Google can use this content to improve its products. Human reviewers can also read this content.

        +

        idstack does not read Canvas grades, Gradebook, SpeedGrader, People, groups, discussions, submissions or Inbox pages. Other pages can show the names of students. Thus, idstack recommends that you audit course materials, not student work.

        +

        If you save a Consensus key, idstack also sends the claim of each finding to Consensus. This claim can contain text from the page.

        diff --git a/extension/sidepanel/renderer-helper.js b/extension/sidepanel/renderer-helper.js index 1cca9e6..b805bdd 100644 --- a/extension/sidepanel/renderer-helper.js +++ b/extension/sidepanel/renderer-helper.js @@ -16,7 +16,7 @@ export function renderAuditHTML(data) { if (!data) return ''; const summary = data.summary || { bloomsLevel: 'N/A', alignmentScore: 'N/A', keyTakeaway: '' }; const findings = Array.isArray(data.findings) ? data.findings.filter((f) => f && typeof f === 'object') : []; - const improvedDraft = data.improvedDraft || { title: 'Improved Draft', content: '' }; + const improvedDraft = data.improvedDraft || { title: 'Recommended Draft', content: '' }; const findingsHTML = findings.map(f => { const tierLabel = String(f.tier || 'T1'); @@ -30,7 +30,7 @@ export function renderAuditHTML(data) { let consensusBadgeHTML = ''; if (f.consensus) { - let badgeText = '✓ Consensus Verified'; + let badgeText = '✓ Found on Consensus'; if (f.consensus.meter !== undefined && f.consensus.meter !== null && f.consensus.totalStudies !== undefined && f.consensus.totalStudies !== null) { const count = f.consensus.totalStudies; const countLabel = count === 1 ? '1 paper' : `${count} papers`; @@ -65,7 +65,7 @@ export function renderAuditHTML(data) { const bloomsLevel = escapeHtml(summary.bloomsLevel || 'N/A'); const alignmentScore = escapeHtml(summary.alignmentScore || 'N/A'); const keyTakeaway = escapeHtml(summary.keyTakeaway || ''); - const draftTitle = escapeHtml(improvedDraft.title || 'Improved Draft'); + const draftTitle = escapeHtml(improvedDraft.title || 'Recommended Draft'); const draftContent = escapeHtml(improvedDraft.content || ''); return ` @@ -77,7 +77,7 @@ export function renderAuditHTML(data) {

        ${keyTakeaway}

        -

        Evidence-Based Findings (${findings.length})

        +

        Findings with Evidence (${findings.length})

        ${findingsHTML}
        @@ -89,10 +89,10 @@ export function renderAuditHTML(data) {
        `; } @@ -101,8 +101,8 @@ export function renderDossierListHTML(dossierItems) { if (!Array.isArray(dossierItems) || dossierItems.length === 0) { return `
        -

        No items in dossier yet.

        -

        Audit pages and click “Add to Dossier” to build your course dossier.

        +

        The dossier is empty.

        +

        To add an audit to the dossier, audit a page and click “Add to Dossier”.

        `; } diff --git a/extension/sidepanel/sidepanel.js b/extension/sidepanel/sidepanel.js index 960d046..865496d 100644 --- a/extension/sidepanel/sidepanel.js +++ b/extension/sidepanel/sidepanel.js @@ -36,7 +36,7 @@ async function copyWithFeedback(btn, text, idleLabel) { await navigator.clipboard.writeText(text); btn.textContent = '✓ Copied!'; } catch (e) { - btn.textContent = 'Copy failed'; + btn.textContent = 'Not copied'; } setTimeout(() => { btn.textContent = idleLabel; @@ -73,7 +73,7 @@ export async function updateDossierUI() { }); }); } catch (e) { - listEl.innerHTML = '

        Error loading dossier.

        '; + listEl.innerHTML = '

        idstack cannot read the dossier.

        '; } } @@ -129,7 +129,7 @@ export async function refreshActiveTab() { pageType: activeCourseContext.isCourseRoot ? 'Canvas Course Root' : 'Web Page', content: '', // The service worker shows this instead of auditing an empty page. - emptyReason: 'idstack cannot read this tab. If you just switched to it, click the idstack toolbar icon, then audit again. Browser pages such as chrome:// and the Chrome Web Store cannot be read.' + emptyReason: 'idstack cannot read this tab. If you moved to this tab from a different tab, click the idstack toolbar icon. Then audit again. idstack cannot read browser pages, for example chrome:// pages and the Chrome Web Store.' }; } catch (e) { console.warn('Error refreshing active tab:', e); @@ -214,17 +214,17 @@ export function renderError(errorMessage) { .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') - : 'Unknown error occurred during audit.'; + : 'An unknown error occurred during the audit.'; container.innerHTML = `
        ⚠️ -

        Audit Encountered an Issue

        +

        Audit Error

        ${safeError}

        - +
        @@ -292,7 +292,7 @@ if (auditBtn) { if (progressCard) progressCard.style.display = 'none'; const loaderStatus = document.getElementById('loader-status'); - if (loaderStatus) loaderStatus.textContent = 'Analyzing page content...'; + if (loaderStatus) loaderStatus.textContent = 'idstack examines the page...'; showState('loading'); @@ -306,7 +306,7 @@ if (auditBtn) { if (response && response.success) { renderResults(response.data, sent); } else { - renderError(response?.error || 'Unknown error occurred during audit.'); + renderError(response?.error || 'An unknown error occurred during the audit.'); } }); } catch (err) { @@ -334,7 +334,7 @@ if (auditCourseBtn) { // Not a requestable origin (optional_host_permissions covers https only) } if (!granted) { - renderError(`idstack needs access to ${origin} to audit the whole course.`); + renderError(`idstack must have access to ${origin} to audit the full course.`); return; } @@ -343,20 +343,20 @@ if (auditCourseBtn) { const progressFill = document.getElementById('crawl-progress-fill'); const loaderStatus = document.getElementById('loader-status'); - if (loaderStatus) loaderStatus.textContent = 'Auditing Full Canvas Course...'; + if (loaderStatus) loaderStatus.textContent = 'idstack audits the full Canvas course...'; if (progressCard) progressCard.style.display = 'block'; - if (statusText) statusText.textContent = 'Gathering syllabus...'; + if (statusText) statusText.textContent = 'idstack reads the syllabus...'; if (progressFill) progressFill.style.width = '25%'; showState('loading'); const timer1 = setTimeout(() => { - if (statusText) statusText.textContent = 'Fetching assignments...'; + if (statusText) statusText.textContent = 'idstack reads the assignments...'; if (progressFill) progressFill.style.width = '60%'; }, 600); const timer2 = setTimeout(() => { - if (statusText) statusText.textContent = 'Analyzing constructive alignment...'; + if (statusText) statusText.textContent = 'idstack examines the constructive alignment...'; if (progressFill) progressFill.style.width = '85%'; }, 1300); @@ -381,7 +381,7 @@ if (auditCourseBtn) { pageType: 'Canvas Course (Full Audit)' }); } else { - renderError(response?.error || 'Unknown error occurred during course audit.'); + renderError(response?.error || 'An unknown error occurred during the course audit.'); } }); } catch (err) { diff --git a/test/mutation-test.sh b/test/mutation-test.sh index 8a7c096..c67765f 100755 --- a/test/mutation-test.sh +++ b/test/mutation-test.sh @@ -702,7 +702,7 @@ fresh python3 - "$WORK/r/extension/background/service-worker.js" <<'PY' import sys p = sys.argv[1]; s = open(p, encoding='utf-8').read() -old = "throw new Error('The AI response did not match the audit format. Please retry the audit.');" +old = "throw new Error('The AI response is not in the audit format. Try the audit again.');" assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) s = s.replace(old, "", 1) open(p, 'w', encoding='utf-8').write(s) @@ -889,7 +889,7 @@ fresh python3 - "$WORK/r/extension/sidepanel/sidepanel.js" <<'PY' import sys p = sys.argv[1]; s = open(p, encoding='utf-8').read() -old = " emptyReason: 'idstack cannot read this tab. If you just switched to it, click the idstack toolbar icon, then audit again. Browser pages such as chrome:// and the Chrome Web Store cannot be read.'\n" +old = " emptyReason: 'idstack cannot read this tab. If you moved to this tab from a different tab, click the idstack toolbar icon. Then audit again. idstack cannot read browser pages, for example chrome:// pages and the Chrome Web Store.'\n" assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) s = s.replace(old, "", 1) open(p, 'w', encoding='utf-8').write(s) @@ -1066,7 +1066,7 @@ expect_fail "HTML sign-in page audited as the Google Doc" "$WORK/r/test/test-ext # 30a-30l. The side panel labelled a result with whatever tab was active when # the reply landed, let a slow tab refresh overwrite a newer one, left the last # result behind Add to Dossier after an error, retried the single-page audit -# whatever had failed, wiped Audit Another Page on a vote, and said "Copied!" +# whatever had failed, wiped Audit a Different Page on a vote, and said "Copied!" # when the clipboard write failed. The renderer threw on malformed findings, # inside the sendMessage callback, so the spinner stayed up. # test-sidepanel-state.mjs drives the shipped sidepanel.js through each case. @@ -1195,7 +1195,7 @@ PY expect_fail "Retry audits the course or site now active, not the one that failed" node "$WORK/r/test/test-sidepanel-state.mjs" # 30i. a feedback vote rewrites the whole row -> test-sidepanel-state must fail. -# The row also holds Audit Another Page, the only way back to the ready state. +# The row also holds Audit a Different Page, the only way back to the ready state. fresh python3 - "$WORK/r/extension/sidepanel/sidepanel.js" <<'PY' import sys @@ -1205,7 +1205,7 @@ assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) s = s.replace(old, "row.innerHTML = 'Thank you for your feedback!';", 1) open(p, 'w', encoding='utf-8').write(s) PY -expect_fail "feedback vote wipes Audit Another Page" node "$WORK/r/test/test-sidepanel-state.mjs" +expect_fail "feedback vote wipes Audit a Different Page" node "$WORK/r/test/test-sidepanel-state.mjs" # 30j. the renderer trusts a non-array findings value -> test-renderer-helper must fail. fresh @@ -1269,7 +1269,7 @@ fresh python3 - "$WORK/r/extension/sidepanel/index.html" <<'PY' import sys p = sys.argv[1]; s = open(p, encoding='utf-8').read() -old = " On Google's free tier, Google may use that content to improve its products, and human reviewers may read it." +old = " On Google's free tier, Google can use this content to improve its products." assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) s = s.replace(old, "", 1) open(p, 'w', encoding='utf-8').write(s) @@ -1281,7 +1281,7 @@ fresh python3 - "$WORK/r/extension/sidepanel/index.html" <<'PY' import sys p = sys.argv[1]; s = open(p, encoding='utf-8').read() -old = "Without a key, idstack runs in demo mode: every audit shows the same sample findings, whatever the page says, and the page's text is not sent anywhere." +old = "Without a key, idstack runs in demo mode. Each audit then shows the same sample findings for all pages. In demo mode, idstack does not send the page text out of your browser." assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) s = s.replace(old, "Leave blank to use the built-in free demo tier.", 1) open(p, 'w', encoding='utf-8').write(s) @@ -1758,6 +1758,61 @@ open(p, 'w', encoding='utf-8').write(s) PY expect_fail "course prompt drops the STE rules" "$WORK/r/test/test-extension.sh" +# 46a-46c. The extension's fixed text obeys ASD-STE100. smoke-test sends the printed +# extension output and the side-panel page through bin/idstack-ste-check. Only the +# STE checks catch 46a and 46b: every other suite still passes. +# 46a. The Markdown export labels a recommendation in non-STE text again -> smoke-test must fail. +fresh +python3 - "$WORK/r/extension/shared/dossier-compiler.js" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = " md += `- **Recommendation:** ${f.recommendation || ''}\\n\\n`;\n });" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, " md += `- **Actionable Recommendation:** ${f.recommendation || ''}; consider it before you publish.\\n\\n`;\n });", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the Markdown export writes non-STE text" "$WORK/r/test/smoke-test.sh" "$WORK/r" + +# 46b. The side-panel privacy note says "may" again -> smoke-test must fail. +fresh +python3 - "$WORK/r/extension/sidepanel/index.html" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "Google can use this content to improve its products." +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "Google may use this content to improve its products.", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the side-panel privacy note says may" "$WORK/r/test/smoke-test.sh" "$WORK/r" + +# 46c. An STE rewording drops a refused page from the side-panel privacy note -> +# test-extension must fail. test-disclosures checks the extractor only for the pages +# that the note names, so without its name check the dropped page passes silently. +fresh +python3 - "$WORK/r/extension/sidepanel/index.html" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "Gradebook, SpeedGrader, People" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "Gradebook, People", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "the privacy note drops SpeedGrader" "$WORK/r/test/test-extension.sh" + +# 46d. A side-panel loader step that no test runs gets non-STE text again -> +# smoke-test must fail. The printer also prints the fixed string literals of the +# side panel and the service worker, so text on a path that it does not run is checked. +fresh +python3 - "$WORK/r/extension/sidepanel/sidepanel.js" <<'PY' +import sys +p = sys.argv[1]; s = open(p, encoding='utf-8').read() +old = "'idstack reads the assignments...'" +assert s.count(old) == 1, 'anchor not unique: %d' % s.count(old) +s = s.replace(old, "'Fetching assignments; please wait...'", 1) +open(p, 'w', encoding='utf-8').write(s) +PY +expect_fail "a side-panel loader step writes non-STE text" "$WORK/r/test/smoke-test.sh" "$WORK/r" + echo "" echo "guarded: $pass NOT guarded: $fail skipped: $skip" [ "$fail" -eq 0 ] diff --git a/test/print-extension-output.mjs b/test/print-extension-output.mjs new file mode 100644 index 0000000..a6f37f9 --- /dev/null +++ b/test/print-extension-output.mjs @@ -0,0 +1,125 @@ +// Print the fixed text that the shipped extension shows, so that smoke-test can +// send it through bin/idstack-ste-check. Usage: node test/print-extension-output.mjs markdown|html +// The name does not start with "test-", so test/test-extension.sh does not run it as a test. +import { getDemoAuditResult, getDemoCourseAuditResult } from '../extension/background/parser-helper.js'; +import { autoCorrectNeuromyth } from '../extension/shared/consensus-client.js'; +import { compileSingleAuditToMarkdown, compileDossierToMarkdown } from '../extension/shared/dossier-compiler.js'; +import { renderAuditHTML, renderDossierListHTML } from '../extension/sidepanel/renderer-helper.js'; +import { crawlCanvasCourse } from '../extension/background/canvas-crawler.js'; +import { saveSettings } from '../extension/shared/storage.js'; +import fs from 'node:fs'; +import { installChrome, installDocument, installFetch, jsonResponse, importFresh, loadContentScript, createDocument, flush, resetGlobals } from './extension-harness.mjs'; + +const MYTHS = ['learning_styles', 'hemisphere_learning', 'ten_percent_brain', 'dales_cone_percentages']; + +const page = getDemoAuditResult({ title: 'Lab 1', pageType: 'Canvas Assignment' }); +const course = getDemoCourseAuditResult({ title: 'Biology 101' }); +const corrected = { + summary: { bloomsLevel: 'Apply (Level 3)', alignmentScore: 'Moderate (70%)', keyTakeaway: 'The page has a neuromyth.' }, + findings: MYTHS.map((type) => autoCorrectNeuromyth({ + severity: 'warning', tier: 'T1', citation: '[Learner-1] Learning styles', + observation: 'The page has a neuromyth.', evidence: '', recommendation: '', + }, type)), +}; +// A partial model reply: every fallback label shows. +const sparse = { summary: {}, findings: [{}] }; +const items = [page, course, corrected].map((result, i) => ({ + id: `item-${i}`, title: `Item ${i + 1}`, pageType: 'Canvas Assignment', + url: 'https://canvas.example.edu/courses/1', timestamp: '2026-01-01T00:00:00Z', result, +})).concat([{ id: 'item-sparse', timestamp: '2026-01-01T00:00:00Z', result: sparse }]); + +// The error messages that the side panel shows in its error card. +async function errorMessages() { + const out = []; + const reject = async (promise) => { try { await promise; } catch (e) { out.push(e.message); } }; + const canvas = (course, assignments) => async (url) => { + const body = url.includes('/assignments') ? assignments : course; + return typeof body === 'number' ? { ok: false, status: body } : { ok: true, status: 200, headers: { get: () => null }, json: async () => body }; + }; + await reject(crawlCanvasCourse(null, '1')); + await reject(crawlCanvasCourse('https://canvas.test', '1', canvas(404, []))); + await reject(crawlCanvasCourse('https://canvas.test', '1', canvas({}, 403))); + // Every page links to a next page, so the crawl reaches its page cap. + await reject(crawlCanvasCourse('https://canvas.test', '1', async (url) => ({ ok: true, status: 200, headers: { get: () => `<${url}&n>; rel="next"` }, json: async () => [{}] }))); + + const { extractContentFromDOM, extractPageContent } = loadContentScript('content/extractor.js'); + out.push(extractContentFromDOM(createDocument(''), 'https://canvas.test/courses/1/gradebook').emptyReason); + globalThis.window = { location: { href: 'https://docs.google.com/document/d/abc/edit' } }; + globalThis.document = createDocument(''); + installFetch(async () => ({ ok: false, status: 403, headers: { get: () => '' } })); + out.push((await extractPageContent()).emptyReason); + + const h = installChrome(); + await importFresh('background/service-worker.js'); + const payload = { title: 'T', content: 'one two three four five six seven eight nine ten eleven' }; + out.push((await h.dispatch({ action: 'RUN_AUDIT', payload: { ...payload, content: '' } })).error); + out.push((await h.dispatch({ action: 'CRAWL_AND_AUDIT_COURSE', payload: { origin: 'http://x', courseId: '1' } })).error); + installFetch(async (url) => jsonResponse(url.includes('/assignments') ? [] : { name: 'C', syllabus_body: '' })); + out.push((await h.dispatch({ action: 'CRAWL_AND_AUDIT_COURSE', payload: { origin: 'https://canvas.test', courseId: '1' } })).error); + await saveSettings({ apiKey: 'k' }); + installFetch(async () => jsonResponse({ candidates: [{ content: { parts: [{ text: '{"findings": null}' }] } }] })); + out.push((await h.dispatch({ action: 'RUN_AUDIT', payload })).error); + // A model call that times out. + const timeout = AbortSignal.timeout; + AbortSignal.timeout = () => AbortSignal.abort(); + installFetch(async () => { throw new DOMException('aborted', 'AbortError'); }); + out.push((await h.dispatch({ action: 'RUN_AUDIT', payload })).error); + AbortSignal.timeout = timeout; + // A model call that fails (the body is a neutral fixture, not a real reply) and one that sends no text. + installFetch(async () => ({ ok: false, status: 429, text: async () => '{}' })); + out.push((await h.dispatch({ action: 'RUN_AUDIT', payload })).error); + installFetch(async () => jsonResponse({ candidates: [] })); + out.push((await h.dispatch({ action: 'RUN_AUDIT', payload })).error); + resetGlobals(); + // A path that stops giving its message must fail here, not print "undefined". + if (out.length !== 13 || !out.every(Boolean)) throw new Error(`expected 13 error messages, got ${JSON.stringify(out)}`); + return out; +} + +// The side panel on a tab that it cannot read: the page title, the loading text +// and the error card that shows the reason. +async function sidePanelText() { + const document = installDocument(); + let reply; + const h = installChrome({ tabs: [{ id: 1, active: true }], onRuntimeMessage: () => new Promise((resolve) => { reply = resolve; }) }); + await importFresh('sidepanel/sidepanel.js'); + await flush(); + document.getElementById('audit-btn').click(); + await flush(); + const out = [`

        ${document.getElementById('page-title').textContent}

        `, `

        ${document.getElementById('loader-status').textContent}

        `]; + reply({ success: false, error: h.sent[0].payload.emptyReason }); + await flush(); + const card = document.querySelectorAll('.error-card h4, .error-card p, .error-card button'); + if (card.length !== 4) throw new Error(`expected the error card heading, message and 2 buttons, got ${card.length} elements`); + for (const el of card) { + out.push(`<${el.tagName.toLowerCase()}>${el.textContent}`); + } + resetGlobals(); + return out; +} + +const mode = process.argv[2]; +if (mode === 'markdown') { + // The export puts idstack's own header lines and notes in '>' blocks. The checker skips those as quotations. + // Remove the marker and start a new paragraph, so that the checker reads each line. + const unquote = (md) => md.replace(/^> ?/gm, '\n'); + console.log(unquote(items.map(compileSingleAuditToMarkdown).join('\n\n'))); + console.log(unquote(compileDossierToMarkdown(items, 'Biology 101'))); + console.log(unquote(compileDossierToMarkdown([], 'Biology 101'))); +} else if (mode === 'html') { + console.log([page, course, corrected, sparse].map(renderAuditHTML).join('\n')); + console.log(renderDossierListHTML(items)); + console.log(renderDossierListHTML([])); + for (const message of await errorMessages()) console.log(`

        ${message}

        `); + console.log((await sidePanelText()).join('\n')); + // The fixed strings that the side panel and service worker show only on paths + // that the code above does not run: button labels, loader steps and fallbacks. + const LITERAL = /(?:textContent = |innerHTML = |renderError\(|new Error\(|\|\| )(['"`])((?:(?!\1)[^\\]|\\.)+)\1/g; + for (const f of ['../extension/sidepanel/sidepanel.js', '../extension/background/service-worker.js']) { + const src = fs.readFileSync(new URL(f, import.meta.url), 'utf8'); + for (const m of src.matchAll(LITERAL)) console.log(`

        ${m[2].replace(/\\(.)/g, '$1')}

        `); + } +} else { + console.error('usage: node test/print-extension-output.mjs markdown|html'); + process.exit(2); +} diff --git a/test/smoke-test.sh b/test/smoke-test.sh index ea6f723..d9c186f 100755 --- a/test/smoke-test.sh +++ b/test/smoke-test.sh @@ -362,10 +362,21 @@ if command -v node &>/dev/null; then # targets, column counts) rather than the CSS text. Skips loudly without a browser; the # text suite above still runs. See test/test-rendered-landing.js for why both exist. check "rendered landing page tests pass" "node '$IDSTACK_DIR/test/test-rendered-landing.js'" + # The fixed text that the extension shows must obey ASD-STE100: the demo audits, + # the Markdown export, the rendered findings, the error messages and the side-panel + # page. pipefail: a printer that crashes must fail here, not send empty text. + if command -v python3 &>/dev/null; then + check "extension Markdown output passes the STE checker" "( set -o pipefail; node '$IDSTACK_DIR/test/print-extension-output.mjs' markdown | python3 '$IDSTACK_DIR/bin/idstack-ste-check' --format markdown - )" + check "extension HTML output passes the STE checker" "( set -o pipefail; node '$IDSTACK_DIR/test/print-extension-output.mjs' html | python3 '$IDSTACK_DIR/bin/idstack-ste-check' --format html - )" + check "side-panel page passes the STE checker" "python3 '$IDSTACK_DIR/bin/idstack-ste-check' '$IDSTACK_DIR/extension/sidepanel/index.html'" + else + echo " SKIP: extension STE checks (python3 not installed)" + fi else echo " SKIP: chrome extension tests (node not installed)" echo " SKIP: responsive landing page tests (node not installed)" echo " SKIP: rendered landing page tests (node not installed)" + echo " SKIP: extension STE checks (node not installed)" fi # Check generated files have auto-generated header diff --git a/test/test-crawler.mjs b/test/test-crawler.mjs index 54abf65..875d803 100644 --- a/test/test-crawler.mjs +++ b/test/test-crawler.mjs @@ -60,7 +60,7 @@ assert.strictEqual(stripHtml('

        Hello World & Students
        [COURSE_URL]: COURSE, [PAGE1_URL]: { status: 403, body: { errors: [{ message: 'user not authorized to perform that action' }] } } }); - await assert.rejects(crawlCanvasCourse(ORIGIN, '12345', fetchImpl), /Failed to fetch Canvas assignments \(403\)/); + await assert.rejects(crawlCanvasCourse(ORIGIN, '12345', fetchImpl), /cannot get the Canvas assignments \(403\)/); } // Test 4: a network failure on page 2 is fatal, not a silent 50-item list @@ -89,15 +89,15 @@ assert.strictEqual(stripHtml('

        Hello World & Students
        // Test 6: missing origin or course id is refused before any request { const { fetchImpl, calls } = canvasFetch({}); - await assert.rejects(crawlCanvasCourse(null, '12345', fetchImpl), /Canvas origin and courseId are required/); - await assert.rejects(crawlCanvasCourse(ORIGIN, null, fetchImpl), /Canvas origin and courseId are required/); + await assert.rejects(crawlCanvasCourse(null, '12345', fetchImpl), /must have a Canvas origin and a courseId/); + await assert.rejects(crawlCanvasCourse(ORIGIN, null, fetchImpl), /must have a Canvas origin and a courseId/); assert.strictEqual(calls.length, 0); } // Test 7: a failed course fetch is fatal { const { fetchImpl } = canvasFetch({ [COURSE_URL]: { status: 404, body: { errors: [{ message: 'The specified resource does not exist.' }] } } }); - await assert.rejects(crawlCanvasCourse(ORIGIN, '12345', fetchImpl), /Failed to fetch Canvas course info \(404\)/); + await assert.rejects(crawlCanvasCourse(ORIGIN, '12345', fetchImpl), /cannot get the Canvas course information \(404\)/); } // Test 8: Demo Course Audit Fallback diff --git a/test/test-disclosures.mjs b/test/test-disclosures.mjs index 7630fd9..f1e394b 100644 --- a/test/test-disclosures.mjs +++ b/test/test-disclosures.mjs @@ -88,7 +88,7 @@ check(note, 'index.html has no privacy note'); const at = privacy.indexOf('## idstack Chrome Extension'); check(at >= 0, 'PRIVACY.md has no "## idstack Chrome Extension" section'); const policy = at >= 0 ? privacy.slice(at) : ''; -for (const [word, url] of [ +const REFUSED = [ [/Gradebook/, 'https://canvas.example.edu/courses/1/gradebook'], [/SpeedGrader/, 'https://canvas.example.edu/courses/1/gradebook/speed_grader?assignment_id=1'], [/\bgrades\b/, 'https://canvas.example.edu/courses/1/grades/7'], @@ -98,7 +98,11 @@ for (const [word, url] of [ [/\bsubmissions\b/, 'https://canvas.example.edu/courses/1/assignments/5/submissions/7'], [/Inbox/, 'https://canvas.example.edu/conversations'], [/\bdiscussion/i, 'https://canvas.example.edu/courses/1/discussion_topics/1'], -]) { +]; +// A rewording of the side-panel note must not drop a refused page: the check +// below runs only for the pages that the note names, so a dropped name would pass. +for (const [word] of REFUSED) check(word.test(note || ''), `side-panel privacy note no longer names ${word.source} pages as not read`); +for (const [word, url] of REFUSED) { for (const [name, text] of [['side-panel privacy note', note || ''], ['PRIVACY.md', policy]]) { if (!word.test(text)) continue; const r = extractContentFromDOM(doc, url); @@ -117,5 +121,10 @@ if (html.includes('id="consensus-api-key-input"')) { check(/Consensus/.test(note || ''), 'side-panel privacy note does not say what a saved Consensus key sends to Consensus'); } +// 8. A button the docs tell people to click must be a button the panel shows. +const shown = html.replace(/<[^>]+>/g, ' '); +const named = [...privacy.matchAll(/\*\*([A-Z][^*]*[^.:*])\*\*/g), ...readme.matchAll(/\*\*"([^"*]+)"\*\*/g)].map((m) => m[1]); +for (const label of new Set(named)) check(shown.includes(label), `PRIVACY.md or README.md names a "${label}" button that extension/sidepanel/index.html does not show`); + assert.ok(problems.size === 0, `Extension disclosures disagree with the shipped code:\n ${[...problems].join('\n ')}`); console.log('✅ Extension privacy and capability disclosures match the shipped code.'); diff --git a/test/test-dossier-compiler.mjs b/test/test-dossier-compiler.mjs index 7a3b38c..545c391 100644 --- a/test/test-dossier-compiler.mjs +++ b/test/test-dossier-compiler.mjs @@ -80,8 +80,8 @@ assert.ok(dossierMd.includes('Biggs (1996)')); assert.strictEqual(compileSingleAuditToMarkdown(null), ''); assert.strictEqual(compileSingleAuditToMarkdown({}), ''); const emptyDossierMd = compileDossierToMarkdown([], 'Empty Course'); -assert.ok(emptyDossierMd.includes('*No audit materials in dossier.*')); +assert.ok(emptyDossierMd.includes('*The dossier has no audits.*')); const nullDossierMd = compileDossierToMarkdown(null); -assert.ok(nullDossierMd.includes('*No audit materials in dossier.*')); +assert.ok(nullDossierMd.includes('*The dossier has no audits.*')); console.log('✅ Task 1 Dossier compiler tests passed.'); diff --git a/test/test-renderer-helper.mjs b/test/test-renderer-helper.mjs index eb93548..ec889e1 100644 --- a/test/test-renderer-helper.mjs +++ b/test/test-renderer-helper.mjs @@ -40,7 +40,7 @@ assert.ok(rendered.includes('Strong'), "Must render alignment score"); assert.ok(rendered.includes('Great constructive alignment between rubric and lab analysis.'), "Must render key takeaway"); // Check findings count & citations -assert.ok(rendered.includes('Evidence-Based Findings (2)'), 'Must render correct findings count heading'); +assert.ok(rendered.includes('Findings with Evidence (2)'), 'Must render correct findings count heading'); assert.ok(rendered.includes('[Assessment-8]'), 'Must render citation for finding 1'); assert.ok(rendered.includes('[Alignment-3]'), 'Must render citation for finding 2'); @@ -78,7 +78,7 @@ const emptyData = { } }; const emptyRendered = renderAuditHTML(emptyData); -assert.ok(emptyRendered.includes('Evidence-Based Findings (0)'), 'Must handle 0 findings gracefully'); +assert.ok(emptyRendered.includes('Findings with Evidence (0)'), 'Must handle 0 findings gracefully'); assert.ok(emptyRendered.includes('Remember'), 'Must render Bloom\'s level for empty findings data'); // Test 3: Null / undefined resilience @@ -95,7 +95,7 @@ const partialData = { }; const partialRendered = renderAuditHTML(partialData); assert.ok(partialRendered.includes('Evaluate'), 'Must handle missing findings and improvedDraft'); -assert.ok(partialRendered.includes('Evidence-Based Findings (0)'), 'Must default findings to 0'); +assert.ok(partialRendered.includes('Findings with Evidence (0)'), 'Must default findings to 0'); // Test 5: escapeHtml unit tests assert.strictEqual(escapeHtml(''), '<script>alert("xss")</script>'); @@ -156,9 +156,9 @@ assert.ok(listHtml.includes('Assignment'), 'Must render item page type'); assert.ok(listHtml.includes('data-dossier-id="1"'), 'Must include data-dossier-id attribute'); // Empty and edge cases -assert.ok(renderDossierListHTML([]).includes('No items in dossier yet'), 'Must handle empty array gracefully'); -assert.ok(renderDossierListHTML(null).includes('No items in dossier yet'), 'Must handle null gracefully'); -assert.ok(renderDossierListHTML(undefined).includes('No items in dossier yet'), 'Must handle undefined gracefully'); +assert.ok(renderDossierListHTML([]).includes('The dossier is empty.'), 'Must handle empty array gracefully'); +assert.ok(renderDossierListHTML(null).includes('The dossier is empty.'), 'Must handle null gracefully'); +assert.ok(renderDossierListHTML(undefined).includes('The dossier is empty.'), 'Must handle undefined gracefully'); // HTML Entity Escaping in Dossier List const unsafeDossier = [ @@ -176,9 +176,9 @@ assert.ok(escapedDossierHtml.includes('<script>alert(1)</script>'), // Test 9: malformed model JSON renders instead of throwing (F9). The panel // renders inside a runtime.sendMessage callback, so a throw leaves the spinner up. -assert.ok(renderAuditHTML({ findings: { 0: { tier: 'T1' } } }).includes('Evidence-Based Findings (0)'), 'non-array findings render as none'); +assert.ok(renderAuditHTML({ findings: { 0: { tier: 'T1' } } }).includes('Findings with Evidence (0)'), 'non-array findings render as none'); const junk = renderAuditHTML({ findings: [null, 'oops', { tier: 'T5', citation: '[Alignment-1]' }] }); -assert.ok(junk.includes('Evidence-Based Findings (1)'), 'null and non-object findings are dropped'); +assert.ok(junk.includes('Findings with Evidence (1)'), 'null and non-object findings are dropped'); assert.ok(junk.includes('[Alignment-1]'), 'a valid finding next to junk still renders'); assert.ok(renderAuditHTML({ findings: [{ tier: 1, citation: '[CogLoad-1]' }] }).includes('tier-badge tier-1'), 'a non-string tier is stringified'); @@ -221,7 +221,7 @@ const consensusData = { const renderedConsensus = renderAuditHTML(consensusData); assert.ok(renderedConsensus.includes('consensus-badge'), 'Must render consensus-badge class'); assert.ok(renderedConsensus.includes('✓ 88% Consensus (34 papers)'), 'Must render consensus meter and paper count'); -assert.ok(renderedConsensus.includes('✓ Consensus Verified'), 'Must render default consensus verified text when meter absent'); +assert.ok(renderedConsensus.includes('✓ Found on Consensus'), 'Must render the default Consensus badge text when meter absent'); assert.ok(renderedConsensus.includes('href="https://doi.org/10.1016/j.edurev.2019.100309"'), 'Must render clickable DOI paper URL link'); assert.ok(renderedConsensus.includes('target="_blank"'), 'Must include target="_blank" for DOI link'); assert.ok(renderedConsensus.includes('rel="noopener noreferrer"'), 'Must include rel="noopener noreferrer" for DOI link'); diff --git a/test/test-service-worker.mjs b/test/test-service-worker.mjs index 8d1251c..038387f 100644 --- a/test/test-service-worker.mjs +++ b/test/test-service-worker.mjs @@ -84,7 +84,7 @@ const modelCalls = (h) => h.fetchCalls.filter((c) => new URL(c.url).host === 'ge // Modules item title never reaches the model, with or without a key. for (const [content, apiKey] of [['', 'k'], [' \n ', undefined], ['Week 1 Reading', 'k']]) { h = await loadWorker({ apiKey, fetchHandler: async () => llmReply(VALID) }); - await assertRejected(await h.dispatch({ action: 'RUN_AUDIT', payload: { ...payload, content } }), /too little readable text/); + await assertRejected(await h.dispatch({ action: 'RUN_AUDIT', payload: { ...payload, content } }), /does not have sufficient text/); assert.strictEqual(h.fetchCalls.length, 0, `page content ${JSON.stringify(content)} must not be sent to the model`); } @@ -124,7 +124,7 @@ assert.ok(call.init.signal instanceof AbortSignal && !call.init.signal.aborted, // rejects it with AbortError, so the worker must check the signal, not the name. h = await loadWorker({ apiKey: 'k', fetchHandler: async () => { throw new DOMException('The user aborted a request.', 'AbortError'); } }); timeouts.fire = true; -await assertRejected(await h.dispatch({ action: 'RUN_AUDIT', payload }), /did not respond within 25 seconds/); +await assertRejected(await h.dispatch({ action: 'RUN_AUDIT', payload }), /did not send a result in 25 seconds/); // Control: any other network error passes through unchanged. h = await loadWorker({ apiKey: 'k', fetchHandler: async () => { throw new TypeError('Failed to fetch'); } }); await assertRejected(await h.dispatch({ action: 'RUN_AUDIT', payload }), /^Failed to fetch$/); diff --git a/test/test-sidepanel-state.mjs b/test/test-sidepanel-state.mjs index 60a404e..1bd493f 100644 --- a/test/test-sidepanel-state.mjs +++ b/test/test-sidepanel-state.mjs @@ -119,7 +119,7 @@ const scenarios = { const p = await loadPanel({ tab: { id: 1, url }, payloads: { 1: page('Home', url, 'Canvas LMS Page') } }); await p.$('audit-course-btn').click(); await flush(); - await p.reply({ success: false, error: 'Failed to fetch Canvas course info (401)' }); + await p.reply({ success: false, error: 'idstack cannot get the Canvas course information (401).' }); await p.$('error-retry-btn').click(); await flush(); assert.strictEqual(p.h.sent.filter((m) => m.action === 'CRAWL_AND_AUDIT_COURSE').length, 2, 'Retry sends the course crawl again'); @@ -132,7 +132,7 @@ const scenarios = { const p = await loadPanel({ tab: { id: 1, url }, payloads: { 1: page('Home', url, 'Canvas LMS Page'), 2: page('Blog', blog, 'Web Page') } }); await p.$('audit-course-btn').click(); await flush(); - await p.reply({ success: false, error: 'Failed to fetch Canvas course info (401)' }); + await p.reply({ success: false, error: 'idstack cannot get the Canvas course information (401).' }); await p.switchTab({ id: 2, url: blog }); await p.$('error-retry-btn').click(); await flush(); @@ -147,7 +147,7 @@ const scenarios = { const p = await loadPanel({ tab: { id: 1, url: a }, payloads: { 1: page('Course A', a, 'Canvas LMS Page'), 2: page('Course B', b, 'Canvas LMS Page') } }); await p.$('audit-course-btn').click(); await flush(); - await p.reply({ success: false, error: 'Failed to fetch Canvas course info (401)' }); + await p.reply({ success: false, error: 'idstack cannot get the Canvas course information (401).' }); await p.switchTab({ id: 2, url: b }); assert.notStrictEqual(p.$('audit-course-btn').style.display, 'none', 'course B shows its own course button'); await p.$('error-retry-btn').click(); @@ -163,7 +163,7 @@ const scenarios = { const p = await loadPanel({ tab: { id: 1, url: root }, payloads: { 1: page('Home', root, 'Canvas LMS Page'), 2: page('Lab 9', sub) } }); await p.$('audit-course-btn').click(); await flush(); - await p.reply({ success: false, error: 'Failed to fetch Canvas course info (401)' }); + await p.reply({ success: false, error: 'idstack cannot get the Canvas course information (401).' }); await p.switchTab({ id: 2, url: sub }); await p.$('error-retry-btn').click(); await flush(); @@ -172,7 +172,7 @@ const scenarios = { assert.strictEqual(crawls[1].payload.courseId, '123'); }, - async 'a feedback vote keeps the Audit Another Page button (F14)'() { + async 'a feedback vote keeps the Audit a Different Page button (F14)'() { const p = await loadPanel({ tab: { id: 1, url: A.url }, payloads: { 1: A } }); await p.$('audit-btn').click(); await p.reply({ success: true, data: RESULT }); @@ -189,7 +189,7 @@ const scenarios = { const btn = p.$('copy-improved-btn'); await btn.click(); await flush(); - assert.strictEqual(btn.textContent, 'Copy failed'); + assert.strictEqual(btn.textContent, 'Not copied'); }, async 'Copy Markdown reports a rejected write without rejecting the handler (F14)'() { @@ -199,7 +199,7 @@ const scenarios = { await btn.click(); await flush(); assert.strictEqual(clickErrors.length, before, 'click handler must not reject'); - assert.strictEqual(btn.textContent, 'Copy failed'); + assert.strictEqual(btn.textContent, 'Not copied'); }, async 'malformed findings still reach the results state (F9)'() { From 4597ffa5106f97bf5d9fada25fbbd58b58211706 Mon Sep 17 00:00:00 2001 From: Philippos Savvides Date: Sat, 3 Oct 2026 16:04:09 -0700 Subject: [PATCH 08/10] docs: describe the ASD-STE100 writing standard for contributors Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 28 ++++++++++++++++++++-------- CONTRIBUTING.md | 11 +++++++---- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 51d089e..9793030 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,20 +21,21 @@ bin/idstack-migrate # Migrate an existing .idstack/projec bin/idstack-migrate --init # Also create a canonical manifest when none exists (standalone skill runs) bin/idstack-manifest-merge --section --payload # Canonical manifest write path (atomic, section-scoped) bin/idstack-slugify "" # Derive the used for .idstack/exports/ +bin/idstack-ste-check # Find some ASD-STE100 writing-rule problems in idstack output bin/package-extension.sh # Package Chrome extension into build/ for Web Store distribution ``` Tests (run in CI on every push and PR — see `.github/workflows/test.yml`): ```bash -./test/smoke-test.sh # 365 assertions: install, SKILL.md freshness, frontmatter, version agreement, +./test/smoke-test.sh # 384 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 # 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 -./test/test-status.sh # 22 tests for bin/idstack-status, incl. the --readiness export gate +./test/test-setup.sh # 19 behavioral tests for ./setup (flags, scope, legacy cleanup, failure handling) +./test/test-doctor.sh # 14 behavioral tests for bin/idstack-doctor's PROBLEM/WARNING branches +./test/test-status.sh # 24 tests for bin/idstack-status, incl. the --readiness export gate ./test/test-manifest-merge.sh # bin/idstack-manifest-merge unit tests ./test/test-version-classifier.sh # bin/lib/version-classify.sh unit tests ./test/test-plugin-status.sh # bin/lib/plugin-status.sh unit tests @@ -44,12 +45,13 @@ Tests (run in CI on every push and PR — see `.github/workflows/test.yml`): ./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/test-ste-check.py # bin/idstack-ste-check unit tests (smoke-test also runs them) 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 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. +`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 seventeen 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. The test, mutation and release jobs pin Node 22 for the extension and rendered-landing tests. @@ -105,9 +107,9 @@ Rules for writing the manifest: Rules for writing the report: -- Follow the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md` — observation → evidence → why-it-matters → suggestion, with severity (`critical|warning|info`) and evidence tier (`T1`–`T5`) on every finding. Use the CSS hooks the stylesheet styles: `

        `, ``, ``, `[Domain-N] [TN]`. +- Follow the **visual contract** in `templates/report.html.tmpl` and the **content contract** in `templates/report-format.md` — observation → evidence → why-it-matters → recommendation, with severity (`critical|warning|info`) and evidence tier (`T1`–`T5`) on every finding. Use the CSS hooks the stylesheet styles: `
        `, ``, ``, `[Domain-N] [TN]`. - Every skill that writes a report copies `$_IDSTACK/templates/assets/idstack.css` into `.idstack/exports//assets/idstack.css` so the folder is self-contained when zipped or moved. -- Phrase recommendations as suggestions ("consider…"), not directives. idstack is a collaborator. +- Write all report text in ASD-STE100, as the "Writing Standard (ASD-STE100)" section of `templates/preamble.md` tells. Write a recommendation as "idstack recommends that you …" or "You can …". Do not use "consider", "may", "should" or "suggest". idstack is a collaborator. - Cite every recommendation. Findings without a `[Domain-N] [Tier]` citation belong in *Limitations* or *Notes*, not *Findings*. ### SKILL.md.tmpl structure @@ -120,8 +122,9 @@ Every skill template follows this pattern: 4. **`{{MANIFEST_SCHEMA}}`** placeholder (replaced by `templates/manifest-schema.md`) 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`, 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. 6. **Timeline logging** (logs session data to `.idstack/timeline.jsonl` on completion) +7. **Writing standard check** — after each file that the skill writes for a person, a bash block runs `"$_IDSTACK/bin/idstack-ste-check"` on that file. smoke-test fails if a skill template has fewer check steps than it must have. A skill that starts a sub-agent puts a copy of the preamble's "Writing Standard (ASD-STE100)" section in the sub-agent prompt. -The shared preamble includes: interaction conventions (defines how skills use `AskUserQuestion`, `Agent`, and `Skill`), update check, manifest check, preferences check, designer profile check, and context recovery (reads timeline + learnings for welcome-back messages and pipeline guidance). +The shared preamble includes: interaction conventions (defines how skills use `AskUserQuestion`, `Agent`, and `Skill`), writing standard (the ASD-STE100 rules and word list), update check, manifest check, preferences check, designer profile check, and context recovery (reads timeline + learnings for welcome-back messages and pipeline guidance). 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. @@ -134,6 +137,15 @@ Logic used by more than one script — or that deserves a unit test — lives in Test the shipped file, never a copy. The version classifier drifted across three PRs while a mirrored copy in its test passed green. +### Writing standard (ASD-STE100) + +All text that idstack shows to a person follows the ASD-STE100 Simplified Technical English rules. This includes skill chat, reports, the dashboard, learner content, CLI messages and the Chrome extension. + +- The text between `` and `` in `templates/preamble.md` is the only source of the rules and the word list. `extension/shared/ste-rules.js` has a copy. `test/test-ste-rules.mjs` fails when the two are different. +- `bin/idstack-ste-check` finds the problems that a script can find. Each skill runs it on each file that the skill writes. The CLI suites, `test/smoke-test.sh` and `test/test-consensus-cli.py` run it on fixed output. +- Do not add the ASD-STE100 dictionary, or a list that comes from it, to the repo. The idstack word list has a maximum of 50 words. ASD does not endorse idstack, so do not write "STE-compliant" or "certified". +- The terms of instructional design and software are technical nouns and technical verbs. Do not add them to the word list. + ### Course memory 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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d50b6e..d4f9f1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ allowed-tools: The bare `name` field becomes the slash-command suffix: `name: course-import` is invoked as `/idstack:course-import`. The installed plugin handles the `idstack:` namespace; do not prefix the name yourself. -**2. `{{PREAMBLE}}` placeholder** — this is replaced by `bin/idstack-gen-skills` with the shared preamble (update check, manifest check, context recovery). +**2. `{{PREAMBLE}}` placeholder** — this is replaced by `bin/idstack-gen-skills` with the shared preamble (writing standard, update check, manifest check, context recovery). **3. Skill implementation** — the rest of the file is Markdown that defines the skill's workflow, decision trees, and outputs. @@ -54,6 +54,8 @@ The bare `name` field becomes the slash-command suffix: `name: course-import` is **6. Timeline logging** — at the end, a section that logs the session to `.idstack/timeline.jsonl` with skill-specific fields. +**7. Writing standard check** — after each file that the skill writes for a person, a bash block runs `"$_IDSTACK/bin/idstack-ste-check"` on that file. `test/smoke-test.sh` fails if a skill template has fewer check steps than it must have. A skill that starts a sub-agent puts a copy of the preamble's "Writing Standard (ASD-STE100)" section in the sub-agent prompt. + ### Template system Skills use a template system to share common preamble code. Edit `{skill}/SKILL.md.tmpl`, not `SKILL.md` directly. The generated `SKILL.md` files have an `` header. @@ -85,9 +87,9 @@ Point the test at the shipped file, never at a copy of the logic. The version cl Every skill that produces findings writes **both**: 1. **JSON section** in `.idstack/project.json` — system state for downstream skills, the pipeline orchestrator, and `bin/idstack-status`. -2. **HTML report** at `.idstack/exports//.html` — the branded, self-contained human view. Follow the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md`: observation → evidence → why-it-matters → suggestion, with severity (`critical|warning|info`) and evidence tier (`T1`–`T5`) on every finding. The skill writes the relative path back into its section's `report_path` field, and copies `templates/assets/idstack.css` into the course folder so the deliverable is self-contained when zipped. +2. **HTML report** at `.idstack/exports//.html` — the branded, self-contained human view. Follow the visual contract in `templates/report.html.tmpl` and the content contract in `templates/report-format.md`: observation → evidence → why-it-matters → recommendation, with severity (`critical|warning|info`) and evidence tier (`T1`–`T5`) on every finding. The skill writes the relative path back into its section's `report_path` field, and copies `templates/assets/idstack.css` into the course folder so the deliverable is self-contained when zipped. -Phrase recommendations as suggestions ("consider…"), not directives. Cite every recommendation with `[DomainCode-N] [Tier]`; uncited claims belong in *Limitations* or *Notes*, not *Findings*. +Write report text in ASD-STE100 (see the "Writing Standard (ASD-STE100)" section of `templates/preamble.md`). Write a recommendation as "idstack recommends that you …" or "You can …". Do not use "consider", "may", "should" or "suggest". Cite every recommendation with `[DomainCode-N] [Tier]`; uncited claims belong in *Limitations* or *Notes*, not *Findings*. ### Manifest-write rules @@ -131,7 +133,7 @@ Every suite below runs in CI (`.github/workflows/test.yml`) on every push and pu | Suite | Covers | |-------|--------| -| `test/smoke-test.sh` | Install, `SKILL.md` freshness, YAML frontmatter, version agreement across `VERSION` / `plugin.json` / `CHANGELOG.md`, canonical manifest section names, `/idstack:` namespacing, resolve-snippet lockstep, schema migrations, `bash -n` on every script | +| `test/smoke-test.sh` | Install, `SKILL.md` freshness, YAML frontmatter, version agreement across `VERSION` / `plugin.json` / `CHANGELOG.md`, canonical manifest section names, `/idstack:` namespacing, resolve-snippet lockstep, schema migrations, `bash -n` on every script, and the ASD-STE100 checks on the report templates and the extension output | | `test/integration-test.sh` | Behavioral tests across the `bin/` scripts; also asserts the suite leaves your working tree untouched | | `test/test-setup.sh` | `./setup` behavior — flag parsing, scope selection, all three legacy-cleanup shapes, failure handling. Runs against a repo copy with a fake `$HOME` and a stub `claude` | | `test/test-doctor.sh` | `bin/idstack-doctor` — every PROBLEM/WARNING branch and the exit contract. Runs against a repo copy with a pinned `PATH`, so "claude not found" means genuinely not found | @@ -144,6 +146,7 @@ Every suite below runs in CI (`.github/workflows/test.yml`) on every push and pu | `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 `