From 57581470e4ab20160c66ed1b1076c1d9b8a98b3e Mon Sep 17 00:00:00 2001 From: DonislawDev Date: Tue, 3 Nov 2026 13:02:51 +0100 Subject: [PATCH 1/3] video: a film says it carries its label only when a picture shows it whole The square that steps across a film is drawn over the label, in the label's own ink. On a low, wide film it rides across the text, and a film of 1920x64 and one change drew it over the start of the label in its only picture while the manifest said label_embedded true. A film now says true when at least one of its pictures leaves the text of the label whole, and false with a label_omitted note when the square changes a pixel of the text in every picture. The answer is asked of the text's pixels (imagelabel.Hides): ink over the glyphs' own ink changes nothing, and the band around the text is not the text, the rule Readable keeps for the padding at the foot of a low picture. The square's rectangle has one definition (squareAt), used by both painters and by the answer. The pixels of every film are unchanged. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 13 +- internal/format/imagelabel/imagelabel.go | 49 +++++++ internal/format/video/choose.go | 29 +++- internal/format/video/picture.go | 26 +++- internal/format/video/plan.go | 7 + internal/guard/labelclaim_test.go | 172 +++++++++++++++++++++++ internal/guard/mutation/entries.py | 46 +++++- internal/gui/text/locale/said/en.json | 5 + internal/gui/text/locale/said/pl.json | 1 + 9 files changed, 332 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15012a23..7c2f0367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,12 +90,13 @@ because it turns other people's test suites red. rest without a word, and the set still expected such a workbook to be accepted, certain. Past that width `sheet.xlsx` is now `unspecified` for `count_limit`, like the wide CSV beside it. Up to it, nothing changes. -- **A picture too low for its label no longer says it carries one.** The - label is drawn as large as the width allows, so a low picture cuts it off, - and a BMP 512 px wide and 1 px tall said `label_embedded: true` with no - text in it at all. Every image format and both film formats now say - `false` with a `label_omitted` note when the text is not whole in the - picture. The pixels are the same as before. +- **A picture whose label cannot be read no longer says it carries one.** + The label is drawn as large as the width allows, so a low picture cuts it + off, and a BMP 512 px wide and 1 px tall said `label_embedded: true` with + no text in it. A low, wide film of one second drew its moving square over + the label. Every image format and both film formats now say `false` with a + `label_omitted` note when no picture shows the whole text. The pixels are + the same as before. - **`tfg cleanup --yes` keeps a file that changed after it was checked.** Cleanup checks every file before it removes any, and a file changed in that time was removed without `--force`. It now looks again just before it diff --git a/internal/format/imagelabel/imagelabel.go b/internal/format/imagelabel/imagelabel.go index 67fd17cc..114238d0 100644 --- a/internal/format/imagelabel/imagelabel.go +++ b/internal/format/imagelabel/imagelabel.go @@ -154,6 +154,55 @@ func CellsOf(width, chars int) Cells { return Cells{First: pad, Step: (glyphWidth + 1) * s, Width: glyphWidth * s} } +// TextRect is where Draw puts the text of a label of chars characters across +// width, in a picture tall enough to hold it: from the left edge of the first +// character to the right edge of the last, over the rows of the glyphs - the +// band's padding is not in it. The empty rectangle when Draw would draw +// nothing. A film asks it to know whether the square it draws over the label +// leaves the text whole (Hides). +func TextRect(width, chars int) image.Rectangle { + c := CellsOf(width, chars) + if c.Width == 0 { + return image.Rectangle{} + } + return image.Rect(c.First, pad, c.First+(chars-1)*c.Step+c.Width, textHeight(width, chars)) +} + +// Hides says whether r, painted in the label's ink over the text Draw puts +// across width, changes a pixel of that text - a pixel of the background +// inside it, between the glyphs or within them. Ink over the glyphs' own ink +// changes nothing, and the band around the text is not the text, the rule +// Readable keeps for the padding at the foot: a square over the last column +// of a 7 and the band beside it leaves the label as it was (measured on the +// pixels, 2026-11-03). +func Hides(width int, text string, r image.Rectangle) bool { + t := laidText{Cells: CellsOf(width, len(text)), scale: scaleFor(width, len(text)), runes: []rune(text)} + in := r.Intersect(TextRect(width, len(text))) + // Draw spaces the text by its bytes and draws its runes, so no glyph + // stands past the last rune. + in.Max.X = min(in.Max.X, t.First+(len(t.runes)-1)*t.Step+t.Width) + for i := range max(in.Dx(), 0) * in.Dy() { + if !t.ink(in.Min.X+i%in.Dx(), in.Min.Y+i/in.Dx()) { + return true + } + } + return false +} + +// laidText is a text where Draw lays it out, asked about its pixels. +type laidText struct { + Cells + scale int + runes []rune +} + +// ink says whether the pixel at x, y inside the text is the ink of a glyph, +// rather than the background between the glyphs or within one. +func (t laidText) ink(x, y int) bool { + k, off := (x-t.First)/t.Step, (x-t.First)%t.Step + return off < t.Width && glyph(t.runes[k])[(y-pad)/t.scale*glyphWidth+off/t.scale] == '#' +} + // BandHeight is how tall the band Draw paints for a label of chars characters // across width is, before it is cut to the picture - 0 when the label does not // fit. A film draws a second line under the first and asks this before it diff --git a/internal/format/video/choose.go b/internal/format/video/choose.go index 42642a84..5f2ffda9 100644 --- a/internal/format/video/choose.go +++ b/internal/format/video/choose.go @@ -73,15 +73,30 @@ type Choice struct { Named bool } -// Labelled is whether this picture carries its label where it can be read: -// drawn across its width, and whole in its height. The manifest says this, -// and the drawing asks the package's Labelled, which is the width alone - a -// low film draws its label and cuts it off (R14 of -// docs/STABILITY-REVIEW-2026-10-26.md, where the pixels stayed as they were). -func (c Choice) Labelled() bool { +// Labelled is whether this film carries its label where it can be read: +// drawn across its width, whole in its height, and whole in at least one of +// its pictures under the square. The manifest says this, and the drawing asks +// the package's Labelled, which is the width alone - a low film draws its +// label and cuts it off (R14 of docs/STABILITY-REVIEW-2026-10-26.md), and a +// low wide one of few changes draws the square over it (O288). The pixels +// stayed as they were both times. +func (c Choice) Labelled(t Timeline) bool { + return c.readable() && !c.squareOverLabel(t) +} + +// readable is whether the label is drawn and whole in the picture's height, +// before the square is drawn over it. +func (c Choice) readable() bool { return Labelled(c.Width, c.Label) && imagelabel.Readable(c.Width, c.Height, len(c.Label)) } +// squareOverLabel is whether the square crosses the text of a label drawn +// whole in every picture of the film - the one case the plan owes a note of +// its own for. +func (c Choice) squareOverLabel(t Timeline) bool { + return c.readable() && !geometryOf(c.Width, c.Height, c.Label, t).labelWholeIn(t.Changes(), c.Label) +} + // ClockShown is whether this film's pictures show the clock. func (c Choice) ClockShown(t Timeline) bool { return ClockShown(c.Width, c.Height, c.Label, t) } @@ -292,6 +307,6 @@ func Facts(c Choice, s Settings) map[string]any { "quality": s.Quality, "compression": "av1", "audio": false, - format.PropertyLabelEmbedded: c.Labelled(), + format.PropertyLabelEmbedded: c.Labelled(s.Timeline), } } diff --git a/internal/format/video/picture.go b/internal/format/video/picture.go index 4073a421..e5ab2c37 100644 --- a/internal/format/video/picture.go +++ b/internal/format/video/picture.go @@ -131,6 +131,28 @@ func (g geometry) squareX(c int64) int { return (g.width - g.side) * int(c%squareSteps) / (squareSteps - 1) } +// squareAt is the square standing at column x - what a picture paints, and +// what the label is asked about, so the two cannot come apart. +func (g geometry) squareAt(x int) image.Rectangle { + return image.Rect(x, g.squareY, x+g.side, g.squareY+g.side) +} + +// labelWholeIn says whether the text of the label is whole in at least one +// picture of a film of this many changes - the square, drawn over it in its +// ink, changing none of it there. It stands in squareSteps places, so a film +// of that many changes or more stands in all of them, and a film of one change +// only at the left edge, over the start of the label of a low picture (O288 of +// docs/OBSERVATIONS.md). One whole picture is enough, by the owner's decision +// of 2026-10-27: it is the one that identifies the file. +func (g geometry) labelWholeIn(changes int64, label string) bool { + for c := range min(changes, squareSteps) { + if !imagelabel.Hides(g.width, label, g.squareAt(g.squareX(c))) { + return true + } + } + return false +} + // clockRight is the column after the clock's last character, nought without // a clock. func (g geometry) clockRight() int { @@ -350,7 +372,7 @@ func (f *film) whole(l look) Planes { b := work.Rect imagelabel.Draw(work.SubImage(image.Rect(0, f.clockFrom, b.Dx(), b.Dy())).(*image.RGBA), l.clock) } - draw.Draw(work, image.Rect(l.x, f.squareY, l.x+f.side, f.squareY+f.side), f.square, image.Point{}, draw.Src) + draw.Draw(work, f.squareAt(l.x), f.square, image.Point{}, draw.Src) planes, _ := newPlanes(f.width, f.height) toPlanes(work, planes) return planes @@ -419,7 +441,7 @@ func (p *painter) drawIn(l look, r image.Rectangle) Planes { if f.showClock() && s.Rect.Min.Y <= f.clockFrom && f.clockFrom < s.Rect.Max.Y { imagelabel.Draw(s.SubImage(image.Rect(0, f.clockFrom, f.width, s.Rect.Max.Y)).(*image.RGBA), l.clock) } - draw.Draw(s, image.Rect(l.x, f.squareY, l.x+f.side, f.squareY+f.side), f.square, image.Point{}, draw.Src) + draw.Draw(s, f.squareAt(l.x), f.square, image.Point{}, draw.Src) convertRect(s, out, r.Min, in) } return out diff --git a/internal/format/video/plan.go b/internal/format/video/plan.go index 29c1afc6..70ca5d52 100644 --- a/internal/format/video/plan.go +++ b/internal/format/video/plan.go @@ -63,6 +63,13 @@ func Plan(formatID string, r format.Request, need func(Stream) int64, refuse fun // The answer is c.Labelled, which Facts has already put in the plan. This // is the note a label drawn and cut off at the foot of the picture owes. _, p.Notes = imagelabel.Claim(r.Label && Labelled(c.Width, c.Label), c.Width, c.Height, len(c.Label), p.Notes) + if c.squareOverLabel(s.Timeline) { + p.Notes = append(p.Notes, format.Note{ + Code: "label_omitted", + Detail: core.Says("video.TheSquareCoversTheLabel", "The picture is %dx%d and the square that steps across it covers part of the label in every picture, so this file carries no readable label. Its name and the manifest still identify it.", + core.A("Width", c.Width), core.A("Height", c.Height)), + }) + } if !c.ClockShown(s.Timeline) { p.Notes = append(p.Notes, format.Note{ Code: "clock_omitted", diff --git a/internal/guard/labelclaim_test.go b/internal/guard/labelclaim_test.go index a5322186..5549de11 100644 --- a/internal/guard/labelclaim_test.go +++ b/internal/guard/labelclaim_test.go @@ -8,6 +8,7 @@ import ( "github.com/donislawdev/TestingFilesGenerator/internal/core" "github.com/donislawdev/TestingFilesGenerator/internal/format" "github.com/donislawdev/TestingFilesGenerator/internal/format/imagelabel" + "github.com/donislawdev/TestingFilesGenerator/internal/format/video" ) // A picture says it carries its label only when the text of the label is whole @@ -102,6 +103,177 @@ func TestALabelIsReadableFromTheFirstHeightThatHoldsItsText(t *testing.T) { } } +// A film says it carries its label only when one of its pictures shows the +// text whole under the square. +// +// The square steps across a film in ten places and is drawn over the label in +// the label's own ink. On a low wide film it rides across the text, and until +// 2026-11-03 a film of 1920x64 and one change drew it over "tfg -" in its only +// picture and said label_embedded true (O288 of docs/OBSERVATIONS.md). Asked +// of the pixels: the text rows of every picture are compared with the same +// picture of the film made tall, where the square passes under the label, over +// the columns the label's ink covers - measured from the label drawn on its +// own. Across heights at three scales and widths around the one where the +// second of two pictures first clears the text, so the answer is held at both +// edges it can move at, and each series has to give both answers. +func TestAFilmSaysItCarriesItsLabelOnlyWhenAPictureShowsItWhole(t *testing.T) { + const seed, tall = 7, 256 + label := core.Label("webm", 1<<20, seed) + across := len(label)*4 + 4 // the width that draws it at scale 1, as in the guard above + timeline := func(changes int64) video.Timeline { + tl, err := video.NewTimeline("webm", changes*1000, 60_000, 1000, 30) + if err != nil { + t.Fatal(err) + } + return tl + } + // shown says whether picture c of the film leaves the label's ink as the + // tall film shows it, over every row and column the ink reaches. The tall + // picture is the same at every height, so it is painted once. + talls := map[[2]int64]video.Planes{} + shown := func(w, h int, tl video.Timeline, ink image.Rectangle, c int64) bool { + got := video.WholePicture(w, h, seed, label, tl, c) + key := [2]int64{int64(w)*1000 + tl.Changes(), c} + want, ok := talls[key] + if !ok { + want = video.WholePicture(w, tall, seed, label, tl, c) + talls[key] = want + } + for y := ink.Min.Y; y < ink.Max.Y; y++ { + for x := ink.Min.X; x < ink.Max.X; x++ { + if got.Y[y*w+x] != want.Y[y*w+x] { + return false + } + } + } + return true + } + ask := func(w, h int, tl video.Timeline) (said, seen bool, compared bool) { + said = video.Choice{Width: w, Height: h, Seed: seed, Label: label}.Labelled(tl) + if !video.Labelled(w, label) || !imagelabel.Readable(w, h, len(label)) { + return said, false, false + } + ink := labelInkBounds(w, label) + if text := imagelabel.TextRect(w, len(label)); ink != text { + t.Fatalf("at %d px across the label's ink covers %v and TextRect says %v - this guard reads the ink and the film asks TextRect, so the two have to agree on its label", w, ink, text) + } + for c := range tl.Changes() { + if shown(w, h, tl, ink, c) { + return said, true, true + } + } + return said, false, true + } + type series struct { + name string + cases [][2]int + tl video.Timeline + } + var all []series + for _, changes := range []int64{1, 2, 10} { + for _, scale := range []int{1, 2, 4} { + s := series{name: "scale " + strconv.Itoa(scale) + ", " + strconv.FormatInt(changes, 10) + " change(s), heights", tl: timeline(changes)} + for h := 1; h <= 120; h++ { + s.cases = append(s.cases, [2]int{across * scale, h}) + } + all = append(all, s) + } + } + // The square of the second of two pictures stands a ninth of the way along + // the room it leaves, so at 64 px tall, where it is 64 px and rides over + // the text, a film 9 x + 64 px wide has it at column x. Walked over the + // last two characters and past the text's right edge, which is where it + // stops hiding anything - not at the edge, because the last column of a 7 + // is ink all the way down. + right := imagelabel.TextRect(across*4, len(label)).Max.X + step := imagelabel.CellsOf(across*4, len(label)).Step + edge := series{name: "2 changes at 64 px, widths", tl: timeline(2)} + for x := right - 2*step; x <= right+2; x++ { + edge.cases = append(edge.cases, [2]int{9*x + 64, 64}) + } + all = append(all, edge) + + for _, s := range all { + answers := map[bool]int{} + for _, wh := range s.cases { + said, seen, compared := ask(wh[0], wh[1], s.tl) + if !compared { + if said { + t.Errorf("%s: a film of %dx%d says it carries its label, and the label is not whole in its height", s.name, wh[0], wh[1]) + } + continue + } + answers[seen]++ + if said != seen { + t.Errorf("%s: a film of %dx%d says label_embedded %v, and a picture of it shows the label whole: %v", s.name, wh[0], wh[1], said, seen) + } + } + if answers[true] == 0 || answers[false] == 0 { + t.Errorf("%s: the pictures showed the label whole %d time(s) and covered %d - the series never reached the edge it is there for", s.name, answers[true], answers[false]) + } + } +} + +// And the plan of both film formats says it, with a note when the square is +// what hides the label: the film of O288, and the same film at the default +// ten changes, whose last picture shows the label whole. +func TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan(t *testing.T) { + asked := 0 + for _, id := range format.IDs() { + d, err := format.Get(id) + if err != nil { + t.Fatal(err) + } + if _, ok := settingNamed(d, video.SettingChangeInterval); !ok { + continue + } + asked++ + for _, c := range []struct { + duration string + want bool + }{{"1s", false}, {"10s", true}} { + props := map[string]string{"width": "1920", "height": "64", video.SettingDuration: c.duration} + r := format.Request{Seed: 1, Label: true, Properties: props} + r.Bytes = format.SmallestRemembered(d, r) + 64<<10 + p, err := d.Generator.Plan(r) + if err != nil { + t.Fatalf("%s 1920x64 for %s: %v", id, c.duration, err) + } + embedded, _ := p.Properties[format.PropertyLabelEmbedded].(bool) + omitted := 0 + for _, n := range p.Notes { + if n.Code == "label_omitted" { + omitted++ + } + } + if embedded != c.want || omitted != map[bool]int{true: 0, false: 1}[c.want] { + t.Errorf("%s 1920x64 for %s says label_embedded %v with %d label_omitted note(s), want %v with %d - notes %v", + id, c.duration, embedded, omitted, c.want, map[bool]int{true: 0, false: 1}[c.want], p.Notes) + } + } + } + if asked < 2 { + t.Fatalf("only %d format(s) declare %s - the registry was not read", asked, video.SettingChangeInterval) + } +} + +// labelInkBounds is the rectangle the pixels of a label drawn alone across +// width cover, read from the pixels. +func labelInkBounds(width int, label string) image.Rectangle { + img := image.NewRGBA(image.Rect(0, 0, width, 64)) + imagelabel.Draw(img, label) + band := img.RGBAAt(0, 0) + var ink image.Rectangle + for y := range 64 { + for x := range width { + if c := img.RGBAAt(x, y); c != band && c.A != 0 { + ink = ink.Union(image.Rect(x, y, x+1, y+1)) + } + } + } + return ink +} + func hasNote(notes []format.Note, code string) bool { for _, n := range notes { if n.Code == code { diff --git a/internal/guard/mutation/entries.py b/internal/guard/mutation/entries.py index 8db2edeb..741aa4ac 100644 --- a/internal/guard/mutation/entries.py +++ b/internal/guard/mutation/entries.py @@ -13673,7 +13673,7 @@ # picture from the whole painter, which draws it. "a film's strip painter leaves the square out", "internal/format/video/picture.go", - " draw.Draw(s, image.Rect(l.x, f.squareY, l.x+f.side, f.squareY+f.side), f.square, image.Point{}, draw.Src)", + " draw.Draw(s, f.squareAt(l.x), f.square, image.Point{}, draw.Src)", " _ = f.square", "TestAFilmPaintsOnlyWhatChangesAndGetsTheWholePicture", ), @@ -16361,4 +16361,48 @@ "\t_ = engine.OwnProcess\n", "TestTheWindowSaysItsProcessIsItsOwnBeforeItOpens", ), + # O288 (docs/OBSERVATIONS.md): a film says it carries its label only when + # one of its pictures shows the text whole under the square. + ( + "a film says it carries a label the square covers in every picture", + "internal/format/video/picture.go", + "if !imagelabel.Hides(g.width, label, g.squareAt(g.squareX(c))) {", + "if true {", + "TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan", + ), + ( + "a film asks only its first picture whether the label is whole", + "internal/format/video/picture.go", + "for c := range min(changes, squareSteps) {", + "for c := range min(changes, 1) {", + "TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan", + ), + ( + "the square over the glyphs' own ink is said to hide the label", + "internal/format/imagelabel/imagelabel.go", + "return off < t.Width && glyph(t.runes[k])[(y-pad)/t.scale*glyphWidth+off/t.scale] == '#'", + "return off < t.Width && glyph(t.runes[k])[(y-pad)/t.scale*glyphWidth+off/t.scale] == '#' && false", + "TestAFilmSaysItCarriesItsLabelOnlyWhenAPictureShowsItWhole", + ), + ( + "the rectangle of a label's text ends a column short of its ink", + "internal/format/imagelabel/imagelabel.go", + "return image.Rect(c.First, pad, c.First+(chars-1)*c.Step+c.Width, textHeight(width, chars))", + "return image.Rect(c.First, pad, c.First+(chars-1)*c.Step+c.Width-1, textHeight(width, chars))", + "TestAFilmSaysItCarriesItsLabelOnlyWhenAPictureShowsItWhole", + ), + ( + "a film whose square covers its label gives no note", + "internal/format/video/plan.go", + "if c.squareOverLabel(s.Timeline) {", + "if false {", + "TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan", + ), + ( + "the manifest of a film is not asked about the square", + "internal/format/video/choose.go", + "return c.readable() && !c.squareOverLabel(t)", + "return c.readable()", + "TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan", + ), ] diff --git a/internal/gui/text/locale/said/en.json b/internal/gui/text/locale/said/en.json index 0654f29e..9247585c 100644 --- a/internal/gui/text/locale/said/en.json +++ b/internal/gui/text/locale/said/en.json @@ -3741,6 +3741,11 @@ "hash": "sha256-e6b01f8c0aeb", "other": "at {{.FPS}} frames a second a length has to end on a frame, and lengths at that rate go in steps of {{.Step}}. Ask for {{.Above}}, the shortest film at this rate" }, + "video.TheSquareCoversTheLabel": { + "description": "Said by internal/format/video - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Width}}, {{.Height}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-c37dab3cb322", + "other": "The picture is {{.Width}}x{{.Height}} and the square that steps across it covers part of the label in every picture, so this file carries no readable label. Its name and the manifest still identify it." + }, "video.TooManyChanges": { "description": "Said by internal/format/video - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Duration}}, {{.Interval}}, {{.Pictures}}, {{.Most}}, {{.Shortest}}, written the way the program writes them - keep each spelled exactly that way.", "hash": "sha256-79ad4cf6e19b", diff --git a/internal/gui/text/locale/said/pl.json b/internal/gui/text/locale/said/pl.json index 6a43cba2..663c1ae0 100644 --- a/internal/gui/text/locale/said/pl.json +++ b/internal/gui/text/locale/said/pl.json @@ -745,6 +745,7 @@ "video.NoClock": { "hash": "sha256-9fb32f58b247", "other": "Obraz ma {{.Width}}x{{.Height}} i zegar potrzebuje więcej miejsca, więc ten film nie pokazuje zegara. Kwadrat dalej przesuwa się po nim tam, gdzie ma miejsce na ruch." }, "video.NotOnAFrame": { "hash": "sha256-4471642f4d67", "other": "przy {{.FPS}} klatkach na sekundę długość musi kończyć się na klatce, a przy tej szybkości długości idą co {{.Step}}. Poproś o {{.Below}} albo {{.Above}}" }, "video.NotOnAFrameShortest": { "hash": "sha256-e6b01f8c0aeb", "other": "przy {{.FPS}} klatkach na sekundę długość musi kończyć się na klatce, a przy tej szybkości długości idą co {{.Step}}. Poproś o {{.Above}}, najkrótszy film przy tej szybkości" }, + "video.TheSquareCoversTheLabel": { "hash": "sha256-c37dab3cb322", "other": "Obraz ma {{.Width}}x{{.Height}}, a kwadrat, który przesuwa się po nim, zasłania część etykiety w każdym obrazie, więc ten plik nie niesie czytelnej etykiety. Wciąż wskazują go jego nazwa i manifest." }, "video.TooManyChanges": { "hash": "sha256-79ad4cf6e19b", "other": "liczba obrazów w filmie o długości {{.Duration}} z nowym obrazem co {{.Interval}} to {{.Pictures}}, a film może pokazać najwyżej {{.Most}}. Poproś o {{.Shortest}} albo dłużej albo o krótszy film" }, "video.TooManyKeyFrames": { "hash": "sha256-fab3e045a284", "other": "film o długości {{.Duration}} z klatką kluczową co {{.Interval}} ma ich {{.Keys}}, a film może mieć najwyżej {{.Most}}. Poproś o {{.Shortest}} albo dłużej albo o krótszy film" }, "wav.DropTheLabel": { "hash": "sha256-b8bfddcdab7d", "other": " albo usuń etykietę" }, From a8b5553f0fd1e40c339ec086ebc8249f4ab98100 Mon Sep 17 00:00:00 2001 From: DonislawDev Date: Tue, 3 Nov 2026 13:03:04 +0100 Subject: [PATCH 2/3] mutation: the installer entry no longer pins the actions it does not test The entry that moves the installer job to Linux matched the job's first steps, action pins included, so every Dependabot bump of those actions left it matching nothing. It now matches the comment over the job and its runs-on line, which is all it changes. Co-Authored-By: Claude Opus 5.5 --- internal/guard/mutation/entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/guard/mutation/entries.py b/internal/guard/mutation/entries.py index 741aa4ac..335c0a56 100644 --- a/internal/guard/mutation/entries.py +++ b/internal/guard/mutation/entries.py @@ -12316,8 +12316,8 @@ ( "the installer job runs on Linux, where there is nothing to install", ".github/workflows/ci.yml", - " runs-on: windows-latest\n timeout-minutes: 20\n steps:\n - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1\n with:\n persist-credentials: false\n\n - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0\n with:\n python-version: \"3.14\"\n\n - name: build the installer twice", - " runs-on: ubuntu-latest\n timeout-minutes: 20\n steps:\n - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1\n with:\n persist-credentials: false\n\n - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0\n with:\n python-version: \"3.14\"\n\n - name: build the installer twice", + " # virtual machine instead, 0.3.0 to 0.4.0 (tools/packaging-vm).\n runs-on: windows-latest\n", + " # virtual machine instead, 0.3.0 to 0.4.0 (tools/packaging-vm).\n runs-on: ubuntu-latest\n", "TestTheInstallerIsInstalledOnARunner", ), ( From 86ac3805f41c91bfa063f27b8c0c612666138c83 Mon Sep 17 00:00:00 2001 From: DonislawDev Date: Tue, 3 Nov 2026 14:22:21 +0100 Subject: [PATCH 3/3] mutation: the film's square entry keeps its loop variable in use Replacing the whole condition with "if true" left the picture counter of labelWholeIn unused, so the mutated tree did not compile and the entry was BROKEN, proving nothing. The condition now stays and is ored with true: the first picture still counts as showing the label whole, and the tree builds. Co-Authored-By: Claude Opus 5.5 --- internal/guard/mutation/entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/guard/mutation/entries.py b/internal/guard/mutation/entries.py index 335c0a56..9f90f5fa 100644 --- a/internal/guard/mutation/entries.py +++ b/internal/guard/mutation/entries.py @@ -16367,7 +16367,7 @@ "a film says it carries a label the square covers in every picture", "internal/format/video/picture.go", "if !imagelabel.Hides(g.width, label, g.squareAt(g.squareX(c))) {", - "if true {", + "if !imagelabel.Hides(g.width, label, g.squareAt(g.squareX(c))) || true {", "TestAFilmWhoseSquareCoversItsLabelSaysSoInItsPlan", ), (