back to index

Agentic Coding Questions and Current Workflow


Whisper Transcript | Transcript Only Page

00:00:00.720 | Hello! So I got some questions on Twitter after I asked what maybe people are interested in when
00:00:12.160 | it comes to sort of an update on agentic coding since I shared recently that most of what I'm
00:00:18.240 | doing right now is writing code with a combination of Clot and OpenAI, Codex in particular. And so I
00:00:27.920 | figured I go over the questions that came and yeah try to answer them. Okay, so
00:00:39.040 | maybe one of the ways in which I can go after this is to start with what it is that I have here. So in
00:00:54.960 | this code base I have basically two things. It's a back-end and it's a front-end. And the front-end
00:01:02.560 | is in Solid.js. I can kind of show this. This is what the front-end looks like.
00:01:07.120 | It's basically just a debugging tool. Like this is nobody supposed to see this other than myself.
00:01:14.640 | There's like an internal thing. And it consists of an API. So this is the API here.
00:01:22.880 | Which is auto-generated. This is just an open API visualizer. And this is SolidShares. And
00:01:37.600 | I will maybe share a little bit about like how I build it. But I want to kind of share a little bit
00:01:44.880 | about like how much I care about either one of those things. I actually care very little about the UI.
00:01:48.640 | You can, for instance, see like this thing here is most likely unused. Declared, but it's never read.
00:01:53.120 | Like this is actually like, I don't actually hate the AI output for the UI. It's actually,
00:01:58.480 | it's pretty decent. But I usually consider that to be like a debug tool. Whereas the underlying
00:02:06.320 | API is actually important for me that this works really well because it's going to power,
00:02:12.400 | or it's already in parts powering. I think that I care a lot about, right? So
00:02:17.680 | one of the questions was Codex versus Claude. And maybe before I go into this, I also covered the
00:02:27.360 | other question, which is like how much am I actually writing into any ones of these config files? So
00:02:34.080 | what I have is I have a ClaudeMD, which has just a bunch of commands in it.
00:02:40.560 | And if you have watched the other things that I made, it's probably quite familiar. This is basically
00:02:48.880 | auto-generated. It's kind of boring. And the agent file is just a symlink. So really there's no MCP,
00:02:57.120 | there's no nothing, right? It's like none of this matters as far as I'm concerned. What does matter is,
00:03:02.720 | again, I have a make dev command that I use. In this case, I have clearly in some other window,
00:03:10.400 | stuff running. So I'll close this quickly so that I can show it to you here. Actually, let me close off this.
00:03:16.960 | And close this.
00:03:27.040 | Okay. So you can see this is on the other screen, but in essence, I can now here run mcdev and I have my dev
00:03:39.920 | server running. And you can basically see there's a backend that auto reloads. I have a webhook listener,
00:03:46.720 | which is kind of important for me for debugging. And there's the UI running. And I have already shown
00:03:53.360 | this before, but if I have, I don't know a world, then I also have it here, right? So that's,
00:04:08.560 | that really is like, for as far as I'm concerned, that's what optimizes the loop is that all my logs go
00:04:14.000 | into here. Okay. So maybe we start with, with one of the things of, of, of why I think at the moment,
00:04:26.800 | I'm quite productive with this thing. It's like, this is not optimal code, right? There's a, there's
00:04:32.320 | probably a bunch of stuff we don't like in particular. There's very clearly some, some unreferenced stuff
00:04:37.840 | here. So codex is for refactoring at the moment, my tool of choice. So we already know that in delivery
00:04:49.200 | details, we have some unreferenced code and it's quite likely that we have a little bit more of it.
00:04:53.120 | So what I want to do is I want to say, um, we're going to look into the pages and the components of the
00:05:01.520 | pages and see if there is, um, if there is something we should clean up. All right. So let's say I noticed
00:05:14.080 | that in blah, blah, blah, blah, delivery details, which is this one here, um, we have unused code.
00:05:23.120 | I'm afraid there is probably more like that in other files. Um,
00:05:28.480 | please check all of the components and pages for unused code.
00:05:38.240 | So I would just, oh, actually there's one more thing. I set this to the wrong. I just want codex medium.
00:05:50.560 | Um, so codex is quite good at basically refactoring. Um, and really that is all I'm doing,
00:06:03.600 | right? I'm just talking to the thing. I'm giving you some instructions and it is a way to deal with,
00:06:11.200 | um, challenges with code quality by having this refactoring going. One, one other question that, um,
00:06:17.120 | came up, I think on Hacker News or on Lobsters, which is like, why is there 30,000 lines of go code
00:06:23.360 | here? Um, if, if all it's doing is just email sending and receiving. And the answer is it's
00:06:30.000 | not just doing email sending receiving, because for that, you really don't need to do all that much.
00:06:34.320 | Um, what I built here is basically, um, um, um, an inbox system that, um, is programmable in a way.
00:06:47.360 | So, um, I can kind of demonstrate this for a second here. Um, so I have, um, I have a folder where it's just a whole bunch of test things.
00:07:00.080 | Um, and I think if I do test full, sorry, um, LS ML, I think it's, uh, I think this one here,
00:07:13.200 | let's see. So it sends to this address. So, um, let's do the following. Let's use our API. Let's create a
00:07:20.400 | mailbox here quickly. I don't have a UI for this part yet. So it's going to be in example.local. That's my domain.
00:07:28.720 | I'm going to create here. Uh, let's call this bot. I'll just leave the rest here empty.
00:07:39.600 | Send. So now if I go here, I should have a mailbox, right? So if I do to bot at example.local,
00:07:52.800 | I have a way to do like this test, email, HTML, where is it? Not text. What was it?
00:08:00.960 | This one. Copy this into mail hook, uh, mail drop. So this is a folder that this watches for local development.
00:08:15.680 | It, um, parsed the email, it sent a webhook that was received by the webhook listener.
00:08:21.360 | And if I go here, I can now see that I have an email here and it should have converted that into
00:08:28.880 | other formats too. And I also dump out the, um, HTML. And there's not a ton of really clever software
00:08:37.920 | engineering going on there, but there's enough that it was a project that took a little while, right?
00:08:46.640 | And so we can look a little bit into what it's doing. Um, but I think that the core most important
00:08:56.000 | part of this is optimized for fast iteration, right? Um, so this is why, for instance, if I drop files
00:09:03.680 | into this local folder, it starts processing, even though obviously, um, in production, this is not how
00:09:10.560 | it works. So let's see quickly what this is doing. So, um, it's, it's identifying unused code, right? And
00:09:20.320 | this is one of the reasons why I largely do not review frontend changes in this project. Um,
00:09:27.440 | and, but I do very, very carefully review backend changes. And again, this is, I talked about this
00:09:35.120 | recently, building your own little debug UIs is something that I was not doing all that much in
00:09:40.800 | the past because often I didn't have the time for it, but here I can, right? And so, um, I basically
00:09:47.200 | had it do a bunch of changes over the last couple of days to add me this email rendering here, which
00:09:51.920 | is kind of useful. Um, and, and clearly in the process, we left some shit behind, right? But like
00:09:59.120 | Codex is, is capable of, of doing this. And the other tool that I then use, um, to figure out what's
00:10:06.080 | going on is largely GitHub desktop, actually. I, this is, I wouldn't call it, it's my favorite review tool,
00:10:13.200 | but it's the one that I actually use the most. Um, and so I can show this here when I find the window.
00:10:22.400 | Um, so basically you can see this is that it changes that it sort of dumps in here.
00:10:28.480 | This is largely how I review at the moment. Um, and if I need a little bit more, then I go into,
00:10:37.760 | um, into VS code. So it, it's largely did this now, right? So, um, obviously, um, I do formatting
00:10:48.800 | afterwards because one of the problems that Codex has, for instance, is that it, uh, it destroys
00:10:54.080 | white space. Uh, particularly in Go code, it often throws tabs away. And then I can just review this
00:11:00.720 | quickly. So this was an import. It was clearly unused. Um, we can kind of know that this was unused
00:11:08.160 | because otherwise, um, this whole page would, would have failed to load already. Um, and it would have
00:11:12.800 | picked up on this. Um, there was an unused import. There was even more unused things. This drop down
00:11:22.640 | code. Clearly we got rid of one point. Um, this was reformatting, uh, this it's interesting actually
00:11:32.240 | that we're no longer reading this. Um, so this, this code here can actually check here. There's a piece
00:11:39.840 | of code here where if I go to web hooks, um, so in the delivery log, we should have seen this one
00:11:54.560 | here, right? This was the web hook that fired. That was the one that triggered this request here. So if
00:12:00.400 | I re-delivered this, I should see it here, right? So this did work. Um, so whatever this re-delivering
00:12:08.320 | thing, there might have been a popular one point, but clearly it doesn't exist right now. Um,
00:12:12.720 | there's also, I, I don't like that there's an alert. We're all going to look into this,
00:12:17.600 | but it's not so important for debugging tool. Um, so, so I would say now that this is an acceptable change.
00:12:24.800 | Um, let's call this, uh, UI cleanup, small UI cleanup. And we're just going to check in out of this and say
00:12:35.040 | this, um, small UI cleanup for debug UI, right? So I'm pushed this up and I'm just going to open this
00:12:47.600 | quickly in another browser off screen because I want to make sure that, uh, it doesn't show anything it
00:12:54.000 | shouldn't show. Um, but I'm going to do this here. So I'll show you, this is the pull request. This is an
00:13:02.080 | automated cleanup
00:13:03.280 | that should remove unused code. And regardless of if I'm using codex or if I'm using, um,
00:13:14.240 | Claude, I use codex for reviewing. In this case, it's probably going to be entirely useless,
00:13:23.520 | but kind of just want to show it. This is generally the workflow that I have. Um, and that's good enough,
00:13:29.360 | right? Um, what else? I usually keep a to-do thing here that, um, that keeps open things that I want to
00:13:41.360 | work with. Um, there's actually a bunch of, uh, things I need to take care of if I, before I want to
00:13:49.600 | release it, um, actually don't use this anymore. So this license in question is gone, but there was,
00:13:53.920 | for instance, I pointed it towards parsing of Winmail.dat files. Um, so I have an implementation of
00:14:00.560 | this here. Oops. TN, uh, T, uh, let's, let's call it the transport neutral encapsulation format, right?
00:14:10.320 | So this implementation here is auto-generated by JetGPT, uh, or Claude. I think it was Claude,
00:14:18.640 | and it was against a Python implementation, um, of this parser. And so I think from a licensing point
00:14:25.760 | of view, um, this is a derived work, um, even though a machine generated it. So I need to clarify the
00:14:31.840 | licensing, right? So there's a small little list of stuff that I want to pay attention to before I
00:14:36.480 | release it. But really, for the most part, that's how I work, right? I, um, and so let's see how we
00:14:43.360 | were to implement a backend API, right? So at the moment, I picked Go for this project. And after some
00:14:53.280 | iteration, I came to the conclusion that I want to have this reference API in api.yaml. So this is where all the
00:15:00.320 | the APIs are that I have. And then it generates out, um, with, um, I forgot the name of the library that
00:15:08.400 | I'm using here. Uh, I can look in a link make file. So I'm using, uh, this OAPI code gen tool, which is
00:15:17.840 | this one here, to generate, um, the types and the server, um, implementation for G. And, and let's see
00:15:28.080 | how we can add an endpoint. Um, so what I was thinking here, one endpoint that we can do is at
00:15:33.280 | the moment we have this endpoint, um, which returns us the current tenant, but we don't actually have an
00:15:39.760 | endpoint to create tenants. And this is in parts because we don't have a super user, um, mechanism
00:15:45.840 | to create new tenants, but this is kind of annoying. So what I was thinking what we can do here is we can
00:15:51.280 | create, um, basically an extension to our API key so that we have an extra API key that has super user
00:16:01.280 | permissions, right? So basically we're going to create, um, a new API key that has more permissions.
00:16:08.480 | And then we want to have an API that uses those permissions to create new tenants.
00:16:14.240 | We're actually not going to do all of this, but I kind of want to show you that the system that I do.
00:16:18.880 | So I, I usually have a file called plan.md, which in this case we can delete because that was the last
00:16:24.080 | thing. And so let's say, um, super user tenant management, right? This is what I want to do.
00:16:31.120 | And so let's say we want to add a flag to API keys that gives them root permissions.
00:16:37.840 | Uh, we should come up with a better term than root or super user, but these are internal permissions,
00:16:45.440 | which are allowing us to do cross tenant operations.
00:16:55.120 | So that's the first thing.
00:16:56.160 | These permissions will then be used for a new endpoint that allows us to create and delete tenants.
00:17:06.560 | One thing that we need to consider here is that the system currently has an assumption that the
00:17:15.280 | tenant endpoint can only ever return one tenant. So there's already an open question about what the
00:17:20.320 | tenant endpoint would do if an internal key comes by. All right. So this is, I'm basically just dumping
00:17:27.760 | my thoughts here. And so the idea here is obviously that this endpoint that we have returns a single
00:17:32.960 | tenant because the way the system works is a little bit different for instance than, um, than I did in the
00:17:37.760 | past where, um, each tenant has a URL space that's unique to them. So there's, if, if you list the domains
00:17:45.760 | that you have on your account, which is basically just which, which URLs are associated with it,
00:17:50.560 | um, it always keys to the current tenant, right? So, so that obviously there are different solutions to
00:17:55.760 | which we can do this. This could return more than one tenant, um, or this could error out if, if we come
00:18:02.240 | with a super key. Um, so, so there's a bunch of stuff we can consider. There's also the question if
00:18:08.240 | the internal key should immediately give you access to all of the tenants, or if there should be a
00:18:15.280 | mechanism by which you have to gain access to a tenant by, for instance, sudoing into the tenant.
00:18:21.840 | Right. So this is just a brain dump. And so what I'm doing here now is I'm going to save this.
00:18:27.200 | Um, and so the first thing we can do here is, um, we can think for this problem. So we say like,
00:18:33.440 | I have a plan in plan, plan, oops, plan MD with some open questions.
00:18:42.800 | Uh, we don't want to solve this problem right now. We don't want to implement this problem. I just want
00:18:50.400 | to have a back and forth with you to think through the ramifications of this request.
00:18:56.160 | And that is one of the things that I'm doing, which is I want to just have another human. In this case,
00:19:00.960 | I don't have a human with me just to think for this problem. And so, uh, using codecs to have this
00:19:07.680 | conversation, I think is usually one way for me to think for this a little bit more. Um, so one of the
00:19:15.280 | things it says here is like, we distinguish internal credentials, clearly system or operator keys. So
00:19:19.440 | audit alerts, treat them differently from customer keys. Um, root style, access bypass is kind of
00:19:25.920 | wanting an assumption. Uh, duh, duh, duh, duh. So this actually brings up an interesting point here,
00:19:35.280 | which is, um, we might have this problem here, which is there might be queries, nodes.
00:19:43.600 | We might have queries that assume a single tenant and might only return the first tenant.
00:19:48.000 | Oops. We might have queries that only read a single row because they assume a single tenant,
00:19:54.800 | which might create some security concerns here. We need to be extra careful. All right. Because
00:20:01.920 | if I have a query, it just implicitly assumes there's only one tenant. Um, I don't necessarily
00:20:07.200 | know immediately what's going to happen. Um, so in general, I like the idea of sudoing into, um,
00:20:18.960 | a tenant with a super user key because it makes a very deliberate act and then I can have an audit log
00:20:25.680 | entry, but it also adds some overhead here. So, um, I don't know yet if that's the right solution here.
00:20:34.240 | Um, and also if I sudo into something, where do I store the state, right? So let's say, um,
00:20:38.880 | let's make another note here. Um, so this is basically a follow-up questions.
00:20:46.800 | If we sudo into a tenant, where do we store the state
00:20:52.320 | temporary API key question mark, right? Because we could sign and derive key, but that's, that might be
00:21:00.160 | tremendous over-engineering. Um, this here.
00:21:06.320 | Actually, there's a general question. We don't have deletion at all right now.
00:21:11.440 | Um, it might be, we might need soft deletes for tenants for end days to allow account restoration.
00:21:23.840 | Um, dun-dun-dun-dun-dun.
00:21:25.440 | Okay. So let's start with really this question here.
00:21:33.520 | So the most important question here is if we want to go with the path of sudoing into an account,
00:21:42.480 | which is probably the right way of going because it changed the least amount of assumptions about the API
00:21:47.600 | surface, where would we store ideally in the most simplest way, the data about which
00:21:55.520 | account we're, we suited into. We obviously can't store this globally because then the system key can
00:22:01.920 | only ever have one tenant active at any one time, which would be a concurrency issue.
00:22:06.240 | Um, and if we start doing things such as derived signed keys, um, this might add some overhead,
00:22:13.360 | although maybe this is pretty simple. So let's think through what options we have here.
00:22:17.360 | And the thing is, I don't actually care what it says right now because like for me,
00:22:22.240 | it was already the thinking process that helped. So actually maybe signing here a derived key for
00:22:28.080 | one specific tenant isn't actually that much work. Um, I think I might actually prefer this.
00:22:33.840 | So one thing it says, header per request, allow a super key to send X, uh, with a normal endpoint.
00:22:40.800 | So this would just, I basically just be, um, a regular API request, but you sent an extra information
00:22:48.480 | that you want to operate in a scope of a tenant. That wouldn't be too terrible, but I think it would
00:22:53.280 | also be kind of tricky with how the SDK that I generated kind of works. So I don't know if this is
00:23:01.040 | necessary the right way. Um, can derive a key. The simplest is top priority where the header approach
00:23:09.920 | wins. So you're in a schema. Um, so since I'm pseudo anyways, and it will not require an extra opt in with
00:23:21.440 | the system key, I think we might actually do this header. Um, so if we were to do the header,
00:23:28.240 | um, what would be the right way to annotate in the APIs if we have to add ourselves into a tenant context?
00:23:40.320 | Um, please check the Go code that we currently have that fetches the tenant context from, um,
00:23:47.360 | the HTTP request and also what would be a good strategy for how we make the SDK compatible with
00:23:53.360 | effectively pseudoing into a specific tenant.
00:23:56.400 | So I let it think about this, but in the meantime, I'm going to look at the SDK, um, which is auto-generated.
00:24:05.760 | Um, base client change. Uh, so let's look at the Python one because the problem that I have right now is that
00:24:13.440 | each request that is sent. So actually, uh, let me look at a generated one because otherwise this is going to be hard to look at.
00:24:20.960 | So all of this is vibe coded just to be clear. Um, so let's for instance, say, uh, these are the resources, right?
00:24:29.120 | So I have, um, for instance, I have, um, um, list by domain, which allows me to list all the emails that are on one thing here, right?
00:24:35.600 | So none of these parameters would allow me to pseudo.
00:24:39.280 | And the way the client works is that similar to how the stainless SDK style is where the base client holds references to all the sub resources.
00:24:49.840 | So if I do this here, right, I have basically blah, blah, blah, blah, blah. What is it?
00:24:56.720 | Uh, this is the wrong client. Where's my, where's my client?
00:25:03.280 | Um, base client is probably in it. No, where do I have it?
00:25:10.000 | Uh, maybe I guess it might be here. Is it mail hook?
00:25:17.680 | Okay. So this is the base client, right? So it creates all of these resources here.
00:25:21.200 | So if you do client dot attachments dot, for instance, download, it will do that.
00:25:25.760 | So it, one way obviously would be to have a global variable on the client that switches the whole client
00:25:31.440 | into a different thing, or I'm starting to auto generate, um, a tenant ID into all of these endpoints.
00:25:38.080 | Um, that's going to be a question if we want to do this. Um, so,
00:25:46.480 | So the Python clients are a centralized request preparation in prepare request, um,
00:25:50.560 | yeah, but, yeah. Okay. So one idea would be to say like, okay, the entire client needs to be recreated.
00:26:01.040 | Um, and then we just store it on the base client, which would be here, right? So we could have a flag on here,
00:26:11.920 | which is which tenant we pick that overrides then could even already reuse this, which then overrides.
00:26:21.680 | So you basically would have to sudo the whole client. Okay. Do I like this? Um,
00:26:26.320 | okay. So let's plan for this. Okay. So let's do the following.
00:26:33.600 | And I kind of want to write it into my plan too, so I don't forget. Um, so this question is resolved. Um,
00:26:40.800 | so let's see implementation.
00:26:46.640 | So we're going to add a new X, uh, actually X mail tenant, mail hook tenant header, which allows a
00:26:56.720 | system level key to
00:26:59.440 | select a tenant.
00:27:04.320 | Uh, system level key is only allowed to access
00:27:08.320 | API endpoints.
00:27:11.360 | Um, which are marked for a system to do find a way to mark it in the
00:27:22.080 | api.yaml
00:27:24.160 | or if the X mail hook tenant header was set to a tenant.
00:27:32.640 | And, um, um, we need to add a sudo method to the generated client in Python and TypeScript,
00:27:42.000 | which binds, which creates a derived client, which in turn, um, which has the tenant set
00:27:54.320 | locally as a state, right? So that's the idea. Um, and then here again,
00:28:01.920 | to do, how do we reuse the connection pool?
00:28:05.920 | So we are not going to do any of this yet. So we're basically going to start with add a system,
00:28:11.840 | add a flag to all the API
00:28:14.960 | models that marks it as system level
00:28:19.040 | to do name of that flag. So, um,
00:28:25.120 | so one question here is do we want to reuse the context or if we want to start, I think we're going to
00:28:30.960 | continue with the context. So, uh, I'm going to just
00:28:35.600 | resolve to do's here quickly. So, um,
00:28:39.280 | what do we call this? I think we call this
00:28:43.120 | name of the flag is
00:28:47.280 | system key.
00:28:53.520 | actually maybe we call it key type. And so name of the new attribute is key type with two values,
00:29:05.760 | tenant or internal. All existing keys are tenant.
00:29:15.520 | Um, and the API level keys, which we have. So we have these keys, this list API keys.
00:29:23.760 | This one at the moment would only list keys that the tenant has access to, which is a bunch. The
00:29:31.120 | question now is, do I want to make this endpoint allow it to return cross tenant keys? And I don't
00:29:38.000 | actually know the answer yet. Um, I think, I think the answer might be yes. So, um, which are marked for
00:29:45.440 | system. If, um, so we basically need two ways to mark this here. We, we need to mark endpoints,
00:29:51.840 | which you are in fact allowed to use a system key without sudo and the ones where you have to use sudo.
00:29:58.160 | So, um, when we set somewhere, how to mark an API.yaml, um, we need three types, default, tenant specific, um, also called tenant.
00:30:17.600 | Then we call it internal, only internal keys can access it.
00:30:26.000 | And, um, let's call it tenant or internal can be accessed by either and might affect the existing logic.
00:30:41.120 | Um, so let's see if we can generate ourselves somewhere. Um, existing logic,
00:30:54.960 | for instance, we need to change the query filters, right? Um, so this we do later that the client stuff
00:31:06.480 | we don't care about. So, um, let's do this before implementing, ask me about open questions.
00:31:21.280 | And this part here is not so much about code generation. It's just really just to get my mind to
00:31:25.600 | understanding the problem a little bit. Um, because I didn't really think for this at all yet.
00:31:33.680 | So one of the things that this has, it already has some markers in here. So this is the,
00:31:39.840 | this is the reference out of which I'm generating the code. And for instance, I believe I had X mail hook,
00:31:47.840 | X item potency, right? So I have already this to mark that I said, I didn't potency key support.
00:31:53.600 | Um, so we could actually give it a hint. Uh, we already use custom markers in the api.yaml for
00:32:05.760 | X item potency. So maybe that will help it, right? Um,
00:32:11.600 | so let's see, because it could be friends. I have like X key type or something to address this question here.
00:32:26.960 | Okay. Um, so, so this, um,
00:32:33.280 | okay, look, this is already nice because we already have some endpoints, which probably now
00:32:44.240 | need to change the nullability. Um, okay. So, um, now what I'm doing is I'm going to take all of these,
00:33:00.960 | blah, blah, blah, blah, blah. Just dumping them down here. So, um, here, the answer is
00:33:09.760 | the system does not have a tenant. So the current mode of only ever having one tenant is correct,
00:33:20.400 | but you are right that we will now allow requests to come in without a tenant. And we can use that
00:33:26.400 | information to distinguish system versus tenant access. It's actually, I think it might be safe or
00:33:33.440 | might be better. Yeah. So the off middleware writes exactly one tenant in the context and every header calls
00:33:38.480 | 20. So we need, um, actually the, this is the answer for both of them. Um,
00:33:55.920 | we will allow tenant the key endpoints to return a null tenant for system keys. Um, and this side,
00:34:08.480 | this goes here. Blup, blup, blup, I think. Okay, header semantics, um,
00:34:21.120 | answer. This will be mandatory. Or it errors with 400 bad request. Um, and then we say create a custom
00:34:36.160 | error code because we have error codes in addition to issue status endpoints. Um, and then we have API
00:34:43.280 | surface labeling to mirror existing idempotency extension, um, answer, make it x-axis scope. Blah, blah, blah, blah.
00:34:56.480 | Don't touch the SDKs.
00:35:05.360 | Touch the SDKs. In fact, very important. We don't want to do any of this
00:35:12.880 | Um, don't touch it. As I said, don't create new endpoints yet.
00:35:30.720 | Right. So now, now for now, this should be all, and now let's see what it does. Right. So, um, first of all,
00:35:41.120 | we'll go to make a new branch, internal keys, and then we just let it do some stuff.
00:35:49.600 | And let's see what it does. So one of the, so I'm actually going to stick to codex here just because
00:35:58.080 | it has in the context, but this is maybe the point where I would probably switch to Claude a little bit
00:36:02.960 | at the moment. Um, so let's see. It asks more questions. Um, for enforcement, the enum request context right
00:36:11.440 | now, so the middleware can reject. Um,
00:36:21.440 | For internal or tenant, we allow to hit without x mailhook tenant.
00:36:27.920 | Um, we can look at them by secret to load. We need to either extend that query or at a separate fetch
00:36:35.040 | environment.
00:36:39.760 | Um, get a tenant by API key. Let's see. What does it return?
00:36:46.160 | Actually, this is, this is the part where, um, some of the existing code
00:36:52.960 | I'm not a huge fan of because it's here. We basically call this in a bunch of places.
00:37:00.880 | Here. Um,
00:37:11.520 | I would replace with lookup API key details or get API key or similar and have actually,
00:37:23.840 | uh, replace, what's it called? Get tenant by API key
00:37:28.800 | with actually, maybe we're reading the R. So we're reading just the API key.
00:37:36.000 | Um, with a method that returns both the tenant nilable and the API key.
00:37:47.040 | So we can check the internal flag.
00:37:51.280 | And the name for the error code everything tenant impersonation is good.
00:37:57.040 | Uh, no need to sketch, just go to implementation.
00:38:04.960 | So it is the part where we don't care and necessarily now at this point,
00:38:08.320 | if it generates exactly the code that I want, because now I just want to see the code.
00:38:14.880 | and this is kind of how I work, right? Which is back and forth on, on just
00:38:23.040 | helping my mind a little bit, understand what I'm doing here
00:38:26.480 | and building my way step-by-step to it. And if the code that it generates now is exactly the code that I
00:38:33.840 | need or not, it doesn't really matter for now because
00:38:35.920 | up to this point, most of what I'm doing here was I would have done with another person, right? I would
00:38:43.520 | have probably tried to find the headspace for myself to get into the mood where I can make this decision
00:38:50.800 | myself or have a, try to have a productive conversation with a second person, um, just to, to get into,
00:38:58.160 | uh, finding the solution really, right? Because like a couple of minutes ago, the solution was maybe not
00:39:02.640 | precisely clear to me yet what it would be. Um, but now I'm much closer to, to what I actually want.
00:39:09.440 | And what, what ChatGPT did here was just be a really excellent robot doc.
00:39:16.640 | and so maybe now to, to the question of, um, why I'm stuck with Codex now, that was actually mostly
00:39:25.280 | just laziness now. Um, I, one of the challenges with Codex is we might not even see what it's going to
00:39:31.920 | build because it's very, very slow. Um, I think there's a good chance it's going to rip for like 10
00:39:37.040 | minutes before I even see the output it produces. And this is actually one of my least favorite aspects
00:39:40.880 | of Codex, which is why normally for implementation, I am, I'm still leaning towards, um, Claude because
00:39:47.600 | Claude just, it doesn't just go wide and search everything. It, it, it eventually goes to implementation.
00:39:55.200 | While we're waiting for all of this to happen, um, we can talk about the question of paralyzing.
00:40:01.520 | So the question came up, how do I paralyze? Um, and so one way of paralyzing is now to go and see,
00:40:08.000 | for instance, uh, what our code review did, right? So, um, Codex didn't find any issues, right?
00:40:16.880 | I can have another look, but I already looked previously that this was just simple changes.
00:40:22.320 | So now I can just say, let's merge it. Um, so that's one way to paralyze is just do something
00:40:31.360 | else in the meantime. But now the question here obviously is while it's changing the code here,
00:40:36.960 | how can I do something else, uh, without interfering with it? And so the easiest one is work trees. So
00:40:42.960 | you can do get work tree. Um, like for instance, I added this branch recently. So, um, let's say we want to
00:40:51.920 | do, um, what I want to do, um, mailbox creation, right? I want to add a button to create mailboxes.
00:40:59.840 | Um, and we want to do this off the main branch. So now I have this work tree here.
00:41:10.080 | Um, I actually want to, uh, this is prior to having the UI fixes. Um, so that's okay. But now I can open
00:41:19.920 | Visual Studio Code here and I'm basically sitting in a very similar code base, um, which is within, um,
00:41:28.560 | its own checkout. So I don't need to, uh, isolate the agents from each other because they're not going to
00:41:35.360 | step in here. So now I can say Claud, let's do this with Claud for the moment. Um,
00:41:40.880 | we want to add a new button and, and pop over to the
00:41:48.960 | mailbox creation. Actually, this doesn't make any sense.
00:41:55.920 | We want to add a new button to create mailboxes to the mailbox list in the UI.
00:42:02.720 | Please use the pop over style that we already have for the API. So the web hook management.
00:42:11.360 | So we kind of use one to use the same thing. Please register URL route for the
00:42:18.880 | creating new mailbox screen. We also want to use the same UI component to edit existing mailboxes.
00:42:30.480 | So now we should probably check quickly what the API supports.
00:42:33.440 | So, uh, we have mailboxes, create or update this allow. Okay. Um,
00:42:41.520 | please make this UI have all the functionality to edit and update, uh, and create new mailboxes
00:42:49.840 | with all the settings. Now I can hope, hope that it just goes there, but, um, I can also just say,
00:42:57.360 | um, like, check in API.Yaml, but also this is more or less what the request looks like.
00:43:05.040 | But please make sure to use the generated SDK and not direct API endpoints.
00:43:12.000 | So I hope, um,
00:43:15.920 | that it manages to do that. Actually, one of the problems here is that in this work tree, I don't
00:43:22.160 | have the generated SDK. So that's one of the things that I kind of want to improve. Um,
00:43:27.840 | so I need to go to mailbox creation, SDKs, generator, UV run, Python, sorry, uh, main.py, Python or TypeScript.
00:43:38.000 | And this will actually not work. I need to also run mail, I just suck at this, um, SDKs, TypeScript,
00:43:47.120 | npm run, build, npm i. Okay. Now it will be able to find the SDK hopefully here.
00:43:58.000 | Um, so that's one of the things that's just not optimal with the, with the,
00:44:04.560 | where, with the work trees is that you basically start from scratch and I don't have this perfectly
00:44:11.120 | set up yet for this to work. Um, but now we see how Claude does it. Uh, Claude has a very different
00:44:17.920 | approach. Claude for the most part, um, goes to action much quicker than, um, Codex. Um, but we're
00:44:27.360 | also now seeing that Codex is already in the writing code part. And so while it's doing that, we can already
00:44:34.880 | see what it's doing, right? So, um, so let's look at what the API changes are. So the first thing it did
00:44:42.400 | is clearly we're doing this now, um, because the API key in the storage now is nullable, right? So we have
00:44:51.680 | to do this. Um, and this is a API, this is an endpoint that I, this is a command line tool to create tenants.
00:44:58.000 | So this is a change that makes a ton of sense. Um, here we have the same, um,
00:45:05.520 | this is in the API keys, create and delete. So actually this is going to be a little bit annoying
00:45:13.360 | because it means that we are, um, let me just think for this. It's okay. Um, mailbox test. It's okay.
00:45:26.240 | So all of this is okay. So this is the first change that's kind of interesting. We can already
00:45:30.560 | see that it's fucked up. Um, it, Codex keep fucking up this, um, and I haven't found a way to fix it yet.
00:45:36.720 | For whatever reason it thinks that migrations work like this, they don't. Um, so there's one problem
00:45:42.400 | with Codex right now, which is if I edit this file, it's going to, uh, undo my changes. So I need to
00:45:49.920 | remember now that this migration is screwed. So I will go, um, here into my, uh, plan
00:45:58.960 | and make a note, review notes.
00:46:05.840 | migration is again fucked, right? Um, just, I need to remember this. Um, and we can see that it
00:46:13.840 | didn't really do, uh, also it edited this. Um, so why is it screwed, right? Because it creates
00:46:21.360 | the up migration and the down migration to the same file. That's not how my migration system works. I
00:46:25.600 | don't know where it gets the idea that there's some goose thingy going on. So I need to do this.
00:46:30.240 | This is all generated, but it manually generated. That's okay. It will redo it. If I were to run
00:46:35.520 | the schema migration now, it would actually not do anything because it alters and de-alters basically
00:46:40.560 | in one go. Um, this now is okay. There's new key type going in. So this is all fine. Um, this is kind
00:46:50.080 | of interesting. Um, I don't like this pattern. This is just generally one that I need to look at if
00:46:56.560 | there's a better way of doing it, but it keeps doing that. Um, that's okay. So we have internal
00:47:04.720 | and tenant as the API key type. It's kind of interesting because this means we also have another
00:47:10.080 | type, which is, which key is allowed to go in, which is probably in middleware. So we have the
00:47:17.520 | impersonation header and then this one has, where is it, uh, access scope internal tenant or internal.
00:47:27.920 | Okay. So there, there we should check, right? It's a security related. So it's, if
00:47:38.000 | hate it, hate it, hate it, hate it, hate it, hate it, um, nil check for self.
00:47:44.320 | Right. This is nonsense. Um,
00:47:49.600 | in resolve access scope, right? It's like, like, why, why would I do that?
00:47:57.520 | Um, like the, this, the, you should not call this if s is nil. I don't like this. Um,
00:48:03.760 | I use also the question if access scopes, like, in fact, the whole thing here,
00:48:07.600 | this should be a panic if at all. Um, okay. Okay. Owning tenant. Um,
00:48:20.560 | this also, I don't like this, um, oops, um, key type should not be empty if migration is correct.
00:48:36.160 | So let's actually check the migration. So the migration here should default to tenant. So it
00:48:43.920 | it should never be possible for the access scopes, uh, not the access scopes. Uh, what is it?
00:48:49.760 | The key type would be empty, right? This is, this is a debt, this is a debt, that code path. Um,
00:48:57.520 | yeah, well, we're going to see a bunch of this. And so, um, this is why I, just for a close,
00:49:07.040 | this is why I use generally a lot more codex still for writing code. It's like, this is still going,
00:49:13.680 | and I already don't like what it did. And I can't change it now because if I change it now,
00:49:19.520 | it will regress my changes. So, but what I can do here now is I can, um, I can run the formatter.
00:49:26.160 | It will not format everything because, um, I don't have pretty installed, but that's okay.
00:49:34.400 | Um, maybe it's not okay. Uh, why does it want to run this?
00:49:37.120 | I will
00:49:40.400 | Did it really only create this?
00:49:44.000 | I will add this for now. Edit mailbox creation and edit screen.
00:49:54.560 | I will push this up. So I will not review this at all because this is UI code.
00:50:00.480 | I will straight go to have codex review this. This is how lazy I am. Um, this adds a way to
00:50:07.760 | add and edit mailboxes in the UI.
00:50:10.480 | Right. Codex review. Again, this is UI code for a debug screen. I don't care. I will review it later
00:50:21.520 | if there's a security thing, but like it didn't edit any backend code. So there shouldn't be any way for
00:50:25.600 | it to break authentication or anything dangerous. So I'm going to, um, stick with this for now.
00:50:31.680 | And then I can, in the same time, look into, uh, more of this. So with codex, I'm for now limited to
00:50:43.680 | telling it to respond to my changes. So I'm just going to copy this all in here.
00:50:51.520 | Careful with the migration in particular.
00:50:54.400 | The migration doesn't actually have a down path. So this migration was a no-op migration.
00:51:00.880 | Um, I have no idea if it's going to be able to edit these changes, but we can see.
00:51:07.840 | And I can, in the meantime, review more stuff. So get the API key with tenant. That's okay.
00:51:13.760 | This is okay. This will be interesting. So, um, why does it do a left join?
00:51:20.160 | It does a left join because it's now nullable.
00:51:25.200 | Are we really running this all the time? This was the thing that I wanted to cache.
00:51:30.800 | Um, uh, it's okay. So we're going to create
00:51:40.320 | tenant and the API key. Um, that's okay.
00:51:45.680 | It's not, not amazing, but it's okay. I, I need to cache all of this anyways. I don't have,
00:51:51.520 | I didn't have time for this. Um, okay. But right, you get the idea.
00:51:56.640 | Um, this is basically how I work. There's, there's no, no anything interesting in it. It's just talking to
00:52:03.040 | the machine, treating it like another programmer, not the most optimal programmer of all, but no crazy MCP stuff,
00:52:12.000 | no crazy prompts in the Cloud MD file or anything like this. It's really just, just this, right?
00:52:20.240 | There's, there's no magic in it. Um,
00:52:26.960 | And now the question is, was this more efficient than I would have done otherwise?
00:52:30.320 | Well, it always depends a little bit, right? Like in some ways, sometimes when I come out here with
00:52:34.800 | like having understood what I actually wanted to do, probably could go in and do the changes
00:52:38.640 | myself. But if you think about it, it's actually not so insignificant what it does. And only having
00:52:43.600 | to think about the little bit of stuff to review in the end, it's, I feel like it's good, but also
00:52:50.320 | I don't do multiple things at once. Right? So anyways, I hope this was helpful in one form or another. Um,
00:52:56.640 | and yeah, let me know on Twitter or else if there's something else you want to know.