Decision artifact2026-08-17corrected 2026-08-18decided 2026-08-18gsm.safety v1.1.0 — published

SafetyCensus() shipped: does it stay as public API, or get deprecated out?

gsm.safety v1.1.0 published on 17 August with this function exported and named in its notes — sixteen minutes before this page was written saying the release was held for your answer. That framing is corrected below and the review underneath it is not: seven adversarial reviewers, six briefed to argue the function out of the package and one briefed to keep it, every factual claim checked by an independent fact-checker. What survived stands. What changed is the choice it feeds.

Requirement #229 Task #230 Release gsm.safety v1.1.0 — published 2026-08-17 05:42 UTC (PR#52) Panel 7 reviewers + 7 fact-checkers Worker W0023 Corrected 2026-08-18 · W0071 · #268
DECIDED

What he decided

@jwildfire · 2026-08-18 · dictated to obot-prime in chat from his phone, after the audio episode

“I think that the overall goal of the function stands, so we shouldn't deprecate it. Like, there's a reason that it was created… I think that we should probably keep the function or at least keep an alias that uses the same function name instead of fully deprecating.

Now how that works — I think the function was designed really, really badly and needs a bunch of changes and a big change in approach.

I think that we should model it after the gsm.kri report, where these core numbers that right now are clearly wrong are metrics. Not every metric has to have an action associated with it. So number of deaths should be a metric, not some code wrapped deep in the safety census function. Right now those three safety metrics we have are all, in theory, actionable and flaggable, but that's not the only point of metrics. The point of metrics is to have trustable numbers that we can qualify and validate.

So the safety census information is important, and we should use the gsm metric framework to calculate all of the numbers. And then the census itself probably should just be a report, perhaps with some charting functions as well. The way we do charts is kind of evolving in gsm, but for now the way it works in safety.viz and also in gsm.viz is fine for this.

You need to redesign the safety census report using metrics and reports instead of trying to have this huge function that does a bunch of work on its own. It should mostly be workflows and then helper functions that the workflows call, instead of this massive function that does all of the work.

It stays, but with a major, major refactor, that probably you'll need to design first and then implement, and you may need to ask me questions, and that's fine.”

C1 · D0021.1 · answered

It stays. Not deprecated — rebuilt.

The function is not deprecated and the name survives: he asked for the function kept, or at minimum an alias preserving SafetyCensus(). What changes is everything underneath it. The core numbers become metrics in the gsm metric framework, modelled on the gsm.kri report; the census becomes a report over them, possibly with charting functions in the way safety.viz and gsm.viz already do; and the shape is workflows plus helper functions the workflows call, not one large function doing all the work. He asked for the design first and the implementation second, and said asking him questions along the way is fine.

That is a design brief rather than a verdict, and it is on requirement #274, filed from this decision, which is where the refactor lives from here. This page records the answer; it does not start the work.

C2 · D0021.2 · answered by consequence

The requirement is filed — for a rebuild in place, not a return trip.

C2 asked whether the census comes back lane-shaped in a later release or stays demo-local. It never leaves, so the question resolves into the same requirement from the other direction: the per-visit coverage idea, the blinding stance and the NA-discipline the panel judged worth keeping are carried into #274 rather than into a replacement filed after a removal. The recommendation on C2 was to file the requirement. It is filed.

The recommendation is not overruled — it is answered on a different question

Nothing the panel found is retracted here. Run on the ecosystem's own bundled study the function reports one death where the death records hold at least twelve; it prints false zeros when a column is missing; it lets numerators pass their denominators; it reads a column dialect the specs do not use. Seven lenses, forty-seven claims, every one independently fact-checked, none refuted. Those findings stand exactly as published, and his answer does not touch a single one of them.

The panel judged the implementation and found it indefensible. He is judging what the census is for — a standing safety summary for open.gismo — and that purpose survives bad code. Both were right about different things. The recommendation said the numbers cannot be trusted, which is true; he is saying the answer to numbers that cannot be trusted is to make them trustable, not to remove the thing that needed them.

The load-bearing sentence is about metrics rather than about the census: “Not every metric has to have an action associated with it… The point of metrics is to have trustable numbers that we can qualify and validate.” That cuts against how gsm.safety's existing three metrics were built — all of them flaggable and actionable — and it is what makes the rebuild possible: a death count earns its place in the framework by being a number you can validate, whether or not anything fires on it. Everything else in the brief follows from that.

A note on the transcription, and on the channel

His words reached this page through dictation and three names arrived mangled. They are rendered above rather than left as noise, and named here rather than silently fixed: “GSMK arrive report” is the gsm.kri report, “safety biz” and “GSM biz” are safety.viz and gsm.viz, and “open gizmo” is open.gismo. Nothing else in the quotation is altered — the disfluencies are his and are kept.

