Changelog
All notable changes to Magik. The format follows Keep a Changelog, and versions follow Semantic Versioning.
Magik is one gem with subsystem modules — there is no lockstep across packages and no
per-package version. The single version lives in
lib/magik/version.rb; PUBLISHING.md owns how it is
stamped and released.
Semver does not apply below 0.1.0. Anything in a 0.0.x release may change or disappear
without notice, and 0.0.1 in particular ships no API at all.
## [Unreleased] is the release notes — write the entry as the change lands, not at release time.
Unreleased
Documentation and planning only. No framework behaviour changed, because there is none — the spec was amended from measurement and rewritten as a clean v1, and everything below is the rest of the repository being brought back into agreement with it.
Added
- Architecture decision 13 — authorization is evaluated in exactly one place. Every surface that
reaches a model — screen, action, API resource, channel, job, admin panel — names a verb in a
policyand the framework evaluates it. There is no second door to the data and no per-surface check. The evaluator sits at tier 1, belowmodel,renderandrealtime, because tier 2 has to evaluate it and imports go strictly down; it takes the actor as an opaque value andauthat tier 3 supplies that value later. Authorization is deliberately not a swap point. policyas the twenty-first subsystem —lib/magik/policy.rb, a spec-only stub like the rest:SPEC_PHASE,DSL_SURFACE,STATUS, and a.definethat raisesNotImplementedError. Re-derive the list withruby -Ilib -e 'require "magik"; puts Magik::SUBSYSTEMS.keys.join(" ")'.- Phase 4b — Media, a new phase between jobs and money, delivered at build step 8. It exists
because media spans four phases — the
:filetype is phase 1, the upload component phase 2, derivatives and transcoding are phase 4 jobs, and signed delivery is blocked onpolicy— and a capability whose value arrives only when all four have landed is a phase. It is not optional: the mission sentence names ecommerce and marketplaces and both are blocked on it. layoutandmagik describein the plan —layoutlands in phase 2 and build step 3 as the application shell, so a generated app has a sidebar on its first run;magik describelands in phase 1 and build step 2 as the grammar-as-data, derived from the option tables the DSL validates against rather than hand-maintained.A fourth executable proof for
1.0.0inROADMAP.md: a generated app is safe and usable on its first run, proven by three tests rather than a document — a cross-tenant actor denied by every generated surface, the generated shell rendering correctly at 375px, and the generated signup form throttling without revealing whether an account exists. It exists because the three criteria above it can all be true of an application that is insecure and unusable./planxand/feature, two agent commands (.claude/commands/)./planxwrites a multi-file execution plan another agent runs cold —overview.md, one file per separable slice, and astatus.ymltracker — into.claude/plans/, notdocs/, becauseMagik::Docs::PACKAGED_GLOBSpackagesdocs/**/*.mdinto the gem and a plan there would ship to every app builder./featuretakes one request from plain language to merged, and carries the rules a parallel run goes wrong without: at most four live agents, one checkout and never a worktree, the file set as the only lock, andbin/checkrun by the coordinator alone. Both are adapted from the TypeScript sibling's harness; neither claims any framework behaviour.
Changed
.claude/settings.jsonis no longer committed, and is now ignored. It is a permission allowlist and a post-edit hook — one operator's tolerance for what runs unprompted and one machine's tooling — so committing it handed every contributor those choices silently. Nothing in the repo depended on it: the lint runs becausebin/checkrunsrake rubocop, and publishing is guarded byPUBLISHING.mdandbin/release, not by a deny rule. The templatemagik newwill render into a generated app,lib/magik/cli/templates/app/.claude/settings.json, is unaffected — that one is a product artifact and stays.The server is Rack + Puma, thread-per-request — not Falcon. TruffleRuby's threads are genuinely parallel and it implements no fiber scheduler and no
fork, which makes thread-per-request the correct model, Puma the correct server, and clustered workers impossible. Re-derive withruby scripts/probes/runtime.rb --json; the provenance isdocs/architecture/12-runtime-verification.md. Corrected in.env.example,magik.gemspec's wrap-target list,.github/dependabot.yml,.github/workflows/ci.ymlandCONTRIBUTING.md.Every count now reads ten phases, thirteen build steps and thirteen non-negotiable decisions.
ROADMAP.md,llms.txtandCONTRIBUTING.mdsaid nine, twelve and twelve.ROADMAP.mdis now ordered by delivery and numbered by phase identity, and says so. It previously renumbered the phases silently, so its "Phase 3" was the spec's phase 9. The spec settles the relationship — where the build order and the phase list disagree, build order wins — and the page now states it instead of hiding it. Domains is labelled spec item 12 rather than a phase, matchingdocs/idea/06-phases.md.retries times: 5, backoff: :exponential, neverretry.retryis a Ruby keyword and a declaration named after it does not parse;computed(:name, :type) { … }keeps its parentheses because a brace block binds to the last call. Both were found by running a parser over the drafted DSL, which is the spec's rule: a drafted DSL spelling is not designed until it has been parsed.The TruffleRuby pin in
.devcontainer/Dockerfileis34.0.1, and its header no longer describes the runtime's concurrency as "Ractors and Fibers" — it is real parallel OS threads, verified on both 24.2.1 and 34.0.1.llms.txtanddocs/README.mdmap the pages that exist —docs/idea/10–12anddocs/architecture/09–12were missing from both, andllms.txtstill described the spec as "kept verbatim", listed two working CLI commands where three run, and named twenty subsystems where twenty-one are registered.The
MAGIK_*catalogue is one catalogue again, and the format is a closed set.wiki/Error-Codes.mdhad drifted to 82 codes in a looserMAGIK_<CONDITION>shape, sixteen of them a second name for a guardraildocs/idea/03-guardrails.mdhad already named —MAGIK_STATEFUL_ACTIONbesideMAGIK_ACTION_STATEFUL,MAGIK_ROUTE_COLLISIONbesideMAGIK_ROUTER_PATH_CONFLICT,MAGIK_OFFLINE_UNSUPPORTEDbesideMAGIK_PWA_OFFLINE_UNSUPPORTED. The spec page won every disagreement; the sixteen collapsed onto its spelling and the rest were renamed intoMAGIK_<SUBSYSTEM>_<CONDITION>.MAGIK_WEBHOOK_UNVERIFIED(noverify_signaturedeclared, boot) andMAGIK_WEBHOOK_SIGNATURE_INVALID(the signature did not match, runtime) are two conditions and both stay.Breaking, error surface: three shipped CLI codes were renamed —
MAGIK_UNKNOWN_COMMAND→MAGIK_CLI_UNKNOWN_COMMAND,MAGIK_COMMAND_NOT_IMPLEMENTED→MAGIK_CLI_COMMAND_NOT_IMPLEMENTED,MAGIK_INVALID_OPTION→MAGIK_CLI_INVALID_OPTION. The stability rule protects consumers, and0.0.1is a name reservation with none; the window to fix a convention is before there is anyone to break. The fourMAGIK_DOCS_*codes already fit and are unchanged, andMAGIK_ERRORis kept as the root code by a rule rather than an exception — the reasoning is indocs/architecture/03-error-codes.md.The allowed
<SUBSYSTEM>tokens are written down — a closed set of 30 indocs/architecture/03-error-codes.md, replacing an open-ended example list that ended in a dash, which is what let the catalogue drift.scripts/checks/error_codes.rbnow validates the reserved catalogue too, not only the eight implemented classes: every code in theCodecolumn ofwiki/Error-Codes.mdanddocs/idea/03-guardrails.mdmust use a token from the closed set (MAGIK_DEV_ERROR_CODE_UNKNOWN_SUBSYSTEM), carry a condition (MAGIK_DEV_ERROR_CODE_MALFORMED), not restate a condition another code already names (MAGIK_DEV_ERROR_CODE_NEAR_DUPLICATE), not revive a retired spelling (MAGIK_DEV_ERROR_CODE_RETIRED_SPELLING), and — for every guardrail on the spec page — appear in the manual (MAGIK_DEV_ERROR_CODE_UNCATALOGUED).MAGIK_DEV_ERROR_CODE_ROOT_MISCLAIMEDholds the root-code rule overlib/.MAGIK_HARNESS_STALEandMAGIK_HARNESS_MARKERS_MISSINGare nowMAGIK_CLI_HARNESS_STALEandMAGIK_CLI_HARNESS_MARKERS_MISSING.HARNESSwas never a subsystem, and both names were reserved rather than shipped, so they moved intoMAGIK_<SUBSYSTEM>_<CONDITION>rather than carving an exception into it — the same reasoning as the three CLI renames above, applied one step earlier. The old spellings are inRETIREDand cannot come back.The five
MAGIK_*codes the developer scripts inbin/print now carry aDEVtoken —MAGIK_DEV_NO_RUBY,MAGIK_DEV_RUBY_TOO_OLD,MAGIK_DEV_NO_RAKE,MAGIK_DEV_NO_WATCHER,MAGIK_DEV_NO_VERSION. They were a fourth namespace using the project's prefix under no rule and no check.bin/ships to nobody, so they are deliberately absent fromwiki/Error-Codes.md; they are documented indocs/architecture/03-error-codes.md, andscripts/checks/error_codes.rbnow scansbin/as a third corpus —MAGIK_DEV_ERROR_CODE_BIN_MALFORMEDfor a code outside the token,MAGIK_DEV_ERROR_CODE_BIN_UNFIXABLEfor one printed with nofix:. It matches theCODE: causerendering rather than the prefix, so theMAGIK_YARD_MIN_COVERAGEandMAGIK_DOCS_ROOTenvironment variables stay what they are.Every finding the repository's own checks emit is now
MAGIK_DEV_*too, and one rule covers the whole prefix. The check findings were a fourthMAGIK_namespace that nothing validated, spelled with seven tokens nothing had blessed —MANIFEST,CHANGELOG,VERSION,SPEC,DUMMY,DOC,ERROR_CODE. All 40 were renamed:MAGIK_MANIFEST_DRIFT→MAGIK_DEV_MANIFEST_DRIFT,MAGIK_CHANGELOG_HEADING→MAGIK_DEV_CHANGELOG_HEADING,MAGIK_BOUNDARY_TIER→MAGIK_DEV_BOUNDARY_TIER,MAGIK_ERROR_CODE_MALFORMED→MAGIK_DEV_ERROR_CODE_MALFORMED, and so on throughscripts/checks/andCheck#nothing_scanned. The alternative — blessing seven more tokens — dilutes a closed set of framework tokens with repository-internal concerns and leaves four namespaces under three rules. Folding them in leaves one, statable in a line: aMAGIK_code is either a framework code, whose token comes from the closed set and which an app author can hit, or a repo-internal code underMAGIK_DEV_*, emitted by this repository's own scripts and checks, which ships to nobody. There is no third kind. Nobody outside this repository could ever have matched on one, so nothing breaks; on a red gate theDEV_is signal, because it says this is about the repository, not about your app.MAGIK_BOUNDARY_TIERandMAGIK_BOUNDARY_INTERNAL_REQUIREstay in the seed catalogue as the framework codesmagik checkwill raise inside an application — one name each, neither borrowing the other's.scripts/checks/error_codes.rbgained a fourth corpus so the new rule is enforced rather than promised: it reads everyFinding.new(code: ...)underscripts/, resolving aNAME = "MAGIK_DEV_..."constant against the file that declares it, and raisesMAGIK_DEV_ERROR_CODE_FINDING_MALFORMEDfor a code outsideMAGIK_DEV_<CONDITION>,MAGIK_DEV_ERROR_CODE_FINDING_FRAMEWORK_TOKENfor one claiming a closed-set token — the mistake that reads as correct —MAGIK_DEV_ERROR_CODE_FINDING_UNFIXABLEfor a finding built with nofix:,MAGIK_DEV_ERROR_CODE_FINDING_UNRESOLVEDfor acode:argument it cannot read rather than a silent skip, andMAGIK_DEV_ERROR_FIX_ADVICEfrom the one shared phrase list, so the four corpora cannot disagree about what counts as advice. A fifth rule runs from the catalogue side:MAGIK_DEV_ERROR_CODE_PUBLISHEDrefuses a repo-internal code written intowiki/Error-Codes.mdordocs/idea/03-guardrails.md, which an app author reads and can never reach.
Fixed
magik.gemspecevaluates when itsrequirelines are stripped. Dependabot's Bundler sanitizer rewritesrequirecalls tonilbefore evaluating a gemspec, soMagik::Docswas undefined and every dependency update failed withdependency_file_not_evaluatable. The documentation globs are now read throughdefined?(Magik::Docs), keeping their single home inlib/magik/docs.rb; a real build still packages them, andtest/magik/docs_test.rbfails if it ever stops.- GitHub Pages is enabled, so
.github/workflows/docs.ymlno longer fails atactions/configure-pageswith a 404. That was the one-time human setup step the workflow's own comment names, and the URL it serves is thedocumentation_urithe gemspec has advertised since0.0.1.
Notes
- Nothing in this release is a measurement of Magik. The only numbers anywhere in the repository
are engine measurements from
scripts/probes/, and they belong to Ruby, not to this framework. - No
MAGIK_*code named in the spec is implemented. Eight codes can be raised by a running command — threeMAGIK_CLI_*, fourMAGIK_DOCS_*and the rootMAGIK_ERROR— and everything in the guardrail catalogue is seeded and raised by nothing. Re-derive the real list withjq -r '.error_codes[].code' magik.manifest.json, or the count withruby scripts/checks/error_codes.rb. Do not greplib/for the pattern: it returns eleven, because it also matches theMAGIK_DOCS_ROOTenvironment variable and two code names that appear only inside YARD examples. That over-count is the reason the manifest exists.
0.0.1 - 2026-08-26
A name reservation and the repository's ground work. No framework functionality.
This release exists so the gem name magik is held on RubyGems and so the release path is proven
end to end before there is anything worth releasing. Installing it gives you a version constant and
a CLI that can print it. Nothing else.
What is explicitly not in this release: App.define, model, migrate, component, screen,
action, channel, job, ledger, api, auth, billing, admin_panel, the router, the
server, the test DSL, and every generator. Every one of them is
specified and unimplemented. See the status callout in
README.md.
Added
- The gem skeleton, with zero runtime dependencies —
magik.gemspec,Gemfile,Rakefile,lib/magik.rb,lib/magik/version.rbandexe/magik.required_ruby_versionis">= 3.2"; TruffleRuby is the production runtime target and CRuby is supported for tooling and development.lib/magik.rbis plainautoloadover aMagik::SUBSYSTEMSmap, so requiring the gem reads one file. The gems Magik intends to wrap are listed in the gemspec as comments, to be added by the phase that needs each one. Magik::Error— theMAGIK_*error-code convention, implemented and tested rather than described: a stable code, a one-sentence cause, a runnablefix:line, a deterministic rendered message, and#to_hfor--json. Three subclasses cover the CLI's own failures.magik versionandmagik help— a working CLI, both with--json.helplists every command the spec names and marks eachreadyorplanned, because "not built yet" and "not a command" are different facts.- 19 spec-only subsystem stubs under
lib/magik/, one per planned subsystem:core,model,schema,render,action,router,realtime,jobs,ledger,api,auth,billing,admin,i18n,pwa,notify,testing,domains,check. Each documents itsSPEC_PHASE, itsDSL_SURFACEverbatim from the spec, and aSTATUS; every.defineraisesNotImplementedErrornaming the spec rather than returning a plausible empty answer.cliis the twentieth subsystem and the only one with real behaviour. - The build spec,
docs/idea/00-build-spec.md, as the source of truth, plus thedocs/idea/,docs/architecture/anddocs/ops/README.mddocumentation sets, thewiki/reference manual,ROADMAP.mdandllms.txt. - The agent workflow —
CLAUDE.md(withAGENTS.mdsymlinked to it) and.claude/with its agents and slash commands. - The developer environment —
bin/setup,bin/check(the gate, with--only,--jsonand--list),bin/dev,bin/console,bin/release(a release pre-flight that prints and never publishes),docker/compose.yml,.devcontainer/andlefthook.yml. - Minitest as the test framework, under
test/as*_test.rb, run byrake test— and passing on bare Ruby, with no bundle installed. Never RSpec: a spec decision. rake -T—build,check,docs:coverage,rubocop,test,yard. The RuboCop and YARD tasks are defined even when their gem is absent, and abort with an install hint rather than passing vacuously.- RuboCop (with
rubocop-minitest,rubocop-rake,rubocop-performance) via.rubocop.yml, and YARD via.yardopts. - CI and release plumbing —
.github/workflows/ci.yml,release.ymlanddocs.yml, which publishes the YARD docs to https://developerz-ai.github.io/magik/api/. Releases publish to RubyGems over trusted publishing (OIDC), withrubygems_mfa_requiredset on the gem;PUBLISHING.mddocuments the one-time manual bootstrap that 0.0.1 itself requires. - Project governance —
CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.mdandLICENSE(MIT).
Notes
- Nothing here has been benchmarked, deployed, or run against a real database. No number in this repository is a measurement.
0.0.1is published by hand: RubyGems cannot attach a trusted publisher to a gem that does not exist yet, so the first push is manual and every release after it goes through the workflow.PUBLISHING.mdhas the exact steps.