diff --git a/public/og/googletest-live-at-head.png b/public/og/googletest-live-at-head.png index a28e7cbf..770cff97 100644 Binary files a/public/og/googletest-live-at-head.png and b/public/og/googletest-live-at-head.png differ diff --git a/public/og/spec-author-corrections-fixed.png b/public/og/spec-author-corrections-fixed.png new file mode 100644 index 00000000..a21d951a Binary files /dev/null and b/public/og/spec-author-corrections-fixed.png differ diff --git a/scripts/schedule_doubles.py b/scripts/schedule_doubles.py index a960467a..1987e2f3 100644 --- a/scripts/schedule_doubles.py +++ b/scripts/schedule_doubles.py @@ -160,6 +160,10 @@ # spec post took the second slot, to the first free second slot. "2026-11-01": {"slug": "rust-swift-cpp-interop-toolchains", "hour": 16, "why": "Rust and Swift interop news alongside the evergreen post, moved from 22 October"}, + # A GoogleTest news short. It moved from the first slot of 26 October, where the + # post on the spec author's two corrections took the morning, to the first free second slot. + "2026-11-03": {"slug": "googletest-live-at-head", "hour": 16, + "why": "GoogleTest release news alongside the first tidy-agent episode, moved from 26 October"}, } diff --git a/social/facebook/googletest-live-at-head/caption.md b/social/facebook/googletest-live-at-head/caption.md index 6c5119ff..26277338 100644 --- a/social/facebook/googletest-live-at-head/caption.md +++ b/social/facebook/googletest-live-at-head/caption.md @@ -20,5 +20,5 @@ https://wrocpp.github.io/posts/googletest-live-at-head/ A wro.cpp social card reading "The notes say do not use the release", about GoogleTest recommending users build from trunk. ## Suggested post time -Monday 2026-10-26, 10:00 CET -Reason: Weekend mid morning for a tooling and dependency management read. +Tuesday 2026-11-03, 16:00 UTC +Reason: the afternoon slot, after the morning slot of the first tidy-agent day, moved here from 26 October for the spec author post. diff --git a/social/facebook/googletest-live-at-head/config.yaml b/social/facebook/googletest-live-at-head/config.yaml index 5d7fbc02..85037822 100644 --- a/social/facebook/googletest-live-at-head/config.yaml +++ b/social/facebook/googletest-live-at-head/config.yaml @@ -9,4 +9,4 @@ project: name: "googletest-live-at-head" author: "wro.cpp -- Wroclaw C++ community" email: "office@wro.cpp" - date: "2026-10-26" + date: "2026-11-03" diff --git a/social/facebook/googletest-live-at-head/content.md b/social/facebook/googletest-live-at-head/content.md index e5e756f0..5c2c98e3 100644 --- a/social/facebook/googletest-live-at-head/content.md +++ b/social/facebook/googletest-live-at-head/content.md @@ -4,7 +4,7 @@ template: social/linkedin-post ::::post{type=insight theme=dark logo=top-left} -:::insight{citation="wro.cpp -- 2026-10-26"} +:::insight{citation="wro.cpp -- 2026-11-03"} # The notes say do not use the release Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown. ::: diff --git a/social/facebook/googletest-live-at-head/image.png b/social/facebook/googletest-live-at-head/image.png index a850a41d..e53b81f9 100644 Binary files a/social/facebook/googletest-live-at-head/image.png and b/social/facebook/googletest-live-at-head/image.png differ diff --git a/social/facebook/googletest-live-at-head/index.html b/social/facebook/googletest-live-at-head/index.html index 3f6d9dde..299184ab 100644 --- a/social/facebook/googletest-live-at-head/index.html +++ b/social/facebook/googletest-live-at-head/index.html @@ -23,6 +23,6 @@ -

The notes say do not use the release

Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown.

wro.cpp -- 2026-10-26
ScudoAI
AI-generated
+

The notes say do not use the release

Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown.

wro.cpp -- 2026-11-03
ScudoAI
AI-generated
\ No newline at end of file diff --git a/social/facebook/spec-author-corrections-fixed/README.md b/social/facebook/spec-author-corrections-fixed/README.md new file mode 100644 index 00000000..85dc2af2 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/README.md @@ -0,0 +1,130 @@ +# LinkedIn Square Post (1200x1200) + +Standard LinkedIn feed post at 1:1 square aspect ratio. Works well as a scroll-stopping image in the LinkedIn feed. + +## Usage + +```bash +brand-gen init social/linkedin-post my-post +cd my-post +# edit content.md +brand-gen build # -> index.html +brand-gen image # -> my-post.png (2400x2400 at 2x DPR) +``` + +## Directives + +All templates use one of four post types. Each uses the same directive pattern: + +### Quote + +```markdown +::::post{type=quote theme=dark logo=top-left} + +:::quote{author="Filip Sajdak" role="Founder & CEO, ScudoAI"} + +Sovereign AI is not a feature you bolt on later. It is the architecture. + +::: + +:::: +``` + +### Stat + +```markdown +::::post{type=stat theme=light accent=brand} + +:::stat{value="USD 80B" label="Worldwide sovereign cloud spend" context="Europe is the fastest-growing region at 83% YoY." source="Gartner, Feb 2026"} +::: + +:::: +``` + +### Announcement + +```markdown +::::post{type=announcement theme=light} + +:::announcement{kind="launch" date="April 2026"} + +# ScudoAI joins NVIDIA Inception + +We are thrilled to join the NVIDIA Inception program to accelerate sovereign AI deployment across European regulated industries. + +::: + +:::: +``` + +### Insight + +```markdown +::::post{type=insight theme=dark} + +:::insight{citation="EU AI Act full enforcement: 2 August 2026"} + +# The AI compliance deadline most companies are missing + +Full EU AI Act enforcement for high-risk AI systems begins in 2 August 2026. Most regulated enterprises are not ready for the documentation and audit requirements. Teams running cloud AI will face the hardest transition. + +::: + +:::: +``` + +## Attributes reference + +### `::::post` attributes + +- `type` - quote | stat | announcement | insight (required) +- `theme` - dark | light (default light) +- `accent` - brand | amber | nv (optional colored gradient background) +- `logo` - top-left | top-right | bottom-left | bottom-right (default bottom-right) + +### `:::quote` attributes + +- `author` - attribution name (appears below the quote) +- `role` - attribution role/title (appears under the author) + +### `:::stat` attributes + +- `value` - the big number (e.g. "USD 80B", "3/4", "Hours to minutes") +- `label` - short label under the value +- `context` - 1-2 sentences providing context +- `source` - small citation + +### `:::announcement` attributes + +- `kind` - launch | milestone | event | hire | partnership +- `date` - free-form date string + +### `:::insight` attributes + +- `citation` - small footer citation (e.g. regulation reference) + +## Character limits + +- **Quote body**: ~120 characters for maximum readability at thumbnail size +- **Stat label**: ~40 characters +- **Stat context**: ~200 characters +- **Announcement headline**: ~80 characters +- **Announcement body**: ~250 characters +- **Insight hook**: ~100 characters +- **Insight body**: ~300 characters + +Posts that exceed these limits will still render but typography may shrink or content may clip. + +## Media + +Add a `media/` directory to the project and reference local files: + +```markdown +:::background{image="nvidia-thor.jpg" overlay="dark"} +::: + +:::image{src="product-screenshot.png" alt="ScudoAI compliance dashboard" position="top"} +::: +``` + +Paths are relative to the project's `media/` directory. diff --git a/social/facebook/spec-author-corrections-fixed/assets/brand-kit.json b/social/facebook/spec-author-corrections-fixed/assets/brand-kit.json new file mode 100644 index 00000000..9b349578 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/assets/brand-kit.json @@ -0,0 +1,11 @@ +{ + "template": "social/linkedin-post", + "bundleSource": "scudoai-social.css", + "brandKitVersion": "0.1.0", + "vendoredAt": "2026-10-23T17:30:36.277Z", + "files": [ + "scudoai.css", + "layout.css", + "logo-symbol.svg" + ] +} diff --git a/social/facebook/spec-author-corrections-fixed/assets/layout.css b/social/facebook/spec-author-corrections-fixed/assets/layout.css new file mode 100644 index 00000000..e69de29b diff --git a/social/facebook/spec-author-corrections-fixed/assets/logo-symbol.svg b/social/facebook/spec-author-corrections-fixed/assets/logo-symbol.svg new file mode 100644 index 00000000..1fc44966 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/assets/logo-symbol.svg @@ -0,0 +1,22 @@ + diff --git a/social/facebook/spec-author-corrections-fixed/assets/scudoai.css b/social/facebook/spec-author-corrections-fixed/assets/scudoai.css new file mode 100644 index 00000000..612b5a12 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/assets/scudoai.css @@ -0,0 +1,343 @@ +/* ============================================================ + * wro.cpp social card -- standalone stylesheet + * + * Self-contained styling for brand-gen's `social/linkedin-post` + * output. Replaces (does NOT extend) the scudoai-social.css that + * brand-gen ships with -- the design language here is the + * wro.cpp 'Editorial Magnet' look used by src/styles/global.css, + * not the scudoai blue-indigo deck aesthetic. + * + * Source of truth for tokens: src/styles/global.css:12-30 in the + * wrocpp.github.io repo. + * + * Brand-gen markup we style: + *
+ *
(decorative, hidden) + *
(decorative, hidden) + *
+ *

