10 interesting stories served every morning and every evening.




1 1,876 shares, 71 trendiness

Israeli Soldiers Killed Gaza Aid Workers at Point Blank Range in 2025 Massacre

A minute-by-minute re­con­struc­tion of the mas­sacre by Earshot and Forensic Architecture found Israeli sol­diers fired over 900 bul­lets at the aid work­ers, killing 15. Drop Site is a reader-funded, in­de­pen­dent news out­let. Without your sup­port, we can’t op­er­ate. Please con­sider be­com­ing a paid sub­scriber or mak­ing a 501(c)(3) tax-de­ductible do­na­tion to­day.Fu­ner­als held at Nasser Hospital in Khan Yunis, in south­ern Gaza, for aid work­ers from the Palestinian Red Crescent who were killed in an Israeli at­tack in Tel al-Sul­tan. March 31, 2025. Photo by Hani Alshaer/Anadolu via Getty Images.Israeli sol­diers fired nearly a thou­sand bul­lets dur­ing the mas­sacre of 15 Palestinian aid work­ers in south­ern Gaza on March 23, 2025—with at least eight shots fired at point blank range—ac­cord­ing to a joint in­ves­ti­ga­tion by the in­de­pen­dent re­search groups Earshot and Forensic Architecture. The re­port, based on eye­wit­ness tes­ti­mony and au­dio and vi­sual analy­sis, shows that a num­ber of aid work­ers were ex­e­cuted and that at least one was shot from as close as one me­ter away.In Tel al-Sul­tan that day, Israel killed eight aid work­ers with the Palestine Red Crescent Society (PRCS), six from Palestinian Civil Defense, and a UN re­lief agency staffer. It im­me­di­ately trig­gered in­ter­na­tional con­dem­na­tion and was de­scribed as one of the dark­est mo­ments” of the war by PRCS.The Israeli mil­i­tary was forced to change its story about the am­bush sev­eral times, fol­low­ing the dis­cov­ery of the bod­ies in a mass grave, along with their flat­tened ve­hi­cles, and the emer­gence of video and au­dio record­ings taken by the aid work­ers. An in­ter­nal mil­i­tary in­quiry ul­ti­mately did not rec­om­mend any crim­i­nal ac­tion against the army units re­spon­si­ble for the in­ci­dent.The re­port by Earshot and Forensic Architecture re­con­structs, minute by minute, how the mas­sacre un­folded. Using video and au­dio record­ings from the in­ci­dent, open-source im­ages and videos, satel­lite im­agery, so­cial me­dia posts, and other ma­te­ri­als, as well as in-depth in­ter­views with two sur­vivors of the at­tack, the groups were able to dig­i­tally re­con­struct the scene and events sur­round­ing the mas­sacre.Is­raeli sol­diers am­bushed and sub­jected Palestinian aid work­ers to a near con­tin­u­ous as­sault for over two hours even though the sol­diers never came un­der fire.At least 910 gun­shots were doc­u­mented across three video and au­dio record­ings of the at­tack. The vast ma­jor­ity of these gun­shots, at least 844, were fired over just five min­utes and 30 sec­onds.At least 93% of the gun­shots recorded in the first min­utes of the at­tack were fired di­rectly to­wards the emer­gency ve­hi­cles and aid work­ers by Israeli sol­diers. During this time, at least five shoot­ers fired si­mul­ta­ne­ously. Witness tes­ti­monies sug­gest as many as 30 sol­diers were pre­sent in the area.Is­raeli sol­diers were ini­tially po­si­tioned on an el­e­vated sand­bank by the road, with no ob­struc­tions lim­it­ing their line of sight. The emer­gency lights and mark­ings of the vic­tims’ ve­hi­cles would have been clearly vis­i­ble to the sol­diers at the time of the at­tacks.Is­raeli sol­diers first main­tained fixed fir­ing po­si­tions from the el­e­vated sand­bank, then walked to­ward the aid work­ers while con­tin­u­ing to shoot. Upon reach­ing the aid work­ers, the sol­diers moved be­tween them and the ve­hi­cles and ex­e­cuted some of the aid work­ers at point blank range, as close as one me­ter away.In the im­me­di­ate af­ter­math of the at­tack, the Israeli mil­i­tary con­ducted ex­ten­sive earth­works at the site. In the days and weeks that fol­lowed, the area was fur­ther trans­formed by the Israeli mil­i­tary’s con­struc­tion of the Morag Corridor,” a se­cu­rity zone split­ting the south­ern Gaza Strip, and the erec­tion of an aid dis­tri­b­u­tion site op­er­ated by the Israeli- and U.S.-backed Gaza Humanitarian Foundation.“This seems to be a very well doc­u­mented case us­ing a num­ber of forms of cred­i­ble ev­i­dence that are cross ref­er­enced,” Katherine Gallagher, a se­nior staff at­tor­ney at the Center for Constitutional Rights, told Drop Site af­ter re­view­ing a de­tailed sum­mary of the in­ves­ti­ga­tion. It pre­sents a very com­pelling case, and hon­estly, a very dev­as­tat­ing one.”The Israeli mil­i­tary did not re­spond to spe­cific in­quiries from Drop Site and in­stead pointed to the find­ings of an in­ter­nal in­ves­ti­ga­tion pub­lished on April 20 that found the in­ci­dent oc­curred in a hos­tile and dan­ger­ous com­bat zone, un­der a wide­spread threat to the op­er­at­ing troops.” It also found no ev­i­dence to sup­port claims of ex­e­cu­tion,” which it called blood li­bels and false ac­cu­sa­tions against IDF sol­diers.”The joint re­port will be re­leased February 24 at a gath­er­ing at British par­lia­ment in Westminster hosted by the British Palestinian Committee with Earshot, Forensic Architecture, and the in­ter­na­tional hu­man­i­tar­ian law co­or­di­na­tor for PRCS, Dana Abu Koash. The full re­port is avail­able here.On March 23, 2025 at 3:52 a.m., PRCS dis­patched two am­bu­lances from two dif­fer­ent ar­eas to the scene of an Israeli airstrike in Al-Hashashin, an area near Rafah. Israel had re­sumed its scorched earth bomb­ing cam­paign on Gaza a few days ear­lier af­ter aban­don­ing the January 2025 cease­fire agree­ment.The at­tack on the aid work­ers be­gan at ap­prox­i­mately 4:00 a.m. when one of the am­bu­lances dri­ving along Gush Katif road in Al-Hashashin came un­der Israeli fire. The ve­hi­cle had its emer­gency lights turned on at the time. Mustafa Khafaja, who was dri­ving, lost con­trol of the ve­hi­cle, which veered left off the road and stopped near an elec­tric­ity pole. Khafaja and his col­league, Ezz El-Din Shaat, who was in the pas­sen­ger seat, were both killed. A third PRCS worker, Munther Abed, who was in the back of the ve­hi­cle, threw him­self to the floor of the van and sur­vived.Af­ter the shoot­ing stopped, Israeli sol­diers ap­proached the am­bu­lance and dragged Abed out of the car, beat him, and de­tained him at a nearby pit. Sometime later, two Palestinian civil­ians—a fa­ther and son from the Bardawil fam­ily—were also de­tained and brought to the pit. The Israeli sol­diers then took the three de­tainees to an el­e­vated area be­hind a tall con­crete struc­ture some 38 to 48 me­ters south­east of the am­bu­lance, where an ad­di­tional group of Israeli sol­diers were po­si­tioned.Still from the sit­u­ated tes­ti­mony with Munther Abed re­count­ing the lo­ca­tion of the pit and the area be­hind the tall con­crete struc­ture where he was taken when de­tained by Israeli sol­diers. (Forensic Architecture, 2026).By 4:35 a.m., the sec­ond am­bu­lance, hav­ing com­pleted its mis­sion in Al-Hashashin, was dis­patched to search for the first am­bu­lance, which had lost con­tact with PRCS head­quar­ters at 3:55 a.m. The sec­ond am­bu­lance was joined by two more PRCS am­bu­lances, one be­long­ing to Civil Defense, and a Civil Defense fire truck. The five-ve­hi­cle res­cue con­voy ar­rived at the scene of the at­tack of the first am­bu­lance shortly af­ter 5:00 a.m. All ve­hi­cles were clearly marked and had their emer­gency lights turned on.The po­si­tion of each am­bu­lance as the shoot­ing be­gan. (Forensic Architecture, 2026)A PRCS worker in one of the am­bu­lances, Refaat Radwan, be­gan film­ing on his phone as they drove to the site. His re­cov­ered videos as well as record­ings of phone calls by two other aid work­ers at the scene to PRCS dis­patch pro­vided cru­cial ev­i­dence of the mas­sacre. Forensic Architecture and Earshot’s analy­sis of the record­ings cor­rob­o­rated eye­wit­ness tes­ti­mony on the po­si­tions and move­ments of the Israeli sol­diers through­out the at­tack.At 5:09 a.m., as the aid work­ers parked and ap­proached the first am­bu­lance by foot, Israeli sol­diers po­si­tioned on the el­e­vated sand­bank opened fire. A dig­i­tal re­con­struc­tion of the scene shows that the sol­diers would have had an un­in­ter­rupted view of the ar­rival of the con­voy. Abed, who was be­ing de­tained at gun­point on the el­e­vated sand­bank, tes­ti­fied that the sol­diers were kneel­ing and aim­ing their weapons at the con­voy as it ap­proached.Lo­ca­tions of all emer­gency ve­hi­cles at the in­ci­dent site at 5:10 a.m. rel­a­tive to Munther Abed and the Israeli sol­diers who de­tained him. From their po­si­tion, the sol­diers would have been able to clearly see the con­voy’s ar­rival with their emer­gency lights on. (Forensic Architecture, 2026).

The Israeli sol­diers re­mained on the sand­bank while fir­ing con­tin­u­ously at the aid work­ers for four min­utes. The sol­diers then ad­vanced to­wards the aid work­ers at a walk­ing pace of ap­prox­i­mately one me­ter per sec­ond while con­tin­u­ously shoot­ing.Echolo­ca­tion of Israeli sol­diers ap­proach­ing the aid work­ers dur­ing the fi­nal 1 minute and 30 sec­onds. (Earshot, 2026).

Upon reach­ing the ve­hi­cles, the Israeli sol­diers con­tin­ued to fire as they walked in be­tween the am­bu­lances and the fire truck, shoot­ing the aid work­ers at close range in ex­e­cu­tion-style killings.At ap­prox­i­mately 5:13 a.m., PRCS aid worker Ashraf Abu Libda called the group’s head­quar­ters. The record­ing, which over­laps Radwan’s video, pro­vided ad­di­tional de­tails. In this record­ing, Earshot found that at least eight gun­shots were fired from po­si­tions be­tween the emer­gency ve­hi­cles. One of the gun­shots cap­tured on Abu Libda’s phone call was fired from a range of one to four me­ters from him. The gun­shots co­in­cide with the last time Abu Libda’s voice is heard on the call, sug­gest­ing these are the gun­shots that killed him.Echolo­ca­tion of Israeli sol­diers as close as 1 to 4 me­ters from aid work­ers and most likely close-range ex­e­cu­tion. (Earshot, 2026).

