CompanyHgraph
Repositoryhgraph-io/hg-core
Window8 Jun – 27 Aug 2026
DomainHedera & EVM data infrastructure
Work record

Phil’s role @ Hgraph.com

Software Engineer ApprenticeJunior Software Engineer

I work at the layer where a change is hard to undo: Postgres migrations and row-level-security predicates, GraphQL resolvers and ClickHouse query plans, a streaming AI backend, and a shared React library six applications depend on.

In under three months at Hgraph I landed 143 merged pull requests and 135 commits onmain across 16 services and packages. What changed over those weeks was not the volume — it was the blast radius. Every number and claim on this page is drawn from the git history, the GitHub API, and the Linear issue tracker. Nothing here is estimated.

143Pull requests merged
(151 opened)
135Commits landed on main
16Services & packages touched
179Linear issues assigned
(141 closed as Done)
1,109Distinct files changed
~68.6kLines added
(~13.9k removed)
01 · Measured

The record, and what it does and doesn’t prove

Twelve weeks of merged work, week by week. The useful signal here is not the peak — it is that the output held steady while the difficulty of the work went up, and that the two heaviest projects landed in the quietest weeks.

Contribution share135 of the 582 commits landed on main during my tenure — 23%, third of seven contributors
Tracked issues127 distinct Linear IDs referenced in commit messages
PR shape111 of 143 merged inside 24 hours — small, single-purpose, reviewable
Largest single PR+10,756 lines / 164 files — the marketing site, from zero
Commits on main Pull requests opened
Jun 8 — 9 commits · 14 PRs
Jun 15 — 10 commits · 11 PRs
Jun 22 — 11 commits · 15 PRs
Jun 29 — 12 commits · 9 PRs
Jul 6 — 9 commits · 14 PRs
Jul 13 — 21 commits · 19 PRs
Jul 20 — 12 commits · 10 PRs
Jul 27 — 19 commits · 21 PRs
Aug 3 — 8 commits · 7 PRs
Aug 10 — 8 commits · 7 PRs
Aug 17 — 7 commits · 9 PRs
Aug 24 — 9 commits · 7 PRs

Week beginning, Mondays. Commit counts are squash-merge commits on main, so one commit is one reviewed unit of work; the finer-grained figure across all feature branches is 174.

The honest caveat, before anyone does the division. 143 pull requests and ~68,600 added lines in eleven and a half weeks reads as AI-assisted, and it partly is — Hgraph builds with Claude Code in the loop. The line count also includes generated output: Supabase and GraphQL codegen, lockfile churn, and a single 10,756-line marketing-site drop. The number worth judging me on is not the volume; it is what the changes were allowed to touch, which is what the rest of this page is about.

I have also left the review statistics off this page on purpose. GitHub will tell you I am listed as reviewer on 82 pull requests, but 80 of those are my own. The honest count of other engineers’ pull requests I formally reviewed is two. Reviewing other people’s code is the clearest gap in this record, and it is the first thing I want more of in my next role.

02 · Trajectory

Apprentice to Junior, in the shape of the work

The clearest way to read this is by the dates. On 9 June my merged work was two buttons on one page of one app. On 11 August it was a Postgres function composed into every row-level-security policy that tests organization membership. These are the five stages in between; the ordering is chronological because the scope genuinely escalated in that order.

Jun 2026 · wk 1–2Apprentice
Supervised components

Narrow tickets, one service, someone else’s seam

