{"record":{"id":"b53a66d582f3d4ba","repo":"affaan-m/ECC","slug":"ecc-project-dir-must-be-a-child-path-within-workspace","errorCode":null,"errorMessage":"ECC_PROJECT_DIR must be a child path within /workspace.","messagePattern":"ECC_PROJECT_DIR must be a child path within /workspace\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"docker/plugin-setup/resolve-project-dir.js","lineNumber":20,"sourceCode":"\n'use strict';\n\nconst path = require('path');\n\nconst WORKSPACE_ROOT = '/workspace';\n\nfunction resolveProjectDir(candidate) {\n  if (\n    typeof candidate !== 'string'\n    || !path.posix.isAbsolute(candidate)\n    || /[\\0\\r\\n]/.test(candidate)\n  ) {\n    throw new Error('ECC_PROJECT_DIR must be an absolute path within /workspace.');\n  }\n\n  const resolved = path.posix.resolve(candidate);\n  if (resolved === WORKSPACE_ROOT || !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {\n    throw new Error('ECC_PROJECT_DIR must be a child path within /workspace.');\n  }\n  return resolved;\n}\n\nfunction main() {\n  try {\n    process.stdout.write(`${resolveProjectDir(process.argv[2])}\\n`);\n  } catch (error) {\n    process.stderr.write(`Error: ${error.message}\\n`);\n    process.exitCode = 2;\n  }\n}\n\nif (require.main === module) main();\n\nmodule.exports = { resolveProjectDir };\n","sourceCodeStart":2,"sourceCodeEnd":37,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/docker/plugin-setup/resolve-project-dir.js#L2-L37","documentation":"assertMemoryRootSafe validates the roots configuration object before any memory scope path is resolved. The roots argument must be a non-null, non-array object that also carries a trusted boundary policy map (under the VAULT_ROOT_BOUNDARIES key). If roots is not such an object, this error is thrown immediately. It is the coarsest-grained guard in the memory-vault path-safety chain.","triggerScenarios":"Calling the root accessor (via memory / saveMemory paths) with roots = null, undefined, a string, a number, or an Array instead of the expected plain object containing scope roots plus a boundaries map. Typically caused by passing a misloaded or absent config object.","commonSituations":"Configuration file failed to load so the default config value is undefined; a caller passed just the list of root paths (an array) instead of the full roots object; a refactor renamed the boundaries key so the validated object no longer matches; unit tests passing a partial stub.","solutions":["Pass the complete roots object (scope paths plus the trusted boundary policy map) instead of null/undefined/array.","Fix the config loader so the memory roots section is actually populated before calling the vault API.","Check that you are not filtering/mapping the roots object into an array upstream.","Add a startup validation that the roots config parses to the expected shape before invoking memory operations."],"exampleFix":"// before\nresolveMemoryRoot(null, 'project');\n\n// after\nconst roots = loadMemoryRootsConfig(); // { project: '/path', ..., [VAULT_ROOT_BOUNDARIES]: {...} }\nresolveMemoryRoot(roots, 'project');","handlingStrategy":"validation","validationCode":"function assertValidRoots(roots) {\n  if (!roots || typeof roots !== 'object' || Array.isArray(roots)) {\n    throw new TypeError('Memory roots config must be a plain object with scope paths and a trusted boundary policy.');\n  }\n}","typeGuard":"const isRootsConfig = (v) => !!v && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try {\n  const root = resolveMemoryRoot(roots, scope);\n} catch (err) {\n  if (err.message.includes('trusted boundary policy')) {\n    throw new Error(`Memory roots config invalid — check loadMemoryRootsConfig output: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Validate the roots config shape once at startup, before any memory operation.","Never pass arrays or partial stubs where the full roots object is expected.","Fail fast with a clear message if config loading yields null/undefined.","Type-check config-loading functions so they cannot silently return undefined."],"tags":["configuration","memory-vault","invalid-argument","path-safety"],"backgroundTag":"invalid-config-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}