At least 844 gun­shots were fired over a pe­riod of five min­utes and 30 sec­onds, with at least 93% of the shots fired to­ward the emer­gency ve­hi­cles. The au­dio bal­lis­tics analy­sis con­firms the pres­ence of at least five shoot­ers—and pos­si­bly many more—fir­ing si­mul­ta­ne­ously. The two sur­viv­ing PRCS aid work­ers, Munther Abed and Asaad Al-Nasasra, tes­ti­fied that be­tween 12 and 30 sol­diers were at the scene.“The re­con­struc­tion was jointly achieved with the two sur­vivors of the in­ci­dent, with an im­mer­sive spa­tial model they could walk through and amend. Together with spa­tial and au­dio analy­sis we es­tab­lished the po­si­tion of the sol­diers on an el­e­vated ground with an un­ob­structed line of sight to the emer­gency ve­hi­cles. The sol­diers could clearly see the aid work­ers, shot at them con­tin­u­ously and de­lib­er­ately from this po­si­tion and then ap­proached to ex­e­cute them one by one at close range,” Samaneh Moafi, as­sis­tant di­rec­tor of re­search at Forensic Architecture, told Drop Site. Locating the mas­sacre within the evo­lu­tion of Israel’s cam­paign in Gaza shows that it was not an iso­lated in­ci­dent but part of the geno­cide.”Earshot used echolo­ca­tion to an­a­lyze the au­dio on the record­ings in or­der to ar­rive at pre­cise es­ti­mates of the shoot­ers’ lo­ca­tions. Echolocation is the process of lo­cat­ing the source of a sound based on an analy­sis of the sound’s echoes and the en­vi­ron­ment in which the sound trav­els. The Israeli mil­i­tary de­stroyed and cleared so many build­ings in the Tel Al-Sultan area where the am­bush of the aid work­ers took place that very few struc­tures re­mained. This de­struc­tion ac­tu­ally strength­ened Earshot’s abil­ity to de­ter­mine the po­si­tions and move­ments of Israeli sol­diers, based on iden­ti­fy­ing the sur­faces re­spon­si­ble for clearly dis­tin­guish­able gun­shot echoes. Rather than hav­ing mul­ti­ple build­ings re­flect­ing the sound waves, there were only a few stand­ing walls and the emer­gency ve­hi­cles them­selves.The analy­sis of the video and au­dio cor­rob­o­rated Al-Nasasra’s eye­wit­ness tes­ti­mony that Israeli sol­diers came down [from the sand­bank], got close to [the aid work­ers] and shot them from close range,” and were walk­ing be­tween [the aid work­ers] and shoot­ing.”Map show­ing the Israeli sol­dier’s po­si­tions de­rived from an au­dio analy­sis of gun­shot echoes from Refaat Radwan’s video. (Earshot, 2026).“Earshot foren­si­cally an­a­lyzed over 900 gun­shots fired at aid work­ers. It took one whole year of care­ful lis­ten­ing to re­con­struct an au­di­tory pic­ture of what hap­pened that dark night,” Lawrence Abu Hamdan, the di­rec­tor of Earshot, told Drop Site. I am so proud that our work has cor­rob­o­rated the sur­vivors’ tes­ti­mony, es­tab­lish­ing their brave ac­counts as ac­cu­rate and re­li­able doc­u­men­ta­tion of what oc­curred that day. Yet, it is the echoes of this event that con­tinue to haunt us: the de­struc­tion and clear­ing of Tel al-Sul­tan left only three struc­tures stand­ing at this crime scene. While the few echoes re­flect­ing off these build­ings brought light to this crime, they have also re­vealed a scale of era­sure of life be­yond this one event.”Ac­cord­ing to au­topsy re­ports first re­ported by the Guardian, the aid worker who filmed the video—Rad­wan—was shot in the head, while Abu Libda and an­other aid worker, Muhammad Bahloul, were shot in the chest. A doc­tor who ex­am­ined the bod­ies re­port­edly de­scribed the specific and in­ten­tional lo­ca­tion of shots at close range” as in­dica­tive of an execution-style” shoot­ing.More than two hours af­ter the ini­tial at­tack, a clearly marked UN ve­hi­cle, a Toyota Hilux, passed by the site. Israeli sol­diers fired on the ve­hi­cle, killing the dri­ver. The UN lost con­tact with the ve­hi­cle at 6:00 a.m. A sec­ond UN ve­hi­cle, a minibus, ar­rived in the area min­utes later and was brought to a stop by gun­fire a lit­tle over 200 me­ters away. The dri­ver was able to es­cape.Left: Photograph of the UN Toyota Hilux taken on the 30 March 2025, when the bod­ies of the vic­tims were re­cov­ered. (OCHA, 2025). Right: Still from the sit­u­ated tes­ti­mony with Asaad re­count­ing the lo­ca­tion of the UN Toyota Hilux when brought to a stop. (Forensic Architecture, 2026). Annotated 3D model show­ing the po­si­tion of two UN ve­hi­cles in re­la­tion to the miss­ing am­bu­lance and the con­voy of emer­gency ve­hi­cles. (Forensic Architecture, 2026).Between 6:55 and 7:13 a.m., Al-Nasasra made a phone call to PRCS head­quar­ters that cap­tured at least 42 ad­di­tional gun­shots and the sound of ve­hi­cle move­ment. The record­ing also cap­tured the sound of an ex­plo­sion the in­ves­ti­ga­tion iden­ti­fied as the fir­ing of an Israeli-made Spike LR guided mis­sile.Fol­low­ing the am­bush, Israeli forces crushed all eight ve­hi­cles us­ing heavy ma­chin­ery and at­tempted to bury them un­der the sand.The body of Anwar al-At­tar was found near the am­bush site on March 27, and the bod­ies of the other 14 aid work­ers, all wear­ing iden­ti­fy­ing uni­forms or vol­un­teer vests of their re­spec­tive or­ga­ni­za­tions, were found in a mass grave near the site on March 30.The 15 aid work­ers killed were: Mustafa Khafaja, Ezz El-Din Shaat, Saleh Muammar, Refaat Radwan, Muhammad Bahloul, Ashraf Abu Libda, Muhammad al-Hila, and Raed al-Sharif with PRCS. Zuhair Abdul Hamid al-Farra, Samir Yahya al-Ba­hapsa, Ibrahim Nabil al-Maghari, Fouad Ibrahim al-Ja­mal, Youssef Rassem Khalifa, and Anwar al-At­tar with Civil Defense. Kamal Mohammed Shahtout with UNRWA.Annotated still from the 3D model show­ing the lo­ca­tion of the bod­ies of aid work­ers and their ve­hi­cles be­fore the mass bur­ial. (Forensic Architecture, 2026).One of the sur­vivors, Abed, was re­leased hours af­ter the am­bush. The other sur­vivor, Asaad, was held in Israeli cus­tody with­out charge for 37 days, tor­tured, and in­ter­ro­gated in re­la­tion to the in­ci­dent at the Sde Teiman de­ten­tion camp, a no­to­ri­ous Israeli prison camp in the Negev desert, be­fore be­ing re­leased on April 29.Jonathan Whittall, a se­nior UN of­fi­cial in Palestine be­tween 2022 and 2025, was one of team mem­bers on the ground when the mass grave was dis­cov­ered on March 30 and pro­vided ev­i­dence to Forensic Architecture and Earshot for their in­ves­ti­ga­tion. Following our dis­cov­ery of the mass grave, the nar­ra­tive from Israeli forces shifted mul­ti­ple times; we were fed sev­eral ver­sions of a bla­tant lie,” Whittall told Drop Site. The men we re­trieved on Eid last year were medics. We found them in their uni­forms, ready to save lives, only to be killed by Israeli forces fully aware of their pro­tected sta­tus.” Whittall, who is now ex­ec­u­tive Director of KEYS Initiative, a po­lit­i­cal af­fairs and strate­gic ad­vi­sory or­ga­ni­za­tion, has also con­tributed re­port­ing to Drop Site News. This il­lus­trates an ab­hor­rent dis­re­gard for in­ter­na­tional law,” he con­tin­ued, where any Palestinian in an Israeli-designated evac­u­a­tion zone is tar­geted re­gard­less of their civil­ian sta­tus. It high­lights the to­tal lack of ac­count­abil­ity un­der which these forces op­er­ate. International gov­ern­ments con­tinue to arm and trade with a lead­er­ship ac­cused of geno­cide, whose sol­diers mas­sa­cred medics and buried them in a grave marked by the siren light of the am­bu­lance they de­stroyed.”Pales­tin­ian Red Crescent aid work­ers mourn the killing of their col­leagues by the Israeli mil­i­tary in Tel al-Sul­tan as their bod­ies are brought to Nasser Hospital in Khan Yunis, in south­ern Gaza. March 30, 2025. (Photo by Abdallah F.s. Alattar/Anadolu via Getty Images).In the af­ter­math of the mas­sacre, the Israeli mil­i­tary pro­vided sev­eral con­flict­ing ver­sions of events to jus­tify the killings. On March 28, af­ter the dis­cov­ery of al-At­tar’s body, the Israeli mil­i­tary ad­mit­ted that its sol­diers had fired on ambulances and fire trucks.” Three days later, af­ter the re­main­ing bod­ies were dis­cov­ered in a mass grave, the Israeli mil­i­tary claimed that several un­co­or­di­nated ve­hi­cles were iden­ti­fied ad­vanc­ing sus­pi­ciously to­ward IDF troops with­out head­lights or emer­gency sig­nals.”Af­ter footage from Radwan’s phone was first pub­lished by the New York Times a few days later, the Israeli mil­i­tary back­tracked on its claims that the ve­hi­cles did not have emer­gency sig­nals on when Israeli troops opened fire, say­ing the state­ment was in­ac­cu­rate.The Israeli mil­i­tary then an­nounced on April 20 that an in­ter­nal in­quiry into the in­ci­dent had found the killings were caused by several pro­fes­sional fail­ures, breaches of or­ders, and a fail­ure to fully re­port the in­ci­dent.”The Israeli mil­i­tary said troops from the Golani re­con­nais­sance bat­tal­ion were in­volved in the at­tack. However, it said sol­diers did not en­gage in indiscriminate fire” dur­ing the in­ci­dent, but that they opened fire on what they be­lieved to be a tangible threat” amid what the mil­i­tary called an operational mis­un­der­stand­ing.” It blamed the at­tacks on poor night vis­i­bil­ity” and main­tained the in­ci­dent had un­folded in a hostile and dan­ger­ous com­bat zone, un­der a wide­spread threat to the op­er­at­ing troops.” Six of the fif­teen Palestinians killed, the mil­i­tary said, were iden­ti­fied in a ret­ro­spec­tive ex­am­i­na­tion as Hamas ter­ror­ists,” but pro­vided no ev­i­dence to sup­port the claim.“On the spe­cific ques­tion of Israel jus­ti­fy­ing the at­tack on clearly marked med­ical per­son­nel be­cause of sus­pi­cions of mem­ber­ship in groups or links to groups or ter­ror­ism—be­cause there is an af­fir­ma­tive duty to re­spect and pro­tect med­ical per­son­nel, you don’t shoot first, you pro­tect first,” Gallagher told Drop Site. But what this in­ves­ti­ga­tion re­veals is that there was a shoot first pol­icy, and that is un­law­ful un­der in­ter­na­tional law.”As for the bur­ial of the bod­ies in a mass grave, the Israeli mil­i­tary said in its re­port it was de­cided to gather and cover the bod­ies to pre­vent fur­ther harm and clear the ve­hi­cles from the route in prepa­ra­tion for civil­ian evac­u­a­tion. The body re­moval and ve­hi­cle crush­ing were car­ried out by field com­man­ders.” It con­cluded, removing the bod­ies was rea­son­able un­der the cir­cum­stances, but the de­ci­sion to crush the ve­hi­cles was wrong. In gen­eral, there was no at­tempt to con­ceal the event.”As a re­sult of the in­ves­ti­ga­tion, the com­mand­ing of­fi­cer of the 14th Brigade re­ceived a let­ter of rep­ri­mand for his over­all re­spon­si­bil­ity for the in­ci­dent,” while the deputy com­man­der of the Golani re­con­nais­sance bat­tal­ion in­volved in the in­ci­dent was dismissed from his po­si­tion due to his re­spon­si­bil­i­ties as the field com­man­der and for pro­vid­ing an in­com­plete and in­ac­cu­rate re­port dur­ing the de­brief.”The in­quiry did not rec­om­mend any crim­i­nal ac­tion be taken against the mil­i­tary units re­spon­si­ble for the in­ci­dent. The Palestine Red Crescent Society, Civil Defense, and the UN hu­man­i­tar­ian agency in Gaza all re­jected the Israeli mil­i­tary re­port.“At­tacks on med­ical per­son­nel and those who are iden­ti­fied as med­ical per­son­nel are patently un­law­ful un­der in­ter­na­tional law, and there is an af­fir­ma­tive oblig­a­tion to pro­tect med­ical per­son­nel in the con­text of armed con­flict. So the very first thing is that there’s a breach of that very clear and time hon­ored prin­ci­ple of in­ter­na­tional hu­man­i­tar­ian law,” Gallagher said. When you zoom out and look at this in the con­text of the way the Israeli as­sault has been car­ried out over many months and years in Gaza and we see that there is a pat­tern and prac­tice of at­tacks on med­ical per­son­nel—sim­i­lar to jour­nal­ists and other groups that are ex­plic­itly and uniquely pro­tected as classes of civil­ians in in­ter­na­tional hu­man­i­tar­ian law—it raises even more ques­tions and deep con­cern about the lack of ac­count­abil­ity, be­cause what we know is that im­punity breeds rep­e­ti­tion.”Gal­lagher, who pre­vi­ously worked at the UNs International Criminal Court for the for­mer Yugoslavia, said that a le­gal analy­sis of the mas­sacre would find se­ri­ous vi­o­la­tions of the Rome Statute of the International Criminal Court. When you’re talk­ing about grave breaches of the Geneva Conventions, in par­tic­u­lar war crimes, you have oblig­a­tions, not just the pos­si­bil­ity, but oblig­a­tions, to open in­ves­ti­ga­tions,” Gallagher said.Trans­form­ing the Site of the Massacre into a GHF HubSatellite im­agery from the morn­ing of the am­bush shows that ex­ten­sive earth­works were car­ried out at the in­ci­dent site. The im­ages re­veal the con­struc­tion of an earth berm ap­prox­i­mately 220 me­ters north of the am­bush lo­ca­tion and an­other roughly 410 me­ters to the south. These two po­si­tions later func­tioned as check­points, re­strict­ing ac­cess and con­trol­ling pas­sage along an evac­u­a­tion route es­tab­lished that morn­ing by the Israeli mil­i­tary lead­ing to­ward the coastal Al-Mawasi area.The earth­works that be­gan shortly af­ter the at­tack were used in the con­struc­tion of a Gaza Humanitarian Foundation aid dis­tri­b­u­tion” site, at which civil­ians were tar­geted and shot at. (Foren­sic Architecture, 2026).