The channel is part of the record. He listened to the audio episode of this page and dictated his answer straight into chat from his phone to 🎩🤖 obot-prime — the second decision taken that way on 2026-08-18, after D0020 that morning. Recorded by task #276.

CORRECTION — POSTED 2026-08-18
The premise expired · 2026-08-18

The release this page says is held was already published when the page was written

As published on 17 August this page said gsm.safety v1.1.0 was held at the tag, unpublished pending your answer, and that this was the last moment removing SafetyCensus() would not be a breaking change. Its headline read SafetyCensus(): stays or goes, before v1.1.0 publishes, and its opening section said publication is therefore held at the tag, the exit is still cheap. None of that was true, and none of it was true at the moment it was written.

Checked against the release object itself rather than inferred from the review:

  • v1.1.0 was created 2026-08-17 05:40:40 UTC and published 05:42:53 UTC — not a draft, not a pre-release, marked Latest.
  • This page was committed at 05:58 UTC and its Q&A thread opened at 05:58:59 UTC — both about sixteen minutes after publication.
  • export(SafetyCensus) is line 7 of NAMESPACE at the v1.1.0 tag and on main today, and the function is a headline bullet in the published release notes.

You lifted the hold yourself that morning, acting on your own merging-is-commitment principle. The review kept running against a world that had already changed, and no surface — not this page, not the thread, not the delivery record — noticed.

What this changes, and what it does not

Only the framing. Every verified defect below stands exactly as the panel found it: the death count, the false zeros, the ghost IDs, the wrong column dialect, the workflow-orphan shape. So does the recommendation, reproduced further down in the words it was published in. Nothing about the function changed — what changed is what you are choosing between.

The question is no longer whether this ships. It shipped. The question is whether SafetyCensus() stays as public API of a clinical package, or is deprecated now and removed in the next version. Same evidence, different choice, and the difference is the price: the cheap exit this page argued for is gone, and removal now costs a deprecation cycle — the path every lens on the panel priced worst.

D0021 stays open and keeps its numbers. D0021.1 and D0021.2 still resolve; C1's wording is reframed to the choice that is actually open, and C2 is unaffected.

Below, expired claims are struck through in place with the corrected reading beside them, rather than deleted — a page that quietly changes what it said is worse than one that was wrong. The requirement this correction belongs to is #266, that an artifact should notice when its own premise expires; the correction itself is #268.

THE SITUATION

gsm.safety v1.1.0 is merged and was minutes from publication when you flagged its new SafetyCensus() function — a study-census reducer that counts enrollment, exposure, deaths, disposition and per-visit data coverage — as messy-looking and asked for an adversarial review deciding whether it stays or goes. The function is exported, and gsm.safety is a clinical package: the moment the release publishes, it becomes public API, and removing it afterwards is a breaking change with a deprecation cycle rather than a decision not to ship. Publication is therefore held at the tag, the exit is still cheap, and this page exists so you can close it with one answer.

As corrected, 2026-08-18. The release published at 05:42 UTC that morning, before this page existed. The function is public API of a clinical package today and has been since, so removing it is a breaking change carrying a deprecation cycle — that is the starting position now, not a consequence to be avoided. This page exists so you can close the question that is actually open: it stays, or it is deprecated out.

The one-sentence version

The review found the function is genuinely used by the live demo but produces silently wrong clinical figures — including a death count of one where the ecosystem's own data records at least twelve — and the recommendation, formed only after all seven reviewers had reported, is that it comes out and returns properly built later.

As published: "…is that it comes out before the release publishes and returns properly built later." It did not come out before the release published. The direction is unchanged; the only route left is deprecate, then remove.

HOW THE REVIEW RAN

Adversarial here means refute, not assess. Six reviewers each took one lens — pipeline fit, column conventions, testability, duplication, comprehensibility, and actual usage — and each started from the position that the function should go, required to argue it with evidence anchors for every claim. A seventh reviewer, the steelman, had the opposite job: keep it, arguing the strongest honest case for the function as it stands. All seven worked independently and could not see each other's reports.

Nothing reached this page on a reviewer's word alone. Each report went to a separate fact-checker briefed to reproduce every citation and re-run every probe — 47 claims produced 49 checked verdicts: 29 confirmed as written, 20 overstated in strength (with the accurate version recorded and used here instead), and none refuted outright. Where a number below is called verified, a second agent independently reproduced it, usually by executing the function in R.

The recommendation at the bottom was formed only after every lens and every fact-check had reported — no verdict was drafted, or leaned toward, before the panel finished.

The panel corrected its own briefing twice

Two facts in the commissioning brief did not survive the review, and both corrections cut in the function's favor. The brief said nothing reads the census file the demo study writes — wrong: that came from a stale local checkout, and the deployed demo app fetches and renders it live. The brief also said no issue anywhere asked for a census — too strong: no issue body does, but a decision record on the demo requirement's thread, posted 52 minutes before the function merged, sanctions a safety overview shaped as exposure-and-census, pending clinical input (demo metrics requirement). The steelman-and-verify structure caught both, which is the reason it exists.

WHAT SURVIVED THE REVIEW

The demolition landed on correctness

The heart of the case against is not style — it is that three independent lenses, checked separately, each demonstrated the function printing wrong clinical figures without any warning. Everything below was reproduced by a fact-checker running the code.

Verified — the death count

It under-counts deaths on the ecosystem's own data

The function's only source of deaths is exact string-matching on the study-completion form's reason column. Run with its defaults on the gsm ecosystem's own bundled study, it reports one death — the dedicated death-record table in the same bundle holds twelve, and the mapping layer's own death helper counts thirteen. Feeding it that death table directly returns zero, because it is not the domain the function reads. Reasons like "SUDDEN DEATH" or "DEATH DUE TO PROGRESSION" yield a definite zero, not a missing value, and in one verified run the same output printed a death count of zero while its own disposition table named a death state on the next rows.

Verified — false zeros and inflated counts

Untested paths violate the invariants the tests certify

The ten existing tests are genuinely good on the paths they cover, and all pass. Every failure the panel produced lives one step off those paths, uncovered:

  • If the treatment-time column is absent, "Received study drug" reports zero participants dosed — a false clinical zero, in a function whose own test suite certifies the principle that absent data is NA, never zero.
  • IDs in the disposition table that are not enrolled inflate numerators past denominators: five "Completed" in a four-person study. The ghost-ID guard the tests celebrate for lab data is missing here, and from the coverage counts too.
  • Duplicate rows double-count person-time and dosed counts; a blank completion reason produces the label "Discontinued - NA".
  • All eleven column-name parameters are tested only at their defaults — the entire configurability surface that justifies the sixteen-parameter signature has zero coverage.
Verified — the wrong dialect

Its column defaults miss the mapped pipeline it claims to sit on

On the mapped domains the gsm pipeline actually produces, "Randomised to an arm" silently returns NA (no mapped domain carries an arm column — the blinded pipeline deliberately surfaces randomization as a status domain instead), and visit ordering silently degrades to alphabetical — Week 12 sorting before Week 2 — exactly the behavior its own test forbids. The defaults were proven against one study's bespoke mapping: the demo's. The man page, meanwhile, documents almost none of the real semantics — that "expected" per visit just means total enrollment repeated with no visit schedule, that the dosed figure is a proxy for treatment-time greater than zero, or where the zero-versus-NA boundary sits — so a stranger reading the docs would misread the numbers even doing everything right.

Verified — shape and drift

It is the package's one workflow-orphan, and a second counting lane

It is the only export in gsm.safety that is neither a workflow step nor referenced anywhere in the package's workflows, examples or widgets — its three sibling functions from the same pull request each got a workflow. Its return shape carries no subject or group key, so the pipeline's own machinery rejects it at the summarize step. And where it overlaps the pipeline it drifts: it is a second, parallel counting lane whose enrolled count and person-years agree with the pipeline's today, but whose death count already disagrees on the same study. Two death figures from one package on one study is drift built into public API.

The attack that failed, and what the steelman won

The stranded-helper attack collapsed under verification, and the keep case is real. It is worth stating plainly, because the recommendation goes the other way despite it.

Verified — it is used, today

A live, deployed consumer renders it right now

The chain runs end to end: the function → the demo study's pipeline (step three of its canonical runner) → a committed JSON payload → the demo app's fetch → the safety overview rendered on the live site, with real figures on screen. The v1.1.0 release notes link straight to that page. The consumer code is source-controlled and has its own test suite, which even preserves the function's NA-never-zero semantics through to the UI (live demo).

Verified — the keep case

It passed the human gate, and its design has real virtues

The release-candidate pull request you approved names the function twice in its visible prose — approval you made explicitly conditional on this review (the RC). The pooled-across-arms blinding stance is your recorded decision, FDA-grounded, and the code honors it exactly. The absent-domain-means-NA discipline and the refusal to invent "ongoing" for participants the disposition data does not cover are more careful than the pipeline's own machinery in places. And the per-visit data-coverage table is genuinely novel — nothing anywhere in the ecosystem computes it.

