Pixel

Pixel Updates & Events

Server Updates & Events

Announcements

DEV Community

Your page looks fine in Chrome. Here's what GPTBot actually receives

Open your product page in Chrome and it looks great: pricing table, feature list, customer quotes. Now ask a less flattering question: what does an AI crawler see when it asks for the same URL? For a lot of sites the honest answer is "much less than you think." Here's why, and a small script to check your own pages in a minute. Most AI crawlers don't run your JavaScript Googlebot renders pages: it fetches the HTML, then runs the JavaScript in a headless browser, then indexes the result. Most of the newer AI crawlers don't do that second step. Vercel looked at real crawler traffic across its network in late 2024 and found that the major AI crawlers, including OpenAI's and Anthropic's, fetched JavaScript files but didn't execute them. Whatever your page shows only after scripts run (a pricing table loaded from an API, reviews injected by a widget, tabs rendered client-side) simply isn't in what they read. Google's own AI features are the exception, because they lean on Google's rendering pipeline. Things may change, and crawlers don't publish their rendering behaviour in detail. But "the raw HTML is what they get" is the safe assumption for planning. That matters for AI answers because the engines quote what they can read. If your pricing only exists after hydration, an engine answering "how much does X cost?" has to get it from someone else's page. The check: fetch it like a bot, strip the scripts The script below asks for a URL with the published user agents of GPTBot (OpenAI's training crawler), OAI-SearchBot (the one behind ChatGPT search results) and PerplexityBot, throws away , , and blocks, and reports what's left: the HTTP status (some firewalls quietly return 403 to bot user agents), how many words of real text remain, the title, meta robots and canonical, whether the phrases you care about are actually in there. No dependencies, Python 3.8+. #!/usr/bin/env python3
"""What does an AI crawler actually receive from your page?

Usage:
python3 what_bots_see.py https://example.com/pricing "$29/mo" "free plan"
"""
import re
import sys
import urllib.error
import urllib.request
from html import unescape

User agents as published by OpenAI and Perplexity (checked 14 Nov 2026).

AGENTS = {
"GPTBot": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot",
"OAI-SearchBot": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36; compatible; OAI-SearchBot/1.4; +https://openai.com/searchbot",
"PerplexityBot": "Mozilla/5.0 (compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)",
}

def fetch(url, agent):
req = urllib.request.Request(url, headers={"User-Agent": agent})
try:
with urllib.request.urlopen(req, timeout=20) as r:
return r.status, r.read().decode("utf-8", "replace")
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8", "replace")

def visible_text(html):
html = re.sub(r"(?is)<(script|style|noscript|template)\b.*?</\1>", " ", html)
text = unescape(re.sub(r"(?s)<[^>]+>", " ", html))
return re.sub(r"\s+", " ", text).strip()

def find(pattern, html):
m = re.search(pattern, html, re.I | re.S)
return unescape(m.group(1).strip()) if m else "-"

def main():
if len(sys.argv) < 2:
sys.exit(doc)
url, phrases = sys.argv[1], sys.argv[2:]
for name, agent in AGENTS.items():
status, html = fetch(url, agent)
text = visible_text(html)
print(f"\n== {name}: HTTP {status}, {len(html):,} bytes of HTML, {len(text.split()):,} words of text")
print("title: ", find(r"]>(.?)", html))
print("meta robots:", find(r"]+name=[\"']robots[\"'][^>]+content=\"'", html))
print("canonical: ", find(r"]+rel=[\"']canonical[\"'][^>]+href=\"'", html))
for p in phr...

Powered by Readybot.io

**DEV Community** ## Your page looks fine in Chrome. Here's …

DEV Community

GitHub Actions waiting for approval to run workflows for this PR