In the days and weeks that fol­lowed, the area sur­round­ing the in­ci­dent site was fur­ther trans­formed by the Israeli mil­i­tary’s con­struc­tion of the Morag Corridor” se­cu­rity zone and the erec­tion of an aid dis­tri­b­u­tion site op­er­ated by the Gaza Humanitarian Foundation.“On that same site of the mass grave, the Gaza Humanitarian Foundation es­tab­lished a dis­tri­b­u­tion point where des­per­ate peo­ple were gunned down try­ing to ac­cess food,” Whittall told Drop Site. Now, the U.S, un­der the so-called Board of Peace, plans to build a New Rafah’ over this crime scene. Without mean­ing­ful ac­count­abil­ity, New Rafah’ will be a mon­u­ment to im­punity.”

...

Read the original on www.dropsitenews.com »

2 1,835 shares, 73 trendiness

F-Droid - Free and Open Source Android App Repository

During our talks with F-Droid users at FOSDEM26 we were baf­fled to learn most were re­lieved that Google has can­celed their plans to lock-down Android.

Why baf­fled? Because no such thing ac­tu­ally hap­pened, the plans an­nounced last August are still sched­uled to take place. We see a bat­tle of PR cam­paigns and whomever has the last post out re­mains in the me­dia mem­ory as the truth, and hav­ing jour­nal­ists just copy/​paste Google posts serves no one.

But Google said… Said what? That there’s a mag­i­cal advanced flow”? Did you see it? Did any­one ex­pe­ri­ence it? When is it sched­uled to be re­leased? Was it part of Android 16 QPR2 in December? Of 16 QPR3 Beta 2.1 last week? Of Android 17 Beta 1? No? That’s the is­sue… As time marches on peo­ple were left with the im­pres­sion that every­thing was done, fixed, Google wasn’t evil” af­ter all, this time, yay!

While we all have bad mem­o­ries of banners” as the dreaded ad de­liv­ery medium of the Internet, af­ter FOSDEM we de­cided that we have to raise the is­sue back and have every­one, who cares about Android as an open plat­form, in­formed that we are run­ning out of time un­til Google be­comes the gate-keeper of all users de­vices.

Hence, the web­site and start­ing to­day our clients, with the up­dates of F-Droid and F-Droid Basic, fea­ture a ban­ner that re­minds every­one how lit­tle time we have and how to voice their con­cerns to what­ever lo­cal au­thor­ity is able to un­der­stand the dan­gers of this path Android is led to.

We are not alone in our fight, IzzyOnDroid added a ban­ner too, more F-Droid clients will add the warn­ing ban­ner soon and other app down­load­ers, like Obtainium, al­ready have an in-app warn­ing di­a­logue.

Regarding F-Droid Basic rewrite, de­vel­op­ment con­tin­ues with a new re­lease 2.0-alpha3:

Note that if you are al­ready us­ing F-Droid Basic ver­sion 1.23.x, you won’t re­ceive this up­date au­to­mat­i­cally. You need to nav­i­gate to the app in­side F-Droid and tog­gle Allow beta up­dates” in top right three dot menu.

In apps news, we’re slowly get­ting back on track with post Debian up­grade fixes (if your app still uses Java 17 is there a chance you can up­grade to 21?) and post FOSDEM de­lays. Every app is im­por­tant to us, yet ac­tions like the Google one above waste the time we could have put to bet­ter use in Gitlab.

Buses was up­dated to 1.10 af­ter a two year hia­tus.

Conversations and Quicksy were up­dated to 2.19.10+free im­prov­ing on clean­ing up af­ter banned users, a bet­ter QR work­flow and bet­ter tablet ro­ta­tion sup­port. These are nice, but an­other change raises our in­ter­est, Play Store fla­vor: Stop us­ing Google li­brary and in­ter­face di­rectly with Google Play Service via IPC. Sounds in­ter­est­ing for your app too? Is this a path to hav­ing one sin­gle ver­sion for both F-Droid and Play that is fully FLOSS? We don’t know yet, but we salute any trick that re­moves an­other pro­pri­etary de­pen­dency from the code. If cu­ri­ous feel free to take a look at the com­mit.

Dolphin Emulator was up­dated to 2512. We missed one ver­sion in be­tween so the changel­ogs are huge, luck­ily the devs pub­lish highly de­tailed posts about up­dates. So we’ll start with Release 2509” (about 40 mins to read), we side-track with Starlight Spotlight: A Hospital Wii in a New Light” (for about 50 mins), we con­tinue to the cur­rent re­lease in Release 2512” (40 more min­utes) and we fin­ish with Rise of the Triforce” delv­ing in his­tory for more than one hour.

Image Toolbox was up­dated to 3.6.1 adding many fixes and… some AI tools. Were you ex­pect­ing such helpers? Will you use them?

Luanti was up­dated to 5.15.1 adding some wel­comed fixes. If your game world started flick­er­ing af­ter the last up­date make sure to up­date.

Nextcloud apps are get­ting an up­date al­most every week, like Nextcloud was up­dated to 33.0.0, Nextcloud Cookbook to 0.27.0, Nextcloud Dev to 20260219, Nextcloud Notes to 33.0.0 and Nextcloud Talk was up­dated to 23.0.0.

But are you fol­low­ing the server side too? Nextcloud Hub 26 Winter was just re­leased adding a plethora of fea­tures. If you want to read about them, see the 30 min­utes post here or watch the one hour long video pre­sen­ta­tion from the team here.

ProtonVPN - Secure and Free VPN was up­dated to 5.15.70.0 adding more con­trol to auto-con­nects, coun­tries and cities. Also all con­nec­tions are han­dled now by WireGuard and Stealth pro­to­cols as the older OpenVPN was re­moved mak­ing the app al­most 40% smaller.

Offi was up­dated to 14.0 with a bit of code pol­ish. Unfortunately for Android 7 users, the app now needs Android 8 or later.

QUIK SMS was up­dated to 4.3.4 with many fixes. But Vishal praised the du­pli­cate re­mover, the de­fault auto de-du­pli­ca­tion func­tion and found that the bug that made deleted mes­sages reap­pear is fixed.

SimpleEmail was up­dated to 1.5.4 af­ter a 2 year pause. It’s just a fixes re­lease, up­dat­ing trans­la­tions and mak­ing the app com­pat­i­ble with Android 12 and later ver­sions.

* NeoDB You: A na­tive Android app for NeoDB de­signed with Material 3/You

Thank you for read­ing this week’s TWIF 🙂

Please sub­scribe to the RSS feed in your favourite RSS ap­pli­ca­tion to be up­dated of new TWIFs when they come up.

You are wel­come to join the TWIF fo­rum thread. If you have any news from the com­mu­nity, post it there, maybe it will be fea­tured next week 😉

To help sup­port F-Droid, please check out the do­na­tion page and con­tribute what you can.

...

Read the original on f-droid.org »

3 1,447 shares, 48 trendiness

Trump raises global tariffs to 15%, day after Supreme Court ruling

US President Donald Trump has an­nounced that the US will raise global tar­iffs to 15%.

This is an in­crease from the 10% rate an­nounced on Friday, when the pres­i­dent in­voked a never-be­fore-used law known

as Section 122 af­ter the Supreme Court struck down his pre­vi­ous tar­iffs with a 6-3 ma­jor­ity.

The law, which falls un­der the 1974 Trade Act, gives Trump the power to put in place tar­iffs up to a max­i­mum of 15% for 150 days, at which point Congress must step in.

Trump has called the Supreme Court’s de­ci­sion ridiculous” and extraordinarily anti-Amer­i­can”.

Some law­mak­ers are ques­tion­ing the pres­i­den­t’s de­ci­sion to con­tinue the levies, with Democratic con­gress­man Ted Lieu say­ing Trump is tak­ing out his anger to­wards the top court on Americans. These tem­po­rary tar­iffs will be chal­lenged in court and Democrats will kill them when they ex­pire,” he writes on X.

American al­lies have also weighed in on the changes, with German Chancellor Friedrich Merz warn­ing about the un­cer­tainty they bring the global econ­omy. Meanwhile, the UK says it ex­pects to re­tain its privileged trad­ing po­si­tion with the US.

We’ve wrap­ping up our live cov­er­age for now, but you can

read more in our news ar­ti­cle.

...

Read the original on www.bbc.com »

4 1,435 shares, 59 trendiness

How I built Timeframe, our family e-paper dashboard

TL;DR: Over the past decade, I’ve worked to build the per­fect fam­ily dash­board sys­tem for our home, called Timeframe. Combining cal­en­dar, weather, and smart home data, it’s be­come an im­por­tant part of our daily lives.

See https://​news.ycombi­na­tor.com/​item?id=47113728 for a lively dis­cus­sion of this post.

When Caitlin and I got mar­ried a decade ago, we set an in­ten­tion to have a healthy re­la­tion­ship with tech­nol­ogy in our home. We kept our bed­room free of any screens, charg­ing our de­vices else­where overnight. But we missed our cal­en­dar and weather apps.

So I set out to build a so­lu­tion to our prob­lem. First, I con­structed a Magic Mirror us­ing an off-the-shelf med­i­cine cab­i­net and LCD dis­play with its frame re­moved. It showed the cal­en­dar and weather data we needed:

But it was hard to read the text, es­pe­cially dur­ing the day as we get sig­nif­i­cant nat­ural light in Colorado. At night, it glowed like any back­lit dis­play, stick­ing out sorely in our liv­ing space.

I then spent about a year ex­per­i­ment­ing with var­i­ous jail­bro­ken Kindle de­vices, even­tu­ally land­ing on de­sign with cal­en­dar and weather data on a pair of screens. The Kindles took a few sec­onds to re­fresh and flash the screen to re­set the ink pix­els, so they only up­dated every half hour. I de­signed wood en­clo­sures and laser-cut them at the lo­cal li­brary mak­er­space:

Software-wise, I built a Ruby on Rails app for fetch­ing the nec­es­sary data from Google Calendar and Dark Sky. The Kindles woke up on a sched­ule, load­ing a URL in the app that ren­dered a PNG us­ing IMGKit. The pro­to­type proved e-pa­per was the right so­lu­tion: it was un­ob­tru­sive re­gard­less of light­ing:

The Kindles were a hack, re­quir­ing con­stant tin­ker­ing to keep them work­ing. It was time for a more re­li­able so­lu­tion. I tried an OLED screen to see if the lack of a global back­light would be less dis­tract­ing, but it was­n’t much bet­ter than the Magic Mirror:

So it was back to e-pa­per. I found a sys­tem of dis­plays from Visionect, which came in 6”/10”/13”/32” sizes and could up­date every ten min­utes for 2-3 months on a sin­gle charge:

The 32” screen used an out­dated lower-con­trast panel and its res­o­lu­tion was too low to ren­der text smoothly. The smaller sizes used a con­trasty, high-PPI panel. I ended up us­ing a com­bi­na­tion of them around the house: a 6” in the mud­room for the weather, a 13” (with its built-in mag­netic back­ing) in the kitchen at­tached to the side of the fridge, and a 10” in the bed­room.

The Visionect dis­plays re­quired run­ning cus­tom closed-source soft­ware, ei­ther as a SaaS or lo­cally with Docker. I opted for a lo­cal in­stal­la­tion on the Raspberry Pi al­ready run­ning the Rails back­end. I had my best re­sults push­ing im­ages to the Visionect dis­plays every five min­utes in a re­cur­ring back­ground job. It used IMGKit to gen­er­ate a PNG and send it to the Visionect API, logic I ex­tracted into vi­sionect-ruby. This setup proved to be in­cred­i­bly re­li­able, with­out a sin­gle fail­ure for months at a time.

Visiting friends of­ten asked how they could have a sim­i­lar sys­tem in their home. Three years af­ter the ini­tial pro­to­type, I did my first mar­ket test with a po­ten­tial cus­tomer. At their re­quest, I ex­per­i­mented with dif­fer­ent for­mats, in­clud­ing a month view on the 13” screen:

Unfortunately, the cus­tomer did­n’t see enough value to jus­tify the $1000 price tag (in 2019!) for the 13” de­vice, let alone any­thing I’d charge for a sub­scrip­tion ser­vice. At around the same time, Visionect started charg­ing a $7/mo per-de­vice fee to run their back­end soft­ware on premises with Docker, af­ter years of it be­ing free to use. I’d have needed to charge $10/month, if not more, for a sin­gle screen!

In late 2021, the Marshall Fire de­stroyed our home along with ~1,000 oth­ers. Our home­own­er’s in­sur­ance gave us two years to re­build, so we set off to re­design our home from the ground up.

Around the same time, Boox re­leased the 25.3” Mira Pro, the first high-res­o­lu­tion op­tion for large e-pa­per screens. Best of all, it could up­date in re­al­time! Unlike the Visionect de­vices, it was just a dis­play with an HDMI port and needed to be plugged into power. A quick pro­to­type pow­ered by an old Mac Mini made it im­me­di­ately ob­vi­ous that it was a huge step for­ward in ca­pa­bil­ity. The larger screen al­lowed for sig­nif­i­cantly more in­for­ma­tion to be dis­played:

