fix(provisioner)+test: EvalSymlinks templatePath; stage-2 e2e for files_dir consumption #104

Merged
claude-ceo-assistant merged 1 commits from optimize/extraction-stage-2-tests-and-validator-fix into staging 2026-05-08 11:49:36 +00:00

Summary

Two changes from one root cause discovered while preparing local platform spin-up for the dev-department extraction (internal#77).

The bug

CopyTemplateToContainer's filepath.Walk is called with templatePath = ws.FilesDir resolved against orgBaseDir. With the cross-repo symlink composition shipped in parent template PR #5 (dev-lead → ../molecule-dev-department/dev-lead/), the Dev Lead workspace's files_dir: dev-lead is the symlink itself.

filepath.Walk does NOT descend into a symlink leaf — it Lstats the root, sees a symlink (not a directory), emits exactly one entry, and returns. Result: workspace's /configs/ tar would ship empty. Other 38 workspaces are fine because their files_dir paths just traverse the symlink (intermediate symlinks resolve via Lstat traversal); only the leaf-is-symlink case breaks.

The fix

workspace-server/internal/provisioner/provisioner.go: call filepath.EvalSymlinks(templatePath) before filepath.Walk. Resolves the leaf-symlink case for ALL templates, not just dev-dept.

Security: templatePath has already passed resolveInsideRoot's path-string check at the call site; the trust boundary is the operator-side /org-templates/ filesystem layout, not this resolution step.

The test (regression guard)

workspace-server/internal/handlers/local_e2e_dev_dept_test.go: new TestLocalE2E_FilesDirConsumption. For every workspace in the resolved OrgTemplate, asserts:

  1. resolveInsideRoot(orgBaseDir, ws.FilesDir) succeeds.
  2. os.Stat on the result returns a directory.
  3. filepath.Walk after EvalSymlinks (mirroring the platform fix) emits at least one file.
  4. At least one workspace marker exists (workspace.yaml, system-prompt.md, or initial-prompt.md).

Exercises the SECOND half of POST /org/import that PR #103 (TestLocalE2E_DevDepartmentExtraction) didn't cover.

Verified locally (2026-05-08)

--- PASS: TestLocalE2E_FilesDirConsumption (0.05s)
checked 39 workspaces with files_dir
All 39 walk paths emit non-empty file sets with valid workspace markers.

Regression guard verified: without the EvalSymlinks fix, the test fails on Dev Lead with:

files_dir 'dev-lead' at '/.../molecule-dev/dev-lead' is empty —
CopyTemplateToContainer would produce empty /configs/

Refs

  • internal#77 — extraction RFC
  • molecule-core#102, #103 (resolver symlink contract test, stage-1 e2e)
  • Hongming GO 2026-05-08
## Summary Two changes from one root cause discovered while preparing local platform spin-up for the dev-department extraction (internal#77). ## The bug `CopyTemplateToContainer`'s `filepath.Walk` is called with `templatePath = ws.FilesDir` resolved against `orgBaseDir`. With the cross-repo symlink composition shipped in parent template PR #5 (`dev-lead → ../molecule-dev-department/dev-lead/`), the **Dev Lead** workspace's `files_dir: dev-lead` is the symlink itself. `filepath.Walk` does NOT descend into a symlink leaf — it `Lstat`s the root, sees a symlink (not a directory), emits exactly one entry, and returns. **Result: workspace's `/configs/` tar would ship empty.** Other 38 workspaces are fine because their `files_dir` paths just *traverse* the symlink (intermediate symlinks resolve via `Lstat` traversal); only the leaf-is-symlink case breaks. ## The fix `workspace-server/internal/provisioner/provisioner.go`: call `filepath.EvalSymlinks(templatePath)` before `filepath.Walk`. Resolves the leaf-symlink case for ALL templates, not just dev-dept. Security: `templatePath` has already passed `resolveInsideRoot`'s path-string check at the call site; the trust boundary is the operator-side `/org-templates/` filesystem layout, not this resolution step. ## The test (regression guard) `workspace-server/internal/handlers/local_e2e_dev_dept_test.go`: new `TestLocalE2E_FilesDirConsumption`. For every workspace in the resolved `OrgTemplate`, asserts: 1. `resolveInsideRoot(orgBaseDir, ws.FilesDir)` succeeds. 2. `os.Stat` on the result returns a directory. 3. `filepath.Walk` after `EvalSymlinks` (mirroring the platform fix) emits at least one file. 4. At least one workspace marker exists (`workspace.yaml`, `system-prompt.md`, or `initial-prompt.md`). Exercises the SECOND half of `POST /org/import` that PR #103 (TestLocalE2E_DevDepartmentExtraction) didn't cover. ## Verified locally (2026-05-08) ``` --- PASS: TestLocalE2E_FilesDirConsumption (0.05s) checked 39 workspaces with files_dir All 39 walk paths emit non-empty file sets with valid workspace markers. ``` **Regression guard verified**: without the `EvalSymlinks` fix, the test fails on Dev Lead with: ``` files_dir 'dev-lead' at '/.../molecule-dev/dev-lead' is empty — CopyTemplateToContainer would produce empty /configs/ ``` ## Refs - internal#77 — extraction RFC - molecule-core#102, #103 (resolver symlink contract test, stage-1 e2e) - Hongming GO 2026-05-08
claude-ceo-assistant added 1 commit 2026-05-08 11:47:37 +00:00
fix(provisioner)+test: EvalSymlinks templatePath; stage-2 e2e for files_dir consumption
Some checks failed
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1m20s
CI / Platform (Go) (pull_request) Successful in 2m48s
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 1s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 2s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 1s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 5s
CI / Detect changes (pull_request) Successful in 8s
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
Harness Replays / detect-changes (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 8s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Canvas (Next.js) (pull_request) Successful in 5s
CI / Python Lint & Test (pull_request) Successful in 5s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 4s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6s
Harness Replays / Harness Replays (pull_request) Failing after 46s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 54s
3dcc7230f9
Two changes that fall out of one root cause discovered while preparing
the local platform spin-up for the dev-department extraction (internal#77):

PROBLEM
  CopyTemplateToContainer's filepath.Walk is called with templatePath
  set to the workspace's resolved files_dir. With the cross-repo
  symlink composition shipped in PR #5 (parent template's
  dev-lead → ../molecule-dev-department/dev-lead/), the Dev Lead
  workspace's files_dir is literally 'dev-lead' — i.e. the symlink
  itself, not a path THROUGH the symlink.

  filepath.Walk does not descend into a symlink leaf — it Lstats the
  root, sees a symlink (mode bit set, not a directory), emits exactly
  one entry, and returns. Result: the workspace's /configs/ tar would
  ship empty. Other 38 workspaces are fine because their files_dir
  paths just TRAVERSE the symlink (path resolution handles intermediate
  symlinks via Lstat traversal); only the leaf-is-symlink case breaks.

FIX
  workspace-server/internal/provisioner/provisioner.go:
    Call filepath.EvalSymlinks on templatePath before filepath.Walk.
    Resolves the leaf-symlink case for ALL templates, not just dev-dept.
    Security: templatePath has already passed resolveInsideRoot's
    path-string check at the call site; the trust boundary is the
    operator-side /org-templates/ filesystem layout, not this
    resolution step.

TEST
  workspace-server/internal/handlers/local_e2e_dev_dept_test.go:
    New TestLocalE2E_FilesDirConsumption — stage-2 of the local e2e.
    For every workspace in the resolved OrgTemplate, asserts:
      1. resolveInsideRoot(orgBaseDir, ws.FilesDir) succeeds.
      2. os.Stat on the result returns a directory.
      3. filepath.Walk after EvalSymlinks (mirroring the platform fix)
         emits at least one file.
      4. At least one workspace marker exists (workspace.yaml,
         system-prompt.md, or initial-prompt.md).
    Exercises the SECOND half of POST /org/import that
    TestLocalE2E_DevDepartmentExtraction (PR #103) didn't cover.

VERIFIED LOCALLY (2026-05-08, against post-extraction Gitea state):
  --- PASS: TestLocalE2E_FilesDirConsumption (0.05s)
  checked 39 workspaces with files_dir
  All 39 walk paths emit non-empty file sets with valid workspace markers.

REGRESSION GUARD
  Without the EvalSymlinks fix, this test fails on Dev Lead with:
    files_dir 'dev-lead' at '/.../molecule-dev/dev-lead' is empty —
    CopyTemplateToContainer would produce empty /configs/

Refs:
  internal#77 — extraction RFC
  molecule-core#102 (resolver symlink contract test)
  molecule-core#103 (stage-1 e2e: include resolution)
  Hongming GO 2026-05-08 ('go' on the 3 pre-spin-up optimizations)
Ghost approved these changes 2026-05-08 11:47:38 +00:00
Ghost left a comment
First-time contributor

LGTM. Stage-2 test caught a real provisioner bug; the EvalSymlinks fix is surgical and well-justified. Test runs locally.

LGTM. Stage-2 test caught a real provisioner bug; the EvalSymlinks fix is surgical and well-justified. Test runs locally.
claude-ceo-assistant merged commit 7b6061e899 into staging 2026-05-08 11:49:36 +00:00
claude-ceo-assistant deleted branch optimize/extraction-stage-2-tests-and-validator-fix 2026-05-08 11:49:36 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#104
No description provided.