Skip to content

Phase 1 / Etch / Entity-scoped events and entity-bound async rules#43

Merged
guysenpai merged 24 commits into
mainfrom
phase-1/etch/entity-scoped-events
Jul 5, 2026
Merged

Phase 1 / Etch / Entity-scoped events and entity-bound async rules#43
guysenpai merged 24 commits into
mainfrom
phase-1/etch/entity-scoped-events

Conversation

@guysenpai

Copy link
Copy Markdown
Contributor

Closes milestone M1.0.14 — Entity-scoped events and entity-bound async rules. Brief: briefs/M1.0.14-entity-scoped-events.md (Status: CLOSED).

Five review gates (E1→E5), each GO'd on the pushed diff. Round-trips recorded: one FROZEN-scope amendment at E2 (5589078 — entity-bound async rule execution absorbed); one E3 STOP (ruling renumber to the brief's order); two E4 STOP fixes (string use-after-free stabilization — .string_run, then borrowed .string_persistent).

Closing notes (from the brief)

  • What worked: entity_event realized as global_event + predicates over the SAME per-tick EventStore and the M1.0.11/12 wake/task substrate — no parallel mechanism. The single shared designated-field policy (AstArena.resolveEventEntityTarget) let the type-checker (diagnose) and the interpreter (capture) agree with zero interp→types dependency. Reusing the synchronous selection walk in collect mode (a threaded nullable buffer) gave entity-bound task spawning identical selection order + when semantics for free (ruling 4). Byte-identical wake ticks preserved — every pre-existing M1.0.11/12/13 async + timer test passes UNMODIFIED.
  • What deviated from the original spec: entity-bound async rule execution was absorbed at the E2 STOP round-trip (FROZEN amendment, 5589078) — an end-to-end Etch entity_event awaiter is inexpressible without an entity-scoped awaiter, and no parameterless body expression yields an Entity. Ruling numbering follows the BRIEF (1 = re-arm, 2 = when-gates-spawn) vs spec §9.2's inverted 1/2 (E3 STOP renumber — comment/test-name only, no logic change).
  • What to flag explicitly in review: the two E4 string-stabilization fixes closed a PRE-EXISTING (not milestone-introduced) use-after-free — an @on_event observer reading a computed (.string_run) or borrowed-resource (.string_persistent) event field dangled before this milestone; the deep-copy at both escape boundaries (emit → per-tick store-owned; filter capture → husk buffer, gated by stringNeedsOwning) fixes it and enforces capture-once for string filters. The matched event is NOT consumed (multi-awaiter + observer coexistence — E5 tests 1–2). A post-close drift audit (10 adversarial verifiers over CLAUDE.md + brief vs code) confirmed 8/10 claims verbatim and surfaced one stale runAsyncRule header comment (fixed here, docs(etch) — behavior already correct) plus one wording imprecision in log entry :160 (corrected); the other flagged item (E4 fixes not in Recorded deviations) is by-design — that section is scoped to FROZEN-SECTION changes, and the E4 fixes are bug fixes journalled in the Execution log.
  • Final measurements: no benchmark gate (brief §Benchmarks: None). The event-wake scan is the existing per-tick linear predicate scan (same complexity as the pre-existing global_event path; no entity index added). Test count 975 → 995 passing (+20 tests across E1–E5); full suite 995/1012 pass, 0 failed, 17 pre-existing skips, 255/255 build steps; zig fmt --check + zig build lint green; pre-push gate (debug + ReleaseSafe, ×2) green.
  • Residual risks / tech debt left intentionally: future/Future<T> is the sole remaining fail-loud await target (the last await gap). Event matching is a linear per-tick scan — an entity-keyed event index is deferred to the Phase-2 typed bus. Cancel-on-despawn deferred (Phase 2+): a task whose entity despawns mid-suspension fails loud on the next dead-handle access (ruling 3). Implicit event binding / payload delivery at resume not implemented (unit is normative, §9.4). Non-equality filter predicates, emit-side targeting, and dt/non-entity rule-parameter injection are out of grammar/scope for EBNF v0.6.

Validation checklist (Étape 4)

  • All Scope deliverables present (E1 parser/AST filters; E2 type-check + E0908/E0909 + shared designated-field helper; E3 entity-bound async rules + four rulings; E4 entity_event/filter runtime + capture-once + string stabilization; E5 integration) — gate-by-gate GO on the pushed diffs
  • No drift into Out of scope (future still fail-loud; equality-only filters; unit at resume — no implicit event binding; no entity index; no cancel-on-despawn; no dt/non-entity param injection; emit-side targeting untouched)
  • All Acceptance-criteria tests green in debug and ReleaseSafe (995/1012 pass, 0 failed, 17 pre-existing skips)
  • Benchmarks: none required
  • Observable behavior demonstrable (E5 §9.2 mirror: an entity-bound awaiter emits DialogueStart, suspends on await entity_event(e, DialogueComplete), resumes on its OWN DialogueComplete (a decoy entity's DC coexists without waking it), emits QuestStarted, observed via an @on_event sink)
  • zig build, zig build test (debug + ReleaseSafe), zig fmt --check, zig build lint green
  • CLAUDE.md §3.4 updated and committed on the branch (docs(claude-md): update for M1.0.14)
  • Language audit (§3.6.1) passed — branch diff + brief free of French prose (the sole French is the FROZEN protocol block: process step labels + the mandated signal string, Guy-authored)
  • Local drift audit (§3.6.1) passed — 10 adversarial verifiers; 8/10 CONFIRMED verbatim; one stale runAsyncRule header comment fixed (docs(etch)), one log-entry :160 wording imprecision corrected; the Recorded-deviations flag is by-design (FROZEN-SECTION scope)
  • Closing notes filled
  • Brief Status: CLOSED, Closed: 2026-07-05
  • Final commit docs(brief): close M1.0.14

For Guy: merge (squash-and-merge, one commit on main) and the tag v0.10.14-entity-scoped-events are yours — CC performs neither. The squash message + tag annotation content come from Claude.ai at closure (per protocol), as does any etch-reference-part1.md §9.2/§9.4/§9.12 KB re-upload. One deviation to confirm: the CLAUDE.md patch's item 4 said "append after the M1.0.12 scope-boundary entry," but an M1.0.13 open-decisions entry already exists after M1.0.12 — I placed the new M1.0.14 entry after M1.0.13 (chronological end of list) to avoid an out-of-order list. Move it up one slot if you intended it literally between M1.0.12 and M1.0.13.

🤖 Generated with Claude Code

guysenpai added 24 commits July 4, 2026 16:26
@guysenpai guysenpai merged commit fd6bdbc into main Jul 5, 2026
10 checks passed
@guysenpai guysenpai deleted the phase-1/etch/entity-scoped-events branch July 5, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant