v1.15.0
·
5 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
Added
- Scheduled tasks, declared in
module.json. A module listsschedule[]
entries — aname, a cron expression or alias such as@hourlyinat, and
EITHER ajobor acommand, with optionalqueue,timezoneand
withoutOverlapping.CompileScheduleManifestStageparses every expression
at boot, so an impossible schedule ("0 25 * * *") fails the boot naming the
field instead of silently never running; naming both a job and a command, or
neither, fails it too. One crontab line drives the application:
hkm cli schedule:runevery minute dispatches whatever is due — ajobis
pushed onto the queue, so it gets the worker's retries and timeout, and a
commandruns through the CLI pipeline exactly as if typed.schedule:list
shows what is declared.withoutOverlappingtakes aCachePortlock keyed by
task name, which is what makes the scheduler safe on several servers at once;
with no cache bound, a guarded task is skipped and logged rather than run
unguarded. MetricsPortandTracerPort, and anObservabilityStageon every
request. The kernel already propagated a correlation id; it had no seam for
the aggregate numbers or for where a request's time went. Both ports are
StatsD/OpenTelemetry-shaped and dependency-free. The stage sits inside
CorrelationIdStageand outside security and routing, so a denied request and
an unmatched one (labelledunmatched) are still measured, and it labels by
route TEMPLATE, never by path, so a metric cannot mint one series per id. With
neither port bound, shared no-op implementations cost onehrtime()pair per
request. An adapter must not throw.DriverAware— an optional interface aDatabasePortimplements to name
the SQL dialect behind it. Checked withinstanceof, likeRequestAware, so
the many in-memory test fakes of the port need no change; a consumer falls
back to configuration when the bound port does not implement it.hkm ppkg— the native package manager. The Composer-compatible package
manager frommodules/hkm-ppkg, now a submodule, runs under the launcher:
install,update,require,remove,autoload,audit,outdatedand
the rest, writing the samecomposer.lockandvendor/Composer does.
Composer PLUGINS are not run;hkm ppkg compatnames each one a project uses
and what it would have done.hkm ppkg test-env <dir>builds a plugin's test
vendor/from the kernel's own and the checkouts already on disk.- A feature test bench.
tests/Featureboots a real kernel and pushes real
Requests through the realHttpPipeline— manifests, matcher, dependency
graph, scoped containers and stages all real, only the database, cache and
queue faked — and runs as its ownFeaturesuite. It exists for the class of
defect a unit test cannot see: a controller signature a stage no longer
matches, or a route that resolves to nothing.
Changed
- The route compiler is split into
Kernel\Boot\Routing.
CompileRouteManifestStagewas 1,200 lines doing six jobs and could only be
tested by compiling a whole manifest. The jobs now live inRouteNormalizer,
DomainComposer,GroupExpander,RouteCompilerandRoutePolicy, each
tested on its own, and the stage keeps only their order. The public shape of
route-manifest.phpis unchanged; each dynamic route inroute-index.phpnow
also carries its route key, which is what lets a metric be labelled by
template. An index compiled by an older kernel still serves. - A fetched plugin is verified without Composer when that is possible.
hkm plugins installused to runcomposer installinside every plugin to
reach phpunit — a dependency resolution across a few dozenvcsrepositories,
throttled by GitHub's anonymous rate limit. It now first builds the test
environment from the kernel's ownvendor/plus the sibling checkouts on
disk, and falls back to Composer whenever that cannot be trusted: no phpunit,
or a test dependency with no checkout.HKM_PLUGIN_TESTS_COMPOSER=1forces the
Composer route. RichGrapharticles takeinLanguage— the language of the CONTENT,
which can differ from the page's — and an article with noauthorNamenames
the publishing Organization as its author, as Google recommends for content
that is contributed rather than bylined.
Fixed
- Error responses had no correlation id.
ErrorStagewrapped
CorrelationIdStage, so the request it caught an exception on was the one from
before the id was attached: every error envelope'srequestIdwas empty,
every notifier received an empty correlation id, and error responses carried
noX-Correlation-IDheader — the id was on every response except the ones
anyone would need it for. The two stages now run the other way round. hkm plugins updateoverwrote files a project had edited. It replaced
every published file whose bytes differed from the plugin's copy, with no way
to tell a project's customisation from a stale copy — a customised layout, view
override or translation was lost on the next update — and when two plugins
published the same path, each update overwrote the other's. The installer now
records a SHA-256 of every file it publishes invar/plugin-assets.json. A
file still exactly as published is refreshed; one that was edited, never
recorded, or written by another plugin is KEPT, and the plugin's version is
written beside it as<file>.plugin-new, listed with the other plugin named.
--overwriterestores the old behaviour for one run, andenablenever
overwrites. A manifest written before this release has no hashes, so its first
update keeps every file that differs rather than guessing.- Every page head carried two
<title>elements and two descriptions when
SeoHeadembedded a SiteSEO Open Graph document, which renders its own. Only
that document'sog:andtwitter:tags are kept. - Ctrl+C left the Vite dev server running. The frontend template's hot-file
cleanup installedSIGINT/SIGHUPlisteners, and Node removes its default
terminate the moment one exists — so the shell got its prompt back while the
orphaned server kept the port, and its readline failed withError: read EIO
on top of the next command typed. The handler now cleans up, restores the
default and re-raises, and registers once per process across dev-server
restarts.
Security
composer/composer2.10.2 → 2.10.3 in the kernel's development
dependencies, for CVE-2026-84361 — arbitrary command execution through a
malicious package's Perforce source URL. It was never in a release bundle,
which installs without development dependencies, butcomposer auditfailed
every build until it moved.
Removed
templates/app/apache.conf.exampleandtemplates/app/nginx.conf.example,
andhkm newno longer writes them into a new project. The nginx or Apache
configuration a deployment runs is generated by the Edge plugin
(hkm edge:apply).
What's Changed
🔄 Other Changes
- chore(homebrew): point the formula at v1.14.0 by @hakeemRash in #168
- release: v1.15.0 — scheduled tasks, observability ports, hkm ppkg, and edited plugin files are kept by @hakeemRash in #169
Full Changelog: v1.14.0...v1.15.0