canopycms 0.0.65 → 0.0.66-int.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +1 -3
  2. package/dist/ai/generate.d.ts +13 -0
  3. package/dist/ai/generate.js +9 -3
  4. package/dist/ai/to-plain-text.js +4 -0
  5. package/dist/ai/types.d.ts +20 -1
  6. package/dist/api/admin-branch-health.js +13 -12
  7. package/dist/api/branch.js +2 -2
  8. package/dist/branch-health.js +1 -1
  9. package/dist/branch-metadata-file.d.ts +58 -0
  10. package/dist/branch-metadata-file.js +65 -0
  11. package/dist/branch-metadata.d.ts +2 -26
  12. package/dist/branch-metadata.js +6 -33
  13. package/dist/branch-registry.js +4 -2
  14. package/dist/build/generate-ai-content.js +103 -0
  15. package/dist/cli/cli.js +432 -317
  16. package/dist/cli/generate-ai-content.js +264 -160
  17. package/dist/content-id-index.js +22 -0
  18. package/dist/content-listing.js +3 -0
  19. package/dist/content-reader.d.ts +25 -1
  20. package/dist/content-reader.js +15 -0
  21. package/dist/content-store.d.ts +50 -1
  22. package/dist/content-store.js +102 -4
  23. package/dist/context.d.ts +36 -4
  24. package/dist/context.js +15 -4
  25. package/dist/editor/admin/SystemHealthPanel.js +2 -2
  26. package/dist/entry-schema.d.ts +22 -0
  27. package/dist/git-manager.d.ts +9 -8
  28. package/dist/git-manager.js +35 -9
  29. package/dist/github-service.d.ts +1 -1
  30. package/dist/github-service.js +1 -1
  31. package/dist/paths/branch-name.d.ts +1 -1
  32. package/dist/paths/branch-name.js +1 -1
  33. package/dist/schema/index.d.ts +1 -1
  34. package/dist/schema/index.js +1 -1
  35. package/dist/schema/meta-loader.js +1 -1
  36. package/dist/services.d.ts +22 -0
  37. package/dist/types.d.ts +1 -1
  38. package/dist/url-exclusivity-fixtures.d.ts +76 -0
  39. package/dist/url-exclusivity-fixtures.js +119 -0
  40. package/dist/url-path-resolver.js +8 -4
  41. package/dist/utils/content-write-lock.d.ts +2 -1
  42. package/dist/utils/content-write-lock.js +2 -1
  43. package/dist/utils/error.d.ts +17 -0
  44. package/dist/utils/error.js +17 -0
  45. package/dist/utils/git.d.ts +1 -1
  46. package/dist/utils/git.js +1 -1
  47. package/dist/utils/occ-json-write.js +1 -1
  48. package/dist/worker/cms-worker.d.ts +22 -288
  49. package/dist/worker/cms-worker.js +118 -1995
  50. package/dist/worker/git-sync.d.ts +166 -0
  51. package/dist/worker/git-sync.js +554 -0
  52. package/dist/worker/history-rewrite.d.ts +129 -0
  53. package/dist/worker/history-rewrite.js +216 -0
  54. package/dist/worker/rebase.d.ts +171 -0
  55. package/dist/worker/rebase.js +859 -0
  56. package/dist/worker/task-runner.d.ts +93 -0
  57. package/dist/worker/task-runner.js +529 -0
  58. package/dist/worker/worker-context.d.ts +133 -0
  59. package/dist/worker/worker-context.js +1 -0
  60. package/package.json +1 -1
