feed: add a special missings post #19

Merged
allan merged 14 commits from blog/special-missings-education into main 2026-09-23 22:42:32 +00:00
Collaborator

Adds /feed/sas-special-missings/ - "28 Ways to Be Missing in SAS".

Why a feed post, not a blog post

The special missings material was originally added to content/blog/v4-0-formats-special-missings/. That article is the v4.0 release announcement, so teaching copy does not belong in it. This revision reverts those blog edits and publishes the content as its own feed post instead - the branch is now a single commit adding the feed post, with no blog changes.

What the post covers

  • What special missings are: 28 numeric missing values, why they exist, and how they behave as numbers (arithmetic, PROC MEANS, sort order, NMISS/CMISS, CATS)
  • How Data Controller carries them between the browser and SAS: the SASjs Adapter's type inference, the explicit-format path for columns whose type cannot be inferred, and the values it refuses
  • That the period is optional when typing one: .a and a are the same missing
  • What the validation rules do with them: NOTNULL rejects one, CASE compares as text, HARDREGEX/SOFTREGEX apply rather than exempting them, and the range rules compare in SAS's own order - so MINVAL .A with MAXVAL .C accepts .B and rejects .D, a missing fails a numeric MINVAL and passes a numeric MAXVAL, and a number sits above every missing
  • That the range rules compare in SAS's own order, so MINVAL .A with MAXVAL .C accepts .B and rejects .D
  • The "Special Missings in Data Controller" recording, embedded from vid.4gl.io as a responsive PeerTube iframe (replacing the older YouTube embed, and dropping a stray video: [...] line that was rendering as literal text in the body)

Verified

npm run build succeeds; the page renders at /sas-special-missings/ with the video iframe, it appears in the feed listing and in /rss.xml, and the blog post is back to its original content.

Notes

  • No cover image yet. The image prompt is recorded in a comment at the foot of the file - drop the generated image in as ./cover.jpeg and add previewImg: './cover.jpeg' to the front matter.
  • Reverting the blog edits also drops the one-line video label fix that was in the earlier revision of this PR. The blog post still labels the special missings video as "Retain Formulas when Loading Excel to SAS", which is the wrong video title - worth a separate one-line PR if you want it corrected.
Adds `/feed/sas-special-missings/` - "28 Ways to Be Missing in SAS". ### Why a feed post, not a blog post The special missings material was originally added to `content/blog/v4-0-formats-special-missings/`. That article is the v4.0 **release announcement**, so teaching copy does not belong in it. This revision reverts those blog edits and publishes the content as its own feed post instead - the branch is now a single commit adding the feed post, with no blog changes. ### What the post covers - What special missings are: 28 numeric missing values, why they exist, and how they behave as numbers (arithmetic, PROC MEANS, sort order, NMISS/CMISS, CATS) - How Data Controller carries them between the browser and SAS: the SASjs Adapter's type inference, the explicit-format path for columns whose type cannot be inferred, and the values it refuses - That the period is optional when typing one: `.a` and `a` are the same missing - What the validation rules do with them: NOTNULL rejects one, CASE compares as text, HARDREGEX/SOFTREGEX apply rather than exempting them, and the range rules compare in SAS's own order - so `MINVAL .A` with `MAXVAL .C` accepts `.B` and rejects `.D`, a missing fails a numeric `MINVAL` and passes a numeric `MAXVAL`, and a number sits above every missing - That the range rules compare in SAS's own order, so `MINVAL .A` with `MAXVAL .C` accepts `.B` and rejects `.D` - The "Special Missings in Data Controller" recording, embedded from vid.4gl.io as a responsive PeerTube iframe (replacing the older YouTube embed, and dropping a stray `video: [...]` line that was rendering as literal text in the body) ### Verified `npm run build` succeeds; the page renders at `/sas-special-missings/` with the video iframe, it appears in the feed listing and in `/rss.xml`, and the blog post is back to its original content. ### Notes - No cover image yet. The image prompt is recorded in a comment at the foot of the file - drop the generated image in as `./cover.jpeg` and add `previewImg: './cover.jpeg'` to the front matter. - Reverting the blog edits also drops the one-line video label fix that was in the earlier revision of this PR. The blog post still labels the special missings video as "Retain Formulas when Loading Excel to SAS", which is the wrong video title - worth a separate one-line PR if you want it corrected.
hermes added 1 commit 2026-09-22 17:53:58 +00:00
The special missings material that was added to the v4.0 blog post belongs in
its own post: that article is a release announcement for v4.0, not a teaching
piece. This reverts the blog change and publishes the content under /feed/.

