Repository navigation
Releases: ShkodnikAI/Metalogos-
Release list
0.32.0
Security
-
№689 (gh#1256, В44 — the R-1 audit finding; fix date 2026-10-15):
the mail ПДн (personal data) learned to SPEAK at the flow layer. Before
the fix theprivatemark lived on exactly ONE registry row
(canary_check, №661),imap_readcarried all 9 fieldsuntrusted,
and the label engine was NAME-based only — the registry field-meta
never reached the №323/№325 flow machinery: a program could send the
mail correspondence (from/to/subject/body_text/body_html/attachments)
straight intosmtp_send/http_post/respondwith ZERO findings —
the silent ПДн egress the audit's R-1 described («the machine does not
check the private → sink flow because there are almost no marks»).
The repair, RED-first (tests/naryad_689_private_field_flow.rs): (1)
the REGISTRY marks — the ImapEmail ПДн fields areprivatenow (the
shape read off the impl, №650; uid/date/seen stayuntrusted— the
server metadata); (2) the BRIDGE —
semantic::registry_private_flow_label: a row whose field-meta
carriesprivateseeds the flow layer with the type-layer secret
projection (conf=private, integrity=trusted, the ADR-0154 §2 form), so
the №325 sink gate fires the PII_EGRESS_NETWORK / PII_EGRESS_OUTPUT
classes on the let-bound AND the direct-call paths;redactstays the
only legal downward cure (№326, tested). The honest boundaries: the
bridge is deliberately NARROW (rows without a private mark — the
untrusted/integrity vocabulary — keep the old path; whole-value
fail-closed posture, the per-field precision is the stage-2 contour);
card_contacts/cal_eventskeep the honest JSON-String spelling with
the reason in the row comments (the stage-0 scalar vocabulary carries
no label — aString<private>row would erase to Unknown and regress
the typed floor; the structural typing is the №688-cycle tranche).
Metrics (the actual recount): the field-label share 46/48 = 95.83% —
unchanged; the sensitive surface 73/145 = 5034 bp — unchanged (the
marks changed values, not coverage). Affected: programs egressing the
imap_read fields into the egress sinks — they now refuse at compile
with the PII class named (advisory underprofile legacy, the №325
posture). -
№685 (gh#1252, В44 — the S-1 audit's High finding; fix date 2026-10-15):
the RAW template insert{{{ var }}}inrender()is FAIL-CLOSED now.
Before the fix the N9 raw form (gh#1219) landed its argument VERBATIM in
the page while every static layer answered «sanitized» at the render
boundary —render("Comment", "author", body.author, "text", body.text)
with{{{ text }}}in atext: Stringtemplate passedmlog check,
runandservewith zero findings: the XSS the audit demonstrated,
invisible to the HTML_INJECTION category. The repair is the audit's
prescription, three gates deep: (1) the DECLARATION gate — a template
parameter used in{{{ }}}must be declaredHtml, anything else is
theRAW_INSERT_NOT_HTMLstable-code refusal BEFORE execution on BOTH
backends (check_program + the compile_program gate block, the №673
contour shape; the code joins the №385/ADR-0169 classification, nothing
renamed); (2) the CALL-SITE coercion — a String-shaped argument into an
Html-typed parameter of a statically-known template refuses (the №662
rule-1 twin, the implicit String→Html the runtime accepted silently);
(3) the TAINT/LABEL layers —render()is now a sanitizer for the
ESCAPED positions and a CONDUCTOR for the RAW ones (the template body
decides from the program's declarations; a dynamic/unknown name is a
conservative conductor; the UserInput/LLM egress gates
OPEN_REDIRECT/HTML_INJECTION/TELEGRAM_HTML_INJECTION read the conducted
taint instead of a fake lift). The honest path is unchanged: an
Html-typed parameter built by an explicit Html producer
(render("Outer", render("Inner", s))) renders verbatim on both
backends. Affected: programs that fed String values into raw
{{{ }}}positions — they now refuse at start with the parameter and
the fix named in the message (the class-4 dictionary entry, the guard
scenario per the №653 form; the audit's «do not cut 0.31.0 before S-1»
is satisfied).template_render's raw=true trusted-author contract is
unchanged (the limitations row now tells the two contracts apart). -
№673 (gh#1200, В41 — BREAK_OUTSIDE_LOOP, the R-2 audit's two
«молча неверные» break/continue edges; fix date 2026-10-14): abreak/continue(а)
OUTSIDE any loop (№658-b) or (б) crossing the VALUE channel — an arm
body of a match/if-as-value (№658-a) — now refuses as a SEMANTIC error
BEFORE execution on BOTH backends, with the stable
BREAK_OUTSIDE_LOOPcode stamped first (the №523 shape; the code
joins the №385/ADR-0169 classification, no existing code renamed).
Before the fix both edges were the class-4 dictionary entries (№653)
the honest-limits records pinned: the TW refused at RUNTIME
(«break/continue used outside of a loop»), while the VM either
compiled the bare statement to NOTHING and continued (the audit's
import_batch shape —breakon the error path instead ofreturn,
and the code after it RAN: the recorded pre-fix VM answer was
Ok("published")) or silently ABSORBED the signal inside a
value-channel arm and ran the loop to its condition (the recorded
pre-fix VM answer wasOk("matched;matched;matched;")where the TW
refused). The repair is the audit's recommendation — a semantic gate,
not a runtime mechanism: ONE shared walker
(semantic::break_outside_loop_violations) walks every executable
body (pattern, tool method, server route, hook, on_deny handler,
test) with the loop/value-channel context model — loop bodies open
the loop context, statement-position branches preserve it (the
№658 contract the norms S-BLK-011/012 pin stays UNCHANGED — a break
nested through if/match inside a loop keeps working), value-channel
arm bodies forbid the signal outright, a loop inside an arm re-opens
the legal context for its own body. Wired into BOTH production gates:
check_program(the TW run/serve path) and — for the first time for
a semantic finding class —compile_program(the VM compile→run .mbc
path, which had no semantic gate; only THIS class is wired there, not
the whole check_program — the widening is a separate conversation).
The runtime mirrors stay untouched as backstops. Two conformance
pairs landed (the sblk_011/012 form):sblk_015_break_value_arm
(№658-a) andsblk_016_break_outside_loop(№658-b), both
STATUS: error, CODE: BREAK_OUTSIDE_LOOP— RED before the fix on
the VM (the silent answers above), green after. Honest limits:
the blocks.md №658-a/№658-b records moved to the REPAIRED form
(the №652-a sample; the edge history kept). Affected: programs that
RELIED on the silent absorption (the №653 class-4 guard scenario) —
they now refuse loudly at startup and must usereturn.
Size: M; risks: low-medium (any pre-existing program carrying the
illegal shapes now blocks at startup — that is the repair's purpose;
the legal break/continue surface is contract-pinned unchanged).
Added
-
№688 (gh#1255, В44 — the audit f92c61c S-3; the draft, the owner's
verdict pending): the 0.32 typed-source goal moves to the
FIXED-LIST closure form (ADR-0193 Proposed) — «of the 67 untyped
sensitive sources recorded at the anchorf92c61c, at least 20
closed, the ПДн-class first; the denominator is OUT of the goal».
The machine half:scripts/ci/gate_032_untyped_sources.txt(the 67
ids with the registry lines at the anchor, generated by the anchor
worktree's own №644 inventory) + the--goal032report-only counter
(GREEN at the honest 0/20 start; the--gate032 Nfloor mode exists
mechanically and wires ONLY with the owner's §5 fixation — the
gate_031 one-PR form). The X-3 fraud shape (the 0.31 goal met by the
denominator growth 66/138 → 73/145 whileuntyped 67did not move)
is closed mechanically: only closing REAL rows of the FROZEN list
moves the goal. -
№691 (gh#1258, В44 — the spec topics): the specification grew two
topics — routes.md (Topic 4: S-RT-001 the route declaration
vocabulary, S-RT-002 the direct respond* early answer, S-RT-003 the
depth-2 RESPOND_SWALLOWED refusal, S-RT-004 the raw-insert Html-only
rule, S-RT-005 the protective-header ownership — the middleware's,
not set_header's, S-RT-006 the respond_html dual contract) and
state.md (Topic 5: S-ST-001 the world_state private-by-default
materialization refusals, S-ST-002 the private label gates
materialization — not the handle flow — plus the typed device chain,
S-ST-003 the literal-key memory round-trip). NINE new conformance
pairs (both backends, the №645 runner; the №685 raw-insert pairs
referenced, not duplicated); the probes found NO cross-backend
divergence — the route/state refusals are shared-code builtins and
semantic checks. The normative core: 33 → 42 norms. The №674
mirror-scan IN SYNC (the №668 typed-coarse honesty got its №588-form
limitations row in the same PR); REFERENCE cross-references the
topics from the server and the world_state rows. -
№674 (gh#1201, В41, process/ci — the honest-boundary machine sees
the spec):honest_boundary_check.pynow scans the «Honest limits»
sections ofdocs/spec/*.md— every ACTIVE record (the №-id bullets)
MUST have its mirror row indocs/limitations.md; REPAIRED records
are exempt (the repair closed the edge — the №652-a sample). The scan
is repo-state based and runs in the SAME blocking №604 job on PRs AND
on push/main (the job's push/main branch runs the full retrospective
now instead of exiting early — no new CI cell, №535); the--diff
marker/dictionary modes stay unchanged. The gap it closes: the №659-x
/№659-y records lived indocs/spec/blocks.mdONLY — limitations.md
had no mirror (...
0.30.1
Security
-
№658 (gh#1159) — the VM break/continue parity (the behavior change,
fail-closed; AFFECTS 0.30.0 AND EARLIER on the VM backend; the audit
guard scenario: abreak/continueinside a loop body — top-level,
inside anifguard, inside a match arm, in a nested loop — was
silently IGNORED by the VM: the body ran to the condition cap as if
the statement were absent, and a top-level form kept an unresolved
Jump(0)placeholder that tripped the infinite-loop guard, while the
tree-walking backend honored both statements; the X-1/Y-1/Q-1
silently-wrong control-flow class, the №652-a finding): the VM loop
path mirrors the TWControlFlowcontract 1:1 — every
break/continuereaches its NEAREST enclosing loop on BOTH backends
(breakexits the loop;continuestarts the next iteration — the
condition re-test forwhile, the next item for
each/each_with_index). The compiler now resolves every loop jump
from the loop-stack entry: the shared statement compiler registers a
Jump(0)placeholder in the NEAREST entry at any nesting depth and
the loop compiler patches it from the POPPED entry (the stale-local
patch loops — the pre-№658 root cause — are gone). The norms
S-BLK-011/012 + five conformance pairs pin the record; the TW is
unchanged. Found by №652 (the spec topic 2 probe); the repair follows
the №651/№629 precedent. The fix lands 2026-10-09 (this PR; after the
v0.30.0 tag b4b9abb lineage — the published tag is affected on the VM
backend; no re-tag — the №620 precedent). -
№651 (gh#1145) — the TW/VM condition parity (the behavior change,
fail-closed; AFFECTS 0.30.0 AND EARLIER on the VM backend; the audit
guard scenario: a composite or an opaque value in a condition — e.g.
if [items] { … }orif hash_password(pw) { … }— silently chose a
branch on the VM (a non-empty List took THEN, an opaque handle took
ELSE) while the tree-walking backend refused; the X-1/Y-1/Q-1
silently-wrong-guard class): the VM condition path (the new
Instruction::JumpIfNotCond, emitted for if / else-if / while / the
match guards) mirrors the TWValue::as_bool1:1 — the truthy set of
a condition is exactly {true Bool, Float ≠ 0.0, non-empty String},
Unit is falsy, and every composite / opaque / Fluid value refuses
with the stable[TYPE_MISMATCH]code on BOTH backends (the norm
S-VAL-013; the №629 loud-refusal class). The soft truthiness of
&&/||(the №532 twin, compiled to the legacyJumpIfNot) is
unchanged and stays pinned. Found by №645 (the spec-carcase
conformance build, the finding №645-a); the repair follows the №629
precedent. The fix lands 2026-10-08 (this PR; after the v0.30.0 tag
6eafee1 — the published tag is affected on the VM backend; no re-tag
— the №620 precedent).
Fixed
- #1169/#1170 (the office VM-pilot staging, FO-050; PR — this fix) —
the VM serve-boot db bootstrap (the interpreter-parity boot path; the
behavior change, fail-closed at boot; Fixed-class → rides the
0.30.1 train):db { url: env("NAME") }on the VM serve lane
resolved the URL inside EVERY route handler (the №758 lazy
first-db-access semantics), so the same read went through the №259
serve-route gate — withoutMETALOGOS_ENV_ALLOWLISTevery request
500'd with ENV_NOT_PERMITTED (the interpreter resolves the url
expression at BOOT in the Process context and serves the same file +
env with 200), and the schema-DDL bootstrap only ran when a request
first touched the db — on the office's real corpus (30 routes) a
fresh DB stayed at 0 tables and the first request died with an
unrelated arity 500 instead of an honest bootstrap diagnostic
([vm/db] Connectedwas already on the boot log — the TW
registration lane opened the file — while the VM request lane kept
re-resolving and re-refusing per request). The fix: at VM serve boot
(run_server+ the test harness, on the compiled Program BEFORE it
is shared)boot_resolve_db_urlresolves the env name ONCE in the
Process (boot) context — the resolved literal replacesdb_url_env,
so the request path performs NO env() reads at all — and replays the
schema DDL eagerly on a boot connection (same open + WAL +
DDL-tolerance steps as the lazy open), so a fresh DB carries the
schema BEFORE the first request (the #1169 interpreter parity); an
unset variable is a LOUD BOOT error naming the variable and the
remedy (was: a silent OK boot — the TW registration error line was
swallowed by the merge, then per-request 500s); a non-sqlite URL is
the same LOUD boot error it used to be at the first access (№758
text moved to boot time). The №259 serve-route env gate is
UNCHANGED for program-levelenv()calls — the boot read is the
Process-context class (the №259 contract, the №457 registration
zone). The lazy per-request connection open stays for requests that
never touch the db (№409 cost contract);reset_for_reusesemantics
unchanged (№403 4/4). Contract tests
metalogos-server/tests/issue_1169_1170_vm_boot_db.rs(T1: the
db-touching route serves WITHOUT the allowlist — was 500
ENV_NOT_PERMITTED per request; T2: the fresh DB carries the schema
table immediately after boot; T3: the unset variable fails the BOOT
loudly naming the variable — was a silent OK boot; T4: the
literal-URL twin — no regression on the №758 surface); №758 5/5, №259
7/7, №160 23/23 (1 ignored), №403 4/4, №457 5/5, №399 4/4 GREEN.
TheTeamSkillWired expects 2 argsfirst-request symptom of #1169
(the office corpus) is NOT reproduced minimally and is NOT claimed
fixed — it is expected to disappear with the empty-schema state gone;
the minimal-repro ask stands in the issue thread.
0.30.0
The release verdict (the 0.30 v2 gate)
GREEN at the release cut (tag v0.30.0 @ 6eafee1c — the fresh run of
unfreeze_gate.py --office-tests pass --gate-target 0.30 — exit 0, the
summary is committed in the lockstep PR #1141) and the FULL В34 form read
exit 0 on main @ 6d1aa26 (the branch-protection fact key landed after
the cut per the §5 recorded sequencing — the criterion machine-reads the
audit conclusion):
| Goal | Verdict | Evidence |
|---|---|---|
| parameterized share ≥ 5000 bp (OWNER-FIXED 2026-10-07, the В34 addendum form, ADR-0186 §5) | MET | 6043 bp — 55/91 = 60.43% (the three honest packages: №623 7 rows → №631 27 rows → №637 21 rows) |
| open High findings (server path) = 0 | MET | the release-block counter = 0 (machine-read, never hand-zeroed) |
| the domain quorum | MET | 0/8 groups carry duplicates vs the goal 1/3 |
| the serve-e2e inventory | MET | every state-accumulating declaration done |
| the branch-protection criterion (the В34 addendum Z-3): the LAST branch-protection-audit run GREEN | MET | the switches applied by the owner BEFORE the publication (the №646 request held): 25 required checks = the maintainers.md checklist verbatim, strict, enforce_admins, the PR requirement (approvals 0); the first GREEN audit run 37736590049 on fc856fb; gh#1000 closed manually 2026-10-08; the fact key fact_branch_protection_audit: GREEN landed WITH its checker (№525) in #1143 |
§4 (ADR-0177) GREEN across the board: types 309/516 (5988 bp floor), dedup 0
(mirrors 6/6), debt 11/0/15, the memory office-E2E evidence record GREEN.
The publication is the owner's act (docs/release-checklist.md, step 5 —
"the machine never publishes"): executed by the owner's PAT on the verbatim
chat verdict «публикуй» (2026-10-08), the v0.29.0 precedent.
Security
- №629 (gh#1096) — the VM comparison parity (the behavior change,
fail-closed; AFFECTS 0.29.0 AND EARLIER on the VM backend — all six
comparison operators, including!=; the audit guard scenario
if owner != current_user { … }withowner = UnitPASSED on the
VM — the X-1/Y-1/Q-1 silently-wrong-guard class): the VM's
==/!=/>/</>=/<=now mirror the TW matrix 1:1 — the
heterogeneous equality ("abc" == 5.0,"5" == 5.0), the ordering
on non-Float pairs ("b" > "a",true > false) and the
incomparable types (Struct/List/Html) REFUSE with the stable
[TYPE_MISMATCH]code on BOTH backends; the legacy VM answered a
silentfalse(andtruefor numeric strings) — the
silently-wrong class;Unit == Unitistrueon both now. Found by
the №621 fuzzer sweep the day it stopped being vacuous; the №479
+precedent. The fix landed on main 2026-10-07 (22d62fa — after
the v0.29.0 tag, 4be0f72: the published tag is affected; no re-tag
— the №620 precedent); the affected-release audit 2026-10-08 (the
unified audit of 4830170, the dispatch gh#1136, Q-1).
Changed
-
№630 (gh#1098) — the 0.30 release-gate §5 mechanical transition
(process, the owner-fixed merge):owner_fixed: truein
gate_030_goals.txt(ADR-0186), ADR-0186 Proposed → Accepted, the
DEFAULT gate target ofunfreeze_gate.py0.29 → 0.30; the 0.28
and 0.29 records stay verifiable explicit targets (--gate-target).
The typed parameter of the 0.30 gate = the parameterized share
(≥ 5000 bp, the owner's 2026-10-07 verdict); the machine read is
RED-loud until the movement completes — by design. -
№631 (gh#1099) — the parameterized movement 0.30 №2 (internal):
the second honest package of 27 verified rows — the registry-order
contour carriesList<T>/Struct<Name>spellings now (the
memory/todo/goal/profile/mood families; the polymorphic list ops stay
honestly coarse — the element type is the caller's). The parameter
share 7/91 = 769 bp → 34/91 = 3736 bp; the floors raised in the
same PR (the parameterized baseline 3736, the in-tree PARAM_FLOOR 31). -
№627 (gh#1110) — the field-label metadata (internal, the stage-2
preparation per ADR-0178): the registry specs may carry the
Struct<Name>{field:label,...}form; the labels live in the registry
side-table (BuiltinSpec.field_meta), theTypeenum stays erased;
a malformed section is a loudUnknown(fail-closed). The FOURTH
metric — the field-label share among the parameterized Struct rows
(3/20 = 1500 bp, the first honest package of 28 verified rows:
GeoLocation 9, Weather 13, LlmUsage 6) with the checked-in floor +
the in-tree lock. The parser mirrors (gen_metrics, gen_reference, the
two Rust pins) synced — the generated blocks regenerated. -
№633 (gh#1101) — the dead_code burn (internal): 33 → 15 (18
places removed or justified), the floor re-locked in the same PR. -
№634 (gh#1102) — the self-host lexer lane lifted green
(internal): the §5 test fixture had drifted behind the language
(let-mut, the no-entry flow, the newline in the quote class, the
stale keywords) — repaired; the №617 artifact documented-accepted
with the mutation-verified pin; the ignore floor 14 → 13. -
№635 (gh#1103) — the VM template_render lane lifted green
(internal): both vm_golden corpus sweeps 189/189 (the template gap
was closed by №250; the harness facts fixed — the env sidecars №385,
the candle-gated exclusion mirror, the browser-gated p88); the ignore
floor 13 → 11. -
№622 (gh#1085) —
returninside amatch/ifEXPRESSION arm is
captured as the arm's value on BOTH backends: the TW documented
contract (the expression channel carries values, not control signals)
now holds on the VM —let v = match x { "a" then { return 5.0 } }
binds5and runs the tail instead of terminating the pattern with
"5". The consumer scan (match arms / if-without-else / if-else /
MatchExpr — the implicit block value never becomes an early return;
sandbox/flow have no statement bodies in the grammar). -
№623 (gh#1086) — the parameterized signature vocabulary stage-0
(internal): the registry specs carryList<T>/Struct<Name>
spellings (the enum erases the parameter); the THIRD metric — the
parameterized share among the List/Struct rows (7/91 = 769 bp, the
first honest package of 7 verified rows; the general typed share 5988
bp, the precise share 4224 bp untouched per the Z-2 verdict). -
**№621 (gh#1084) — the differential fuzzer executes again (internal):
** the sweep was vacuously green (0/150 programs ran — the stale
builtin call, the undefined idents, the heterogeneous arithmetic); the
generator produces the №612 loop class (the bare-call bodies, the
mixtures, the tails, the nested shapes) and the permanent LIVENESS
ratchet pins the executability (≥ 60% of the sweep on both backends). -
№617 (gh#1075) — the semantic checker grows three compile-time
refusals (the gh#967 §3 lane): the variable-scope walk (the TW env
model verbatim — flat, never popped, the entities as globals, the
render/reflex_* name-reference slots exempt), the undefined-variable
read refusal (the runtime message verbatim + the stable
UNDEFINED_VARIABLEcode), the assignment-to-undefined refusal, and
the static opaque-concat check (the declared-fact twin of the runtime
guard —cannot concatenate opaque type …). The three#[ignore]d
gh#967 §3 tests lifted green; the debt floor 17 → 14; the №465 fuzzer
VM lane now carries the №523 semantic gate (the production order). -
№616 (gh#1077) — the PRECISE movement 0.30 №1: 61 registry rows
typed against the verified single-shape handler facts — the precise
share 3042 → 4224 bp (218/516), the general share 4670 →
5852 bp (302/516), TYPED_FLOOR 237 → 298; the floors raised in the
same PR (the №757 procedure); the 0.29 gate fact synced (the
machine-read rule). -
№615 (gh#1078) — the 0.30 gate DRAFT (Proposed, ADR-0186):
gate_030_goals.txtwithowner_fixed: false— the draft parameters
strictly above the live facts (typed 6100 > 5852, precise 4500 >
4224, the X-3 rule); the gate reads it honestly RED on the fixation
fact; the DEFAULT gate target stays 0.29 (the blocking CI never reads
a draft) — the fixation is the OWNER's gate. -
№618 (gh#1076) — the Камертон cycle closure report: the external
review report artifact (docs/audits/kamerton-report-v0.29.0.md) —
the SHA256 of the v0.29.0 assets re-run on the downloaded artifacts
(3/3 OK against the attached SHA256SUMS), the R1–R6 verdicts from real
runs of the repro corpus (scripts/repro_kamerton/, the t92
reconstruction) on the fixed main — every probe now behaves as its
contract demands (the mutation probe goes red:assert_eq failed: 3 != 999), the D1/D2 → PR/commits map, the affected releases
(v0.28.1, v0.28.2 — update to v0.29.0), and the honest protocol
corrections verbatim from t92. The forwarding to the external
reviewer is the owner's gate (the external channel).
0.29.0
The release verdict (the 0.29 v2 gate)
GREEN at the release cut (main @ 4be0f72, the fresh run of
unfreeze_gate.py --office-tests pass --gate-target 0.29 — exit 0, the
summary is committed in the lockstep PR):
| Goal | Verdict | Evidence |
|---|---|---|
| typed share ≥ 3500 bp (OWNER-FIXED 2026-10-04) | MET | 4670 bp — 241/516 = 46.70% |
| precise share ≥ 3000 bp (№605, the audit X-3, OWNER-FIXED 2026-10-06, ADR-0181 §3.1) | MET | 3042 bp — 157/516 = 30.42%; the honest RED record (2131 bp, PR #1069) landed BEFORE the movement |
| open High findings (server path) = 0 | MET | the release-block counter = 0 (machine-read, never hand-zeroed) |
| the domain quorum | MET | 0/8 groups carry duplicates |
| the serve-e2e inventory | MET | every state-accumulating declaration done |
§4 (ADR-0177) GREEN across the board: types 4670 bp, dedup 0 (mirrors 6/6),
debt 17/0 + 33/33, the memory office-E2E evidence record GREEN.
The publication is the owner's act (docs/release-checklist.md, step 5 —
"the machine never publishes"): executed by the owner's PAT on the verbatim
chat verdict «Делай релизный наряд 0.29.0» (2026-10-06), the v0.28.2
precedent.
What is in the release
- №612 (gh#1061, PR #1068) — the loop semantics fixed: a bare call in
the body NEVER terminates the loop, the tails execute; the vacuous
greens eliminated as a class (the test harness reads ControlFlow now —
an earlyreturnin a test body is a LOUD failure, the corpus scan:
zero test bodies use explicit return). Pre/post on the honest-count
program: 1 → 3. The TW↔VM parity e2e pinned (the honest count 3 on
BOTH backends). - №611 (gh#1060, PR #1067) — the schema-as-code references fixed: the
qualified name parsed in EVERY spacing (the dot is the separator —
references(parent.id)no longer drops the modifier silently, the
brokenREFERENCES parent(.)clause is impossible);mlog check
validates the generated DDL on an in-memory SQLite dry-run over the
SSOT renderer (ast::schema_table_ddl) — a DDL SQLite rejects fails
the check LOUDLY, never the apply. - The 0.29 gate movement: №605 (gh#1046, PR #1062) — the X-3
precise-share goalgoal_precise_share_bp: 3000OWNER-FIXED
(ADR-0181 §3.1), the coarse goal stays the floor; №613 (gh#1070,
PR #1071) — 47 verified PRECISE rows, 2131 → 3042 bp, the floors
raised in the same PR per the №757 procedure (precise 3042, general
4670, TYPED_FLOOR 190 → 237); the between-releases enforcement is the
precise-baseline ratchet (the share may ONLY GROW).
The owner's decisions archived (the records)
- ADR-0184 (№603, gh#1044, PR #1063) — the depth-≥2 respond boundary:
REFUSED FOREVER, variant Б.RESPOND_SWALLOWEDstays forever — the
barerespond*/respond_html*form at depth ≥ 2 is refused at startup
on BOTH backends; the migration remains one word (return respond(...)). - ADR-0185 (№609, gh#1050, PR #1066) — the memory_forget container
form: DEFERRED to the memory-phase planning (postponed, NOT
rejected; the return trigger is the memory-phase planning).
Process
- №608 (gh#1049, PR #1064): the Y-4 quota line in the wave report — the
counter learns the real formats, the--linemode; the quota is
machine-counted on every wave report. - №610 (gh#1051, PR #1065): the newcomer path — the broken first step
fixed (the clone URLs), the live pool list rendered from
gh#951–gh#959, 9/9 acceptance criteria. - The 0.28.2 post-publication sync (PR #1059):
fact_open_high_server
closed at 0 on the live tag, REALITY §6.17 → PUBLISHED.
The full verbatim wave records: CHANGELOG [0.29.0]; the release prep:
REALITY §6.18.
The assets (attached by the release pipeline, release: published →
the tagged commit is built):
mlog-linux-x86_64— the release binary (cargo build --release --locked -p metalogos-server --bin mlog; the exact toolchain is
recorded in BUILD-INFO.txt).mlog-linux-x86_64.cdx.json— CycloneDX SBOM describing exactly that
binary.SHA256SUMS— digests of the binary, the SBOM and BUILD-INFO.- Two Sigstore attestations via GitHub artifact attestations: the SLSA
build provenance and the SBOM attestation bound to the same binary
subject.
Built from the tag v0.29.0 (4be0f72), the version lockstep 0.29.0 in
the workspace manifests (№614, gh#1072, PR #1073 — the №583 pattern:
Cargo.toml + Cargo.lock ×5 members + the README badge + the generated
SSOT blocks).
The post-publication fact sync: REALITY §6.18 release state →
PUBLISHED — the syncing PR #1074 (merged
2026-10-06; ADR-0181 §4.1 step 1 — the syncing PR linked from the
release notes). The gate facts unchanged by the release:
fact_open_high_server 0 (no release-block carriers opened), the
precise share 3042 bp (the ratchet holds — only up), the quorum 0/8.
Security (ADVISORY — restart your tests)
0.28.x and earlier are affected on the TW interpreter — the fix ships IN
0.29.0; users of earlier versions should re-run their existing tests on
≥ 0.29.0. The Камертон matrix finding (the unified audit of e40ce8e /
v0.29.0, the dispatch gh#1089) traced the №612 loop defect to its
user-facing consequences: until 0.29.0, a call with a non-Unit result at
the END of a loop body was read as an early Return of the ENCLOSING
function — the loop ran exactly ONE iteration, the tail after the loop
was silently cut, and in mlog test bodies the harness flattened the
fabricated Return into Ok — a GREEN run that never executed the
checks. The return here was FABRICATED by the implicit block-value
mechanics, not written by the author — the dictionary class «a
respond*/deny*/return being IGNORED» (№604), mirrored: a return being
INVENTED.
each row in rows { db_execute("INSERT …", row) }— with the INSERT
call last in the body, the first iteration returned from the function:
one row inserted, the rest skipped, exit code 0.
-
The guard patterns with an audit loop: a loop that re-checks a
condition (audit/retry/scan) stopped after the first iteration — the
code after the loop, the second half of the guard, never ran. -
The batch inserts/updates: a loop over rows executed exactly ONE
element — the remaining rows were silently skipped (no error, no
warning, exit code 0). -
The false confidence of vacuous green runs: in
mlog test, a test
whose body tripped the shape reported GREEN while its asserts never
executed. The harness reads ControlFlow NOW (№612): an early
termination is a LOUD failure — a green run on ≥ 0.29.0 means the body
ran to completion. -
Affected:
mlog run,mlog test, andmlog servewith
METALOGOS_SERVE_BACKEND=interpreter(the fallback backend; the serve
DEFAULT is the VM) — on every published release BEFORE 0.29.0. The VM
backend is NOT affected. The fix landed in 0.29.0 (№612, PR #1068). -
The user action: restart your existing tests on ≥ 0.29.0; re-check
the batch insert/update loops and the guard patterns with audit loops
written before 0.29.0. The fix/audit date: 2026-10-06/07.
0.28.2
Security (UPDATE REQUIRED)
0.28.1 and earlier are affected on BOTH backends — update required. The В31 corrective wave (the unified audit of 25b375e / v0.28.1, the dispatch gh#1052) found the audit §3 Y-1 regression (High): №584 retired the RESPOND_NOT_TERMINAL gate for ALL bare-respond forms at once — including the ONE form that never worked. The audit's guard scenario:
a guard
if is_admin(...) == false { respond("403") }nested inside another block-formif—db_execute("DELETE …")executed for a NON-administrator, both backends answered200 purged; the compiler gave only a style warning, and the honest limitations.md row did not reach the user.
- Y-1 (High) — the swallowed respond regression (№600, gh#1041, PR #1054):* a bare
respond*/respond_html*call carried by a statement NESTED under a top-level block-formif/elsebranch (depth ≥ 2) does NOT stop the route on EITHER backend — the serve lane's per-statement branch walk discards nested-statement responses and the branch continues; №584 had lowered the whole early-answer surface at once and this form lost the fail-closed refusal it had under №581. Fixed: the blockingRESPOND_SWALLOWEDsemantic error refuses every such program at startup on BOTH backends again (fail-closed, the №581 posture; the SSOTRespondPositionpredicate is shared by the semantic walk and the №584 lowering — the compiler stays the reference enumerator). The migration is one word — writereturn respond(...)— the same shape the advisory already recommends. The fix/audit date: 2026-10-05/06. - The repair rider (the honest-boundary finding of the №601-mandated test, PR #1054): the TW serve lane's top-level branch walk discarded an explicit
return respond(...)carried by a NESTED statement (the ControlFlow::Return signal was flattened into a plain value and dropped) while the VM answered it — the migration path above was silently broken on the TW since №584. Fixed in the same PR: the branch walk now propagates ONLY the explicit Return signal; plain tail values stay discarded (the etalon; the depth-≥2 semantics question itself is the owner gate №603). - The test contract rewire (№601, gh#1042, PR #1054): the depth-2 swallow shape now expects the startup refusal with the stable
[RESPOND_SWALLOWED]code; the audit §3 guard scenario is pinned as a negative test plus its one-word migration end-to-end on both backends (403 to the non-admin BEFORE the protected code, 200 + the marker to the admin); the №584 early-answer surfaces stay green; the predicate is mutation-verified at both transition points.
The release-block label discipline (ADR-0179 §4) held until this live tag: fact_open_high_server 0 → 1 (the №600 carrier) → 0 (the release naryad №602's closure on the tag).
The wave records riding this release (the В29 tail + В30, verbatim) are in the CHANGELOG [0.28.2] section — the branch-protection audit job (№586), the wave-30 Камертон enrichment (№590–№596), the post-wave docs syncs.
The assets (attached by the release pipeline, release: published → the tagged commit is built):
mlog-linux-x86_64— the release binary (cargo build --release --locked -p metalogos-server --bin mlog; the exact toolchain is recorded in BUILD-INFO.txt).mlog-linux-x86_64.cdx.json— CycloneDX SBOM describing exactly that binary.SHA256SUMS— digests of the binary, the SBOM and BUILD-INFO.- Two Sigstore attestations via GitHub artifact attestations: the SLSA build provenance and the SBOM attestation bound to the same binary subject.
Built from the tag v0.28.2 (c694b6d), the version lockstep 0.28.2 in the workspace manifests (№602, gh#1043, PR #1055 + the lockstep PR #1058).
The post-publication fact sync: the release-block carrier (#1041) closed on this live tag per its own condition; fact_open_high_server 1 → 0 in both goals files and the REALITY §6.17 release state → PUBLISHED — the syncing PR #1059 (merged 2026-10-06; ADR-0181 §4.1 step 1 — the syncing PR linked from the release notes).
0.28.1
Security (UPDATE REQUIRED)
0.28.0 and earlier are affected on the VM backend (mlog serve runs the bytecode VM by default since 2026-09-21) — update required. The consolidated audit d63cc1d (2026-10-04) found two server-path divergences; both were honestly named in the b678def commit body (№574.1) before the fix existed, and both are closed by the wave-29 security wave (the dispatch gh#1004; the wave-29 rule: the gates read the truth — fact_open_high_server 0 → 2 → 1 → 0).
- X-1 (High) — the guard bypass (№581, gh#995, PR #1007): a bare
respond*/respond_html*call in a NON-tail position of a route body did NOT stop route execution on the VM backend — the TW answered immediately from any block, the VM compiled the call withInstruction::Pop, so the guard patternif is_admin(...) == false { respond("403", "forbidden") }silently FELL THROUGH to the protected code (an unauthorized caller reached the protected handler). Fixed: theRESPOND_NOT_TERMINALsemantic gate refuses every such program at startup on BOTH backends (fail-closed); the migration is one word —return respond(...). The fix/audit date: 2026-10-04. - X-2 (Medium) — the leftover-local data leak (№582, gh#996, PR #1008): a route/branch body whose final statement is a
let/assignment made the VM's route epilogue pop the LAST LOCAL SLOT and serialize it as the response body (route { let row = query_row(...) }returned the row to the caller; the interpreter answered the plain200 OK). Fixed: thePushUnitroute-epilogue invariant — the body exit never reads a local slot; the fall-through answers the same shared default as the interpreter lane. The fix/audit date: 2026-10-04.
The honest-boundary trail: b678def (№574.1) named both divergences in the commit body; the wave-29 dispatch (gh#1004) made the gates read that truth; the release-block label discipline (ADR-0179 §4) held until this live tag. The stage-2 parity step (the VM serves respond in ANY position — №584) and the route-body differential-fuzzer lane (№585) follow in the 0.29 cycle.
Assets and provenance
mlog-linux-x86_64— the release binary (cargo build --release --locked -p metalogos-server --bin mlog; the exact toolchain is recorded in BUILD-INFO.txt).mlog-linux-x86_64.cdx.json— CycloneDX SBOM describing exactly that binary.SHA256SUMS— digests of the binary, the SBOM and BUILD-INFO.- Two Sigstore attestations via GitHub artifact attestations: the SLSA build provenance and the SBOM attestation bound to the same binary subject.
Built from the tag v0.28.1 (d5af542), the version lockstep 0.28.1 in the workspace manifests (№583, gh#997, PR #1010).
The post-publication fact sync: the release-block carrier (#997) closed on this live tag per its own condition; fact_open_high_server 1 → 0 in both goals files, the [0.28.1] CHANGELOG date, the REALITY §6.14 release state — the syncing PR #1024 (merged 2026-10-05; ADR-0181 §4.1 step 1 — the syncing PR linked from the release notes).
0.28.0
Security
The audit-driven security release: the v0.27.1 tag (2026-09-29)
predates every fix below. v0.27.1 and earlier are affected — update is
mandatory.
- №523 (gh#832; the consolidated audit A+D 30.09, N-1, High):
semantic findings now BLOCKmlog runandmlog serve— the
liar-string security-check bypass is closed. v0.27.1 blocked only two
message-substring classes (contains("distill_to"),
contains("[DENY_")),mlog serveran NO semantic pass at all, and
an unknown function returned the STRING
"[ERROR: unknown function 'x']"as the call's VALUE — a non-empty
string is truthy inis_truthy, soif is_admn(user)with
is_admnundefined took the then-branch: the security check became
its own bypass (a typo in a function name was enough). Now every
semantic::check_programerror blocks the run path and the serve
startup (exemptions classify ONLY by the structured error kind in
ONE explicit place — never by message substring), the unknown
function is the coded[UNDEFINED_FUNCTION]compile error, and the
refusal carries the stable code at position 0. The tree-walking
runtime was the affected path; the VM backend's compile stage
already refused the class — the exploit shape is now refused on BOTH
backends (pinned intests/naryad_523_semantic_gate.rs). - №526 (gh#835; the consolidated audit A+D 30.09, N-2): the GDPR
Art. 17 erasure path for voiceprints LANDS —voice_delete(handle)
(idempotent; the persisted twin runs the SECURE-DELETE path: the
ciphertext blob is zero-overwritten in place BEFORE the row removal)
andvoice_list()(the informed-deletion basis; the listing never
decrypts, the embedding bytes never enter any result). Neither is
feature-gated — the erasure right cannot depend on a build flag.
v0.27.1 and earlier had ONLY insert/replace: a persisted print
outlived every consent revocation ("Delete path: ABSENT",
privacy.md §2.1). - №527 (gh#836; the consolidated audit 30.09, N-3): the voiceprint
ciphertext is BOUND TO ITS SUBJECT — the AES-256-GCM AAD carries the
(subject_id, registry, schema version) triplet, so a blob
transplanted onto another subject's row FAILS authentication (the
swap attack the bare tag accepted is closed), and the decoded key
buffer lives underZeroizing. The transitional empty-AAD read
announced by №527 existed ONLY on main — its deadline is honored IN
THIS RELEASE: the fallback is removed, a legacy row refuses with
[VOICEPRINT_DECRYPT], the only path back is re-enroll/re-save. No
released version ever wrote a legacy row (the GCM store itself ships
in 0.28.0). - №517 (gh#801) + №512 (gh#796; the consolidated audit 28.09
C-06): the honest at-rest crypto for voiceprints — real
AES-256-GCM with a fresh random 96-bit nonce per write, the key
ONLY through the secret()-gate semantics (env-sourced hex-256,
never derived from the name), a keyless save fails closed
([VOICE_INSECURE_STORE]); №512 removed the FAKE crypto claims
first (the pre-№517 store's schema said AES-256-GCM while the bytes
were name-XOR plaintext). v0.27.1 and earlier persisted voiceprints
without honest at-rest encryption.
Changed and fixed
-
Release supply-chain assets (CI infrastructure; no language or runtime
change): the new.github/workflows/release.ymlruns after the owner
publishes a GitHub Release and attaches themlogbinary, a CycloneDX 1.5
SBOM that describes exactly that binary (cargo-cyclonedx 0.5.9, pinned),
BUILD-INFO.txt(exact toolchain) andSHA256SUMS, plus two Sigstore-signed
attestations (SLSA build provenance
and the SBOM bound to the binary). Until now releases were a tag and notes
with no assets. Honest limits: reproducibility not claimed or verified, glibc of the
pinnedubuntu-24.04runner, no human-held key; releases up to v0.27.1 have
no assets.
Verification recipe:SECURITY.md→ "Verifying release artifacts";
operator step:docs/release-checklist.mdstep 6. -
The №527 deadline honored in the release prep (issue #836 — the
limitations.md TRANSITION row, closed in this PR per the №524 rule):
the transitional empty-AAD read (the second decrypt attempt) is
REMOVED — a legacy №517-era voiceprint row refuses with
[VOICEPRINT_DECRYPT](the message names the legacy possibility and
the path back: re-enroll/re-save).VoiceprintCryptoStatusloses
theLegacyNoAadvariant (no producer remains); the
status-returning load keeps its signature (AadBoundfor every
readable row). Tests:tests/naryad_527_voice_aad.rs— T2 (the
legacy row refuses after the deadline), T3 (re-enroll/re-save is
the ONLY path back; the rebound row refuses the swap again), T4
(the single-attempt wrong-key refusal); T1/T5 unchanged. No released
version ever wrote a legacy row — the transitional population was
main-only. -
Naryad №550 (issue #911; Wave 25 P0, the release prep): the
unfreeze-gate DEFAULT target is 0.28 — a bareunfreeze_gate.pyrun
now checks the 0.28 ABSOLUTE goals (ADR-0179 §5, the v2 gate)
instead of the legacy §4-only reading; the legacy reading stays
available explicitly (--gate-target legacy). The workspace version
is 0.28.0; the README version badge is re-rendered from Cargo.toml
(the №497 badge-sync gate). -
Naryad №551 (issue #912; Wave 25 P0; the audit 02.10 M-1 + §6.2): the
"merge without CI" class closes on both halves. The POLICY half:
docs/maintainers.md "When CI is down" — the verbatim audit rule ("No
merges while GitHub Actions is degraded. A merge without a green
required-check run is a process violation, not a judgement call"),
the unverified-≠-green posture ("no red X" is not "green" — the dead
event delivery of 30.09 looked exactly like that), and the
branch-protection CHECKLIST for the owner (require PR + the required
set fact-checked against the ci.yml display names + up-to-date +
no-bypass; applying it is an admin action the executor cannot
perform). The MACHINE half: scripts/ci/merge_ci_audit.py + the
scheduledmerge-ci-auditworkflow (Mondays 05:00 UTC) — every main
merge of the last 7 days is re-checked against a GREEN run of the
required set on its PR's head SHA; a skipped check is not green, an
unfetchable PR head is a divergence (fail-closed: unknown is never
green), a divergence = a red run + an automatically filed issue with
the list; the script only reports (no merge/revert/history edits).
The first scheduled run is EXPECTED to flag the documented 30.09
outage merges — the issue asks for verdicts, not for panic. Pinned by
tests/naryad_551_merge_ci_audit.rs (the fixture self-test driver +
the required-set sync between the script, the test and the
checklist). -
Naryad №566 (issue #928; Wave 25 P2 tail; the audit 02.10 §7.2 W-4):
the grantors' digest stops lagging the machine metrics —
docs/PLAN-SUMMARY.md's CURRENT numbers are GENERATED by
scripts/gen_metrics.py (the same sources as the README metrics block:
one SSOT, one process computes both, they cannot disagree). The
audit's exact finding is fixed first: the digest's wave-24 section
claimed "now 2110 bp" while the live floor is 3195 bp (162/507 =
31.95%, precise 83/507 = 16.37% — the №560 second share side by
side); the hand-written current number is GONE, the generated block
(between explicit markers, the №460 posture) carries the floor and
the registry size instead — the narrative stays hand-written (the
№566 boundary: the digest's prose is the publisher's voice; the
historical wave-23 snapshot "floor 998→2110 bp" is a frozen fact and
stays).gen_metrics.py --checknow verifies BOTH targets and the
blocking metrics job runs it — a future drift is CI-red by
construction. Pinned by tests/naryad_566_plan_summary_numbers.rs
(3 tests): the digest block == an INDEPENDENT recomputation from
registry.rs (the exact type_signature_share.py regex contract),
the generator --check green right now, the stale "now 2110 bp"
claim must not return. -
Naryad №565 (issue #927; Wave 25 P2 tail; the audit 02.10 §7.2 W-3):
the TWO-ARGUMENTrespond_html(a, b)form is deprecated — its sense
depends on the CONTENT of the first argument (a first token 100..=599
reads as an HTTP status, anything else as a document title: the
audit'srespond_html("200 причин выбрать нас", body)served HTTP 200
and silently dropped the title; a DB- or LLM-sourced string would
decide the response's meaning). THE EXPLICIT FORMS LAND:
respond_html_status(status, body)— the body verbatim at the given
status; the status must open with an HTTP code in 100..=599 or be a
whole number 100..=599, otherwise a LOUD argument error (no legacy
silent-200 fallback — the №514 rule);respond_html_doc(title, body)— the title is never read as a status and never dropped, the
same full-document shape as the office form (№892). Both serve
Content-Type: text/html; charset=utf-8, both are typed in the
registry ("Struct" — the HttpResponse shape), classified as
Sink/Public/Irreversible, and ride EVERY HTML egress gate exactly
likerespond_html(LLM-output, open-redirect, secret-leak,
recall-taint — the explicit name is not a bypass). The deprecation
carries a WARN-ONLY semantic pass (the №474 posture): every 2-arg
call whose first argument is NOT a string literal is announced with
both migration paths; a literal first argument keeps working
silently this release; removal no earlier than one release with the
warning. The 1-arg form is the UNCHANGED SSOT contract (gh#899).
The floors moved WITH the rows that earned them (the №757
procedure): the general typed floor 3195 → 3241 bp (165/509), the
precise floor 1637 → 1650 bp (84/509 — query_param ships typed
"String", both paths verified), the in-tree compiled lock
158 → 161. Pinned by tests/naryad_565_respond_html_forms.rs (13):
the explicit forms on BOTH backends (T...
0.27.1
Security
The audit-driven security release: the v0.27.0 tag predates every fix
below. 0.27.0 is affected — update is mandatory.
- №510 (gh#794; the consolidated audit 28.09 C-01, High):
!=in
rule conditions,flowbranch conditions andmutate … rollback_if
worked as==on the VM (the compiler wildcard arms) — and the rule
path could not even parse (the grammar deadlock: every comparison
operator, not just!=, failed to parse in rule conditions); the VM
rollback kept-table disagreed with the TW for Gt/Ge/Eq/Ne. 0.27.0
is affected — update is mandatory. - №474 (gh#722; the audit 26.09 §3.2, High): the db parity —
query_row_vmparameters route through the same parameterization as
the TW lane (convert_params), closing the silent parameter shift. - №475 (gh#723; the audit 26.09 §3.1, High): the fs_gate facade —
one gated FS entry point + theclippy.tomldisallowed-methods
ratchet; the pdf/smtp/config bypass paths are gone. - №477 (gh#725; the audit v0.26.1 §3.5): the
mcp-servecontext
inversion — the strict-by-default exec context at the entrypoint. - №478 (gh#726; the audit v0.26.1 §3.6 + the 27.09 action): the mock
unification onto the 454 SSOT + the loudenv/read_filewith the
explicit-silence*_ornaming. - Also landed since v0.27.0: №481 (the loud env refusal), №482 (the
unfreeze-gate CI job), №483 (the media/vision dedup ratchet at zero).
Changed and fixed
-
Naryad №510 (issue #794; the consolidated audit 28.09 C-01, Wave 21
P0): the!=operator is honest end-to-end. THREE defects, one
class — comparison operators silently degrading, each invisible to
the gates for its own reason. (1) The compiler wildcard:AstCompareOp
match arms inmutate rollback_if, theflowbranch conditions and
compile_ruleended with_ => ConditionOp::Eq // Ne and others fall back to Eq—CompareOphas exactly six variants, so the wildcard
hid exactlyNe:rollback_if: accuracy != 0.9and flow branches
cold(x.status != "b")compiled asEqon the VM backend. All three
arms are explicit now, and#![deny(clippy::wildcard_enum_match_arm)]
stands at the top ofsrc/compiler.rs— the lint surfaced 16
wildcard sites in the file (rules, branches, rollback, BinOp
fall-through, Statement loops, Expr simplifications, the JIT purity
disallow-mirror); every one is an explicit variant list, so a new
enum variant is a compile error until it is consciously classified.
(2) The grammar deadlock the compiler fix exposed: the
compare_conditionrule (expression ~ compare_op ~ expression)
could NEVER match — the greedy leftexpressionlayers comparisons
as infix (compare_expr), consumed the wholea != band left no
compare_op for the condition.rule If(...)with ANY comparison
operator (not just!=:>,>=,<,<=,==too) failed to
PARSE — onlycontainsworked; the consolidated audit's premise
("rules accept!=") was lexically true and structurally dead, so
the compiler wildcard was unreachable dead code on the rule path.
The parser now accepts an expression-shaped condition and decomposes
its top-level comparison intoCondition::Compare; anything else at
the top level is a loud parse error. The documented syntax
(rule If(target.field op value)) now matches reality. (3) The VM's
mutate kept-table disagreed with the TW interpreter for every
operator except Lt/Le: Gt/Ge ALWAYS rolled back, Eq was inverted,
Ne was missing (the always-keep default). The table now mirrors
interpreter/hooks.rs exactly (kept = NOT(accuracy OP threshold)).
The diff-fuzzer №465 generated zero rules — the seeded rule block
(all six operators, deterministic per seed) now rides the program
output through the flow head, so the class stays visible to CI
forever. Verified: the №510 contracts C1–C10 green on BOTH backends
(rule fire/silence, branch taken/not-taken loud consistency,
rollback kept/rolled-back across!=/>/==); the fuzzer suite
green with the rule generation; clippy-D warningsclean. -
Naryad №505 (issue #788; the audit 28.09 §3.5): the naryad number
uniqueness gate —scripts/ci/naryad_number_check.py+ the
Naryad numbering (blocking)CI job next to the adr-check. Under
one number different works have already lived (№474, №475, №476,
№493 — the external audit stumbled on it twice): the gate parses
the PR title's naryad claim and refuses a number that already
landed as a DIFFERENT work, listing the occupied works. The
exception grammar is explicit and pinned by the script's self-test:
the work-group suffix (№466 group M), the dotted re-issue
(№475.1), the re-land suffixes (reland/retry/revert/rerun/redo/
take-N) and the same-issue follow-up (the occupied record pointing
at the sameissue #M). The occupied set comes from the merged
git history (the ENnaryad Nand RUНаряд №Nsubject forms) and
the closed issue titles via GH_TOKEN (best effort — the history
alone suffices). The four known collisions are documented in the
naryad's report so external links stop confusing them. The script
does not rewrite history and does not rename old naryads. -
Naryad №495 (issue #780; the audit 28.09 §3.1 High, the P0 core of
Wave 19): distillation inmlog servenow WORKS on both backends —
the new server-levelDistillHub(src/distill_hub.rs). Before
this, the per-request fresh program context destroyed every
recorded example with its request on the TW lane and the VM pool
resetdistill_stateson every check-in, so the training threshold
max(distill_after, 10)was unreachable in serve by construction,
silently, with no error and no audit event. The hub holds the
examples, the TEACHING/DISTILLED modes, the trained-model registry,
the in-flight flags and ONE background training thread per process
(the №489 posture brought to serve — the VM lane stops training
synchronously in-request); both backends call it through the
DistillAccesstrait (theDbAccesssample, №474/№484), which also
collapses the TW/VM distill mirrors —simple_embeddingnow has
ONE canonical copy (byte-identical by construction, the №502 mirror
count moves down). Examples persist in the SQLite table
distill_samples(the №166 plan shape, with thesource
provenance column) in the server's memory-persist file; weights keep
persisting throughreflex_save. Migration: existing saved weights
read as before; the example ledger starts accumulating from zero
(stated honestly here — no backfill exists). Fix-in-shared-point
corollaries the work surfaced: the TW route body executor now builds
its context through the SAMEfresh_program_contextthe ticks use
(the hand-rolled copy had already drifted), andload_program
registers the learnable table from the program's declarations —
before it, a VM route body calling a learnable pattern failed loud
with "learnable index N not found" (main_code never runs on serve);
the №496 e2e (the audit's headline recommendation) drove both
findings out. Verified: the e2e green on BOTH serve backends
(TEACHING accumulation → the audit eventsdistill.training-*→
DISTILLED answers with the LLM counter frozen → the accumulated
examples survive a server RESTART); both rollback mutations (hub
attach off, VM injection off) turn the e2e red; lib 872/0; clippy
-D warningsclean. -
Naryad №500 (issue #783; the audit 28.09 §3.2, the hardening core
of Wave 19): the third-party path-APIs no longer bypass the №475
filesystem facade. Every pdf load/save site (13 across
pdf_fill_form,pdf_rotate_page,pdf_delete_pages,
pdf_extract_images,pdf_merge,pdf_split,pdf_metadata,
pdf_set_metadata) routes throughcrate::fs_gate: reads go
read_bytes(path, purpose)→LopdfDocument::load_mem, writes go
save_to(&mut Vec)→write_bytes(output_path, purpose)— the
library's own file open (invisible to any lint) is gone, and the
audit's three vectors are dead: overwritingapp.mlog(the hard
write deny), writing/home/app/.ssh/authorized_keys(the sandbox
absolute-path refusal), reading/srv/other-tenant/contract.pdf
(the read gate). The №500 ratchet extendsclippy.toml
disallowed-methods to the library path-APIs themselves
(lopdf::Document::load/save,rusqlite::Connection::open,
image::open,std::fs::copy/rename/remove_dir_all/ create_dir_all) — the explicitly-justified service sites (the
weights store, the memory/KG/checkpoint journals, the reminder db,
the doctest harness, vector.rs's post-sandbox open) carry named
#[allow]s; the pdf output dirs create through the new
fs_gate::create_dir_all(the full write-gate vocabulary on a
DIRECTORY path). The write-side canonical re-check mirrors the read
side's documented one: a symlink named innocently but pointing at
the application image is refused BY THE POLICY (naming the swap)
before the OS's O_NOFOLLOW does — pinned by the blocking
reproductions (7 tests: the three vectors, the..traversal, the
symlink swap, the deny-list read, and the legit
pdf_set_metadataround-trip proving honest pdf scenarios
unchanged). Verified: grep of direct lopdf path-APIs in pdf.rs = 0;
mutation checks (raw-write injection → the vectors turn red);
lib 872/0; clippy-D warningswith the extended list clean. -
Naryad №506 (issue #789; the audit 28.09 immediate item):
docs/limitations.mdgains the three honest rows the audit asked
for, before the 0.27.1 tag. (1) Distillation inmlog servedoes
NOT accumulate examples between requests — the per-request fresh
program context resets the TEACHING state,distill_afteris never
reached across requests (minimal ClassifyIntent example; repair:
№495). (2) The two pinned ok↔err outcome divergences are
user-visible as differentmlog runvsmlog servebehavior —
`m...
v0.27.0 — the unfreeze release (the owner lift, gh#680)
The unfreeze release — the ADR-0177 domain freeze is lifted by the owner's explicit decision.
The freeze window (ADR-0177 §6) closes with this release. The lift is recorded by the owner in the gate thread gh#680 (the lift record comment 5851623870, 2026-09-27; the office-channel trace_id 1a0e06d976daf0c9); the ADR Status is updated by a naryad, not silently (№493, PR #752). All four §4 criteria carried their evidence at the lift (REALITY §6.12): the enum Type stages 0 and 1 (№467, №474), the TW/VM dedup 20/20 — one-way down from 60 (№462, №466), the debt gate 85/49/37, exit 0 (№468), and the FO-056 memory office E2E on 0.26.1 — 16/16 + 3/3 (№475).
What opens with the unfreeze:
- the media/vision dedup residue (the 20 names) and the physical crate split move forward per the №472 roadmap (docs/refactoring-split-plan.md);
- the domain work resumes under the standing strategic discipline — the gates and the ratchets stay, the freeze does not.
The release naryad: №494 (issue #753, PR #754, squash 0d1cce9). The previous release: v0.26.2 — Wave 18, the unfreeze path (the §4 criteria finish).
Full Changelog: https://github.com/ShkodnikAI/Metalogos-/blob/main/CHANGELOG.md
v0.26.2 — Wave 18: the unfreeze path (the ADR-0177 §4 criteria finish)
The Wave 18 release — the unfreeze path. The closing docs release
(naryad #476, issue #744, strictly last; PR #750).
- The №466 dedup transfer completes (the groups 5–7 of the dispatch
#745): the audit-ledger five, the recipe pair and the server/runtime
eight leave both backends into the shared live modules
(src/audit_ops.rs,src/recipe_ops.rs,src/runtime_ops.rs); the
№462 counter moves 35 → 20 (60 at the start) — the remaining 20
names are exactly the media/vision cluster riding the 0.27 crate
split. Every transfer PR carried the №465 diff-fuzzer before/after
proof (the divergence classes unchanged). - The enum Type stage 1 lands (naryad #474, PR #749): the let-type
inference, WARN-ONLY (the №467 canon) — the[stage1 types]
diagnostics inmlog check; no program is rejected; the runtime is
unchanged. - The FO-056 evidence refresh (naryad #475): the memory-office E2E
dogfood re-run on 0.26.1 — green (16/16 + pytest 3/3), recorded in the
gate thread gh#680.
The ADR-0177 §4 unfreeze criteria all read GREEN with the evidence
pointers (REALITY §6.12). The LIFT belongs to the owner alone (recorded
in gh#680). 0.27.0 = the release of freeze-lifting — the tag is NOT
published until that explicit lift.
Docs: REALITY §6.12, PLAN-SUMMARY (the Wave 18 row), CHANGELOG 0.26.2.