System Admin Shared Components Boundary Implementation Plan
For agentic workers: Execute the tasks in order and update each checkbox as work is completed. Do not modify unrelated files, commit, or push without Eron's explicit approval.
Goal: Replace system-admin's direct TypeScript imports from packages/components/src with stable public package subpaths, restore the Docker ARM64 build, and document every remaining blocker to a standalone system-admin repository.
Root cause: tools/system-admin imports packages/components/src/dialog.ts through a relative filesystem path, but docker/system-admin/Dockerfile does not copy that file into the builder. A local Vite build sees the full repository and succeeds; the selective Docker context does not and fails in Rollup.
Architecture: tools/system-admin declares @hereby/components as a workspace dependency and imports only zero-domain-dependency public subpaths (/dialog and /icons). Vite maps those subpaths to monorepo sources during the current source-based Docker build. Published package exports remain buildable so a future standalone repository can consume a versioned registry package without changing TypeScript application imports. Shared HTML, design-system CSS/fonts, and database seed ownership remain separate extraction blockers and are audited explicitly in Task 7.
Non-goals: Do not extract system-admin into a new repository, publish a package, change dialog behavior, change UI markup/CSS, move fonts, move database seed ownership, or introduce @hereby/sdk, API, or database dependencies into the system-admin frontend build.
Global Constraints
- Preserve all unrelated working-tree changes.
- Do not add compatibility fallbacks or dual import paths.
- New code comments must be English.
- Do not import from the
@hereby/componentsroot entry; use narrow subpaths to avoid pulling SDK-coupled components into system-admin. - Do not copy all of
packages/components/srcinto the Docker image. - Do not commit or push without Eron's explicit approval.
- The filtered Docker install has a mandatory decision gate in Task 5. If it requires copying SDK/API/database workspace packages, follow the defined blocked path; do not make an implementation choice ad hoc.
Expected File Set
packages/components/package.json
packages/components/tsup.config.ts
tools/system-admin/package.json
tools/system-admin/src/app.ts
tools/system-admin/src/main.ts
tools/system-admin/vite.config.ts
docker/system-admin/Dockerfile
pnpm-lock.yamlAudited Extraction Blockers (Not Modified in This Plan)
tools/system-admin/index.html
tools/system-admin/src/styles/main.css
tools/system-admin/server/routes/provision.ts
packages/components/html/ds-dialog.html
packages/static/styles/design-system.css
packages/static/styles/tokens.css
packages/static/fonts/**
packages/database/src/seeds/default-permissions.tsThese files are intentionally absent from the expected modification set. Task 7 records the required ownership changes before repository extraction; the current Docker fix must not silently absorb that larger migration.
Task 1: Record the failing baseline
Files: None
- [x] Confirm the current local workspace build succeeds:
pnpm --filter @hereby/system-admin buildExpected: PASS because the complete repository source is present.
- [x] Confirm the current Docker build fails at the known module resolution error:
docker buildx build \
--platform linux/arm64 \
--file docker/system-admin/Dockerfile \
--progress plain \
--target builder \
.Expected failure:
Could not resolve "../../../packages/components/src/dialog" from "src/main.ts"- [x] Stop if the failure differs; establish the new root cause before editing.
Task 2: Expose narrow public component subpaths
Files:
- Modify:
packages/components/package.json - Modify:
packages/components/tsup.config.ts
./dialog is already a public export and a standalone tsup entry. icons.ts is also dependency-free but currently lacks an equivalent public subpath.
- [x] Add an
./iconsexport matching the existing./dialogexport shape:
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js",
"require": "./dist/icons.cjs"
}[x] Add
icons: "src/icons.ts"to the standalone tsup entry map besidedialogandtooltip.[x] Update the existing tsup comment in English if needed; do not add Korean code comments.
[x] Build the package and confirm both public artifacts exist:
pnpm --filter @hereby/components build
test -f packages/components/dist/dialog.js
test -f packages/components/dist/icons.jsExpected: all commands PASS. Do not commit generated dist files.
Task 3: Declare the system-admin dependency and replace direct imports
Files:
Modify:
tools/system-admin/package.jsonModify:
tools/system-admin/src/app.tsModify:
tools/system-admin/src/main.tsModify:
pnpm-lock.yaml[x] Add the workspace dependency:
"@hereby/components": "workspace:*"- [x] Replace dialog imports with the public subpath:
import { askConfirm, showPrompt } from "@hereby/components/dialog";
import { createDialogStore } from "@hereby/components/dialog";- [x] Replace the icon import with its public subpath:
import { getIcon } from "@hereby/components/icons";- [x] Update the lockfile with the repository's pinned pnpm version:
pnpm install --lockfile-only- [x] Confirm no direct component source imports remain in system-admin:
rg -n 'packages/components/src' tools/system-admin/src/app.ts tools/system-admin/src/main.tsExpected: no matches.
Task 4: Resolve public subpaths from source inside the monorepo build
Files:
- Modify:
tools/system-admin/vite.config.ts
The application code must know only public package names. The current monorepo build may use a Vite source alias because component dist is intentionally excluded from the Docker context.
- [x] Add exact aliases for the two public subpaths:
resolve: {
alias: {
"@hereby/components/dialog": resolve(
import.meta.dirname,
"../../packages/components/src/dialog.ts",
),
"@hereby/components/icons": resolve(
import.meta.dirname,
"../../packages/components/src/icons.ts",
),
},
},[x] Keep aliases exact; do not alias the entire
@hereby/componentspackage.[x] If adding a comment, explain in English that the alias is the monorepo source adapter and is removed when system-admin consumes a published package.
[x] Run the local build:
pnpm --filter @hereby/system-admin buildExpected: PASS.
- [x] Run the strict frontend typecheck so the public subpath declarations are validated:
pnpm --filter @hereby/system-admin exec tsc -p tsconfig.json --noEmitExpected: PASS.
Task 5: Make the selective Docker context match the declared dependency
Files:
Modify:
docker/system-admin/Dockerfile[x] Copy the components manifest before the filtered install in both Docker stages:
# builder stage
COPY packages/components/package.json packages/components/
# runner stage
COPY packages/components/package.json packages/components/The runner copy is required because the runner performs a second pnpm install; copying the manifest only in the builder stage is insufficient.
- [x] Run the exact filtered install inside the selective Docker context before proceeding:
docker buildx build \
--platform linux/arm64 \
--file docker/system-admin/Dockerfile \
--progress plain \
--target builder \
.The relevant Docker instruction must remain:
RUN pnpm install --frozen-lockfile --filter @hereby/core --filter @hereby/system-admin --ignore-scripts[x] Apply exactly one of these decision outcomes:
- Gate A — install succeeds: PASSED. The declared
@hereby/componentsworkspace dependency is traversed without installing its SDK-coupled dev dependencies. No redundant@hereby/componentsfilter was added. - Gate B — install fails because
@hereby/componentsresolves@hereby/sdk, API, or other absent workspace packages: stop this plan and report the exact pnpm error to Eron. Do not copy additional workspace manifests. The predetermined next architecture is a separately planned zero-dependency platform package (for example@hereby/ui-primitives) owning dialog, icons, template, CSS, and font assets. That package split requires its own approved implementation plan; it is not an Agent-local fallback.
- Gate A — install succeeds: PASSED. The declared
[x] Keep selective source copies for the two aliased, dependency-free entries:
COPY packages/components/src/dialog.ts packages/components/src/dialog.ts
COPY packages/components/src/icons.ts packages/components/src/icons.ts[x] Do not invoke the full
@hereby/components buildinside this Dockerfile. The Vite build bundles the two source entries, and a full build would introduce unrelated SDK-coupled entries.[x] Do not copy
packages/components/src/wholesale.[x] Update existing Dockerfile comments in English so they describe both public subpaths rather than only
icons.ts.
Task 6: Verify the fix and dependency boundary
Files: None unless verification reveals an in-scope defect
- [x] Verify changed-package builds and formatting:
pnpm --filter @hereby/components build
pnpm --filter @hereby/system-admin build
pnpm exec biome check packages/components/package.json packages/components/tsup.config.ts tools/system-admin/package.json tools/system-admin/src/app.ts tools/system-admin/src/main.ts tools/system-admin/vite.config.ts docker/system-admin/Dockerfile
git diff --check- [ ] Verify the repository-wide lint:
pnpm lint:checkResult: blocked by pre-existing unrelated diagnostics in apps/admin, apps/employee-app, and scripts/; the changed files pass the targeted Biome check above.
- [x] Verify the repository build:
pnpm buildResult: PASS — 22/22 tasks successful. Turbo reports an existing docs output configuration warning only.
- [x] Verify the same ARM64 Docker builder stage that failed in CI:
docker buildx build \
--platform linux/arm64 \
--file docker/system-admin/Dockerfile \
--progress plain \
--target builder \
.Expected: PASS through pnpm --filter @hereby/system-admin build.
- [x] Verify the complete image can be exported locally:
docker buildx build \
--platform linux/arm64 \
--file docker/system-admin/Dockerfile \
--progress plain \
--tag hereby-system-admin:ci-test \
--load \
.Expected: PASS. Do not push the image.
- [x] Review the final diff and confirm it contains only the expected file set; unrelated pre-existing working-tree changes remain untouched.
Task 7: Audit and record standalone-repository blockers
Files: No production files modified
This task is an architectural acceptance check, not permission to migrate these resources in the current change.
- [x] Confirm the shared HTML dependency remains:
tools/system-admin/index.html
-> ../../packages/components/html/ds-dialog.html
-> copied by docker/system-admin/DockerfileBefore extraction, the dialog template must be exported by a versioned UI package and the Vite HTML include plugin must resolve package resources. A relative filesystem include cannot cross repositories.
- [x] Confirm the design-system asset dependency chain remains:
tools/system-admin/src/styles/main.css
-> packages/static/styles/design-system.css
-> packages/static/styles/tokens.css
-> packages/static/fonts/**Before extraction, choose one owner for CSS and fonts. The recommended direction is a versioned platform UI/assets package with public CSS and font assets. Do not create an @hereby/static workspace dependency unless it is designed as a publishable package with explicit exports and asset ownership.
- [x] Confirm the backend seed dependency remains:
tools/system-admin/server/routes/provision.ts
-> packages/database/src/seeds/default-permissions.ts
-> copied into the runtime image by docker/system-admin/DockerfileBefore extraction, replace this runtime source import with a versioned management API/contract or an explicitly owned platform policy package. A control plane should not depend on another ERP repository's database source tree.
- [x] Confirm all current cross-boundary Docker copies are accounted for:
packages/components/html
packages/components/src/dialog.ts
packages/components/src/icons.ts
packages/static/styles
packages/static/fonts
packages/database/src/seeds/default-permissions.ts- [x] Record the audit outcome in the handoff/final report. System-admin is not yet repository-independent; its TypeScript shared-component imports now use public package boundaries and the current Docker build is repaired.
Acceptance Criteria
- [x] No relative TypeScript import from
tools/system-adminintopackages/components/srcremains. The two exact Vite monorepo aliases are intentional and are removed when consuming a published package. - [x] system-admin imports
@hereby/components/dialogand@hereby/components/iconsonly through public subpaths. - [x]
@hereby/componentsis declared intools/system-admin/package.json. - [x] Components build emits
dialogandiconsstandalone artifacts for future registry publication. - [x] The system-admin Docker build succeeds for
linux/arm64without copying the entire components package or adding SDK/API/database build dependencies. - [ ] Local package builds, repository lint, repository build, and
git diff --checkpass. Repository-wide lint remains blocked by pre-existing unrelated diagnostics; package builds, repository build, targeted lint, andgit diff --checkpass. - [x] No image is pushed, and no commit is created without Eron's approval.
- [x] The final report lists shared HTML, design-system CSS/fonts, and the database seed as unresolved extraction blockers.
Future Repository Extraction Follow-up
This is intentionally outside the current implementation and is required before repository extraction:
- Publish a versioned private
@hereby/componentspackage, or preferably split repository-independent UI code/assets into@hereby/ui-primitivesif Gate B is reached. - Export the dialog template through that package and teach the HTML include plugin to resolve package resources instead of repository-relative paths.
- Give
design-system.css,tokens.css, and their font files one publishable owner; consume them through a public package asset path. - Replace
workspace:*with a registry version such as^1.0.0. - Remove the Vite source aliases and every shared-source Docker
COPYinstruction while keeping application imports unchanged. - Replace system-admin's direct database seed coupling with versioned management APIs/contracts or an explicitly owned platform policy package before treating it as an ERP-wide control plane.