Single-page, single-layer tickets scoped by someone else, against APIs and a component library other people defined. No cross-service surface and nothing another engineer depended on.

  • PR #1232, first commit on main 9 June: refresh buttons on the Usage page metrics and RPS chart, +75/−52.
  • PR #1254: a half-filled trailing time bucket was making live traffic look like it had collapsed. Fixed in the chart’s own hook rather than the shared resolver, because the resolver had other consumers — the first time I picked a layer for a reason.
  • Early design-system tickets (#1246, #1250, #1251): a link font default, a ghost IconButton, removing a Button variant.
Jun – Jul 2026Vertical ownership
One engineer, every layer

A whole customer-facing feature, top to bottom

Handed the API Usage dashboard and carried it through every layer myself: ClickHouse SQL and a DDL migration, the GraphQL resolvers in services/api, and the React UI inservices/app. 30 Linear issues, roughly 28 merged pull requests. The step up is that nobody handed me a seam — I chose the metric enums, the pagination contract, and the bucketing rules.

  • PR #1304: the milestone-one dashboard, +5,160/−777 across 57 files.
  • PRs #1384 / #1394 / #1407: cursor-paginated request logs shipped as three ordered PRs — additive resolver, then the UI migration, then deleting the dead offset resolver (+0/−175).
  • PRs #1447 / #1440: GraphQL errors return HTTP 200, so status_code alone never saw them. Added a join to analytics.graphql_operations and an error-type grouping that partitions the failed set with no double-counting.
  • ClickHouse migration 008: bloom_filter skip indexes on org_id and user_id, because the table is ordered by (api_key_id, timestamp) and tenant-scoped filters were scanning every granule.
Jul 2026Producer side
Other engineers depend on it

Publishing the shared library instead of consuming it

Moved from using @hgraph-io/design-system as one of six apps to publishing it: 21 component pull requests, 10 releases cut including the library’s firstv1.0.0, and consumer migrations app by app. This is the first work where my mistake breaks someone else’s build.

  • 10 releases cut, v1.0.0 (#1253) through v1.22.0 (#1848); 37 of my commits touch the package; its test suite went from 386 cases at 1.0.0 to 514.
  • v1.0.0 removed a beta entry point, a Button variant, nine colour-math helpers and two hooks — each with a documented replacement or migration note.
  • CodePanel shipped behind a separate /code entry point (#1484) so the four consumers that never install Monaco stay Monaco-free, verified against the built bundle.
  • Introduced one class-merging convention repo-wide: 42 component files onto a cn helper (#1578), then 62 call sites in the app (#1710), then a lint guard landed last so nothing could regress behind me.
Jul – Aug 2026Repo-wide programs
Production failure modes

Migrations, quality gates, and retiring a live key format

Took a dependency audit delegated to me and turned it into a five-issue program, rebuilt the repo’s commit gates, refactored the Taskfile that deploys five hosts, and ran a staged retirement of an API key format that was live in production.

  • Apollo Server 4→5 ahead of end-of-life (#1452), React 18.3→19.2 across eight packages (#1519, #1534), react-router 6→7 across 58 files (#1532), TypeScript 5.6→5.9, recharts 2→3.
  • PR #1528: production charts silently lost their axes, grid and tooltips under React 19. recharts 2.15.4 flattens fragment children through react-is@18, which does not recognise React 19’s element type — so everything inside a fragment was dropped. Hotfixed, then upgraded to recharts 3 to retire the bug class.
  • PR #1308: split a ~700-line pre-commit hook into pre-commit static gates and pre-push unit suites, with shared logic factored out. PR #1576: gitleaks secret scanning on the staged diff plus a pinned CI job.
  • PR #1708: collapsed five drifted compose dispatchers into one, and made the six CI-deployed services refuse a local task up — which on the app VM could otherwise place a shadow container on the production network.
Aug 2026Junior Software Engineer
Two concurrent flagships

Two multi-service projects with real security boundaries

Two projects at once, each spanning several codebases, each with a security or cost boundary rather than a feature surface. Enterprise SAML single sign-on, and a full rebuild of hgraph.ai’s AI agent. Both are written up in full below.

  • SAML SSO: four Postgres migrations, an organization boundary enforced in row-level security and in the GraphQL layer, three containment triggers, an admin provisioning endpoint, and the customer-facing configuration guide. Sole author on all ten pull requests.
  • Agent v2: a Server-Sent-Events chat service, a thread store under row-level security, server-side credential isolation, org-scoped spend ceilings, multi-chain inference, and inline chart / table / entity rendering.
  • Both delivered while the weekly commit count went down — August’s 32 commits contain the two largest non-marketing pull requests of my tenure.
03 · Flagship

Enterprise SAML single sign-on

Enterprise prospects treated single sign-on as table stakes, and one enterprise customer needed their team signing into the Hgraph dashboard through their own Okta. I designed and built the whole capability — ten pull requests, sole author on every one, reaching seven areas of the monorepo. Released to production on 13 August 2026.

LinearProject Okta SSO — project lead, author of all 14 issues
Pull requests#1721 · #1738 · #1751 · #1763 · #1775 · #1776 · #1823 · #1849 · #1860 — sole author
Migrations077, 078, 079, 081
Tests67 SSO integration cases against a real Postgres, plus 9 unit cases
Releasedapi-v2.11.1 / app-v2.43.1, 13 Aug 2026
DeliveredHG-3333 → HG-3389 · 3 Aug – 25 Aug 2026

One predicate, and every policy inherits the boundary

The design decision I am proudest of is where the guarantee lives. Enforcement is not in a controller and not at account creation — it is a single Postgres function composed into the membership predicates, so every row-level-security policy that already tested “is this user in this org” inherited an SSO boundary without being rewritten.

01Work emailTyped at the login form; the SSO call itself doubles as the domain-capture probe, so no lookup endpoint leaks which companies are customers.
02OktaThe customer’s identity provider authenticates and POSTs a signed SAML assertion.
03Kong → GoTrueAn open route carries the assertion to self-hosted GoTrue’s ACS — open because a browser POST from Okta carries no API key.
04Provision or absorbA new user is provisioned just in time; a user with a pre-existing password account has it absorbed in one transaction.
05BoundaryEvery read and write is gated on the SAML provider in the session token matching the organization’s.
Enforcing where a guarantee can actually hold

GoTrue offers no hook to reject a social login by email domain, so a user at a captured domain can click “Continue with Google” and succeed. Rather than unreliably preventing that account, I let it exist as a zero-access shell and moved enforcement to the authorization layer. api.session_can_access_org() reads the SAML provider out of the session token’s amr claim and is ANDed intoapi.is_org_member and api.is_org_admin. Assume the application layer is buggy, and put the boundary underneath it.

Containment in triggers, not controllers

Three BEFORE triggers stop an SSO account from owning an org outside its mapping, from joining another org through any write path including service-role inserts, and from being invited elsewhere. No application bug can route around them. Defence in depth: row-level security, a resolver gate across 24 org-addressed resolvers, and the triggers.

Absorbing an account that already exists

api.migrate_sso_user() is a SECURITY DEFINER function that runs the whole migration in one transaction: it verifies the domain is genuinely captured by that user’s provider, re-points 19 user-linked tables — memberships, keys, credentials, projects, saved editor state, ownership columns — resolves role collisions by rank, bans the old account by writing banned_until directly, and deletes its sessions and refresh tokens. Idempotent under concurrency, execute revoked from anon and authenticated, with an integration test asserting it cannot be invoked through the REST layer.

Blast radius, deliberately bounded

Every check keys on the organization’s sso_enforced flag or on thesso: identity prefix, so organizations without SSO and personal accounts are provably unaffected. The migration also revokes the table-level grant onapi.organization and re-grants an explicit column list, because a permissive read policy would otherwise have exposed SSO configuration through the auto-generated REST API.

The parts that only show up once real users hit it
  • The social-login trap. A captured-domain user signing in with Google succeeded, got a stray personal workspace minted, and was therefore no longer zero-org — so absorption would never run on any later login either. Fix: every callback arrival, whether PKCE, hash token, or an existing session, funnels through one finishing path that checks the domain before provisioning anything.
  • No raw error strings, ever. The callback parses the error code from both the query string and the URL fragment, because a magic-link redirect delivers it in the fragment — where it previously fell through to a generic “authentication timed out” after a ten-second stall. The banned-account code produced by my own absorption logic now renders an inline work-email prompt that continues into SSO.
  • A first-login race. Two concurrent provisioning attempts could collide on an organization slug. Slugs became deterministic per user, email and attempt, so a retry converges instead of racing.
  • A flash of the wrong screen. A brand-new SSO user’s very first sign-in briefly showed the full-screen “this organization requires single sign-on” error before the overview rendered. Traced through six hops to a client mutation document that selected too few fields, so the freshly seeded organization was cached without the flag the guard reads.
  • Operator footguns closed. The provider ID has no foreign key, so a hand-copied UUID typo fails silently and only surfaces as a broken login for the customer. The admin endpoint registers the provider and maps it to the org in one atomic action, and any org pointing at a provider that no longer exists is flagged as a broken mapping in the UI. There is deliberately no delete control — deregistering strands existing identities, and their next login would create duplicates that absorption will not absorb.
  • The customer-facing surface. I wrote the Okta configuration guide published in the docs site, and the admin provisioning screens — because the operator’s 2am failure mode is part of the feature, not someone else’s ticket.
4 Postgres migrations24 resolvers behind one gate wrapper42 files referencing the enforcement helper3 containment triggers19 tables re-pointed by absorption67 integration test cases10 PRs, sole author
04 · Flagship

hgraph.ai Agent v2

hgraph.ai is the front door to Hgraph’s paid Hedera and EVM data API: you ask a question in plain English, the agent infers the chain, runs real GraphQL, SQL or RPC through Hgraph’s tool server, and answers with rendered data plus the exact query it ran. Version 1 was a stateless one-screen chatbot with the API key held in the browser. I rebuilt it as a persistent, shareable product — ten pull requests, ~16,600 lines across 270 files, spanning four codebases.

LinearProject hgraph.ai Agent v2 — project lead, 8-part epic under HG-3371
Pull requests#1843 · #1845 · #1846 · #1852 · #1861 · #1862 · #1866 · #1869 · #1870 · #1873
Biggest#1852 backend — +6,329/−571 across 58 files
Migration081 — 437 lines, column-level grants, 4 RLS policies
Tests~200 agent unit tests, 805 API integration tests green
DeliveredHG-3371 · 17 Aug – 27 Aug 2026

The API key stopped touching the browser

The single most important change between v1 and v2 is that the client no longer holds a credential. The browser sends a session token and nothing else; the backend resolves the caller’s organization, looks up that org’s live credential server-side, and uses it only as the tool server’s auth header. X-Api-Key was deleted from the contract.

01JWT onlyThe SPA posts a question with a Supabase session token. No credential leaves the server side.
02Resolve orgThread’s org, then requested org with membership enforced, then oldest membership — every branch SSO-gated.
03Cap checkPer-seat daily allowance and a per-org monthly USD ceiling, on durable counters.
04Tool loopA bounded tool-use loop against the MCP tool server, streaming tokens and tool narration in arrival order.
05Finalize onceOne idempotent write persists the turn, its tool trace and four-way token accounting — on every exit path.
Streaming that survives a disconnect

POST /agent/chat emits a typed Server-Sent-Events contract of eleven named events — org, thread, attachment, usage, status, tool start, tool result, text delta, stats, complete, error — with per-token deltas from the Anthropic streaming SDK. A single finalize() behind a guard flag runs on every exit path, so a client that closes the tab mid-answer still persists what completed rather than losing the turn.

A schema the browser physically cannot abuse

The thread store uses column-level grants rather than blanket table grants: an authenticated client may insert only five columns and update only two. Share slugs, publish state and soft deletes are unwritable from the client by construction, not by validation. Four row-level-security policies scope reads. The schema carries a surfacecolumn on purpose, so a future in-dashboard assistant writes into the same history instead of needing a migration.

Cost as a first-class constraint

Spend control prices real input, output, cache-read and cache-write tokens into a per-seat daily allowance for the expensive reasoning mode and a per-org monthly USD ceiling, held on durable Valkey counters with alert webhooks at 80% and 100%. The counter path runs as a dedicated cache user restricted to two key prefixes and a ten-command allowlist, so the agent’s spend accounting cannot read or write anything else in a shared cache.

Rendering that degrades instead of breaking

The model emits a validated chart specification in a fenced block, which promotes to a line, area or bar chart on a shared axis and tooltip base. An unclosed fence mid-stream shows a “preparing chart” placeholder rather than raw JSON; an invalid specification falls back to visible markdown so nothing is ever silently lost. Answers also carry the exact query that produced them, copyable and deep-linked into the GraphQL playground.

Chat state as a pure reducer

Streaming state is a reducer over segments and pending tool slots rather than scatteredsetState calls, which makes event ordering, tool-slot correlation and delta coalescing unit-testable instead of hopeful. Every callback is guarded on an abort signal and aborts on unmount.

Provisioning, sharing, multi-chain, and the four components I kept extractable
  • Provisioning on the first message. A user with no organization gets one provisioned before their question is answered, through the same idempotent path the dashboard uses — I extracted it from the dashboard’s own controller rather than writing a second one. The stream emits the org event ahead of the thread event so the interface hydrates its switcher in the right order.
  • Share links. Publishing mints a 144-bit unguessable slug. A shared page can be frozen at its publish moment or kept live, and its query traces can be stripped from the payload entirely. There is no anonymous database path at all — public reads go only through explicit endpoints, and the link 404s immediately on unpublish. Unguessable and revocable, which is exactly as strong as I would describe it.
  • Fork, across an OAuth round trip. “Continue this chat” copies a shared thread into the visitor’s own workspace and survives the sign-in redirect; if the message copy fails, the fork deletes itself rather than leaving a half-thread.
  • Multi-chain inference. The cached system prompt carries chain-inference rules plus the live chain catalog from the tool server. The chain became a default rather than a pin, so the model can load context for any other chain, cached per chain and topic for 24 hours, and degrades gracefully when the catalog is unreachable.
  • Redaction on the way back in. Tool results are fed back to the model, so every tool result and error goes through a secret-redaction pass first.
  • Extractability as a design constraint. The four primitives the design system lacked — data table, line chart, entity card, command menu — were built props-in / events-out with no agent imports and design-system tokens only, so promoting them into the shared library is a file move rather than a rewrite.
11-event SSE contract~16,600 lines / 270 files4 codebasesColumn-level grants + 4 RLS policies~200 agent unit tests805/805 API integration tests
05 · The rest of it

Six more projects, in Linear’s own grouping

My 179 assigned issues sit across eight Linear projects. These are the six that are not the two flagships — including the one I would actually point a senior engineer at first, which is the least glamorous of them.

30 issues · ~28 PRs · Jun–Jul

ClickHouse usage analytics

The customer-facing API Usage dashboard, carried alone from ClickHouse SQL through GraphQL resolvers to the React interface. This was the pivot from ticket-taking to owning a feature.

  • Latency percentiles, rate-limit headroom, error metrics split between transport and GraphQL failures, and a cursor-paginated request-log explorer.
  • Adaptive time bucketing so a chart’s point count stays bounded whatever range you ask for, with aligned bucket edges and a server-side ceiling.
  • Query performance: bounded joins for partition pruning, bloom-filter skip indexes for tenant-scoped filters, and skipping a cross-tenant join entirely when nothing selected needs it.
  • The judgment call: a swallowed ClickHouse error was rendering as “0 requests, 0% of quota”. A plausible wrong number is worse than a visible failure, so I raised an explicit unavailable state instead.
API unit suite 83 → 282 tests as this work landed
3 ordered stages · Jul

Retiring a live API key format

The unglamorous one, and the one I would hand a senior reviewer first. Two key variants had to be removed from a system that was actively serving traffic.

  • Stage one stopped publishing the dev variants in all four cache-sync paths, deliberately leaving the removal paths intact so anything already published still got cleaned up.
  • Instrumentation proved zero dev-key requests in 24 hours against 14.07 million production requests before anything was dropped.
  • The migration PR was held as a draft until stage one had run a full job cycle, on the reasoning that reverting a code deploy is cheap and reverting a migration is not.
  • The instrumentation was removed last, in its own PR, so the temporary code did not become permanent.
PRs #1663 → #1670 → #1684, in that order on purpose
34 issues · 10 releases · Jun–Aug

Design system

Producer-side ownership of @hgraph-io/design-system: the library six frontends build against. 21 component PRs, 10 releases cut, consumer migrations app by app.

  • Shipped the date picker, date-range picker, time picker, code panel, theme toggle, site footer, loading skeletons and the canonical brand favicons.
  • Cut the library’s first v1.0.0, with each removal carrying a documented replacement.
  • Narrowed the React peer range to 19 only after every consumer in the monorepo had already migrated — sequencing, not just upgrading.
  • Rolled each release out to the consumers myself rather than leaving five teams to discover the bump.
Suite 386 → 514 test cases; 37 commits to the package
164 files · +10,756 lines · Jul

hgraph.com marketing site

A production marketing website built from zero — the largest single pull request of my tenure. The more interesting fact is what happened next.

  • Then I deleted 1,891 lines of my own hand-rolled CSS in favour of a proper Tailwind theme-and-layer structure, replacing custom components with design-system ones.
  • Migrated it onto React Router 7 framework mode with server rendering off and every static and content route prerendered at build time.
  • Legal and policy pages, mobile navigation and hero fixes, a generated sitemap, and hiding embeds that had gone dead.
Refactoring my own recent work down, not defending it
20 issues · Jun–Aug

Repo infrastructure & security hygiene

The gates and tooling every engineer in the repo runs dozens of times a day.

  • Split a ~700-line pre-commit hook into fast static gates on commit and unit suites on push, with the shared logic factored into one library.
  • Added repo-wide gitleaks secret scanning — on the staged diff locally, and as a pinned CI job on every pull request, with a documented allowlist runbook.
  • Collapsed five drifted compose dispatchers into one, and made CI-deployed services refuse a local bring-up that could otherwise put a shadow container on the production network.
  • Dead-code detection tightened across all eight leaf services at the time, and an edge-level fix so denied requests still return CORS headers.
PRs #1308 · #1576 · #1577 · #1708 · #1768 · #1824
6 issues · Jul

Activation & onboarding

Getting a new signup from account to first successful API call, measured rather than assumed.

  • A get-started checklist backed by real activation events written from the actions themselves — key reveal, first playground query — not from page views.
  • Guided product tours for each checklist item.
  • A new table for app-owned user state, plus a backfill migration so existing active users were not shown an onboarding flow they had long outgrown.
HG-3224 · HG-3228 · PRs #1571 · #1573 · #1589 · #1595 · #1608 · #1618
06 · Before and after

What actually changed, dimension by dimension

Same person, same repo, eleven weeks apart. I find this the most honest way to show a level change: not a title, but a widening of what a single change of mine is trusted to touch.

Week 1 versus week 11, drawn from the same git history
DimensionJuneAugust
Blast radiusRefresh buttons on one page of one app. +75/−52.One Postgres function composed into the membership predicates, so every row-level-security policy testing org membership inherited an SSO boundary — plus 24 resolvers behind one gate wrapper.
Layers per changeInterface only, against an API and a component library other people defined.A DDL migration, GraphQL resolvers and the React interface in one feature; Agent v2 spanning four codebases plus gateway and cache configuration.
Who depends on itConsumed the shared UI library as one of six apps.Published it: 10 releases, breaking changes with documented replacements, peer ranges narrowed only after every consumer had migrated.
Debugging depthFixed a stale time-window bug in a chart I had written days earlier.Traced a production chart outage to a charting library flattening fragment children through a React version that could not recognise React 19 elements — then upgraded to retire the whole bug class.
Deploy riskSelf-contained pull requests; nothing to sequence.A three-stage retirement of a live key format, the migration held back until the code stage had been observed through a job cycle, each intermediate state verified locally.
Security postureNo security surface in the early tickets.Column-level grants replacing a table grant; definer functions with execute revoked from public roles; three containment triggers; a credential that never reaches the browser.
AudienceAn internal page for internal users.A customer-facing identity-provider setup guide, an admin provisioning surface, and the operator failure mode designed for rather than left open.
07 · Stack

What I actually used, not what I’ve read about

Everything below appears in code I authored and merged in this repository. I have left out anything I only configured or only read.

Languages & runtime

TypeScript 5.9SQL — PostgreSQLSQL — ClickHouseBunNode / Express 5Bash

Frontend

React 19React Router 7 (framework mode)TanStack Queryrecharts 3Tailwind CSS v4react-aria-componentsViteVitestStorybookaria-live / aria-sort / inert

API & backend

GraphQL — Apollo Server 5Selection-set-aware query planningServer-Sent Events streamingSupabase / PostgRESTKong routing & pluginsHAProxyValkey / Redis — ACL users, atomic countersHasura metadata

Data & analytics

ClickHouse quantile / countIf aggregationBloom-filter skip indexesBounded joins & partition pruningQuery deadlines & abort signalsPostgres row-level securitySECURITY DEFINER / INVOKERColumn-level grantsCursor / seek paginationGrafana dashboards

Auth & security

SAML 2.0 federation (self-hosted GoTrue)JIT provisioning & account absorptionJWT claim-based authorizationDefence in depth — RLS + resolver gate + triggersPKCE / OAuth callbacksServer-side credential isolationSecret redactiongitleaks scanning

AI engineering

Anthropic streaming SDKBounded tool-use loopsMCP client integrationPrompt caching with a stable prefixFour-way token accountingCost ceilings & spend countersGraceful degradation on tool failure

Platform & tooling

npm library ownership — 10 releasessemver & peer-range disciplineMonorepo framework migrationsGit hooks architectureDocker / Podman ComposeTaskfileGitHub ActionsknipESLint plugin config

How I work

Small single-purpose PRsAdditive-then-migrate-then-delete sequencingFix at the layer that owns the bugWrite the test that would have caught itClaude Code in the loop, reviewed by handDocs and runbooks as part of the feature
08 · Unfinished

What I know is still open

I would rather tell you the gaps than have you find them. As of 27 August 2026:

  • Agent v2 is at 6 of 8. Share links and the funnel handoffs are in review; accessibility hardening and deleting the legacy agent are the two issues after that. The v2 surface is behind a build flag until then.
  • SSO provider management is thin. Registration and mapping are automated; refreshing identity-provider metadata, editing captured domains and re-pointing a provider are still backlogged, and there is no delete control on purpose.
  • No SCIM and no deprovisioning path. Provisioning is just-in-time on first sign-in. Removing a user from the identity provider does not yet remove their Hgraph access, and that is the next thing I would build.
  • Spend ceilings are pre-flight, not hard stops. The check runs before a turn and accrual happens after it, so one in-flight turn can overshoot, and the counter fails open if the cache is unreachable. That is a deliberate availability trade-off, and it is documented as one rather than being presented as a hard limit.
  • Reviewing other people’s code. Two pull requests, honestly counted. The clearest thing missing from this record.