Skip to content

fix(author): a licence's version and a gap's marks are said where a chat reads, and what the build can prove wrong is a problem - #188

Merged
alekswebnet merged 3 commits into
mainfrom
fix/author-gap-and-licence-problems
Nov 12, 2026
Merged

alekswebnet merged 3 commits into
mainfrom
fix/author-gap-and-licence-problems

Conversation

@alekswebnet

@alekswebnet alekswebnet commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Closes #187.

Haiku with a shell and the skill built each of the 32 content types that need no media twice: 64 packages, all built, all passing h5p-verify, and four played wrong with no word from either. Two were a text's own marks: a Fill in the Blanks asked for the answers 1/2, 10:30 and km/h, where *10:30* is the answer 10 with the tip 30 and *km/h* takes km or h; and a Drag the Words with a colon in a gap's feedback, where the learner drags the word with its tip still in it. In the chat position, of 30 first replies on 0.9.3, none wrote the licence's version in its own field, and the build's own problem for it had every reply drop the version (#187 has the numbers).

What changes

  • SKILL.md, step 2, for the chat that has no type file:
    • a CC BY licence's version, CC BY-SA's and the others' too, is a field of its own, written where the user gave one: "CC BY 4.0" is "license": "CC BY", "licenseVersion": "4.0", and CC0 1.0 is one code, as it stands;
    • in a gap of H5P.Blanks / separates answers and : starts a tip, in a gap of H5P.DragText : starts a tip, and where one belongs to the answer itself it is written / or :. A backslash escapes nothing.
  • The two type files say it with their own examples: *km/h* takes km/h where *km/h* takes km or h; in Drag the Words : stands once in a gap, before the tip, and feedback holds none.
  • The licence's problem names the licence it read and where the version goes, the SPDX spelling too: license: "CC BY 4.0" holds the version too: write "license": "CC BY" and "licenseVersion": "4.0". A name that is no licence gets the list as before.
  • A backslash before / or : in a gap's answers is a problem, the gap written out as the library takes it: params.questions[0]: `*1\/2*`: a backslash escapes nothing in a gap, so H5P.Blanks takes "1\" or "2" here. In an answer write `/` as `/` and `:` as `:`: `*1/2*` . An answer that is a backslash and no more is left alone.
  • A gap of Drag the Words whose tip or feedback pattern matches twice is a problem, with the word as the library would leave it: params.textField: gap 1, `*hablo:primera persona\+Correcto: termina en -o.\-No: la fo…`: its feedback holds a `:`, where H5P.DragText ends the feedback and starts another tip, and it then leaves them in the word: the learner would drag "hablo:primera persona: termina en -o.: la forma es «hablo».". Write `:` once in a gap, before the tip, and none in the feedback. A colon in the tip itself is none: the tip runs on over it.
  • Both checks are layouts (LAYOUTS['H5P.Blanks'], LAYOUTS['H5P.DragText']), so they run wherever the type sits, in a Column or a Question Set too, and in checkContent on saved content. Each finds its gaps as its library does: handleBlanks and parseSolution of H5P.Blanks 1.14, parseText and lex of H5P.DragText 1.10.
  • design/author.md says why, and what was measured.

*10:30* as written is not refused. It is a tip to the library and may be one to its author, and a build cannot tell which. Nor is a Drag the Words gap with one colon after \+ or \- and no tip before it: played, *habla\+Bien.:tercera persona* has the feedback "Bien." and the tip "tercera persona", a tip written after the feedback, which the build cannot tell from a colon meant for the feedback.

cubic's review

  • A colon after \+ with no tip before it (P2, types.mjs): not refused, as above. Refusing it would refuse a source that plays as its author meant; the type file and SKILL.md say feedback holds no colon, and the layout's test now holds a tip written after the feedback to be taken.
  • CC0 1.0 is one code (P2 SKILL.md, P2 and P3 design/author.md): right. The sentence now names CC BY and its family and says CC0 1.0 is one code; the design note says the same; a test holds CC0 1.0 to build as it is, and "CC0" with "licenseVersion": "1.0" to two problems. Checked on Haiku in the chat position with the new sentence: 11 of 11 right: "CC0 1.0" kept whole 4 of 4, "CC BY 4.0" split 4 of 4, "CC BY-SA" with no version left without one 3 of 3.
  • CodeQL (js/incomplete-multi-character-sanitization, types.mjs): a Blanks gap's tags are taken out until none is left, where the library takes them out in one pass; the two differ only for a tag written inside a tag.

The entity

"Nothing escapes a gap's marks" is not so, which the first cut of this assumed: parseSolution decodes each answer's entities, and : is no colon as the gap is split. Played in the verifier's browser:

  • Fill in the Blanks with *10:30*, *km/h*, *1/2:a fraction* and *3:1/3 to 1:…*: the key is 10:30, km/h, 1/2 and 3:1 or "3 to 1"; those answers typed score 4 of 4; "10", "km" and "1" typed are wrong.
  • Drag the Words with the word *7:45* and : in feedback: the word is drawn 7:45 and scores, the feedback shows a colon, and no attribute or text in the frame holds the entity as written. The key in its xAPI statement does: 7:45.
  • Mark the Words with *10:30*, *km/h* and *1/2*: each is one word, and the right one. Neither character is a mark there, so the skill's line names the two other types only.

Tested

  • pnpm --filter @missing-elements/h5p-author test passes, 184 tests with the browser one, and so does the typecheck. The tags in a Blanks gap are taken out until none is left, where the library takes them out in one pass, for CodeQL's js/incomplete-multi-character-sanitization (a tag inside a tag is the only difference). New: the licence's four messages; the two checks' problems word for word, in a Column too; and what both must take, alternatives, a tip, an answer that is a backslash, a tip that holds a colon, the entities.

  • pnpm --filter @missing-elements/h5p-skills test passes, 77 tests: every type file is current with the generator, and every example builds and plays.

  • False alarms. The two checks over the 94 real packages of the owner's corpus and the demo, 53 of these two types in them with 266 gaps: nothing reported. Over the 524 distinct sources these tests stored, 199 of the two types with 765 gaps: only the 14 known to play wrong.

  • Haiku as a chat, the skill's text and no tool, each reply built with this branch, played, and the library's own key held against the request. First replies that wrote a source:

    0.9.3 This branch
    The licence's version in its own field 0 of 30 47 of 47
    Fill in the Blanks, the three answers taken as asked 0 of 10 23 of 23
    Drag the Words with its type file, its words whole 3 of 5 9 of 9
    Built at the first try 2 of 20 41 of 47
    Plays wrong once it builds 9 of 20 0 of the 41 that built

    0.9.3's column counts 30 replies for the licence, which SKILL.md alone decides, and for the rest the 20 written with 0.9.3's own files.

    • A licence the user skipped was U in 5 of 5. A licence named without a version got none in 5 of 5; with the version in SKILL.md's example, as first written, 3 of 7 added "4.0", which is why it is in the sentence and not the example.
    • Three Mark the Words kept a sentence with 10:30 and km/h in it word for word, its words marked as they stand.
    • The six that did not build at the first try, none of them for a gap or a licence: three Drag the Words without the type file, distractors written as a list twice and a field of the chat's own making once, and the three Mark the Words, for the task description the type requires.
  • The replies that had gone wrong, the build's problems pasted back in the build page's words: 8 of 8 right in one round, 4 Blanks with the answers asked for and 4 Drag the Words, and the licence right in all. With 0.9.3's problems, 1 of 12 gaps fixed and the version dropped in 12 of 12.

  • Where the build cannot prove it, a first reply with *10:30*: the type file handed back beside the licence's problem had 1 of 3 write the entities; the two others built and play wrong, as before.

  • The first twenty chats of the last round were started with another line than the earlier rounds' and 6 asked a question before writing; 0.9.3's text under that line did the same in 2 of 10, and this branch's text under the old line in 0 of 10. The counts above are of replies that wrote a source.

Not verified

  • A text edited afterwards in H5P's own editor: its rich-text widget may write an entity back as its character, and the gap would then be a tip again.
  • An LMS's report of a Drag the Words whose word holds an entity: the statement's key has it as written.

To release

h5p-author 0.9.4, a patch: no export added or removed. A source that built still builds unless it holds one of the two gaps, which played wrong. Then the site's pin moves to it, for /h5p/skill/ and the build page. Installed copies of the skill need npx skills update -g and a new upload to the Claude app.

…hat reads, and what the build can prove wrong is a problem

A licence written with its version, "CC BY 4.0", is a problem that names the
licence it read and where the version goes. A backslash before / or : in a
Fill in the Blanks gap is a problem with the gap written out as the library
takes it, the two characters as entities. A gap of Drag the Words whose tip or
feedback matches twice is a problem with the word the learner would drag.
SKILL.md and the two type files say the same before a source is written.

Closes #187
Comment thread packages/author/lib/types.mjs Fixed

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/author/lib/types.mjs">

<violation number="1" location="packages/author/lib/types.mjs:743">
P2: `*word\+Feedback:extra*` passes this guard because H5P finds only one `tips` match, yet its lexer treats `:extra` as the tip and truncates the correct feedback to `Feedback`. Detect a colon after `\+` or `\-` even when it is the only colon, and cover this case in the layout tests.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

const tips = part.match(/(:([^\\*]+))/g) ?? []
const right = part.match(/(\\\+([^\\*:]+))/g) ?? []
const wrong = part.match(/(\\-([^\\*:]+))/g) ?? []
if (tips.length < 2 && right.length < 2 && wrong.length < 2) continue

@cubic-dev-ai cubic-dev-ai Bot Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: *word\+Feedback:extra* passes this guard because H5P finds only one tips match, yet its lexer treats :extra as the tip and truncates the correct feedback to Feedback. Detect a colon after \+ or \- even when it is the only colon, and cover this case in the layout tests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/author/lib/types.mjs, line 743:

<comment>`*word\+Feedback:extra*` passes this guard because H5P finds only one `tips` match, yet its lexer treats `:extra` as the tip and truncates the correct feedback to `Feedback`. Detect a colon after `\+` or `\-` even when it is the only colon, and cover this case in the layout tests.</comment>

<file context>
@@ -642,6 +642,120 @@ const imageHotspotQuestion = {
+      const tips = part.match(/(:([^\\*]+))/g) ?? []
+      const right = part.match(/(\\\+([^\\*:]+))/g) ?? []
+      const wrong = part.match(/(\\-([^\\*:]+))/g) ?? []
+      if (tips.length < 2 && right.length < 2 && wrong.length < 2) continue
+      let word = part.slice(1, -1)
+      for (const matches of [tips, right, wrong]) if (matches.length) word = word.replace(String(matches), '')
</file context>
Fix with cubic

Comment thread design/author.md Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread skills/h5p-author/SKILL.md Resolved
Comment thread design/author.md Resolved
@alekswebnet alekswebnet self-assigned this Nov 12, 2026
@alekswebnet
alekswebnet merged commit de78bb6 into main Nov 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

h5p-author: a licence's version, an answer that holds / or :, and a colon in feedback go wrong with no word from the build

2 participants