Skip to content

chore(release): bump @hasna/loops to 0.4.38 - #187

Merged
andrei-hasna merged 1 commit into
mainfrom
release/0.4.38
Aug 2, 2026
Merged

chore(release): bump @hasna/loops to 0.4.38#187
andrei-hasna merged 1 commit into
mainfrom
release/0.4.38

Conversation

@andrei-hasna

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

Copy link
Copy Markdown
Contributor

What

Bumps @hasna/loops 0.4.37 -> 0.4.38. Version bump only; one line in package.json.

Why

main carries three merged, unreleased fixes at an already-published version number.
npm view @hasna/loops version reads 0.4.37 (published 2026-08-02T00:14:46.891Z) and
origin/main's package.json read 0.4.37 too, so there was no version under which these
could ship.

commit change
6ccb634 fix(scheduler): reap a runner's own expired lease once its slot has moved past (#184)
7c37557 fix(hygiene): restore grace-ceiling reclaim disabled by #182 (P1) (#183)
075b557 fix(hygiene): reclaim loop runs stuck running with no live process (#182)

All three confirmed ancestors of this branch's base 6ccb634. The ancestry probe was
controlled in both directions — a true ancestor returns rc=0, and the reversed query
(HEAD as ancestor of an old commit) returns rc=1 — so it can both pass and fail.

Evidence the fix is genuinely absent from what the fleet runs

Measured against the installed 0.4.37 bundle at
/home/hasna/.bun/install/global/node_modules/@hasna/loops:

grep -ro "reclaim"    dist/  ->  1   (only dist/lib/storage/postgres-loop-storage.d.ts, unrelated)
grep -ro "hygiene"    dist/  ->  48  (control, fires)
grep -ro "duplicates" dist/  ->  26  (control, fires)
src/lib/hygiene.ts           ->  24  occurrences of "reclaim"

loops hygiene --help on installed 0.4.37 lists names, duplicates, scripts,
route-tasksno reclaim verb.

The controls validate the INSTRUMENT (grep -ro over the whole dist/ tree) and the
POPULATION (recursive, not a bounded page). The zero is therefore an observation, not a
broken probe.

Gates at this tree

Measured unpiped, output redirected to a file, exit code read directly from the command:

gate result
bun run build rc=0
bun run typecheck rc=0
bun test --timeout 120000 rc=01127 pass / 56 skip / 0 fail, 11 snapshots, 8921 expect() calls, 1183 tests across 74 files, 531.55s

This matches the figure both independent reviewers measured at head c659c2e on #184.

Test isolation was verified as an outcome, not as a precaution: LOOPS_DATA_DIR was
pinned to a temp dir, that temp dir gained loops.db (proving the variable was honoured and
reached the store), and the newest mtimes under the live ~/.hasna/loops are byte-identical
before and after the run.

Scope — read this before inferring what the release achieves

This bump ships the CLI package on npm. claimRuns() — the function that decides which
loops get claimed — lives in src/api/index.ts, the API server, and station01's
loops mode reports truth=self_hosted_control_plane, control_plane=true,
source=HASNA_LOOPS_API_URL. The fleet's claiming is therefore performed by the hosted
control plane, which runs a server image (see .github/workflows/ecr-candidate.yml), not
this npm package.

So the ladder here has an extra rung and it is the load-bearing one:

MERGED != PUBLISHED != INSTALLED != DEPLOYED (server image) != RUNNING

Publishing and installing 0.4.38 updates the CLI. It does not change one line of what the
control plane executes, and this PR must not be read as fixing the currently wedged loops.

A server deploy is a separate operation with its own authority and rollback and is explicitly
not in scope here.

Risk

Lowest-risk change shape available: a single version string, on content that was already
reviewed and merged. Rollback is npm deprecate plus a revert of this one line.

Task: 438c6dde


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

main has carried three merged, unreleased fixes at the already-published
0.4.37 version number. This bump cuts a releasable version for them.

  6ccb634  fix(scheduler): reap a runner's own expired lease once its slot
           has moved past (#184)
  7c37557  fix(hygiene): restore grace-ceiling reclaim disabled by #182 (P1) (#183)
  075b557  fix(hygiene): reclaim loop runs stuck running with no live process (#182)

Verified absent from the installed 0.4.37 bundle with controls: `reclaim`
occurs once in dist/ and only in an unrelated postgres .d.ts, while
src/lib/hygiene.ts has 24 occurrences; controls `hygiene` (48) and
`duplicates` (26) fire on the same dist.

Gates at this tree, measured unpiped with the exit code read directly:
build rc=0, typecheck rc=0, bun test rc=0 -- 1127 pass / 56 skip / 0 fail
across 74 files.

Task: 438c6dde

Agent: agent-chief-planning
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO-GO — #187 @ 6c5c281 — lens: release-bump, reviewer reviewer-release-bump-019fc487 (1 of 1)

Independent adversarial reviewer, dispatched synchronously as a headless codewith worker
(gpt-5.5, reasoning effort high, auth-profile account014, session 019fc487-1571-75f0-b62d-042fc1ff31c5).
It was handed the QUESTION ("should #187 be merged, or not?") and deliberately not the
dispatcher's conclusion, so that its instrument pointed at the subject rather than at the
framing it was given.

What it confirmed independently

  • Diff against the correct base is the version bump alone: package.json | 2 +-,
    -"version": "0.4.37" / +"version": "0.4.38". Its diff probe was controlled in both
    directions — git diff --quiet origin/main...HEAD -- package.json gave rc=1 (changed)
    while the same probe on CHANGELOG.md gave rc=0 (unchanged).
  • All three named fixes are ancestors of HEAD: 075b557, 7c37557, 6ccb634 each rc=0.
  • The installed-bundle claim holds, with its own controls: installed dist reclaim search
    returned only
    /home/hasna/.bun/install/global/node_modules/@hasna/loops/dist/lib/storage/postgres-loop-storage.d.ts:1,
    while its positive controls returned multiple matching lines including
    /home/hasna/.bun/install/global/node_modules/@hasna/loops/dist/cli/index.js:42.
  • Gates it ran clean: typecheck rc=0, test:boundary rc=0
    (Loops private cloud boundary scan passed), check:contracts rc=0,
    check:branding 4 pass / 0 fail rc=0, check-packed-boundary rc=0.
  • Worktree stayed clean throughout its review.

Its three blocking P1s

  1. Test gate failing at the PR tree — MCP timeout failures, e.g.
    (fail) Loops MCP server > rejects invalid tool input through MCP schema validation [60009.06ms]
  2. Registry check incomplete —
    "summary": "request to https://registry.npmjs.org/@hasna%2floops failed, reason: getaddrinfo EAI_AGAIN registry.npmjs.org", rc=1
  3. Supply-chain audit incomplete — ConnectionRefused: audit request failed, rc=1

It was right to block on these rather than wave them through: from inside its sandbox it
genuinely could not tell a broken subject from a broken environment, and it explicitly
declined to trust the absent registry result rather than reading the failure as "not
published". That is the correct instinct.

Remediation evidence follows in the next comment.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

Remediation cycle 1 — all three P1s re-measured OUTSIDE the reviewer's sandbox

Same tree, same sha 6c5c28142d0978d59ee47eb3d22db64e02bfab48, git status --short empty.
The only variable changed is the execution environment.

The reviewer ran under codewith --sandbox workspace-write, which grants write access to
[workdir, /tmp, $TMPDIR] only and provides no outbound network. Its own transcript shows
the filesystem half of that directly:

/home/hasna/.bun/bin/bun: line 63: /home/hasna/.hasna/test-guard/guard.log: Read-only file system

Two runs of the same command disagreeing is a reason to compare environments before
concluding the subject is broken. Here is each finding re-run unsandboxed.

P1 (1) — MCP tests: NOT REPRODUCIBLE, sandbox artefact

$ bun test --timeout 120000 src/mcp/index.test.ts
 11 pass
 0 fail
 132 expect() calls
Ran 11 tests across 1 file. [17.00s]
MCP_TEST_RC=0

17.00s and zero failures, against 60-second timeouts in the sandbox. The mechanism is visible
in the reviewer's own error payload:

McpError: MCP error -32001: Request timed out
 data: { timeout: 60000 }

Those tests spawn an MCP server subprocess and speak to it; the sandbox prevented that, so the
client hit its protocol deadline. Worth stating precisely: 60453.55ms in that output is the
timeout firing, i.e. the budget
— it is not a measured duration, and no overshoot can be
computed from it.

The full suite at this tree, measured unpiped with the exit code read directly from the
command, was already rc=01127 pass / 56 skip / 0 fail, 1183 tests across 74 files,
531.55s.

P1 (2) — registry: 0.4.38 is genuinely unpublished, with a positive control

$ npm view @hasna/loops versions --json     VIEW_RC=0
latest_in_list=0.4.37
has_0.4.38=false
has_0.4.37=true  <- POSITIVE CONTROL, must be true

The control is the load-bearing half: has_0.4.37=true proves the probe can return true for
a version that is on the registry, so has_0.4.38=false is an observation about 0.4.38 and
not a broken query returning a confident zero. Control validated INSTRUMENT, SUBJECT and KEY;
POPULATION is the full versions array, not a bounded page.

P1 (3) — supply-chain audit: passes

$ bun run check:supply-chain:audit          AUDIT_RC=0
$ bun audit && bun audit --production
No vulnerabilities found
No vulnerabilities found

And the complete gate, which is what prepublishOnly actually runs:

$ bun run check:supply-chain                SUPPLY_CHAIN_RC=0
Loops private cloud boundary scan passed
Loops packed artifact boundary and loops-api export smoke passed
hasna-loops-0.4.38.tgz
Total files: 132
Unpacked size: 6.42MB

The pack dry-run naming hasna-loops-0.4.38.tgz is independent confirmation the bump reached
the packed artefact, not just the source file.

Summary

finding status cause
MCP test failures not reproducible sandbox blocked the MCP subprocess; 60000ms budget, not a duration
registry unverifiable resolved, controlled sandbox had no DNS; 0.4.38 confirmed absent, 0.4.37 present
audit unverifiable resolved sandbox had no network; rc=0, no vulnerabilities

None of the three was a defect in the change. All three were the reviewer correctly refusing to
trust a result its environment could not produce.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #187 @ 6c5c281 — lens: correctness+security+gates, reviewer unresolved-account003 (1 of 1)

What I read

  • Confirmed the live PR base is main at 6ccb63427bfd7151e8bd58b183c0ea8d9971fcd5 and the head is the named 6c5c28142d0978d59ee47eb3d22db64e02bfab48.
  • git log --oneline origin/main..HEAD (exit 0): one commit, the 0.4.38 release bump.
  • git diff origin/main...HEAD --stat (exit 0): one changed file, one insertion and one deletion.
  • Read the complete diff and full surrounding package.json, plus the changelog, recent package-version history, and the three merged fixes the release commit names. The candidate changes only @hasna/loops from 0.4.37 to 0.4.38; it changes no runtime code or trust boundary.
  • npm view @hasna/loops version (exit 0) returned 0.4.37, so 0.4.38 is not a registry version collision.
  • git diff --check origin/main...HEAD (exit 0).

Commands and gates

  • bun install — exit 0; 152 packages installed. Setup only, not reported as the repository test result. Its prepare hook also completed the build successfully.
  • bun run typecheck — exit 0; tsc --noEmit produced 0 diagnostics (test pass/fail counts not applicable).
  • bun run test — exit 0; 1127 pass, 56 skip, 0 fail, 11 snapshots and 8921 expect calls across 74 files.

Blocking P0/P1 findings

  • None. No changed source, authorization, credential, persistence, mutation, migration, rollback, or session-integrity path is reachable from this version-only diff, and the applicable gates are green.

Non-blocking follow-ups

  • CHANGELOG.md currently has no 0.4.38 section for the three fixes named by the release commit. This is release-documentation follow-up only and is not material to the current correctness, security, or declared-gate acceptance criteria.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[MERGE BLOCKED] gh pr merge 187 --repo hasna/loops --squash --delete-branch exited 1.

drain-merge-guard found the earlier attributed [REVIEW] NO-GO from reviewer-release-bump-019fc487 still live at current head 6c5c28142d0978d59ee47eb3d22db64e02bfab48. The guard states that the later GO at the same SHA does not clear it.

I did not bypass the guard, push a no-op change, or use another merge path. The earlier comment's three failures were already re-measured outside its sandbox as environment artifacts, so there is no candidate-code remedy to commit. I have asked the named reviewer to post an explicit withdrawal only if they accept that remediation; merge must remain open until then or until the head changes for a real fix and is reviewed again.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #187 @ 6c5c281 — lens: release-bump, reviewer reviewer-release-bump-019fc487 (1 of 1)

Remediation cycle 1, re-review scoped to the three prior blockers only — not a fresh
whole-system review, and the previously-clean measurements (diff shape, ancestry, installed
bundle controls, typecheck, boundary, contracts, branding, packed-boundary) were not reopened.

The reviewer was granted network access and write access to /home/hasna/.hasna for this
cycle, specifically so it could verify the counter-evidence itself rather than accept the
dispatcher's word. Every resolution below is its own measurement.

All three resolved

MCP test blocker — resolved.

11 pass
0 fail
Ran 11 tests across 1 file. [17.69s]
rc=0

Its own note on why this is a valid resolution rather than a disappearing symptom: the earlier
failure output proved the runner can emit (fail) lines for this file, so the instrument is
capable of reporting failure — the rerun validates SUBJECT and POPULATION under the corrected
environment.

npm registry blocker — resolved, with the control in the right direction.

positive control:  "0.4.37"                                   rc=0
target:            "No match found for version 0.4.38"        rc=1
                   npm error 404 No match found for version 0.4.38

Validates INSTRUMENT, SUBJECT, KEY and the exact-version POPULATION. 0.4.38 is genuinely
unpublished; the zero is an observation, not a broken query.

Supply-chain blocker — resolved.

audit:              No vulnerabilities found / No vulnerabilities found     rc=0
full supply-chain:  Loops packed artifact boundary and loops-api export smoke passed   rc=0

No regressions from the reruns

## release/0.4.38...origin/release/0.4.38
rc=0

git diff --stat produced no output at rc=0, the dry-run pack left no .tgz in the repo
root, and no Bun process remained.

VERDICT: GO — no blocking findings.

Bounded review terminates here: one reviewer, one remediation cycle, verdict GO at the exact
head sha. The head sha has not moved since the first review and the base has not been
retargeted, so the reviewed content is the content that will land.

@andrei-hasna
andrei-hasna merged commit d4628e6 into main Aug 2, 2026
5 checks passed
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