What removal actually costs — verified, and less than feared

Nothing deployed breaks. The demo site is static and keeps serving its committed census file; the app catches an absent census and renders without it; the demo pipeline downgrades a failing census step to a printed note and exits clean. The only real consequences are that the census file stops being refreshed on future pipeline runs, one release-notes bullet and a few demo README lines need editing, and gsm.safety needs a removal PR and a release-candidate approval from you.

As corrected, 2026-08-18. The technical cost above holds — it was measured against the deployed chain, not against the release state. What is added on top is the deprecation cycle: the export cannot simply be dropped now, so removal is a deprecation notice in one release and the removal in the next, with the reason stated plainly in both sets of notes. The v1.1.0 release notes themselves cannot be edited away from what they already claim; the honest move is to say in the successor's notes what changed and why.

THE OPTIONS, IN RELEASE TERMS — REFRAMED 2026-08-18

One tension is named before the options, not after: the same day this review was commissioned, you set the principle that what is in main gets released, and deprecating later is fine provided it is done very transparently — merging, not publishing, is the commitment point. Holding this release for this review is an exception you created yourself, minutes from publication. The options below respect both facts: the principle is a real argument for shipping, and the hold is a real window you opened on purpose.

As corrected, 2026-08-18. There was no hold, and no exception. You applied that principle to this very release and published it while the review ran, which settles the shipping half of the tension by fact rather than by argument. What is left is the half the principle itself names — deprecating later, done very transparently — and the options below are what that looks like. They are the original Paths 2 and 3, reframed; the original Path 1 is struck out because the window it needed has closed.

Path 1 — withdrawn 2026-08-18, no longer available

Pull the export before publication; bring the census back properly later

A small PR removes the function, its tests and its release-notes bullet; you approve a fresh release candidate; v1.1.0 publishes clean, hours late. The demo keeps working on its committed data. What it costs: the removal PR and one more RC approval from you, a demo census that stops refreshing until a replacement lands, and the loss — temporarily — of a live surface's data source. What it forecloses: nothing. The census can return in a later release, lane-shaped and under a real requirement, keeping the coverage idea and the blinding stance.

Why it is gone. v1.1.0 published at 05:42 UTC on 17 August with the function exported and named in its notes, sixteen minutes before this page was written. There is no pre-publication window left to act in, and the removal it describes would now be a breaking change on shipped public API rather than a decision not to ship. Its second half survives untouched — the census returning later, lane-shaped, under a real requirement, keeping the coverage idea and the blinding stance — as C2 below and as the tail of Path B.

Path A — it stays (was Path 2)

Leave it as public API; fix forward transparently

Nothing is done to the export and the demo chain keeps refreshing. What it costs: the function is public API of a clinical package with its current behavior — a death count that is wrong on canonical data, false zeros on missing columns, numerators that can exceed denominators — and the fixes are not doc edits: correcting them changes published clinical figures, each a breaking behavior change on public API to be managed under your transparency rule.

As published: "Publication unblocks immediately… the function becomes public API… What it forecloses: the cheap exit. This is the last moment removing it is not a breaking change." Nothing unblocks, because nothing was blocked; the function already is public API; and the cheap exit was spent at 05:42 on 17 August. Choosing this path is now choosing to leave things as they are.

Path B — it goes, by the only route left (was Path 3)

Deprecate now, remove in the next version

A deprecation warning on the function in the next release with the reason stated plainly in its notes, the demo consumer migrated off it, and the export removed a version later. What it costs is exactly what the panel priced when this was Path 3 — and that pricing is carried forward rather than softened, because it is now the price of removal itself.

As published, as Path 3, named but not recommended: "Every lens that priced this path priced it worst: the wrong figures stand in a published safety tool for the whole interim, and the package then pays the deprecation cycle anyway. It is listed because it is the default of doing nothing after publication, not because anyone argued for it."

As corrected, 2026-08-18. That verdict stands and it is uncomfortable: the worst-priced path is the only exit. Two things follow honestly from it. The wrong figures standing through the deprecation window is a real cost, so this path pairs with fixing the death-domain input rather than leaving it broken while it ages out. And "the package pays the deprecation cycle anyway" is no longer an argument against the path — it is the fixed cost of every route except leaving the function in permanently. What Path B still buys over Path A is that gsm.safety stops carrying a second, drifting counting lane as permanent public API.

Reproduced below exactly as published on 17 August, unaltered. Its direction stands; the mechanism it names has expired. The note beneath it says how it lands now.

Recommendation — formed after all seven lenses reported