GitHub Actions waiting for approval to run workflows means a maintainer has to release the run before any job is created. Nothing has failed, nothing is queued on a runner, and the checks that are required to merge cannot report until somebody clicks. The hold happens before dispatch, so there is no job to inspect. GitHub deletes runs that have been awaiting approval for more than 30 days. What this error means A pull request from a contributor shows no progress at all. There is no red check and no running job, just a note that a workflow is awaiting approval, and the merge box reports that required checks have not completed. From the contributor's side it looks like the automation is broken; from the maintainer's side it is easy to miss, because nothing failed and nothing sent a failure notification. ```Pull request merge box, quoted from timqian/open-source-jobs#181 1 workflow awaiting approval This workflow requires approval from a maintainer.

Common causes

The contributor has not had a pull request merged here before

The common configuration. First-time contributors are held and everyone else is not, which is why the situation appears sporadically and usually to the people least able to interpret it.

The policy applies to all fork pull requests

A stricter setting holds every outside contribution regardless of history. Repositories that receive occasional drive-by contributions can sit under this without anyone noticing, because the maintainers never trigger it themselves.

An organization or enterprise policy is stricter than the repository

The repository settings look permissive and runs are still held, because a higher level is deciding. In our experience this is the version that consumes the most time, since the repository page appears to contradict the behavior.

The pull request was opened by an automation account treated as an outside contributor

Worth ruling out when a bot's pull requests stall. An account without prior merged contributions is subject to the same rule, so a newly introduced automation can start needing approval on every run it opens.

How to fix it

Approve the run from the pull request

  1. Open the pull request and read the changed files, including any change to a workflow file.
  2. Click the Awaiting approval button at the top right to open the Merge status panel.
  3. Click Approve workflows to run.
  4. Expect to repeat this for later pushes until the contributor has a merged pull request here.

Decide the policy deliberately rather than inheriting it

Review the setting at the repository level and check whether the organization or enterprise is imposing something stricter. A project that wants drive-by contributions and a project that runs expensive jobs on every push want different answers, and the default is not tuned for either.

Make the contributor-facing checks need no approval to be useful

If the valuable signal is lint and unit tests, consider whether those need secrets or write access at all. Work that runs safely without either is the work that argues best for a looser approval policy.

Watch the 30 day limit on long-lived pull requests

A pull request that stalls in review can lose its pending run entirely. If a run has disappeared rather than completed, push a commit or close and reopen the pull request to produce a fresh event, then approve that.

How to prevent it

  • Subscribe the people who triage contributions to the pull requests that will hold, since nothing sends a failure notification.
  • Say in the contributing guide that a maintainer has to release the first run, so a contributor does not read silence as breakage.
  • Check the organization and enterprise settings before concluding the repository is misconfigured.
  • Keep the checks required for merge to ones that can run without approval wherever that is possible.

Nothing is wrong, and that is the difficulty

This is not an error and it does not appear in any l...

Powered by Readybot.io

**DEV Community** ## GitHub Actions waiting for approval to run …

** Latest from PC Gamer **

Queen's Domain is the best Dark Souls 1 since Dark Souls 1

King's Field of dreams.

Powered by Readybot.io

** Latest from PC Gamer ** ## Queen's Domain is …

DEV Community

Building on AI APIs? The $901B Question Behind the Anthropic IPO

Anthropic's IPO filing contains a number that should interest anyone who builds on AI APIs: roughly $901 billion in computing commitments, most of it take-or-pay, running into the 2030s. The money is owed largely to Google, Microsoft and Amazon — the same companies now buying shares in the offering. Google alone is putting in $10 billion, and it is also the landlord on the other side of the contract. Why should a developer care about an IPO filing? Three reasons. Pricing. Take-or-pay bills do not disappear when demand dips; they get recovered somewhere, and API pricing is the somewhere. The era of heavily subsidized inference is financed by contracts like these. Capacity. When compute is scarce, allocation is a decision. Your provider's biggest suppliers are now also its shareholders — and they run competing AI products of their own. Where your quota sits in that queue is a fair question. Lock-in cuts both ways. The labs are locked into their suppliers for a decade, which limits how fast they can switch or renegotiate. But the same structure de-risks continuity: a provider whose largest creditors just became shareholders is unlikely to be allowed to fold quietly. The full breakdown of the commitments, the seven buyers and the contract dates is in TechSonder's report on the filing. If you build on a single AI provider today, does this structure make you more comfortable — or less?

Powered by Readybot.io

**DEV Community** ## Building on AI APIs? The $901B Question …

DEV Community

Mobile App Development Cost in Kuwait Across 11 Industries

A Kuwait City restaurant wants direct pickup orders with a read-only stock feed from its POS. The app can mark items unavailable and show staff when the last successful sync ran. A full two-way integration is a later project. Across the 11 scopes below, a first release budgets from KWD 5,700 to 19,500. The app in the opening example is budgeted separately as a worked estimate. All figures are illustrative planning scenarios. The rates are editable assumptions, and the figures do not represent surveyed local vendor prices. Mobile app development cost by industry in Kuwait Each estimate includes discovery, design, development, backend, basic administration where needed, QA and release preparation. The MVP generally uses one shared iOS and Android codebase. The taxi estimate includes separate rider and driver apps and a web dispatch panel. An advanced price covers the full expanded build. Timelines are estimated calendar weeks with some work happening in parallel. Industry Example app Core MVP features MVP cost and timeline Advanced cost and timeline Healthcare Clinic appointment app Doctor profiles, scheduling, reminders, staff dashboard, front-desk appointment overrides 6,500–11,500 · 8–12 weeks 16,000–28,100 · 16–26 weeks Real Estate Property listing app Listings, search filters, map view, enquiry routing, listing enquiry deduplication 5,700–10,100 · 7–10 weeks 13,200–23,200 · 14–22 weeks Food Restaurant ordering app Menu, cart, online checkout, order status, kitchen order view, read-only POS stock feed and availability flags 7,000–12,400 · 8–12 weeks 18,000–31,700 · 18–28 weeks Education Course and quiz app Course catalog, lesson player, quizzes, progress tracking, course admin, bulk lesson imports 6,000–10,600 · 8–11 weeks 13,900–24,400 · 14–22 weeks Entertainment Event discovery and ticketing app Event listings, checkout, QR tickets, staff scanning view, ticket inventory by event session 6,200–11,000 · 8–12 weeks 14,600–25,600 · 15–24 weeks Video Streaming On-demand video subscription app Catalog, playback, user accounts, subscription access, content admin, manual catalog publishing workflow 7,600–13,500 · 9–14 weeks 22,200–38,900 · 20–32 weeks Travel Tour and activity booking app Package listings, availability calendar, booking, payments, seat inventory per departure 6,500–11,500 · 8–12 weeks 16,000–28,100 · 16–26 weeks Logistics & Supply Chain Shipment tracking and proof-of-delivery app Shipment status, driver updates, proof of delivery, dispatcher view, inventory status feed 7,300–13,000 · 9–14 weeks 18,700–32,900 · 18–28 weeks Automobile Vehicle service booking app Vehicle profiles, service slots, reminders, work-order status, parts availability view 6,000–10,600 · 8–11 weeks 15,300–26,900 · 15–25 weeks Taxi Ride-hailing app Ride requests, driver acceptance, GPS, fare estimates, basic dispatch, vehicle availability by shift 11,100–19,500 · 12–18 weeks 24,900–43,800 · 22–34 weeks On-Demand Home services marketplace Service catalog, provider profiles, booking, payments, provider web view, provider capacity by time window 8,300–14,700 · 10–15 weeks 20,800–36,500 · 19–30 weeks Prices are in KWD. How the estimate works Effort. The hours for each scope are listed below and cover the full delivery team. Rate. The budget uses a blended rate of KWD 12–21 per hour and a 15% contingency. The lower endpoint rounds down and the upper endpoint rounds up to the nearest KWD 100. Recurring costs. Ask for a separate estimate covering hosting, messaging, maps, payment processing, licences and support. Taxes are excluded from the model. For video, include transcoding, storage and bandwidth based on the hours viewers are expected to watch. Timeline. Check who owns approvals and when test credentials will be ready. A dependency can add calendar time without adding the same amount of engineering effort. Features and costs for each industry Healthcare — Clinic appointment app Clinics can let patients find a clinician, reserve a slot and re...

Powered by Readybot.io

**DEV Community** ## Mobile App Development Cost in Kuwait Across …

DEV Community

AGI readiness in 2026: scoring every major model on what actually matters

OpenAI calls GPT-6 the start of the "AGI era." Sequoia Capital published an essay in January 2026 arguing that long-horizon agents are "functionally AGI." Stanford's 2026 AI Index confirms frontier models beat human baselines on PhD-level science and competition math. So are we there? I ran every major current model through a six-dimension AGI readiness framework -- not benchmark scores, which are increasingly gamed, but the capabilities that actually matter for genuine general intelligence. Here's what I found. The Framework Six dimensions, scored 1-5: Reasoning depth -- can it hold a complex multi-step argument without drifting Reliability -- does it know what it doesn't know, or hallucinate confidently Autonomy -- can it complete a long task without hand-holding Memory/continuity -- does it maintain context or lose the thread Self-correction -- does it catch its own mistakes Tool use -- can it act on the world, not just describe it Models assessed: Claude Fable 5.1 / Opus 5.5 (Anthropic), GPT-6 Astra (OpenAI), Gemini 4 Argon (Google), Grok 4.7 (xAI), Llama 4 Maverick (Meta), Mistral Large 4 / Medium 3.5 (Mistral). The Scores Dimension Fable 5.1 GPT-6 Gemini 4 Grok 4.7 Llama 4 Mistral L4 Reasoning depth 5 5 4 3 3 3 Reliability 4 3 3 3 3 4 Autonomy (long tasks) 2 3 2 2 2 2 Memory/continuity 2 3 2 2 3 2 Self-correction 4 3 3 2 2 3 Tool use / real-world action 3 4 3 3 3 3 AGI readiness total 20/30 21/30 17/30 15/30 16/30 17/30 What the data actually says Reasoning is effectively solved at the short end Fable 5.1 leads the Artificial Analysis Intelligence Index at 66. GPT-6 scores 96% on GPQA Diamond (PhD-level science) and 97.6% on FrontierMath Tier 4 -- described by researchers as "saturation." Stanford's 2026 AI Index confirms several frontier models beat human baselines on competition math and multimodal reasoning. This dimension is no longer where AGI is blocked. Long-horizon autonomy is not solved -- and the numbers are brutal Every agent tested on LongCLI-Bench (long-horizon command-line tasks) stays under 20% pass rate. Every single one. GPT-6's impressive ARC-AGI-3 score of 99.9% only holds under an expensive custom stateful harness -- standard API performance is far lower. ARC-AGI-3 launched at 0.51% for all frontier AI (humans: 100%). The best SWE-AGI coding result (86.4%) degrades sharply on medium and hard task tiers. This is the AGI gap in a single stat: short-task brilliance, long-task collapse. Memory scaffolds are actively making things worse Frontier models show the highest "meltdown" rates on long-horizon tasks -- over-ambitious planning strategies that start well and fall apart. More raw capability without better memory architecture makes the problem harder, not easier. This is the most underreported finding in current AI discourse. We have been assuming that smarter models will automatically handle longer tasks better. The data says the opposite: without reliable memory continuity, increasing capability just means more confident failure. OpenAI's "AGI era" claim is marketing GPT-6 is genuinely impressive -- OSWorld agentic tasks at 72.6%, ScreenSpot-Pro at 92.7%. But the ARC-AGI-3 score only reaches 99.9% under conditions that bear no resemblance to how agents actually operate in production. One AI coding agent reportedly deleted a production database and its backups during this period. That is not AGI behaviour. That is a capable but unreliable tool operating without adequate continuity. Security is quietly getting worse as capability grows Only 33.5% of code generated by top-tier models is rated secure (Endorlabs analysis). GPT-6 crosses OpenAI's own internal "Critical" cybersecurity threshold -- now gated behind a special access program called "Daybreak." The International AI Safety Report 2026 flags an "evaluation gap" where pre-deployment tests do not predict real-world behaviour. Higher capability without better verification equals higher risk, not lower. Open weights are healthier than the headlin...

Powered by Readybot.io

**DEV Community** ## AGI readiness in 2026: scoring every major …

DEV Community

We're building an ERP you run from WhatsApp. Here's why.

Most growing businesses in India don't run on their ERP. They run on Tally, a few Excel sheets and a lot of WhatsApp messages. The owner asks "how much stock of X do we have?" on WhatsApp. Someone checks a sheet, someone else checks Tally, and the answer comes back an hour later, sometimes wrong. My co-founder Thirumurugan and I started GOFTUS in 2025 after watching too many software projects impress in a demo and then fall apart in real work. The pattern was always the same: the software was fine, but people kept going back to WhatsApp. So we asked a simple question: what if the ERP came to WhatsApp instead? What we built That's AIRP. It's a full ERP (inventory, purchasing, sales and billing, accounts and GST, production, HR/payroll) with an "AI employee" you talk to on WhatsApp. Ask for today's sales or stock, and it fetches live data. Ask it to raise a purchase order, and it drafts one. Ask for outstanding payments, and you get the list in the chat. The AI doesn't act alone This is the design decision we care about most. Anything that spends money or leaves the company (a PO, an invoice, a price change) goes to a person for approval first. Every action is logged. Access follows each person's role. The AI drafts; a human approves. An AI that can quietly send a ₹5 lakh purchase order is a liability, not a feature. Speed comes from the AI. Safety comes from the approval step. You need both. Boring setup, on purpose Most ERP projects for small businesses die during implementation. So we made go-live boring: live in 7 days, not 7 months. Small businesses don't have a year to spare for an ERP project. What's next We've just launched our new site and are working with our first businesses in India, with the Gulf next. If you run or work in a manufacturing, distribution or trading business, I'd love to hear one thing in the comments: what's the one report or task you'd want to do from WhatsApp? 👉 goftus.com · Book a 15-minute demo: goftus.com/contact

Powered by Readybot.io

**DEV Community** ## We're building an ERP you run from …

DEV Community

GitHub Actions GITHUB_TOKEN cannot access another repository in CI

A GitHub Actions GITHUB_TOKEN cannot access another repository failure does not announce itself: GitHub returns the same answer it gives for a repository that was never created. The token is an installation token for one repository, and outside it there is nothing to refuse. Quoted from GitHub's own description of the automatic token: "The token's permissions are limited to the repository that contains your workflow." What this error means A checkout, a submodule fetch or an API call against a second repository fails, while every call against the workflow's own repository succeeds in the same job. The message is about absence rather than permission: git reports that the repository was not found, and the REST API returns 404. Nobody is told that a boundary was crossed, so the first suspicion is a typo in the repository name, and the name is usually correct. ```Actions log, quoted from actions/checkout#2210 remote: Repository not found. fatal: repository 'https://github.com//.git/' not found Error: Process completed with exit code 128.

