feat: Align open-monitor with @hasna/contracts - #7
Merged
andrei-hasna merged 4 commits intoJul 30, 2026
Conversation
Align open-monitor with @hasna/contracts Run the @hasna/contracts conformance check for this repo and fix every violation until it passes — ensure a valid hasna.contract.json, published_artifact_gate satisfied (artifact-scan wired into prepack), storage waiver where applicable, and all required metadata/scripts present. Keep changes minimal and conformant. Open a PR; do not merge. X-Factory-Run: run_d037ca5a5af7 X-Factory-Task: 45101057-07b2-4d9e-8961-e2c525d27b5a
The hasna.contract.json added in this branch matched no schema: every top-level key was invented, so `contracts repo-conformance .` failed at manifest_valid and the repo was no more contract-aligned than before. Rewrite it in the real hasna.service_contract.v1 shape — schema, name, class, contractVersion, kitVersion, bins, hosting, serviceSurfaces and storage. The `prepack` script also broke publishing. `contracts artifact-scan` requires a packed .tgz and the script passed none, so it exited 1 and took `npm pack`, `bun pm pack` and `npm publish` down with it — an already-published package became unreleasable. Pack the tarball inside the gate instead, with --ignore-scripts so prepack does not recurse, and declare the script through metadata.release.artifactScan. `contract-check` named `contracts check`, which the CLI does not expose; point it at `repo-conformance` and pin the kit version so the gate is reproducible. The pin now lives once, in kitVersion. Nothing exercised any of this, and the repo had no CI at all. Add src/contract.test.ts over the manifest shape, the bin allowlist and the gate wiring — including a pack that reproduces the prepack abort — plus a ci workflow running typecheck, tests, the contract gate and the pack. Six of the ten new tests fail against the previous commit. Two gaps stay open and are recorded in the manifest rather than hidden: the monitor-server and monitor-web bins fall outside the contract bin allowlist and cannot be renamed without a breaking change to a published package, and PostgresAdapter busy-waits with Bun.sleepSync so every query deadlocks, which is why postgres is waived rather than declared.
…check
Two reviewer findings on the first-ever CI workflow for this repo.
1. `bun test` was red on the GitHub runner, so the workflow turned main's
board red and the two gates it exists to add never ran.
Root cause is the pre-existing timeout probe in
src/collectors/command.test.ts, which ran `sleep 30 & wait`. `wait` with no
operands always exits 0, so whenever the backgrounded job dies early the
shell exits 0 immediately and the probe degenerates into "a shell ran" —
`result.ok` comes back true and the timeout is never exercised. That is
reproducible locally: with a PATH the login profile leaves without `sleep`,
the old command returns {code:0,signal:null,timedOut:false} in ~48ms,
matching the runner's 0.18ms failure exactly.
The probe now blocks in the foreground, using shell builtins only, so
neither `wait`'s exit status nor a login profile's PATH can hollow it out. A
background holder is still started, so a kill that reaches only the direct
child leaves a grandchild holding the stdio pipes and defers `close` — the
test still fails when `killChildTree` drops its negative-pid kill. A
negative control pins "exited 0 before the deadline" as distinct from
"killed on the deadline", which is the distinction the old assertion
collapsed, and the failing path now dumps the whole result instead of
reporting only "Expected: false, Received: true".
The gates also now run before the suite, so one red test cannot skip them
again, and src/contract.test.ts asserts that ordering.
2. `bun run contract-check` — the authority — still exits 1 on
bins_match_package, and nothing in the repo or CI ran it. The kit ignores
metadata.contractAlignment.pendingBinRenames, and contract-gate is
deliberately weaker at exactly that point, so a green board sat over a
non-conformant repo.
scripts/contract-baseline.ts runs the authority and holds it to an explicit
baseline declared in the manifest. It passes only when the failing set is
exactly {bins_match_package: monitor-server, monitor-web}: a new failure, a
drifted detail, or a baseline entry that stops failing all break the build,
as does output with no verdict lines, so the gate cannot pass vacuously if
the kit changes its report format. CI runs it as its own step.
The bin resolution itself is unchanged — dropping monitor-server and
monitor-web is a breaking change to a published package and remains an owner
decision, now recorded as an enforced deferral rather than an invisible one.
Verified: 299 pass / 0 fail / 725 expect across 27 files (bun test); lint,
contract-gate --online, contract-baseline, scan:artifact and npm pack --dry-run
all exit 0. Adding a third undeclared bin makes contract-baseline exit 1.
Corrects the root cause of the red CI in the previous commit, which named the
wrong one. The diagnostics added there paid for themselves immediately: the
runner reported
ok:true, exitCode:0, durationMs:1, timedOut:false,
stdout:"1 0 root Ss 0.1 1024 120 init /sbin/init\n100 1 root R 2.3 ..."
That stdout is `fakePsOutput` from src/collectors/local.test.ts. No shell ever
ran. local.test.ts calls `mock.module("./command.js", ...)`, which rewrites the
registry for the whole `bun test` process, and bun binds each file's imports as
it loads that file — so the stub reached every file loaded afterwards.
src/collectors/command.test.ts imports the same "./command.js", so whether its
timeout probe tested the real function or a canned stub came down to bun's
directory iteration order: this repo's suite was green on one machine and red on
the runner for that reason alone, and running the file on its own always passed,
which is why the flake looked environment-specific.
Restoring the module in `afterAll` does not work — measured: the afterAll runs,
and bindings already made are unaffected. So the stub moves to the class seam
instead. `collect()` reaches the shell only through the public
`LocalCollector.runCommand`, so a `StubShellLocalCollector` subclass overriding
that one method gives local.test.ts what it needs with no registry mutation and
no change to product code.
command.test.ts gains the tripwire that was missing: it now asserts the
command's own stdout comes back, which a canned stub cannot fake. Every
ok/exitCode/timedOut assertion in that file was satisfiable by the stub, which is
why the leak went unnoticed.
The previous commit's rewrite of the timeout probe stands on its own merits —
`X & wait` really does exit 0 whenever the backgrounded job dies early, since
`wait` with no operands always returns 0, reproduced locally at
{code:0,signal:null,timedOut:false} in ~48ms with a `sleep`-less login PATH — but
it was not what CI was failing on.
Verified: reproduced the CI failure locally by giving the probe file a name bun
loads after local.test.ts (2 fail, identical payload to the runner), then 0 fail
with this change. Full suite 299 pass / 0 fail / 726 expect across 27 files;
lint, contract-gate --online, contract-baseline, scan:artifact and
npm pack --dry-run all exit 0. Run 30515066588 already showed Contract gate,
Conformance baseline and Pack succeeding on the runner with Test the only
failure.
andrei-hasna
deleted the
factory/45101057-07b2-4d9e-8961-e2c525d2-d60aaca3
branch
July 30, 2026 05:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Align open-monitor with @hasna/contracts
Run the @hasna/contracts conformance check for this repo and fix every violation until it passes — ensure a valid hasna.contract.json, published_artifact_gate satisfied (artifact-scan wired into prepack), storage waiver where applicable, and all required metadata/scripts present. Keep changes minimal and conformant. Open a PR; do not merge.
Verification
Run
run_d037ca5a5af7· backendcodewith· task45101057-07b2-4d9e-8961-e2c525d27b5a🏭 Generated by @hasnaxyz/factory
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.