AI 资讯
SSKCore: Turning Production Pain Into an Android Platform [PART-2]
📚 This is part 2 of a series. Part 1: The Origin Story Part 2: [Current Article] Part 3: Coming soon... Let me tell you about the day my crash reporting UI crashed. The Grey Screen One afternoon, my Android app's crash screen rendered all-grey. No content. No report button. Just a blank slate where the app's last line of defense should have been. The root cause? A stale file from Gradle's build cache after a major refactor. The compiled resource IDs no longer matched the packaged resource table. ViewBinding inflated the wrong layout, and a silent NullPointerException killed the crash screen itself. It was invisible in CI. It only appeared in specific rebuild scenarios. And it took hours to trace. That bug taught me something important: The fix isn't done when the patch ships. It's done when the lesson becomes automated. So I wrote a build-time task that reads the compiled class files directly, compares them against the final packaged resources, and verifies every constant matches. It runs automatically after every packaging step. You never have to remember to invoke it. That was the first of many incident-driven tools I built. The FAB That Disappeared A few weeks later, a developer tools Floating Action Button vanished from consumer apps. Debug menus inaccessible. Secure screens incorrectly enabled. Turns out, my shared library's BuildConfigUtils was reading the library's own BuildConfig —which is baked as "release" at publish time. An AAR can never know the consumer's build type. 25 files across 34 call sites were silently broken. I built a Gradle plugin that generates a SskBuildConfig object per consumer module, per variant, using AGP's onVariants callback. It registers generated source via KotlinCompile.source() —not reflection, which broke across AGP versions. It detects Android plugins by extension type, not hardcoded IDs, so it works with com.android.application , com.android.library , com.android.dynamic-feature , and any future Google plugin. Same package as
AI 资讯
A Windows Desktop App Is “Not Responding”: Diagnose the Wait Before Reinstalling
A frozen desktop window is a state, not a diagnosis. Windows adds Not Responding when the UI thread stops processing messages for long enough. That can happen because the application is doing legitimate work, waiting for disk or network I/O, blocked by another process, stuck behind a modal dialog, or caught in a real deadlock. Reinstalling may replace files, but it does not tell you what the process was waiting for. Preserve a few minutes of evidence first. Define the symptom precisely Keep these cases separate: Slow: the window still repaints and eventually accepts input. Not responding: the frame is visible, but Windows reports that the app is not processing messages. Blank: the frame appears while the content surface fails to render. Invisible: the process runs without a visible main window. Crash: the process exits and may create an application error event. This distinction matters. A blank WebView surface and a blocked UI thread can look similar to a user, but they leave different evidence. Use one repeatable action Restart the application once and perform the smallest action that reproduces the freeze. Record: the exact click or file that triggers it; the time the action starts; how long the window remains responsive; whether CPU, disk, or network activity changes; whether the process recovers without being terminated. Avoid opening several test files or clicking repeatedly. Extra input can queue more work and hide the original transition. Watch the process before ending it Open Task Manager and identify the correct process ID. Expand child processes if the application uses helpers or a web-rendering runtime. Useful observations include: High sustained CPU: a loop, intensive parsing, OCR, compression, or rendering work is plausible. Near-zero CPU with disk activity: the process may be waiting for storage. Near-zero CPU with network activity: an online request, proxy, DNS, or TLS operation may be blocking progress. Near-zero activity everywhere: look for a hidd
AI 资讯
Building a Plug-and-Play JVM Compiler for Android and Desktop with Bytesmith
What if adding Kotlin and Java compilation to your application didn't mean building an entire compilation pipeline yourself? What if you could add Bytesmith, configure the filesystem once, provide your source files and output destination, and simply compile? That's the idea behind Bytesmith . Bytesmith is a Kotlin and Java compiler toolkit designed for JVM and Android applications. It provides a unified API for Kotlin, Java, and mixed-language compilation, while also supporting filesystem abstraction, custom classpaths, boot classpaths, compiler plugins, packaging, and diagnostics. Configure the environment, provide the source, specify the output, and compile. The problem Compiler tooling can become surprisingly difficult when it is tightly coupled to the environment in which it was originally designed to run. You might need to deal with: Kotlin compiler versions Kotlin standard libraries Java compilation Bootclasspath configuration Dependency classpaths Source discovery Output handling Android storage Storage Access Framework URIs Packaging Compiler diagnostics And then there is the question of where those files actually live. On a desktop JVM, you might have traditional filesystem paths: /home/user/project/src/Main.kt On Android, you might be working with application storage or files selected through the Storage Access Framework: content://... If your compiler API directly depends on java.io.File , your compilation code becomes coupled to one filesystem model. Bytesmith takes a different approach. Adding Bytesmith The goal is to make compilation something you can plug into an application. With Gradle: implementation ( "io.github.sifisofakude.bytesmith:bytesmith-common:1.0.0" ) After adding Bytesmith, configure the filesystem your application wants to use. For a JVM application: FileSystems . current = JvmFileSystem () For Android: FileSystems . current = AndroidSafFileSystem ( context ) Once the filesystem is configured, the rest of the compilation layer can opera
AI 资讯
My Caption Width Guard Passed Every Test. It Was Measuring Text the Renderer Never Drew.
Originally published on hexisteme notes . A user complaint sent me into a caption pipeline: "the subtitles cut to two words in places where the sentence doesn't make sense." The fix I shipped for that complaint introduced a second bug, one word narrower and easy to miss, because the code that measured whether a line of text would fit reproduced an assumption about the text that the code drawing the line didn't share. Every test passed the whole time. I only found it by watching the rendered video. The bug the complaint pointed at The captioning system splits a transcript into short chunks that pop onto screen a few words at a time. The chunking function was doing fixed-size slicing — take the next N words, regardless of what came before or after. That's blind to sentence boundaries, so two unrelated sentences could land in the same chunk: loss. Today reads as one visual unit even though it's the tail of one sentence and the head of the next. The fix was a rule set, not a single tweak: hard break after terminal punctuation ( . ! ? … ) soft break at commas, semicolons, and em-dashes extend or push a chunk rather than let it end on a function word ( of , the , than , is , and about thirty others) target three words per chunk, four as a ceiling a pixel-width cap on the rendered chunk, measured against the actual caption font (Montserrat ExtraBold), with a budget of 1080 × 0.92 = 993.6px The first four rules are about where a line is allowed to break. The fifth is a physical constraint: however good the break points are, a chunk still has to fit on screen at the font size actually in use. That's the one that went wrong. What the width guard actually measured To get the pixel width of a candidate chunk, the guard rendered the chunk's text through the font and measured the result — which is the correct approach in principle, not a shortcut. Text width isn't a fixed number of pixels per character; it depends on the specific glyphs, so measuring the real string through the r
AI 资讯
Linkdaze’s smart calendar is built to run a household, not just track a schedule
Linkdaze's smart digital calendar stands out for not putting its features behind a paywall, including an AI meal planner tool.
科技前沿
Flock CEO calls for ‘compromise’ as surveillance company faces growing backlash
Flock Safety faces a growing public outcry over concerns that its surveillance technology could be misused.
AI 资讯
What if you don't have to build a login page again?
How do you usually build a login page in an application? The first project Imagine you are working on a project that needs a login page. Let's call it Aurora (Project A). The login page is the entry point to the application. Users who have access can log in to the application with the permissions they have. We are not going to talk about the details of the login method yet, such as email + password, username + password, phone + password, social login, magic link, or others. Let's say we use email + password for this example. For this, we usually need user data for the application, for example a users table in the database. If we use email and password as the login method, the users table would at least need email and password columns. Of course, the password should be hashed. After the application is developed, users can log in using the email and password registered in the database. During development, we can simply inject user data directly into the database. Adding one or two users manually is still fine. If we need more users, we can create a database script to insert them. Then another requirement appears. We need to manage users directly from the application. Previously, user data could only be accessed directly from the database. Now the application needs to show a list of users, user details, and provide features to create, update, and delete users. We need to build several new pages for this user management feature. Eventually, the feature is completed. Now you can add users whenever you want, and they can immediately use their account to log in to Aurora. At this point, the user requirements for Aurora might be enough. The second project Then you have another project that also needs a login page. Let's call it Borealis (Project B). This is a different project from Aurora, but the login works in a similar way. Since you already built the login feature in the previous project, you can duplicate the existing code into Project B, including the user management
AI 资讯
Next step to client-side storage
Next step to client-side storage In my past one blog, I wrote about how I improve the performance of the application using the local storage. And the problem local storage solves. But now I face another problem about the client storage. My project is simply about order management software for the rental clothing industry. In the rental clothing industry, Showrooms or small shops have a big problem. The problem starts when one order has a single or multiple items that are booked in a particular time range. Now, a second order wants the same item in between that particular time range. If, by mistake, the second order books that item, then the problem starts. The item is booked two times in that particular time range. That is called double booking of the item. This mistake is created by the use of traditional register booking. Now, when I need to store the items data, that is a small amount of data, so I simply use the local storage. But now I need another and a big storage for storing order details. I build two features: first one is for showing all the orders and second one is for showing the full order. To implement those features and to maintain the user experience, I decide to store a small amount of data about the order on the client side. First, I decide to store data in local storage. But to store data in the local storage is not a good option because the local storage is used for storing small details about the application, and storing order details in the local storage compromises the performance of the application. Now I want a new storage option for storing order details. And again I find out, and that is the IndexedDB. To integrate IndexedDB in my application, I want to learn about that storage. I search multiple videos about IndexedDB, but no one is teaching me properly. After finding hundreds of tutorials, I finally found one tutorial that is teaching properly how to integrate IndexedDB in the application. Now I want to share that learning with you. To i
AI 资讯
Product Engineering Alignment
A feature takes three days to code and three weeks to deliver. The difference is not always engineering capacity. A developer starts implementation and discovers that an eligibility rule is undefined. Product needs an answer from operations. A missing UX state appears next. Then engineering finds that the requested behavior conflicts with the current data model, which forces a scope decision. The code may still take three days. The delivery system takes three weeks. This is where product engineering alignment becomes an engineering leadership problem. The visible work happens in code, but much of the elapsed time happens between decisions: waiting for clarification, resolving constraints, revisiting scope, and discovering assumptions that should have surfaced earlier. The common response is to improve requirements, add meetings, or demand better estimates. Those actions may help, but they do not address the core issue. Product-engineering alignment is primarily a decision-flow problem . The useful question is not: Are product and engineering communicating enough? It is: Where does work stop because the person holding it cannot make the next decision? That question is more useful because it exposes where delivery actually slows down. Why Product and Engineering Become a Delivery Bottleneck Product and engineering approach the same feature with different knowledge. Product typically understands the customer problem, business priorities, stakeholder expectations, commercial constraints, and desired outcome. Engineering typically understands architecture, dependencies, operational risk, implementation alternatives, and the cost of changing the system. Neither side has the full picture, that is normal. The problem begins when the process assumes one side can finish its thinking before the other begins. Consider a requirement that appears simple: Allow customers to cancel an order. Engineering cannot implement that correctly without answering several questions: Until what
AI 资讯
Shipping Stock CLIs as Subprocess Instead of Static-Linking SDKs
I'm building yyzTools, which bundles 9 third-party engines (OpenSSL, FFmpeg, ImageMagick, pdfcpu, Aria2, 7-Zip, RapidOCR, Everything...). I chose to spawn them as subprocesses rather than static-link their SDKs. Here's why—and the cost. The conventional approach When your app needs OpenSSL crypto, FFmpeg video processing, ImageMagick image ops—you reach for the SDK. Link libssl, link libav*, link libMagick. One binary, no external deps, fast function calls. It's the textbook answer. I did the opposite. yyzTools ships the stock CLI binaries (openssl.exe, ffmpeg.exe, magick.exe, pdfcpu, aria2c, 7z) and spawns them as subprocesses. The C++ layer is a thin loop: build args → CreateProcess → read stdout → wrap as JSON → return. It doesn't know what -gravity southeast or sm4-cbc means. It just passes the algorithm name through. Why I went this way Upgrades without recompiling This is the big one for a desktop app. OpenSSL ships a CVE, or adds sm2/sm3/sm4 support in 3.x. If you've static-linked, you recompile the whole app, run full regression, re-release, and every user reinstalls. With the subprocess model, I drop in a new openssl.exe. Zero C++ changes. The update is a few-MB delta, not a full reinstall. For a product where users won't tolerate reinstalling for a library bump, this is the deciding factor. No symbol conflicts OpenSSL, zlib, libpng—multiple libraries want to own these symbols. Static linking them all into one binary is a recipe for "which inflate did I just call?" With subprocess CLIs, each tool brings its own dependencies in its own process. No conflict. Transparent supply chain openssl version, ffmpeg -version—auditing which version of each tool is live is trivial. It's an independent binary. Far easier than digging symbols out of a statically-linked blob. Free crash isolation If ffmpeg.exe misbehaves, it exits non-zero and my host wraps that as an error. My main process keeps running. A static-linked bug can take down the whole app. The process boundary
AI 资讯
ByteByteGo in 2026: Is It Still Worth It for System Design Interview Prep?
Disclosure: This post includes affiliate links; I may receive compensation if you purchase products or services from the different links provided in this article. Credit - ByteByteGo Hello Devs, if you're preparing for a System Design interview in 2026 , there is a good chance you've come across ByteByteGo and its founder, Alex Xu, author of another popular System Design interview resource and book, the System Design Interview - An Insider's Guide . But with so many system design courses, books, YouTube channels, newsletters, and interview platforms available today, an important question remains: Is ByteByteGo still worth it for System Design interview preparation in 2026? After spending considerable time exploring the platform and Alex Xu's system design material, my answer is yes — especially if you prefer visual, structured, and practical explanations of complex distributed systems. What makes ByteByteGo particularly interesting is that it has grown beyond the original system design material. The platform now covers areas such as Object-Oriented Design, Machine Learning System Design, Generative AI System Design, and Coding Interview Patterns , all the important topics you need to master to crack any FAANG-level interview. The biggest strength, however, remains the same: making complicated system design concepts easier to understand through diagrams, examples, trade-offs, and real-world case studies. In this article, I'll take a fresh look at ByteByteGo in 2026, explain what it offers, who should use it, what you'll learn, and whether I think it's worth paying for. If you're already looking for a system design resource, you can check out ByteByteGo here . What Is ByteByteGo? ByteByteGo is an online learning platform created by Alex Xu , the author of the popular System Design Interview — An Insider's Guide books. The platform started with a strong focus on system design interview preparation and has evolved into a broader technical learning resource. One of the t
AI 资讯
The Matrix: Writing Code That Doesn't Need Comments
The Quest Begins (The "Why") I still remember the first time I opened a legacy codebase and felt like I’d stepped into a dark dungeon without a torch. The file was a single 800‑line function called processData . Inside, variables bore names like tmp , x , flag , and comments that tried to explain every line: // TODO: refactor this mess function processData ( input ) { let r = []; // result array for ( let i = 0 ; i < input . length ; i ++ ) { // loop over items if ( input [ i ] > 10 ) { // if value greater than threshold let v = input [ i ] * 2 ; // double it if ( v % 2 === 0 ) { // if even r . push ( v ); // add to result } } } return r ; } I spent three hours tracing why a certain edge case produced an empty array, only to discover the comment “if value greater than threshold” was outdated—the threshold had changed to 12 in a later commit, but the comment never got updated. The code lied, the comments misled, and I felt like a hero who’d just swung at a shadow. That frustration sparked a question: What if we could write code so clear that comments became unnecessary? Not because we’re lazy, but because the code itself tells the story. The Revelation (The Insight) The treasure I uncovered wasn’t a new framework or a slick library—it was a mindset shift: make the code self‑documenting through intention‑revealing names and small, focused functions . When a variable, function, or class name reads like a sentence, the reader can infer what’s happening without a side note. Think of it like reading a well‑written novel. You don’t need footnotes to understand that “She opened the door and stepped into the rain” means she’s going outside. The same principle applies to code: if you name a function filterValuesAboveThreshold , the intent is obvious. Why does this matter? Because comments decay. They become outdated, they get ignored, and they add noise. Self‑explanatory code, on the other hand, stays accurate as long as the name stays accurate. It also forces you to think ab
AI 资讯
Understanding Gitworkflow
Git Workflow Git is a local version control system that tracks code changes, while GitHub is a cloud-based platform used to host those changes and collaborate with others. Together, they form the backbone of modern software development by allowing multiple developers to work on the same codebase simultaneously without overwriting each others work Working directory of git This is the actual, physical folder on your computer's filesystem where you view, create, edit, and delete your project files. It can either contain : Tracked files : files that Git actively monitors and includes in version control history Untracked files : are any files in your working directory that have not yet been added to your Git repository's snapshots or staging area. Staging Staging is the process of preparing specific file changes to be included in your next commit. Reasons for staging Atomic Commits : It allows you to group related changes together. If you fix a bug and work on a new feature at the same time, you can stage and commit the bug fix separately from the incomplete feature. Review Mechanism : It provides a safe buffer zone to double-check exactly what lines of code are moving forward. Work Checkpointing : You can stage a file at a certain point of success, continue experimenting on that file in your working directory, and still preserve your staged checkpoint. Staging commands git add "filename" Stages a specific file. git init Manages project. git status To see what files are currently sitting in staging vs your working directory. git diff Shows differences between your working directory and your staging area. git diff --staged Shows differences between your staging area and your last commit git restore --staged "filename" Removes Changes from Staging Commit and push To save your local changes and upload them to git you need to stage your changes, commit them locally, and push them to the server. Commands used in commit and push The block of code below is used in the given ord
AI 资讯
Planning Feature Integrations Before Development: A Practical Approach
When working on a web project, one of the easiest ways to create unnecessary development work is to start coding before the feature requirements and integration approach are clear. I’ve found that creating an issue, proposal, or short technical plan before development can make a big difference. It gives everyone an opportunity to discuss the idea, identify potential problems, and agree on an implementation approach before code changes begin. This is particularly useful for projects that evolve over time. New features can affect existing components, user flows, APIs, databases, and the overall interface. Thinking about these dependencies early can reduce redesigns and duplicated work. For example, while working on projects such as Simulator Drag Race , planning new simulation features before implementation helps keep the existing functionality organized while making room for future improvements. A simple pre-development process can be: Describe the feature and the problem it solves. Create an issue or proposal for discussion. Identify which existing components will be affected. Discuss possible implementation approaches. Agree on the approach before development starts. Break the approved approach into smaller development tasks. This process doesn't need to be complicated. Even a short issue with clear requirements and a few implementation notes can prevent misunderstandings later. Another benefit is that early communication gives maintainers and contributors visibility into upcoming changes. Someone may already be working on a related feature, or a maintainer may know about an architectural limitation that isn't immediately obvious. For open-source and collaborative projects, I think this approach is especially valuable. Good communication before development can be just as important as the code itself. How does your team handle feature proposals before development? Do you prefer detailed technical proposals, simple GitHub issues, or discussing the implementation dire
AI 资讯
Pixel 11 Pro XL review: Snappier cameras can’t hide an iterative upgrade
Google’s Pixel 11 Pro XL brings snappier cameras and genuinely useful AI features like Rambler, but its iterative upgrades may not be enough to tempt recent Pixel owners.
AI 资讯
Bulletproofing AI Agents: How to Prevent $2,000 Infinite API Loops
Implement multi-layer circuit breakers, payload hashing, and financial cutoffs before an autonomous agent drains your backend. The Bottleneck in Production Autonomous AI agents running in tool-use loops fail unpredictably. When an LLM encounters an unexpected schema, a transient network error, or an ambiguous prompt, it often enters a hallucinated retry storm. In standard web apps, a runaway loop hits a rate limit or returns a 500 Internal Server Error . In agentic architectures, an unconstrained ReAct loop executes external API calls continuously, burning tokens, exhausting upstream quotas, and running up massive cloud bills in minutes. Here is the anti-pattern running in far too many codebases: # Anti-pattern: Unbounded autonomous agent loop while not task_complete : action = llm . decide_action ( state ) result = external_api . call ( action . endpoint , action . params ) state = update_state ( result ) If the LLM fails to transition state due to an unparseable response, this loop runs indefinitely. Cloud providers do not issue refunds for self-inflicted API usage. The System Architecture & Fix To make AI agent tool execution production-safe, never allow direct API calls from agent code. Route every external request through an isolated API Safety Wrapper implementing three distinct layers of defense: Deterministic Request Firewall: A hard cap on execution count per task session (Time-To-Live counter). Sliding-Window Loop Detector: Hashing outgoing request payloads to catch repetitive or oscillating tool invocations. Financial Kill Switch: A pre-flight budget validator that cuts credentials immediately if projected cost exceeds session limits. [ AI Agent Engine ] │ ▼ [ API Safety Wrapper ] ├── 1. Call Counter Check (Limit < N) ├── 2. Hash Duplicate Detector (Window: last 3 calls) └── 3. Pre-flight Cost Estimator (Budget < Limit) │ ┌────┴──────────────────────────┐ [ Passed ] [ Tripped ] │ │ ▼ ▼ [ External Upstream API ] [ Emergency Kill Switch ] (Revoke Token & Ab
AI 资讯
How to Check Closed-Source Firmware for Known CVEs (No Source Code Needed)
A router, an IP camera, an industrial controller: somewhere in that device's firmware there's a Linux kernel with modules, a handful of statically linked binaries, and a userspace built from a dozen open source components. You don't have the vendor's source tree. What you have is a .bin file, or after unpacking it, a pile of .ko , .o and stripped ELF binaries. The question you actually need answered is boring but important: is any of this running something with a known CVE? This comes up constantly in embedded and IoT work, and it's a different problem from auditing your own codebase. You're not hunting for a new bug, you're checking for old ones the vendor never patched. In practice that's the more common finding: not a novel zero-day, but a five-year-old OpenSSL or BusyBox build nobody was tracking. Unpack first, guess later binwalk is still the first move. Point it at the firmware image and let it scan for known magic bytes: SquashFS, CramFS, JFFS2, gzip streams, kernel headers. Most consumer and SOHO firmware is a bootloader plus a compressed filesystem, and binwalk's extraction mode gets you the actual filesystem tree instead of one opaque blob. Once you have that, you're auditing files, not guessing at a blob. Fingerprint by version string, not by hash Hash-matching binaries against known-vulnerable databases sounds appealing and mostly doesn't work here, because vendors relink, strip and sometimes patch without touching anything else. What works more often: grep the extracted binaries for version banners. strings on busybox , openssl , dropbear , lighttpd , zlib and similar userspace binaries usually still leaks a version string even when the binary is stripped of debug symbols, because those strings are compiled-in constants the program itself prints or logs, not debug metadata. strings <binary> | grep -iE "openssl|busybox|dropbear|zlib" is unglamorous and it's the single highest-signal step in this whole process. Cross-reference what you find Once you have
开发者
완전자동매매 시스템에 사람이 직접 개입해야 했던 사례 3가지
자동으로 돌아가게 만든 것과, 자동으로 끝까지 처리되는 것은 다른 문장이었습니다 이 시스템은 사람 승인 없이 스스로 판단하고 매매하는 걸 목표로 설계했습니다. 실계좌 주문 실행과 안전장치 (새 창)도 그 목표에 맞춰 만들었습니다. 그런데 최근 한 달 사이 실계좌에서 세 번, 사람이 직접 개입해야 하는 상황이 있었습니다. 세 사례 모두 "왜 자동 로직이 이 상황을 못 넘겼는지"의 구조가 서로 달랐습니다. 1. 배분 규칙이 특정 주문을 구조적으로 굶겼다 특정 종목 하나가 여러 날째 매도 계획이 서 있는데도 계속 팔리지 않는 걸 발견했습니다. 시스템은 매일 이 종목을 매도 후보로 올렸지만, 실제 주문까지는 못 갔습니다. 원인은 하루 매매 한도를 여러 라운드에 나눠 배분하는 규칙이었습니다. 이 종목의 주문 금액이 그날 남은 매도 한도보다 항상 컸습니다. 라운드 순서를 아무리 바꿔도 통과할 수 없는 구조였습니다. 한도 자체는 정상 작동하고 있었습니다. 문제는 "이번엔 못 나가도 다음 기회에 나간다"는 전제가 이 종목엔 애초에 성립하지 않았다는 점입니다. 잔여 한도가 매번 주문 금액보다 작으면, 기회는 계속 오지만 한 번도 충분하지 않습니다. 당장 못 나간 주문 1건은 사람이 직접 처리했습니다. 실계좌에서 이뤄진 되돌릴 수 없는 매도였습니다. 이후 배분 규칙 자체를 손봐서 같은 구조로 다시 굶는 일이 없도록 정리했습니다. 2. 안전장치가 스냅샷과 누적치를 혼동했다 다른 날엔 반대 방향의 사고가 있었습니다. 누적 손실을 감지하는 안전장치가 정상적인 매수 2건을 잘못 차단했습니다. 지수는 그날 거의 보합이었는데, 이 안전장치가 재는 손실률은 훨씬 크게 찍혀 있었습니다. 원인을 보니 이 장치는 "고점 대비 누적 하락"을 감지하는 용도였는데, 정작 비교하는 현재값은 장중 순간 스냅샷이었습니다. 장중 잠깐의 변동이 누적 지표를 밀어 올려서, 실제로는 발동하면 안 될 상황에서 발동한 겁니다. 누적을 재는 장치와 순간을 재는 장치가 뒤섞여 있었던 셈입니다. 막힌 매수 2건은 사람이 판단해서 직접 집행했습니다. 이후 이 안전장치가 장중 순간값이 아니라 "그날 마감 대 전날 마감" 기준으로만 반응하도록 구조를 바꿨습니다. 장중 급락에는 이제 다른 안전장치가 대신 반응하도록 역할을 나눴습니다. 3. 개입 경로 자체가 "새로 사는 경우"를 몰랐다 두 번째 사례를 수습하는 과정에서 사고가 하나 더 있었습니다. 수동으로 낸 주문을 원장에 반영하는 도구를 썼는데, 반영이 안 되고 조용히 빠졌습니다. 이 도구는 사람이 손으로 낸 거래를 세 가지 경우 중 하나로 분류합니다. 기존 보유 종목을 판 경우, 기존 보유 종목을 더 산 경우, 그리고 시스템과 무관한 거래인 경우입니다. 그런데 이번 매수는 원장에 없던 새 종목을 사람이 처음 사들인 경우였습니다. 세 분류 중 어디에도 안 맞았고, 도구는 이걸 "시스템과 무관한 거래"로 잘못 넘겼습니다. 그 결과 실제로는 산 자산이 잠깐 원장 밖에 있는 것처럼 표시됐습니다. 이 도구는 애초에 사람 개입을 위해 만든 경로였습니다. 그런데 그 경로를 설계할 때, "사람이 아예 새로운 자리에 처음 진입하는 경우"는 상정하지 않았습니다. 개입 경로 자체가 개입의 한 형태를 놓치고 있었던 셈입니다. 순서(먼저 다른 매도를 부기하고, 그다음 이 매수를 부기)를 지켜서 바로 수습했고, 검증 결과 원장과 실계좌 잔고는 정확히 일치했습니다. 분류 로직에 이 경우를 추가하는 건 아직 남은 과제입니다. 세 사례를 묶어보면 셋 다 "자동으로 처리되게 만들었다"와 "실제로 끝까지 처리된다"가 다른 문장이라는 걸 보여줬습니다. 첫 번째는 규칙이 있었지만 그 규칙이 특정 입력에서 절대 통과할 수 없는 구조였습니다. 두 번째는 장치가 있었지만 재는 대상(순간 대 누적)이 설계 의도와 어긋나 있었습니다. 세 번째는 사람 개입을 위한 경로가 있었지만 그 경로 자체가 특정 개입 형태를 몰랐습니다. 세 가지 모두 "자동화가 이 케이스를 놓칠 수 있다"는 걸 사전에 안 게 아니라, 실제로 놓친 뒤에야 알았습니다. 일반화하면 완전자동을 목표로 설계할수록,
AI 资讯
3 Cases Where Fully Automated Trading Still Needed a Human
Making something run automatically and having it actually get handled to completion turned out to be two different sentences This is the English version of a post originally written in Korean for my algorithmic trading system devlog (new tab). I designed this system to judge and trade on its own, without needing human approval for each decision. The order execution and safety-guard layer (new tab) was built around that same goal. Over the past month, though, there were three separate moments where I had to step in and act directly on the live account. Each one failed for a structurally different reason. 1. An allocation rule structurally starved one order I noticed a particular ticker had a sell plan queued for several days running, yet it never actually went out. The system kept nominating it as a sell candidate every day, but the order never reached execution. The cause was the rule that splits the daily trading budget across multiple rounds. This position's order size was consistently larger than whatever sell budget remained that day. No matter how the rounds were reordered, it could never clear. The budget cap itself was working exactly as designed. The problem was that the underlying assumption — "if it doesn't clear this time, it'll clear next time" — never held for this position. New opportunities kept arriving, but none of them was ever big enough. I executed the one blocked order by hand. It was an irreversible sell on the live account. Afterward, I reworked the allocation rule itself so the same starvation pattern couldn't recur. 2. A safety guard confused a snapshot with a cumulative reading On a different day, the opposite kind of failure happened. A guard meant to detect cumulative drawdown wrongly blocked two legitimate buy orders. The index was nearly flat that day, but the loss figure this guard was tracking read much larger. Looking closer, the guard was designed to measure "decline from peak," but the current value it compared against was an intra
开发者
개발일지 자동화가 한 달 가까이 멈춰 있었던 이유
로그조차 안 쌓이니, 돌고 있는지 죽어 있는지 구분할 방법이 없었습니다 이 블로그의 개발일지는 매일 밤 자동으로 마무리됩니다. 그날 대화로 초안을 썼으면 변환해서 로그에 남기고, 없으면 스킵했다는 한 줄만 남깁니다. 최근 이 파이프라인을 들여다볼 일이 있었는데, 7월 24일 이후로 로그가 통째로 비어 있었습니다. 무슨 일이 있었나 자동화 로그( .automation.log ) 마지막 줄이 2026-07-24였습니다. 그 뒤로 8월 22일까지, 거의 한 달 가까이 스킵 기록조차 한 줄도 없었습니다. 자동화가 아예 안 돌았나 싶어서 실행 로그( cron_output.log )를 열어봤습니다. 그런데 거기엔 매일 밤 실행된 흔적이 빼곡했습니다. 날짜 확인하고, 초안 있으면 내용 정리하고, 크로스링크까지 챙긴 요약이 매일 밤 남아 있었습니다. 일은 하고 있었는데, 결과물만 하나도 남지 않고 있었던 겁니다. 왜 아무도 몰랐나 실행 로그를 읽어보니 원인은 매일 같았습니다. 파일 쓰기 권한 승인을 기다리다가 그대로 끝난 겁니다. "workspace has not been trusted"라는 경고가 매 실행마다 찍혀 있었습니다. 초안을 잘 정리해놓고도, 마지막 파일 쓰기 한 줄이 승인 대기에 막혀서 아무것도 저장되지 않은 채 세션이 끝나는 패턴이 한 달 가까이 반복됐습니다. 문제는 이게 하필 로그를 남기는 단계 자체가 막힌 상황 이었다는 겁니다. 스킵한 날엔 스킵했다는 한 줄도 못 남겼습니다. 그러니 로그만 보면 "자동화가 멈췄다"와 "쓸 게 없어서 조용했다"를 구분할 수가 없었습니다. 무인 자동화이니 매일 밤 누가 화면을 지켜보는 것도 아닙니다. 결과적으로 이 공백은 사람이 우연히 로그 파일을 열어보기 전까지는 발견될 방법이 없는 구조였습니다. 진짜 원인 원인은 신뢰(trust) 설정이었습니다. 이 헤드리스 자동화 세션은 프로젝트 폴더 단위로 파일 쓰기 권한을 신뢰받아야 동작하는데, 그 신뢰 설정 키가 이 블로그 폴더가 아니라 상위 디렉터리(프로젝트들이 모여 있는 루트) 단위로 걸려 있었습니다. 즉 이 블로그 폴더만 놓고 보면 "아직 한 번도 대화형으로 신뢰 승인을 받은 적 없는 새 작업공간" 취급을 받고 있었던 셈입니다. 설정 파일 안에 이미 허용 규칙 10개가 들어 있었는데도, 그 규칙들이 걸려 있는 범위 자체가 무시되고 있었습니다. 헤드리스로 도는 야간 자동화는 대화형 승인 프롬프트에 응답할 사람이 없습니다. 범위가 어긋난 신뢰 설정 하나가, 매일 밤 정확히 같은 지점에서 조용히 실행을 무력화하고 있었던 겁니다. 흥미로운 디테일 하나 — 유령 완료 기록 이 공백을 되짚어보다가 특이한 줄 하나를 발견했습니다. 8월 22일 낮 12시 41분에 "weekly: 완료"라는 로그 한 줄이 남아 있었는데, 그 시각에 대응하는 실제 산출물 파일은 없었습니다. 같은 날 오후 4시에 다시 수동으로 실행된 기록이 있었고, 이번엔 실제 파일까지 정상적으로 만들어졌습니다. 앞선 12시 41분 기록이 왜 실물 없이 "완료"라고만 남았는지는 원인을 특정하지 못했습니다. 권한 문제가 한창이던 구간이라 어떤 형태로든 쓰기 절차 일부만 성공하고 일부는 실패한 걸로 추정만 할 뿐입니다. 다만 이 한 줄은 별도로 눈에 띄는 교훈을 남겼습니다. "완료"라고 적힌 로그도 그 자체로 완전히 믿을 수는 없다 는 겁니다. 로그와 실제 산출물을 따로 대조하지 않았다면 이 유령 기록을 그냥 지나쳤을 겁니다. 어떻게 고쳤나 신뢰 설정을 이 블로그 폴더 기준으로 다시 걸어주니, 그날 밤부터 바로 정상화됐습니다. 별도의 복잡한 조치는 필요 없었습니다. 문제는 고치는 방법이 아니라, 한 달 가까이 그 문제를 놓치고 있었다는 사실 쪽이었습니다. 일반화된 교훈 이번 일로 다시 확인한 건, 무인 자동화에서 "로그가 없다"는 상태 자체가 하나의 신호라는 겁니다. 그런데 그 신호를 신호로 취급하려면, 애초에 "침묵"과 "성공적인 무동작"을 구분할 수 있게 설계돼 있어야 합니다. 이번 파이프라인은 스킵한 날에도 로그 한 줄을 남기게 되어 있었습니다. 그 설계 덕분에, "로그가 아예 안