Common causes

A step checks out a second private repository with the default token

The common one. A build that needs a shared library, a config repository or a private submodule adds a second checkout and leaves token unset, so the automatic token is used and the clone reports the repository as missing.

Submodules are private and the parent checkout enables them

Subtler, because the failing name is a submodule rather than anything written in the workflow. submodules: true makes checkout fetch each one with the same credential, and a private submodule in another repository stops the whole checkout, taking every job that depends on it.

An API call or a repository dispatch targets another repository

A workflow that triggers a downstream build, opens an issue elsewhere or reads another repository's releases gets 404 from every one of those calls. Because 404 reads as a wrong URL, the endpoint gets rewritten several times before the credential is suspected.

The target repository was made private after the workflow was written

Worth checking when nothing in the workflow changed. A public dependency that becomes private turns a working anonymous read into a not-found, and the commit that broke the build is in a different repository from the one that is failing.

How to fix it

Establish that the boundary is the cause before changing credentials

  1. Confirm the repository name resolves in a browser while signed in.
  2. Check whether the same job succeeds against its own repository, which shows the token is working.
  3. If the target is public and the call is a read, the token is not the problem and something else is.

Use a GitHub App installation token for the durable version

Install an app on both repositories and mint an installation token in the job. The token is scoped to the installation rather than to one repository, it expires quickly, and it is not tied to a person who may leave. This is the option that survives contact with an organization.

```.github/workflows/build.yml (illustrative)
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: shared-library
- uses: actions/checkout@v5
with:
repository: ${{ github.repository_owner }}/shared-library
token: ${{ steps.app-token.outputs.token }}
path: shared-library
Use a fine-grained personal access token for the quick version Select the target repository explicitly and grant only the permission the call needs. It works immediately and carries a person's access, which is its weakness: the build breaks when that person's access changes. Expect a differently worded refusal if the permission is too narrow, because the token type changes the message. Avoid the second repo...

Powered by Readybot.io

**DEV Community** ## GitHub Actions GITHUB_TOKEN cannot access another repository …

DEV Community

Factors Shaping Lifecycle Policies for AI-Generated Image Storage — Private Access Rules

Short answer: The factors shaping lifecycle policies for AI-generated image storage are business purpose, tenant rules, sensitivity, legal holds, references, and deletion evidence; keep the object for the obligation and make private download access expire sooner. Keep an AI-generated image only as long as a defined business obligation requires it. That rule separates two things teams often tangle together: retention is a data-governance decision, while a signed download link is a short-lived access decision. For a B2B SaaS application storing private generated images, I would record the image's purpose, tenant, sensitivity class, current state, and deletion deadline as metadata at creation time. I would then enforce access through an authorization service and enforce deletion through a worker that can be retried and audited. The storage backend is the durable layer; it should not be the policy engine. What should a lifecycle policy know before it deletes an image? The useful inputs are more specific than object age. An image created as a temporary preview has a different end date from an image attached to an approved report. Tenant contract terms, legal hold, privacy requirements, user deletion requests, regeneration behavior, and whether the image is still referenced all matter. AI generation adds a less obvious question: is this original output still needed, or has a later revision replaced it? I start with an explicit state machine rather than a single expires_at field. A small set of states is enough: active, superseded, quarantined, legal_hold, and deleted. The deadline is still useful, but it is a signal to evaluate policy, not permission to delete blindly. A reference count is also not a complete answer because an old database row can outlive the product feature that created it. The deletion boundary should be conservative. If metadata says an image is on hold, the worker must leave the object alone even when its normal deadline has passed. If a tenant's policy changes, the next evaluation should use the new policy while preserving the audit record that explains the previous decision. The architecture decision record The invariants are straightforward: a tenant can read only its own objects; a download grant expires independently of object retention; deletion is idempotent; a policy decision is observable; and a failed cleanup attempt does not silently convert into permanent retention. Those invariants define the failure boundaries better than a particular storage API does. Decision Prefer Why Failure boundary Object naming Opaque tenant-scoped identifier Avoids leaking prompts or customer identifiers Metadata must still be protected Download access Short-lived server-issued grant Keeps authorization separate from retention A revoked session must not revive a grant Retention input Versioned policy plus object metadata Explains why an object is kept or removed Unknown policy version goes to review Cleanup Claim, delete, verify, record Handles retries without double effects A failed claim is retried, not skipped Reuse New object version with lineage Preserves reproducibility and auditability Old versions follow their own state This is deliberately boring. Boring is valuable in a deletion path. The clock is not the policy. The critical path for issuing a download link should not wait for a cleanup scan, and the cleanup path should not trust a link as proof that an object is still authorized. A generic Python sketch makes that separation visible: from datetime import datetime, timedelta, timezone

LINK_TTL = timedelta(minutes=10)

def issue_download_grant(actor, asset, policy, signer):
now = datetime.now(timezone.utc)

if asset.tenant_id != actor.tenant_id:
    raise PermissionError("tenant boundary")
if asset.state in {"deleted", "quarantined"}:
    raise PermissionError("asset is unavailable")
if policy.is_on_hold(asset) or not policy.can_read(actor, asset):
    raise PermissionError("acce...


Powered by Readybot.io

**DEV Community** ## Factors Shaping Lifecycle Policies for AI-Generated Image …

DEV Community

Patch Verification Checklist for CVE-2026-65339 Across Apple Platforms

Patch Verification Checklist for CVE-2026-65339 Across Apple Platforms A remediation ticket for CVE-2026-65339 should not close because an update was offered. It should close when each device runs an Apple release that actually contains the Audio fix. The fix spans multiple platforms and an older-iOS backport, so evidence collection and version verification need more care than a single package deployment. Know the target set Apple documents the CVE in the security content for iOS 26.6.1 and iPadOS 26.6.1, macOS Tahoe 26.6.2, iOS 18.7.11 and iPadOS 18.7.11, macOS Sequoia 15.8, tvOS 27, watchOS 27, and visionOS 27. The 18.7.11 advisory covers older supported hardware, including iPhone XS, iPhone XS Max, iPhone XR, and iPad seventh generation. Create target groups per platform and branch. Do not collapse every iPhone or iPad into one group that expects the latest major version. A device that cannot upgrade to the newest release may still be remediated by the older-device backport. A managed Mac on Sequoia should be compared with 15.8 rather than a Tahoe-only baseline. Verify the patch artifact For the mobile branch, confirm the device reports iOS or iPadOS 26.6.1, or the applicable 18.7.11 build for an eligible model. For macOS, confirm Tahoe 26.6.2 or Sequoia 15.8 according to the installed line. For tvOS, watchOS, and visionOS, use the corresponding Apple security advisory and verify the listed release. A device management console can display the expected version while the endpoint remains offline or out of date. Validate stubborn records with a second source when possible. Record the collection time, the source of the version information, and the device identifier used to resolve the record. Account for rollout state Users can postpone installation, run out of storage, or interrupt a restart. A device may report that an update is available while the vulnerable version remains active. The remediation report should show the installed version, not only the desired policy assignment. Tickets for downloaded-but-not-installed updates should remain open. Track exceptions. A personally owned device with a management profile, a shared tablet, or a Mac used in a lab may need a different owner and verification path. A clear exception record is more useful than marking a device compliant without confirming its build. Validate the security condition The fix changed the identity check used in Apple's audio daemon. Apple describes the issue as an Audio logic flaw that may leak sensitive information. Meta Red Team X reported that a sandboxed app could record after permission denial and, on the analyzed build, hide app attribution and the orange indicator. After patching, a normal application should no longer be able to select the daemon identity through the vulnerable property path. Security teams do not need to reproduce the exploit on production devices. They can verify the vendor release, the installed build, and the expected application inventory. Avoid exploit tests on users' devices unless a controlled laboratory process explicitly authorizes them. Review detection data after deployment Check whether management and endpoint tools can distinguish operating system version, application installation, microphone permission, and audio process activity. If the tools cannot retain a suspect app's identity, improve collection before an incident occurs. A patched fleet still needs reasonable evidence when an application behaves unexpectedly. Do not use a broad ZoomEye product count as the remediation metric. The service returned 1,591,938 app="iOS" matches and zero CVE-indexed matches on November 15, 2026. Those observations provide external context only. The authoritative count is the internal population that runs a fixed build. Evidence to retain Keep the following with the closure record: The Apple advisory consulted for each platform. The scope and time of the device inventory query. The number of devices in scope and the number on fixed ...

Powered by Readybot.io

**DEV Community** ## Patch Verification Checklist for CVE-2026-65339 Across Apple …

DEV Community

Managing LXD from Python: Reemote's lxd module

LXD turns one Ubuntu server into a fleet of system containers and VMs, and Canonical's first-steps tutorial shows how easily you can drive it by hand. This article walks the same first steps from Python: Reemote is a library that manages LXD over sshd and the lxc client, plans before it applies, and hands you every result as data you can await, inspect, and test. The problem: configuration management with LXD on Ubuntu Ubuntu plus LXD is a compelling shape. One box, LXD snap-installed and initialized in two commands, and suddenly you have a fleet of containers that behave like lightweight virtual machines — full Linux init systems you can launch, constrain, snapshot and roll back in seconds. Unlike Docker's single-application containers, an LXD container runs a whole userland: systemd, apt, sshd, the lot. Canonical's first-steps tutorial covers the CLI moves: install and init, launch, exec, file push/pull, resource limits, snapshots. By hand, each is a one-liner. The pain starts when those moves need to live inside a program — provisioning a container per tenant from an API, rebuilding a staging fleet from a database query, gating a rollout on what actually changed, rolling back automatically when a health check fails. That is the gap: there is no serious configuration-management library you can import and point at an LXD host. Ansible can drive LXD, but it is a CLI-shaped tool — a YAML playbook interpreted by an executable you shell out to, with results you scrape from stdout. Reemote is that library, and lxd ships as a first-class namespace with facts, operations, and the same two-phase prepare-then-apply runner everything else uses. Why Python has real advantages over Ansible Ansible chose YAML because it is readable and does not require knowing a programming language. That is a fair trade when your team does not write Python. When it does — and most automation teams today do — the trade costs more than it saves: Your editor knows your code. lxd.instance and lxd.instance_snapshot are functions: go-to-definition, rename, autocomplete, and a type check that catches a typo before it reaches a production host. A misspelled YAML module name surfaces at runtime, in the middle of a rollout. No Jinja2 as a second language. A container's CPU limit, a device property, an image reference — in reemote each is just a Python expression, evaluated by the interpreter you already trust, not a {{ hostvars[...] }} string your IDE cannot see through. Composition is import, not directory layout. A fleet is a loop over a list of tenants; a reusable chunk of provisioning is a generator you import and call. No tasks/main.yml, defaults/main.yml, handlers/main.yml convention to memorise. The result is data, not stdout. If your application needs to know whether the snapshot was created, you read report.entries[i].changed — you do not regex-scrape a terminal or depend on callback plugins to reshape the log. Testing is pytest. A deploy function is a pure generator; you can inspect the requests it yields without touching a server. No molecule, no testinfra, no ansible-lint. await is your concurrency model. Hosts progress in parallel because the transport is asyncssh, not because a CLI forked worker processes you have to configure. The solution: Reemote is a library Reemote is a Python package (pip install reemote) with two properties that separate it from every CLI-shaped tool. Async end to end, on asyncssh The transport is asyncssh, and the entire execution path — connecting, gathering facts, sending commands, closing — is coroutines. You await a deploy and hosts progress in parallel; network waits overlap across hosts while each host's commands stay in order. Your deploy script remains a plain synchronous generator, so you never write async inside an operation. plans, report, hosts = await run(inventory, deploy)
run is the single entry point: it validates the inventory, opens connections lazily, plans and applies in two phases, then closes ev...

Powered by Readybot.io

**DEV Community** ## Managing LXD from Python: Reemote's `lxd` module …

DEV Community

Show DEV: Shoti — AI headshots and country-compliant passport photos from one selfie

Hi DEV! I'm Caglar, and this is a #showdev post — I'm the maker, so full disclosure: this is my own product. Shoti (shoti.app) turns one clear selfie into two kinds of useful photos: Professional AI headshots — business wardrobe, balanced studio lighting, natural expression control, with an identity-preserving finish (the goal is "polished you", not "a stranger with your name") Country-compliant passport / visa / ID photos — the specification (size, background, head position, DPI) is selected per country and document type, then enforced automatically What I think makes it interesting (the engineering) Every photo passes biometric validation (MediaPipe face landmarker with OpenCV fallbacks) before generation — a selfie where the face isn't detectable is rejected early instead of producing garbage Document photos are spec-driven: physical dimensions, aspect ratio, DPI, and head-height ratios come from per-country PhotoSpec configuration — no hardcoded sizes Print-ready PDF sheets for A4, US Letter, and 4×6 photo paper with crop guides, so you can print at home at 100% scale Impossible crops and undersized sheets are rejected with actionable errors rather than patched with white borders Segmentation runs on the alpha channel (a fun bug: using the photo's luminance as the mask made white hair vanish into the background — exactly the kind of thing that looks fine in tests and broken on real photos) Practical bits Free tier: 3 AI-enhanced headshots, no card required Larger one-time packs from $4.90 — no subscription Available in English, Türkçe, Deutsch, Français, Español Honest limitation: document rules vary by authority and change over time — Shoti shows the selected spec and known limitations, but you should always confirm current official guidance before submitting documents I built it solo on Django 6 + DRF / Celery / PostgreSQL with a Next.js 16 + TypeScript frontend, running on a small single-board tablet behind Cloudflare — a deliberately low-cost stack. Feedback very welcome — especially from anyone who has recently battled passport-photo requirements for a specific country. Which document specs should I add next?

Powered by Readybot.io

**DEV Community** ## Show DEV: Shoti — AI headshots and …

DEV Community

GitHub Actions composite action cannot access secrets directly

A GitHub Actions composite action cannot access secrets because the secrets context is not one of the names the expression parser recognizes inside an action.yml. The failure is a parse error rather than an empty value, so nothing runs at all. The runner builds the message in ParseException as the description, the raw token in quotes, then the position and expression when a token is available. What this error means The run fails immediately with an annotation naming a file, a line and a column, and no job appears in the run at all. The file named is the action's action.yml, which may live in another repository entirely, so the first place you look is the workflow that called it and the workflow is fine. The message quotes the offending name in single quotes and, when the parser has a token to point at, appends the position and the whole expression. ```Actions log, quoted from BrunoSobrino/TheMystic-Bot-MD#384 [error] thomasbnt/hello-to-news-contributions/v1.0/action.yml (Line: 14, Col: 23): Unrecognized named-value: 'secrets'