@@ -27,7 +27,7 @@ export function sanitizeBranchName(branchName) {
27
27
  * operating-mode/deployment-name.ts's `resolveDeploymentName`). Exported from
28
28
  * this dependency-free module -- not constructed ad hoc at each call site --
29
29
  * so every consumer that needs to recognize the settings-branch namespace
30
- * agrees on the exact string: worker/cms-worker.ts's `pushSettingsBranches`
30
+ * agrees on the exact string: worker/git-sync.ts's `pushSettingsBranches`
31
31
  * (never push a `canopycms-settings-*` branch this deployment doesn't own),
32
32
  * and api/branch.ts's `createBranchHandler` (reject a user-requested branch
33
33
  * whose SANITIZED name falls in this namespace).
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @example
9
9
  * ```ts
10
- * import { resolveSchema } from 'canopycms/schema'
10
+ * import { resolveSchema } from 'canopycms/server'
11
11
  *
12
12
  * const { schema, sources } = await resolveSchema(contentRoot, entrySchemaRegistry)
13
13
  * ```
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @example
9
9
  * ```ts
10
- * import { resolveSchema } from 'canopycms/schema'
10
+ * import { resolveSchema } from 'canopycms/server'
11
11
  *
12
12
  * const { schema, sources } = await resolveSchema(contentRoot, entrySchemaRegistry)
13
13
  * ```
@@ -4,7 +4,7 @@ import { z } from 'zod';
4
4
  import chokidar from 'chokidar';
5
5
  import { getErrorMessage } from '../utils/error.js';
6
6
  // canopyLogError/canopyLogWarn, not console.*: this module sits in the
7
- // worker's runtime import closure (worker/cms-worker.ts ->
7
+ // worker's runtime import closure (worker/git-sync.ts and worker/rebase.ts ->
8
8
  // content-index-generation.ts -> branch-schema-cache.ts -> schema/resolver.ts
9
9
  // -> here), so a bare console line would land in worker.log without the
10
10
  // ISO-8601 prefix the CloudWatch agent's multi_line_start_pattern needs (see
@@ -1,3 +1,25 @@
1
+ /**
2
+ * `CanopyServices` — the long-lived, per-deployment service container.
3
+ *
4
+ * Built once by `createCanopyServices` and threaded through `ApiContext.services`
5
+ * to every API handler. Holds config, the schema registry and cache, the ACL
6
+ * checkers, the git-manager factory, the branch registry, the GitHub service, and
7
+ * four branch-workflow operations (`commitFiles`, `submitBranch`,
8
+ * `commitToSettingsBranch`, `getSettingsBranchRoot`).
9
+ *
10
+ * SCOPE WARNING: this is a service locator with 15 members, reached by every one of
11
+ * the 26 api modules, so adding a field is free and therefore constant — and no handler's
12
+ * signature reveals what it actually touches. Prefer giving a new capability its
13
+ * own narrow interface over a 23rd field here.
14
+ *
15
+ * Not to be confused with `context.ts`, which is the per-REQUEST facade built on
16
+ * top of this.
17
+ *
18
+ * `createTestCanopyServices` is test-only despite living in a production module;
19
+ * it reaches the published surface through a wildcard re-export.
20
+ *
21
+ * Module map: ./AGENTS.md.
22
+ */
1
23
  import type { CanopyConfig } from './config/index.js';
2
24
  import type { EntrySchemaRegistry } from './schema/types.js';
3
25
  import type { BranchContext } from './types.js';
package/dist/types.d.ts CHANGED
@@ -140,7 +140,7 @@ export interface WorkerStatusReport {
140
140
  }[];
141
141
  /**
142
142
  * Outcome of reconciling remote.git's `refs/heads/*` against GitHub's
143
- * fetched tips (worker/cms-worker.ts's `reconcileTrackedBranches`) --
143
+ * fetched tips (worker/git-sync.ts's `reconcileTrackedBranches`) --
144
144
  * the non-destructive replacement for the old fetch refspec that used
145
145
  * to write GitHub's refs directly into `refs/heads/*`.
146
146
  */
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The one-URL-per-entry invariant, as a reusable probe + report.
3
+ *
4
+ * `listEntries` assigns every entry exactly one `urlPath` and documents the round trip through
5
+ * `readByUrlPath` as safe. The reverse direction has repeatedly been the looser of the two: it
6
+ * has answered at URLs no forward surface emits, and those extra URLs were found one at a time,
7
+ * a release apart, by adopters (see
8
+ * `.claude/future-tasks/resolved/url-resolver-index-entry-extra-url.md`, then
9
+ * `resolved/readbyurlpath-entry-type-candidate-phantom-url.md`). This module exists so the whole
10
+ * invariant is asserted at once instead: enumerate, then probe every ADJACENT URL the resolver
11
+ * would try and require it to be a miss.
12
+ *
13
+ * Deliberately free of `vitest` -- this is a plain `src` module in the shape of
14
+ * `operating-mode/deployment-name-fixtures.ts`, so more than one test file can import it without
15
+ * dragging that file's `vi.mock` calls and `describe` blocks along. It therefore ASSERTS NOTHING:
16
+ * it returns a report and the caller does the expecting, which also puts the offending URLs in
17
+ * the assertion message rather than behind a boolean.
18
+ *
19
+ * Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
20
+ * with a type their collection declares -- i.e. exactly the set `listEntries` can see. A legacy
21
+ * untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
22
+ * see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
23
+ */
24
+ import { type RootCollectionConfig } from './config/index.js';
25
+ import type { ListEntriesItem } from './content-listing.js';
26
+ import type { CanopyContext } from './context.js';
27
+ import { type DuplicateUrlPath } from './static/index.js';
28
+ /**
29
+ * Every URL adjacent to the published set that the resolver would actually attempt.
30
+ *
31
+ * Three families, each one a shape that HAS resolved at some point in this package's history:
32
+ *
33
+ * 1. `/<collection>/<entryTypeName>` -- `resolveUrlPathCandidates`' index-fallback candidate lands
34
+ * on a registered entry-TYPE schema item, which `ContentStore.buildPaths` delegates to the
35
+ * parent collection with slug `index`, resolving that collection's index entry.
36
+ * 2. `/<collection>/<entryTypeName>/<slug>` -- the same delegation reached through the
37
+ * DIRECT-entry candidate instead, resolving the collection's entry `<slug>`. Wider than family
38
+ * 1: it needs no index entry at all, so it applies to every listed entry.
39
+ * 3. `/<publishedUrl>/index`, and its case variants -- an index entry answering at the literal
40
+ * spelling its collapsed URL replaced.
41
+ *
42
+ * Entry-type names are appended VERBATIM, after `computeEntryUrl` has lowercased the collection
43
+ * part. That asymmetry is real, not sloppiness: `flattenSchema` puts `entryType.name` into the
44
+ * logical path unchanged and `normalizeFilesystemPath` does not lowercase, so only the declared
45
+ * spelling can reach the entry-type schema item at all. A lowercased probe would miss the schema
46
+ * item, return null for the wrong reason, and pass vacuously.
47
+ */
48
+ export declare const buildProbeUrls: (schema: RootCollectionConfig, items: readonly ListEntriesItem[], contentRoot?: string) => string[];
49
+ export interface UrlExclusivityReport {
50
+ /** Every `urlPath` the listing published, sorted. */
51
+ published: string[];
52
+ /** URLs claimed by more than one entry. A precondition failure, not a resolver finding. */
53
+ duplicates: DuplicateUrlPath[];
54
+ /** Published URLs `readByUrlPath` did NOT resolve -- the round trip broken going forward. */
55
+ unresolved: string[];
56
+ /** Published URLs that resolved to some OTHER entry than the one that published them. */
57
+ mismatched: Array<{
58
+ urlPath: string;
59
+ expectedEntryId?: string;
60
+ actualEntryId?: string;
61
+ }>;
62
+ /** Every adjacent URL probed, sorted. */
63
+ probes: string[];
64
+ /** Probes that resolved despite not being published -- the phantom URLs. Must be empty. */
65
+ phantoms: string[];
66
+ }
67
+ /**
68
+ * Enumerate, round-trip, then probe. See `UrlExclusivityReport` for what each field means and
69
+ * `buildProbeUrls` for which URLs are probed.
70
+ *
71
+ * A probe whose LOWERCASED form is published is skipped rather than asserted on: a collection
72
+ * literally named `index` publishes `/docs/index`, which family 3 also generates, and whether
73
+ * `/docs/Index` should resolve is a separate, pre-existing question about collection-path case
74
+ * sensitivity that this invariant does not speak to.
75
+ */
76
+ export declare const collectUrlExclusivityReport: (ctx: Pick<CanopyContext, "listEntries" | "readByUrlPath">, schema: RootCollectionConfig, contentRoot?: string) => Promise<UrlExclusivityReport>;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The one-URL-per-entry invariant, as a reusable probe + report.
3
+ *
4
+ * `listEntries` assigns every entry exactly one `urlPath` and documents the round trip through
5
+ * `readByUrlPath` as safe. The reverse direction has repeatedly been the looser of the two: it
6
+ * has answered at URLs no forward surface emits, and those extra URLs were found one at a time,
7
+ * a release apart, by adopters (see
8
+ * `.claude/future-tasks/resolved/url-resolver-index-entry-extra-url.md`, then
9
+ * `resolved/readbyurlpath-entry-type-candidate-phantom-url.md`). This module exists so the whole
10
+ * invariant is asserted at once instead: enumerate, then probe every ADJACENT URL the resolver
11
+ * would try and require it to be a miss.
12
+ *
13
+ * Deliberately free of `vitest` -- this is a plain `src` module in the shape of
14
+ * `operating-mode/deployment-name-fixtures.ts`, so more than one test file can import it without
15
+ * dragging that file's `vi.mock` calls and `describe` blocks along. It therefore ASSERTS NOTHING:
16
+ * it returns a report and the caller does the expecting, which also puts the offending URLs in
17
+ * the assertion message rather than behind a boolean.
18
+ *
19
+ * Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
20
+ * with a type their collection declares -- i.e. exactly the set `listEntries` can see. A legacy
21
+ * untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
22
+ * see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
23
+ */
24
+ import { flattenSchema } from './config/index.js';
25
+ import { findDuplicateUrlPaths } from './static/index.js';
26
+ import { computeEntryUrl } from './utils/entry-url.js';
27
+ /** Append raw segments to a URL base, without normalizing their case (see buildProbeUrls). */
28
+ const joinUrl = (base, ...segments) => (base === '/' ? '' : base) + segments.map((s) => `/${s}`).join('');
29
+ /**
30
+ * Every URL adjacent to the published set that the resolver would actually attempt.
31
+ *
32
+ * Three families, each one a shape that HAS resolved at some point in this package's history:
33
+ *
34
+ * 1. `/<collection>/<entryTypeName>` -- `resolveUrlPathCandidates`' index-fallback candidate lands
35
+ * on a registered entry-TYPE schema item, which `ContentStore.buildPaths` delegates to the
36
+ * parent collection with slug `index`, resolving that collection's index entry.
37
+ * 2. `/<collection>/<entryTypeName>/<slug>` -- the same delegation reached through the
38
+ * DIRECT-entry candidate instead, resolving the collection's entry `<slug>`. Wider than family
39
+ * 1: it needs no index entry at all, so it applies to every listed entry.
40
+ * 3. `/<publishedUrl>/index`, and its case variants -- an index entry answering at the literal
41
+ * spelling its collapsed URL replaced.
42
+ *
43
+ * Entry-type names are appended VERBATIM, after `computeEntryUrl` has lowercased the collection
44
+ * part. That asymmetry is real, not sloppiness: `flattenSchema` puts `entryType.name` into the
45
+ * logical path unchanged and `normalizeFilesystemPath` does not lowercase, so only the declared
46
+ * spelling can reach the entry-type schema item at all. A lowercased probe would miss the schema
47
+ * item, return null for the wrong reason, and pass vacuously.
48
+ */
49
+ export const buildProbeUrls = (schema, items, contentRoot = 'content') => {
50
+ const collections = flattenSchema(schema, contentRoot).filter((i) => i.type === 'collection');
51
+ const probes = new Set();
52
+ for (const collection of collections) {
53
+ const base = computeEntryUrl(collection.logicalPath, '', contentRoot);
54
+ for (const entryType of collection.entries ?? []) {
55
+ // Family 1.
56
+ probes.add(joinUrl(base, entryType.name));
57
+ // Family 2 -- every listed entry of this collection, under every type name the collection
58
+ // declares (not just the entry's OWN type: the directory scan `buildPaths` runs matches on
59
+ // slug alone, so any declared name reaches any entry).
60
+ for (const item of items) {
61
+ if (String(item.collectionPath) !== String(collection.logicalPath))
62
+ continue;
63
+ probes.add(joinUrl(base, entryType.name, item.slug));
64
+ }
65
+ }
66
+ }
67
+ // Family 3.
68
+ for (const item of items) {
69
+ for (const spelling of ['index', 'Index', 'INDEX']) {
70
+ probes.add(joinUrl(item.urlPath, spelling));
71
+ }
72
+ }
73
+ return [...probes];
74
+ };
75
+ /**
76
+ * Enumerate, round-trip, then probe. See `UrlExclusivityReport` for what each field means and
77
+ * `buildProbeUrls` for which URLs are probed.
78
+ *
79
+ * A probe whose LOWERCASED form is published is skipped rather than asserted on: a collection
80
+ * literally named `index` publishes `/docs/index`, which family 3 also generates, and whether
81
+ * `/docs/Index` should resolve is a separate, pre-existing question about collection-path case
82
+ * sensitivity that this invariant does not speak to.
83
+ */
84
+ export const collectUrlExclusivityReport = async (ctx, schema, contentRoot = 'content') => {
85
+ const items = await ctx.listEntries();
86
+ const unresolved = [];
87
+ const mismatched = [];
88
+ for (const item of items) {
89
+ const result = await ctx.readByUrlPath(item.urlPath);
90
+ if (!result) {
91
+ unresolved.push(item.urlPath);
92
+ continue;
93
+ }
94
+ if (result.meta.entryId !== item.entryId) {
95
+ mismatched.push({
96
+ urlPath: item.urlPath,
97
+ expectedEntryId: item.entryId,
98
+ actualEntryId: result.meta.entryId,
99
+ });
100
+ }
101
+ }
102
+ const publishedLower = new Set(items.map((i) => i.urlPath.toLowerCase()));
103
+ const probes = buildProbeUrls(schema, items, contentRoot);
104
+ const phantoms = [];
105
+ for (const probe of probes) {
106
+ if (publishedLower.has(probe.toLowerCase()))
107
+ continue;
108
+ if (await ctx.readByUrlPath(probe))
109
+ phantoms.push(probe);
110
+ }
111
+ return {
112
+ published: items.map((i) => i.urlPath).sort(),
113
+ duplicates: findDuplicateUrlPaths(items),
114
+ unresolved: unresolved.sort(),
115
+ mismatched,
116
+ probes: [...probes].sort(),
117
+ phantoms: phantoms.sort(),
118
+ };
119
+ };
@@ -34,10 +34,14 @@ export function resolveUrlPathCandidates(urlPath, contentRoot) {
34
34
  // `index` is not a contrived segment either — it is the slug the index convention requires on
35
35
  // disk, so the collision was structural rather than accidental.
36
36
  //
37
- // This closes the `.../index` spelling, NOT every extra URL: candidate 2 below still resolves an
38
- // index entry at `/<collection>/<entryTypeName>`, because that path is a registered entry-TYPE
39
- // schema item which `buildPaths` delegates to the parent collection. Open, and tracked in
40
- // .claude/future-tasks/readbyurlpath-entry-type-candidate-phantom-url.md.
37
+ // This closes the `.../index` spelling only. The other extra URLs an entry answered at —
38
+ // `/<collection>/<entryTypeName>` via candidate 2, and `/<collection>/<entryTypeName>/<slug>`
39
+ // via candidate 1 — are closed DOWNSTREAM instead, by `readByUrlPath` requiring every
40
+ // candidate's `entryPath` to be a collection (`ReadContentInput.urlAddressableOnly`). They have
41
+ // to be: telling those apart needs the branch's schema, and this module is deliberately pure
42
+ // and schema-free so that the candidate shapes stay a fact about URLs rather than about
43
+ // content. The consequence to keep in mind when reading this file: the candidates below are
44
+ // what is ATTEMPTED, not what can resolve.
41
45
  //
42
46
  // Compared case-INSENSITIVELY, through the shared `isIndexSlug`. This function is the one
43
47
  // consumer that sees a raw, un-normalized URL segment — everything downstream lowercases
@@ -5,7 +5,8 @@
5
5
  * Production runs a Lambda (API) and an EC2 worker over one EFS filesystem.
6
6
  * `ContentStore`'s write/delete/rename critical sections were guarded only by
7
7
  * the in-process mutex (`utils/async-mutex`), which does not cross that
8
- * boundary, while `CmsWorker.rebaseActiveBranches()` rewrites the very same
8
+ * boundary, while the worker's rebase loop (`rebaseOneBranch` in
9
+ * worker/rebase.ts) rewrites the very same
9
10
  * working tree. A write landing after `git rebase` started is destroyed two
10
11
  * ways -- `git checkout --theirs <file>` overwrites it with the branch's
11
12
  * committed version (and the rebase then SUCCEEDS, logging nothing), and
@@ -5,7 +5,8 @@
5
5
  * Production runs a Lambda (API) and an EC2 worker over one EFS filesystem.
6
6
  * `ContentStore`'s write/delete/rename critical sections were guarded only by
7
7
  * the in-process mutex (`utils/async-mutex`), which does not cross that
8
- * boundary, while `CmsWorker.rebaseActiveBranches()` rewrites the very same
8
+ * boundary, while the worker's rebase loop (`rebaseOneBranch` in
9
+ * worker/rebase.ts) rewrites the very same
9
10
  * working tree. A write landing after `git rebase` started is destroyed two
