refactor(project): one builder and parser for project addresses; short links for GitHub projects (G-A1c) - #187
Conversation
… (G-A1c)
projectUrl/tryProjectUrl take the page as plain text ('queries/x', split at
each '/') or as its segments (['query', 'customers/invoices']): an element is
ONE segment whatever it holds, so a folder-qualified query id is written
`%2F` and addressed by one `query/:queryId` route. The sites of the sweep
need this; the text form is unchanged.
DEMO_PROJECT_REF is the demo project (datatug/chinook-demo, the repository
root): the one place the home page, the store page and the hand-off page get
its id from. Its address is /project/github.com/datatug/chinook-demo, and it
is the one trusted project of isTrustedProjectAddress.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t links for GitHub projects (G-A1c)
Every place that builds or parses a project path now uses projectUrl /
tryProjectUrl / parseProjectUrl, found by content. A GitHub project's
in-app links are at its short address (/project/github.com/<owner>/<repo>
[/tree/<ref>[/<dir>]/-/<page>]); the old /store/github.com/project/<id> form
still opens (redirecting it is G-A1d, held). Agent and Firestore projects
keep their addresses and behaviour.
datatug-main:
- DatatugNavService (goProject, goProjPage, goEnvironment, goCatalog,
goEntity, goQuery, goBoard, goTable, projectPageUrl) writes its address
with tryProjectUrl; a project with no exact address is logged, not
navigated to a guess.
- DatatugNavContextService reads the store, project and page with
parseProjectUrl instead of regular expressions over the whole URL (a query
string is no part of an environment or a table any more).
- back links and router commands: entity-edit, queries, query page, SQL
editor, servers (goDbServer), environment page, project page folder,
investigation context and table page (open query), incident evidence
links; projectPageHref is the one helper for a link in a template.
- the new-project form and the store page's "Open a GitHub project" form
write the project id with formatGithubProjectId (the one id, owner and
repo in lower case, the default folder dropped); the store page row links
to the project's address.
- the address check's default-branch redirect is written with projectUrl.
- the home page's demo entry, and the store page's, are the demo project at
the root of datatug/chinook-demo (DEMO_PROJECT_REF).
datatug-app:
- main.ts: the agent base URL comes from agentBaseUrlOfPath (store only, so
the parser is not pulled into the initial bundle): a GitHub project, at
either address, has no agent. It used to get localhost:8989 on its short
address.
- the hand-off page's "Open the demo project" link is the demo project's
short address.
- cli-chat-capability.ts is left as is, with a test that the short address
does not match it.
Guard: project-address-guard.spec.ts scans the sources for hand-built project
paths and GitHub id joins, and proves the short and old spellings of a
project resolve to the same {storeId, projectId}.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…github-store (G-A1c) - github-store.spec.ts opens the demo-projects project at /project/github.com/datatug/datatug-demo-projects/tree/HEAD/demo-project-1 (its pages follow /-/), and gains three cases: the side menu of a project opened at its short address links to short addresses; the old address still opens it, with no redirect, and its in-app links are short; the home page's demo entry opens /project/github.com/datatug/chinook-demo. - DATATUG_E2E_GITHUB_FAKE=<a datatug-demo-projects checkout> serves that checkout in place of api.github.com, raw.githubusercontent.com and cdn.jsdelivr.net (helpers/fake-github.ts), so the spec needs no network and spends none of the 60 anonymous API requests an hour. Unset, it reads the real GitHub as before (CI). - demo-handoff.spec.ts: the holding page's "Open the demo project" link is the demo project's short address. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
[review r1 #187] Adversarial review (Opus): production builds of this head, main and the merge with #186 served side by side; click-through of every changed navigation for agent (real Reviewed-Head: 5a1e253 Agent and Firestore projects navigate the same as on main; GitHub projects navigate correctly from both forms; switching between projects never loses or swaps the project; query ids with a slash open the right query; the agent base URL is unchanged for every agent address and no GitHub page calls the local agent; the demo entry renders boards, entities, queries, environments, servers and chat with no console errors; initial bundle +287 B. The two CI e2e failures did not reproduce (82/82); no new race found; likely GitHub rate limits on shared runners. Merge with #186 is clean (demo-handoff e2e 113/113 on the merged tree). Minors, carried to #184:
Not verified: Firestore projects with real data, signed-in store rows, Cloudflare's VERDICT: blockers=0 majors=0 minors=5 land=yes 🤖 Generated with Claude Code |
What
G-A1c of
docs/design/demo-as-github-project.md(backstage, section 8): every place that builds or parses a project path usesparseProjectUrl/projectUrl/tryProjectUrl(libs/datatug/project-address, re-exported fromlibs/datatug/main/src/lib/nav/), found by content, not by the list in 3.4. A GitHub project's in-app links are at its short address (/project/github.com/<owner>/<repo>[/tree/<ref>[/<dir>]/-/<page>]). The old/store/github.com/project/<id>form still opens, with no redirect (G-A1d is held). Agent-store and Firestore-store projects keep their addresses and behaviour.Based on
a2481560(G-A1b, #183). Three commits: the library change, the sweep, the e2e.Sites changed (file:line of the new code, before -> after)
Library
libs/datatug/project-address/src/lib/project-url.tstryProjectUrl/projectUrltake the page as text ('queries/x', split at each/) or as segments (['query', 'customers/invoices'], one element = one segment, a/in it is written%2F).DEMO_PROJECT_REF(chinook-demo@datatug@) added.libs/datatug/main/src/lib/services/nav/datatug-nav.service.ts(the builders named in 3.4::84,:116-125,:201-206, plusgoProjPage,goTable,goEntity...)goProject :87,goEnvironment :110,goCatalog :129,goEntity :149,goQuery :168,goBoard :206,goProjPage :260,goTable :277:['store', storeId, 'project', id, ...]arrays and/store/${...}/project/${...}strings ->tryProjectUrl(ref, page). A project with no exact address is logged and not navigated to a guess.projectPageUrl :224:/store/${getStoreId(..)}/project/${..}/<name>[/<id>]->projectPageHref(same address for agent stores).libs/datatug/main/src/lib/nav/project-page-href.ts(new):projectPageHref(ref, page)= the address, or/when there is no project / no exact address. Used by every template link below.libs/datatug/main/src/lib/services/nav/datatug-nav-context.service.ts:35-:55the regexesreStore,reProj(and thelegacyShapeOfrewrite to the old shape) ->locationOf :302:parseProjectUrlgives store, project and the page after it;reEnv/reEnvDb/reTablenow run on that page only (a?queryis no longer part of an environment or a table). Short addresses still need the short route to have opened them (unchanged rule).Back links and navigations
pages/signed-in/entity-edit/entity-edit-page.component.ts:109`/store/${storeId}/project/${projectId}/entities`->projectPageHref(ref, 'entities')queries/queries/queries-page.component.ts:115(defaultBackHref) ->projectPageHref(ref)queries/query/page/query-page.component.html:5+.ts(queriesBackHref),queries/query/sql-query/sql-query-editor.component.html:5+.ts:defaultHref="/store/{{..}}/project/{{..}}/queries?folder=.."->[defaultHref]="queriesBackHref"pages/signed-in/servers/servers-page.component.ts:280(goDbServer):['store', .., 'project', .., 'servers', 'db', driver, id]->tryProjectUrl(ref, [...])pages/signed-in/environment/environment-page.component.ts:114(defaultBackUrl, was the constant/store/localhost:8989for every project) -> the store page of the project's own store; the old constant until a project is known (agent onlocalhost:8989: identical).:209(goEnvSubPage, was/project/<id>/env/.., a path that matched no route) ->tryProjectUrl.pages/signed-in/env-db-table/env-db-table.page.ts:599andpages/signed-in/investigation-context/investigation-context-page.component.ts:682(open a query):router.navigate(['/store', .., 'project', .., 'query', encodeURIComponent(id)], {queryParams})->router.navigateByUrl(tryProjectUrl(ref, ['query', id]) + '?id=' + encodeURIComponent(id), {state})pages/signed-in/project/project-page.component.ts:222(goProjFolder, no caller; wasproject/<id>/<items>) ->tryProjectUrlpages/signed-in/store/datatug-store-page.component.ts:436(projectLink) +.html:67:[routerLink]="'project/' + project.id"->projectLink(project)(short address for GitHub).:498:`${repository}@${owner}@${folder}`->formatGithubProjectId.project/new-project/new-project-form.component.ts:300:`${repo}@${org}@${folder}`->formatGithubProjectId(the one id: lower case owner/repo, default folder dropped).incidents/incident-evidence-targets.ts:66-142:['/store', storeId, 'project', id, ...]->[tryProjectUrl(..), ...page segments](router commands, so the page segments stay raw for the router to encode).routes/github-project-address-check.ts:212(defaultBranchPath): the hand-written/project/github.com/<o>/<r>/tree/prefix and string surgery ->projectUrl.Home page and demo entry
pages/home/my-projects/my-datatug-projects.component.ts:72:datatug-demo-projects@datatug@demo-project-1->DEMO_PROJECT_REF: opens/project/github.com/datatug/chinook-demo. Title unchanged.pages/signed-in/store/datatug-store-page.component.ts:82(the store page's copy of the same entry, "kept in sync by hand") -> the same constant.apps/datatug-app/src/app/demo-holding-page.strings.ts:54(DEMO_PROJECT_PATH, the holding page's "Open the demo project" link): the old-form array ->projectUrl(DEMO_PROJECT_REF).App
apps/datatug-app/src/main.ts:103:location.pathname.match(/\/store\/([^/]+)/)with a fall back tolocalhost:8989->agentBaseUrlOfPath(pathname)(src/app/agent-base-url.ts): any/project/...address and the GitHub store (either id) get no agent (about:blank, not an address a request can reach; the semantic client's agent context throws on an empty base);/store/<id>as before; anything else the local agent as before. A GitHub project at its short address used to gethttp://localhost:8989/datatug(the page calledlocalhost:8989); at the old address it got//github.com:undefined/datatug.apps/datatug-app/src/app/cli-chat-capability.ts:4left as it is, withcli-chat-capability.spec.ts: the short address (and look-alikes) do not match.Left as is, with the reason
project-page.component.html:6('/store/' + storeId) andenvironment-page.component.ts:114: they write the address of a STORE page, which stays/store/<id>in both shapes; not a project address. Both are allowed files of the guard (withcli-chat-capability.ts), each with its reason.board-page.component.ts:268,boards-page.component.ts:194: back links computed by dropping the last path segment oflocation.pathname; they work on either shape, equally (the boards one was already odd for the old shape).datatug-nav.service.tsgoStore: a store address.The regression guard (design 3.4, point 3)
libs/datatug/project-address/src/lib/project-address-guard.spec.ts:{storeId, projectId, rest}, and the old one's canonical address is the short one (6 projects); every other store has the old shape only;projectUrlwrites only the two shapes;apps/datatug-app/srcand everylibs/datatug/*/src(300+ files, comments stripped) fails on a hand-built project path (`/store/${`,'/store/' +,/store/{{,['store', .., 'project'],`/project/${`, regexes over/store/or/project/,.split('@'), a repo@owner join). Three allowed files, each with its reason; each must still match. Checked by mutation: a`/store/${s}/project/${p}`added to a source file fails it.Checks
pnpm run check:zoneless: OK.github-store.spec.ts9 / 11 on the production build, the 2 others read component state throughwindow.ng.getComponent, which a production build does not have; all 11 pass on the development server (DATATUG_E2E_GITHUB_FAKE, below).datatug serve0.51.0, dev server): 17 / 17. The default chromium project (agent-mocking suites + github-store): 36 passed, 1 skipped.github-store.spec.tsnow opens the demo-projects project at its short address, and has three new cases (side menu of a short address links to short addresses; the old address opens as typed and its in-app links are short; the home page's demo entry opens/project/github.com/datatug/chinook-demo).DATATUG_E2E_GITHUB_FAKE=<datatug-demo-projects checkout>(e2e/helpers/fake-github.ts) serves that checkout in place of the three GitHub hosts; unset (CI), the spec reads the real GitHub as before.Screenshots: overview, queries, chat; 1280 and 390; agent project and GitHub project
Production builds of
origin/main(a248156) and of this branch, side by side; the agent project is a realdatatug serve(the demo project) on one port for both; the GitHub project is the demo-projects project served by the fake in both. Each page is a fresh browser context. 30 images, compared pixel by pixel:Files:
.../scratchpad/apps-g-a1c/shots/<agent|github>-<base|new|new-short|new-old>-<overview|queries|chat>-<1280|390>.png.Initial bundle (production build)
main-*.jsrawFirst attempt (
agentBaseUrlOfPathusingparseProjectUrland importingGITHUB_STORE_ID) cost +7 kB raw: the parser was not in the initial bundle at all, and importing a constant kept all ofgithub-project-address.ts(+2.9 kB). SoagentBaseUrlOfPathreads the store only, with the two ids written out (a spec asserts they equal the library's).Design and reality
main.ts:103should use the two functions; doing so costs ~7 kB in the initial bundle against "a few hundred bytes". It reads the store only (it never needed the project), so it does not use the parser; a GitHub project still never gets an agent address.project-page.component.html:6andenvironment-page.component.ts:110as hard-coded old shapes; both write a store address (see above). The environment page's fixedlocalhost:8989back link is now the project's own store.e2e/journey/*): agent addresses are unchanged, so nothing to rewrite there; the fixture still mountsdatatug-demo-projects/demo-project-1(the table's "repoint to the new repo" waits for the CLI to be able to servechinook-demoat its root; I could not check that here, there is nochinook-democheckout). Passing 17 / 17 as they are.chinook-demo, as the home page's entry does (its own comment says they must match). The hold page, the question handling and the safety net are untouched (82 / 82).datatug-demo-projects/demo-project-1: the form requires a folder, so it cannot open a project at a repo root.legacyShapeOfis gone, replaced bylocationOf; it was not an item of the issue. Theion-headere2e item was already fixed in feat(project): short address for GitHub projects (G-A1b) #183.)Not verified
label-spacing.spec.ts(no fake) ran against it, about 4 anonymous API calls in all (2 per cold visit), within the default chromium run.tryProjectUrlfor agent stores; the short route does not match under that profile, so a GitHub project in an incident link would not open there; incident links are agent-only today).github-store.spec.tscases that needwindow.ngon the production build; they pass on the development server.🤖 Generated with Claude Code