{"slug": "the-discoverable-evidence-of-ai-assisted-software-porting", "title": "The Discoverable Evidence of AI-Assisted Software Porting", "summary": "A developer used OpenAI's Codex AI assistant to port the williamcotton.com website from a custom language called Web Pipe to Rust, with the AI successfully recreating the site and integrating Contentful for dynamic content. The process was documented through discoverable evidence in Codex's log files, revealing the AI's step-by-step reasoning and code generation.", "body_md": "## The Discoverable Evidence of AI-Assisted Software Porting\n\nJune 25th, 2026\n\nWe start with a simple instruction without a lot of detail:\n\n```\ncopy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs\n\nthe goal is to recreate williamcotton.com entirely in rust\n```\n\nAnd Codex is off to the races. It \"thinks\" for some time, checks the subfolder with my copy of this website ([written](https://github.com/williamcotton/williamcotton.com/blob/main/app.wp) in a language of my own called [Web Pipe](https://github.com/williamcotton/webpipe)), curls the actual website, and then it gets to work. Ah, the familiar green and red text that shows my work being done for me. Copies of Google Chrome come and go with brief glimpses of my website.\n\nAnd then finally, finished! Only it has just hardcoded all of the pages inside of wiring up to Contentful. So some further prodding is required:\n\n```\nyou've made a critical mistake - the articles are hard coded but should come from contentful instead\n```\n\nAnd now the real thinking begins. I drink some coffee and in the meantime I fire up another terminal window:\n\n``` bash\n$ cd ~/.codex\n$ grep \"I've done a cargo init in wmct-copy-codex-rust/src/main.rs\" . -R\n./2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl:{\"timestamp\":\"2026-06-26T12:06:00.155Z\",\"type\":\"response_item\",\"payload\":{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\"}],\"internal_chat_message_metadata_passthrough\":{\"turn_id\":\"019f03d2-94c2-7900-bba1-9363baf4f8d5\"}}}\n./2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl:{\"timestamp\":\"2026-06-26T12:06:00.155Z\",\"type\":\"event_msg\",\"payload\":{\"type\":\"user_message\",\"message\":\"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\",\"images\":[],\"local_images\":[],\"text_elements\":[]}}\n```\n\nWhat do we have here? It looks like a couple of lines from a JSONL file, one being an \"event message\" and then other being a \"response item\". The response item has a \"turn id\". Interesting.\n\nOh, but Codex has finally finished! I can tell it works because I load this self-same website URL of `http://localhost:1234/articles/the-discoverable-evidence-of-ai-assisted-software-porting`\n\nto the running instance and see the at the time burgeoning article in question.\n\nI look at the code, and in typical fashion, the entire application is in just a single `main.rs`\n\nfile. Reading through the code I can see that it successfully ported the key parts. It most definitely relied on the render_rich_text function, which takes a recursive Contentful-provided JSON tree of nodes and returns fully formatted HTML ready for consumption by someone's browser of choice. It's also made sure it uses HTMX! It has tests!\n\nLet's look around a bit again.\n\n``` bash\n$ cd ~/.codex\n$ grep \"I've done a cargo init in wmct-copy-codex-rust/src/main.rs\" . -R\nBinary file ./logs_2.sqlite matches\n./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl:{\"timestamp\":\"2026-06-26T12:06:00.155Z\",\"type\":\"response_item\",\"payload\":{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\"}],\"internal_chat_message_metadata_passthrough\":{\"turn_id\":\"019f03d2-94c2-7900-bba1-9363baf4f8d5\"}}}\n./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl:{\"timestamp\":\"2026-06-26T12:06:00.155Z\",\"type\":\"event_msg\",\"payload\":{\"type\":\"user_message\",\"message\":\"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\",\"images\":[],\"local_images\":[],\"text_elements\":[]}}\nBinary file ./state_5.sqlite matches\n./history.jsonl:{\"session_id\":\"019f03d0-f7d1-7931-a089-3cb1c1f627cd\",\"ts\":1782475560,\"text\":\"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\"}\nBinary file ./state_5.sqlite-wal matches\n```\n\nInteresting! We see some SQLite databases with the matches now as well. We'll take a look at those in a second. We still see our \"response item\" and our \"event message\" but we also see a match in a JSONL file that seems to contain our message history. And this one has a session id. And this session id matches part of the name of the JSONL file. Maybe it's time to take a closer look at this `rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl`\n\nfile.\n\n``` bash\n$ cat ./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl | wc\n     477   65760 1462884\n```\n\nNot a lot of lines but quite a number of characters. I take a look at the file in a text editor. I come across `data:image/png;base64`\n\n- well this explains at least some of the file size.\n\nLet's poke around in a more civilized manner.\n\n``` bash\n$ cat ./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl \\\n| jq -sr 'map(.type) | group_by(.) | map ({type: .[0], count: length})'\n[\n  {\n    \"type\": \"event_msg\",\n    \"count\": 125\n  },\n  {\n    \"type\": \"response_item\",\n    \"count\": 349\n  },\n  {\n    \"type\": \"session_meta\",\n    \"count\": 1\n  },\n  {\n    \"type\": \"turn_context\",\n    \"count\": 2\n  }\n]\n```\n\nAlright, so mainly \"response items\".\n\n``` bash\n$ cat ./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl \\\n| jq -sr 'map(.payload.type) | group_by(.) | map ({type: .[0], count: length})'\n[\n  {\n    \"type\": null,\n    \"count\": 3\n  },\n  {\n    \"type\": \"agent_message\",\n    \"count\": 46\n  },\n  {\n    \"type\": \"custom_tool_call\",\n    \"count\": 9\n  },\n  {\n    \"type\": \"custom_tool_call_output\",\n    \"count\": 9\n  },\n  {\n    \"type\": \"function_call\",\n    \"count\": 114\n  },\n  {\n    \"type\": \"function_call_output\",\n    \"count\": 114\n  },\n  {\n    \"type\": \"message\",\n    \"count\": 50\n  },\n  {\n    \"type\": \"patch_apply_end\",\n    \"count\": 9\n  },\n  {\n    \"type\": \"reasoning\",\n    \"count\": 52\n  },\n  {\n    \"type\": \"task_complete\",\n    \"count\": 2\n  },\n  {\n    \"type\": \"task_started\",\n    \"count\": 2\n  },\n  {\n    \"type\": \"token_count\",\n    \"count\": 63\n  },\n  {\n    \"type\": \"user_message\",\n    \"count\": 2\n  },\n  {\n    \"type\": \"web_search_call\",\n    \"count\": 1\n  },\n  {\n    \"type\": \"web_search_end\",\n    \"count\": 1\n  }\n]\n```\n\nAnd here we mainly see functional calling.\n\nNow let's take a look at the messages we typed into Codex itself.\n\n``` bash\n$ cat ./sessions/2026/06/26/rollout-2026-06-26T07-04-14-019f03d0-f7d1-7931-a089-3cb1c1f627cd.jsonl \\ \n| jq 'select(.payload.type == \"user_message\")'\n{\n  \"timestamp\": \"2026-06-26T12:06:00.155Z\",\n  \"type\": \"event_msg\",\n  \"payload\": {\n    \"type\": \"user_message\",\n    \"message\": \"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\",\n    \"images\": [],\n    \"local_images\": [],\n    \"text_elements\": []\n  }\n}\n{\n  \"timestamp\": \"2026-06-26T12:15:38.716Z\",\n  \"type\": \"event_msg\",\n  \"payload\": {\n    \"type\": \"user_message\",\n    \"message\": \"you've made a critical mistake - the articles are hard coded but should come from contentful instead\",\n    \"images\": [],\n    \"local_images\": [],\n    \"text_elements\": []\n  }\n}\n```\n\nOur foray into the land of JSONL is probably complete for now,\n\nLet's poke around in these SQLite databases now.\n\n``` bash\n$ sqlite3 ./logs_2.sqlite '.tables'        \n_sqlx_migrations  logs            \n$ sqlite3 ./logs_2.sqlite '.schema logs'\nCREATE TABLE logs (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    ts INTEGER NOT NULL,\n    ts_nanos INTEGER NOT NULL,\n    level TEXT NOT NULL,\n    target TEXT NOT NULL,\n    feedback_log_body TEXT,\n    module_path TEXT,\n    file TEXT,\n    line INTEGER,\n    thread_id TEXT,\n    process_uuid TEXT,\n    estimated_bytes INTEGER NOT NULL DEFAULT 0\n);\nCREATE INDEX idx_logs_ts ON logs(ts DESC, ts_nanos DESC, id DESC);\nCREATE INDEX idx_logs_thread_id ON logs(thread_id);\nCREATE INDEX idx_logs_thread_id_ts ON logs(thread_id, ts DESC, ts_nanos DESC, id DESC);\nCREATE INDEX idx_logs_process_uuid_threadless_ts ON logs(process_uuid, ts DESC, ts_nanos DESC, id DESC)\nWHERE thread_id IS NULL;\n```\n\nOk, so there's a logs table. What does this look like?\n\n``` bash\n$ sqlite3 -header -column ./logs_2.sqlite \\\n  'SELECT * FROM logs LIMIT 1;'\nid         ts          ts_nanos  level  target                            feedback_log_body                   module_path        file                                                                                                            line  thread_id process_uuid                                    estimated_bytes\n---------  ----------  --------  -----  --------------------------------  ----------------------------------  --------------------------------  --------------------------------------------------------------------------------------------------------------  ----  --------- ----------------------------------------------  ---------------\n107247977  1781611464  18308000  TRACE  hyper_util::client::legacy::pool  idle interval checking for expired  hyper_util::client::legacy::pool  /Users/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-util-0.1.20/src/client/legacy/pool.rs  806 pid:10447:d93df5c9-186c-40c3-b9be-e7126cb2c42b  213\n```\n\nOh boy, this is going to take some deeper diving to figure out. Let's see if we can even find where our text is stored in this database and in what format. After some poking I settled on this query:\n\n``` bash\n$ sqlite3 -header -column ./logs_2.sqlite \"\nSELECT rowid, level, target                   \nFROM logs\nWHERE feedback_log_body LIKE '%I''ve done a cargo init in wmct-copy-codex-rust/src/main.rs%';\n\"\nid         level  target                                  \n---------  -----  ----------------------------------------\n136150688  TRACE  codex_api::endpoint::responses_websocket\n136152594  TRACE  codex_api::endpoint::responses_websocket\n136154340  TRACE  codex_api::endpoint::responses_websocket\n136155560  TRACE  codex_api::endpoint::responses_websocket\n136156345  TRACE  codex_api::endpoint::responses_websocket\n136159063  TRACE  codex_api::endpoint::responses_websocket\n136161668  TRACE  codex_api::endpoint::responses_websocket\n136162148  TRACE  codex_api::endpoint::responses_websocket\n136163302  TRACE  codex_api::endpoint::responses_websocket\n136164592  TRACE  codex_api::endpoint::responses_websocket\n```\n\nSo we at least know that this is related to web sockets in some manner?\n\nThe \"feedback log body\" is gigantic so we'll just take a little peek.\n\n``` bash\n$ sqlite3 -noheader ./logs_2.sqlite \"\nSELECT substr(feedback_log_body, 1, 200)\nFROM logs\nWHERE feedback_log_body LIKE '%I''ve done a cargo init in wmct-copy-codex-rust/src/main.rs%'\nLIMIT 1;\n\"\nsession_loop{thread_id=019f03d0-f7d1-7931-a089-3cb1c1f627cd}:submission_dispatch{otel.name=\"op.dispatch.user_input\" submission.id=\"019f03db-68d2-7cd3-9b82-e8b81ba57791\" codex.op=\"user_input\"}:turn{ote\n```\n\nGreat, it's some custom log format that we're definitely not going to attempt to parse at this point. But we can poke around a bit more around a little bit:\n\n``` bash\n$ sqlite3 -noheader ./logs_2.sqlite \"\nSELECT feedback_log_body\nFROM logs\nWHERE feedback_log_body LIKE '%I''ve done a cargo init in wmct-copy-codex-rust/src/main.rs%'\nLIMIT 1;\n\" > /tmp/log.txt\n```\n\nAnd then after manually searching through the text for my prompt string in a text editor I find some JSON.\n\n```\n{\n  \"type\": \"message\",\n  \"role\": \"user\",\n  \"content\": [\n    {\n      \"type\": \"input_text\",\n      \"text\": \"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\"\n    }\n  ],\n  \"internal_chat_message_metadata_passthrough\": {\n    \"turn_id\": \"019f03d2-94c2-7900-bba1-9363baf4f8d5\"\n  }\n}\n```\n\nAh, but this is a different kind of message. Let's see if it's contained in the JSONL we were looking at before:\n\n```\n{\n  \"timestamp\": \"2026-06-26T12:06:00.155Z\",\n  \"type\": \"response_item\",\n  \"payload\": {\n    \"type\": \"message\",\n    \"role\": \"user\",\n    \"content\": [\n      {\n        \"type\": \"input_text\",\n        \"text\": \"copy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \\n\\nthe goal is to recreate williamcotton.com entirely in rust\"\n      }\n    ],\n    \"internal_chat_message_metadata_passthrough\": {\n      \"turn_id\": \"019f03d2-94c2-7900-bba1-9363baf4f8d5\"\n    }\n  }\n}\n```\n\nSo the same payload but a different container.\n\nLet's change tact now and look at this other database. This time we're write some Python to help us search through all of the tables.\n\n``` python\nimport sqlite3\n\nneedle = \"I've done a cargo init in wmct-copy-codex-rust/src/main.rs\"\n\ndb = sqlite3.connect(\"state_5.sqlite\")\ncur = db.cursor()\n\ntables = [\n    r[0] for r in cur.execute(\n        \"SELECT name FROM sqlite_master WHERE type='table'\"\n    )\n]\n\nfor table in tables:\n    cols = cur.execute(f\"PRAGMA table_info('{table}')\").fetchall()\n    for _, col, typ, *_ in cols:\n        if typ.upper() == \"TEXT\":\n            try:\n                sql = f'SELECT rowid, \"{col}\" FROM \"{table}\" WHERE \"{col}\" LIKE ?'\n                for rowid, value in cur.execute(sql, (f\"%{needle}%\",)):\n                    print(f\"{table}.{col} rowid={rowid}\")\n                    print(value[:200])\n                    print()\n            except Exception:\n                pass\n```\n\nWith our output being:\n\n```\nthreads.title rowid=375\ncopy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \n\nthe goal is to recreate williamcotton.com entirely in rust\n\nthreads.first_user_message rowid=375\ncopy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \n\nthe goal is to recreate williamcotton.com entirely in rust\n\nthreads.preview rowid=375\ncopy williamcotton.com - I've done a cargo init in wmct-copy-codex-rust/src/main.rs \n\nthe goal is to recreate williamcotton.com entirely in rust\n```\n\nAnd taking a look at the threads table, with the schema extracted from a bunch of triggers and other ephemera and cleaned up a bit.\n\n```\nCREATE TABLE threads (\n    id TEXT PRIMARY KEY,\n    rollout_path TEXT NOT NULL,\n    created_at INTEGER NOT NULL,\n    updated_at INTEGER NOT NULL,\n    source TEXT NOT NULL,\n    model_provider TEXT NOT NULL,\n    cwd TEXT NOT NULL,\n    title TEXT NOT NULL,\n    sandbox_policy TEXT NOT NULL,\n    approval_mode TEXT NOT NULL,\n    tokens_used INTEGER NOT NULL DEFAULT 0,\n    has_user_event INTEGER NOT NULL DEFAULT 0,\n    archived INTEGER NOT NULL DEFAULT 0,\n    archived_at INTEGER,\n    git_sha TEXT,\n    git_branch TEXT,\n    git_origin_url TEXT,\n    cli_version TEXT NOT NULL DEFAULT '',\n    first_user_message TEXT NOT NULL DEFAULT '',\n    agent_nickname TEXT, agent_role TEXT,\n    memory_mode TEXT NOT NULL DEFAULT 'enabled',\n    model TEXT,\n    reasoning_effort TEXT,\n    agent_path TEXT,\n    created_at_ms INTEGER,\n    updated_at_ms INTEGER,\n    thread_source TEXT,\n    preview TEXT NOT NULL DEFAULT '',\n    recency_at INTEGER NOT NULL DEFAULT 0,\n    recency_at_ms INTEGER NOT NULL DEFAULT 0\n);\n```\n\nSo now let's take a look at just the first row and not the 375th row:\n\n``` bash\n$ sqlite3 ./state_5.sqlite -header -column 'SELECT title, first_user_message, preview FROM threads LIMIT 1;'\ntitle                                         first_user_message                                                                                  preview     \n-----------------------------------------------------------------------------------------------------------------------------------------------------------  -----------------------------------------------------------------------------------------------------------------------------------------------------------  -----------------------------------------------------------------------------------------------------------------------------------------------------------\nwrite an html to markdown converter - use recursive descent to make an ast - first things first, parse to an AST, we can make the AST to markdown step next  write an html to markdown converter - use recursive descent to make an ast- first things first, parse to an AST, we can make the AST to markdown step next  write an html to markdown converter - use recursive descent to make an ast - first things first, parse to an AST, we can make the AST to markdown step next\n```\n\nThis has nothing to do with our current session! In fact this looks like the very first prompt I put into Codex.\n\n```\nwrite an html to markdown converter - use recursive descent to make an ast - first things first, parse to an AST, we can make the AST to markdown step next\n```\n\nLet's count the number of rows in this table:\n\n``` bash\n$ sqlite3 ./state_5.sqlite 'SELECT count(*) FROM threads;'                                  \n375\n```\n\nAnd the count is the row number of the message we were looking for!\n\nAlas, at least for this approach, we've only got the first message from the prompt. But at least we still have our JSONL files to look through!\n\nNow that we've poked around in the stored changes, which can help describe the *intent* of using such tools, let's take a look at the code itself to see what we can find.\n\n``` php\nfn main() -> std::io::Result<()> {\n    let host = env::var(\"HOST\").unwrap_or_else(|_| \"127.0.0.1\".to_string());\n    let port = env::var(\"PORT\").unwrap_or_else(|_| \"3000\".to_string());\n    let address = format!(\"{host}:{port}\");\n    let listener = TcpListener::bind(&address)?;\n\n    println!(\"Serving williamcotton.com Rust copy at http://{address}\");\n\n    for stream in listener.incoming() {\n        match stream {\n            Ok(stream) => {\n                thread::spawn(|| {\n                    if let Err(error) = handle_client(stream) {\n                        eprintln!(\"request failed: {error}\");\n                    }\n                });\n            }\n            Err(error) => eprintln!(\"connection failed: {error}\"),\n        }\n    }\n\n    Ok(())\n}\n```\n\nWell part of our prompt ended up in the source code as there is an explicit mention of this being a \"Rust copy\". Let's see what else we can find.\n\nA very important consideration in this code comparison is that Web Pipe is a very different kind of language than Rust. It's a DSL. It is probably closer to a declarative configuration language than anything else. It certainly isn't a general purpose programming language.\n\nBut we still see fingerprints all over the code. For example, the code continues to use HTMX and keeps the very same HTML.\n\nRust code:\n\n``` php\nfn blog_layout(title: &str, description: Option<&str>, content: &str) -> String {\n    base_layout(title, description, content, true)\n}\n\nfn base_layout(title: &str, description: Option<&str>, content: &str, blog_scripts: bool) -> String {\n    let meta_tags = description\n        .filter(|value| !value.is_empty())\n        .map(|value| format!(\"<meta name=\\\"description\\\" content=\\\"{}\\\">\", escape_html(value)))\n        .unwrap_or_default();\n    let head_extras = if blog_scripts {\n        r#\"\n    <script src=\"https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/fsharp.min.js\"></script>\n    <script src=\"/hljs-wp.js\"></script>\"#\n    } else {\n        \"\"\n    };\n    let footer_scripts = if blog_scripts {\n        r#\"\n    <script>try{hljs.highlightAll();}catch(e){}</script>\"#\n    } else {\n        \"\"\n    };\n\n    format!(\n        r##\"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,minimum-scale=1,initial-scale=1\"/>\n    <link rel=\"preload\" href=\"LigaMenlo-Regular.woff\" as=\"font\" type=\"font/woff\" crossorigin>\n    <title id=\"page-title\">{}</title>\n    <link rel=\"stylesheet\" href=\"/app.css\"/>\n    {}{}\n</head>\n<body>\n    <div id=\"app\">\n        <div class=\"sitewrapper\">\n            <header>\n                <h1>\n                    <a href=\"/\" hx-get=\"/htmx/\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/\">williamcotton.com</a>\n                </h1>\n                <nav>\n                    <a href=\"/about\" hx-get=\"/htmx/about\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/about\">About</a>\n                    <a href=\"/bio\" hx-get=\"/htmx/bio\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/bio\">Bio</a>\n                    <a href=\"/contact\" hx-get=\"/htmx/contact\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/contact\">Contact</a>\n                </nav>\n            </header>\n            <div class=\"content\" id=\"main-content\">\n{}\n            </div>\n            <footer>\n                <p>&copy; 2025 William Cotton</p>\n            </footer>\n        </div>\n    </div>{}\n</body>\n</html>\n\"##,\n        escape_html(title),\n        meta_tags,\n        head_extras,\n        content,\n        footer_scripts\n    )\n}\n\nfn render_article_list_item(article: &ArticleSummary) -> String {\n    let slug = escape_html(&article.slug);\n    let title = escape_html(&article.title);\n    let date = if article.formatted_date.is_empty() {\n        String::new()\n    } else {\n        format!(\"    <p class=\\\"published-date\\\">{}</p>\\n\", escape_html(&article.formatted_date))\n    };\n\n    format!(\n        r##\"\n<article>\n    <h2>\n        <a href=\"/articles/{slug}\" \n           hx-get=\"/htmx/articles/{slug}\" \n           hx-target=\"#main-content\" \n           hx-swap=\"innerHTML show:window:top\"\n           hx-push-url=\"/articles/{slug}\">{title}</a>\n    </h2>\n{date}{body}\n\n</article>\n\"##,\n        body = article.body_preview\n    )\n}\n```\n\nWeb Pipe code:\n\n```\nhandlebars baseLayout = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width,minimum-scale=1,initial-scale=1\"/>\n    <link rel=\"preload\" href=\"LigaMenlo-Regular.woff\" as=\"font\" type=\"font/woff\" crossorigin>\n    <title id=\"page-title\">{{> title}}</title>\n    <link rel=\"stylesheet\" href=\"/app.css\"/>\n    {{> metaTags}}\n    {{> headExtras}}\n</head>\n<body>\n    <div id=\"app\">\n        <div class=\"sitewrapper\">\n            <header>\n                <h1>\n                    <a href=\"/\" hx-get=\"/htmx/\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/\">williamcotton.com</a>\n                </h1>\n                <nav>\n                    <a href=\"/about\" hx-get=\"/htmx/about\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/about\">About</a>\n                    <a href=\"/bio\" hx-get=\"/htmx/bio\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/bio\">Bio</a>\n                    <a href=\"/contact\" hx-get=\"/htmx/contact\" hx-target=\"#main-content\" hx-swap=\"innerHTML\" hx-push-url=\"/contact\">Contact</a>\n                </nav>\n            </header>\n            <div class=\"content\" id=\"main-content\">\n{{> @partial-block}}\n            </div>\n            <footer>\n                <p>© 2025 William Cotton</p>\n            </footer>\n        </div>\n    </div>\n    {{> footerScripts}}\n</body>\n</html>\n`\n\nhandlebars blogLayout = `\n{{#> baseLayout}}\n  {{#*inline \"metaTags\"}}{{#if description}}<meta name=\"description\" content=\"{{description}}\">{{/if}}{{/inline}}\n  {{#*inline \"headExtras\"}}\n    <script src=\"https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/fsharp.min.js\"></script>\n    <script src=\"/hljs-wp.js\"></script>\n  {{/inline}}\n  {{#*inline \"footerScripts\"}}\n    <script>try{hljs.highlightAll();}catch(e){}</script>\n  {{/inline}}\n{{> @partial-block}}\n{{/baseLayout}}\n`\n\nhandlebars articleListItem = `\n<article>\n    <h2>\n        <a href=\"/articles/{{fields.slug}}\" \n           hx-get=\"/htmx/articles/{{fields.slug}}\" \n           hx-target=\"#main-content\" \n           hx-swap=\"innerHTML show:window:top\"\n           hx-push-url=\"/articles/{{fields.slug}}\">{{fields.title}}</a>\n    </h2>\n    {{#if fields.formattedDate}}<p class=\"published-date\">{{fields.formattedDate}}</p>{{/if}}\n{{#if fields.bodyPreview}} {{{fields.bodyPreview}}} {{/if}}\n\n</article>\n`\n```\n\nIt even keeps the same names, \"blog layout\", \"base layout\" and \"article list item\".\n\nYou can probably guess that the CSS is directly copied from one project to another just based on the class and id names on the DOM elements.\n\nThere's all sorts of other similarities, from the exact same error messages, to the order of the route handler definitions, test fixtures and more. With a careful eye the list grows considerably.\n\nNow of course the porting from one language to another could be instructed to use tailwind, a different set of error messages, a different approach to syntax highlighting and many other instructions to help obfuscate the source. But the *intent* of these changes would still be captured in the evidence gleaned from our `~/.codex`\n\ndirectory.\n\nWe haven't even started to look at an AGENTS.md file or a docs directory filled with instructions for an LLM to follow. What we do know is that the paper trail for porting from one codebase to another using an LLM is most probably something that can be discovered.", "url": "https://wpnews.pro/news/the-discoverable-evidence-of-ai-assisted-software-porting", "canonical_source": "https://williamcotton.com/articles/the-discoverable-evidence-of-ai-assisted-software-porting", "published_at": "2026-06-26 17:46:36+00:00", "updated_at": "2026-06-26 18:05:07.153766+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "generative-ai"], "entities": ["OpenAI", "Codex", "Contentful", "Rust", "Web Pipe", "HTMX", "Google Chrome"], "alternates": {"html": "https://wpnews.pro/news/the-discoverable-evidence-of-ai-assisted-software-porting", "markdown": "https://wpnews.pro/news/the-discoverable-evidence-of-ai-assisted-software-porting.md", "text": "https://wpnews.pro/news/the-discoverable-evidence-of-ai-assisted-software-porting.txt", "jsonld": "https://wpnews.pro/news/the-discoverable-evidence-of-ai-assisted-software-porting.jsonld"}}