Common causes

The action.yml references secrets directly

The literal case. A composite step uses ${{ secrets.NPM_TOKEN }} in a run, an env or a with, having been written by copying a workflow step into an action. The name is valid in the file it came from and not in the file it landed in.

A step-level or job-level if tests a secret

The same message from a plain workflow. Guarding work on whether a secret is set is a reasonable thing to want, and if is the obvious place to put it, but secrets is not available in either if. This is the version that appears in repositories with no composite actions at all.

A secret is referenced in a key that does not accept it

Less common and harder to spot, because the same file uses secrets successfully a few lines away. Keys like runs-on, strategy and a job's environment do not accept the context, so one expression fails while its neighbors are fine.

The action was refactored from a reusable workflow

Reusable workflows do accept a secrets block, so moving one to a composite action moves code that was correct into a file where it is not. The diff looks like a simple relocation, which is why the error arrives as a surprise.

How to fix it

Declare the secret as an input on the action

Add an input to the action and read it through the inputs context. Composite actions do not receive INPUT_ environment variables automatically, so use the context rather than the variable.

action.yml (illustrative) name: publish inputs: npm-token: description: 'Token used to publish' required: true runs: using: composite steps: - shell: bash env: NODE_AUTH_TOKEN: ${{ inputs.npm-token }} run: npm publish Pass it explicitly at the call site The caller is the only place that can read the secret, so it hands it over as an ordinary input. The value remains masked in logs..github/workflows/release.yml (illustrative) uses: ./.github/actions/publish with: npm-token: ${{ secrets.NPM_TOKEN }} For an if condition, go through env Assign the secret to an environment variable at the job or step level, where the context is accepted, and test the variable in the condition. This is the supported shape for branching on whether a secret is configured..github/workflows/release.yml (illustrative) jobs: publish: runs-on: ubuntu-latest env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} steps: - if: env.NPM_TOKEN != '' run: npm publish

Consider a reusable workflow when there are many secrets

A reusable workflow accepts a secrets block and supports secrets: inherit, so it is the better shape when a unit of work genuinely needs several secrets. A composite action is the better shape when it needs one or two and should be usable from other repositories.

How to prevent it

  • Treat an action's inputs as its whole interface, and write every credential it needs into that list.
  • When moving st...

    Powered by Readybot.io
**DEV Community** ## GitHub Actions composite action cannot access secrets …

DEV Community

TokenBpe A editorial on how tokenizers can be sped up.

