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 policy and the framework evaluates it. There is no second door to the data and no per-surface check. The evaluator sits at tier 1, below model, render and realtime, because tier 2 has to evaluate it and imports go strictly down; it takes the actor as an opaque value and auth at tier 3 supplies that value later. Authorization is deliberately not a swap point.
  • policy as the twenty-first subsystem — lib/magik/policy.rb, a spec-only stub like the rest: SPEC_PHASE, DSL_SURFACE, STATUS, and a .define that raises NotImplementedError. Re-derive the list with ruby -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 :file type is phase 1, the upload component phase 2, derivatives and transcoding are phase 4 jobs, and signed delivery is blocked on policy — 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.
  • layout and magik describe in the plan — layout lands in phase 2 and build step 3 as the application shell, so a generated app has a sidebar on its first run; magik describe lands 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.0 in ROADMAP.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.

  • /planx and /feature, two agent commands (.claude/commands/). /planx writes a multi-file execution plan another agent runs cold — overview.md, one file per separable slice, and a status.yml tracker — into .claude/plans/, not docs/, because Magik::Docs::PACKAGED_GLOBS packages docs/**/*.md into the gem and a plan there would ship to every app builder. /feature takes 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, and bin/check run by the coordinator alone. Both are adapted from the TypeScript sibling's harness; neither claims any framework behaviour.

Changed

  • .claude/settings.json is 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 because bin/check runs rake rubocop, and publishing is guarded by PUBLISHING.md and bin/release, not by a deny rule. The template magik new will 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 with ruby scripts/probes/runtime.rb --json; the provenance is docs/architecture/12-runtime-verification.md. Corrected in .env.example, magik.gemspec's wrap-target list, .github/dependabot.yml, .github/workflows/ci.yml and CONTRIBUTING.md.

  • Every count now reads ten phases, thirteen build steps and thirteen non-negotiable decisions. ROADMAP.md, llms.txt and CONTRIBUTING.md said nine, twelve and twelve.

  • ROADMAP.md is 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, matching docs/idea/06-phases.md.

  • retries times: 5, backoff: :exponential, never retry. retry is 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/Dockerfile is 34.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.txt and docs/README.md map the pages that exist — docs/idea/10–12 and docs/architecture/09–12 were missing from both, and llms.txt still 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.md had drifted to 82 codes in a looser MAGIK_<CONDITION> shape, sixteen of them a second name for a guardrail docs/idea/03-guardrails.md had already named — MAGIK_STATEFUL_ACTION beside MAGIK_ACTION_STATEFUL, MAGIK_ROUTE_COLLISION beside MAGIK_ROUTER_PATH_CONFLICT, MAGIK_OFFLINE_UNSUPPORTED beside MAGIK_PWA_OFFLINE_UNSUPPORTED. The spec page won every disagreement; the sixteen collapsed onto its spelling and the rest were renamed into MAGIK_<SUBSYSTEM>_<CONDITION>. MAGIK_WEBHOOK_UNVERIFIED (no verify_signature declared, boot) and MAGIK_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, and 0.0.1 is a name reservation with none; the window to fix a convention is before there is anyone to break. The four MAGIK_DOCS_* codes already fit and are unchanged, and MAGIK_ERROR is kept as the root code by a rule rather than an exception — the reasoning is in docs/architecture/03-error-codes.md.

  • The allowed <SUBSYSTEM> tokens are written down — a closed set of 30 in docs/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.rb now validates the reserved catalogue too, not only the eight implemented classes: every code in the Code column of wiki/Error-Codes.md and docs/idea/03-guardrails.md must 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_MISCLAIMED holds the root-code rule over lib/.

  • MAGIK_HARNESS_STALE and MAGIK_HARNESS_MARKERS_MISSING are now MAGIK_CLI_HARNESS_STALE and MAGIK_CLI_HARNESS_MARKERS_MISSING. HARNESS was never a subsystem, and both names were reserved rather than shipped, so they moved into MAGIK_<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 in RETIRED and cannot come back.

  • The five MAGIK_* codes the developer scripts in bin/ print now carry a DEV token — 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 from wiki/Error-Codes.md; they are documented in docs/architecture/03-error-codes.md, and scripts/checks/error_codes.rb now scans bin/ as a third corpus — MAGIK_DEV_ERROR_CODE_BIN_MALFORMED for a code outside the token, MAGIK_DEV_ERROR_CODE_BIN_UNFIXABLE for one printed with no fix:. It matches the CODE: cause rendering rather than the prefix, so the MAGIK_YARD_MIN_COVERAGE and MAGIK_DOCS_ROOT environment 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 fourth MAGIK_ 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 through scripts/checks/ and Check#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: a MAGIK_ 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 under MAGIK_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 the DEV_ is signal, because it says this is about the repository, not about your app. MAGIK_BOUNDARY_TIER and MAGIK_BOUNDARY_INTERNAL_REQUIRE stay in the seed catalogue as the framework codes magik check will raise inside an application — one name each, neither borrowing the other's.

  • scripts/checks/error_codes.rb gained a fourth corpus so the new rule is enforced rather than promised: it reads every Finding.new(code: ...) under scripts/, resolving a NAME = "MAGIK_DEV_..." constant against the file that declares it, and raises MAGIK_DEV_ERROR_CODE_FINDING_MALFORMED for a code outside MAGIK_DEV_<CONDITION>, MAGIK_DEV_ERROR_CODE_FINDING_FRAMEWORK_TOKEN for one claiming a closed-set token — the mistake that reads as correct — MAGIK_DEV_ERROR_CODE_FINDING_UNFIXABLE for a finding built with no fix:, MAGIK_DEV_ERROR_CODE_FINDING_UNRESOLVED for a code: argument it cannot read rather than a silent skip, and MAGIK_DEV_ERROR_FIX_ADVICE from 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_PUBLISHED refuses a repo-internal code written into wiki/Error-Codes.md or docs/idea/03-guardrails.md, which an app author reads and can never reach.

