mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 01:56:04 +00:00
Addresses C1-C7 from Rames's adversarial review + N2/N3 nits:
- C1 (positionFixed): inline probe now value-scopes to 'fixed' — previously
fired on any position value (absolute, relative, sticky), producing a
false-positive anatomy that would mislead maintainers pattern-matching
the 'sub-comp + position:fixed capture' bug family.
- C2 (overflowHidden): symmetric fix — inline path now catches
style='overflow: hidden' via the value-scoped probe, matching the
<style>-tag branch. Also handles overflow-x/overflow-y variants.
- C3 (VISUAL_DEFECT_KEYWORDS): drop 'render' — CLI's primary command is
'hyperframes render', so build/perf/hang reports were triggering an
inappropriate COMPOSITION_STRUCTURE: nudge on the most common failure
mode. Rely on the more specific tokens (black, blank, flicker, corrupt,
wrong frame) to identify actual visual defects.
- C4 (mentionsVisualDefect): compile keywords into a word-bounded regex.
'blackboard', 'blanket', 'visualize', 'corruptible' no longer false-
positive. Accepted tradeoff: plural forms ('flickers') don't match.
- C5 (marker case-normalization): REPRO COMMAND: / COMPOSITION_STRUCTURE:
checks now case-insensitive, matching mentionsVisualDefect's
normalization. Reporters using 'Repro command:' or lowercase
'composition_structure:' get credit for compliance.
- C6 (background/mask shorthand): inline branch previously required the
longhand 'background-image:' / 'mask-image:' — style='background:
url(bg.png)' silently returned false. Now checks both longhand AND
shorthand-with-url() inline forms.
- C7 (usesGsap docstring): trim promise of data-gsap-* attribute scanning
that detectGsap never implemented — attribute scan lives outside the
<script>-only detection path.
- N2 (EMPTY_VALUES): include 'inherit', 'revert', 'revert-layer' — a
style='position: inherit' is authored intent to defer, not authored
intent to place.
- N3 (input size cap): early-exit to a zero census on HTML > 20 MB
rather than feeding linkedom a hostile input. Not expected in normal
usage; guard for future callers that might pass raw user uploads.
Extends the test locks: 6 new census tests (value-scoping, size cap) and
5 new lint tests (word-boundary rejections, render-noise rejections,
lowercase-marker acceptance). All existing tests unchanged in intent —
only the 'flickers' plural in one test updated to 'flicker' to reflect
the new word-boundary rule.
No behavior change to the wire path: lint is still soft-warn, census is
still never called from feedback.ts, no new dependencies.
128 lines
5.0 KiB
TypeScript
128 lines
5.0 KiB
TypeScript
import { FEEDBACK_RATING_SCALE } from "./feedbackRating.js";
|
|
|
|
/**
|
|
* Keywords that suggest the reporter is describing a visual defect (as opposed
|
|
* to a build failure, missing feature, or plain workflow friction). When these
|
|
* appear in a non-10 feedback comment, the reporter should include a
|
|
* `COMPOSITION_STRUCTURE:` block so maintainers can pattern-match against
|
|
* known bug families without receiving the composition ZIP. Matched
|
|
* case-insensitively, word-bounded against the raw comment (so "black" fires
|
|
* on "black frame" but not "blackboard" / "no black frame at all").
|
|
*
|
|
* `"render"` intentionally omitted: `hyperframes render` is the CLI's primary
|
|
* command, so build/perf/hang reports mention it constantly and would drown
|
|
* the structure warning in false positives. Rely on the more specific tokens
|
|
* ("black", "blank", "flicker", "corrupt", "wrong frame") to identify actual
|
|
* visual defects.
|
|
*/
|
|
export const VISUAL_DEFECT_KEYWORDS: readonly string[] = [
|
|
"black",
|
|
"flicker",
|
|
"corrupt",
|
|
"wrong frame",
|
|
"blank",
|
|
"visual",
|
|
] as const;
|
|
|
|
/**
|
|
* Ratings that should mandate `COMPOSITION_STRUCTURE:` when the comment
|
|
* contains a visual-defect keyword. 7 and below covers "clearly broken" —
|
|
* 8-9 are usually "worked, but noticed a nit" which doesn't need the full
|
|
* structural anatomy.
|
|
*/
|
|
export const COMPOSITION_STRUCTURE_RATING_CEILING = 7;
|
|
|
|
const REPRO_MARKER = "REPRO COMMAND:";
|
|
const STRUCTURE_MARKER = "COMPOSITION_STRUCTURE:";
|
|
|
|
export interface FeedbackLintInput {
|
|
rating: number;
|
|
comment: string | undefined;
|
|
}
|
|
|
|
export interface FeedbackLintWarning {
|
|
code: "missing-repro-command" | "missing-composition-structure";
|
|
message: string;
|
|
}
|
|
|
|
/**
|
|
* Soft-warn lint on the `hyperframes feedback` comment body. Never blocks
|
|
* submission — some legitimate reports (a one-line "cloudrun quota bumped
|
|
* yesterday, fine now") won't fit the mold. The warning is just a nudge and
|
|
* a pointer to the auto-census helper.
|
|
*
|
|
* Rules:
|
|
* 1. `rating === 10` — no check. A perfect run doesn't need a repro packet.
|
|
* 2. Comment missing / empty — no check. `feedback --rating 6` with no
|
|
* comment is a valid quick vote; the maintainer sees rating drift without
|
|
* the reporter having to synthesize a fake repro.
|
|
* 3. Comment present + rating < 10 + no `REPRO COMMAND:` — warn.
|
|
* 4. Comment present + rating ≤ 7 + visual-defect keyword + no
|
|
* `COMPOSITION_STRUCTURE:` — warn (in addition to any #3 warning).
|
|
*/
|
|
export function lintFeedbackComment(input: FeedbackLintInput): FeedbackLintWarning[] {
|
|
const { rating, comment } = input;
|
|
if (rating === FEEDBACK_RATING_SCALE) return [];
|
|
const trimmed = comment?.trim();
|
|
if (!trimmed) return [];
|
|
|
|
// Marker checks are case-insensitive to match `mentionsVisualDefect`'s
|
|
// normalization. A reporter who writes `Repro command:` shouldn't get warned
|
|
// for compliance just because they lowercased the marker.
|
|
const upperTrimmed = trimmed.toUpperCase();
|
|
const warnings: FeedbackLintWarning[] = [];
|
|
|
|
if (!upperTrimmed.includes(REPRO_MARKER)) {
|
|
warnings.push({
|
|
code: "missing-repro-command",
|
|
message: [
|
|
`Comment on a ${rating}/${FEEDBACK_RATING_SCALE} report is missing a "${REPRO_MARKER}" block —`,
|
|
"maintainers can't rerun the failure from a symptom summary alone.",
|
|
"See `references/preview-render.md` → feedback for the required packet shape.",
|
|
].join(" "),
|
|
});
|
|
}
|
|
|
|
if (
|
|
rating <= COMPOSITION_STRUCTURE_RATING_CEILING &&
|
|
mentionsVisualDefect(trimmed) &&
|
|
!upperTrimmed.includes(STRUCTURE_MARKER)
|
|
) {
|
|
warnings.push({
|
|
code: "missing-composition-structure",
|
|
message: [
|
|
`Comment describes a visual defect at ${rating}/${FEEDBACK_RATING_SCALE} but omits a`,
|
|
`"${STRUCTURE_MARKER}" block. Agents can auto-fill this via the composition-census helper`,
|
|
"(`buildCompositionCensus`/`renderCompositionCensusBlock` in `packages/cli/src/utils/compositionCensus.ts`)",
|
|
"so maintainers can pattern-match against known bug families without the composition ZIP.",
|
|
].join(" "),
|
|
});
|
|
}
|
|
|
|
return warnings;
|
|
}
|
|
|
|
// Compile once. Word-boundary at both sides prevents "black" matching
|
|
// "blackboard", "blank" matching "blanket", "visual" matching "visualize".
|
|
// Plural forms (e.g. "flickers") won't match — accepted tradeoff for a soft
|
|
// warn: false negatives skip the nudge, false positives waste the reporter's
|
|
// attention.
|
|
function escapeRegex(input: string): string {
|
|
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|
|
|
|
const VISUAL_DEFECT_REGEX = new RegExp(
|
|
`(^|[^A-Za-z0-9_])(?:${VISUAL_DEFECT_KEYWORDS.map(escapeRegex).join("|")})(?![A-Za-z0-9_])`,
|
|
"i",
|
|
);
|
|
|
|
/**
|
|
* Case-insensitive word-bounded probe against `VISUAL_DEFECT_KEYWORDS`.
|
|
* Exposed for tests and reuse. Word boundaries are enforced on both sides so
|
|
* partial-word false positives ("blackboard", "visualize", "corruptible")
|
|
* don't trigger the structure-block nudge.
|
|
*/
|
|
export function mentionsVisualDefect(comment: string): boolean {
|
|
return VISUAL_DEFECT_REGEX.test(comment);
|
|
}
|