It goes. Pull the export before v1.1.0 publishes, and bring the census back in a later release as a properly lane-shaped, spec-checked function under a real requirement — keeping the per-visit coverage idea, the blinding stance, and the NA-discipline that the panel confirmed are worth keeping. The usage lens saved this function from being called dead code, but one in-house consumer that degrades gracefully is weak demand, and against it stand three independently verified ways the published numbers can be silently false — led by a wrong death count on the ecosystem's own data. A safety census whose death figure cannot be trusted is not API to freeze into a clinical package for the sake of shipping tonight instead of this week. Your merging-is-commitment principle argues for Path 2; the reason to make the exception is that this defect class — silently wrong clinical figures — is the one an honest label cannot cure, and you held the release precisely to keep this exit open.

How the recommendation lands now · 2026-08-18

Unchanged in direction, impossible in mechanism. "Pull the export before v1.1.0 publishes" cannot be done — v1.1.0 published sixteen minutes before those words were written — and its closing clause, that you held the release precisely to keep this exit open, is not what happened: you published it, deliberately, on your own principle.

Read as its argument rather than as its instruction, the recommendation still says the same thing: a census whose death figure cannot be trusted is not API to freeze into a clinical package on the strength of one gracefully-degrading in-house consumer. That argument survives every word of the correction and it points at Path B. What it can no longer claim is that the exit is cheap. That single claim is the whole of what expired, and it is why this is a correction rather than a rewrite.

THE QUESTIONS
C1 · answered 2026-08-18 — see the decisions section at the top

Does SafetyCensus() stay as public API of gsm.safety, or is it deprecated now and removed in the next version?

As published: "Does SafetyCensus() ship in gsm.safety v1.1.0 as public API, or come out before publication?" It shipped, on 17 August. The question keeps its number — D0021.1 — and moves to the choice that is actually open.

This is the whole decision. Everything above is its evidence, and the evidence is unchanged by the correction.

Recommendation: it comes out. A removal PR lands against main, you approve the refreshed release candidate, and v1.1.0 publishes without it. Unchanged in direction — it comes out — by the only route left: a deprecation notice in the next release saying plainly why, the demo consumer migrated off it, the export removed a version later, and the death-domain input fixed rather than left to age out.
C2 · answered 2026-08-18 — see the decisions section at the top

Is a requirement filed to bring the census back lane-shaped in a later release — keeping the per-visit coverage idea — or does the census stay demo-local?

The per-visit coverage table has no counterpart anywhere in the ecosystem and the panel judged it genuinely valuable; the blinded-denominators idea has your recorded design decision behind it. If C1 pulls the function deprecates the function out, this decides whether that value returns through the front door — a requirement, a design, a workflow-shaped implementation with a death-domain input — or stays a private script in the demo study. Unaffected by the correction: this question reads the same either way.

Recommendation: file the requirement. The function's ideas earned a return trip; its implementation did not earn a freeze.
WHAT UNBLOCKS

Answered 2026-08-18. C1 went the way listed above as “it stays”, so nothing happens to the release, the tag or the export. The branch it describes next is superseded in one respect: the panel's confirmed defects do not become a loose follow-up issue set on gsm.safety, they are carried by the rebuild he asked for in #274 — where the death count stops being logic inside the census and becomes a metric in the framework. C2 resolves into the same requirement.

Method and record: seven independent reviewers (six refute lenses, one steelman) plus seven independent fact-checkers, run as a 14-agent workflow under worker W0023 on 2026-08-17; 47 claims, 49 checked verdicts, none refuted. The full panel record — every claim, anchor and verdict — is committed beside this page as panel-record.json. Sources: gsm.safety origin/main at the v1.1.0 merge, the gsm.core and gsm.kri installed packages, the gsm.mapping specs, the demo study repo and its deployed site, and the gsm ecosystem conventions. Commissioned by requirement #229 / task #230.

Correction, 2026-08-18: the release this page was written to hold had already published sixteen minutes earlier, verified against the release object (created 05:40:40 UTC, published 05:42:53 UTC, not a draft), against NAMESPACE at the v1.1.0 tag and on main, and against the published release notes. Only the framing changed — the panel record, the verified defects and the recommendation are as published, with expired claims struck through in place rather than removed. Correction task #268, under requirement #266.

This page was drafted by 😺🤖 Claude Code (Claude Fable 5, worker W0023). Corrected by 👯🤖 Claude Code (Claude Opus 5, worker W0071) on 2026-08-18. @jwildfire answered it later the same day, against the recommendation; his words are at the top of the page, verbatim and complete, and the record was written by 👯🤖 Claude Code (Claude Opus 5, worker W0075) under task #276.