ChatGPT Plus aboneliği Malta vatandaşlarına ücretsiz oldu
Tarihte ilk kez bir ülkenin tüm vatandaşlarına ücretsiz olarak ChatGPT aboneliği sunuluyor. Malta ve OpenAI iş birliği ile tüm ülke yapay zekânın nimetlerinden faydalanacak.
Tarihte ilk kez bir ülkenin tüm vatandaşlarına ücretsiz olarak ChatGPT aboneliği sunuluyor. Malta ve OpenAI iş birliği ile tüm ülke yapay zekânın nimetlerinden faydalanacak.打开vscode打开 vscode
1.按 Cmd + Shift + P
2.输入
Shell Command: Install 'code' command in PATH
A free, self-paced track that walks through Dapr Workflow in a .NET Aspire solution. Runs in your browser and connects to a cloud-based sandbox, so there’s nothing to install locally.
The new learning track covers:
Start the track at Dapr University.
Scalable, Cost-Optimized Log Parsing: Building an Enterprise-Grade Backend Routing Layer for CI/CD Triage
The Problem with Brute-Force Log Parsing
Imagine hiring a brilliant, highly compensated senior engineer, but before they are allowed to fix a critical production bug, you force them to read 50,000 lines of standard system startup messages out loud. It would be an absurd waste of their time, their cognitive bandwidth, and your company’s money.
Now apply that exact same logic to AI agents in a Continuous Integration and Continuous Deployment (CI/CD) pipeline.
When a pipeline breaks, the standard, naive approach is to take the massive, multi-megabyte terminal log and dump it directly into a commercial Large Language Model (LLM) like GPT-4o or Claude 3.5 Sonnet. This brute-force method introduces severe production bottlenecks. It wastes millions of API tokens on irrelevant success messages, triggers context-window hallucinations, and racks up exorbitant cloud computation bills.
Furthermore, many enterprise organizations have strict data governance policies preventing raw operational logs—which often contain internal IP or environment variables—from leaving their secure perimeters without prior sanitization. An AI agent that bankrupts your API budget and leaks raw data isn’t a solution; it is a new operational liability.
What The Routing Layer Is

To solve this, our team engineered a high-performance, cost-optimized backend routing architecture for our CI/CD Triage Agent. It acts as a financial and cognitive firewall.
Instead of indiscriminately sending raw data to the cloud, the agent utilizes an in-process runtime intelligence layer. It intercepts the raw log, strips out the noise locally, and strictly controls which pieces of data are allowed to consume premium cloud API credits. We didn’t just build a wrapper around an LLM; we built a dynamic routing engine designed for enterprise scale.
The Architecture: From Noise to Signal

A 50,000-line CI/CD log is mostly noise. Downloading progress bars, successful unit test checks, and standard compiler warnings drown out the actual error. The stack trace of the failure is the signal.
The core insight of our backend is that a heavy, expensive cloud model should only ever see the signal. Our orchestration logic uses a tiered execution hierarchy to achieve this:
1. Isolate (Tier 1 Local Preprocessing)
When the log is ingested, it is routed through a fully local Ollama instance (running highly quantized models like Llama-3-8B). This model reads the data for free. It applies structural sanitization to strip out repetitive standard outputs, extracting only the specific anomaly where the build crashed. By keeping this phase local, we ensure zero latency in data transfer and absolute data privacy during the noisiest part of the analysis.
2. Gate (Confidence and Budget Evaluation)
Once isolated, cascadeflow evaluates the task complexity. If it is a simple syntax error (e.g., a missing parenthesis or a standard HTTP 504 Gateway Timeout), the local model generates the fix itself. If the anomaly is a deeply nested architectural issue, the system checks the defined API budget before escalating.
3. Escalate (Tier 3 Premium Routing)
Here is how these operations combine in our Python backend using Cascadeflow’s API. Notice how we establish a hard financial cap per pipeline execution:
import cascadeflow
# The core routing logic embedding the quality gate
router = cascadeflow.Router(
primary_model="ollama/llama3", # Tier 1: Free Local Compute
fallback_model="groq/llama3-70b-8192", # Tier 3: Premium Escalation
budget_cap_usd=0.01, # Strict Token Enforcement
max_retries=2
)
def triage_pipeline_crash(compressed_log_chunk):
try:
# Cascadeflow evaluates structural complexity and routes accordingly
response = router.execute(
prompt=f"Deduce the root cause of this anomaly: {compressed_log_chunk}",
confidence_threshold=0.88 # High threshold for local approval
)
return {
"diagnosis": response.text,
"tokens_saved": response.cost_metrics.tokens_saved,
"source": response.model_used
}
except cascadeflow.BudgetExceededError:
return trigger_local_fallback(compressed_log_chunk)
Resilience: The Local Fallback

