feat(skills): add composition-structure block + soft-warn feedback lint

Extend the CLI feedback reproduction packet (#2498) with a fifth
mandated field, `COMPOSITION_STRUCTURE:`, and enforce presence of
`REPRO COMMAND:` / `COMPOSITION_STRUCTURE:` at feedback-submit time.

- Skill + reference now specify `COMPOSITION_STRUCTURE:` — a
  privacy-preserving structural anatomy (element census + attribute
  presence + timeline shape + delta + defect location) — required for
  any rating <=7 that describes a visual defect.
- `buildCompositionCensus()` + `renderCompositionCensusBlock()`
  auto-fill the block from composition HTML so agents don't ask the
  human user to hand-count `<video>` / `<img>` / sub-comp mounts.
  Counts + presence flags only — no file paths, no src URLs, no user
  text.
- `hyperframes feedback` soft-warns (never blocks) when a non-10
  `--comment` is missing `REPRO COMMAND:`, and when a rating-<=7
  visual-defect comment is missing `COMPOSITION_STRUCTURE:`. The
  warning points at the auto-census helper so agents remediate
  themselves.
- `coreSkillContent.test.ts` locks the new literal in both the skill
  and the reference file, following #2498's pattern.

Extends #2498. Follow-up: no change to `doctorSummary` generation, no
change to the feedback-submission API endpoint, no refactor of
#2498's doc-content Jest test.

Signed-off-by: Via
This commit is contained in:
Via
2026-07-17 02:58:11 +00:00
parent 9d148d288a
commit 0aaac7aa30
9 changed files with 619 additions and 2 deletions
@@ -35,6 +35,20 @@ describe("hyperframes-core contract docs", () => {
expect(renderReference).toContain("OUTCOME:");
expect(renderReference).toContain("WORKAROUND:");
});
it("mandates a composition-structure block for visual-defect feedback", () => {
const skill = read("skills", "hyperframes-cli", "SKILL.md");
const renderReference = read("skills", "hyperframes-cli", "references", "preview-render.md");
// Skill teaches the mandate at a high level.
expect(skill).toContain("COMPOSITION_STRUCTURE:");
// Reference carries the fillable block + agent-helper pointer.
expect(renderReference).toContain("COMPOSITION_STRUCTURE:");
expect(renderReference).toContain("elements: video=");
expect(renderReference).toContain("attributes:");
expect(renderReference).toContain("timeline:");
expect(renderReference).toContain("buildCompositionCensus");
});
});
describe("media-use TTS documentation", () => {
+8
View File
@@ -12,6 +12,7 @@ import { buildIssueUrl, HYPERFRAMES_REPO_URL } from "../utils/feedbackIssue.js";
import { VERSION } from "../version.js";
import { c } from "../ui/colors.js";
import { parseFeedbackRating } from "../utils/feedbackRating.js";
import { lintFeedbackComment } from "../utils/feedbackLint.js";
export const examples: Example[] = [
["Submit render feedback", 'hyperframes feedback --rating 8 --comment "fast but font missing"'],
@@ -156,6 +157,13 @@ export default defineCommand({
const comment = normalizeComment(args.comment);
const doctorSummary = await getDoctorSummary();
// Soft-warn (never blocks) when the comment for a non-clean report is
// missing the mandated reproduction-packet markers. Prints before the
// submission ack so the reporter sees the nudge while their run is fresh.
for (const warning of lintFeedbackComment({ rating, comment })) {
console.log(c.warn(`${warning.message}`));
}
// The standalone command runs separately from `render`, so it has no real
// elapsed time to report. Omit it rather than recording a fake duration.
trackRenderFeedback({ rating, comment, doctorSummary });