Fixed

  • magik.gemspec evaluates when its require lines are stripped. Dependabot's Bundler sanitizer rewrites require calls to nil before evaluating a gemspec, so Magik::Docs was undefined and every dependency update failed with dependency_file_not_evaluatable. The documentation globs are now read through defined?(Magik::Docs), keeping their single home in lib/magik/docs.rb; a real build still packages them, and test/magik/docs_test.rb fails if it ever stops.
  • GitHub Pages is enabled, so .github/workflows/docs.yml no longer fails at actions/configure-pages with a 404. That was the one-time human setup step the workflow's own comment names, and the URL it serves is the documentation_uri the gemspec has advertised since 0.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 — three MAGIK_CLI_*, four MAGIK_DOCS_* and the root MAGIK_ERROR — and everything in the guardrail catalogue is seeded and raised by nothing. Re-derive the real list with jq -r '.error_codes[].code' magik.manifest.json, or the count with ruby scripts/checks/error_codes.rb. Do not grep lib/ for the pattern: it returns eleven, because it also matches the MAGIK_DOCS_ROOT environment 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.rb and exe/magik. required_ruby_version is ">= 3.2"; TruffleRuby is the production runtime target and CRuby is supported for tooling and development. lib/magik.rb is plain autoload over a Magik::SUBSYSTEMS map, 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 — the MAGIK_* error-code convention, implemented and tested rather than described: a stable code, a one-sentence cause, a runnable fix: line, a deterministic rendered message, and #to_h for --json. Three subclasses cover the CLI's own failures.
  • magik version and magik help — a working CLI, both with --json. help lists every command the spec names and marks each ready or planned, 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 its SPEC_PHASE, its DSL_SURFACE verbatim from the spec, and a STATUS; every .define raises NotImplementedError naming the spec rather than returning a plausible empty answer. cli is 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 the docs/idea/, docs/architecture/ and docs/ops/README.md documentation sets, the wiki/ reference manual, ROADMAP.md and llms.txt.
  • The agent workflow — CLAUDE.md (with AGENTS.md symlinked to it) and .claude/ with its agents and slash commands.
  • The developer environment — bin/setup, bin/check (the gate, with --only, --json and --list), bin/dev, bin/console, bin/release (a release pre-flight that prints and never publishes), docker/compose.yml, .devcontainer/ and lefthook.yml.
  • Minitest as the test framework, under test/ as *_test.rb, run by rake 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.yml and docs.yml, which publishes the YARD docs to https://developerz-ai.github.io/magik/api/. Releases publish to RubyGems over trusted publishing (OIDC), with rubygems_mfa_required set on the gem; PUBLISHING.md documents the one-time manual bootstrap that 0.0.1 itself requires.
  • Project governance — CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md and LICENSE (MIT).

Notes

  • Nothing here has been benchmarked, deployed, or run against a real database. No number in this repository is a measurement.
  • 0.0.1 is 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.md has the exact steps.