This monorepo is consumed by parent repos (e.g., inspect_scout, inspect_ai) as a git submodule. This guide covers the submodule workflow for frontend contributors working in a parent repo.
Examples below use
inspect_scoutpaths. The same patterns apply to any parent repo that embeds this monorepo as a submodule — just substitute the path:
- inspect_scout:
src/inspect_scout/_view/ts-mono/- inspect_ai:
src/inspect_ai/_view/ts-mono/
The parent repo pins this monorepo to a specific commit via a git submodule. The built dist/ is committed to the parent repo, so Python-only contributors and end users need no Node.js — they get the frontend automatically.
Only frontend contributors need to be aware of the submodule workflow.
The submodule directory exists but is empty. Run:
cd <parent-repo-root>
git submodule update --init
cd <submodule-path>
pnpm installgit clone --recurse-submodules <parent-repo-url>
cd <parent-repo-root>/<submodule-path>
pnpm installBy default, git pull in the parent updates the submodule pointer but does not update the submodule's working tree. This means your local code can silently fall out of sync. Set this once to avoid that entirely:
git config submodule.recurse trueWith this setting, git pull automatically updates the submodule working tree to match the pinned commit. Without it, you must manually run git submodule update after every pull.
Changes that don't touch the Python API.
-
Branch in both repos:
# Parent repo git checkout -b feature/my-ui-change # Monorepo (submodule) cd <submodule-path> git checkout -b feature/my-ui-change
-
Develop in the submodule (
pnpm devorpnpm watch), editing files underapps/orpackages/. -
Build the production bundle:
pnpm build # outputs to the parent repo's dist/ path -
Commit and push the monorepo branch, then open a PR:
git add . git commit -m "feat: description of the change" git push origin feature/my-ui-change
-
Merge the monorepo PR through its normal review process. The PR may be squash-merged or rebased — that's fine.
-
Update the submodule to the merged commit. This is the critical step — switch to
mainand pull so the submodule is on a branch (not detached HEAD) at the commit that landed:cd <submodule-path> git checkout main git pull
-
Commit the submodule bump and dist in the parent repo:
cd <parent-repo-root> git add <dist-path> <submodule-path> git commit -m "feat: description of the change" git push origin feature/my-ui-change
-
Merge the parent repo PR. CI validates that
dist/matches the submodule pointer.
Changes that require new or modified Python API endpoints.
- Update the Python API models
- Re-export the OpenAPI schema:
.venv/bin/python scripts/export_openapi_schema.py
- Regenerate TypeScript types (the
types:generatescript lives inapps/scout, not the monorepo root):cd <submodule-path>/apps/scout pnpm types:generate
- Develop the frontend against the new types (
pnpm dev) - Build and commit as in the UI-only workflow above, including the updated
openapi.jsonandgenerated.ts
The submodule URL in .gitmodules is HTTPS (works for everyone, including CI). To use SSH locally:
git config submodule.<submodule-path>.url git@github.com:<org>/<monorepo>.gitThis is local-only and won't affect other contributors.
All commands run from the parent repo root. The git -C <path> flag runs a git command as if you'd cd'd into that directory.
You can also keep a dedicated terminal cd'd into the submodule for day-to-day frontend work — whatever feels natural.
# Initialize the submodule (once)
git submodule update --init
# Update submodule after git pull
git submodule update
# Check which commit the submodule points to
git submodule status
# Advance submodule to latest upstream commit
git -C <submodule-path> pull
# Run any git command in the submodule from the parent root
git -C <submodule-path> status
git -C <submodule-path> log --oneline -5
# Auto-update submodule on pull (set once)
git config submodule.recurse trueQ: I pulled but the frontend looks stale or broken.
You probably need to update the submodule working tree:
git submodule updateIf you want this to happen automatically, set git config submodule.recurse true.
Q: The submodule is in "detached HEAD" state — is that normal?
Yes. The parent repo pins the submodule to a specific commit. When you run git submodule update, it checks out that exact commit, which results in a detached HEAD. To do development work, create a branch inside the submodule first.
Q: How do I check if my submodule is out of sync?
git submodule statusA + prefix means the submodule working tree is at a different commit than what the parent repo expects.
Q: CI says dist/ is mismatched — what do I do?
Rebuild from the submodule and commit the result:
cd <submodule-path>
pnpm install
pnpm build
cd <parent-repo-root>
git add <dist-path>
git commit -m "Rebuild dist"Q: CI says openapi.json is mismatched — what do I do?
Re-export the schema from the Python API:
.venv/bin/python scripts/export_openapi_schema.py
git add <openapi-json-path>
git commit -m "Re-export openapi.json"Q: CI says generated.ts is mismatched — what do I do?
Regenerate types from the committed openapi.json:
cd <submodule-path>/apps/scout
pnpm types:generateThen commit the updated generated.ts in the monorepo.
Q: Markdown shows as raw source (and images, highlighting, links are missing) where I render shared components.
Rich rendering in @tsmono/react and @tsmono/inspect-components is opt-in: outside a ContentTrustProvider, content is treated as untrusted and shown as plain text. Wrap the tree in <ContentTrustProvider value="trusted"> (from @tsmono/react/components) for content you trust, or derive the value from the log it came from (logContentTrust(header) from @tsmono/inspect-components/content), as the Inspect viewer does.