An AI system that fails during a critical outage is a massive liability. Our routing layer addresses this with a dual-mode fallback architecture.
During an active incident, if the Groq cloud endpoint is unreachable due to a network partition, or if the API budget cap is explicitly hit, cascadeflow gracefully catches the exception. Instead of crashing the pipeline, it routes the compressed error chunk back to the local Ollama instance.
The local response might not be as architecturally profound as a 70-billion parameter cloud model, but it is fast, deterministic, and always available. During a production outage, a local heuristic response is infinitely better than a system hang. Our Streamlit UI explicitly flags the source as ‘local’ to maintain operational transparency with the on-call engineers.
The Deeper Idea
The engineering teams that respond best to incidents aren’t the ones throwing the most expensive AI models at the wall to see what sticks. They are the ones who control their data flow.
By separating raw data ingestion from heavy-duty logical reasoning, this backend routing engine proves that modern AI agents can scale within real enterprise constraints. We don’t just parse logs; we orchestrate intelligence efficiently. By combining localized preprocessing with premium escalation, we have reduced API token spend by over 95% while maintaining absolute system resilience.
Our fully documented codebase and routing configurations are available on our team GitHub repository:
https://github.com/Sharanya03-stack/AI-agent.git
Meta, yapay zeka odaklı bir yeniden yapılanmaya gidiyor ve bu kapsamda 7.000 çalışanın yeni yapay zeka araçları ve uygulamaları geliştirmeye odaklanan şirket içi dört yeni organizasyona kaydırılacağı aktarılıyor. Bu konuda çalışanlara resmi bildirimi geçtiği söylenen şirket, bu yeniden yapılanma ile verimi artırmayı hedefliyor. Meta’nın 20 Mayıs itibarıyla dünya genelindeki çalışanlarının yaklaşık yüzde 10’unu, yani 8.000’e yakın kişiyi işten çıkarmaya başlayacağı da aktarılıyor. CEO Mark Zuckerberg’in bunun arkasında devasa yapay zeka harcamalarının yer aldığını söylediği belirtiliyor. Zuckerberg’in bir şirket toplantısında yaptığı konuşmada maliyetlerin temel olarak bilişim altyapısı ve personel giderleri olmak üzere iki ana noktada toplandığını ifade ettiği, yapay zeka donanımlarına daha fazla yatırım yapıldıkça çalışan kadrosuna ayrılan payın azaldığını dile getirdiği söyleniyor. Zuckerberg’in ayrıca yılın ilerleyen dönemlerinde daha fazla işten çıkarmanın yapılabileceğine işaret ettiği de gelen bilgiler arasında yer alıyor.
Meta, yapay zeka odaklı bir yeniden yapılanmaya gidiyor yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.
Python has consistently ranked among the world’s most popular programming languages, and for good reason. Its clean syntax, extensive ecosystem, and broad applicability across domains from web development and data science to automation and artificial intelligence make it an exceptionally strong first language for aspiring developers.
However, getting started can feel overwhelming. The sheer volume of tutorials, courses, and conflicting advice online often leaves beginners unsure of where to focus their energy. This article cuts through that noise by providing a deliberate, structured learning path covering the foundational concepts every Python developer needs, presented in the order that makes the most sense for building lasting understanding.
Each section includes practical code examples you can run immediately. Whether you are exploring programming for the first time or transitioning from another discipline, this guide is designed to give you a clear and confident starting point.
1. Install Python
Before anything else, get Python on your machine. Head to python.org, download the latest stable version, and install it. Then write your very first program:
# Your very first Python program
print("Hello, World! I'm learning Python 🐍")
Run it. See those words appear on your screen. That’s your rite of passage in programming — welcome aboard!
Tip: Use VS Code with the Python extension. It gives you syntax highlighting, error hints, and a run button right in the editor.
2. Variables and Data Types 📦
Think of variables as labelled boxes that hold information. Python has four basic types you’ll use constantly:
# String — text
name = "Amara"
# Integer — whole number
age = 24
# Float — decimal number
height = 1.72
# Boolean — True or False
is_student = True
print(f"Hi, I'm {name} and I'm {age} years old.")
# Output: Hi, I'm Amara and I'm 24 years old.
💡 Python figures out the type automatically no need to declare
int x = 5like in Java or C. This is called dynamic typing, and it makes Python very beginner-friendly.
3. Control Flow — Making Decisions
Programs need to make decisions. That’s where if, elif, and else come in.
score = 75
if score >= 90:
print("🏆 Distinction!")
elif score >= 60:
print("✅ You passed!")
else:
print("📚 Keep studying, you've got this.")
# Output: ✅ You passed!
Notice that Python uses indentation (spaces) to define code blocks — no curly braces {} needed. This forces clean, readable code from day one.
4. Loops — Doing Things Repeatedly
Instead of writing the same line 10 times, loops do the repetition for you.
# for loop — great for going through a list
fruits = ["mango", "banana", "avocado"]
for fruit in fruits:
print(f"I love {fruit}! ")
# while loop — runs as long as a condition is True
count = 1
while count <= 3:
print(f"Count: {count}")
count += 1
Use a for loop when you know how many times to repeat. Use a while loop when you’re waiting for a condition to change.
5. Functions — Reusable Blocks of Code 🔧
Functions let you write code once and use it many times. This is one of the most important ideas in all of programming.
def greet(name, language="English"):
if language == "Swahili":
print(f"Karibu, {name}! 🇰🇪")
else:
print(f"Welcome, {name}! 👋")
greet("Brian")
greet("Amara", language="Swahili")
# Output: Welcome, Brian! 👋
# Output: Karibu, Amara! 🇰🇪
Rule of thumb: If you find yourself copy-pasting the same code more than twice, it belongs in a function.
6. Lists and Dictionaries
Python has powerful built-in ways to organize data. Two you’ll use constantly:
Lists — ordered, changeable collections:
tasks = ["learn Python", "build a project", "get a job"]
tasks. Append("celebrate 🎉")
print(tasks[0]) # learn Python
print(len(tasks)) # 4
Dictionaries — store data as key-value pairs:
user = {
"name": "Juma",
"age": 28,
"city": "Nairobi"
}
print(user["city"]) # Nairobi
user["age"] = 29 # update a value
Dictionaries are incredibly useful — you’ll see them everywhere in real Python projects.
7. Working with Files
Real programs read and write data. Python makes file handling simple and safe:
# Writing to a file
with open("notes.txt", "w") as f:
f.write("Python is awesome!n")
f.write("I'm going to build great things.n")
# Reading from a file
with open("notes.txt", "r") as f:
content = f.read()
print(content)
The
withkeyword automatically closes the file when the block ends — preventing data corruption or memory leaks. Always use it!
8. Modules and the Standard Library
Python ships with a huge collection of ready-made tools. No need to reinvent the wheel:
import random
import datetime
import math
print(random.choice(["keep going", "you're doing great", "almost there!"]))
print("Today is:", datetime.date.today())
print("√144 =", math.sqrt(144)) # 12.0
Once you’re comfortable with the basics, explore popular third-party packages using pip install:
| Package | What it does |
|---|---|
requests |
Fetch data from the web |
pandas |
Data analysis and spreadsheets |
flask |
Build simple web apps |
pygame |
Build games |
beautifulsoup4 |
Scrape websites |
🗺️ Your 7-Week Learning Roadmap
Don’t rush — spend real time on each step before moving forward.
| Week | Topic | Focus Areas |
|---|---|---|
| Week 1 | The Basics | Variables, types, print(), input(), operators |
| Week 2 | Control Flow |
if/elif/else, for loops, while loops |
| Week 3 | Functions |
def, return, parameters, scope |
| Week 4 | Data Structures | Lists, dicts, tuples, sets |
| Week 5–6 | Files & Modules | File I/O, stdlib, pip packages |
| Week 7+ | Build Something! | CLI tool, quiz app, data script — anything! |
Common Beginner Mistakes to Avoid
Indentation Error. Always use 4 spaces (or your editor will handle it).= and == — = assigns a value, == compares two values. Where to Learn More 📖
Here are some free, high-quality resources to keep you going:
Final Thoughts
The most important thing is write code every single day, even if it’s just 15 minutes. Reading tutorials is not the same as building things. Break stuff, fix it, Google the error messages, and repeat.
You don’t need to know everything before you start building. Start with something small a number guessing game, a to-do list, a weather script and grow from there.
Writing the docs is what surfaced both mistakes. There’s a meta-lesson in there about how docs are the cheapest design review you can run, but that’s another post.
This is “that other post” — and the first thing it has to do is correct the teaser. The teaser oversells. Docs caught one of the two mistakes. The other was caught by the first real consumer of the API, which I was building in parallel. The two reviews worked in tandem: docs review the shape of an API, the consumer reviews the use of it. Together they catch what tests can’t see.
If you ship anything behind an interface — a library, a CLI, any entity behind a contract — these are the two reviews you don’t want to skip.
Recap
“Three majors, two mistakes” covers the engine and the v4 pause API — onStep, onDebugBreak, per-state debug flags — in detail. I’ll lean on it here without rehashing it. The v4 → v6 trajectory shipped three breaking majors: a hook rename, a halt-semantics hardening, and a dispatch-tick collapse. The first two surfaced in the demo. The third surfaced in the docs.
The demo case: v4 → v5
While shipping v4 I started building machines-demo — an interactive Turing-machine debugger and the engine’s first non-test client.
The demo is a natural first consumer: it has a dual purpose. The product goal — public distribution and showing the engine in action. The technical goal — road-testing changes and validating concepts against a live API surface. Both goals make building the demo an integral part of the release cycle, not an optional add-on.
The demo used both hooks at once: onStep populated a per-iteration command buffer for the trace UI; onDebugBreak drove the pause/resume cycle.
The demo built. The tests passed. But it was uncomfortable to write, for two reasons.
First, onDebugBreak’s after-fire came with data from the previous yield — the same data the previous onStep had already shown. The demo processed the same thing twice, and the question “why two hooks for one event?” was being asked not by me but by the code itself, in a way. I filed turing-machine-js#109 as an RFC about the relationship between these hooks, listing four sketches; the resolution narrowed to naming. onDebugBreak framed the purpose of use as “debugging”, while the consumer’s verb was “pause”. Shameless rename, no alias. v5.
Second, the demo’s UI gained a “pause before halt” scenario — letting the user glimpse the machine’s final state before it shuts down. The natural implementation: a debug flag on haltState itself. The first test case set haltState.debug = { before: true, after: true } because the symmetry looked right. Only before fired. Worse: the after-fire on the iteration that led to halt never reached the consumer — the loop exited as soon as state.isHalt became true. turing-machine-js#108 split it in two: restore the lost after-fire (bug); throw on assignment to haltState.debug.after (API).
Both complaints came from the consumer side. The demo didn’t surface a code bug — it surfaced an API interaction format. The names didn’t fit the use. Being permissive in input didn’t match what the user was trying to do. The tests verified behavior against the engine’s own internal model — ok, green. The demo verified behavior against the consumer’s mental model — and produced two specific complaints (not ok).
The docs case: v5 → v6
v5 shipped. The README needed updating. The new dispatch-order section had to explain — in words — when each of onPause(before), onStep, and onPause(after) fired relative to the iteration they described.
The first honest paragraph went something like:
onPause(after, K)fires on iteration K+1’s yield, with the payload substituted from iteration K’s snapshot, beforeonPause(before, K+1)oronStep(K+1)fire.
I stared at that sentence for a while. There was no shorter version. The reader wasn’t supposed to need a sentence about substitution.
The code worked. The tests passed. The demo consumed the hooks correctly. The mistake wasn’t in any of those — it was in the shape of the dispatch, and that shape was only visible when you had to put it into words.
The fix collapsed the lifecycle: before(K) → step(K) → after(K) on the same yield. No substitution. No cross-iteration scheduling. No final drain for the halting iteration (in “Three majors, two mistakes” I called it “post-loop drain”; I’d probably shorten it to “final drain” now). The README paragraph now reads:
On iteration K’s yield, hooks fire in lifecycle order.
And that’s the kind of sentence the reader glides past without effort. turing-machine-js#119 shipped as v6.
A direct quote from the previous article:
The code worked, the tests passed, and the docs were correct. The shape was just wrong.
What I’d add: the docs were correct only on the condition that the reader accepted three explanatory sentences they shouldn’t have had to read. That’s not “correct docs” — that’s docs apologizing for the shape.
The docs review caught the shape, not the use. The demo worked. The tests passed. This time, only prose pressure uncovered the issues hiding in the implementation.
Three reviews, three layers
Here’s what each one checks:
— Tests verify code against itself. Internal consistency. The engine yields what the engine should yield. Green tests are the baseline.
— The first real consumer verifies code against a consumer’s mental model. Does the API format match what the user is trying to do? Reviewing real interaction with the API forced API changes: a rename and a halt-after restriction.
— The docs as *prose* (not JSDoc — a connected README narrative) verify code against the author’s own explanation. Does the design have an honest one-paragraph description? Without one, we had to look hard at the substitution dance — and abandon it.
Each review catches what the layer below misses. Tests don’t catch shape; the consumer doesn’t catch interaction problems it can skillfully bypass; the docs don’t catch UX wrinkles they don’t have to mention.
The cost is asymmetric. Building a real consumer is the most expensive of the three reviews; writing the docs is the cheapest. Still, even the most expensive costs less than reworking the API after the problems surface for users — hence “cheap” in the title. The real consumer has to be built before the major cut — it’s the only review that makes the API comfortable to use. Docs then paper over what’s left.
Heuristics for the next major
Three majors. One README. One demo. The tests had nothing to say.
Code: turing-machine-js (engine) and machines-demo (the first non-test consumer where v4 → v5 surfaced).
I spent the last two weeks shipping four breaking major versions of @turing-machine-js/machine — v3, v4, v5, v6 — and the most interesting part wasn’t any single feature. It was watching the same API surface (a pause/breakpoint hook on the run loop) get redesigned twice in three versions, each time because the previous shape exposed something it shouldn’t have.
This post is the post-mortem. If you’re designing pause/step/breakpoint APIs for a generator-loop interpreter, scheduler, or DSL runtime, the mistakes I made are easy to make and worth seeing in someone else’s code first.
The setup
The engine runs a Turing machine. Internally it’s a generator: each yield corresponds to one step (one transition firing on one tape symbol). The driver loop looks roughly like this:
async function run({ initialState, onStep }) {
for (const m of runStepByStep({ initialState })) {
await onStep?.(m);
if (m.state.isHalt) return;
}
}
MachineState (m) is a snapshot per iteration: the state about to execute, the current and next symbols, the moves the heads will make. A consumer (a logger, a UI, a test) can hang off onStep to observe.
What I wanted to add in v4 was a way to pause execution at chosen points — like a breakpoint in a debugger. Any State should be able to carry a debug config:
myState.debug = { before: [symA], after: [symA] };
…and the run loop should give the consumer a chance to inspect the machine and decide when to resume.
v4: ship it
The v4 shape was straightforward. I made run() async, added an onDebugBreak hook, and routed it like this:
await machine.run({
initialState,
onStep: (m) => { /* every step */ },
onDebugBreak: async (m) => {
if (m.debugBreak?.before) console.log('before:', m.state.name);
if (m.debugBreak?.after) console.log('after:', m.state.name);
// hold here until promise resolves
},
});
The per-state debug field is mutable. state.debug = { before: true } sets a pre-step break; before: [symA] filters by what the head is reading. The hook is awaited, so any consumer can implement “freeze until the human clicks Resume” by simply not resolving the promise.
I also wanted breakpoints on halt:
haltState.debug = { before: true }; // pause before exit
That worked. Symbol-list filters on haltState were silent no-ops, because haltState has no head symbol to filter by. Fine, I thought — be permissive in input, the wildcard true is the one that matters.
Two things in this design were wrong. I didn’t notice either until I started writing docs and a UI on top.
Mistake 1: the hook describes the engine, not the consumer
onDebugBreak reads, on paper, like a perfectly reasonable name. The engine fires a “debug break”; you hook into it.
But what does the consumer do in that hook? They pause. They inspect. They wait for input. They resume. The hook isn’t really notifying you that a thing happened — it’s offering you a cooperation point.
The name onDebugBreak carries one specific framing: “debugging”. But the same hook is exactly what you want for a step-through visualization, a slow-motion playback control, an animation tween, a “press space to advance” UI. None of those are debugging. They’re all pausing.
This sounds like a small thing. It’s not, because once consumers see the name they design around it: their code path is called handleDebugBreak, their state machine has a debugging boolean, their UI button says “Stop debugging”. The name leaks into every consumer’s vocabulary.
I wrote an RFC (turing-machine-js#109) and renamed it in v5. Hard rename, no deprecation alias:
await machine.run({
initialState,
- onDebugBreak: (m) => { ... },
+ onPause: (m) => { ... },
});
The payload field m.debugBreak stayed — it’s metadata describing why this yield fired ({ before: true } or { after: true }), and “break” works fine inside the payload. But the hook name is the consumer’s contract: onPause describes what they do, not what the engine does.
Rule for next time: when naming a hook, name the consumer’s verb, not the engine’s event. Hooks are cooperation points, not event listeners.
Restriction: haltState.debug.after is nonsense
The second v4 mistake hid under an instinct to be permissive in input. haltState.debug.after accepted writes silently. It just didn’t fire.
Why? Because the after semantics in this engine mean “fire on the iteration after the one where the filter matched”. That makes perfect sense for normal states — you transition out of state K, the next iter (K+1) runs, and at that yield the consumer is told “by the way, K’s after-filter fired last step”. But halt is terminal. There is no K+1 after halt. The after event has nothing to anchor on.
I’d silently swallowed haltState.debug.after = true in v4. Worse, I’d let { before: true, after: true } through — half the assignment was meaningful, half was a no-op, and nothing told the consumer.
In v5 I made both throw at write time (turing-machine-js#108 part 2):
- haltState.debug = { before: true, after: true }; // v5: throws — 'after' on halt has nothing to anchor on
+ haltState.debug = { before: true };
Rule for next time: if a configuration has no semantics, throw early. Silently no-op input looks user-friendly until a consumer spends an afternoon wondering why their UI doesn’t fire on halt. “Be permissive” is a false economy when permissiveness silences a real mistake.
While I was in there, I also fixed a related bug: the halting iter’s own after filter wasn’t firing either (turing-machine-js#108 part 1). The driver loop exited as soon as state.isHalt became true — and the after-event from the previous iter, which would normally fire on this final yield, got dropped on the floor. v5 added a post-loop drain to fire it.
I’ll come back to this.
And one v5 bonus: a master switch on run() for the whole pause system.
await machine.run({ initialState, debug: false, onPause: ... });
When false, all pause-fires are suppressed regardless of what state.debug says across the graph (turing-machine-js#106). You can A/B “debug mode” without rewriting the graph or clearing every state.debug field. The flag dispatches onPause; the underlying m.debugBreak payload field still populates on the generator’s yields (it’s a property of the iteration, not of how run() chose to surface it).
Mistake 2: the substitution dance
This is the one I’m proudest to have caught, because the code worked, the tests passed, and the docs were correct. The shape was just wrong.
In v4 and v5, after K (the after-fire for iteration K) actually fired on iteration K+1’s yield. The driver loop carried a pendingAfterFromPrev flag across yields, and on the next iter’s yield it dispatched the after hook first, then the before hook, then onStep. The hook for K’s after-fire had to see K’s state — not K+1’s. So I substituted the previous yield’s snapshot into the m.state field of K+1’s yield, just for the duration of the after dispatch.
This worked. But it had three knock-on effects:
The substitution leaked. Consumers wanted access to the un-substituted, “real” iteration state (the one the step hook saw) — see turing-machine-js#107. Some users were reading raw MachineState.debugBreak from runStepByStep directly and were surprised that m.state referred to the iter on which the after-fired-from, not the iter that produced it.
The halt case needed a special path. As mentioned: the halting iter’s own after fires after the loop exits. v5 added a post-loop drain to handle it. That drain is its own code path, with its own substitution, separate from the in-loop dispatch.
The generator’s return type widened. Because the post-loop drain needed to return a final yielded value out of the iterator, runStepByStep’s return type became Generator<MachineState, MachineState | null> — a yield type and a return type. The canonical for..of consumer doesn’t see the return value, but anyone reading the type signature now had to understand why there were two slots.
All of this came from one design choice: dispatching after K on iter K+1’s yield instead of on iter K’s own yield.
In v6 I collapsed it (turing-machine-js#119). The per-iter lifecycle is now plainly before → step → after. The after fire rides on the same yield as the iter that produced it. No substitution. No pendingAfterFromPrev. No post-loop drain. The generator return type narrows back to Generator<MachineState>.
// v6
await machine.run({
initialState,
onStep: (m) => { /* unchanged */ },
onPause: (m) => {
if (m.debugBreak?.before) console.log('before:', m.state.name);
if (m.debugBreak?.after) console.log('after:', m.state.name);
},
});
The dispatch order across hooks changed (any test asserting pause(after K-1) → pause(before K) → step(K) had to flip to pause(before K) → step(K) → pause(after K)), but the set of dispatched calls and per-iter semantics are unchanged. Consumers treating the hooks as independent observers see no change at all (turing-machine-js#107 — “expose un-substituted state” — disappeared as a problem: there’s no substitution to expose around).
Rule for next time: if your hook payload requires substituting state from a different iteration than the one that’s yielding, the dispatch sequence is wrong. Lifecycle phases (before, step, after) belong on the iter they describe. The substitution wasn’t a clever trick — it was a leak telling me the events were on the wrong tick.
What I’d carry to the next pause/breakpoint API
Name hooks for the consumer’s verb, not the engine’s event. onPause, not onDebugBreak. onProgress, not onChunkReceived. The name leaks into every consumer’s mental model — pick the framing you want them to inherit.
Throw on impossible configurations early. “Permissive in input” is fine for shapes that might be meaningful; it’s a trap for shapes that can’t be. haltState.debug.after = true had no possible semantics. Silently swallowing it cost an afternoon to one user before I noticed.
Per-iter phases belong on the iter they describe. If you’re tempted to dispatch after K on K+1’s yield, ask what payload you have to substitute to make it look right. The substitution is the signal.
A master switch is cheap and worth it. A boolean on the entry point to flip the whole feature off (without rewriting graph-level flags) makes the feature safe to ship to production and toggle in tests. run({ debug: false }) was one of the smaller v5 adds and one of the most-used by downstream consumers.
Breaking changes are worth the major bump. Three majors in two weeks sounds expensive. It isn’t, if the lockstep downstream is one repo (in my case @post-machine-js/machine, which bumped its peer dep ^4.0.0 → ^6.0.0 and renamed its own internal __onDebugBreak → __onPause in step). API ergonomics compound across every consumer for the life of the library — pay the migration cost while the API is young.
The v6 shape is the one I should have shipped in v4. I didn’t have the vocabulary then — I was thinking “expose the engine’s events” instead of “design the consumer’s contract”. Writing the docs is what surfaced both mistakes. There’s a meta-lesson in there about how docs are the cheapest design review you can run, but that’s another post.
Code: turing-machine-js (engine) and post-machine-js (a Post machine built on top, kept in lockstep). The interactive demo at demo.machines.mellonis.ru consumes the v6 onPause hook for its Step / Run / Stop controls.
Firefox, “sallayarak özetle” özelliğini bugün resmi olarak Android sürümüne de getirdi. Bu yapay zeka temelli özellik açılan internet sitesini analiz ediyor ve kısa bir özet oluşturuyor. 5.000 kelimelik içeriklere kadar destek veren özelliği aktif etmek için cihazın gerçekten sallanması gerekiyor. iOS’a geçtiğimiz yıl gelen özellik, belirli durumlarda büyük zaman kazancı sağlayabiliyor.
Firefox, “sallayarak özetle” özelliğini Android sürümüne de getirdi yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.
8 Haziran tarihinde düzenlenecek WWDC26 etkinliğinde tanıtılacak macOS 27 işletim sistemi, Intel işlemcili Mac’lere yüklenemeyecek. Bu bilgiyi aslında daha önce veren ve mevcut macOS Tahoe’nin Intel işlemcili Mac’lere yüklenebilecek son sürüm olacağını duyuran Apple, şu anda macOS Tahoe tarafından desteklenen son Intel işlemcili MacBook Pro (16-inç, 2019), MacBook Pro (13-inç, 2020, dört Thunderbolt 3 portlu), iMac (2020) ve Mac Pro (2019) modellerini yazılımsal açıdan geride bırakmaya hazırlanıyor.
macOS 27, Intel işlemcili Mac’lere yüklenemeyecek yazısı ilk olarak Teknoloji haberleri – LOG üzerinde yayınlandı.