The new post covers what SAS special missings are, how the SASjs Adapter
carries them between the browser and SAS, and how Data Controller's validation
rules treat them - NOTNULL passes, MINVAL fails, MAXVAL passes, and the regex
rules apply as they would to any other value.

The LinkedIn version of the copy, and the image prompt for the cover, are
recorded in a comment at the foot of the file.
hermes force-pushed blog/special-missings-education from d059b72f6b to 4937edd7f4 2026-09-22 17:53:58 +00:00 Compare
hermes changed title from blog: teach special missings properly, and fix the video label to feed: add a special missings post 2026-09-22 17:54:12 +00:00
hermes added 1 commit 2026-09-22 18:10:31 +00:00
The special missings video was labelled "Retain Formulas when Loading Excel
to SAS", which is a different video. Restores the one-line correction that
was in the earlier revision of this branch, kept separate from the feed post.
hermes left a comment
Author
Collaborator

Reviewed the full base-to-head diff (new feed post plus the blog video-label fix) and repo-level hardening at this head.

  • content/feed/sas-special-missings/index.md:1-10 - front matter carries no previewImg and the post directory contains only index.md, no cover image. The file's own instructions at index.md:93 say to save the generated cover as ./cover.jpeg and set previewImg: './cover.jpeg' in the front matter. The templates tolerate the omission (src/templates/postpreview/index.tsx:50 renders the image conditionally), so the build stays green, but the post ships into the feed list and social shares with no image; 14 of the 16 existing feed posts carry a cover. Generate the cover and add previewImg before merge, or drop the prompt comment if the image is landing separately.

  • Repo-level, standing (not introduced by this PR): no .pre-commit-config.yaml and no gitleaks hook anywhere in this repo, so no staged-content secret scan exists at commit time. Add .pre-commit-config.yaml with repo: https://github.com/gitleaks/gitleaks, rev pinned to the exact current release tag (v8.30.1 at time of review), hooks: - id: gitleaks.

  • CI (.gitea/workflows/publish.yml and .github/workflows/publish.yml): npm ci + build + publish on push to main only. No npm audit step exists in any workflow and no pipeline runs on pull requests, so nothing gates dependency vulnerabilities or a broken build before merge. Add a strict audit step (default audit level, hard failure, no continue-on-error) and run at least the build on pull_request.

  • Pre-existing (not from this PR): all 37 entries across dependencies and devDependencies in package.json are ^ ranges, contradicting the committed .npmrc save-exact=true; the lockfile holds the line today but the ranges invite drift on the next dependency touch. Also .gitignore has no .env or .env.* entry, although Gatsby conventionally loads configuration from .env files.

No semantic-release or conventional-commit tooling in this repo, so the non-conventional commit subjects (feed:, blog:) have no release effect.

4 findings above for review.

Reviewed the full base-to-head diff (new feed post plus the blog video-label fix) and repo-level hardening at this head. - content/feed/sas-special-missings/index.md:1-10 - front matter carries no previewImg and the post directory contains only index.md, no cover image. The file's own instructions at index.md:93 say to save the generated cover as ./cover.jpeg and set previewImg: './cover.jpeg' in the front matter. The templates tolerate the omission (src/templates/postpreview/index.tsx:50 renders the image conditionally), so the build stays green, but the post ships into the feed list and social shares with no image; 14 of the 16 existing feed posts carry a cover. Generate the cover and add previewImg before merge, or drop the prompt comment if the image is landing separately. - Repo-level, standing (not introduced by this PR): no .pre-commit-config.yaml and no gitleaks hook anywhere in this repo, so no staged-content secret scan exists at commit time. Add .pre-commit-config.yaml with repo: https://github.com/gitleaks/gitleaks, rev pinned to the exact current release tag (v8.30.1 at time of review), hooks: - id: gitleaks. - CI (.gitea/workflows/publish.yml and .github/workflows/publish.yml): npm ci + build + publish on push to main only. No npm audit step exists in any workflow and no pipeline runs on pull requests, so nothing gates dependency vulnerabilities or a broken build before merge. Add a strict audit step (default audit level, hard failure, no continue-on-error) and run at least the build on pull_request. - Pre-existing (not from this PR): all 37 entries across dependencies and devDependencies in package.json are ^ ranges, contradicting the committed .npmrc save-exact=true; the lockfile holds the line today but the ranges invite drift on the next dependency touch. Also .gitignore has no .env or .env.* entry, although Gatsby conventionally loads configuration from .env files. No semantic-release or conventional-commit tooling in this repo, so the non-conventional commit subjects (feed:, blog:) have no release effect. 4 findings above for review.
hermes added 1 commit 2026-09-22 21:17:44 +00:00
Replaces the placeholder one-liner with the prompt actually intended for the
cover: the pack of wolves on a snow plain at dusk with one animal standing
apart, plus the negative constraints, the output spec (./cover.jpeg at
1200x627, previewImg in front matter) and a fallback single-subject variant.

Two deliberate choices are recorded so they are not lost on a regenerate:

- No text, letters or numbers anywhere in the image. The subject is letters
  standing in for numbers, so a stray glyph undercuts the cover.
- No exact head count of 28. Generators cannot count, and a crowded pack reads
  worse than a dozen clear animals; the number belongs in the headline.

Links to the full prompt and variants on paste.4gl.io.
hermes added 1 commit 2026-09-22 21:36:41 +00:00
Cover art for "28 Ways to Be Missing in SAS": a pack of wolves on a snow plain
at dusk with one animal standing apart, carrying the post's opening line - a
numeric missing is not a lone wolf, there are 28 of them.

Source image was 4:3, so it is cropped to 1.91:1 (1200x627) to match the other
feed covers and double as the LinkedIn share card. The crop was chosen to keep
both the lone wolf on the left and the full pack on the right in frame, losing
only sky above the clouds and foreground snow.

Sets previewImg in the front matter; the template renders it, so it is not
embedded in the body as well. Verified through gatsby build - the image
pipeline emits 300/600/1200-wide variants.
hermes added 1 commit 2026-09-23 07:17:52 +00:00
- heading + intro now say these are Data Controller's MPE_VALIDATIONS rules,
  applied in the browser
- new paragraph on options MISSING: a regular missing prints as . unless the
  option changes it (eg to blank); special missings are never affected
- CASE split out of the rule list: it is a character rule, and a special
  missing always reaches the browser as an uppercase letter
- HARDREGEX and SOFTREGEX separated (SOFTREGEX warns rather than blocks, and
  is ignored when the column also has a HARDREGEX)
- LinkedIn copy kept in sync

Behaviour confirmed against the deployed services on a real Viya estate.
hermes added 1 commit 2026-09-23 07:29:07 +00:00
Verified on a real SAS estate:
- a physical NOT NULL (or primary key) constraint rejects a special missing,
  and getdata merges that constraint into a frontend NOTNULL rule - which
  passes a special missing, so the editor is more lenient than the constraint
- a special missing as a MINVAL rule value fails every cell; as a MAXVAL rule
  value it fails every real number
- a HARDFORMULA/SOFTFORMULA reading a special-missing cell returns #VALUE!
- PRX and the JS engine agree on the value; SAS pads the numeric-to-character
  conversion, so an anchored pattern re-used in SAS needs strip()