10
11
  * ways -- `git checkout --theirs <file>` overwrites it with the branch's
11
12
  * committed version (and the rebase then SUCCEEDS, logging nothing), and
@@ -35,6 +35,23 @@ export declare function getErrorMessage(err: unknown): string;
35
35
  * (CMS-internal layout like `.canopy-dev/remote.git` is useful for debugging
36
36
  * and not sensitive); absolute paths outside it are replaced with `<path>`.
37
37
  */
38
+ /**
39
+ * TAG: `[REDACT]`
40
+ *
41
+ * Grep `[REDACT]` to find every site that persists or surfaces raw error text on
42
+ * a path that reaches a browser — worker task errors folded into
43
+ * `worker-status.json`, `branch.json` parse failures served by the admin
44
+ * branch-health endpoint, rebase-failure messages, and their tests.
45
+ *
46
+ * Those sites carried the tag `[HIGH-1]`/`[MEDIUM-2]` until 2026-08-23, IDs from
47
+ * a review pass whose findings list was never committed — 13 occurrences across
48
+ * 6 files resolving to nothing. Renamed rather than deleted because the
49
+ * underlying rule is real and genuinely cross-file: a Node error embeds absolute
50
+ * paths, and a git remote URL can embed a token, so anything on that path is
51
+ * redacted through the function below.
52
+ *
53
+ * The tag is a grep aid, not the rule. The rule is stated at each site.
54
+ */
38
55
  export declare function sanitizeErrorMessage(message: string): string;