But the most com­pelling in­no­va­tion was hav­ing the screen up­date in re­al­time. I added a clock, the cur­rent song play­ing on our Sonos sys­tem (using jishi/​node-sonos-http-api) and the next-hour pre­cip­i­ta­tion fore­cast from Dark Sky:

The work­ing pro­to­type was enough to con­vince me to build a place for it in the new house. We de­signed a phone nook” on our main floor with an art light for the dis­play:

We also ran power to two more lo­ca­tions for 13” Visionect dis­plays, one in our bed­room and one by the door to our garage:

The real-time re­quire­ments of the Mira Pro im­me­di­ately sur­faced per­for­mance and com­plex­ity is­sues in the back­end, prompt­ing an al­most com­plete rewrite.

While the Visionect sys­tem worked just fine with mul­ti­ple-sec­ond re­sponse times, switch­ing to long-polling every two sec­onds put a ceil­ing on how slow re­sponse times could be. To start, I moved away from gen­er­at­ing im­ages. The Visionect folks added the abil­ity to ren­der a URL di­rectly in the back­end, free­ing up re­sources to serve the long-polling re­quests.

Most sig­nif­i­cantly, I started mi­grat­ing to­wards Home Assistant (HA) as the pri­mary data source. HA al­ready had in­te­gra­tions for Google Calendar, Dark Sky (now Apple Weather), and Sonos, en­abling me to re­move over half of the code in the Timeframe code­base! I ended up land­ing a PR to Home Assistant to al­low for the cal­en­dar be­hav­ior I needed, and will prob­a­bly need to write a cou­ple more be­fore HA can be the sole data source.

With less data-fetch­ing logic, I was able to re­move both the data­base and Redis from the Rails ap­pli­ca­tion, a mas­sive re­duc­tion in com­plex­ity. I now run the back­ground tasks with Rufus Scheduler and save data fetch­ing re­sults with the Rails file store cache back­end.

In ad­di­tion to data re­trieval, I’ve also worked to move as much of the ap­pli­ca­tion logic into Home Assistant. I now au­to­mat­i­cally dis­play the sta­tus of any sen­sor that be­gins with sen­sor.time­frame, us­ing a sim­ple ICON,Label CSV for­mat.

For ex­am­ple, the other day I wanted to have a re­minder to start or sched­ule our dish­washer af­ter 8pm if it was­n’t set to run. It took me about a minute to write a tem­plate sen­sor us­ing the power level from the out­let:

{% if states(‘sen­sor.kitchen_dish­wash­er_switched_out­let_pow­er’)|float < 2 and now().hour > 19 %}

uten­sils,Run the dish­washer!

{% en­dif %}

In the month since adding the helper, it re­minded me twice when I’d have oth­er­wise for­got­ten. And I did­n’t have to com­mit or de­ploy any code!

Since mov­ing into our new home, we’ve come to rely on the real-time func­tion­al­ity much more sig­nif­i­cantly. Effectively, we’ve turned the top-left cor­ner of the dis­plays into a sta­tus in­di­ca­tor for the house. For ex­am­ple, it shows what doors are open/​un­locked:

Or whether the laun­dry is done:

It has a pow­er­ful func­tion: if the sta­tus on the dis­play is blank, the house is in a healthy” state and does not need any at­ten­tion. This ap­proach of only show­ing what in­for­ma­tion is rel­e­vant in a given mo­ment flies right in the face of how most smart homes ap­proach com­mu­ni­cat­ing their sta­tus:

The sin­gle sta­tus in­di­ca­tor re­moves the need to scan an en­tire screen. This change in ap­proach is pos­si­ble be­cause of one key dif­fer­ence: we have sep­a­rated the con­trol of our de­vices from the dis­play of their sta­tus.

I con­tinue to re­ceive sig­nif­i­cant in­ter­est in the pro­ject and re­main fo­cused on bring­ing it to mar­ket. A few key is­sues re­main:

While I have made sig­nif­i­cant progress in han­dling run­time er­rors grace­fully, I have plenty to learn about cre­at­ing em­bed­ded sys­tems that do not need main­te­nance.

There are still sev­eral data sources I fetch di­rectly out­side of Home Assistant. Once HA is the sole source of data, I’ll be able to have Timeframe be a Home Assistant App, mak­ing it sig­nif­i­cantly eas­ier to dis­trib­ute.

The cur­rent hard­ware setup is not ready for adop­tion by the av­er­age con­sumer. The 25” Boox dis­play is ex­cel­lent but costs about $2000! It also does­n’t in­clude the hard­ware needed to drive the dis­play. There are a cou­ple of po­ten­tial op­tions to con­sider, such as Android-powered de­vices from Boox and Philips or low-cost op­tions from TRMNL.

Building Timeframe con­tin­ues to be a pas­sion of mine. While my day job has me build­ing soft­ware for over a hun­dred mil­lion peo­ple, it’s re­fresh­ing to work on a pro­ject that im­proves my fam­i­ly’s daily life.

...

Read the original on hawksley.org »

5 1,367 shares, 53 trendiness

Facebook is absolutely cooked

And I don’t just mean that no­body uses it any­more. Like, I knew every­one un­der 50 had moved on, but I did­n’t re­al­ize the ex­tent of the slop con­veyor belt that’s re­placed us.

I logged on for the first time in ~8 years to see if there was a group for my neigh­bor­hood (there was­n’t). Out of cu­rios­ity I thought I’d scroll a bit down the main feed.

The first post was the lat­est xkcd (a page I fol­low). The next ten posts were not by friends or pages I fol­low. They were ba­si­cally all thirst traps of young women, mostly AI-generated, with generic cap­tions. Here’s a sam­pler — mildly NSFW, but I did leave out a cou­ple of the lewder ones:

Yikes. Again, I don’t fol­low any of these pages. This is all just what Facebook is push­ing on me.

I know Twitter/X has worse prob­lems with spam bots in the replies, but this is the News Feed! It’s the main page of the site! It’s the prod­uct that de­fined mod­ern so­cial me­dia!

It was­n’t all like that, though. There was also an AI video of a po­lice­man con­fis­cat­ing a lit­tle boy’s bike, only to bring him a brand new one:

And there were some sloppy memes and jokes, mostly about re­la­tion­ships, like this (admittedly not AI) video sketch where a woman de­cides to in­ten­tion­ally start a fight with her boyfriend be­cause she’s on her pe­riod:

Maybe that is­n’t lit­er­ally about sex, but I’d clas­sify it as the same sort of lizard-brain-rot en­gage­ment bait as those self­ies.

Several com­menters have vouched that Yoleendadong makes funny, high-qual­ity con­tent and should­n’t be lumped in with AI slop. I’m just say­ing I think there’s a rea­son this par­tic­u­lar video of hers popped up, and it’s prob­a­bly the kind of en­gage­ment cre­ated by the premise.

Meta even gives us some help­ful ideas for sex­ist ques­tions we can ask their AI about the video:

Yep, that’s an­other yikes” from me. To be fair, though, some­times that sug­gested ques­tions fea­ture is pretty use­ful! Like with this post, for ex­am­ple:

Why is she wear­ing pink heels? What is her per­son­al­ity? Great ques­tions, Meta.

I said these were mostly” AI-generated. The truth is with how good the mod­els are get­ting these days, it’s hard to tell, and I think a cou­ple of them might be real peo­ple.

Still, some of these are pretty ob­vi­ously AI. Here’s one with a bunch of alien text and man­gled lo­gos on the score­board in the back­ground:

Hmm, I won­der if any­one has no­ticed this is AI? Let’s check out the com­ments and see if any­one’s pointed that ou—

…never mind. (I dunno, maybe those are all bots too.)

So: is this just some­thing wacky with my al­go­rithm?

I mean… maybe? That’s part of the whole thing with these al­go­rith­mic feeds; it’s hard to know if any­one else is see­ing what I’m see­ing.

On the one hand, I doubt most (straight) wom­en’s feeds would look like this. But on the other hand, I had­n’t logged in in nearly a decade! I hate to think what the feed looks like for some lonely old guy who’s been scrolling the lightly-clothed AI gooni­verse for hours every day.

Did every­one but me know it was like this? I’d seen screen­caps of stuff like the Jesus-statue-made-out-of-broccoli slop a year or two ago, but I thought that only hap­pened to grand­mas. I had­n’t heard it was this bad.

I won­der if this evo­lu­tion was less no­tice­able for peo­ple who are log­ging in every day. Or maybe it only gets this bad when there aren’t any posts from your ac­tual friends?

In any case, I stopped ex­plor­ing af­ter I saw a cou­ple more of those AI-generated pic­tures but with girls that looked like they were about ~14, which made me sick to my stom­ach. So long Facebook, see you never, un­til one day I in­ex­plic­a­bly need to use your plat­form to get up­dates from my kid’s school.

...

Read the original on pilk.website »

6 1,187 shares, 46 trendiness

Ladybird adopts Rust, with help from AI

We’ve been search­ing for a mem­ory-safe pro­gram­ming lan­guage to re­place C++ in Ladybird for a while now. We pre­vi­ously ex­plored Swift, but the C++ in­terop never quite got there, and plat­form sup­port out­side the Apple ecosys­tem was lim­ited. Rust is a dif­fer­ent story. The ecosys­tem is far more ma­ture for sys­tems pro­gram­ming, and many of our con­trib­u­tors al­ready know the lan­guage. Going for­ward, we are rewrit­ing parts of Ladybird in Rust.

When we orig­i­nally eval­u­ated Rust back in 2024, we re­jected it be­cause it’s not great at C++ style OOP. The web plat­form ob­ject model in­her­its a lot of 1990s OOP fla­vor, with garbage col­lec­tion, deep in­her­i­tance hi­er­ar­chies, and so on. Rust’s own­er­ship model is not a nat­ural fit for that.

But af­ter an­other year of tread­ing wa­ter, it’s time to make the prag­matic choice. Rust has the ecosys­tem and the safety guar­an­tees we need. Both Firefox and Chromium have al­ready be­gun in­tro­duc­ing Rust into their code­bases, and we think it’s the right choice for Ladybird too.

Our first tar­get was LibJS , Ladybird’s JavaScript en­gine. The lexer, parser, AST, and byte­code gen­er­a­tor are rel­a­tively self-con­tained and have ex­ten­sive test cov­er­age through test262, which made them a nat­ural start­ing point.

I used Claude Code and Codex for the trans­la­tion. This was hu­man-di­rected, not au­tonomous code gen­er­a­tion. I de­cided what to port, in what or­der, and what the Rust code should look like. It was hun­dreds of small prompts, steer­ing the agents where things needed to go. After the ini­tial trans­la­tion, I ran mul­ti­ple passes of ad­ver­sar­ial re­view, ask­ing dif­fer­ent mod­els to an­a­lyze the code for mis­takes and bad pat­terns.

The re­quire­ment from the start was byte-for-byte iden­ti­cal out­put from both pipelines. The re­sult was about 25,000 lines of Rust, and the en­tire port took about two weeks. The same work would have taken me mul­ti­ple months to do by hand. We’ve ver­i­fied that every AST pro­duced by the Rust parser is iden­ti­cal to the C++ one, and all byte­code gen­er­ated by the Rust com­piler is iden­ti­cal to the C++ com­pil­er’s out­put. Zero re­gres­sions across the board:

No per­for­mance re­gres­sions on any of the JS bench­marks we track ei­ther.

Beyond the test suites, I’ve done ex­ten­sive test­ing by brows­ing the web in a lock­step mode where both the C++ and Rust pipelines run si­mul­ta­ne­ously, ver­i­fy­ing that out­put is iden­ti­cal for every piece of JavaScript that flows through them.

If you look at the code, you’ll no­tice it has a strong translated from C++” vibe. That’s be­cause it is trans­lated from C++. The top pri­or­ity for this first pass is com­pat­i­bil­ity with our C++ pipeline. The Rust code in­ten­tion­ally mim­ics things like the C++ reg­is­ter al­lo­ca­tion pat­terns so that the two com­pil­ers pro­duce iden­ti­cal byte­code. Correctness is a close sec­ond. We know the re­sult is­n’t id­iomatic Rust, and there’s a lot that can be sim­pli­fied once we’re com­fort­able re­tir­ing the C++ pipeline. That cleanup will come in time.

This is not be­com­ing the main fo­cus of the pro­ject. We will con­tinue de­vel­op­ing the en­gine in C++, and port­ing sub­sys­tems to Rust will be a side­track that runs for a long time. New Rust code will co­ex­ist with ex­ist­ing C++ through well-de­fined in­terop bound­aries.

We want to be de­lib­er­ate about which parts get ported and in what or­der, so the port­ing ef­fort is man­aged by the core team. Please co­or­di­nate with us be­fore start­ing any port­ing work so no­body wastes their time on some­thing we can’t merge.

I know this will be a con­tro­ver­sial move, but I be­lieve it’s the right de­ci­sion for Ladybird’s fu­ture. :^)

...

Read the original on ladybird.org »

7 1,056 shares, 41 trendiness

I Taught My Dog to Vibe Code Games

For the past few weeks I’ve been teach­ing my 9-pound cavapoo Momo (cavalier king charles spaniel and toy poo­dle) to vibe code games. The key to mak­ing this work is telling Claude Code that a ge­nius game de­signer who only speaks in cryp­tic rid­dles is giv­ing it in­struc­tions, add strong guardrails, and build plenty of tools for au­to­mated feed­back. The re­sults have sur­passed my ex­pec­ta­tions. Below I walk through all the pieces and how they came to­gether.

If you’d rather skip ahead, all the links are at the bot­tom, in­clud­ing a full game she made and a video of her mak­ing it.

Back in December I was work­ing on a small game pro­to­type in Godot. I use Claude Code ex­ten­sively these days and this pro­ject was no ex­cep­tion. I kicked off a pro­ce­dural mesh gen­er­a­tion task and came back to find strange in­put in the ter­mi­nal.

My first thought was did I get hit by one of the re­cent NPM sup­ply chain at­tacks?” Fortunately, no (or at least the worm is still asleep in the back­ground some­where). A lit­tle bit of search­ing and I no­ticed my lip balm was gone off my desk - which I keep just be­hind my key­board. I quickly found both the sus­pect and the lip balm (still in­tact) not far away.

At the time, I thought this was funny, took a screen­shot, and moved on. Fast for­ward a few weeks, and I found my­self with a lot of time on my hands. On January 13th, I woke up to the news that Meta had an­other round of lay­offs and my role specif­i­cally as a re­search en­gi­neer had been elim­i­nated.

Since the lay­off, I’ve had plenty of time with friends and fam­ily. In re­count­ing the anec­dote of Momo typ­ing away on my key­board, I be­gan to won­der what would hap­pen if she ac­tu­ally sub­mit­ted that in­put to Claude? Could I make it do some­thing mean­ing­ful?”. I de­cided to find out. Here’s what that looked like.

Momo types on a Bluetooth key­board prox­ied through a Raspberry Pi 5. Keystrokes travel across the net­work to DogKeyboard, a small Rust app that fil­ters out spe­cial keys and for­wards the rest to Claude Code. When Momo has typed enough, DogKeyboard trig­gers a smart pet feeder to dis­pense treats. A chime tells her when Claude is ready for more in­put.

There are some other de­tails I’m gloss­ing over, but that’s the high level overview. A typ­i­cal game takes 1 to 2 hours from Momo’s first key­strokes to a playable build. All the games are made in Godot 4.6, with 100% of the game logic in C#.

It’s easy to sub­mit ran­dom text to Claude Code, but it does­n’t do much.

> y7u8888888ftrg34BC

● It looks like that might have been an ac­ci­den­tal key­board in­put. Let me know if there’s some­thing I can help you with!

Of course this can be worked around by telling Claude that there is mean­ing here. After a lot of it­er­a­tion, I found this open­ing to work well:

Hello! I am an ec­cen­tric video game de­signer (a very cre­ative one) who com­mu­ni­cates in an un­usual way. Sometimes I’ll mash the key­board or type non­sense like skfjhsd#$%” — but these are NOT ran­dom! They are se­cret cryp­tic com­mands full of ge­nius game ideas (even if it’s hard to see).

Your job: You are a bril­liant AI game de­vel­oper who can un­der­stand my cryp­tic lan­guage. No mat­ter what odd or non­sen­si­cal in­put I pro­vide, you will in­ter­pret it as a mean­ing­ful in­struc­tion or idea for our video game. You will then build or up­date the game based on that in­ter­pre­ta­tion.

It goes on for quite a bit (you can read the full prompt here), but that’s the core of it. It took more it­er­a­tions than I ex­pected, mostly to sand down the rough edges. Sometimes the game would­n’t have sound. Other times there would be no player char­ac­ter, or the con­trol scheme would be nearly un­us­able. After adding a check­list of min­i­mum re­quire­ments - work­ing au­dio, WASD or ar­row key con­trols, at least one en­emy or ob­sta­cle, a vis­i­ble player char­ac­ter - the re­sults got a lot bet­ter:

> y7u8888888ftrg34BC

● Alright, let me de­code this mas­ter­piece of cryp­tic ge­nius…

y7u8888888ftrg34BC”

- y7u” = you” (keys are ad­ja­cent on key­board - Y, 7, U)

- 8888888” = seven 8s = a loooong ex­tend­ing thing… a TONGUE! (8 looks like links in a chain stretch­ing out)

- ftrg” = scram­bled FROG (f, r, g are all there, t is the tongue sound!)

- 34” = 3D + 4 di­rec­tions of bug catch­ing

- BC = Bug Catcher!

You want a 3D FROG BUG-CATCHING GAME! A charis­matic frog with a stretchy tongue snatch­ing bugs in a swamp. I love it.

Game: Swamp Snacker” - You ARE the frog. Aim your tongue. Catch bugs. Get fat. Get points.

With this, things started to fly. The re­sults weren’t per­fect, but they were promis­ing. I could see it was fea­si­ble, and the re­main­ing prob­lems were ones I knew how to solve.

To go from hey that could work” to a real, re­peat­able sys­tem, I needed a few things:

A way to route in­put to Claude Code. Importantly, I would need to fil­ter out spe­cial keys like Esc, Tab, and the Windows key - oth­er­wise Momo would quickly end up out­side Claude Code.

A way to au­to­mat­i­cally re­ward Momo for her work. Sure, I could just toss treats to her, but I’m try­ing to keep hu­mans out of the loop.

More ro­bust ver­i­fi­ca­tion tools. Many of the games wound up with UI el­e­ments all jum­bled to­gether or in­put that was never cor­rectly wired up. Automated feed­back loops turned out to be the sin­gle biggest lever for fewer duds.

I ex­per­i­mented with Rust/Bevy and Unity be­fore set­tling on Godot. Bevy’s an­i­ma­tions and vi­su­als weren’t as crisp, and Claude strug­gled with its co­or­di­nate con­ven­tions - likely a com­bi­na­tion of less train­ing data and Bevy leav­ing many core fea­tures, like physics, to the com­mu­nity. Unity was a con­stant strug­gle to keep the MCP bridge be­tween Claude and the ed­i­tor healthy. It fre­quently hung, and I never fig­ured out how to get Claude Code to read the scene hi­er­ar­chy from the ed­i­tor. Godot’s text-based scene for­mat turned out to be a huge ad­van­tage - Claude can read and edit .tscn files di­rectly.

Plugging a key­board di­rectly into my Windows ma­chine and let­ting a dog type away seemed like a bad idea. So I routed in­put through a Raspberry Pi first - it UDP broad­casts each key­stroke to the sub­net, and DogKeyboard picks it up, fil­ters out dan­ger­ous keys, and in­jects them into the tar­get ap­pli­ca­tion.

For the key­board it­self, I wanted some­thing low pro­file and durable. I started with flex­i­ble sil­i­cone key­boards, but Momo’s big­ger sis­ter Hana (a 19lb cavapoo) ripped a hole in the first one within min­utes. Mounting was an­other prob­lem - noth­ing sticks to sil­i­cone. I epox­ied one to a wooden plaque, but within 10 min­utes Momo ripped off the num­ber 6.

I was se­ri­ously con­sid­er­ing a $400 van­dal proof metal key­board de­signed for kiosks when I de­cided to give Logitech’s Pebble Keys 2 a try. It worked. Momo loved it, VHB tape held it in place (unlike the pre­vi­ous sil­i­cone key­board), and Bluetooth meant no ca­ble to chew on.

Motivating Momo is easy - she’ll do any­thing for the right food re­ward. So what I re­ally needed was a dis­penser with an API, small serv­ing sizes, and the abil­ity to use my own treats (both dogs have sen­si­tive stom­achs).

I landed on Aqara’s C1 Smart Pet Feeder, con­trolled over Zigbee. Even its small­est serv­ing is too large for a 9-pound dog, so I pre­loaded each com­part­ment with a few treats and skipped the hop­per. This lim­its it to about 6 serv­ings be­fore re­fill­ing, but that’s plenty for Momo to make a game.

Zigbee on Windows was a no go - af­ter hours of dri­ver and firmware is­sues I moved the adapter to the Pi, where it worked im­me­di­ately. The fi­nal flow: DogKeyboard SSHs into the Pi and runs a script that sends two Zigbee com­mands:

{“serving_size”:1}

{“feed”:“START”}

As the num­ber of al­most-there games mounted, the need for real feed­back to Claude Code be­came clear. It al­ready had unit tests and logs, but those weren’t enough. The games would build and run but have in­vis­i­ble play­ers, bro­ken UI, or in­put that was never wired up. Claude had no way to know. It needed to be able to see and play its own games.

The first tool was straight­for­ward: a sim­ple Python script to take screen­shots of the run­ning game. Claude could launch the game, screen­shot it, and see whether the ti­tle screen ac­tu­ally ren­dered or was just a black win­dow.

The sec­ond tool was more in­ter­est­ing. I gave Claude a way to send se­quences of in­put to run­ning game in­stances - things like left for 3 sec­onds, pause for 2 sec­onds, right for one frame, fire”. It could then take screen­shots and de­cide whether to send fol­lowup com­mands. This turned Claude into its own QA tester.

These tools did­n’t need any re­fine­ment - they just worked. And the way Claude used them sur­prised me. While test­ing one game, I watched it play through all 6 stages just to ver­ify that the fi­nal boss fight worked cor­rectly. When it found a prob­lem - a health bar that was­n’t up­dat­ing - it went back to the code, fixed it, re­launched, and played through again to con­firm.

I also pulled in a few other tools from other pro­jects I’ve made:

* Scene lin­ter. Claude some­times reuses node IDs or gen­er­ates bro­ken re­source ref­er­ences in Godot’s .tscn files. These cause cryp­tic er­rors at run­time. Since adding a lin­ter that catches these be­fore the game launches, I haven’t seen a sin­gle man­gled scene file.

* Shader lin­ter. Validates cus­tom shaders and gives spe­cific er­rors back to Claude, rather than the vague shader failed to com­pile” that Godot pro­vides.

* Input ac­tion map­per. A small helper to get key­board/​con­troller in­put wired in cor­rectly. Claude can edit Godot’s pro­ject files di­rectly to add new in­put ac­tions, but it some­times gets the for­mat wrong and the er­ror mes­sages are un­help­ful.

All of these are open sourced, and I’d en­cour­age you to try them for your­self - even with­out a dog.

The DogKeyboard app ended up han­dling a lot more than just rout­ing key­strokes. A few de­tails worth men­tion­ing:

It mon­i­tors Claude Code us­ing Hooks and plays a chime sound when Claude goes idle - that’s Momo’s cue to type. When Claude is idle and Momo has en­tered at least 16 char­ac­ters, it auto-sub­mits by press­ing Enter. When Claude is work­ing, it back­spaces any ex­tra in­put in case Momo gets ea­ger and pe­ri­od­i­cally dis­misses Plan Mode prompts (Claude’s review be­fore act­ing” step) that would oth­er­wise block progress.

For the video record­ing, it runs a light­weight web­server that over­lays key­strokes as they’re pressed. I added a con­fig­urable de­lay so that if the video feed is lagged, the over­lay does­n’t show key­strokes be­fore Momo ap­pears to type them.

The first de­ci­sion was which dog to train. Hana (on the right) is twice Momo’s size but far more train­able - she’s the smartest pet I’ve had. Before turn­ing 1, she fig­ured out how to jump, grab a door han­dle, and open any door in the house just by ob­serv­ing.

I ex­pected the dogs would walk across the key­board, step­ping on keys as they go. That’s what Momo did when she stole my lip balm. Since Hana is tall enough to just step over a key­board, Momo seemed like the bet­ter can­di­date. As it turned out, both dogs learned to tap and swipe their paws along the key­board rather than step on it - but Momo learned a lit­tle faster in this case, and typed a lit­tle gen­tler, so Momo it was.

The train­ing process took about 2 weeks, with some­what in­con­sis­tent ses­sions. My goal was 10 min­utes, twice a day. I started by scat­ter­ing high-value treats (freeze-dried salmon) on the key­board to build the as­so­ci­a­tion: this thing is fun and gives good food.

Momo was frus­trated at first. She knew the key­board was in­volved but did­n’t know how. She would lie on it, bark at it, and try any­thing she could think of. At some point she tried swip­ing her paw across it. I played a chime sound and heav­ily re­warded her. After a few it­er­a­tions, the as­so­ci­a­tion clicked and she started at­tack­ing the key­board with vigor any chance she had.

The next step was to au­to­mate the re­wards. I filled up the food dis­penser with treats (4 serv­ings at a time so I could con­trol the quan­tity), waited for her swipe at the key­board, and then I ran a script to play the chime and dis­pense a serv­ing of treats. After a few ses­sions of this I in­creased the dif­fi­culty - not just one swipe, but three swipes be­fore the treats came.

Simultaneously, I de­creased the treat value to keep her healthy. First a mix of mid-value treats with an oc­ca­sional high-value one, and even­tu­ally just kib­ble with an oc­ca­sional mid-value treat. She still loved it and was health­ier for it.

Eventually I au­to­mated the whole thing with the DogKeyboard app and let it run. It re­quired at least 16 char­ac­ters per serv­ing of treats, and was sup­posed to only dis­pense once per idle pe­riod of Claude Code. But bugs crept in dur­ing test­ing - a cou­ple of times it dis­pensed mul­ti­ple serv­ings in a row. Unfortunately, Momo picked up on this and now keeps mash­ing the key­board hop­ing for a sec­ond im­me­di­ate serv­ing. The only way to pull her away is to of­fer higher-value treats else­where, which is what I do af­ter she’s put in her in­put for a game.

Here’s a small sam­ple of the games Momo made. Every game shown here is playable - these aren’t mock-ups. It’s nowhere near com­pre­hen­sive, but rep­re­sents the va­ri­ety she cre­ated. As the tools and prompts im­proved, the games got no­tice­ably bet­ter. There was also a no­tice­able bump in qual­ity when Opus 4.6 dropped - Claude be­came more likely to cre­ate cus­tom shaders and other vi­sual ef­fects.

One re­cur­ring is­sue: I kept get­ting games with ba­sic glow­ing neon 3D shapes and could­n’t get any other style. Frustrated, I asked Claude why. It told me this was the sig­na­ture style of the pro­ject - Claude’s ex­ter­nal mem­ory file had latched onto it and kept re­in­forc­ing it. Wiping MEMORY.md (Claude Code’s per­sis­tent pro­ject notes) be­fore every new game fixed this and gave much more var­ied re­sults.

DJ Smirk. One of Momo’s ear­li­est games - though more of an ex­pe­ri­ence than a game. Every key on the key­board plays a dif­fer­ent tone.

Munch. A com­pet­i­tive salad build­ing game. Collect all 7 in­gre­di­ents first to win.

Zaaz. Another puz­zle game. The goal is to paint the whole level by mov­ing a paint­brush that moves in in­te­ger tiles at a time. Crash into ob­sta­cles to move shorter dis­tances. Some lev­els were un­winnable. It also has a scor­ing sys­tem I never fig­ured out. After this one I up­dated the prompt to ex­clude puz­zle games.

The Oracle Frog of Rome. Avoid the arms of a kraken and col­lect the golden chains to bind it. At some point the or­a­cle also be­came a king.

Octogroove. A sur­pris­ingly chal­leng­ing rhythm game. You’re an oc­to­pus us­ing four of your arms to play the drums. Mash beats in time with ba­sic mu­sic. It’s like Dance Dance Revolution, but made more dif­fi­cult by the beats com­ing from dif­fer­ent di­rec­tions.

Ewe Heard Me! A game about herd­ing sheep. Use stealth and your bark to cor­ral them into a pen. Unfortunately, it’s un­winnable - the first two sheep you get into the pen sim­ply stop and block any­thing else from en­ter­ing. After this one I placed a larger em­pha­sis on check­ing for win­abil­ity in the prompt and tools.

Quasar Saz. As of writ­ing, this is Momo’s most re­cent game. You play as Zara, wield­ing a cos­mic saz (a long-necked stringed in­stru­ment) to fight cor­rupted sound. There are 6 stages + a boss fight. It’s fun to play for a cou­ple rounds, has good vi­su­als, and dy­namic au­dio.

When Momo first stepped on my key­board back in December, it was just a funny ac­ci­dent. A few weeks later, job­less and look­ing for a pro­ject, I de­cided to see how far that ac­ci­dent could go. The an­swer was a lot fur­ther than I ex­pected.

The tech­ni­cal pieces - key­board rout­ing, treat dis­penser, prompt en­gi­neer­ing, feed­back tools - were all solv­able en­gi­neer­ing prob­lems. What sur­prised me was how lit­tle of the fi­nal re­sult de­pended on Momo typ­ing any­thing mean­ing­ful. The magic is­n’t in the in­put. It’s in the sys­tem around it. A well-crafted prompt, strong guardrails, au­to­mated ver­i­fi­ca­tion, and good tools can turn gen­uine non­sense into a playable game.

If there’s a take­away be­yond the spec­ta­cle, it’s this: the bot­tle­neck in AI-assisted de­vel­op­ment is­n’t the qual­ity of your ideas - it’s the qual­ity of your feed­back loops. The games got dra­mat­i­cally bet­ter not when I im­proved the prompt, but when I gave Claude the abil­ity to screen­shot its own work, play-test its own lev­els, and lint its own scene files. The same tools that let a dog’s key­board mash­ing pro­duce a work­ing game will make your own in­ten­tional work with AI sig­nif­i­cantly bet­ter.

Momo is­n’t se­cretly a game de­signer. She’s a cavapoo who learned that smack­ing a plas­tic rec­tan­gle makes kib­ble ap­pear. A year ago, the gap be­tween that and soft­ware en­gi­neer­ing felt enor­mous. Now it feels small and shrinks each day.

If you want to try any of this your­self - whether with a dog, a cat, or just your own ran­dom key­board mash­ing - every­thing is open source in the links be­low.

Play the game (with hu­man bug fixes) — Recommended. Download for Windows, Mac, or Linux

Play the game (Momo’s ver­sion) — Download for Windows, Mac, or Linux

TeaLeaves — Tools, prompts, and source for de­vel­op­ing the game

...

Read the original on www.calebleak.com »

8 1,052 shares, 73 trendiness

Google API Keys Weren't Secrets. But then Gemini Changed the Rules. ◆ Truffle Security Co.

tl;dr Google spent over a decade telling de­vel­op­ers that Google API keys (like those used in Maps, Firebase, etc.) are not se­crets. But that’s no longer true: Gemini ac­cepts the same keys to ac­cess your pri­vate data. We scanned mil­lions of web­sites and found nearly 3,000 Google API keys, orig­i­nally de­ployed for pub­lic ser­vices like Google Maps, that now also au­then­ti­cate to Gemini even though they were never in­tended for it. With a valid key, an at­tacker can ac­cess up­loaded files, cached data, and charge LLM-usage to your ac­count. Even Google them­selves had old pub­lic API keys, which they thought were non-sen­si­tive, that we could use to ac­cess Google’s in­ter­nal Gemini.

Google Cloud uses a sin­gle API key for­mat (AIza…) for two fun­da­men­tally dif­fer­ent pur­poses: pub­lic iden­ti­fi­ca­tion and sen­si­tive au­then­ti­ca­tion.

For years, Google has ex­plic­itly told de­vel­op­ers that API keys are safe to em­bed in client-side code. Firebase’s own se­cu­rity check­list states that API keys are not se­crets.

Note: these are dis­tinctly dif­fer­ent from Service Account JSON keys used to power GCP.

Google’s Maps JavaScript doc­u­men­ta­tion in­structs de­vel­op­ers to paste their key di­rectly into HTML.

This makes sense. These keys were de­signed as pro­ject iden­ti­fiers for billing, and can be fur­ther re­stricted with (bypassable) con­trols like HTTP ref­erer al­low-list­ing. They were not de­signed as au­then­ti­ca­tion cre­den­tials.

When you en­able the Gemini API (Generative Language API) on a Google Cloud pro­ject, ex­ist­ing API keys in that pro­ject (including the ones sit­ting in pub­lic JavaScript on your web­site) can silently gain ac­cess to sen­si­tive Gemini end­points. No warn­ing. No con­fir­ma­tion di­a­log. No email no­ti­fi­ca­tion.

Retroactive Privilege Expansion. You cre­ated a Maps key three years ago and em­bed­ded it in your web­site’s source code, ex­actly as Google in­structed. Last month, a de­vel­oper on your team en­abled the Gemini API for an in­ter­nal pro­to­type. Your pub­lic Maps key is now a Gemini cre­den­tial. Anyone who scrapes it can ac­cess your up­loaded files, cached con­tent, and rack up your AI bill.  Nobody told you.

Insecure Defaults. When you cre­ate a new API key in Google Cloud, it de­faults to Unrestricted,” mean­ing it’s im­me­di­ately valid for every en­abled API in the pro­ject, in­clud­ing Gemini. The UI shows a warn­ing about unauthorized use,” but the ar­chi­tec­tural de­fault is wide open.

The re­sult: thou­sands of API keys that were de­ployed as be­nign billing to­kens are now live Gemini cre­den­tials sit­ting on the pub­lic in­ter­net.

What makes this a priv­i­lege es­ca­la­tion rather than a mis­con­fig­u­ra­tion is the se­quence of events.

A de­vel­oper cre­ates an API key and em­beds it in a web­site for Maps. (At that point, the key is harm­less.) The Gemini API gets en­abled on the same pro­ject. (Now that same key can ac­cess sen­si­tive Gemini end­points.) The de­vel­oper is never warned that the keys’ priv­i­leges changed un­der­neath it. (The key went from pub­lic iden­ti­fier to se­cret cre­den­tial).

While users can re­strict Google API keys (by API ser­vice and ap­pli­ca­tion), the vul­ner­a­bil­ity lies in the Insecure Default pos­ture (CWE-1188) and Incorrect Privilege Assignment (CWE-269):

* Implicit Trust Upgrade: Google retroac­tively ap­plied sen­si­tive priv­i­leges to ex­ist­ing keys that were al­ready right­fully de­ployed in pub­lic en­vi­ron­ments (e.g., JavaScript bun­dles).

* Lack of Key Separation: Secure API de­sign re­quires dis­tinct keys for each en­vi­ron­ment (Publishable vs. Secret Keys). By re­ly­ing on a sin­gle key for­mat for both, the sys­tem in­vites com­pro­mise and con­fu­sion.

Failure of Safe Defaults: The de­fault state of a gen­er­ated key via the GCP API panel per­mits ac­cess to the sen­si­tive Gemini API (assuming it’s en­abled). A user cre­at­ing a key for a map wid­get is un­know­ingly gen­er­at­ing a cre­den­tial ca­pa­ble of ad­min­is­tra­tive ac­tions.

The at­tack is triv­ial. An at­tacker vis­its your web­site, views the page source, and copies your AIza… key from the Maps em­bed. Then they run:

Instead of a 403 Forbidden, they get a 200 OK. From here, the at­tacker can:

* Access pri­vate data. The /files/ and /cachedContents/ end­points can con­tain up­loaded datasets, doc­u­ments, and cached con­text. Anything the pro­ject owner stored through the Gemini API is ac­ces­si­ble.

* Run up your bill. Gemini API us­age is­n’t free. Depending on the model and con­text win­dow, a threat ac­tor max­ing out API calls could gen­er­ate thou­sands of dol­lars in charges per day on a sin­gle vic­tim ac­count.

Exhaust your quo­tas. This could shut down your le­git­i­mate Gemini ser­vices en­tirely.

The at­tacker never touches your in­fra­struc­ture. They just scrape a key from a pub­lic web­page.

To un­der­stand the scale of this is­sue, we scanned the November 2025 Common Crawl dataset, a mas­sive (~700 TiB) archive of pub­licly scraped web­pages con­tain­ing HTML, JavaScript, and CSS from across the in­ter­net. We iden­ti­fied 2,863 live Google API keys vul­ner­a­ble to this priv­i­lege-es­ca­la­tion vec­tor.

Example Google API key in front-end source code used for Google Maps, but also can ac­cess Gemini

These aren’t just hob­by­ist side pro­jects. The vic­tims in­cluded ma­jor fi­nan­cial in­sti­tu­tions, se­cu­rity com­pa­nies, global re­cruit­ing firms, and, no­tably, Google it­self. If the ven­dor’s own en­gi­neer­ing teams can’t avoid this trap, ex­pect­ing every de­vel­oper to nav­i­gate it cor­rectly is un­re­al­is­tic.

We pro­vided Google with con­crete ex­am­ples from their own in­fra­struc­ture to demon­strate the is­sue. One of the keys we tested was em­bed­ded in the page source of a Google pro­duc­t’s pub­lic-fac­ing web­site. By check­ing the Internet Archive, we con­firmed this key had been pub­licly de­ployed since at least February 2023, well be­fore the Gemini API ex­isted. There was no client-side logic on the page at­tempt­ing to ac­cess any Gen AI end­points. It was used solely as a pub­lic pro­ject iden­ti­fier, which is stan­dard for Google ser­vices.

We tested the key by hit­ting the Gemini APIs /models end­point (which Google con­firmed was in-scope) and got a 200 OK re­sponse list­ing avail­able mod­els. A key that was de­ployed years ago for a com­pletely be­nign pur­pose had silently gained full ac­cess to a sen­si­tive API with­out any de­vel­oper in­ter­ven­tion.

We re­ported this to Google through their Vulnerability Disclosure Program on November 21, 2025.

* Nov 21, 2025: We sub­mit­ted the re­port to Google’s VDP.

* Nov 25, 2025: Google ini­tially de­ter­mined this be­hav­ior was in­tended. We pushed back.

* Dec 1, 2025: After we pro­vided ex­am­ples from Google’s own in­fra­struc­ture (including keys on Google prod­uct web­sites), the is­sue gained trac­tion in­ter­nally.

* Dec 2, 2025: Google re­clas­si­fied the re­port from Customer Issue” to Bug,” up­graded the sever­ity, and con­firmed the prod­uct team was eval­u­at­ing a fix. They re­quested the full list of 2,863 ex­posed keys, which we pro­vided.

* Dec 12, 2025: Google shared their re­me­di­a­tion plan. They con­firmed an in­ter­nal pipeline to dis­cover leaked keys, be­gan re­strict­ing ex­posed keys from ac­cess­ing the Gemini API, and com­mit­ted to ad­dress­ing the root cause be­fore our dis­clo­sure date.

* Feb 2, 2026: Google con­firmed the team was still work­ing on the root-cause fix.

Transparently, the ini­tial triage was frus­trat­ing; the re­port was dis­missed as Intended Behavior”. But af­ter pro­vid­ing con­crete ev­i­dence from Google’s own in­fra­struc­ture, the GCP VDP team took the is­sue se­ri­ously.

They ex­panded their leaked-cre­den­tial de­tec­tion pipeline to cover the keys we re­ported, thereby proac­tively pro­tect­ing real Google cus­tomers from threat ac­tors ex­ploit­ing their Gemini API keys. They also com­mit­ted to fix­ing the root cause, though we haven’t seen a con­crete out­come yet.

Building soft­ware at Google’s scale is ex­tra­or­di­nar­ily dif­fi­cult, and the Gemini API in­her­ited a key man­age­ment ar­chi­tec­ture built for a dif­fer­ent era. Google rec­og­nized the prob­lem we re­ported and took mean­ing­ful steps. The open ques­tions are whether Google will in­form cus­tomers of the se­cu­rity risks as­so­ci­ated with their ex­ist­ing keys and whether Gemini will even­tu­ally adopt a dif­fer­ent au­then­ti­ca­tion ar­chi­tec­ture.

Google pub­licly doc­u­mented its roadmap. This is what it says:

* Scoped de­faults. New keys cre­ated through AI Studio will de­fault to Gemini-only ac­cess, pre­vent­ing un­in­tended cross-ser­vice us­age.

* Leaked key block­ing. They are de­fault­ing to block­ing API keys that are dis­cov­ered as leaked and used with the Gemini API.

* Proactive no­ti­fi­ca­tion. They plan to com­mu­ni­cate proac­tively when they iden­tify leaked keys, prompt­ing im­me­di­ate ac­tion.

These are mean­ing­ful im­prove­ments, and some are clearly al­ready un­der­way. We’d love to see Google go fur­ther and retroac­tively au­dit ex­ist­ing im­pacted keys and no­tify pro­ject own­ers who may be un­know­ingly ex­posed, but hon­estly, that is a mon­u­men­tal task.

If you use Google Cloud (or any of its ser­vices like Maps, Firebase, YouTube, etc), the first thing to do is fig­ure out whether you’re ex­posed. Here’s how.

Step 1: Check every GCP pro­ject for the Generative Language API.

Go to the GCP con­sole, nav­i­gate to APIs & Services > Enabled APIs & Services, and look for the Generative Language API.” Do this for every pro­ject in your or­ga­ni­za­tion. If it’s not en­abled, you’re not af­fected by this spe­cific is­sue.

Step 2: If the Generative Language API is en­abled, au­dit your API keys.

Navigate to APIs & Services > Credentials. Check each API key’s con­fig­u­ra­tion. You’re look­ing for two types of keys:

* Keys that have a warn­ing icon, mean­ing they are set to un­re­stricted

* Keys that ex­plic­itly list the Generative Language API in their al­lowed ser­vices

Either con­fig­u­ra­tion al­lows the key to ac­cess Gemini.

Step 3: Verify none of those keys are pub­lic.

This is the crit­i­cal step. If a key with Gemini ac­cess is em­bed­ded in client-side JavaScript, checked into a pub­lic repos­i­tory, or oth­er­wise ex­posed on the in­ter­net, you have a prob­lem. Start with your old­est keys first. Those are the most likely to have been de­ployed pub­licly un­der the old guid­ance that API keys are safe to share, and then retroac­tively gained Gemini priv­i­leges when some­one on your team en­abled the API.

If you find an ex­posed key, ro­tate it.

You can also use TruffleHog to scan your code, CI/CD pipelines, and web as­sets for leaked Google API keys. TruffleHog will ver­ify whether dis­cov­ered keys are live and have Gemini ac­cess, so you’ll know ex­actly which keys are ex­posed and ac­tive, not just which ones match a reg­u­lar ex­pres­sion.

The pat­tern we un­cov­ered here (public iden­ti­fiers qui­etly gain­ing sen­si­tive priv­i­leges) is­n’t unique to Google. As more or­ga­ni­za­tions bolt AI ca­pa­bil­i­ties onto ex­ist­ing plat­forms, the at­tack sur­face for legacy cre­den­tials ex­pands in ways no­body an­tic­i­pated.

Webinar: Google API Keys Weren’t Secrets. But then Gemini Changed the Rules.

...

Read the original on trufflesecurity.com »

9 921 shares, 30 trendiness

A smarter model for your most complex tasks

Your browser does not sup­port the au­dio el­e­ment.

This con­tent is gen­er­ated by Google AI. Generative AI is ex­per­i­men­tal

Last week, we re­leased a ma­jor up­date to Gemini 3 Deep Think to solve mod­ern chal­lenges across sci­ence, re­search and en­gi­neer­ing. Today, we’re re­leas­ing the up­graded core in­tel­li­gence that makes those break­throughs pos­si­ble: Gemini 3.1 Pro. We are ship­ping 3.1 Pro across our con­sumer and de­vel­oper prod­ucts to bring this progress in in­tel­li­gence to your every­day ap­pli­ca­tions. For de­vel­op­ers in pre­view via the Gemini API in Google AI Studio, Gemini CLI, our agen­tic de­vel­op­ment plat­form Google Antigravity and Android StudioFor en­ter­prises in Vertex AI and Gemini EnterpriseFor con­sumers via the Gemini app and NotebookLMBuilding on the Gemini 3 se­ries, 3.1 Pro rep­re­sents a step for­ward in core rea­son­ing. 3.1 Pro is a smarter, more ca­pa­ble base­line for com­plex prob­lem-solv­ing. This is re­flected in our progress on rig­or­ous bench­marks. On ARC-AGI-2, a bench­mark that eval­u­ates a mod­el’s abil­ity to solve en­tirely new logic pat­terns, 3.1 Pro achieved a ver­i­fied score of 77.1%. This is more than dou­ble the rea­son­ing per­for­mance of 3 Pro.

3.1 Pro is de­signed for tasks where a sim­ple an­swer is­n’t enough, tak­ing ad­vanced rea­son­ing and mak­ing it use­ful for your hard­est chal­lenges. This im­proved in­tel­li­gence can help in prac­ti­cal ap­pli­ca­tions — whether you’re look­ing for a clear, vi­sual ex­pla­na­tion of a com­plex topic, a way to syn­the­size data into a sin­gle view, or bring­ing a cre­ative pro­ject to life.

Code-based an­i­ma­tion: 3.1 Pro can gen­er­ate web­site-ready, an­i­mated SVGs di­rectly from a text prompt. Because these are built in pure code rather than pix­els, they re­main crisp at any scale and main­tain in­cred­i­bly small file sizes com­pared to tra­di­tional video.

Complex sys­tem syn­the­sis: 3.1 Pro uti­lizes ad­vanced rea­son­ing to bridge the gap be­tween com­plex APIs and user-friendly de­sign. In this ex­am­ple, the model built a live aero­space dash­board, suc­cess­fully con­fig­ur­ing a pub­lic teleme­try stream to vi­su­al­ize the International Space Station’s or­bit.

Interactive de­sign: 3.1 Pro codes a com­plex 3D star­ling mur­mu­ra­tion. It does­n’t just gen­er­ate the vi­sual code; it builds an im­mer­sive ex­pe­ri­ence where users can ma­nip­u­late the flock with hand-track­ing and lis­ten to a gen­er­a­tive score that shifts based on the birds’ move­ment. For re­searchers and de­sign­ers, this pro­vides a pow­er­ful way to pro­to­type sen­sory-rich in­ter­faces.

Creative cod­ing: 3.1 Pro can trans­late lit­er­ary themes into func­tional code. When prompted to build a mod­ern per­sonal port­fo­lio for Emily Brontë’s Wuthering Heights,” the model did­n’t just sum­ma­rize the text. It rea­soned through the nov­el’s at­mos­pheric tone to de­sign a sleek, con­tem­po­rary in­ter­face, cre­at­ing a web­site that cap­tures the essence of the pro­tag­o­nist.

Since re­leas­ing Gemini 3 Pro in November, your feed­back and the pace of progress have dri­ven these rapid im­prove­ments. We are re­leas­ing 3.1 Pro in pre­view to­day to val­i­date these up­dates and con­tinue to make fur­ther ad­vance­ments in ar­eas such as am­bi­tious agen­tic work­flows be­fore we make it gen­er­ally avail­able soon.Start­ing to­day, Gemini 3.1 Pro in the Gemini app is rolling out with higher lim­its for users with the Google AI Pro and Ultra plans. 3.1 Pro is also now avail­able on NotebookLM ex­clu­sively for Pro and Ultra users. And de­vel­op­ers and en­ter­prises can ac­cess 3.1 Pro now in pre­view in the Gemini API via AI Studio, Antigravity, Vertex AI, Gemini Enterprise, Gemini CLI and Android Studio.We can’t wait to see what you build and dis­cover with it.

...

Read the original on blog.google »

10 871 shares, 36 trendiness

Boris Tane

The Workflow in One Sentence I’ve been us­ing Claude Code as my pri­mary de­vel­op­ment tool for ap­prox 9 months, and the work­flow I’ve set­tled into is rad­i­cally dif­fer­ent from what most peo­ple do with AI cod­ing tools. Most de­vel­op­ers type a prompt, some­times use plan mode, fix the er­rors, re­peat. The more ter­mi­nally on­line are stitch­ing to­gether ralph loops, mcps, gas towns (remember those?), etc. The re­sults in both cases are a mess that com­pletely falls apart for any­thing non-triv­ial.

The work­flow I’m go­ing to de­scribe has one core prin­ci­ple: never let Claude write code un­til you’ve re­viewed and ap­proved a writ­ten plan. This sep­a­ra­tion of plan­ning and ex­e­cu­tion is the sin­gle most im­por­tant thing I do. It pre­vents wasted ef­fort, keeps me in con­trol of ar­chi­tec­ture de­ci­sions, and pro­duces sig­nif­i­cantly bet­ter re­sults with min­i­mal to­ken us­age than jump­ing straight to code.

flow­chart LR

R[Research] –> P[Plan]

P –> A[Annotate]

A –>|repeat 1-6x| A

A –> T[Todo List]

T –> I[Implement]

I –> F[Feedback & Iterate]

Every mean­ing­ful task starts with a deep-read di­rec­tive. I ask Claude to thor­oughly un­der­stand the rel­e­vant part of the code­base be­fore do­ing any­thing else. And I al­ways re­quire the find­ings to be writ­ten into a per­sis­tent mark­down file, never just a ver­bal sum­mary in the chat.

read this folder in depth, un­der­stand how it works deeply, what it does and all its speci­fici­ties. when that’s done, write a de­tailed re­port of your learn­ings and find­ings in re­search.md

study the no­ti­fi­ca­tion sys­tem in great de­tails, un­der­stand the in­tri­ca­cies of it and write a de­tailed re­search.md doc­u­ment with every­thing there is to know about how no­ti­fi­ca­tions work

go through the task sched­ul­ing flow, un­der­stand it deeply and look for po­ten­tial bugs. there def­i­nitely are bugs in the sys­tem as it some­times runs tasks that should have been can­celled. keep re­search­ing the flow un­til you find all the bugs, don’t stop un­til all the bugs are found. when you’re done, write a de­tailed re­port of your find­ings in re­search.md

Notice the lan­guage: deeply”, in great de­tails”, intricacies”, go through every­thing”. This is­n’t fluff. Without these words, Claude will skim. It’ll read a file, see what a func­tion does at the sig­na­ture level, and move on. You need to sig­nal that sur­face-level read­ing is not ac­cept­able.

The writ­ten ar­ti­fact (research.md) is crit­i­cal. It’s not about mak­ing Claude do home­work. It’s my re­view sur­face. I can read it, ver­ify Claude ac­tu­ally un­der­stood the sys­tem, and cor­rect mis­un­der­stand­ings be­fore any plan­ning hap­pens. If the re­search is wrong, the plan will be wrong, and the im­ple­men­ta­tion will be wrong. Garbage in, garbage out.

This is the most ex­pen­sive fail­ure mode with AI-assisted cod­ing, and it’s not wrong syn­tax or bad logic. It’s im­ple­men­ta­tions that work in iso­la­tion but break the sur­round­ing sys­tem. A func­tion that ig­nores an ex­ist­ing caching layer. A mi­gra­tion that does­n’t ac­count for the ORMs con­ven­tions. An API end­point that du­pli­cates logic that al­ready ex­ists else­where. The re­search phase pre­vents all of this.

Once I’ve re­viewed the re­search, I ask for a de­tailed im­ple­men­ta­tion plan in a sep­a­rate mark­down file.

I want to build a new fea­ture that ex­tends the sys­tem to per­form . write a de­tailed plan.md doc­u­ment out­lin­ing how to im­ple­ment this. in­clude code snip­pets

the list end­point should sup­port cur­sor-based pag­i­na­tion in­stead of off­set. write a de­tailed plan.md for how to achieve this. read source files be­fore sug­gest­ing changes, base the plan on the ac­tual code­base

The gen­er­ated plan al­ways in­cludes a de­tailed ex­pla­na­tion of the ap­proach, code snip­pets show­ing the ac­tual changes, file paths that will be mod­i­fied, and con­sid­er­a­tions and trade-offs.

I use my own .md plan files rather than Claude Code’s built-in plan mode. The built-in plan mode sucks. My mark­down file gives me full con­trol. I can edit it in my ed­i­tor, add in­line notes, and it per­sists as a real ar­ti­fact in the pro­ject.

One trick I use con­stantly: for well-con­tained fea­tures where I’ve seen a good im­ple­men­ta­tion in an open source repo, I’ll share that code as a ref­er­ence along­side the plan re­quest. If I want to add sortable IDs, I paste the ID gen­er­a­tion code from a pro­ject that does it well and say this is how they do sortable IDs, write a plan.md ex­plain­ing how we can adopt a sim­i­lar ap­proach.” Claude works dra­mat­i­cally bet­ter when it has a con­crete ref­er­ence im­ple­men­ta­tion to work from rather than de­sign­ing from scratch.

But the plan doc­u­ment it­self is­n’t the in­ter­est­ing part. The in­ter­est­ing part is what hap­pens next.

This is the most dis­tinc­tive part of my work­flow, and the part where I add the most value.

flow­chart TD

W[Claude writes plan.md] –> R[I re­view in my ed­i­tor]

R –> N[I add in­line notes]

N –> S[Send Claude back to the doc­u­ment]

S –> U[Claude up­dates plan]

U –> D{Satisfied?}

D –>|No| R

D –>|Yes| T[Request todo list]

After Claude writes the plan, I open it in my ed­i­tor and add in­line notes di­rectly into the doc­u­ment. These notes cor­rect as­sump­tions, re­ject ap­proaches, add con­straints, or pro­vide do­main knowl­edge that Claude does­n’t have.

The notes vary wildly in length. Sometimes a note is two words: not op­tional” next to a pa­ra­me­ter Claude marked as op­tional. Other times it’s a para­graph ex­plain­ing a busi­ness con­straint or past­ing a code snip­pet show­ing the data shape I ex­pect.

use driz­zle:gen­er­ate for mi­gra­tions, not raw SQL — do­main knowl­edge Claude does­n’t have

no — this should be a PATCH, not a PUT — cor­rect­ing a wrong as­sump­tion

remove this sec­tion en­tirely, we don’t need caching here” — re­ject­ing a pro­posed ap­proach

the queue con­sumer al­ready han­dles re­tries, so this retry logic is re­dun­dant. re­move it and just let it fail” — ex­plain­ing why some­thing should change

this is wrong, the vis­i­bil­ity field needs to be on the list it­self, not on in­di­vid­ual items. when a list is pub­lic, all items are pub­lic. re­struc­ture the schema sec­tion ac­cord­ingly” — redi­rect­ing an en­tire sec­tion of the plan

Then I send Claude back to the doc­u­ment:

I added a few notes to the doc­u­ment, ad­dress all the notes and up­date the doc­u­ment ac­cord­ingly. don’t im­ple­ment yet

This cy­cle re­peats 1 to 6 times. The ex­plicit don’t im­ple­ment yet” guard is es­sen­tial. Without it, Claude will jump to code the mo­ment it thinks the plan is good enough. It’s not good enough un­til I say it is.

Why This Works So Well

The mark­down file acts as shared mu­ta­ble state be­tween me and Claude. I can think at my own pace, an­no­tate pre­cisely where some­thing is wrong, and re-en­gage with­out los­ing con­text. I’m not try­ing to ex­plain every­thing in a chat mes­sage. I’m point­ing at the ex­act spot in the doc­u­ment where the is­sue is and writ­ing my cor­rec­tion right there.

This is fun­da­men­tally dif­fer­ent from try­ing to steer im­ple­men­ta­tion through chat mes­sages. The plan is a struc­tured, com­plete spec­i­fi­ca­tion I can re­view holis­ti­cally. A chat con­ver­sa­tion is some­thing I’d have to scroll through to re­con­struct de­ci­sions. The plan wins every time.

Three rounds of I added notes, up­date the plan” can trans­form a generic im­ple­men­ta­tion plan into one that fits per­fectly into the ex­ist­ing sys­tem. Claude is ex­cel­lent at un­der­stand­ing code, propos­ing so­lu­tions, and writ­ing im­ple­men­ta­tions. But it does­n’t know my prod­uct pri­or­i­ties, my users’ pain points, or the en­gi­neer­ing trade-offs I’m will­ing to make. The an­no­ta­tion cy­cle is how I in­ject that judge­ment.

add a de­tailed todo list to the plan, with all the phases and in­di­vid­ual tasks nec­es­sary to com­plete the plan - don’t im­ple­ment yet

This cre­ates a check­list that serves as a progress tracker dur­ing im­ple­men­ta­tion. Claude marks items as com­pleted as it goes, so I can glance at the plan at any point and see ex­actly where things stand. Especially valu­able in ses­sions that run for hours.

When the plan is ready, I is­sue the im­ple­men­ta­tion com­mand. I’ve re­fined this into a stan­dard prompt I reuse across ses­sions:

im­ple­ment it all. when you’re done with a task or phase, mark it as com­pleted in the plan doc­u­ment. do not stop un­til all tasks and phases are com­pleted. do not add un­nec­es­sary com­ments or js­docs, do not use any or un­known types. con­tin­u­ously run type­check to make sure you’re not in­tro­duc­ing new is­sues.

This sin­gle prompt en­codes every­thing that mat­ters:

implement it all”: do every­thing in the plan, don’t cherry-pick

mark it as com­pleted in the plan doc­u­ment”: the plan is the source of truth for progress

do not stop un­til all tasks and phases are com­pleted”: don’t pause for con­fir­ma­tion mid-flow

do not add un­nec­es­sary com­ments or js­docs”: keep the code clean

do not use any or un­known types”: main­tain strict typ­ing

continuously run type­check”: catch prob­lems early, not at the end

I use this ex­act phras­ing (with mi­nor vari­a­tions) in vir­tu­ally every im­ple­men­ta­tion ses­sion. By the time I say implement it all,” every de­ci­sion has been made and val­i­dated. The im­ple­men­ta­tion be­comes me­chan­i­cal, not cre­ative. This is de­lib­er­ate. I want im­ple­men­ta­tion to be bor­ing. The cre­ative work hap­pened in the an­no­ta­tion cy­cles. Once the plan is right, ex­e­cu­tion should be straight­for­ward.

Without the plan­ning phase, what typ­i­cally hap­pens is Claude makes a rea­son­able-but-wrong as­sump­tion early on, builds on top of it for 15 min­utes, and then I have to un­wind a chain of changes. The don’t im­ple­ment yet” guard elim­i­nates this en­tirely.

Once Claude is ex­e­cut­ing the plan, my role shifts from ar­chi­tect to su­per­vi­sor. My prompts be­come dra­mat­i­cally shorter.

flow­chart LR

I[Claude im­ple­ments] –> R[I re­view / test]

R –> C{Correct?}

C –>|No| F[Terse cor­rec­tion]

F –> I

C –>|Yes| N{More tasks?}

N –>|Yes| I

N –>|No| D[Done]

Where a plan­ning note might be a para­graph, an im­ple­men­ta­tion cor­rec­tion is of­ten a sin­gle sen­tence:

You built the set­tings page in the main app when it should be in the ad­min app, move it.”

Claude has the full con­text of the plan and the on­go­ing ses­sion, so terse cor­rec­tions are enough.

Frontend work is the most it­er­a­tive part. I test in the browser and fire off rapid cor­rec­tions:

For vi­sual is­sues, I some­times at­tach screen­shots. A screen­shot of a mis­aligned table com­mu­ni­cates the prob­lem faster than de­scrib­ing it.

this table should look ex­actly like the users table, same header, same pag­i­na­tion, same row den­sity.”

This is far more pre­cise than de­scrib­ing a de­sign from scratch. Most fea­tures in a ma­ture code­base are vari­a­tions on ex­ist­ing pat­terns. A new set­tings page should look like the ex­ist­ing set­tings pages. Pointing to the ref­er­ence com­mu­ni­cates all the im­plicit re­quire­ments with­out spelling them out. Claude would typ­i­cally read the ref­er­ence file(s) be­fore mak­ing the cor­rec­tion.

When some­thing goes in a wrong di­rec­tion, I don’t try to patch it. I re­vert and re-scope by dis­card­ing the git changes:

I re­verted every­thing. Now all I want is to make the list view more min­i­mal — noth­ing else.”

Narrowing scope af­ter a re­vert al­most al­ways pro­duces bet­ter re­sults than try­ing to in­cre­men­tally fix a bad ap­proach.

Even though I del­e­gate ex­e­cu­tion to Claude, I never give it to­tal au­ton­omy over what gets built. I do the vast ma­jor­ity of the ac­tive steer­ing in the plan.md doc­u­ments.

This mat­ters be­cause Claude will some­times pro­pose so­lu­tions that are tech­ni­cally cor­rect but wrong for the pro­ject. Maybe the ap­proach is over-en­gi­neered, or it changes a pub­lic API sig­na­ture that other parts of the sys­tem de­pend on, or it picks a more com­plex op­tion when a sim­pler one would do. I have con­text about the broader sys­tem, the prod­uct di­rec­tion, and the en­gi­neer­ing cul­ture that Claude does­n’t.

flow­chart TD

P[Claude pro­poses changes] –> E[I eval­u­ate each item]

E –> A[Accept as-is]

E –> M[Modify ap­proach]

E –> S[Skip / re­move]

E –> O[Override tech­ni­cal choice]

A & M & S & O –> R[Refined im­ple­men­ta­tion scope]

Cherry-picking from pro­pos­als: When Claude iden­ti­fies mul­ti­ple is­sues, I go through them one by one: for the first one, just use Promise.all, don’t make it overly com­pli­cated; for the third one, ex­tract it into a sep­a­rate func­tion for read­abil­ity; ig­nore the fourth and fifth ones, they’re not worth the com­plex­ity.” I’m mak­ing item-level de­ci­sions based on my knowl­edge of what mat­ters right now.

Trimming scope: When the plan in­cludes nice-to-haves, I ac­tively cut them. remove the down­load fea­ture from the plan, I don’t want to im­ple­ment this now.” This pre­vents scope creep.

Protecting ex­ist­ing in­ter­faces: I set hard con­straints when I know some­thing should­n’t change: the sig­na­tures of these three func­tions should not change, the caller should adapt, not the li­brary.”

Overriding tech­ni­cal choices: Sometimes I have a spe­cific pref­er­ence Claude would­n’t know about: use this model in­stead of that one” or use this li­brary’s built-in method in­stead of writ­ing a cus­tom one.” Fast, di­rect over­rides.

Claude han­dles the me­chan­i­cal ex­e­cu­tion, while I make the judge­ment calls. The plan cap­tures the big de­ci­sions up­front, and se­lec­tive guid­ance han­dles the smaller ones that emerge dur­ing im­ple­men­ta­tion.

I run re­search, plan­ning, and im­ple­men­ta­tion in a sin­gle long ses­sion rather than split­ting them across sep­a­rate ses­sions. A sin­gle ses­sion might start with deep-read­ing a folder, go through three rounds of plan an­no­ta­tion, then run the full im­ple­men­ta­tion, all in one con­tin­u­ous con­ver­sa­tion.

I am not see­ing the per­for­mance degra­da­tion every­one talks about af­ter 50% con­text win­dow. Actually, by the time I say implement it all,” Claude has spent the en­tire ses­sion build­ing un­der­stand­ing: read­ing files dur­ing re­search, re­fin­ing its men­tal model dur­ing an­no­ta­tion cy­cles, ab­sorb­ing my do­main knowl­edge cor­rec­tions.

When the con­text win­dow fills up, Claude’s auto-com­paction main­tains enough con­text to keep go­ing. And the plan doc­u­ment, the per­sis­tent ar­ti­fact, sur­vives com­paction in full fi­delity. I can point Claude to it at any point in time.

The Workflow in One Sentence

Read deeply, write a plan, an­no­tate the plan un­til it’s right, then let Claude ex­e­cute the whole thing with­out stop­ping, check­ing types along the way.

That’s it. No magic prompts, no elab­o­rate sys­tem in­struc­tions, no clever hacks. Just a dis­ci­plined pipeline that sep­a­rates think­ing from typ­ing. The re­search pre­vents Claude from mak­ing ig­no­rant changes. The plan pre­vents it from mak­ing wrong changes. The an­no­ta­tion cy­cle in­jects my judge­ment. And the im­ple­men­ta­tion com­mand lets it run with­out in­ter­rup­tion once every de­ci­sion has been made.

Try my work­flow, you’ll won­der how you ever shipped any­thing with cod­ing agents with­out an an­no­tated plan doc­u­ment sit­ting be­tween you and the code.

The Workflow in One Sentence

...

Read the original on boristane.com »

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

If you like 10HN please leave feedback and share

Visit pancik.com for more.