hermes added 1 commit 2026-09-23 07:48:46 +00:00
The validator fix lands in dc/dc (PR #323), so the post describes the fixed
behaviour rather than the mismatch: a special missing fails NOTNULL, matching
a physical SAS NOT NULL / primary key constraint.

Also records what ROUND and SOFTSELECT/HARDSELECT do with one, and groups
MINVAL/MAXVAL/HARDFORMULA/SOFTFORMULA under 'not supported'.
hermes added 1 commit 2026-09-23 08:19:37 +00:00
Also reflects the HARDSELECT fix: a strict dropdown now accepts a special
missing when the list contains it.
hermes added 1 commit 2026-09-23 08:37:09 +00:00
hermes added 1 commit 2026-09-23 15:38:44 +00:00
hermes added 1 commit 2026-09-23 21:01:21 +00:00
Three corrections to the special missings post:

- a special missing is typed with or without its leading period - `.a` and `a`
  are the same missing
- MINVAL and MAXVAL both accept a special missing, and both still reject a real
  number that is out of range. A minimum constrains a number, and a missing is
  not a number; NOTNULL is the rule for a column that must be populated
- the formula rules are the one case where a special missing genuinely does not
  work, so the range rules are no longer grouped with them
hermes added 1 commit 2026-09-23 21:27:59 +00:00
A range rule keys both sides into the order SAS uses for a numeric variable, so a
range written in special missings means what SAS would mean by it: MINVAL .A with
MAXVAL .C accepts .B and rejects .D, a blank fails a floor of .A, and a number
sits above every missing - passing a floor of .A and failing a ceiling of .C.
Against a numeric bound the same order gives the obvious answer: a missing fails
MINVAL 1 and passes MAXVAL 100.

Replaces the "the range rules step aside for a missing value" wording.
hermes added 1 commit 2026-09-23 22:00:19 +00:00
Embeds the "Special Missings in Data Controller" recording from vid.4gl.io as a
responsive PeerTube iframe, in place of the older YouTube embed, and drops the
stray `video: [...]` line that was sitting in the body as literal text.

Copy corrections, each verified against the running app rather than the source:

- the range rules compare in SAS's order, so MINVAL .A with MAXVAL .C takes .B
  and refuses .D, a missing fails a numeric MINVAL and passes a numeric MAXVAL,
  and a number sits above every missing. The old wording said the range rules
  step aside for a missing value
- a lone "." is another way of typing the regular missing; the previous claim
  that a literal "." is refused in favour of null is not what happens
- a strict dropdown does accept a special missing when its list holds one (the
  list shows it as a bare letter), and still rejects a value that is not listed
- NOTNULL rejects a special missing, and a primary key column is NOT NULL
  whether or not a rule is configured for it

The LinkedIn version of the post is kept in sync, and carries the new video URL.
hermes added 1 commit 2026-09-23 22:41:14 +00:00
The LinkedIn opener did not parse - "A SAS numeric missing is not a lone wolf.
It is 28." - and reading the rest of the post and its LinkedIn version together
turned up a further set of style defects:

- the article opener made the missing value the wolf, and padded the count
- "All numeric values means numeric; all strings means character" was clumsy
- procedure and function names were lowercase in places (proc means, cats,
  options MISSING), and "and friends" was too casual for a list of procedures
- "gotcha" and "harmless" undersold the points they introduced
- "One thing worth knowing" opened two separate paragraphs
- "a value ... and as a value with its own place" repeated itself, in both the
  article and the LinkedIn version
- the adapter bullet list mixed trailing periods with none
- the LinkedIn list was split in two by a stray blank line, and "a physical
  constraint" did not say which constraint
- "the most common of them" echoed the "28 of them" in the line above

No claims changed - the range-rule behaviour, the period, the primary key and
the strict dropdown all read as they did.
allan merged commit 59ff2b13fc into main 2026-09-23 22:42:32 +00:00
allan deleted branch blog/special-missings-education 2026-09-23 22:42:32 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/datacontroller.io#19