39
56
  /**
40
57
  * Redact only credential material from a message, leaving filesystem paths
@@ -43,6 +43,23 @@ export function getErrorMessage(err) {
43
43
  * (CMS-internal layout like `.canopy-dev/remote.git` is useful for debugging
44
44
  * and not sensitive); absolute paths outside it are replaced with `<path>`.
45
45
  */
46
+ /**
47
+ * TAG: `[REDACT]`
48
+ *
49
+ * Grep `[REDACT]` to find every site that persists or surfaces raw error text on
50
+ * a path that reaches a browser — worker task errors folded into
51
+ * `worker-status.json`, `branch.json` parse failures served by the admin
52
+ * branch-health endpoint, rebase-failure messages, and their tests.
53
+ *
54
+ * Those sites carried the tag `[HIGH-1]`/`[MEDIUM-2]` until 2026-08-23, IDs from
55
+ * a review pass whose findings list was never committed — 13 occurrences across
56
+ * 6 files resolving to nothing. Renamed rather than deleted because the
57
+ * underlying rule is real and genuinely cross-file: a Node error embeds absolute
58
+ * paths, and a git remote URL can embed a token, so anything on that path is
59
+ * redacted through the function below.
60
+ *
61
+ * The tag is a grep aid, not the rule. The rule is stated at each site.
62
+ */
46
63
  export function sanitizeErrorMessage(message) {
47
64
  let result = redactCredentials(message);
48
65
  // Paths under the project root become relative (split/join avoids regex
@@ -27,7 +27,7 @@ export declare function isNetworkRemoteUrl(url: string): boolean;
27
27
  *
28
28
  * Deliberately narrow: ordinary transient push failures -- network drops,
29
29
  * auth/permission denial, lock contention -- must keep retrying with backoff
30
- * (see worker/cms-worker.ts's `isPermanentTaskFailure`, which treats git
30
+ * (see worker/task-runner.ts's `isPermanentTaskFailure`, which treats git
31
31
  * failures as transient precisely so those cases still get retried). Only
32
32
  * this specific, structurally-unretryable shape should fail fast (worker) or
33
33
  * return 409 (API) instead.
package/dist/utils/git.js CHANGED
@@ -79,7 +79,7 @@ const REJECTION_HINT = 'Updates were rejected because';
79
79
  *
80
80
  * Deliberately narrow: ordinary transient push failures -- network drops,
81
81
  * auth/permission denial, lock contention -- must keep retrying with backoff
82
- * (see worker/cms-worker.ts's `isPermanentTaskFailure`, which treats git
82
+ * (see worker/task-runner.ts's `isPermanentTaskFailure`, which treats git
83
83
  * failures as transient precisely so those cases still get retried). Only
84
84
  * this specific, structurally-unretryable shape should fail fast (worker) or
85
85
  * return 409 (API) instead.
@@ -227,7 +227,7 @@ export async function withOccFileLock(filePath, fn) {
227
227
  // process's lockfile heartbeat stops (crash) or the lock naturally
228
228
  // expires, so this is logged rather than rethrown.
229
229
  //
230
- // [NIT-1] ENOENT specifically is expected, not an error: the lock
230
+ // ENOENT specifically is expected, not an error: the lock
231
231
  // marker's target directory legitimately vanishes when an admin purge
232
232
  // (see api/admin-branch-health.ts) renames the branch tree out from
233
233
  // under a held lock. Every other release failure still warns.