...

+ *

...

+ *
...
+ *
+ *
+ * + * ScudoAI (hidden; replaced via ::after) + *
+ *
+ * + * Other card types (card-quote, card-stat, card-announcement) + * get analogous rules at the bottom. + * ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +:root { + --paper: #FCFAF5; + --paper-2: #F3EFE6; + --paper-3: #E8E1D0; + --ink: #1B1B1D; + --ink-muted:#55555A; + --ink-faint:#8A8A90; + --rule: #D7D2C4; + --orange: #EA541E; + --orange-2: #D6481A; + --blue: #3EB5E9; + --blue-deep:#2E8FB8; + + --font-display: 'Quicksand', system-ui, -apple-system, sans-serif; + --font-body: 'Source Serif 4', Georgia, serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; +} + +html, body { + margin: 0; + padding: 0; + background: var(--paper); + color: var(--ink); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: var(--font-body); + font-size: 18px; + line-height: 1.6; +} + +/* Hide brand-gen decorative noise -- the wro.cpp aesthetic is calm + * and editorial, not a dark deck with glow + grid backgrounds. */ +.grid-bg, .glow { display: none !important; } + +/* ---------------- Card frame (1200x1200) ---------------- */ +.card { + position: relative; + width: 1200px; + height: 1200px; + background: var(--paper); + /* Faint warm radial wash, mirrors body { background-image } in + * src/styles/global.css:44-49 -- gives the paper texture without + * looking flat. */ + background-image: + radial-gradient(900px 700px at 80% -10%, rgba(234, 84, 30, 0.06), transparent 60%), + radial-gradient(800px 600px at -10% 110%, rgba(62, 181, 233, 0.05), transparent 60%); + overflow: hidden; + box-sizing: border-box; + padding: 130px 110px 130px 110px; + display: flex; + flex-direction: column; + /* Faint inner border gives the card a printed-page feel. */ + border-bottom: 6px solid var(--orange); +} + +/* Override: theme=dark cards still use paper. We keep the editorial + * light look across the board so the wro.cpp brand stays consistent. + * If the user really wants a dark variant later, this is the hook. */ +.card.dark { background: var(--paper) !important; color: var(--ink) !important; } +.card.dark h1, .card.dark h2, .card.dark p, +.card.dark blockquote { color: var(--ink) !important; } + +/* ---------------- INSIGHT card ---------------- */ +.card-insight { + justify-content: center; + padding-top: 200px; /* reserve space for logo watermark (top:90px + 64px + safety) */ +} +.card-insight .insight-content { + display: flex; + flex-direction: column; + gap: 60px; + max-width: 980px; +} +.card-insight h1 { + font-family: var(--font-display); + font-weight: 700; + font-size: 92pt; + line-height: 1.02; + letter-spacing: -0.025em; + color: var(--ink); + margin: 0; +} +.card-insight h1.title-long { + font-size: 72pt; + line-height: 1.06; + letter-spacing: -0.02em; + max-height: 420px; +} +.card-insight h1.title-xlong { + font-size: 56pt; + line-height: 1.10; + letter-spacing: -0.015em; + max-height: 440px; +} +/* Auto-accent: ++ in C++/C++26 turns orange, sentence-final period + * (when wrapped in ) turns blue. The skill writes + * the heading with these spans for the wro.cpp signature look. */ +.card-insight h1 .accent { color: var(--orange); } +.card-insight h1 .dot { color: var(--blue-deep); } +.card-insight p { + font-family: var(--font-body); + font-size: 30pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 0; + max-width: 920px; +} +.card-insight .insight-citation { + font-family: var(--font-mono); + font-size: 18pt; + letter-spacing: 0.04em; + color: var(--ink-faint); + margin-top: 40px; + padding-top: 30px; + border-top: 1px solid var(--rule); + text-transform: lowercase; +} +.card-insight .insight-citation::before { + content: '// '; + color: var(--orange); + font-weight: 700; +} + +/* ---------------- QUOTE card ---------------- */ +.card-quote { justify-content: center; padding: 140px 110px; } +.card-quote .quote-content { display: flex; flex-direction: column; align-items: flex-start; } +.card-quote .quote-mark { + font-family: var(--font-display); + font-weight: 700; + font-size: 200pt; + line-height: 0.8; + color: var(--orange); + opacity: 0.20; + /* In-flow above the text (not floated in the corner) with a negative + * bottom margin so the mark nearly touches the first quote line. */ + margin: 0 0 -0.62em; +} +.card-quote .quote-body { + font-family: var(--font-display); + font-weight: 600; + font-size: 64pt; + line-height: 1.15; + letter-spacing: -0.02em; + color: var(--ink); + max-width: 980px; + margin: 0; +} +.card-quote .quote-attribution { + margin-top: 50px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-muted); +} +.card-quote .quote-attribution::before { content: '-- '; color: var(--orange); } +/* author and role are adjacent spans with no separator in the markup; + * add one so they do not run together ("wro.cppC++26 ..."). */ +.card-quote .quote-attribution .role::before { content: ' / '; color: var(--ink-faint); } + +/* ---------------- STAT card ---------------- */ +.card-stat { justify-content: center; } +/* Value and label sit side by side (label wraps to stacked lines whose + * combined height roughly matches the value); context + source span the + * full width underneath. */ +.card-stat .stat-content { + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: + "value label" + "context context" + "source source"; + column-gap: 64px; + align-items: center; +} +.card-stat .stat-value { + grid-area: value; + font-family: var(--font-display); + font-weight: 700; + font-size: 240pt; + line-height: 0.82; + letter-spacing: -0.04em; + color: var(--orange); + margin: 0; +} +.card-stat .stat-label { + grid-area: label; + font-family: var(--font-display); + font-weight: 700; + font-size: 90pt; + line-height: 1.0; + letter-spacing: -0.02em; + color: var(--ink); + /* min-content forces a per-word wrap, so "codegen steps" stacks into + * two lines next to the value instead of running on one long line. */ + width: min-content; + margin: 0; +} +.card-stat .stat-context { + grid-area: context; + font-family: var(--font-body); + font-size: 26pt; + line-height: 1.45; + color: var(--ink-muted); + margin: 56px 0 0; + max-width: 880px; +} +.card-stat .stat-source { + grid-area: source; + margin-top: 44px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-faint); +} +.card-stat .stat-source::before { content: '// '; color: var(--orange); } + +/* ---------------- ANNOUNCEMENT card ---------------- */ +.card-announcement { justify-content: center; } +.card-announcement .announcement-meta { + font-family: var(--font-mono); + font-size: 20pt; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--orange); + margin-bottom: 30px; +} +.card-announcement .announcement-content h1, +.card-announcement .announcement-content h2 { + font-family: var(--font-display); + font-weight: 700; + font-size: 78pt; + line-height: 1.05; + letter-spacing: -0.02em; + color: var(--ink); + margin: 0; +} +.card-announcement .announcement-content p { + font-family: var(--font-body); + font-size: 28pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 40px 0 0; + max-width: 900px; +} + +/* ---------------- LOGO WATERMARK ---------------- */ +/* brand-gen hard-codes "ScudoAI" in .logo-wordmark; we hide it and + * draw "wro.cpp" via ::after on the watermark. The orange ".cpp" mirrors + * the brand-mark in the site header. */ +.logo-watermark { + position: absolute; + display: flex; + align-items: center; + gap: 18px; + height: 64px; + z-index: 10; + color: var(--ink); +} +.logo-watermark.top-left { top: 90px; left: 110px; } +.logo-watermark.top-right { top: 90px; right: 110px; } +.logo-watermark.bottom-left { bottom: 60px; left: 110px; } +.logo-watermark.bottom-right { bottom: 60px; right: 110px; } +.logo-watermark svg { + width: 64px; + height: 64px; + flex-shrink: 0; + /* Magnet logo PNG already has transparent paper-tone background; + * no rounded box needed -- it sits on the paper card directly. */ +} +.logo-wordmark { + font-size: 0 !important; + line-height: 0 !important; + /* Hide the upstream "ScudoAI" text node entirely. */ +} +.logo-watermark::after { + content: 'wro.cpp'; + font-family: var(--font-display); + font-weight: 700; + font-size: 28pt; + letter-spacing: -0.025em; + color: var(--ink); + line-height: 1; + /* The .cpp portion is given orange via a ::first-line trick below + * isn't reliable for inline -- so the SKILL writes the wordmark + * with explicit spans when it owns the markup. For this default + * rendering, the whole wordmark is ink and the orange comes through + * the bottom border + eyebrow. */ +} +.logo-watermark.top-right::after, +.logo-watermark.bottom-right::after { order: -1; margin-right: 18px; } + +/* ---------------- AI DISCLOSURE BADGE ---------------- */ +/* A small, honest "AI-GENERATED" label so the disclosure rides with the + * card image on social. Bottom-right corner (logo is top-left, citation is + * center-bottom, so this corner is free). Injected into index.html by + * inject.sh. See the /ai policy page. */ +.ai-badge { + position: absolute; + bottom: 74px; /* clears the 6px orange accent bar; sits in the bottom padding band */ + right: 110px; /* matches the card side padding / logo inset */ + z-index: 10; + font-family: var(--font-mono); + font-size: 14pt; + font-weight: 500; + letter-spacing: 0.10em; + text-transform: uppercase; + color: var(--ink-faint); +} + +/* ---------------- Defensive overrides ---------------- */ +/* Anything brand-gen might have set inline using its tokens. */ +* { box-sizing: border-box; } diff --git a/social/facebook/spec-author-corrections-fixed/caption.md b/social/facebook/spec-author-corrections-fixed/caption.md new file mode 100644 index 00000000..b2578be2 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/caption.md @@ -0,0 +1,18 @@ +# Two problems confirmed by the spec's author, fixed in both bindings + +## Body +The author of the Qt container spec confirmed two problems in it. I had called one a trade-off. It was a mistake, and both generated versions now announce a batch removal while the rows can still be read. + +The other was a row lookup that rebuilt itself after every change. A hash drain at 10,000 entries went from 193 microseconds per removal to 320 nanoseconds. + +https://wrocpp.github.io/posts/spec-author-corrections-fixed/ + +## Hashtags +#cpp #qt #testing #wrocpp + +## Alt-text +wro.cpp card with the magnet logo. Headline reads "193 microseconds became 320 nanoseconds" with a subtitle about two problems confirmed by the spec's author. Citation footer: wro.cpp, 2026-10-26. + +## Suggested post time +Monday 2026-10-26, 08:00 UTC +Reason: the morning slot, taken from the GoogleTest short, so the Qt models thread keeps moving while the spec author's answer is fresh. diff --git a/social/facebook/spec-author-corrections-fixed/config.yaml b/social/facebook/spec-author-corrections-fixed/config.yaml new file mode 100644 index 00000000..9a54c08a --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/config.yaml @@ -0,0 +1,12 @@ +template: social/linkedin-post + +size: + width: 1200 + height: 1200 + dpr: 2 + +project: + name: "spec-author-corrections-fixed" + author: "Filip Sajdak, Founder & CEO" + email: "office@scudoai.com" + date: "2026-10-26" diff --git a/social/facebook/spec-author-corrections-fixed/content.md b/social/facebook/spec-author-corrections-fixed/content.md new file mode 100644 index 00000000..383e1341 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/content.md @@ -0,0 +1,12 @@ +--- +template: social/linkedin-post +--- + +::::post{type=insight theme=dark logo=top-left} + +:::insight{citation="wro.cpp -- 2026-10-26"} +# 193 microseconds became 320 nanoseconds +Per removal from a hash of 10,000 entries, after the spec's author confirmed two problems: a row map rebuilt after every change, and a removal announced after the rows were gone. +::: + +:::: diff --git a/social/facebook/spec-author-corrections-fixed/image.png b/social/facebook/spec-author-corrections-fixed/image.png new file mode 100644 index 00000000..ff6694f7 Binary files /dev/null and b/social/facebook/spec-author-corrections-fixed/image.png differ diff --git a/social/facebook/spec-author-corrections-fixed/index.html b/social/facebook/spec-author-corrections-fixed/index.html new file mode 100644 index 00000000..1df51098 --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/index.html @@ -0,0 +1,28 @@ + + + + + + spec-author-corrections-fixed + + + + + + + + + + +

193 microseconds became 320 nanoseconds

Per removal from a hash of 10,000 entries, after the spec's author confirmed two problems: a row map rebuilt after every change, and a removal announced after the rows were gone.

wro.cpp -- 2026-10-26
ScudoAI
AI-generated
+ + \ No newline at end of file diff --git a/social/facebook/spec-author-corrections-fixed/media/.gitkeep b/social/facebook/spec-author-corrections-fixed/media/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/social/facebook/spec-author-corrections-fixed/media/README.md b/social/facebook/spec-author-corrections-fixed/media/README.md new file mode 100644 index 00000000..bb81c7fa --- /dev/null +++ b/social/facebook/spec-author-corrections-fixed/media/README.md @@ -0,0 +1,9 @@ +# Media directory + +Drop images here (PNG, JPG, SVG) for use in your post. + +Reference them in your content.md with: + + :::image{src="screenshot.png" alt="Alt text" position="top"} + +The renderer resolves paths relative to this media/ directory. diff --git a/social/linkedin/googletest-live-at-head/caption.md b/social/linkedin/googletest-live-at-head/caption.md index c5264a2e..d1165bf9 100644 --- a/social/linkedin/googletest-live-at-head/caption.md +++ b/social/linkedin/googletest-live-at-head/caption.md @@ -26,5 +26,5 @@ Do you pin a release or a commit for your test framework? A wro.cpp social card reading "The notes say do not use the release", about GoogleTest recommending users build from trunk. ## Suggested post time -Monday 2026-10-26, 10:00 CET -Reason: Weekend mid morning for a tooling and dependency management read. +Tuesday 2026-11-03, 16:00 UTC +Reason: the afternoon slot, after the morning slot of the first tidy-agent day, moved here from 26 October for the spec author post. diff --git a/social/linkedin/googletest-live-at-head/config.yaml b/social/linkedin/googletest-live-at-head/config.yaml index 5d7fbc02..85037822 100644 --- a/social/linkedin/googletest-live-at-head/config.yaml +++ b/social/linkedin/googletest-live-at-head/config.yaml @@ -9,4 +9,4 @@ project: name: "googletest-live-at-head" author: "wro.cpp -- Wroclaw C++ community" email: "office@wro.cpp" - date: "2026-10-26" + date: "2026-11-03" diff --git a/social/linkedin/googletest-live-at-head/content.md b/social/linkedin/googletest-live-at-head/content.md index e5e756f0..5c2c98e3 100644 --- a/social/linkedin/googletest-live-at-head/content.md +++ b/social/linkedin/googletest-live-at-head/content.md @@ -4,7 +4,7 @@ template: social/linkedin-post ::::post{type=insight theme=dark logo=top-left} -:::insight{citation="wro.cpp -- 2026-10-26"} +:::insight{citation="wro.cpp -- 2026-11-03"} # The notes say do not use the release Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown. ::: diff --git a/social/linkedin/googletest-live-at-head/image.png b/social/linkedin/googletest-live-at-head/image.png index a28e7cbf..770cff97 100644 Binary files a/social/linkedin/googletest-live-at-head/image.png and b/social/linkedin/googletest-live-at-head/image.png differ diff --git a/social/linkedin/googletest-live-at-head/index.html b/social/linkedin/googletest-live-at-head/index.html index 3f6d9dde..299184ab 100644 --- a/social/linkedin/googletest-live-at-head/index.html +++ b/social/linkedin/googletest-live-at-head/index.html @@ -23,6 +23,6 @@ -

The notes say do not use the release

Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown.

wro.cpp -- 2026-10-26
ScudoAI
AI-generated
+

The notes say do not use the release

Every GoogleTest release since 1.16 recommends building from the latest commit instead. Compiler Explorer offers 1.10.0 or trunk, which is that advice as a dropdown.

wro.cpp -- 2026-11-03
ScudoAI
AI-generated
\ No newline at end of file diff --git a/social/linkedin/spec-author-corrections-fixed/README.md b/social/linkedin/spec-author-corrections-fixed/README.md new file mode 100644 index 00000000..85dc2af2 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/README.md @@ -0,0 +1,130 @@ +# LinkedIn Square Post (1200x1200) + +Standard LinkedIn feed post at 1:1 square aspect ratio. Works well as a scroll-stopping image in the LinkedIn feed. + +## Usage + +```bash +brand-gen init social/linkedin-post my-post +cd my-post +# edit content.md +brand-gen build # -> index.html +brand-gen image # -> my-post.png (2400x2400 at 2x DPR) +``` + +## Directives + +All templates use one of four post types. Each uses the same directive pattern: + +### Quote + +```markdown +::::post{type=quote theme=dark logo=top-left} + +:::quote{author="Filip Sajdak" role="Founder & CEO, ScudoAI"} + +Sovereign AI is not a feature you bolt on later. It is the architecture. + +::: + +:::: +``` + +### Stat + +```markdown +::::post{type=stat theme=light accent=brand} + +:::stat{value="USD 80B" label="Worldwide sovereign cloud spend" context="Europe is the fastest-growing region at 83% YoY." source="Gartner, Feb 2026"} +::: + +:::: +``` + +### Announcement + +```markdown +::::post{type=announcement theme=light} + +:::announcement{kind="launch" date="April 2026"} + +# ScudoAI joins NVIDIA Inception + +We are thrilled to join the NVIDIA Inception program to accelerate sovereign AI deployment across European regulated industries. + +::: + +:::: +``` + +### Insight + +```markdown +::::post{type=insight theme=dark} + +:::insight{citation="EU AI Act full enforcement: 2 August 2026"} + +# The AI compliance deadline most companies are missing + +Full EU AI Act enforcement for high-risk AI systems begins in 2 August 2026. Most regulated enterprises are not ready for the documentation and audit requirements. Teams running cloud AI will face the hardest transition. + +::: + +:::: +``` + +## Attributes reference + +### `::::post` attributes + +- `type` - quote | stat | announcement | insight (required) +- `theme` - dark | light (default light) +- `accent` - brand | amber | nv (optional colored gradient background) +- `logo` - top-left | top-right | bottom-left | bottom-right (default bottom-right) + +### `:::quote` attributes + +- `author` - attribution name (appears below the quote) +- `role` - attribution role/title (appears under the author) + +### `:::stat` attributes + +- `value` - the big number (e.g. "USD 80B", "3/4", "Hours to minutes") +- `label` - short label under the value +- `context` - 1-2 sentences providing context +- `source` - small citation + +### `:::announcement` attributes + +- `kind` - launch | milestone | event | hire | partnership +- `date` - free-form date string + +### `:::insight` attributes + +- `citation` - small footer citation (e.g. regulation reference) + +## Character limits + +- **Quote body**: ~120 characters for maximum readability at thumbnail size +- **Stat label**: ~40 characters +- **Stat context**: ~200 characters +- **Announcement headline**: ~80 characters +- **Announcement body**: ~250 characters +- **Insight hook**: ~100 characters +- **Insight body**: ~300 characters + +Posts that exceed these limits will still render but typography may shrink or content may clip. + +## Media + +Add a `media/` directory to the project and reference local files: + +```markdown +:::background{image="nvidia-thor.jpg" overlay="dark"} +::: + +:::image{src="product-screenshot.png" alt="ScudoAI compliance dashboard" position="top"} +::: +``` + +Paths are relative to the project's `media/` directory. diff --git a/social/linkedin/spec-author-corrections-fixed/assets/brand-kit.json b/social/linkedin/spec-author-corrections-fixed/assets/brand-kit.json new file mode 100644 index 00000000..324d62e9 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/assets/brand-kit.json @@ -0,0 +1,11 @@ +{ + "template": "social/linkedin-post", + "bundleSource": "scudoai-social.css", + "brandKitVersion": "0.1.0", + "vendoredAt": "2026-10-23T17:30:36.175Z", + "files": [ + "scudoai.css", + "layout.css", + "logo-symbol.svg" + ] +} diff --git a/social/linkedin/spec-author-corrections-fixed/assets/layout.css b/social/linkedin/spec-author-corrections-fixed/assets/layout.css new file mode 100644 index 00000000..e69de29b diff --git a/social/linkedin/spec-author-corrections-fixed/assets/logo-symbol.svg b/social/linkedin/spec-author-corrections-fixed/assets/logo-symbol.svg new file mode 100644 index 00000000..1fc44966 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/assets/logo-symbol.svg @@ -0,0 +1,22 @@ + diff --git a/social/linkedin/spec-author-corrections-fixed/assets/scudoai.css b/social/linkedin/spec-author-corrections-fixed/assets/scudoai.css new file mode 100644 index 00000000..612b5a12 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/assets/scudoai.css @@ -0,0 +1,343 @@ +/* ============================================================ + * wro.cpp social card -- standalone stylesheet + * + * Self-contained styling for brand-gen's `social/linkedin-post` + * output. Replaces (does NOT extend) the scudoai-social.css that + * brand-gen ships with -- the design language here is the + * wro.cpp 'Editorial Magnet' look used by src/styles/global.css, + * not the scudoai blue-indigo deck aesthetic. + * + * Source of truth for tokens: src/styles/global.css:12-30 in the + * wrocpp.github.io repo. + * + * Brand-gen markup we style: + *
+ *
(decorative, hidden) + *
(decorative, hidden) + *
+ *

...

+ *

...

+ *
...
+ *
+ *
+ * + * ScudoAI (hidden; replaced via ::after) + *
+ *
+ * + * Other card types (card-quote, card-stat, card-announcement) + * get analogous rules at the bottom. + * ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +:root { + --paper: #FCFAF5; + --paper-2: #F3EFE6; + --paper-3: #E8E1D0; + --ink: #1B1B1D; + --ink-muted:#55555A; + --ink-faint:#8A8A90; + --rule: #D7D2C4; + --orange: #EA541E; + --orange-2: #D6481A; + --blue: #3EB5E9; + --blue-deep:#2E8FB8; + + --font-display: 'Quicksand', system-ui, -apple-system, sans-serif; + --font-body: 'Source Serif 4', Georgia, serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; +} + +html, body { + margin: 0; + padding: 0; + background: var(--paper); + color: var(--ink); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: var(--font-body); + font-size: 18px; + line-height: 1.6; +} + +/* Hide brand-gen decorative noise -- the wro.cpp aesthetic is calm + * and editorial, not a dark deck with glow + grid backgrounds. */ +.grid-bg, .glow { display: none !important; } + +/* ---------------- Card frame (1200x1200) ---------------- */ +.card { + position: relative; + width: 1200px; + height: 1200px; + background: var(--paper); + /* Faint warm radial wash, mirrors body { background-image } in + * src/styles/global.css:44-49 -- gives the paper texture without + * looking flat. */ + background-image: + radial-gradient(900px 700px at 80% -10%, rgba(234, 84, 30, 0.06), transparent 60%), + radial-gradient(800px 600px at -10% 110%, rgba(62, 181, 233, 0.05), transparent 60%); + overflow: hidden; + box-sizing: border-box; + padding: 130px 110px 130px 110px; + display: flex; + flex-direction: column; + /* Faint inner border gives the card a printed-page feel. */ + border-bottom: 6px solid var(--orange); +} + +/* Override: theme=dark cards still use paper. We keep the editorial + * light look across the board so the wro.cpp brand stays consistent. + * If the user really wants a dark variant later, this is the hook. */ +.card.dark { background: var(--paper) !important; color: var(--ink) !important; } +.card.dark h1, .card.dark h2, .card.dark p, +.card.dark blockquote { color: var(--ink) !important; } + +/* ---------------- INSIGHT card ---------------- */ +.card-insight { + justify-content: center; + padding-top: 200px; /* reserve space for logo watermark (top:90px + 64px + safety) */ +} +.card-insight .insight-content { + display: flex; + flex-direction: column; + gap: 60px; + max-width: 980px; +} +.card-insight h1 { + font-family: var(--font-display); + font-weight: 700; + font-size: 92pt; + line-height: 1.02; + letter-spacing: -0.025em; + color: var(--ink); + margin: 0; +} +.card-insight h1.title-long { + font-size: 72pt; + line-height: 1.06; + letter-spacing: -0.02em; + max-height: 420px; +} +.card-insight h1.title-xlong { + font-size: 56pt; + line-height: 1.10; + letter-spacing: -0.015em; + max-height: 440px; +} +/* Auto-accent: ++ in C++/C++26 turns orange, sentence-final period + * (when wrapped in ) turns blue. The skill writes + * the heading with these spans for the wro.cpp signature look. */ +.card-insight h1 .accent { color: var(--orange); } +.card-insight h1 .dot { color: var(--blue-deep); } +.card-insight p { + font-family: var(--font-body); + font-size: 30pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 0; + max-width: 920px; +} +.card-insight .insight-citation { + font-family: var(--font-mono); + font-size: 18pt; + letter-spacing: 0.04em; + color: var(--ink-faint); + margin-top: 40px; + padding-top: 30px; + border-top: 1px solid var(--rule); + text-transform: lowercase; +} +.card-insight .insight-citation::before { + content: '// '; + color: var(--orange); + font-weight: 700; +} + +/* ---------------- QUOTE card ---------------- */ +.card-quote { justify-content: center; padding: 140px 110px; } +.card-quote .quote-content { display: flex; flex-direction: column; align-items: flex-start; } +.card-quote .quote-mark { + font-family: var(--font-display); + font-weight: 700; + font-size: 200pt; + line-height: 0.8; + color: var(--orange); + opacity: 0.20; + /* In-flow above the text (not floated in the corner) with a negative + * bottom margin so the mark nearly touches the first quote line. */ + margin: 0 0 -0.62em; +} +.card-quote .quote-body { + font-family: var(--font-display); + font-weight: 600; + font-size: 64pt; + line-height: 1.15; + letter-spacing: -0.02em; + color: var(--ink); + max-width: 980px; + margin: 0; +} +.card-quote .quote-attribution { + margin-top: 50px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-muted); +} +.card-quote .quote-attribution::before { content: '-- '; color: var(--orange); } +/* author and role are adjacent spans with no separator in the markup; + * add one so they do not run together ("wro.cppC++26 ..."). */ +.card-quote .quote-attribution .role::before { content: ' / '; color: var(--ink-faint); } + +/* ---------------- STAT card ---------------- */ +.card-stat { justify-content: center; } +/* Value and label sit side by side (label wraps to stacked lines whose + * combined height roughly matches the value); context + source span the + * full width underneath. */ +.card-stat .stat-content { + display: grid; + grid-template-columns: auto 1fr; + grid-template-areas: + "value label" + "context context" + "source source"; + column-gap: 64px; + align-items: center; +} +.card-stat .stat-value { + grid-area: value; + font-family: var(--font-display); + font-weight: 700; + font-size: 240pt; + line-height: 0.82; + letter-spacing: -0.04em; + color: var(--orange); + margin: 0; +} +.card-stat .stat-label { + grid-area: label; + font-family: var(--font-display); + font-weight: 700; + font-size: 90pt; + line-height: 1.0; + letter-spacing: -0.02em; + color: var(--ink); + /* min-content forces a per-word wrap, so "codegen steps" stacks into + * two lines next to the value instead of running on one long line. */ + width: min-content; + margin: 0; +} +.card-stat .stat-context { + grid-area: context; + font-family: var(--font-body); + font-size: 26pt; + line-height: 1.45; + color: var(--ink-muted); + margin: 56px 0 0; + max-width: 880px; +} +.card-stat .stat-source { + grid-area: source; + margin-top: 44px; + font-family: var(--font-mono); + font-size: 18pt; + color: var(--ink-faint); +} +.card-stat .stat-source::before { content: '// '; color: var(--orange); } + +/* ---------------- ANNOUNCEMENT card ---------------- */ +.card-announcement { justify-content: center; } +.card-announcement .announcement-meta { + font-family: var(--font-mono); + font-size: 20pt; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--orange); + margin-bottom: 30px; +} +.card-announcement .announcement-content h1, +.card-announcement .announcement-content h2 { + font-family: var(--font-display); + font-weight: 700; + font-size: 78pt; + line-height: 1.05; + letter-spacing: -0.02em; + color: var(--ink); + margin: 0; +} +.card-announcement .announcement-content p { + font-family: var(--font-body); + font-size: 28pt; + line-height: 1.5; + color: var(--ink-muted); + margin: 40px 0 0; + max-width: 900px; +} + +/* ---------------- LOGO WATERMARK ---------------- */ +/* brand-gen hard-codes "ScudoAI" in .logo-wordmark; we hide it and + * draw "wro.cpp" via ::after on the watermark. The orange ".cpp" mirrors + * the brand-mark in the site header. */ +.logo-watermark { + position: absolute; + display: flex; + align-items: center; + gap: 18px; + height: 64px; + z-index: 10; + color: var(--ink); +} +.logo-watermark.top-left { top: 90px; left: 110px; } +.logo-watermark.top-right { top: 90px; right: 110px; } +.logo-watermark.bottom-left { bottom: 60px; left: 110px; } +.logo-watermark.bottom-right { bottom: 60px; right: 110px; } +.logo-watermark svg { + width: 64px; + height: 64px; + flex-shrink: 0; + /* Magnet logo PNG already has transparent paper-tone background; + * no rounded box needed -- it sits on the paper card directly. */ +} +.logo-wordmark { + font-size: 0 !important; + line-height: 0 !important; + /* Hide the upstream "ScudoAI" text node entirely. */ +} +.logo-watermark::after { + content: 'wro.cpp'; + font-family: var(--font-display); + font-weight: 700; + font-size: 28pt; + letter-spacing: -0.025em; + color: var(--ink); + line-height: 1; + /* The .cpp portion is given orange via a ::first-line trick below + * isn't reliable for inline -- so the SKILL writes the wordmark + * with explicit spans when it owns the markup. For this default + * rendering, the whole wordmark is ink and the orange comes through + * the bottom border + eyebrow. */ +} +.logo-watermark.top-right::after, +.logo-watermark.bottom-right::after { order: -1; margin-right: 18px; } + +/* ---------------- AI DISCLOSURE BADGE ---------------- */ +/* A small, honest "AI-GENERATED" label so the disclosure rides with the + * card image on social. Bottom-right corner (logo is top-left, citation is + * center-bottom, so this corner is free). Injected into index.html by + * inject.sh. See the /ai policy page. */ +.ai-badge { + position: absolute; + bottom: 74px; /* clears the 6px orange accent bar; sits in the bottom padding band */ + right: 110px; /* matches the card side padding / logo inset */ + z-index: 10; + font-family: var(--font-mono); + font-size: 14pt; + font-weight: 500; + letter-spacing: 0.10em; + text-transform: uppercase; + color: var(--ink-faint); +} + +/* ---------------- Defensive overrides ---------------- */ +/* Anything brand-gen might have set inline using its tokens. */ +* { box-sizing: border-box; } diff --git a/social/linkedin/spec-author-corrections-fixed/caption.md b/social/linkedin/spec-author-corrections-fixed/caption.md new file mode 100644 index 00000000..e86a7e2b --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/caption.md @@ -0,0 +1,20 @@ +# Two problems confirmed by the spec's author, fixed in both bindings + +## Body +The author of the container spec wrote back: section 8.3 is a mistake in the spec, not a trade-off, and the lazy row map is in the real code too. + +I had called the first one a trade-off because the spec says so. A batch removal erased the entries before it announced the rows, so a view reading a row in rowsAboutToBeRemoved saw nothing. Both generated bindings now announce first. + +The second fix has the number. The list model rebuilt its key-to-row map after every change. A row is now found by binary search, and a hash drain at 10,000 entries went from 193 microseconds per removal to 320 nanoseconds. Each change has a test that fails on the old code. + +https://wrocpp.github.io/posts/spec-author-corrections-fixed/ + +## Hashtags +#cpp #qt #testing #modelview #reflection #wrocpp + +## Alt-text +wro.cpp card with the magnet logo. Headline reads "193 microseconds became 320 nanoseconds" with a subtitle about two problems confirmed by the spec's author. Citation footer: wro.cpp, 2026-10-26. + +## Suggested post time +Monday 2026-10-26, 08:00 UTC +Reason: the morning slot, taken from the GoogleTest short, so the Qt models thread keeps moving while the spec author's answer is fresh. diff --git a/social/linkedin/spec-author-corrections-fixed/config.yaml b/social/linkedin/spec-author-corrections-fixed/config.yaml new file mode 100644 index 00000000..9a54c08a --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/config.yaml @@ -0,0 +1,12 @@ +template: social/linkedin-post + +size: + width: 1200 + height: 1200 + dpr: 2 + +project: + name: "spec-author-corrections-fixed" + author: "Filip Sajdak, Founder & CEO" + email: "office@scudoai.com" + date: "2026-10-26" diff --git a/social/linkedin/spec-author-corrections-fixed/content.md b/social/linkedin/spec-author-corrections-fixed/content.md new file mode 100644 index 00000000..383e1341 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/content.md @@ -0,0 +1,12 @@ +--- +template: social/linkedin-post +--- + +::::post{type=insight theme=dark logo=top-left} + +:::insight{citation="wro.cpp -- 2026-10-26"} +# 193 microseconds became 320 nanoseconds +Per removal from a hash of 10,000 entries, after the spec's author confirmed two problems: a row map rebuilt after every change, and a removal announced after the rows were gone. +::: + +:::: diff --git a/social/linkedin/spec-author-corrections-fixed/image.png b/social/linkedin/spec-author-corrections-fixed/image.png new file mode 100644 index 00000000..a21d951a Binary files /dev/null and b/social/linkedin/spec-author-corrections-fixed/image.png differ diff --git a/social/linkedin/spec-author-corrections-fixed/index.html b/social/linkedin/spec-author-corrections-fixed/index.html new file mode 100644 index 00000000..1df51098 --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/index.html @@ -0,0 +1,28 @@ + + + + + + spec-author-corrections-fixed + + + + + + + + + + +

193 microseconds became 320 nanoseconds

Per removal from a hash of 10,000 entries, after the spec's author confirmed two problems: a row map rebuilt after every change, and a removal announced after the rows were gone.

wro.cpp -- 2026-10-26
ScudoAI
AI-generated
+ + \ No newline at end of file diff --git a/social/linkedin/spec-author-corrections-fixed/media/.gitkeep b/social/linkedin/spec-author-corrections-fixed/media/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/social/linkedin/spec-author-corrections-fixed/media/README.md b/social/linkedin/spec-author-corrections-fixed/media/README.md new file mode 100644 index 00000000..bb81c7fa --- /dev/null +++ b/social/linkedin/spec-author-corrections-fixed/media/README.md @@ -0,0 +1,9 @@ +# Media directory + +Drop images here (PNG, JPG, SVG) for use in your post. + +Reference them in your content.md with: + + :::image{src="screenshot.png" alt="Alt text" position="top"} + +The renderer resolves paths relative to this media/ directory. diff --git a/src/content/posts/2026-10-22-qt-model-edge-cases-catalogue.mdx b/src/content/posts/2026-10-22-qt-model-edge-cases-catalogue.mdx index 494a0bf5..4e62c525 100644 --- a/src/content/posts/2026-10-22-qt-model-edge-cases-catalogue.mdx +++ b/src/content/posts/2026-10-22-qt-model-edge-cases-catalogue.mdx @@ -47,7 +47,7 @@ The protocol tests found five gaps in the models that a view can see. Three of t - EditRole. The model said a cell was editable but answered nothing for `Qt::EditRole`. A default editor opens empty. Committing it without typing writes an empty value back to the entry. - A foreign index. The data and edit functions never checked the index. An index of another model, or column 1 of a table given to a list model, was read as the row with the same number. -- A removal that reads empty. Qt wants the data of a removed row to be there while `rowsAboutToBeRemoved` is announced. A batch removal erased the entries first, so those rows read as empty. The spec I ported from had the same property on purpose. +- A removal that reads empty. Qt wants the data of a removed row to be there while `rowsAboutToBeRemoved` is announced. A batch removal erased the entries first, so those rows read as empty. The spec I ported from had the same property, and I first took that for a deliberate choice. Its author has since said it was a mistake. The two bindings of that spec now announce a batch removal before they erase (see the follow-up). - Zero columns. A table with no columns emitted `dataChanged` with two invalid indexes. - QML. Nothing to bind a row count to: `rowCount()` has no notify signal, so a binding stayed stale while `ListView.count` followed. diff --git a/src/content/posts/2026-10-22-regenerate-qt-containers-from-a-spec.mdx b/src/content/posts/2026-10-22-regenerate-qt-containers-from-a-spec.mdx index e4602572..c2d7afbc 100644 --- a/src/content/posts/2026-10-22-regenerate-qt-containers-from-a-spec.mdx +++ b/src/content/posts/2026-10-22-regenerate-qt-containers-from-a-spec.mdx @@ -59,12 +59,16 @@ The 15 readings and the places the spec could not be applied to binding B are in ## Where the spec looks wrong -Section 8.3 calls `beginRemoveRows` after the container has already erased the entries, so `data()` of a doomed row is invalid during the cycle. A proxy or view that reads those rows in `rowsAboutToBeRemoved` sees nothing. The spec chose this on purpose, so it is a trade-off to state, not a bug. +Section 8.3 calls `beginRemoveRows` after the container has already erased the entries, so `data()` of a doomed row is invalid during the cycle. A proxy or view that reads those rows in `rowsAboutToBeRemoved` sees nothing. I took the spec's own wording at face value and called this a trade-off. + +- **Update, 23 October.** The spec's author replied that I had it wrong: "Section 8.3 is a mistake in the spec, not a trade-off." The removal cycle should start at the "about" notification. The entry is still in the container then, so a view can read the row it is losing. Both bindings are changed to do that, in the next post. Section 7.1 says a value already equal changes nothing, but `replace` always notifies. The `Self = C` template trick that defers the `static_assert` can be defeated. A caller can name another container type in the call and skip the check. The cost is in section 8.1. The list model keeps a lazy key-to-row map that every change marks stale and the next lookup rebuilds. So the next removal after any change pays for a rebuild. That is 11 microseconds per removal at 1,000 hash entries and 193 microseconds at 10,000, in both bindings. The row index in the follow-up costs 167 to 473 nanoseconds per change at 100,000 hash entries and 10,000 map entries. The row order differs, because the spec sorts rows by key and my models keep insertion order for a hash. Read it as an indication, not a like-for-like comparison. +- **Update, 23 October.** The author confirmed that the real list model does the same, and put the measurement on the team's list. Both bindings now find a row by binary search, and the same drain costs 320 and 346 nanoseconds at 10,000 hash entries. + ## What binding A costs against binding B One source, built against each binding through the same API, seven runs alternating the two binaries. Medians on a GitHub x86_64 runner, with a spread mostly under 5 percent. A gate runs first: both bindings pass their tests, and a copy of each whose list model removes runs in the wrong order is caught. diff --git a/src/content/posts/2026-10-26-spec-author-corrections-fixed.mdx b/src/content/posts/2026-10-26-spec-author-corrections-fixed.mdx new file mode 100644 index 00000000..3d083351 --- /dev/null +++ b/src/content/posts/2026-10-26-spec-author-corrections-fixed.mdx @@ -0,0 +1,45 @@ +--- +title: "Two problems confirmed by the spec's author, fixed in both bindings" +slug: "spec-author-corrections-fixed" +pubDate: 2026-10-26 +author: filip-sajdak +language: en +kind: short +series: cpp26-reflection +series_order: 40 +audience: working-cpp +tags: [reflection, cpp26, qt, models, testing, ai] +summary: "The author of the container spec wrote that section 8.3 is a mistake, not a trade-off, and that the lazy row map is in the real code. Both generated bindings now announce a batch removal while the entries still exist and find a row by binary search: a hash drain at 10,000 entries went from 193 microseconds per removal to 320 nanoseconds." +draft: false +relatedPosts: [regenerate-qt-containers-from-a-spec, qt-model-edge-cases-catalogue] +--- + +import PostLink from '../../components/PostLink.astro'; + +Leonardo Rossi, who wrote the spec, answered the post about regenerating it with two corrections from the real code. Section 8.3 "is a mistake in the spec, not a trade-off", and "I wrote it the wrong way round." The lazy row map is real too: the real list model also rebuilds its index after every change. I had called the first one a trade-off, because the spec says so. Both bindings are now changed by hand, each change with a test that fails on the old code. + +## What was wrong in 8.3 + +The spec has a batch removal erase the entries first and announce the removed rows afterwards. A view that reads a row in `rowsAboutToBeRemoved` then finds nothing. Qt expects the row to be readable there. The real model kit starts the removal at the "about" notification, while the entry is still in the container. + +Both list models now announce every run of the batch in that handler. A new test removes scattered keys with `removeMany` and with `takeMany`, from a map and from a hash. It reads each doomed row while the row is announced, and it fails on the old models. The tests that had pinned the old behaviour now assert the values. + +## What the lazy map cost + +Every change marked the key-to-row map stale, and the next lookup rebuilt it. The rows were already sorted by key, so a row is now found by binary search, and nothing is rebuilt. The matrix kept the same kind of lazy index for rows and columns. It now updates the index where it changes: an append adds keys, a rename moves one, an erase renumbers the rows after it. + +| operation on a hash, x86_64 | binding A before | binding A after | binding B before | binding B after | +|---|---|---|---|---| +| drain, 1,000 entries, per removal | 11.2 µs | 224 ns | 11.7 µs | 236 ns | +| drain, 10,000 entries, per removal | 193 µs | 320 ns | 198 µs | 346 ns | +| `removeMany` of 100 contiguous keys, 10,000 entries | 454 µs | 14.6 µs | 455 µs | 7.1 µs | + +Seven runs on a GitHub runner, medians, same script as before. The rows that never touch the lookup, such as fill, replace, `insertMany` and the matrix `setValues`, stayed within about 10 percent. I did not measure the Mac again. + +## How it was checked + +- Random mixes of inserts, removals, batches, replaces and clears compare the new lookups with the container after every step. A second mix does the same for the matrix indexes. +- The mutation script covers both trees. Binding A: 53 mutants, 52 killed, one judged equivalent. Binding B: 54 mutants, 53 killed, one judged equivalent. One mutant survived the first run, which showed that a single removal still needed its own test, and that test is added. +- Both bindings still pass their 17 acceptance entries, and the MSVC job for binding A passes. + +The changes are hand edits to generated code, listed in the [README](https://github.com/wrocpp/reflect-moc/tree/b2534166aa858f1b5acde22b7c1cc087d27ec4e3/examples/qt/spec_regen). The agents' own reports are in `notes` next to it. The spec itself stays as it is until its author publishes the next version, and then I will regenerate from that. diff --git a/src/content/posts/2026-10-26-googletest-live-at-head.mdx b/src/content/posts/2026-11-03-googletest-live-at-head.mdx similarity index 99% rename from src/content/posts/2026-10-26-googletest-live-at-head.mdx rename to src/content/posts/2026-11-03-googletest-live-at-head.mdx index 02699924..6b86aaf9 100644 --- a/src/content/posts/2026-10-26-googletest-live-at-head.mdx +++ b/src/content/posts/2026-11-03-googletest-live-at-head.mdx @@ -1,7 +1,7 @@ --- title: "GoogleTest tells you not to use its releases" slug: "googletest-live-at-head" -pubDate: 2026-10-26 +pubDate: 2026-11-03 author: filip-sajdak language: en kind: short