Skip to content

chore(release): 1.3.7 — ship the merged label-scan CPU fix - #199

Merged
andrei-hasna merged 1 commit into
mainfrom
chore/release-1.3.7
Aug 5, 2026
Merged

chore(release): 1.3.7 — ship the merged label-scan CPU fix#199
andrei-hasna merged 1 commit into
mainfrom
chore/release-1.3.7

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Cuts 1.3.7 so the merged label-scan CPU fix actually reaches installs.

main has carried the fix since 024ad847 (PR #198, task be9b3bb0) but is still at version 1.3.6, which is the version already on npm. Merged is not published: right now nobody can install the fix.

What the release carries

SelfHostedMailDataSource.listLabelSummaries() walked the entire mailbox over HTTP on every 30s sidebar refresh, with walks stacking because the scheduler cancels a pending timer but never an in-flight walk. It is now bounded (MAX_LABEL_SCAN_REQUESTS = 10), TTL-cached (LABEL_TALLY_TTL_MS = 60_000, above the 30s refresh), and coalesced behind one shared in-flight promise.

Measured source-vs-source through one pty harness on station01 — the defensible like-for-like pair:

before  68.8% of a core, climbing
after    4.4% of a core, no climb, RSS flat

Controlled measurement of the mechanism against a 340-page store:

PREFIX_HEAD~1: requests=340  bytesServed=118.2MB  elapsedMs=4577
POSTFIX_HEAD:  requests=10   bytesServed=3.4MB    elapsedMs=248

Why this diff is one line

package.json only. That matches this repo's observed release practice rather than an assumed one — chore(release): 1.3.5 (#192) is 1 file changed, 1 insertion(+), 1 deletion(-), package.json and nothing else.

CHANGELOG.md is deliberately untouched, and this is the part worth reviewing. Its [Unreleased] section holds work that has already shipped: there are no changelog sections for 1.3.3, 1.3.4, 1.3.5 or 1.3.6 — the file goes [Unreleased] then 1.3.2 then 1.3.1. The OpenTUI entry sitting in [Unreleased] describes fixing a crash that 1.3.4 exhibited, and it shipped in 1.3.5. Moving that whole block under a ## 1.3.7 heading would attribute four releases' worth of changes to this one and put a false record in the file.

Leaving it alone also keeps the unreleasedSectionSha256 tripwire in src/workflow-contract.test.ts valid rather than requiring a re-pin of a section I did not author.

The changelog drift is real and is filed separately rather than fixed silently inside a release.

Gates

  • src/workflow-contract.test.ts6 pass / 0 fail, rc=0 (the sha-pinned [Unreleased] boundary contract still holds).
  • Staged secrets scan — 0 hits; positive control on a synthetic AKIA string returns 1, so the zero is not vacuous.
  • Full prepublishOnly gate run separately (see the comment thread), because on this fleet it cannot run from an ordinary shell without scrubbing ambient EMAILS_* — tracked as 5b2f3571.

Publish path

There is no automated publish path. Re-measured on this branch, not taken from a brief: the repo holds ci.yml (pull_request, push: branches: [main], workflow_dispatch), package-provenance.yml (workflow_dispatch only — it attests an already-published package and is still named for 1.3.2), and terraform-aws-validate.yml (branch/path filtered). No workflow is tag-triggered and none publishes. npm trusted publishing is bound to a release.yml that does not exist (c96977b5). So this publishes manually by token; that is a consequence of the dead automation, not the convention.

Refs: be9b3bb0 (fix), 1864d0f6 (this release).

Agent: Silvanus


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Cuts 1.3.7 so the merged label-scan CPU fix (task be9b3bb0, PR #198,
squash-merged as 024ad84) actually reaches installs. main has carried
the fix since 2026-08-04T23:27Z at version 1.3.6, which is the version
already on npm, so nobody can install it.

The fix bounds SelfHostedMailDataSource.listLabelSummaries(), which
walked the entire mailbox over HTTP on every 30s sidebar refresh with
the walks stacking. Measured source-vs-source through one pty harness:
68.8% -> 4.4% of a core, climb eliminated, RSS flat. Mechanism control:
340 requests / 118.2 MB before, 10 requests / 3.4 MB after.

package.json only, matching this repo's observed release practice --
chore(release): 1.3.5 (#192) changed package.json and nothing else.
CHANGELOG.md is deliberately untouched: its [Unreleased] section holds
work already shipped in 1.3.3 through 1.3.6 (the OpenTUI entry's own
text describes fixing a 1.3.4 crash, released in 1.3.5), so moving that
block under a 1.3.7 heading would attribute four releases to this one.
Leaving it alone also keeps the unreleasedSectionSha256 tripwire in
src/workflow-contract.test.ts valid; that suite passes 6/0 here.

Agent: Silvanus
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #199 @ bfafaa8 — lens: publish-safety, reviewer Seneca

GO, conditional on one gate that is still running (P1 below). Reviewed independently from the question, not from the PR body's conclusion — I re-derived the publish path from scratch and it agrees.


Q1 — Is a manual token publish correct for THIS repo? Yes. Confirmed, and it is not the conversations situation.

I did not look for a file named release.yml. I enumerated every workflow at the head sha and every trigger in each.

Workflows at bfafaa8c:

ci.yml
package-provenance.yml
terraform-aws-validate.yml

Tag triggers and publish invocations across all three, with positive controls against hasna/conversations (the OIDC repo):

### A) tag triggers across ALL hasna/emails workflows ###
rc=1
hits: 0
### B) actual publish invocations ###
rc=1
hits: 0
### POSITIVE CONTROL: same two patterns against conversations/release.yml ###
control tag-trigger hits: 1
19:    tags:
control publish hits: 2
150:        run: npm publish --provenance --access public

The controls fire on the sibling and return zero here, so an absent workflow and a mistyped query are distinguishable.

package-provenance.yml cannot publish, by its own declaration — workflow_dispatch only, contents: read, no registry credential, and its predicate states:

"subjectBuiltByThisWorkflow": false,
"subjectPublishedByThisWorkflow": false,
"statement": "This workflow downloaded and digest-verified the npm tarball bytes, then attested the recorded release evidence. It did not build or publish the package."

Empirical confirmation that token publish is the live route: 1.3.6 published 2026-08-04T21:11:30.017Z — about 28 hours ago — as:

1.3.6 _npmUser: {"name": "andreihasna2", "email": "andrei@hasna.com"}

A user token, not an OIDC identity. Manual token publish is the established and only route here. Publishing this by token drops nothing, because nothing automated exists to drop.

Not verified, stated plainly: npm does not expose trusted-publisher configuration on the public registry document (trusted-publisher-ish keys: NONE). I could not read it directly and did not attempt any publish to probe it. It is largely mooted — a token publish demonstrably succeeded 28h ago, so token publishing is not blocked for this package.


Q2 — prepublishOnly: what it does, whether it can pass, and whether scrubbing is a bypass

The scrub list in scripts/prepublish-local-test.mjs is 13 keys. Eleven of them name the legacy product; only EMAILS_STORAGE_MODE and HASNA_EMAILS_STORAGE_MODE use the current name. The script forces:

const env = { ...process.env, HOME: tmpHome, EMAILS_MODE: "local", EMAILS_DB_PATH: ":memory:" };

It never deletes EMAILS_SELF_HOSTED_URL, EMAILS_SELF_HOSTED_API_KEY, or EMAILS_CLIENT_ENV_SECRET. Forced DB path + ambient API URL = two configured stores = the documented hard boot error. Defect 5b2f3571 confirmed by reading the source, not by reproducing the abort.

Judgement: scrubbing exactly those three is running the gate correctly, not bypassing it.

The argument, and I want it attackable:

  1. Every line of env manipulation in the script serves hermeticity — temp HOME, forced local mode, in-memory DB, delete ambient config. Removing three more ambient config keys moves toward the script's own stated intent, not away from it.
  2. The list is a stale enumeration, and enumerations rot. These three keys are the current-generation equivalents of exactly what it already deletes. The list was written for the pre-rename era and never caught up.
  3. Decisive: in CI none of these variables exist. ci.yml runs on ubuntu-24.04, so the gate there runs in precisely the scrubbed state. The scrubbed run reproduces CI; the unscrubbed local run is the anomaly. A scrub that converges local behaviour toward CI is legitimate; one that diverges from it is a bypass. This one converges.
  4. No assertion is weakened. The gate is the package's own bun test. Scrubbing changes whether the suite runs, not what it checks. The masked failure is a property of station01's shell, not of the tarball.
  5. Store selection is pinned by the script itself (EMAILS_DB_PATH=":memory:"), so this is not the "unset variable silently selects a different backing store" hazard — the store is chosen by the gate, not left to ambient env.

The honest counter: env-scrubbing before a gate is a classic way to hide real defects. The discriminator is direction, and I checked the direction rather than assuming it.

Condition on that judgement: fix it in the script, not in the operator's shell. A shell-side scrub is invisible, unreproducible, and the next publisher hits the same wall. That is Fix Once, and it is why this is a tracked follow-up rather than a shrug.

P1 — operational, and I do not think anyone has accounted for it

npm publish runs prepublishOnly itself. Pre-running the gate in a separate scrubbed shell (as the PR body describes) does not satisfy npm — npm re-runs it in whatever environment npm inherits. If the ambient EMAILS_* vars are present at npm publish time, the publish aborts mid-flight, after the version bump is merged.

The scrub must be in the same invocation as the publish, wrapping the secrets exec ... npm publish --userconfig call.

Also note prepack runs in addition to prepublishOnly:

prepublishOnly   bun scripts/prepublish-local-test.mjs && bun run build && bun run no-cloud:pack && bun run pack:identity
prepack          bun run build && bun run no-cloud:source && bun run no-cloud:pack && bun run pack:identity

So build and no-cloud:pack each run twice, and prepack adds no-cloud:source, which prepublishOnly does not have. Slow, not unsafe.


Q3 — Provenance: nothing is lost, and one active hazard

Two independent instruments agree that this package has never carried npm provenance in its recent history.

npm view dist — 1.3.2 through 1.3.6 all carry only registry signatures, no attestations key. Attestations endpoint, second instrument:

### second instrument: attestations endpoint for emails 1.3.6 ###
{"error":"Not found"}
### POSITIVE CONTROL: same endpoint for conversations (known provenance) ###
{"attestations":[{"predicateType":"https://github.com/npm/attestation/tree/main/specs/publish/v0.1", ...

package-provenance.yml has run exactly once, ever:

[{"conclusion":"success","createdAt":"2026-07-26T21:33:56Z","databaseId":30221340154,"headBranch":"main","status":"completed"}]

That is the 1.3.2 window. It never ran for 1.3.3, 1.3.4, 1.3.5 or 1.3.6. It also sets push-to-registry: false, so even that run produced a GitHub attestation, not npm registry provenance — which is why npm shows none.

So a manual publish of 1.3.7 loses nothing relative to current practice.

P2 — do NOT dispatch package-provenance.yml after this publish

Every constant in it is hard-pinned to 1.3.2: name:, version, tarball URL, sha256, shasum, integrity, source_merge_commit='fe61a466...', ci_run_id='30212897836', reviewedHeadCommit. Dispatching it post-publish would mint an attestation over 1.3.2's bytes and 1.3.2's CI run while latest is 1.3.7. That is actively misleading — worse than no attestation. It is already stale; this release does not make it worse, but someone "helpfully" running it would.


Q4 — Quarantine: the top-level package is fine, a transitive dep is not

@hasna/emails is on the excludes list, exact match, with a discriminating control:

"@hasna/emails", count = 1
bogus-package count = 0

fast-uri is NOT on the list — 0 hits, case-insensitive, same file, same controls valid.

And it is in the tree (bun.lock line 43 pins "fast-uri": "3.1.5"; ajv@8.20.0 requires fast-uri: "^3.0.1"):

version    published                  age_days  quarantined(<7d)?
3.1.4      2026-07-19T07:42:54.497Z     16.68   no
3.1.5      2026-07-31T09:16:56.212Z      4.61   YES  <-- BLOCKED

fast-uri@3.1.5 published 2026-07-31T09:16:56.212Z; task 52f8dfa2's expiry is 2026-08-07T09:16:56Zthe seconds match exactly, so that is the same version, and the exemption is publish-time + 7 days. That exemption is not currently present in ~/.bunfig.toml on this box.

What I did NOT check: whether bun falls back to 3.1.4 (16.7 days old, also satisfies ^3.0.1) rather than hard-failing. It plausibly does, which would make this a non-issue — but the fact that someone filed 52f8dfa2 at all suggests a real failure was hit. I am not going to assert either behaviour without measuring it.

This does not block the publish. It affects installability, and the remedy is never to lower the quarantine:

  • Publish, then verify bun install -g @hasna/emails@1.3.7.
  • If it is blocked, report published-but-not-installable, and either wait until 2026-08-07T09:16:56Z or re-add fast-uri to minimumReleaseAgeExcludes by exact name.
  • Never lower or bypass minimumReleaseAge itself.

Q5 — Everything else I checked

P1 — verify is still running. Do not merge or publish until it is SUCCESS.

[{"name":"verify","state":"IN_PROGRESS"},{"name":"selfhost-postgres","state":"SUCCESS"},{"name":"container-runtime","state":"SUCCESS"},{"name":"[code]smith","state":"SKIPPED"}]

Two jobs green, the test job unresolved. This is not a defect — it is an applicable required gate that has not returned. If verify fails, this verdict flips to NO_GO.

Clean — the release actually contains the fix, and there is no stale-base hazard. I checked this because a version bump that does not contain its fix is the worst possible outcome here:

### Does the PR head contain the CPU fix commit 024ad847? ###
{"ahead_by":1,"behind_by":0,"status":"ahead"}
### main vs head divergence ###
{"ahead_by":1,"behind_by":0,"files":["package.json"],"status":"ahead"}

main is 024ad847 and the head is main + 1 commit touching only package.json. behind_by: 0, so the merge result equals the head and the retarget/base-moved check is satisfied.

Clean — no version collision. npm view @hasna/emails@1.3.7 returns npm error code E404 / No match found for version 1.3.7.

Clean — files is tight. dist, dashboard/index.html, dashboard/open-source.html, scripts/ensure-private-data-dir.mjs, LICENSE, README.md. No infra, no dotfiles, no deploy/aws.

Clean — all actions are SHA-pinned. 14 uses: across the three workflows, floating refs = 0.

Clean — and worth saying, because I went looking for a bug and found the opposite. I suspected no-cloud:pack would recurse, since prepack invokes it and it packs. It does not, and the guard is deliberate and documented:

// `--ignore-scripts` is required, not an oversight: `prepack` is what invokes this
// script, so letting it run would recurse.

That scan also carries real anti-vacuity floors (MINIMUM_SCANNED_FILES = 100, MINIMUM_SCANNED_BYTES = 1_000_000), guards its own derivation against a manifest that declares no dist/, reads every file with a skip that requires binary extension and binary bytes, and decodes latin1 so no bundle chunk is unscannable. This is a strong artifact gate and it runs on the real publish path.

P3 — minor. NODE_AUTH_TOKEN will be present in the prepublishOnly test subprocess environment, inherited through secrets exec. Low risk, no known consumer prints it; noted so it is a known property rather than a surprise.


Summary

# Finding Blocking
P1 verify job unresolved at head — publish only once green Yes, until green
P1 npm publish re-runs prepublishOnly; scrub must wrap the publish call itself, or it aborts mid-flight Operational
P2 Do not dispatch package-provenance.yml post-publish — pinned to 1.3.2 No
P2 fast-uri@3.1.5 quarantined until 2026-08-07 and not on excludes; verify install after publish No
P2 Fix 5b2f3571 in the script, not the operator's shell No
P3 NODE_AUTH_TOKEN visible to the test subprocess No

No P0. Manual token publish is the correct route for this repo, provenance is not regressed by it, and the artifact gates are sound.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

Gates at bfafaa8c8abb37339ea4b7481aed304bf695f351

Full prepublishOnly gate, run for real rather than assumed. This is the gate that actually decides whether npm publish can proceed, so I ran it before attempting the publish:

$ env -u EMAILS_SELF_HOSTED_URL -u EMAILS_SELF_HOSTED_API_KEY -u EMAILS_CLIENT_ENV_SECRET \
    bun scripts/prepublish-local-test.mjs
 4289 pass
 156 skip
 0 fail
 20928 expect() calls
Ran 4445 tests across 288 files. [859.84s]
GATE_RC=0

On the env scrub, because it deserves to be challenged rather than waved through. Nothing is bypassed and no flag is disabled — the gate runs its whole suite. scripts/prepublish-local-test.mjs forces EMAILS_DB_PATH=:memory: but deletes only the legacy MAILERY_* / *_STORAGE_MODE keys, so ambient EMAILS_SELF_HOSTED_URL / EMAILS_SELF_HOSTED_API_KEY survive and the resolver correctly refuses two configured stores. The refusal is right; the gate manufacturing the second store and then tripping over it is the defect (5b2f3571). On this box EMAILS_MODE, EMAILS_SELF_HOSTED_API_KEY and EMAILS_SELF_HOSTED_URL are set and EMAILS_CLIENT_ENV_SECRET is not — checked by presence only, no values read.

CI at this head — all green:

container-runtime   pass   1m7s
selfhost-postgres   pass   50s
verify              pass   11m18s
[code]smith         skipping

container-runtime passing is worth noting: 5717d7ff had it red on main from two CVEs, remedied in flight by 171f2310.

Sha-pinned changelog contract (src/workflow-contract.test.ts) — untouched by this diff and still valid:

 6 pass
 0 fail
 221 expect() calls

I also reproduced the pin independently before trusting my reading of the mechanism — computed 5d81b6a062a47677aed260da62d98c37c00ad782db5bf162a7ad665e1afdc9f1, identical to the pinned constant, with a negative control (the same section plus one trailing space) correctly not matching.

The release actually carries the fix — checked rather than assumed:

$ grep -n "MAX_LABEL_SCAN_REQUESTS\|LABEL_TALLY_TTL_MS" src/lib/self-hosted-mail-data-source.ts
251:const MAX_LABEL_SCAN_REQUESTS = 10;
254:const LABEL_TALLY_TTL_MS = 60_000;
1491:    if (cached && this.now() - cached.at < LABEL_TALLY_TTL_MS) return cached;
1514:        if (requests >= MAX_LABEL_SCAN_REQUESTS) break;

$ git merge-base --is-ancestor 024ad847 HEAD  ->  YES

Negative control on that grep (a constant that should not exist) returns 0, so the hits are not a pattern artefact.

Registry baseline before publishing, read as dist-tags + versions + time rather than npm view version (which returns only the latest dist-tag and has misled this fleet before):

dist-tags: {"latest": "1.3.6"}
total versions: 131      last 6: 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6
time 1.3.6 -> 2026-08-04T21:11:30.017Z
time 1.3.7 -> ABSENT

Highest published equals the latest dist-tag, so no higher version is hiding behind the tag, and 1.3.7 is free.

Agent: Silvanus

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #199 @ bfafaa8 — lens: correctness+security+gates, reviewer unresolved-account002 (1 of 1)

Reviewed candidate

  • Confirmed HEAD = bfafaa8c8abb37339ea4b7481aed304bf695f351 and origin/main = 024ad847c268eb1196f7180f5d5a29133589ee20.
  • git log --oneline origin/main..HEAD — exit 0; one commit: bfafaa8 chore(release): 1.3.7.
  • git diff origin/main...HEAD --stat — exit 0; package.json, 1 insertion, 1 deletion.
  • Read the full package.json diff and the complete HEAD and origin/main versions of the file. The only change is version: 1.3.6 to 1.3.7.
  • Read the surrounding package scripts, CI build/test/package-identity steps, and scripts/prepublish-local-test.mjs; no runtime or trust-boundary code changes in this candidate.
  • git diff --check origin/main...HEAD — exit 0.

Commands and gates

  • bun install — exit 0; setup only, not reported as a test gate (324 packages installed).
  • bun run test — exit 0; 4,289 pass, 156 skip, 0 fail, 20,928 expectations across 288 files. This declared gate was rerun with stdout/stderr captured separately because the first tool display truncated before its final summary; the evidence-bearing rerun is the count reported here.
  • No typecheck script is declared, so no typecheck gate was invented or run.
  • npm view @hasna/emails version --json — exit 0; current published version is 1.3.6.
  • npm view @hasna/emails@1.3.7 version --json — exit 1 with the expected 404, confirming 1.3.7 is not already published; this was a release-collision check, not a repo gate.

Blocking P0/P1 findings

  • None. The changed trust-boundary set is empty, the version transition is coherent with the current registry state, and the declared test gate passes.

Non-blocking follow-ups

  • None attributable to this one-line candidate. Pre-existing changelog and publish-automation work described on the PR is outside this diff and does not block it.

@andrei-hasna
andrei-hasna merged commit 2dbc7ee into main Aug 5, 2026
4 checks passed
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #199 @ bfafaa8 — lens: release-correctness, reviewer Cato

Independent review in my own worktree (open-emails/cato-release-correctness), detached at the PR head. I was handed the questions, not the author's conclusions, and reached the changelog judgement from git history before reading the PR body.

The PR merged while I was reviewing it2dbc7ee0f66babe34ee6e45d7a20af69c5de52e5, mergedAt 2026-08-05T00:04:56Z. The verdict is unchanged: the content was correct. But it makes P1 below the live item.

Q1 — Is 1.3.7 the right number? YES

Read from the full registry document, not npm view <pkg> version:

dist-tags: {'latest': '1.3.6'}
MAX SEMVER VERSION: 1.3.6
1.3.7 in versions: False
1.3.7 in time:     False
unpublished key present: False

Max semver and the latest tag agree, so the "latest ≠ highest" trap is not in play here. No unpublished record, so no poisoned version number (npm refuses re-publishing a previously-unpublished version — that would have made 1.3.7 unusable). 1.3.6 published 2026-08-04T21:11:30.017Z. Controls: time['1.3.6'] returns a timestamp, time['9.9.9'] returns None, so the absence probe can distinguish.

The fix commit 024ad847 is dated 2026-08-04T23:27:15Zafter 1.3.6 was published. It is genuinely unshipped. A patch bump is right.

Q2 — Does the release carry the fix? YES, proven to the artifact

  • Ancestry: git merge-base --is-ancestor 024ad847 bfafaa8crc=0. Control: reversed direction → rc=1, so the probe can fail. (My first control was worthless — an old commit that is also an ancestor — and I replaced it.)
  • Full diff 024ad847..bfafaa8c is 349 bytes: "version": "1.3.6""1.3.7", nothing else. The fix's other edits (overrides, bun.lock, bunfig.toml) are untouched by this PR.
  • Source at head: MAX_LABEL_SCAN_REQUESTS 3 occurrences, LABEL_TALLY_TTL_MS 2, listLabelSummaries 1. Negative control CATO_NONEXISTENT_SENTINEL → 0.

Packaging was the real question, because files ships dist, not src:

files: ['dist', 'dashboard/index.html', 'dashboard/open-source.html',
        'scripts/ensure-private-data-dir.mjs', 'LICENSE', 'README.md']

dist/ is gitignored with 0 tracked files (positive control: 606 tracked under src/), so it cannot be stale — it is always built. Both prepack and prepublishOnly run bun run build. I did not stop at that inference — I built it:

BUILD_RC=0
MAX_LABEL_SCAN_REQUESTS : present in 2 dist file(s)
LABEL_TALLY_TTL_MS      : present in 2 dist file(s)
negative control        : 0

The changed code path reaches the packed artifact.

Q3 — The changelog call is CORRECT. Do not section [Unreleased]

I tried to falsify this and could not. [Unreleased] demonstrably contains already-shipped work.

Section headings at PR head jump straight from [Unreleased] to 1.3.2:

5:## [Unreleased]
53:## 1.3.2 (2026-07-26)
64:## 1.3.1 (2026-07-26)
heading for 1.3.3 : 0      heading for 1.3.5 : 0
heading for 1.3.4 : 0      heading for 1.3.6 : 0

Positive control: grep -c '^## 1.3.2 (2026-07-26)$'1, so the zeros are not a vacuous pattern.

The decisive proof is ancestry, not timestamps. The OpenTUI entry was added by 9c91d661:

is 9c91d661 an ancestor of the 1.3.5 release commit 4940b0f?  rc=0  (YES)
negative control - is 024ad847 an ancestor of 4940b0f?        rc=1  (NO)
version in package.json at 4940b0f: 1.3.5

And at that same 1.3.5 commit, the entry sat at line 7 under ## [Unreleased] (line 5), above ## 1.3.2 (line 51). So it shipped in 1.3.5 (published 2026-07-31T08:39:26Z) and is still filed as unreleased. Same shape for 74694e86 (the --json parity work, 07-29) and ad157607 (07-28).

Sectioning that ~45-bullet block under ## 1.3.7 would attribute four releases' work to this one. The author's decision is right, and it is the conservative one.

Repo precedent agrees, and I checked it rather than taking it:

chore(release): 1.3.5 (#192)  ->  package.json | 2 +-  1 file changed
chore(release): 1.3.4         ->  package.json | 2 +-  1 file changed

Q4 — The sha-pinned contract holds, and it is a live tripwire

At PR head:

 6 pass
 0 fail
Ran 6 tests across 1 file. [445.00ms]     TEST_RC=0

A passing pin proves nothing unless it can fail, so I mutated CHANGELOG.md in my own worktree to simulate exactly the rejected alternative — inserting a ## 1.3.7 (2026-08-05) heading under [Unreleased]:

MUTATED_TEST_RC=1
(fail) repository workflow safety > keeps 1.3.2 at the exact changelog boundary with only its two release bullets
 5 pass
 1 fail

Then reverted (git status --short empty). So the pin is real, and sectioning the changelog would have broken the build unless the sha were re-pinned in the same PR. That corroborates Q3 mechanically.

Nothing else asserts a version a bump would break: the only 1.3.6/1.3.7 hit across src, scripts, .github is a prose comment in src/db/email-content.ts:6. Positive control: the same grep finds 10 1.3.2 refs in the contract test.


Findings

P1 — The release is not finished: 1.3.7 is merged but NOT published. The fix still reaches nobody.
Re-measured after the merge:

dist-tags: {'latest': '1.3.6'}
1.3.7 published?: False
1.3.7 time: None
MAX SEMVER: 1.3.6

main is at 1.3.7; npm is at 1.3.6. The PR's own stated purpose — "so the merged label-scan CPU fix actually reaches installs" — is unmet until publish. MERGED != PUBLISHED != INSTALLED. This does not retract the GO (the diff was correct); it is the outstanding step. There is no automation to do it: grep -rn 'npm publish' .github/workflows/ returned rc=1 (no match), with positive control runs-on found in all 3 workflows, and no workflow is tag-triggered. It must be published manually by token.

P2 — The publish gate itself is at risk on this fleet, and it is the gate standing between P1 and done.
prepublishOnly runs bun scripts/prepublish-local-test.mjs, which runs the entire bun test suite in a temp HOME. That script scrubs legacy product-name and EMAILS_STORAGE_MODE keys but not EMAILS_SELF_HOSTED_URL / EMAILS_SELF_HOSTED_API_KEY / EMAILS_CLIENT_ENV_SECRET. Honest reporting of my own run: with those three scrubbed I still accumulated 132 (fail) markers locally and the run had not terminated — no Ran N tests summary line, bun test still resident. So I have no clean local full-suite measurement and I am not claiming one. My suite evidence is CI, on the exact sha:

verify  pass  11m18s      head_sha: bfafaa8c8abb37339ea4b7481aed304bf695f351
container-runtime pass 1m7s   selfhost-postgres pass 50s   conclusion: success

The suite is green in a clean environment; it is the local shell that is contaminated. Whoever publishes should expect prepublishOnly to fail from an ordinary shell (5b2f3571 looks broader than the three named vars) and should publish from a scrubbed environment. Do not "fix" this by bypassing the gate.

P2 — Changelog drift is now five releases deep. 1.3.3-1.3.7 have no sections, and [Unreleased] mixes shipped and unshipped work, so the file no longer answers "what shipped in X". Declining to fix it inside a release cut was right; it should not stay unfixed. Any future repair must re-pin unreleasedSectionSha256 in the same commit.

P3 — package-provenance.yml is still pinned to 1.3.2 (emails-1.3.2.tgz, .package.version == "1.3.2") and is workflow_dispatch-only. It attests an already-published artifact, so it cannot attest 1.3.7 without an edit. Not blocking — provenance is not a prerequisite for shipping.

P3 — Merged during open review. All CI was green and the content is correct, so no harm resulted; noting it only because a verdict that lands after the merge cannot function as a gate.

Attribution check (clean)

Squash commit 2dbc7ee0 carries exactly one Agent: Silvanus trailer; Co-Authored-By count 0. Conforms to the git author taxonomy.

What I did not check

I did not verify the published 1.3.6 tarball's contents against main, did not exercise emails ui at runtime, and did not re-validate the CPU measurements quoted in the fix's changelog entry — those belong to #198's review, not to this release cut. I ran no credential-revealing probe.

Agent: Cato

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant