going to test deep reply branch merge.

This commit is contained in:
ed committed 2026-08-24 23:26:44 -04:00
1 parent dc635997a9
commit 8741077173
40 files changed
+266 -72

No files matched your search

+4
View File
@@ -131,3 +131,7 @@ Quartz remotes live in `site/` (`cd site; git pull`). After a Quartz pull, re-co
Current major is **Quartz 5**. Edit `publish/quartz.config.yaml`, not the clone. Canvas uses `@quartz-community/canvas-page`. Drafts use `@quartz-community/remove-draft`.
Engine license: MIT (Quartz). Vault content: Unlicense (`LICENSE`).
## Notice
I'm not a web dev. Look away at the scripts this is just done because it doesn't exist, so it was done badly.
@@ -7,8 +7,8 @@ author: "0x44"
handle: 0x44_
post_id: "1951048949667619280"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Matthew Rusch"
handle: 2Sexy4MyGPU
post_id: "1950935584714654191"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Nicholas Wilt"
handle: CUDAHandbook
post_id: "1951018231856312578"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Nick Tasios"
handle: Karyuutensei
post_id: "1950905493053923408"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "vulkan tamer"
handle: NickDrisc0ll
post_id: "1950899486059851961"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "PunteroCrudo"
handle: PunteroCrudo
post_id: "1950929528131326083"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Robert William Cummings"
handle: SquaredApe
post_id: "1950915633811534012"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Sterence"
handle: Stererence
post_id: "1950970860694913461"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "VicThor Dev"
handle: VicThorDev6
post_id: "1950927812401004774"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Naf 👨🏽‍💻"
handle: _naf_90
post_id: "1950948334002159684"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "David Sena"
handle: biovf
post_id: "1950863824522739861"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "D J"
handle: cyberbemon
post_id: "1950940978229236154"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Ian Hanschen"
handle: furan
post_id: "1950955895351083303"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Ian Hanschen"
handle: furan
post_id: "1950956007737463015"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "NEOAethyr"
handle: konigssohne
post_id: "1950961762419044639"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Arnaud Carré"
handle: leonard_coder
post_id: "1950889332375470135"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Manuel Montoto"
handle: m6502
post_id: "1950913023851278616"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "𝖒𝖒𝖆𝖑𝖊𝖝"
handle: mmalex
post_id: "1950886322341630045"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Chrinovic Mukanya"
handle: mu_chrinovic
post_id: "1950864277624979480"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Michael Spector"
handle: nullspector
post_id: "1950865695140364456"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Boga"
handle: o__boga
post_id: "1950939824183001106"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "opdroid1234"
handle: opdroid1234
post_id: "1950902422362046528"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "PlasmaBitcoin"
handle: plasmabitcoin
post_id: "1950963702569771336"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "unevenprankster"
handle: pranksterware
post_id: "1950872517700206902"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "᠌᠌ ᠌ ᠌"
handle: roasted6read
post_id: "1950914379412566114"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Rúben Cabaço"
handle: ruben_cabaco
post_id: "1950894646881611793"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Simplex"
handle: simplex_fx
post_id: "1951011391827010009"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Vlad Erium 🇯🇵"
handle: ssh4net
post_id: "1950870099809681578"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Mark Coleman"
handle: thecolemanthe
post_id: "1950956186783895852"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Alonne"
handle: totalwert
post_id: "1950900720770625916"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -31,6 +31,7 @@ parent_post_id: "1950860870818439202"
@NOTimothyLottes
<video controls src="https://video.twimg.com/tweet_video/GxL93LLW8AA-5hM.mp4"></video>
## Related
- Spine: [[archive/threads/NOTimothyLottes/2025-07-31-people-claim-assembly-is-hard-a-good-counter-to]]
@@ -0,0 +1,35 @@
---
title: "@NOTimothyLottes I'm gonna start pretending I understood everything you said"
type: archive
source: twitter
source_url: "https://x.com/AnshumanKhanna5/status/1951095609567617288"
author: "Anshuman Khanna"
handle: AnshumanKhanna5
post_id: "1951095609567617288"
date: 2025-08-01
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
- NOTimothyLottes
description: "@NOTimothyLottes I'm gonna start pretending I understood everything you said"
in_reply_to: ""
parent_post_id: "1950868863836410096"
---
## Source
- URL: https://x.com/AnshumanKhanna5/status/1951095609567617288
- Author: Anshuman Khanna (@AnshumanKhanna5)
- Posted: 2025-08-01 01:40:17
## Branch
**1/**
@NOTimothyLottes I'm gonna start pretending I understood everything you said
## Related
- Spine: [[archive/threads/NOTimothyLottes/2025-07-31-people-claim-assembly-is-hard-a-good-counter-to]]
@@ -7,8 +7,8 @@ author: "Anshuman Khanna"
handle: AnshumanKhanna5
post_id: "1951095292377571395"
date: 2025-08-01
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Ægis Stranding"
handle: John31625320803
post_id: "1951198394329829393"
date: 2025-08-01
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Karthik Kumar Viswanathan"
handle: _vkaku
post_id: "1951170620542296537"
date: 2025-08-01
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "Ben Schulz"
handle: schulzb589
post_id: "1951093394786361833"
date: 2025-08-01
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -7,8 +7,8 @@ author: "NOTimothyLottes"
handle: NOTimothyLottes
post_id: "1950860870818439202"
date: 2025-07-31
archived: 2026-08-23
draft: true
archived: 2026-08-24
draft: false
tags:
- archive
- twitter
@@ -50,3 +50,5 @@ So Valve standardizing on Proton saves a lot of trouble with porting across ABIs
**6/**
Few probably remember the old Unix before pthreads and ELF. It was better then. Much easier to use fork and shm to do platform audio than today. And that holds for all the vintage workstations (which I did implement back in the day)
Branches: [[archive/threads/NOTimothyLottes/2025-07-31-people-claim-assembly-is-hard-a-good-counter-to/2025-08-01-AnshumanKhanna5-im-gonna-start-pretending-i-understood-everything]]
+4 -1
View File
@@ -9,6 +9,7 @@ python C:\projects\Threadwell\scripts\twitter\tw.py graph --id <snowflake>
python C:\projects\Threadwell\scripts\twitter\tw.py refresh --id <snowflake> --tip
python C:\projects\Threadwell\scripts\twitter\tw.py refresh --id <spine-tip> --tip --branch <branch-tip>
python C:\projects\Threadwell\scripts\twitter\tw.py add-branch --id <original-spine-tip> --from <reply-node>
python C:\projects\Threadwell\scripts\twitter\tw.py emit --id <spine-tip> --tip --preserve-existing
python C:\projects\Threadwell\scripts\twitter\tw.py audit-media --id <snowflake>
python C:\projects\Threadwell\scripts\twitter\tw.py publish --id <snowflake>
python C:\projects\Threadwell\scripts\twitter\tw.py sync --handle <handle>
@@ -23,7 +24,9 @@ python C:\projects\Threadwell\scripts\twitter\tw.py migrate-media --all --apply
`refresh` = refetch (cookies) + ingest + emit `--force` + media_merge. `--tip` treats `--id` as the tip and walks back to the OP regardless of handle. Repeatable `--branch <tip-id>` captures only those additional explicit tip paths and merges them into the same `thread_data.json`; it does not discover replies automatically. Captures run sequentially with gallery-dl retries disabled and five-second extractor/request pacing. Each thread emits exactly one archive directory owned by the OP (the post with `reply_to_id == None`); cross-author responders do not get a directory. Does not flip `draft`. Does not commit. Frozen ids abort.
`add-branch` incrementally extends an already emitted thread. `--id` must be the original stored spine tip; repeatable `--from <reply-node>` values are each captured once. The command locally retains each node's missing attachment path and entire visible descendant subtree, merges only previously absent posts into the existing `thread_data.json`, downloads media for the new posts, and re-emits with the original spine unchanged. It preserves existing origin/local/fallback/derived/OCR media records and adds provider-origin plus local-copy records for new branch media. A visible leaf is only the deepest reply returned by that capture; the command does not query descendants individually or claim provider-wide completeness. Fallback upload remains a separate explicit action after origin unavailability is confirmed.
`emit --preserve-existing` is the safe completion step after inspecting a fresh scratch capture. Fresh records win for ids the provider returned; posts present only in the existing emitted `thread_data.json` are retained after the command verifies both captures share a conversation root. This prevents an incomplete gallery-dl refresh from deleting archived replies. The merged scratch dataset keeps the requested `--id` as its stored spine tip before normal emission and media-manifest reconciliation.
`add-branch` incrementally extends an already emitted thread. `--id` must be the original stored spine tip; repeatable `--from <reply-node>` values are each captured once. The command locally retains each node's missing attachment path and entire visible descendant subtree, merges only previously absent posts into the existing `thread_data.json`, downloads media for the new posts, and re-emits with the original spine unchanged. It preserves existing origin/local/fallback/derived/OCR media records and adds provider-origin plus local-copy records for new branch media. A visible leaf is only the deepest reply returned by that capture; the command does not query descendants individually or claim provider-wide completeness. Gallery-dl may return only the ancestor chain when replies are behind nested show-more cursors; then the command adds nothing and known reply/stream-tip ids must be supplied explicitly. Fallback upload remains a separate explicit action after origin unavailability is confirmed.
Usual publish path after you like the note:
@@ -17,11 +17,13 @@ from twitter.tw import (
_copy_selected_media,
_gallery_base_args,
_merge_branch_posts,
_merge_existing_capture,
_merge_gallery_files,
_select_branch_capture,
_validate_capture_ids,
build_parser,
cmd_add_branch,
cmd_emit,
)
@@ -333,6 +335,107 @@ class TwMediaCommandTests(unittest.TestCase):
with self.assertRaisesRegex(SystemExit, "stored spine tip"):
cmd_add_branch(Namespace(id="200", from_ids=["300"]))
def test_merge_existing_capture_keeps_fresh_and_retains_missing(self) -> None:
old_100 = make_post("100", None)
old_200 = make_post("200", "100")
old_300 = make_post("300", "100")
existing = ThreadData(
root_post_id="100",
posts=(old_100, old_200, old_300),
source_url="https://x.com/i/status/100",
)
fresh_100 = replace(old_100, text="fresh root")
fresh_200 = replace(old_200, text="fresh tip")
fresh_400 = make_post("400", "200")
fresh = ThreadData(
root_post_id="200",
posts=(fresh_100, fresh_200, fresh_400),
source_url="https://x.com/i/status/200",
)
merged, retained = _merge_existing_capture(fresh, existing, "200")
self.assertEqual(
merged.posts,
(fresh_100, fresh_200, fresh_400, old_300),
)
self.assertEqual(merged.root_post_id, "200")
self.assertEqual(merged.source_url, fresh.source_url)
self.assertEqual(retained, ("300",))
def test_merge_existing_capture_rejects_different_conversation(self) -> None:
existing = ThreadData(
root_post_id="100",
posts=(make_post("100", None),),
source_url="https://x.com/i/status/100",
)
fresh = ThreadData(
root_post_id="200",
posts=(make_post("200", None),),
source_url="https://x.com/i/status/200",
)
with self.assertRaisesRegex(SystemExit, "different conversation"):
_merge_existing_capture(fresh, existing, "200")
def test_emit_and_refresh_accept_preserve_existing(self) -> None:
emit_args = build_parser().parse_args([
"emit", "--id", "200", "--tip", "--preserve-existing",
])
refresh_args = build_parser().parse_args([
"refresh", "--id", "200", "--tip", "--preserve-existing",
])
self.assertTrue(emit_args.preserve_existing)
self.assertTrue(refresh_args.preserve_existing)
def test_emit_preserve_existing_merges_before_running_emitter(self) -> None:
with TemporaryDirectory() as tmp:
root = Path(tmp)
scratch = root / "refetch_200"
assets = root / "assets"
notes = root / "notes"
notes.mkdir()
existing = ThreadData(
root_post_id="100",
posts=(
make_post("100", None),
make_post("200", "100"),
make_post("300", "100"),
),
source_url="https://x.com/i/status/100",
)
fresh = ThreadData(
root_post_id="200",
posts=(
replace(make_post("100", None), text="fresh"),
replace(make_post("200", "100"), text="fresh"),
make_post("400", "200"),
),
source_url="https://x.com/i/status/200",
)
write_test_thread(assets / "thread_data.json", existing)
write_test_thread(scratch / "thread_data.json", fresh)
with (
patch.object(tw_module, "scratch_dir", return_value=scratch),
patch.object(tw_module, "dump_dir", return_value=root / "dump"),
patch.object(tw_module, "locate", return_value=(assets, notes)),
patch.object(tw_module, "locate_all", return_value=[]),
patch.object(tw_module, "check_frozen"),
patch.object(tw_module, "run") as run,
redirect_stdout(StringIO()),
):
result = cmd_emit(Namespace(
id="200",
tip=True,
slug=None,
preserve_existing=True,
))
self.assertEqual(result, 0)
self.assertTrue(run.called)
merged = load_thread(scratch / "thread_data.json")
self.assertEqual(
tuple(post.post_id for post in merged.posts),
("100", "200", "400", "300"),
)
self.assertEqual(merged.root_post_id, "200")
def test_migrate_defaults_to_dry_run(self) -> None:
args = build_parser().parse_args(["migrate-media", "--id", "100"])
self.assertEqual(args.cmd, "migrate-media")
+47 -1
View File
@@ -320,6 +320,25 @@ def _merge_branch_posts(
)
def _merge_existing_capture(
fresh: ThreadData,
existing: ThreadData,
tip_id: str,
) -> tuple[ThreadData, tuple[str, ...]]:
"""Keep fresh post data while retaining archived posts it omitted."""
if tip_id not in {post.post_id for post in fresh.posts}:
raise SystemExit(f"spine tip {tip_id} missing from fresh capture")
fresh_roots = {
post.post_id for post in fresh.posts if post.reply_to_id is None
}
existing_roots = {
post.post_id for post in existing.posts if post.reply_to_id is None
}
if not fresh_roots or fresh_roots.isdisjoint(existing_roots):
raise SystemExit("fresh and existing captures are a different conversation")
return _merge_branch_posts(fresh, [existing.posts])
def _copy_selected_media(
source: Path,
destination: Path,
@@ -447,7 +466,12 @@ def cmd_add_branch(args: argparse.Namespace) -> int:
}
_copy_selected_media(capture_dir, output_dir, selected_added)
_write_thread(output_dir / "thread_data.json", merged)
cmd_emit(argparse.Namespace(id=post_id, tip=True, slug=None))
cmd_emit(argparse.Namespace(
id=post_id,
tip=True,
slug=None,
preserve_existing=False,
))
added_text = ",".join(added_ids) if added_ids else "-"
leaf_text = ";".join(
@@ -469,6 +493,23 @@ def cmd_emit(args: argparse.Namespace) -> int:
src = dump_dir(post_id)
if not (src / "thread_data.json").is_file():
raise SystemExit(f"no thread_data.json for {post_id}")
if args.preserve_existing:
assets, _notes = locate(post_id)
if assets is None:
raise SystemExit(f"no existing emitted thread for {post_id}")
existing_path = assets / "thread_data.json"
if not existing_path.is_file():
raise SystemExit(f"missing {existing_path}")
fresh = load_thread(src / "thread_data.json")
existing = load_thread(existing_path)
merged, retained_ids = _merge_existing_capture(
fresh,
existing,
post_id,
)
_write_thread(src / "thread_data.json", merged)
retained = ",".join(retained_ids) if retained_ids else "-"
print(f"preserve-existing retained={retained}")
cmd = [
sys.executable,
str(HERE / "emit_archive.py"),
@@ -795,6 +836,11 @@ def build_parser() -> argparse.ArgumentParser:
if name in ("emit", "refresh"):
p.add_argument("--tip", action="store_true", help="use --id as tip")
p.add_argument("--slug", default=None)
p.add_argument(
"--preserve-existing",
action="store_true",
help="retain archived posts omitted by the fresh capture",
)
if name == "lift":
p.add_argument("--orig", action="store_true")
if name == "ocr":