lib/update-manifest.mjs
Rama main · réplica de lectura del repositorio original.
/**
* lib/update-manifest.mjs — the system layer the updater ships (#5554).
*
* `node update-system.mjs apply` checks out exactly these paths from upstream
* and nothing else. A PR that adds a file every install should receive (a
* script, a mode, a provider, a doc) must add that file's entry here. A
* trailing `/` makes an entry a directory prefix; every other entry names one
* file.
*
* The updater READS this file; it does not import it. update-system.mjs must
* stay self-loading (#1706): a pre-#1245 client re-execs the new updater with
* only update-system.mjs checked out, so a static import of this module would
* crash that upgrade before it starts. Instead the updater extracts the array
* below from source with extractArrayFromSource(), the same parser it uses on
* the TARGET release's manifest and on a rollback's backup ref. Keep the
* declaration in that shape:
*
* - one exported SYSTEM_PATHS array literal, closed by a bracket and a
* semicolon, holding quoted string literals only: no spread, no template
* literals, no computed entries, and no bracket-semicolon inside an entry;
* - comments anywhere (the parser blanks them first);
* - no imports in this file, so it loads wherever it is read.
*
* tests/update-manifest.test.mjs fails when the parsed list and the imported
* one disagree, which is what an unparseable entry would look like.
*
* The user layer stays in update-system.mjs (USER_PATHS, LOCAL_PATHS_FILE and
* MANIFEST_USER_PATH_OVERLAPS). Every entry here is checked against it at run
* time — canonical form before any checkout, then rejectUserLayerPaths() per
* entry in apply() and isSafeManifestPath() in rollback() — so an entry that
* names user data is refused rather than shipped. Those checks cover the user
* layer only. Some system files must stay out of this list for other reasons
* (.gitignore, SIGNATURES.md: see their notes below and in update-system.mjs),
* and the order matters for one entry (.gitattributes, first).
*/
export const SYSTEM_PATHS = [
// .gitattributes governs how every other path below is written to disk, and
// `apply` checks paths out one at a time in this order: if it landed later,
// everything before it would be written under the old core.autocrlf setting
// on an existing install, silently (once text=auto is live, git status stays
// clean and only a second update would repair it).
'.gitattributes',
'dead-boards.mjs',
'provider-id-cache.mjs',
'modes/README.md',
'modes/_shared.md',
'modes/_writing.md',
'modes/_profile.template.md',
'modes/_custom.template.md',
'modes/_brief.template.md',
'voice-dna.template.md',
'modes/oferta.md',
'modes/oferta-signals/',
'modes/master-profile.md',
'modes/pdf.md',
'modes/ats.md',
'modes/text.md',
'modes/pdf/',
'modes/cover.md',
'modes/email.md',
'modes/add.md',
'modes/expand.md',
'modes/scan.md',
'modes/discover.md',
'modes/batch.md',
'modes/apply.md',
'modes/auto-pipeline.md',
'modes/contacto.md',
'modes/deep.md',
'modes/ofertas.md',
'modes/pipeline.md',
'modes/triage.md',
'modes/project.md',
'modes/tracker.md',
'modes/training.md',
'modes/interview.md',
'modes/interview-redflag.md',
'modes/latex.md',
'modes/latex-tex.md',
'modes/followup.md',
'modes/offer-prep.md',
'modes/interview-prep.md',
'modes/interview/',
'interview-prep/sessions/.gitkeep',
'interview-prep/sessions/README.md',
'modes/patterns.md',
'modes/calibrate.md',
'modes/titles.md',
'modes/upskill.md',
'modes/intake.md',
'modes/retro.md',
'documents/.gitkeep',
// Empty scaffolds are system-owned exceptions inside otherwise user-owned
// directories. Ship only these exact files; the data they sit beside stays
// in USER_PATHS and is never checked out by the updater.
'data/.gitkeep',
'data/offers/.gitkeep',
'data/parser-output/.gitkeep',
'jds/.gitkeep',
'output/.gitkeep',
'reports/.gitkeep',
'documents/README.md',
'modes/update.md',
'modes/agent-inbox.md',
'modes/reply-watch.md',
'modes/outcome.md',
'modes/ar/',
'modes/da/',
'modes/de/',
'modes/de/interview/',
'modes/fr/',
'modes/fr/interview/',
'modes/hi/',
'modes/es/',
'modes/es/interview/',
'modes/id/',
'modes/id/interview/',
'modes/it/',
'modes/it/interview/',
'modes/ja/',
'modes/ja/interview/',
'modes/ko/',
'modes/ko/interview/',
'modes/nb/',
'modes/nl/',
'modes/pl/',
'modes/pl/interview/',
'modes/pt/',
'modes/pt/interview/',
'modes/ru/',
'modes/ru/interview/',
'modes/sg/',
'modes/tr/',
'modes/ua/',
'modes/ua/interview/',
'modes/vi/',
'modes/heuristics/',
'modes/regional/',
'modes/zh/',
'modes/zh/interview/',
'modes/zh-TW/',
'CLAUDE.md',
'CODEX.md',
'OPENCODE.md',
'AGENTS.md',
'GEMINI.md',
'KIMI.md',
'build-dashboard.mjs',
'clean-markers.mjs',
'cv-experience-order.mjs',
'generate-pdf.mjs',
'hired-share.mjs',
'community-asks.mjs',
'hired-wall-build.mjs',
'HIRED.md',
'theme-style.mjs',
'generate-latex.mjs',
'install-latex-engine.mjs',
'extract-latex-content.mjs',
'patch-latex-content.mjs',
'lib/ascii-fold.mjs',
'lib/cli-flags.mjs',
'lib/cli-launch.mjs',
'lib/failure-excerpt.mjs',
'lib/endpoint-headers.mjs',
'lib/entry-response-cap.mjs',
'lib/entry-timeout.mjs',
// Imported by scan.mjs: the one reading of the providers partial-read signal.
'lib/board-truncation.mjs',
'lib/gemini-node-floor.mjs',
'lib/jd-page-fetch.mjs',
'lib/jd-parse.mjs',
'lib/inbox-rank.mjs',
'lib/node-floor.mjs',
'lib/chromium-launch-error.mjs',
// Shared by every browser-backed command and imported by doctor.mjs.
'lib/browser-launch.mjs',
'lib/local-today.mjs',
'lib/jd-archive-section.mjs',
'lib/machine-summary-section.mjs',
'lib/parse-date.mjs',
'lib/personalization.mjs',
'lib/reply-proposals.mjs',
'lib/placeholder-cell.mjs',
'lib/tracker-addition.mjs',
'lib/scan-summary-marker.mjs',
'lib/scan-history-columns.mjs',
'lib/posting-close.mjs',
'lib/pipeline-sections.mjs',
'lib/small-board.mjs',
'lib/admission-gate.mjs',
'lib/tsv-formula-escape.mjs',
'lib/is-main-module.mjs',
// This file. update-system.mjs reads it at run time (see the header), so it
// ships like any other system file.
'lib/update-manifest.mjs',
'lib/mjs-files.mjs',
'lib/syntax-check.mjs',
'lib/syntax-check-child.mjs',
'lib/walk-tree.mjs',
'lib/dir-lock.mjs',
// The shared PDF painter. generate-pdf.mjs imports it when a batch opts in.
'lib/pdf-server.mjs',
// Ships alongside test-all.mjs, which imports it.
'lib/test-queue.mjs',
'lib/run-bounded.mjs',
'lib/status-hooks.mjs',
'lib/scratch-dirs.mjs',
'lib/story-bank.mjs',
'lib/outcome-dir.mjs',
'lib/outcome-types.mjs',
'lib/latex-escape.mjs',
'lib/cv-payload-schema.mjs',
'lib/cv-payload-facts.mjs',
'lib/career-profile.mjs',
'lib/profile-cv-selection.mjs',
'lib/cv-markdown.mjs',
'lib/page-format.mjs',
'lib/template-manifest.mjs',
'lib/agent-cli.mjs',
'lib/agent-launch.mjs',
'remember-cli.mjs',
'lib/yaml-document.mjs',
'scan-hn.mjs',
'scripts/check-syntax.mjs',
'scripts/claude-plugin-session-start.mjs',
'scripts/export-ats-text.mjs',
'scripts/followup-sweep.sh',
'scripts/ui-server.mjs',
'story-provenance-check.mjs',
'lib/latex-content.mjs',
'lib/context-budget.mjs',
// Ship alongside linkedin-watch.mjs and the scan schedule.
'lib/linkedin-mail-drop.mjs',
'lib/linkedin-auto.mjs',
// Ships alongside doctor.mjs, which imports it: an upgraded install whose
// updater had no entry for it would check out a doctor that cannot load.
'lib/title-filter-conflicts.mjs',
// Ships alongside liveness-api.mjs, which imports it.
'lib/linkedin-url.mjs',
// Ships alongside linkedin-import.mjs, which imports it.
'lib/linkedin-import.mjs',
// Retired 2026-09-05: the suite moved to tests/context-budget.test.mjs. The
// entry stays so staleSystemFiles() prunes the orphan on an upgraded install;
// drop it once a release has shipped past that move.
'lib/context-budget.test.mjs',
'lib/golden-budget-analysis.mjs',
'lib/privacy-guard.mjs',
'lib/llm-outbound.mjs',
'lib/pii-interceptor.mjs',
'img-to-pdf.mjs',
'apply-scope.mjs',
'archive-posting.mjs',
'jd-capture.mjs',
'application-answers.mjs',
'generate-cover-letter.mjs',
'merge-tracker.mjs',
'persist-evaluation-report.mjs',
'url-key.mjs',
'sync-pdf-flags.mjs',
'tracker-links.mjs',
'tracker.mjs',
'find.mjs',
'verify-pipeline.mjs',
'verify-tracker-url-match.mjs',
'discard-analytics.mjs',
'sync-batch-input.mjs',
'reconcile-pipeline.mjs',
'dedup-tracker.mjs',
'precheck-duplicate.mjs',
'add-entry.mjs',
'github-evidence.mjs',
'evidence-freshness.mjs',
'role-matcher.mjs',
'tracker-utils.mjs',
'tracker-parse.mjs',
'tracker-aliases.json',
'session-activity.mjs',
'set-status.mjs',
'onboard-write.mjs',
'mark-pdf-ready.mjs',
'normalize-statuses.mjs',
'fix-report-links.mjs',
'cv-sync-check.mjs',
'i18n-drift.mjs',
'verify-cv-facts.mjs',
'cv-voice.mjs',
'verify-cv-structure.mjs',
'verify-ats.mjs',
'ats-payload.mjs',
'update-system.mjs',
'path-resolver.mjs',
'ats-vendor.mjs',
'history-ats-seeds.mjs',
'branded-successfactors.mjs',
'reserve-report-num.mjs',
'scan.mjs',
'migrate-scan-runs.mjs',
'pipeline-lock.mjs',
'portal-health-lock.mjs',
'classify-tier.mjs',
'scan-ats-full.mjs',
'scan-interamt.mjs',
'scan-dayforce.mjs',
'scan-seek.mjs',
'scan-local-lib.mjs',
'company-funded.mjs',
'match-star.mjs',
'jd-skill-gap.mjs',
'role-clarity.mjs',
'career-profile.mjs',
'cv-title-check.mjs',
'filter-audit.mjs',
'prepare-application.mjs',
'application-artifacts.mjs',
'evaluation-cache.mjs',
'batch-evaluate-gemini.mjs',
'providers/',
'data-static/',
'seeds/',
'tests/',
// ── Retired paths ─────────────────────────────────────────────────────────
// These files no longer exist upstream: #3765 moved four root suites into
// tests/, and #4758 moved tracker-columns-tests.mjs once it no longer
// needed its own timeout. They
// stay in the manifest anyway, because SYSTEM_PATHS is what `apply()` prunes
// AGAINST — `staleSystemFiles` (see pathMatchesManifest) only deletes a local
// file that is gone from the remote tree AND matches an entry here. Drop the
// entry and an upgrading install keeps its copy of the old root file forever,
// where tests/root-tests-registration.test.mjs then reports it as an
// unregistered suite and turns `node test-all.mjs` red on a healthy install.
//
// Probe on this list vs. the pre-#3765 one, with a local tree holding the
// four and a remote tree without them: without these entries the prune
// returns nothing at all; with them it returns all four.
//
// NB: keep square brackets out of every comment in this array. Several
// assertions in test-all.mjs extract the manifest with a NON-GREEDY regex
// that ends at the first closing bracket, so one inside a comment truncates
// the parsed list and every entry below it reads as missing. That is not
// hypothetical: the first draft of this block wrote the probe result as an
// empty-array literal and turned the check-table-freshness assertion red.
//
// They are therefore expected to be ABSENT from the working tree, which is
// why updater-migration-tests.mjs lists them in ALLOWED_MISSING_ENTRIES.
// Safe to delete once no supported install can still be carrying them.
'agent-inbox-tests.mjs',
'followup-seed-tests.mjs',
'paste-reply-tests.mjs',
'set-status-tests.mjs',
'tracker-columns-tests.mjs',
// ── end retired paths ─────────────────────────────────────────────────────
'user-agent.mjs',
'doctor.mjs',
'jsonc-parse.mjs',
'check-liveness.mjs',
'liveness-core.mjs',
'liveness-api.mjs',
'liveness-browser.mjs',
'robots-check.mjs',
'browser-extract.mjs',
'fetch-jd.mjs',
'himalayas-posting.mjs',
'jd-years.mjs',
'analyze-patterns.mjs',
'keyword-match.mjs',
'calibrate.mjs',
'upskill.mjs',
'skill-extract.mjs',
'skip-blockers.mjs',
'intake.mjs',
'learn.mjs',
'stats.mjs',
'funnel-stages.mjs',
'detect-reposts.mjs',
'rank-pipeline.mjs',
'discover-ats.mjs',
'discover-new-companies.mjs',
'check-table-freshness.mjs',
'check-jd-archive.mjs',
'fingerprint-core.mjs',
'listing-fingerprint.mjs',
'process-quality.mjs',
'company-history.mjs',
'rejection-latency.mjs',
'closing-soon.mjs',
'close-stale.mjs',
'import-tracker.mjs',
'reeval-candidates.mjs',
'salary-gap.mjs',
'negotiation-roi.mjs',
'funnel-velocity.mjs',
'assessment-log.mjs',
'contacts.mjs',
'contact-lookup.mjs',
'linkedin-join.mjs',
'linkedin-import.mjs',
'sponsor-check.mjs',
'weekly-digest.mjs',
'linkedin-watch.mjs',
'tracker-sync-check.mjs',
'followup-cadence.mjs',
'invite-match.mjs',
'ics-interview.mjs',
'panelist-roles.mjs',
'coverage-check.mjs',
'agent-inbox.mjs',
'followup-seed.mjs',
'debrief-sync.mjs',
'profile-language.mjs',
'title-keywords.mjs',
'gemini-eval.mjs',
'ollama-eval.mjs',
'openai-eval.mjs',
'openai-tailor.mjs',
'eval-golden.mjs',
'evals/',
'openrouter-runner.mjs',
'privacy-status.mjs',
'jd-similarity.mjs',
'test-all.mjs',
// Retired 2026-10-04: the suite moved to tests/tracker-writer-lock.test.mjs
// (#4759). The entry stays so staleSystemFiles() prunes the orphan on an
// upgraded install; drop it once a release has shipped past that move.
'tracker-writer-lock-tests.mjs',
'validate-portals.mjs',
'validate-profile.mjs',
'verify-portals.mjs',
'audit-portals.mjs',
'fix-slugs.mjs',
'updater-migration-tests.mjs',
'validate-system-paths-coverage.mjs',
'validate-untrusted-content-coverage.mjs',
'reply-matcher.mjs',
'reply-watch.mjs',
'paste-reply.mjs',
'contact-extract.mjs',
// Retired 2026-10-04: the suite moved to tests/contact-extract.test.mjs. The
// entry stays so staleSystemFiles() prunes the orphan on an upgraded install;
// drop it once a release has shipped past that move.
'contact-extract-tests.mjs',
'outcome.mjs',
'batch/batch-prompt.md',
'batch/batch-prompt-match.md',
'batch/batch-runner.sh',
'batch/company-scope.mjs',
'batch/jd-rescue.mjs',
'batch/aggregate-tokens.mjs',
'batch/README.md',
'utils/privacy-filter.mjs',
'utils/token-tracker.mjs',
'batch-tailor.mjs',
'dashboard/',
'templates/',
'config/cv-facts.example.json',
'fonts/',
'examples/',
'config/profile.example.yml',
'config/privacy.example.yml',
'config/local-paths.example.txt',
'.env.example',
'.editorconfig',
'.agents/',
'.claude/skills/',
'.opencode/skills/',
'.opencode/commands/',
'.claude-plugin/',
'.codex-plugin/',
'claude-directory/',
'openai-directory/',
'.cursor-plugin/',
'cursor-directory/',
'antigravity-directory/',
'hooks/hooks.json',
'.qwen/',
'.antigravitycli/skills/',
'.grok/skills/',
'.kimi/skills/',
'.omp/AGENTS.md',
'docs/',
'writing-samples/README.md',
'VERSION',
'DATA_CONTRACT.md',
'MANIFESTO.md',
'manifesto.mjs',
// SIGNATURES.md cannot join SYSTEM_PATHS: unlike every other system file it
// is a pure append-only ledger of who signed the manifesto, and it churns
// far faster than the code it would ship beside (49 commits in the 30 days
// before this was written). Nothing on an install reads it — manifesto.mjs
// parses MANIFESTO.md and never opens it — so shipping it buys an install
// nothing, while listing it here puts it in the pathspec check() diffs via
// systemTreeDiffers, which turns every new signature into a
// system-files-changed report on every install in the world that no apply
// can clear for long (#4062). That is one cause of the #3149 class of
// permanent update-available, beside the SHA-vs-content bug of #2630, the
// ignore-rule route of #2756, and the symlinked skill entrypoints that a
// core.symlinks=false checkout materialises into regular files. Do not fix
// that last one the way this entry was fixed: the entrypoints must stay in
// SYSTEM_PATHS and be excluded from the drift comparison instead, because
// ensureSkillEntrypoints only refreshes an entry that still holds the
// pointer, so an entrypoint dropped from the manifest silently freezes.
// The SIGNATURES.md repo-only coverage is declared in
// validate-system-paths-coverage.mjs, and the behaviour is pinned by
// tests/updater-signature-ledger-drift.test.mjs.
//
// Keep this comment free of straight quotes: updater-migration-tests.mjs
// parses this array with a comment-blind regex, so an apostrophe here
// becomes a phantom manifest entry.
'CONTRIBUTING.md',
'MAINTAINERS.md',
'ARCHITECTURE.md',
'README.md',
'README.ar.md',
'README.cn.md',
'README.da.md',
'README.de.md',
'README.es.md',
'README.fr.md',
'README.hi.md',
'README.ja.md',
'README.ko-KR.md',
'README.pl.md',
'README.pt-BR.md',
'README.ru.md',
'README.ta.md',
'README.ua.md',
'README.zh-TW.md',
'README.tr.md',
'README.vi.md',
'README.id.md',
'CHANGELOG.md',
'CODE_OF_CONDUCT.md',
'CONTRIBUTORS.md',
'.all-contributorsrc',
'GOVERNANCE.md',
'LEGAL_DISCLAIMER.md',
'SECURITY.md',
'SUPPORT.md',
'TRADEMARK.md',
'LICENSE',
'CITATION.cff',
'funding.json',
'.well-known/',
'.editorconfig',
'.github/',
'package.json',
'build-cv-latex.mjs',
'build-cv-html.mjs',
'cv-sections-core.mjs',
'cv-templates.mjs',
'playwright.cv.config.mjs',
'scaffolder/',
'Dockerfile',
'docker-compose.yml',
'.dockerignore',
'.codexignore',
'cops',
'DOCKER.md',
'plugins/',
'plugins.mjs',
'plugins-registry/',
'plugin-install.mjs',
'plugin-audit.mjs',
'validate-plugin-registry.mjs',
'config/plugins.example.yml',
'opencode.example.json',
'seed-fixture.mjs',
'test-fixtures/',
'upgrade-tests.mjs',
];
// Frozen after the literal, not with `Object.freeze([...])` around it, so the
// declaration keeps the `const NAME = [ ... ];` shape the parser matches.
Object.freeze(SYSTEM_PATHS);