Stop generating, start rendering: personal apps backed by your System of Record
How fighting AI content-bloat pushed me to fix my System of Record — and got me a one-shot, dependency-aware roadmap that beats GitLab’s native date-based one.
The trap #
AI is amazing at rapidly generating huge volumes of content and mockups. This is a blessing and a curse which I immediately fell into. This all started with me trying to do a planning session. GitLab didn’t give me the roadmap view I wanted, so I never bothered to maintain all the epic metadata and relationships — I kept them in my head. I initially shared this extra hidden knowledge with AI and got it to cross-reference it with my KB and GitLab. It created a very pretty, accurate static HTML roadmap with the extra info included. But it hit me in the face that this data now only resides in this artefact and would immediately rot. Ideally it should live in GitLab (my SoR) or if it doesn’t fit there in my Knowledge Base.
What’s the rule? If it can fit into your System of Record and is a canonical record, maintain it there. Do not duplicate.
System of Record (SoR)
The authoritative source of truth for a given set of data — the one place that holds the canonical copy, that everything else syncs from. For work items that’s your tracker (GitLab, Jira); for code, your Git forge. It’s about authority over data, not documentation — if two systems disagree, the SoR wins.
The rot #
Managing context and knowledge is hard and all AI wants to do is verbosely generate more. I’ve been managing mine in markdown and Obsidian for years — it’s not bad but it goes stale quickly, only grows, is not very well cross-referenced and frankly was a mess. I also maintained a project management system but it suffered from some of the same flaws and doesn’t quite work in the way I want it to.
PKM & KB
A Personal Knowledge Management system (PKM) is where you keep your own notes and thinking — Obsidian, a pile of markdown, your “second brain”. A Knowledge Base (KB) is the shared, team-facing version. Either way it’s human-readable knowledge — how things work, what you decided — not structured records.
AI needs high-quality context and access to your current (not stale) knowledge. AI also works best with strict spec-driven development with fixed defined scope and goals (that sounds a lot like epics and tickets…)
Both you and AI need a common System of Record. AI can hit up the APIs to fetch the work items and you can fight the UI to maintain them.
The two UXes #
The human UX on most tools is generally bad; bloated, slow and too many clicks (looking at you, Jira) or too lean and minimal (GitHub). I personally favour GitLab for completeness but I struggle to view the work items at a suitable resolution (roadmaps and strategy). The robot UX is either REST or GraphQL API, and AI is expert at both. GraphQL is optimal because the AI can pick and choose the exact data it needs and minimise token burn.
The payoff #
The payoff: a live roadmap better than GitLab’s own, backed entirely by canonical SoR data — and building it made me improve the records themselves.
So I vibe-coded a personal app. I knew GitLab Pages could do this quickly and securely — it has a decent API with public-client PKCE. And boom. It converted my roadmap into an app backed by canonical SoR data. Better still, building it made me improve the records: populating metadata, standardising templates, building relationships. This is something I would never have done via the GitLab UI — it’s too slow and it doesn’t always surface properly in my mental model. But shouting at Claude with Wispr Flow is one of my favourite pastimes. Collaborating with AI is much more fun than doing it yourself.
So I now had the visual aids that I needed, which were live, and AI had the rich data in the SoR. It was now less likely to rot, because the live view always reflects the SoR.
Security
Use OAuth and let your app consumers authenticate securely with their own credentials to access the app. This ensures that data stays secure and only the right people can access it. Do not save a copy of sensitive SoR data and share it outside your existing controls. Do not bake sensitive tokens or secrets into the frontend app.
And mind the token after login: PKCE protects the code exchange, not what you do with the token afterwards. Keep the access token in memory, never localStorage. Request the narrowest scope that works (read_api, only adding write where you genuinely need it — like updating a snippet). Your frontend is only as safe as your XSS and CSP posture, so treat both as existential.
What else could you build? #
- Decks/Presentations vibe-code a private gitlab pages site and for each slide populate the slides with Title:
&epic .title,&epic .description #Summary. Or#ticket .note. Cross-link back to the canonical source so collaborators want to see the canonical source for more info. - Collaborative microsites vibe-code a private gitlab pages site where each page is a ticket
#ticket .title,.description, label it aspage::/the-big-idea. Encourage collaborators to comment/edit on the tickets (auto capture the history/changes) and render them inline. Enable page interaction with (GitLab) ticket emoji reactions and project stars. - Workshop collaboration space vibe-code a front-end for your project wiki (all wikis are ugly). Edit inline and push the changes back.
(&epic and #ticket are GitLab notation. .attribute #title are made-up selectors for the record details etc.)
Need printed material? Print to PDF in the browser and make extensive use of @media print CSS. Or need a different format, go bananas with URL.createObjectURL(blob) or window.showSaveFilePicker() and allow people to download dynamically generated artefacts where the canonical source is the SoR.
It doesn’t matter that these personal apps might be short-lived. Code is now cheap — which is exactly why well-structured data and knowledge in your SoR is priceless.
Takeaways #
- Identify your SoR (you likely already have one without fully appreciating it).
- Define what lives where and try to use the fewest SoR/data repositories.
- Define their boundaries, relationships and workflows. Vibe-code some apps backed by your SoR (you can hook up APIs to multiple) and ensure that data stays in one place.
- Do not create more static content.
- Be security conscious.
- Use frontend OAuth flow so data remains gated behind your existing auth.
You, your team, and your AI will benefit.
Pixie apps
After the initial draft of this post I wrote up the pattern separately at Vibe-Coding Shareable, Infinitely Scalable Personal Apps.
The Build #
Claude build me a React SPA, use the GitLab API (implementing public PKCE - no secrets) and render all my epics as cards. Display labels and appropriate metadata. Match the epic and label colours.Wow. That was easy. Now to sort them.
Claude I can't afford GitLab Ultimate (I only have Premium), so epics don't have parents. Instead I label parent epics as X and child epics as Y and link them as related items. I want to display parent epics as swimlanes. Child epics are linked as blocked or blocking. Fetch all epics and display them top left to bottom right sequenced in blocking order.Boom. In 30 mins I have a roadmap based on dependencies that is significantly better than GitLab’s native date-based Roadmap.
Claude when I click on a child epic card, highlight the critical path and filter out swimlanes that are irrelevant.Now I have a clear view of my critical path and a clear visual aid of where I am at.
Claude when I click on a card, bring in a ⅓ side panel on the right that has all the epic details.Claude find my epic templates and create a linter to highlight poorly written epics. Parse the description sections/components against the template, highlight missing items and add a warning glyph to the epic card.Claude fetch all my labels from GitLab and interview me to understand my workflow. Update the linter to show missing or noncompliant labels.I could go on ad nauseam. But hopefully you get the point. I now had the clearest view of my roadmap ever.
The Live View #
I then went back to the static roadmap AI had built only a few hours earlier. It was already wrong — I’d added relationships and made edits since — while the live view was never out of date. Both my AI and I now work from the same current picture.
I also stress this is only a view. I am not trying to replace GitLab and build a competing UI. That is madness. I dictate with Wispr Flow to Claude for mass updates and refresh. I still use the GitLab UI for most tasks.
I cannot overstate the value of this milestone. I now have my SoR up to date. I’m now motivated and have the tools to keep it up to date — and AI will consume exactly the same information.
It’s now key to document the workflow so both you and AI are on the same page.
Claude update my user CLAUDE.md with the workflow in which I manage my System of Record (GitLab). Rehearse with me how you fetch and link work items and document API quirks.* GitLab’s APIs are spread across REST and GraphQL, with deeply nested GraphQL structures, and it can be a challenge to fetch the right thing. Ordering work items on epics and boards takes a bit of experimentation.
Bonus: Shared State and Storage #
I then continued to refine my backlog but it was difficult for my AI to understand what I was looking at and discussing.
Claude add a copy button to each work item that copies the GitLab work item reference to the clipboard.Even this got a bit tedious after a while. So I wondered how I could give AI access to the page…
I could bastardise a work item and labels in the SoR but I figured I’d eventually need some settings and state — so a GitLab snippet (same as a GitHub gist) would be just the thing.
Claude create a private user GitLab snippet and update it whenever I click on a work item. Whenever I talk to you about a work item, query the snippet for context.Claude was running as a limited service account, so couldn’t access my private snippet. So I had to create a private project snippet instead.
Claude add a "Share" button, which creates a private user configuration project and an empty snippet. Add an option to add a service account as a Reporter member to the configuration project. When I click on a work item, update the snippet with the current item in focus. Whenever I talk to you about a work item, query the snippet for context.Now you can store simple state and as a bonus share it with peers that access via the API.
Hand written by me. Copy-editted by Claude. I used em-dashes before the slop, they’re my style and here to stay.