Introduction Every LLM pipeline starts with a tokenizer and almost no one thinks about it. The model gets the headlines, the tokenizer is quietly burning microseconds per token per request, forever. If you're serving millions of requests or just training on a corpus that doesn't fit in memory that "quietly" adds up. This post is a walk through TokenBpe, a BPE tokenizer that I've written from the ground up in C++20. It covers what the various components are, how each one works, what specific techniques give you a performance gain and why. All of this is based on the actual code (include/tokenizer/, src/, utils/) and the benchmark figures are from a properly audited 3 way comparison against OpenAI's tiktoken (Rust) and Hugging Face tokenizers. Headline numbers (Shakespeare corpus, 1.06 MB, GPT-2 vocabulary, 12 hardware threads, Intel i5-11260H): Operation TokenBpe tiktoken HF tokenizers Encode, 12T (zero-copy) 239.98 MB/s 45.83 MB/s 12.08 MB/s Encode, 1T 34.22 MB/s 11.55 MB/s 2.11 MB/s Decode, 1T (zero-copy) 341.01 MB/s 117.34 MB/s 24.93 MB/s ~5.2× tiktoken and ~19.9× HF on 12-thread encoding. And ~6.1× scaling from 1 to 12 threads natively. Let's open the hood. What a BPE tokenizer actually does BPE encoding has two phases: Pre-tokenisation: split the raw text into word-like pieces ("pre-tokens"). GPT-2's approach is to split on letter runs, digit runs, whitespace runs, contractions (i.e. 's, 're, 'll etc), punctuation runs and to fold the leading space into the next token (so " hello" is one pre-token rather than " " + "hello"). Merge based encoding: each pre-token is a sequence of single bytes. You repeatedly find the adjacent pair with the lowest merge rank and replace it with the merged token id, until no more merges can be applied. Decoding is trivial really: just map each token id to its bytes and concatenate. The naive implementation of this is fine. The real question is where the time goes and the answer, as ever, is everywhere, a little bit at a time. So TokenBpe attacks every layer: text ─▶ SWAR pre-tokenizer ─▶ dual-cursor split ─▶ per-thread cache ─▶ linked-list merge ─▶ token ids
with a persistent thread pool under the whole thing, a flat binary model format and a zero copy NumPy boundary on top. Component tour 1. The pre-tokenizer: SWAR instead of regex The problem is GPT-2's official tokenizer uses a regular expression to split the text. Regex engines are general purpose monsters backtracking, capture groups, Unicode classes all of which are completely unnecessary when you're just trying to determine if a run of bytes is letters or not. The technique. SWAR (SIMD Within A Register) does the 8 bytes per 64 bit word classification using pure integer arithmetic. No SSE/AVX intrinsics at all, it's all done in the 64 bit ALU. // swar.hpp (simplified)
// All 8 lanes are bytes; HIGH_BITS is OR'd in before subtracting
// min so no lane can borrow from its neighbour.
bool all_bytes_in_range(uint64_t v, uint8_t min, uint8_t max);
bool all_ascii_lower(uint64_t v); // [a-z]
bool all_ascii_upper(uint64_t v); // [A-Z]
bool all_ascii_letters(uint64_t v); // both, via v | 0x20
bool all_ascii_digits(uint64_t v); // [0-9]
bool all_bytes_equal(uint64_t v, uint8_t b);
The range check is written very carefully indeed: it rejects the high bit bytes first, then ORs in HIGH_BITS prior to subtracting min, so a lane can never borrow from its neighbour. load64 does an unaligned 64-bit load via memcpy which ends up as a single instruction. The splitter (pre_tokenizer.hpp) then scans: letter runs and digit runs 8 bytes at a time, scalar tail; space runs 8 bytes at a time with all_bytes_equal; contractions's, 't, 'm, 'd, 're, 've, 'll; punctuation runs (all other non-alphanumeric/non-space/non-apostrophe characters are grouped) And crucially next_token() returns a std::string_view into the original buffer. No pre-token is ever materialised as a string. The caller owns the text lifetime, the splitter just points into it. The speed pa...

Powered by Readybot.io

**DEV Community** ## TokenBpe A editorial on how tokenizers can …

DEV Community

SQLite on Device: A Safe Migration Path

The Situation Say you are a mobile app developer using SQLite on device for data storage. Your app has been using an older approach to manage its database, and you're looking to migrate to a newer, more efficient method without interrupting service. You've tried to update the database schema in the past, but it resulted in data corruption or app crashes, costing you a significant amount of time and user trust. The current approach is no longer scalable, and you're facing issues with data consistency and performance. Why It Happens SQLite on device is a file-based database, and updating its schema or migrating to a new approach can be challenging. The main issue is that the database is locked for writing when the app is using it, making it difficult to perform updates without interrupting service. Additionally, SQLite's transactional nature means that any errors during the migration process can leave the database in an inconsistent state. The Fix, Step by Step To migrate your SQLite database safely, follow these steps: Create a backup: Before making any changes to the database, create a backup of the current database file. This ensures that you can recover your data in case something goes wrong during the migration process. Use a temporary database: Create a temporary database with the new schema or approach. This allows you to test and validate the migration process without affecting the live database. Migrate data incrementally: Write a script to migrate the data from the old database to the new one in small chunks. This approach helps to avoid locking the database for an extended period and reduces the risk of data corruption. Validate data consistency: After each chunk of data is migrated, validate the data consistency to ensure that the migration process is working correctly. Switch to the new database: Once the migration is complete, switch the app to use the new database. This can be done by updating the database file path or by using a fallback mechanism to switch between the old and new databases. What It Costs, and When Not to Do It The safe migration path described above requires additional development time and resources to implement. However, it ensures that the migration process is done without interrupting service and minimizes the risk of data corruption or app crashes. This approach is particularly useful when working with large datasets or when the app has a high volume of user traffic. On the other hand, if the app has a small user base or a simple database schema, a more straightforward migration approach might be sufficient. At Your Level Starting out: Focus on understanding the basics of SQLite and database migration. Practice migrating a simple database schema to get a feel for the process. Working engineer: Consider using a library or framework that provides a safe migration path for SQLite, such as React Native's react-native-sqlite-storage. Implement the incremental migration approach to minimize downtime and ensure data consistency. Senior or staff: Evaluate the trade-offs between different migration approaches and consider factors such as data consistency, performance, and scalability. Develop a customized migration plan that suits your app's specific needs. Lead or director: Allocate resources and prioritize the migration effort based on the app's business requirements and user needs. Ensure that the migration plan is thoroughly tested and validated before deployment. In the Interview When discussing SQLite migration in an interview, the interviewer might ask: "How would you migrate a large SQLite database to a new schema without interrupting service?" A weak answer might focus on simply updating the database schema without considering the potential risks and consequences. A strong answer would describe the incremental migration approach, highlighting the importance of data consistency, performance, and scalability. The interviewer might follow up with questions such as: "How would you handle err...

Powered by Readybot.io

**DEV Community** ## SQLite on Device: A Safe Migration Path …

DEV Community

Git Security Release: 6 Vulnerabilities and CVE-2026-111760

Last updated: 15 November 2026. This is a recent security release; see theupdate logand check the Git announcement for changes. On 10 November 2026 the Git project released fixes for six vulnerabilities. One of them is the kind that makes developers nervous: cloning a malicious repository with git clone --recurse-submodules can lead to attacker-controlled code running on your machine. The other five are a mix of file-write, memory-corruption and denial-of-service bugs that matter most to servers and tools that handle untrusted repositories. This article walks through all six, says which ones you should worry about, shows how to check your Git version (with a script I tested), and lists what you can do until you have patched. A caution up front: the Git announcement gives no severity scores, and I could not run the exploits, so the severity talk below is based on what the maintainers wrote. The short version Fixed in: Git 2.56.1 and the maintenance releases 2.55.1, 2.54.1, 2.53.1, 2.52.1, 2.51.3 and 2.50.2. Affected: per the announcement, all versions back to v2.27.0. Versions older than 2.50 are no longer serviced upstream. The serious one: CVE-2026-111760, code execution during a recursive clone of an attacker-controlled repository. Exploited in the wild: none of the coverage I read reports it. Action: update Git, and update every tool that bundles its own copy of Git. The six vulnerabilities at a glance CVE What happens Triggered by Impact (per the announcement) CVE-2026-111760 Integer truncation in path handling lets a crafted path be written as .git git clone --recurse-submodules of an attacker's repository Remote code execution CVE-2026-113408 Unvalidated HEAD symref from a server (protocol v2) with .. segments Cloning from a malicious server, then a later operation like git commit A ref file written outside the refs directory CVE-2026-114467 More than two GPG signatures overflow a fixed array in remove_signature() Merging a tag whose message has more than two PGP signatures Stack corruption: denial of service, a risk for forges CVE-2026-114469 Binary patch size check looks only at the compressed size Applying an attacker's binary patch (for example git apply) Out-of-bounds read and crash CVE-2026-105437 Size overflow in the three-way merge buffer, with many conflicts and a large conflict-marker-size Any textual three-way merge: merge, rebase, cherry-pick, merge-tree Heap buffer overflow; on forges that read attributes from trees, a risk for anyone who can push and request a merge CVE-2026-114466 fast-import features rewrite-submodules-from/to and export-pack-edges access arbitrary files Feeding an untrusted fast-import stream Arbitrary file read (content can leak in error messages) and append CVE-2026-111760: how a clone can run code The Git maintainers describe the bug like this. Git stores the length of a path name in a 32-bit field, but computes it as a larger type. A path of about 232 bytes makes the stored length wrap around to 4. Path validation looks at the full, long name and finds nothing wrong. Checkout, however, writes only the first 4 bytes, and those 4 bytes can be .git. If the entry is a symlink, it can replace the .git file of a submodule. Normally a submodule's .git is a small file that points to the real repository directory. Pointing it at a directory the attacker controls, one that holds hooks or configuration, means Git will later run attacker-specified code. Git refuses to replace a .git directory with a symlink, but a freshly cloned submodule's .git is a file, and The Hacker News notes that this check does not block that case. Requirements, per the announcement: You clone an attacker-controlled repository with --recurse-submodules. Such a repository fails the LARGE_PATHNAME and TREE_NOT_SORTED fsck checks, so major forges would be expected to refuse a push of it. But a malicious .gitmodules in a normal-looking repository that points at one is enough. That second point is the practical risk. The ...

Powered by Readybot.io

**DEV Community** ## Git Security Release: 6 Vulnerabilities and CVE-2026-111760 …

DEV Community

Show your working: why an online calculator should print its formula

Most online calculators give you one number and nothing else. You type the area of a room and get "53 tiles". If you want to know where 53 came from, you are on your own. We built Calc around the opposite idea: the result is only half of the answer. The other half is the working. The formula with your numbers in it Under every result, the page prints the formula with your values already substituted. For tiles it looks like this: 12 m² × 1.10 ÷ 0.25 m² = 52.8, round up to the next whole tile. That one line answers the questions people actually ask. Where did the 10% go? Why 53 and not 52? Is the tile area right? Anyone can check it with a phone calculator. Assumptions as a list, not a footnote A calculation is only as good as its assumptions. Calc shows them as a list next to the result: the waste margin, the rounding rule, what the calculator does not account for. If a number depends on a rate or a standard, the constant has a source and the date it was checked. If we cannot back a number with a source, the field stays empty and asks for your own value instead of inventing a default. "I don't know this value" The hardest part of a calculator is often not the maths but the input. Next to fields where a number is hard to know, there is a card that tells you where to find it: the device spec sheet, your contract, a label, or another calculator. Nothing leaves the browser while you calculate The calculation runs in the browser. The only exception is the "calculation receipt" link: it writes the values into the address so you can send the whole scenario to a colleague or come back to it later. Because the values are in the address, the page says plainly that they end up in the server log when someone opens the link. Checked outside the code Each formula is checked against test cases by a separate calculation that does not call the site's code. If the two disagree, the calculator is wrong, not the test. That is the whole philosophy: no ads inside the result, no "AI estimates" where arithmetic will do, and a formula you can redo by hand. Calc has calculators for energy and backup power, money, small business and e-commerce, home renovation, cars, shipping and dates. They are available in Ukrainian, and most of them also in English, Polish, German and French: https://calc.uk.app/en/

Powered by Readybot.io

**DEV Community** ## Show your working: why an online calculator …

DEV Community

Turning a Spare Mini PC into an Always-On Claude Code Machine (Remote Control + tmux + cron)

Hi, I'm Shu. I'm building Memotori, an on-device transcription and summary app, for both Mac and Windows. Almost all of the code is written by AI (Claude). To make it easier to hand that work off at any time, I installed Ubuntu on a spare mini PC (ASUS VC65) and turned it into a dedicated, always-on Claude Code machine. Now I can start Claude Code sessions against the Memotori folder on that box from the Claude mobile app or from a browser (claude.ai/code), at any time. I can ask it to fix a bug while I'm out and review the diff when I get home. The key feature is Claude Code's Remote Control, which is available on the Pro and Max plans too. This post walks through: Installing Claude Code and logging in Starting Remote Control (and choosing a spawn mode) Keeping it running with tmux Starting it automatically at boot with cron Setup Machine: ASUS VC65 mini PC OS: Ubuntu Desktop Claude Code (CLI) tmux cron (@reboot) Remote Control vs. similar options Option What it does Plans Remote Control Drive Claude Code sessions running on your own machine from your phone or the web Includes Pro / Max Claude desktop app Lets claude.ai chats reach your PC's folders and screen (Windows / Mac) All plans Self-hosted runner Registers your own servers as the execution target for an organization's cloud sessions Team / Enterprise For a personal always-on box, Remote Control is the simplest choice. The CLI and the desktop app are not interchangeable: CLI (Remote Control): remote shell, file, and coding work. Runs headless. Desktop app: connects claude.ai chats to your PC, including computer use (seeing and operating the screen). If your work is mostly coding, the CLI alone is enough. You can also run both. How Memotori's folders are laid out The Mac and Windows versions of Memotori live in separate git repositories. Above them sits a shared workspace (also a git repository) that holds the specs both versions follow and the working rules for the AI (CLAUDE.md). Memotori/
├── Memotori-mac/ ← the Mac repository
├── Memotori-win/ ← the Windows repository
├── spec/ ← specs shared by both versions
├── promo/ ← promotion notes and drafts (including this post)
└── CLAUDE.md ← working rules for the AI
I run Remote Control in that shared folder. Sessions started from my phone read CLAUDE.md too, so rules like "explain the plan and get my OK before changing code" still apply. I wrote about this shared spec folder in detail in an earlier dev log. 1. Install Claude Code curl -fsSL https://claude.ai/install.sh | bash
claude --version
If claude isn't found, open a new terminal or run source ~/.bashrc. Then log in: claude
Follow the browser login, then type /exit. 2. Start Remote Control Go to your project folder and start it: cd ~/develop/projects/myapp
claude remote-control
The first time, it asks: Spawn mode for this project:
[1] same-dir — sessions share the current directory (default)
[2] worktree — each session gets an isolated git worktree
This setting decides which directory each new session works in when you start one from your phone or the web. Mode Behavior Best for same-dir All sessions share the current directory One task at a time, or folders without git worktree Each session gets its own git worktree (an isolated working copy) Running several sessions in parallel Pass the mode as a flag so you aren't prompted again: claude remote-control --spawn=worktree
Notes on worktree mode The folder must be a git repository with at least one commit: git init
git add . && git commit -m "init"
Each session works on its own branch, so you (or Claude) merge the results. Untracked files such as .env are not copied into worktrees. Clean up occasionally: check with git worktree list, then run git worktree prune. 3. Keep it running with tmux Run it inside tmux so it survives SSH disconnects: sudo apt install -y tmux
tmux new -s claude
claude remote-control --spawn=worktree
Detach with Ctrl+B then D. Reattach with tmux at...

Powered by Readybot.io

**DEV Community** ## Turning a Spare Mini PC into an …

DEV Community

JavaScript noindex Removal: Fix the Initial HTML Before Trusting the DOM

If a public page's initial HTML contains a noindex robots tag, do not rely on JavaScript to remove it later. Fix the initial response for the pages intended to be indexed, then verify the rendered document and the route states separately. A clean browser DOM is useful evidence, but it does not erase an earlier directive in a different processing path. For a SaaS team, this can hide a clear buyer answer behind a template mistake. The product page may describe a real capability, load normally and pass a visual review. The writer keeps improving the answer while the original response still tells a search engine to exclude it. Trace the directive before expanding the copy. Establish the intended policy for the exact URL Start by naming the public page and deciding whether it is supposed to be eligible for indexing. Record the full URL, including any query string relevant to the problem. Do not begin by deleting every noindex tag across the application. A public product explanation and a private account screen have different purposes, even when they share a frontend shell. Write down the reason for the intended policy. A product guide may answer a buyer question that the company wants people to find. A temporary internal preview may intentionally stay out of search. That distinction belongs in the diagnosis, so a developer does not “repair” a deliberate publishing choice while trying to help marketing. Keep access protection separate from this editorial policy. Removing an indexing directive is not a reason to publish private content or weaken authentication. Work on a public, approved page and preserve the existing protection around other routes. If the intended audience is unclear, resolve that specific question with the page owner before changing the response. Inspect the initial HTML before the rendered head Google's JavaScript SEO documentation warns that encountering noindex may cause Google to skip rendering and JavaScript execution. Removing or changing that tag with JavaScript may therefore fail to have the intended effect. Google advises against putting noindex in the original code when the page should be indexed. Use that documented behavior to choose the first diagnostic step. Capture the response for the exact public URL using your normal development tools. Search the returned HTML for robots directives and retain the relevant portion of the head. Record the response conditions and release version alongside the finding so another developer can reproduce it. Then inspect the rendered head in a fresh browser load. Compare what arrived with what the application eventually produced. If the initial response says noindex and the later document does not, that difference is the issue to investigate. Avoid calling the later DOM the “real page” in a way that makes the earlier response disappear from the evidence. Trace which layer supplied the directive A fictional product, Fieldglass Notes, uses one application shell for public guides and account pages. Its public guide about approval history receives a default noindex tag. After the page loads, a component removes the tag for public content. This is a hypothetical failure pattern, not an observed behavior of a real service. The fix begins with ownership. Ask which part of the publishing path creates the initial head: a template, a route-specific server response, a static export or another documented layer in that application. Follow the actual output back to its source. Do not assume the page component that removes the tag also owns the response that added it. Propose a narrow change: public guide URLs receive their intended initial indexing policy, while account and preview routes keep theirs. Review a representative URL from each affected class. The important regression question is not merely whether one guide lost noindex. It is whether the implementation now applies the right policy to each type of page it serves. Verify direct loads and client-side navigation Test a d...

Powered by Readybot.io

**DEV Community** ## JavaScript noindex Removal: Fix the Initial HTML …

DEV Community

Building a Bench of Senior Developers Without Sitting on Idle Cost

Every software services firm lives with the same tension. When a new client says yes, they want a strong team next week, not in two months. But every senior developer sitting on the bench waiting for that yes is pure cost. Carry too much bench and margin disappears. Carry too little and you either lose the deal or staff it with whoever happens to be free. Earlier in my career, as a partner, I helped grow a software consulting company from about $6M to $60M in under three years, and later I founded and ran a fully remote consultancy. At that pace of growth, bench strategy is not a side topic. It decides whether you can say yes to good work, and whether saying yes leaves you any profit. Here is how I think about building a bench of senior developers without sitting on idle cost. Why the Bench Is a Strategy Problem, Not a Staffing Problem Most firms treat the bench as an accident. People roll off a project, nobody has lined up their next one, and they wait. That kind of bench is expensive and demoralizing, because nobody chose it. A healthy bench is deliberate. You decide how much ready capacity the business needs to win and start work quickly, you decide which skills belong on it, and you decide what bench time is for. Once you make those choices on purpose, the bench stops being a leak and becomes a sales and delivery asset. Size the Bench From Your Pipeline, Not From Hope The right bench size comes from the sales pipeline, not from a gut feeling. Look at deals by stage and likely start date. A signed deal starting in two weeks needs named people now. A deal still in discovery needs a rough plan, not idle engineers. Plan for the roles that are hardest to find. Mid-level developers are usually easier to source quickly. Senior engineers and tech leads who can face a client on day one are not. Those are the people worth keeping ready. Review it every week. Pipeline and roll-off dates change constantly. A short weekly meeting between sales and delivery, looking at who rolls off when and which deals are close, prevents most bench surprises. Make Bench Time Productive Bench time does not have to be wasted time. The firms that handle it well give senior people real work between client projects. Internal tools and accelerators. Starter templates, deployment scripts, test harnesses and reference architectures make every future engagement start faster. Senior people on the bench are the right people to build them. Pre-sales support. A senior engineer who joins discovery calls, writes technical approaches and estimates scope makes proposals sharper and wins more of them. It also means the person who scoped the work can lead it. Shadowing and overlap. Placing a bench engineer on an active project for a few weeks, at reduced or no cost to the client, builds context before a planned rotation and makes future handoffs far smoother. Skill building with a target. Learning should point at demand you actually see in the pipeline, not at whatever is trending. Use a Flexible Outer Ring You do not need to carry every possible skill as full-time bench. A small core of senior people, surrounded by a trusted outer ring, covers far more ground. That outer ring can include vetted contractors you have worked with before, partner firms with complementary skills, and alumni who left on good terms and would come back for the right project. The key word is vetted. The outer ring only works if you already know the quality of the work, so you are not running a fresh hiring process every time a deal closes. Running a fully remote consultancy made this much easier. When you hire across regions and time zones, the pool of strong senior people is larger, and you can match people to projects instead of to an office. Watch the Right Numbers A few simple measures tell you whether the bench is healthy. Utilization of senior people over a quarter, not a week. Short dips are normal. A senior engineer on the bench for months is a signal that either sales or staffing h...

Powered by Readybot.io

**DEV Community** ## Building a Bench of Senior Developers Without …

DEV Community

Building a Consistent AI Art Pipeline for a Tarot Content Site

The problem We run a small tarot reading site and needed a steady stream of original illustration for articles and social posts. Stock fantasy art is either overused, wrong in mood, or licensed awkwardly. Hiring an illustrator for every post didn't scale. The obvious answer was AI image generation — but anyone who has tried it for a brand knows the real problem isn't generating a pretty picture, it's generating the same pretty picture, on demand, with a consistent palette and iconography. This post walks through the pipeline we settled on and what we learned about controlling style. Defining a reproducible style Our anchor visual is what we call the "Moon Oracle" composition: a robed figure with long violet hair seated among clouds, holding a glowing astral sphere, framed by a row of moon phases across the top, lit candles in brass holders, and amethyst / quartz crystals at the base. Deep purples and gold carry the whole palette. To make that reproducible we broke it into a controlled prompt schema rather than freeform prose: Subject block: figure, pose, key prop (the star-and-orbit sphere), expression direction Iconography block: moon phase arc, constellation embroidery on the robe, crystal clusters, candlesticks Style block: digital painting, luminous cell-shaded rendering, nebula backdrop, symmetric composition Color block: dominant violet/purple, gold accent stars, warm candle glow as the only warm light source One useful trick: treat the color block as non-negotiable and the subject block as variable. That's what lets us generate variations — a card-specific figure, a different season backdrop — while everything still reads as the same brand on a feed thumbnail. Generation and refinement loop The workflow is roughly: Draft with an open diffusion model, CFG around 7, heavy negative prompts (extra fingers, cluttered borders, watermark artifacts). Generate 8–16 candidates per concept at low resolution. Hand-select candidates on composition first, detail second — silhouettes and symmetry read at thumbnail size, small details don't. Upscale the survivors, then do targeted inpainting on hands, faces, and the sphere's orbit lines, which are the three things diffusion models reliably get wrong. The moon phase arc is a good example of why inpainting matters. Models can paint a moon, but a correctly ordered sequence — waxing crescents through full moon to waning — almost never comes out right from the base prompt. We composite or inpaint that band explicitly. Results and tradeoffs What worked: throughput went from days per illustration to hours, and the consistent violet-and-gold look gives the site a recognizable visual identity that plain stock art never did. What we gave up: Hands and jewelry still need manual fixes on most finals. Budget for an inpainting pass every time. Symbolic accuracy is on you. If a moon phase arc is wrong, no model will care. Astrological and tarot imagery demands a human review step for correctness, not just aesthetics. Over-uniformity risk. Lean too hard on the style block and every image looks like a recolor of the last. We keep the subject block loose enough that compositions stay varied. The biggest lesson: treat AI art like code — define a spec (the prompt schema), automate the boring parts (batch generation, upscaling), and keep a human review gate for anything that claims to be symbolically meaningful. If you want to see the output of this pipeline in the wild, the illustrations live on our site: https://oracleblooms.top AI-generated 'Moon Oracle' illustration: a violet-haired robed figure seated among clouds holding a glowing star-orbit sphere, with a seven-phase moon arc overhead, constellation embroidery on the gow

Powered by Readybot.io

**DEV Community** ## Building a Consistent AI Art Pipeline for …

DEV Community

How to Build an Offline, On-Device Voice-to-Voice AI Agent in React Native

Short answer: To build an on-device Voice-to-Voice AI Agent in React Native, chain five stages that all run on the phone: a wake word detector, optional speaker verification, speech-to-text (STT), a small local LLM that calls your app's tools, and on-device text-to-speech (TTS). Stream the LLM's reply into TTS sentence by sentence, and hand the microphone carefully between stages. This tutorial does exactly that with the open-source DaVoice React Native example app and three npm packages from the DaVoice Voice AI Agent SDK: react-native-wakeword, react-native-davoice and react-native-davoice-llm. The result is an agent that listens for "Hey Coach", understands "open the rear right window", runs a real function in your app, and answers out loud: "I opened the rear right window." No cloud LLM, no per-token bill, and no audio leaving the device during the voice loop. What "token-free" means here. A local LLM still processes tokens. "Token-free" means you pay no cloud LLM inference token fees, because inference runs on the user's phone. DaVoice SDKs are licensed separately; evaluation and non-commercial proofs of concept are free, and production use needs a commercial license. Used in production The same on-device voice stack already runs in shipped products: Verbali (MaTalk AI, an AAC app for non-verbal children) moved its wake word, speech-to-text and text-to-speech fully on-device with DaVoice, so no audio leaves the tablet and voice keeps working without internet. In Verbali's own write-up, DaVoice reached 97.65% wake word accuracy with zero false positives observed on their test setup, versus about 75% for the other engines they evaluated. In their words: "We have no affiliation with DaVoice beyond being a happy customer." Volkswagen chose DaVoice for OTTO, its generative AI car assistant launched in Brazil (customer story · video). LunaFit built its entire voice-to-voice AI agent on DaVoice, including speaker-aware speech-to-text that hears its user in a noisy gym (video). The interruption pattern in section 8 comes from LunaFit's workout mode. Table of contents What you will build Architecture: wake word → STT → local LLM → TTS What is really offline (and what is not) Prerequisites Run the DaVoice example app Implementation, stage by stage Add the agent to your existing React Native app Advanced: speaker verification, voice cloning, interruptions Testing and troubleshooting FAQ Resources 1. What you will build A hands-free assistant inside a React Native app (iOS and Android) that: listens continuously for a custom wake word ("Hey Coach" in the example); optionally checks that the speaker is the enrolled owner; transcribes the request in real time; lets a 0.6B-parameter on-device model decide whether to call one of your app's tools, ask a clarifying question, decline, or hand the request to an (optional) remote model; speaks the result with an on-device neural voice, which can be a cloned voice. The example ships two demos you can try immediately: Demo Model (downloaded on first use) Tools Car demo DaVoice Automotive Agent 0.6B (~380 MB GGUF) 22 simulated car tools: windows, doors, locks, climate, navigation, radio Calories demo DaVoice Personal Agent 0.6B (~380 MB GGUF) calorie tracker, personal memory, web search Honest status from the repo: the automotive model is labeled a test model, still in training. On DaVoice's frozen automotive test set it gets about 82% of Brazilian Portuguese commands exactly right (tool + arguments) and picks the right tool about 90% of the time. Every car action is simulated and checked by app-side guards. Treat it as a preview of the voice pipeline, not final accuracy. 2. Architecture: how a Voice-to-Voice AI Agent works on-device ┌────────────────────────── on the phone ──────────────────────────┐
microphone ──► │ Wake word ──► Speaker ──► Speech-to- ──► Local LLM │
│ "Hey Coach" verification text (STT) Agent │
...

Powered by Readybot.io

**DEV Community** ## How to Build an Offline, On-Device Voice-to-Voice …

DEV Community

GitHub Actions unexpected symbol in a ${{ }} expression

GitHub Actions unexpected symbol in a ${{ }} expression means the expression parser reached a character it has no rule for and stopped. The YAML is fine, the workflow never starts, and the message hands you the offending token, its position and the expression it came from, which is usually enough to fix it without running anything. Four fields in one line. The file position points at the YAML key, the position inside the expression points at the character that stopped the parser. What this error means The workflow does not run at all. There is no job, no log and no annotation on a step, because the file was rejected before any of that existed; GitHub shows it as an invalid workflow file against the line in the editor and in the commit status. The message has four parts, and each one is doing work. It names the file and the line and column of the YAML key that holds the expression. It names the kind of problem. It gives you the raw token it choked on, in quotes. Then it gives you a position inside the expression and the expression text itself, so you can count across to the character it means. The position is an index into the expression, not into the file, which is the part that sends people to the wrong place first. ```Invalid workflow file, quoted from SciKit-Surgery/sustainable-pkg-stats#24 The workflow is not valid. .github/workflows/tests.yml (Line: 33, Col: 27): Unexpected symbol: '-'. Located at position 7 within expression: '{{ ' -

Common causes

A string literal in double quotes

The single most common one, because every other language in the repository accepts them. The parser reads the opening double quote as a symbol with no rule attached and reports it immediately, usually with the quote and the first word of the string as the raw token.

An operator the expression language does not have

Arithmetic, a single & or |, a bare =, or a ternary. The operator table has twelve entries and none of those are in it. In our experience this arrives when logic that belongs in a run: step is being squeezed into a condition.

A stray or mismatched brace

A ${{ opened twice, a }} that closes the wrong one, or a quoted YAML scalar that swallowed part of the delimiter. The parser then receives a fragment, and the token it reports is whatever punctuation was left dangling, which is why the position often points somewhere that looks fine.

Literal text glued to an expression

Concatenating by writing text next to ${{ ... }} inside the same expression rather than outside it. The parser reads the text as a symbol. The format() function exists for this, and it keeps the quoting in one place instead of three.

How to fix it

Swap the double quotes for single quotes

  1. Find the token the message printed in quotes and locate it in the expression text on the same line.
  2. Replace the surrounding double quotes with single quotes, and double a literal single quote to escape it.
  3. Leave YAML quoting alone: this is about quotes inside the expression, not around the whole value.

.github/workflows/ci.yml (illustrative) if: ${{ github.event.head_commit.message == 'it''s fine' }} Rewrite the logic with operators that exist Use && and ||, and reach for contains(), startsWith() or format() rather than inventing syntax. Anything that needs arithmetic or a ternary belongs in a step, where the shell can do it and the result can come back as a step output..github/workflows/ci.yml (illustrative) if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}

Count the delimiters before you count the characters

When the expression printed at the end of the message does not look like the one you wrote, the problem is the delimiters rather than the contents. Check that each ${{ has one }}, and that a value wrapped in YAML quotes has not absorbed part of either.

Lint the expressions, not just the YAML

A YAML validator will pass a file that the expression par...

Powered by Readybot.io

**DEV Community** ## GitHub Actions unexpected symbol in a ${{ …

DEV Community

One Hand-Typed Number Made My Drug Valuation 4.5x Too High

My drug valuation desk said a Phase 2 cancer drug was worth $1.29 billion. After two small fixes, the same drug with the same sales forecast was worth $283.5 million. Neither bug crashed anything. The screen looked great the whole time. That is the kind of bug this post is about. The setup The desk is called Somota. My company Codebat finished the first version in March 2025, under the name PharmFlow. It values a drug with risk-adjusted NPV (rNPV): forecast the cash flows, then weight them by the chance the drug actually reaches the market. This week my AI coding assistant was tracing where every input on the screen came from. Most inputs had a source. One did not. Bug 1: a success rate with no source The probability of success for the cancer asset was 30%. I had typed it in by hand, a long time ago, and nothing on the screen said so. The best public benchmark I know of is the BIO, Informa Pharma Intelligence and QLS report, Clinical Development Success Rates 2011-2020 (February 2021). It gives the odds of passing each phase transition, by therapy area. For oncology: Transition Success rate Phase 1 to Phase 2 48.8% Phase 2 to Phase 3 24.6% Phase 3 to filing 47.7% Filing to approval 92.0% A drug in Phase 2 still has three of those doors in front of it. The chance it gets through all of them is the product: BENCH = {
"oncology": {"p12": 0.488, "p23": 0.246, "p3n": 0.477, "pna": 0.920},
}
STAGE_STEPS = {
"Phase 1": ("p12", "p23", "p3n", "pna"),
"Phase 2": ("p23", "p3n", "pna"),
"Phase 3": ("p3n", "pna"),
}

def bench_pos(asset):
"""Likelihood of approval from the asset's current phase."""
b, v = BENCH[asset["area"]], 1.0
for k in STAGE_STEPS[asset["stage"]]:
v *= b[k]
return round(v, 4)

bench_pos({"area": "oncology", "stage": "Phase 2"}) # 0.108
So 10.8%, about one in nine. The model had 30%. Bug 2: costs multiplied by the same probability The second bug was in how development costs were risk-adjusted. The old code did roughly this: risked_dev_cost = development_cost * launch_probability
That treats every dollar of trial spending as if it only happens when the drug succeeds. It is like a college saying you will probably drop out, so it will only charge you 30% of the tuition. Real trial bills do not work that way. You pay for the trial you are running now in full. You only pay for the next trial if this one works. So each year of spending gets its own weight: the chance of still being in development that year. def cost_weights(asset):
"""Development cost is spent over 3 years. Year 1 is the current phase,
paid for sure. Each later year is paid only if the trials before it passed."""
b, s = BENCH[asset["area"]], asset["stage"]
if s == "Phase 1":
w = [1.0, b["p12"], b["p12"] * b["p23"]]
elif s == "Phase 2":
w = [1.0, b["p23"], b["p23"] * b["p3n"]]
else: # Phase 3 runs two of the three years, then filing
w = [1.0, 1.0, b["p3n"]]
return [round(x, 4) for x in w]

cost_weights({"area": "oncology", "stage": "Phase 2"}) # [1.0, 0.246, 0.1173]
The result Same asset: $1.5B peak sales, 12.5% discount rate, $600M development cost to go, three years to launch. Old Fixed Probability of success 30%, hand-typed 10.8%, sourced Development cost weights x 0.30 every year 1.0, 0.246, 0.117 Risk-adjusted NPV $1,292.2M $283.5M Then I ran a tornado chart: push each input up and down and see how far the value moves. Success rate, plus or minus 10 points: $755.8M swing Discount rate: $216.1M swing Peak sales: $206.6M swing The one input nobody had sourced was the one that moved the price the most. A check you can copy This works on any model, not only drug valuations. Put a source label next to every input on the screen: public data, computed from public data, or typed by hand. Run a sensitivity check and sort the inputs by how far they move the output. Look at the top of that list first. If the biggest driver is typed by hand, go find a source before...

Powered by Readybot.io

**DEV Community** ## One Hand-Typed Number Made My Drug Valuation …

DEV Community

Put yourself in a viral clip with one photo — no prompt

I shipped Genjutsu AI — a small web tool for a narrow job: take a video people already share, swap the people for photos you pick, and keep the original take. No prompt. No storyboard. You upload the clip, the app splits it into scenes and lists who’s in frame. You add one still photo per person you want to change. Everyone else, the background, and the props stay as filmed. What stays the same Shot order, timing, and camera Cuts and pacing Original audio What changes Only the people you assign a photo to. One photo = one identity across every shot they appear in. That can be you, a friend, a pet, a product, or a character. How it works Upload mp4, mov, or webm (up to 80MB), or pick a template (Hotel Lobby, Tiny Desk AI, Night Dance, Bulin 47, and others). Scenes and faces are detected automatically. Attach a photo for each face you want to replace. Generate. Download is 720p with the source audio. Under the hood it’s a Next.js app with a Python cast pipeline for scene/face work, Prisma + Postgres for accounts and credits, object storage for uploads, and a video model API for the swap. Happy to talk stack details in the comments if anyone’s curious. Limits worth knowing Rule Detail Scene length A single scene cannot exceed 15 seconds (shorter clips work best). Resolution Output is 720p. Credits 10 credits per 15 seconds; failed runs refund credits. Sign-in Google — generations stay on your account. Credits don’t expire. First 15-second video on Starter is $6.90, then $9.99 (10 credits). Plus and Creator packs are on the pricing page. Not the same as “Higgsfield Genjutsu” Genjutsu AI is a separate product: you bring the video, and you attach one photo per person you want in the frame. Same filmed motion, different faces. Try it 👉 ai-genjutsu.com A clip under 15 seconds is enough for a first run. I’d love feedback on scene splits, face consistency, and whether the credit pricing feels fair — especially from folks who ship short-form or meme clips. Disclosure: I built this. It’s a paid credit product, not open source.

Powered by Readybot.io

**DEV Community** ## Put yourself in a viral clip with …