@thincoder/core 0.10.7 → 0.10.8

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 (125) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/advisor/compaction.mjs +1 -1
  3. package/advisor/run.mjs +1 -1
  4. package/agent/chat-call.mjs +40 -27
  5. package/agent/child-marks.mjs +1 -1
  6. package/agent/dispatch-gates.mjs +7 -4
  7. package/agent/dispatch-run.mjs +1 -0
  8. package/agent/family-tools.mjs +11 -6
  9. package/agent/helpers.mjs +6 -4
  10. package/agent/image-heal.mjs +65 -0
  11. package/agent/light-round.mjs +4 -4
  12. package/agent/live-beat.mjs +4 -4
  13. package/agent/record-results.mjs +9 -1
  14. package/agent/run-stages.mjs +2 -1
  15. package/agent/setup-reminders.mjs +14 -13
  16. package/agent/setup.mjs +62 -46
  17. package/agent/spawn-child.mjs +1 -1
  18. package/agent/subagent-event.mjs +1 -1
  19. package/agent/suspension.mjs +2 -1
  20. package/agent/timers.mjs +8 -8
  21. package/agent/turn-loop.mjs +1 -1
  22. package/agent-tools/advisor.mjs +1 -1
  23. package/agent-tools/async-discard.mjs +40 -5
  24. package/agent-tools/async-settle.mjs +3 -1
  25. package/agent-tools/audit-block.mjs +1 -1
  26. package/agent-tools/batch-paths.mjs +20 -20
  27. package/agent-tools/checkpoint.mjs +1 -1
  28. package/agent-tools/design-slots.mjs +44 -5
  29. package/agent-tools/doc-check.mjs +37 -0
  30. package/agent-tools/eng.mjs +5 -3
  31. package/agent-tools/manifest.mjs +18 -18
  32. package/agent-tools/parent-channel.mjs +3 -3
  33. package/agent-tools/review-facts.mjs +2 -2
  34. package/agent-tools/settings.mjs +1 -1
  35. package/agent-tools/spawn-gates.mjs +8 -6
  36. package/agent-tools/ssh-session.mjs +439 -0
  37. package/agent-tools/ssh-transport.mjs +311 -0
  38. package/agent-tools/subagent-scheduler.mjs +6 -5
  39. package/agent-tools/subagent.mjs +7 -5
  40. package/agent-tools/task.mjs +1 -1
  41. package/agent-tools/timer.mjs +6 -0
  42. package/agent-tools/zero-write-watch.mjs +2 -2
  43. package/agent-tools.mjs +3 -2
  44. package/attachments.mjs +4 -4
  45. package/browser/cdp.mjs +2 -2
  46. package/browser/launch.mjs +50 -4
  47. package/browser/snapshot.mjs +1 -1
  48. package/config-io.mjs +7 -2
  49. package/config-watch.mjs +2 -2
  50. package/config.mjs +7 -0
  51. package/declaration.mjs +5 -1
  52. package/doc-check/anchors.mjs +100 -0
  53. package/doc-check/index.mjs +61 -0
  54. package/doc-check/width.mjs +131 -0
  55. package/file-refs.mjs +6 -6
  56. package/generate-title.mjs +1 -1
  57. package/hooks.mjs +1 -1
  58. package/i18n.mjs +11 -1
  59. package/ledger-cmd.mjs +4 -5
  60. package/ledger-db.mjs +100 -21
  61. package/ledger-migrate.mjs +3 -3
  62. package/ledger-read.mjs +6 -7
  63. package/ledger-tools.mjs +24 -9
  64. package/ledger.mjs +23 -54
  65. package/log.mjs +17 -3
  66. package/manifest-discovery.mjs +21 -18
  67. package/manifest-schema.mjs +2 -13
  68. package/manifest.mjs +37 -22
  69. package/mcp/transport-stdio.mjs +1 -1
  70. package/mcp.mjs +2 -2
  71. package/memory/core.mjs +1 -1
  72. package/memory/scan.mjs +1 -1
  73. package/memory/sweep.mjs +1 -1
  74. package/model-specs.mjs +7 -7
  75. package/notify-policy.mjs +6 -6
  76. package/package.json +2 -1
  77. package/permission.mjs +14 -0
  78. package/prompts/advisor-design.md +43 -13
  79. package/prompts/advisor-round1.md +25 -5
  80. package/prompts/advisor-round2.md +17 -4
  81. package/prompts/advisor-round3.md +17 -4
  82. package/prompts/common.md +60 -6
  83. package/prompts/consult-base.md +8 -2
  84. package/prompts/discipline-engineering.md +11 -10
  85. package/prompts/discipline-normal.md +22 -14
  86. package/prompts/persona-coder.md +1 -1
  87. package/prompts/persona-eng-designer.md +8 -3
  88. package/prompts/persona-engineering.md +21 -5
  89. package/prompts/persona-normal.md +4 -1
  90. package/provider/errors.mjs +8 -0
  91. package/provider/google.mjs +3 -0
  92. package/provider/normalize.mjs +42 -12
  93. package/provider/responses-request.mjs +1 -1
  94. package/provider/responses.mjs +2 -2
  95. package/provider/wait-status.mjs +1 -1
  96. package/provider-flows.mjs +1 -1
  97. package/queued.mjs +2 -2
  98. package/rules.mjs +4 -4
  99. package/session-gc.mjs +1 -1
  100. package/session-lifecycle.mjs +1 -1
  101. package/session-slot-write.mjs +2 -2
  102. package/session-slots-manifest.mjs +1 -1
  103. package/session.mjs +1 -1
  104. package/shell-candidates.mjs +2 -2
  105. package/shell-identity.mjs +1 -1
  106. package/team.mjs +61 -4
  107. package/think-off.mjs +3 -3
  108. package/token-ttl.mjs +1 -1
  109. package/tool-docs/bash.md +1 -1
  110. package/tool-docs/browser.md +1 -1
  111. package/tool-docs/doc_check.md +31 -0
  112. package/tool-docs/manifest.md +14 -2
  113. package/tool-docs/ssh.md +34 -0
  114. package/tool-docs/subagent.md +2 -0
  115. package/tool-docs/timer.md +2 -1
  116. package/tools/browser.mjs +1 -1
  117. package/tools/execute.mjs +1 -1
  118. package/tools/git-run.mjs +2 -2
  119. package/tools/git.mjs +8 -8
  120. package/tools/index.mjs +3 -0
  121. package/tools/ops.mjs +1 -1
  122. package/tools/process-tree.mjs +2 -2
  123. package/tools/ssh-askpass.mjs +35 -0
  124. package/tools/ssh.mjs +92 -0
  125. package/vision-reader.mjs +4 -4
@@ -12,6 +12,7 @@
12
12
  * (subagent-spawn.mjs). See docs/design/ENG-TOKEN-BINDING-TUNING.md §5/§5.1 (F-R16).
13
13
  * #41(2026-09-18):enter 分支改「先判后翻」——判据 = 入口决策树单源
14
14
  * `resolveEngineeringManifest`(docs/core/design/MANIFEST.md §2.8 F2 / KD-M1-20/21);
15
+ * **可拒面 = 仅「档非法」一格**(工作区 `workspace` 放行——零附着,模式照开);
15
16
  * 拒翻 ⇒ 返回原因串 + **零副作用**(模式保持 OFF)。OFF 方向与幂等 enter 零改。
16
17
  */
17
18
  import { ENG_ON_REMINDER, ENG_OFF_REMINDER } from "../agent.mjs"
@@ -74,15 +75,16 @@ export const engTool = {
74
75
  return "Engineering mode already active. Existing design tokens stay valid."
75
76
  }
76
77
  // #41 先判后翻(MANIFEST.md §2.8 F2 / KD-M1-21):判据 = 入口决策树**单源**
77
- // (resolveEngineeringManifest——KD-M1-20),判据通过才写态。拒翻 ⇒ 返回原因串 +
78
+ // (resolveEngineeringManifest——KD-M1-20),判据通过才写态。**可拒面 = 仅「档非法」格**
79
+ // (工作区 `workspace` ⇒ 放行——**零附着**:无项目档可附,模式照开);拒翻 ⇒ 返回原因串 +
78
80
  // **零副作用**(不清 token / 不重置 _advisorRuns / 不入列 ENG_ON_REMINDER /
79
81
  // 不动 _lastEngState / 不触镜像缝);准 ⇒ 缺档格就地建档(writer:'main')+
80
82
  // agent.manifest ← 结果(翻转面唯一赋值点——相位行当回合起活,§2.8 F2 放行行)。
81
83
  const r = resolveEngineeringManifest(ctx.agent.cwd ?? ctx.cwd ?? process.cwd(), { writer: "main" })
82
- if (!r.ok) {
84
+ if (!r.ok && r.code !== "workspace") {
83
85
  return `Error: cannot enter engineering mode — ${r.message} (mode unchanged)`
84
86
  }
85
- ctx.agent.manifest = r.manifest
87
+ ctx.agent.manifest = r.ok ? r.manifest : null // 工作区放行 ⇒ 零附着(显式置 null——§2.8 F2 放行行)
86
88
  ctx.agent.config.agent.engineering = true
87
89
  // FR31 ③ / KD10(翻转点①):工程模式 ⇒ planMode 恒 false——中途开模式时清残留
88
90
  //(planMode 位 + reminder 计数 + 未注入的 plan 提示语),半状态(工程纪律 × plan 只读)不产生。
@@ -3,7 +3,7 @@
3
3
  * agent 侧读写出口(2026-10-08 · 台账 #1098;权威设计 = `docs/core/design/MANIFEST.md` §2.10)。
4
4
  *
5
5
  * 三动作(KD-M1-38):
6
- * - `read` ⇒ `projectView(target)` 五态报明(稳定锚 `state=ok|missing|invalid|ambiguous|no-project`);
6
+ * - `read` ⇒ `projectView(target)` 五态报明(稳定锚 `state=ok|missing|invalid|workspace|no-project`);
7
7
  * `ok` 态再 `readManifest(view.root)` 取细节面(`projectView` 面不含 `missingKeys` /
8
8
  * `unknownKeys`)——**不抛**(缺档报明,非拒)。
9
9
  * - `init` ⇒ 缺档建默认档(`initManifest`——内容 = `DEFAULT_MANIFEST`);**已带档 ⇒ 拒**
@@ -15,12 +15,12 @@
15
15
  * (`version` / `phase` / `promptsLanding` / `codePaths` 整键)+ `nestedKeys` 子键
16
16
  * (`docRoot.*` / `checkConfig.*` / `index.*`(含 `publicRepos`)/ `advisor.*`);**四族整键写
17
17
  * (传整个对象)⇒ 拒**;白名单外 ⇒ 拒(拼写保护——未知键写盘 = 静默死键,KD-M1-36 问题本体)。
18
- * `value` 解析 = 同款口径 `settings.mjs:216-228`(先 `JSON.parse`,失败取字面串;`"null"` ⇒ `null`
18
+ * `value` 解析 = 同款口径 `settings.mjs` 的 `parseValue`(先 `JSON.parse`,失败取字面串;`"null"` ⇒ `null`
19
19
  * ——`docRoot.*` 的「本面无」,KD-M1-37)。
20
20
  *
21
21
  * 目标面(KD-M1-39——判据复用零新解析器):`read` = `projectView`;`init` / `write` =
22
- * `projectRootView`(歧义 ⇒ 拒 + 候选全列——不猜);`resolveProjectRoot` 对歧义塌缩 `null` 再回落 cwd
23
- * ⇒ 写 / 建两动作**不得**直用。
22
+ * `projectRootView`(工作区 ⇒ 拒 + 下辖项目全列 + 显式 `target` 指引);`resolveProjectRoot` 对
23
+ * 工作区塌缩 `null` 再回落 cwd ⇒ 写 / 建两动作**不得**直用。
24
24
  *
25
25
  * 写路径复用(零新写路径):落盘恒经 `writeManifest` / `initManifest`——`writer` 恒显式 `'main'`
26
26
  * (缺省 `'subagent'` = 写门 fail-closed 缺省,KD-M1-3 / AC-M1-5 零改)。
@@ -43,7 +43,7 @@ function refuse(action, reason) {
43
43
  return new Error(`manifest ${action}: refused — ${reason}`)
44
44
  }
45
45
 
46
- /** `target` 解析(先例 `eng.mjs:81` 缺省会话锚 ∥ `git.mjs:32-35` 相对形归一):缺省 = 会话锚
46
+ /** `target` 解析(先例 `eng.mjs` 的 `resolveEngineeringManifest` 缺省会话锚 ∥ `git.mjs` 的 `resolveBaseDir` 相对形归一):缺省 = 会话锚
47
47
  * (`ctx.agent.cwd ?? ctx.cwd ?? process.cwd()`);显式相对形按会话锚归一(绝对形原样)。 */
48
48
  function resolveTarget(args, ctx) {
49
49
  const base = ctx?.agent?.cwd ?? ctx?.cwd ?? process.cwd()
@@ -65,7 +65,7 @@ function isWritableKey(key) {
65
65
  return false
66
66
  }
67
67
 
68
- /** `value` 解析(同款口径 = `settings.mjs:216-228`):先 `JSON.parse`(`"null"` ⇒ `null`;数值 /
68
+ /** `value` 解析(同款口径 = `settings.mjs` 的 `parseValue`):先 `JSON.parse`(`"null"` ⇒ `null`;数值 /
69
69
  * 布尔 / 数组皆 JSON 形),失败取字面串(裸串如 `docs/design`)。 */
70
70
  function parseValue(raw) {
71
71
  const s = String(raw)
@@ -83,14 +83,14 @@ function setKeyPath(obj, path, value) {
83
83
  cur[segs[segs.length - 1]] = value
84
84
  }
85
85
 
86
- /** 歧义拒文案(候选全列——不猜;init / write 共用):多候选 = 显式指定 target,机制不代选。 */
87
- function ambiguousReason(target, candidates) {
88
- return `ambiguous target ${target} — ${candidates.length} candidate projects (the mechanism never picks one); pass target explicitly:\n` +
86
+ /** 工作区拒文案(下辖项目全列 + 显式 `target` 下一步——init / write 共用;事实行,非二义框定)。 */
87
+ function workspaceReason(target, candidates) {
88
+ return `${target} is a workspace holding ${candidates.length} projects — pass the project root explicitly via target:\n` +
89
89
  candidates.map((c) => `- ${c}`).join("\n")
90
90
  }
91
91
 
92
92
  /** read 动作(五态报明——**不抛**):`state=` 稳定锚 + 各态附行(ok 细节面 / missing 引导 /
93
- * ambiguous 候选全列 / invalid 错误行)。 */
93
+ * workspace 下辖项目全列 / invalid 错误行)。 */
94
94
  function readAction(target) {
95
95
  const view = projectView(target)
96
96
  const lines = [`state=${view.state}`]
@@ -105,10 +105,10 @@ function readAction(target) {
105
105
  lines.push(`hint: run manifest action=init to create the default manifest here`)
106
106
  } else if (view.state === "no-project") {
107
107
  lines.push(`hint: no project on the ancestor chain and none below — manifest action=init can create one at the target`)
108
- } else if (view.state === "ambiguous") {
109
- lines.push(`candidates (${(view.candidates ?? []).length}):`)
108
+ } else if (view.state === "workspace") {
109
+ lines.push(`projects (${(view.candidates ?? []).length}):`)
110
110
  for (const c of view.candidates ?? []) lines.push(`- ${c}`)
111
- lines.push(`hint: pass target explicitly — the mechanism never picks one`)
111
+ lines.push(`hint: pass target explicitly — the target must be one of the projects above`)
112
112
  } else if (view.state === "invalid") {
113
113
  if (view.errors?.length) lines.push(`errors=${view.errors.join(";")}`)
114
114
  }
@@ -122,13 +122,13 @@ export const manifestTool = {
122
122
  type: "object",
123
123
  properties: {
124
124
  action: { type: "string", enum: ["read", "init", "write"], description: "read — current manifest + validation state (five-state anchor, never throws); init — create the default manifest when missing (refused when one already exists); write — point-edit one node (key + value) of an existing manifest" },
125
- target: { type: "string", description: "Explicit target directory — defaults to the session anchor; relative paths resolve against it. Ambiguous targets are refused with the full candidate list (never picks one)." },
125
+ target: { type: "string", description: "Explicit target directory — defaults to the session anchor; relative paths resolve against it. A workspace target (several projects below it) is refused with the full project list — pass the project root you mean." },
126
126
  key: { type: "string", description: "(write) Dot path of the node to set — version / phase / promptsLanding / codePaths, or a sub-key of docRoot / checkConfig / index / advisor (e.g. docRoot.design / index.publicRepos / advisor.docMap); whole-object writes of those four families are refused", },
127
127
  value: { type: "string", description: "(write) New value — parsed as JSON first (null / true / 5 / [\"a\"]), otherwise kept as a literal string; \"null\" is the explicit no-such-face for docRoot.<key>" },
128
128
  },
129
129
  required: ["action"],
130
130
  },
131
- readonly: false, // 动作级分类(dispatch-gates `isSubagentReadonlyAction`):read = 只读类;init / write 保持侧效门
131
+ readonly: false, // 动作级分类(dispatch-gates `isReadonlyAction`):read = 只读类;init / write 保持侧效门
132
132
  async execute(args, ctx) {
133
133
  const action = args?.action
134
134
  if (!["read", "init", "write"].includes(action)) {
@@ -138,10 +138,10 @@ export const manifestTool = {
138
138
 
139
139
  if (action === "read") return readAction(target)
140
140
 
141
- // init / write:目标面 = `projectRootView`(歧义直读形——不猜;KD-M1-39)
141
+ // init / write:目标面 = `projectRootView`(直读形——工作区不成目标;KD-M1-39)
142
142
  const rootView = projectRootView(target)
143
- if (rootView.state === "ambiguous") {
144
- throw refuse(action, ambiguousReason(target, rootView.candidates))
143
+ if (rootView.state === "workspace") {
144
+ throw refuse(action, workspaceReason(target, rootView.candidates))
145
145
  }
146
146
 
147
147
  if (action === "init") {
@@ -34,7 +34,7 @@
34
34
  * 两端共用的提示行携参(队首 ask 的 `from` + 单行归一截断的 `message`);两端各持一份 = 漂移源(D2)。
35
35
  *
36
36
  * 模块图:静态 import 核单点 `async-settle.mjs`(`carrierField` / `getAsyncPool` /
37
- * `tombstoneOf` / `wakeAsyncWaiters`——同层既有导出,同 `async-discard.mjs:35` 先例;唤醒走
37
+ * `tombstoneOf` / `wakeAsyncWaiters`——同层既有导出,同 `async-discard.mjs` 的 `async-settle.mjs` 静态导入先例;唤醒走
38
38
  * 既有静态边,零新增)+ `../context.mjs`(`pushReal`)
39
39
  * / `../agent/helpers.mjs`(`escapeXml`)/ `../log.mjs`(`logEvent`);单向、叶子向、无环。
40
40
  * 登记册导出(`agent-tools.mjs`)→ 家族矩阵装配(`agent/family-tools.mjs` depth>0 段)。
@@ -55,7 +55,7 @@ export const UPSTREAM_ASK_MAX_INFLIGHT = 1
55
55
 
56
56
  /** 工具返回注(异步形——sync: false;答复按普通指令在下回合边界到达)。 */
57
57
  const ASYNC_NOTE =
58
- "delivered to your parent's queue — consumed at the parent's next turn boundary (non-blocking). " +
58
+ "delivered to your parent's queue — consumed at the parent's next delivery round (idle wake or suspension window; not during a user turn), non-blocking. " +
59
59
  "Keep working on the unaffected parts; a reply arrives as an ordinary instruction at your next turn boundary. " +
60
60
  "If your run ends first, report the unanswered part as not done."
61
61
 
@@ -73,7 +73,7 @@ const refused = (error) => JSON.stringify({ status: "error", error })
73
73
  * ① 父字段在场 ⇒ 父对象(CLI 形主容器——写侧不变);
74
74
  * ② 缺 ⇒ `carrierField` 回退 `history` 命中 ⇒ **借用同一容器**(不另建分叉);
75
75
  * ③ 两者皆无 ⇒ 建在父字段 + **载体别名**(`parent.history._childUpstream = parent._childUpstream`
76
- * ——同 `writeTombstone` 借用规则扩张句的既有形态,`agent-tools/async-settle.mjs:73-83`)。
76
+ * ——同 `writeTombstone` 借用规则扩张句的既有形态,`agent-tools/async-settle.mjs` 的 `writeTombstone`)。
77
77
  */
78
78
  function upstreamHolder(parent) {
79
79
  if (!Array.isArray(parent._childUpstream)) {
@@ -142,11 +142,11 @@ export function noteReviewDelivered(agent, run) {
142
142
  }
143
143
 
144
144
  /** 分支③候选底座(`MANIFEST.md` §2.5「路径归一增量」族第四笔 · 2026-10-05 · #945):现行
145
- * `projectRootView(cwd)` 集(ok → [root];ambiguous → candidates 按名序;none → 空集)+
145
+ * `projectRootView(cwd)` 集(ok → [root];workspace → candidates 按名序;none → 空集)+
146
146
  * cwd 祖先链全量目录(自父目录起至盘根、最近→远、去重、现行集在前)——容器相对形在子仓锚下
147
147
  * 可解析;受理判据(目标解析位最近 manifest 声明面)∥ 围栏 ∥ 可读唯一化 ∥ fail-closed 歧义零改。 */
148
148
  function candidateRoots(view, cwd) {
149
- const current = view.state === "ok" ? [view.root] : view.state === "ambiguous" ? view.candidates : []
149
+ const current = view.state === "ok" ? [view.root] : view.state === "workspace" ? view.candidates : []
150
150
  const ancestors = []
151
151
  for (let d = dirname(resolve(cwd)); ; ) {
152
152
  ancestors.push(d)
@@ -127,7 +127,7 @@ function _checkShape(path, spec, value) {
127
127
  }
128
128
  return
129
129
  }
130
- // nonEmptyString —— defaultModel 另加 provider:model 形态面(与 model-ref.mjs:25-36 同判据;
130
+ // nonEmptyString —— defaultModel 另加 provider:model 形态面(与 model-ref.mjs 的 `parseModelRef` 同判据;
131
131
  // 存在性面不查——D-S2.5)
132
132
  if (typeof value !== "string" || !value.trim()) throw _expectsError(path, spec.expects, value)
133
133
  if (spec.providerModel) {
@@ -87,18 +87,20 @@ const MANIFEST_BASENAME = "project-manifest.json"
87
87
 
88
88
  /**
89
89
  * F6 files 声明面拦截谓词(ENGINEERING-MODE-V2-MODULE-DELEGATION §2.2):
90
- * scripts/**(工程工具面不入域——内容产物 only)与过程档(父侧职责)→ 收集全部违规
91
- * 后抛合并错误(fail-closed——错误即工具结果 JSON——无排队残留)。目录声明不在本
92
- * 谓词(normalizeFileList 循环内先行拒——错误序不变)。调用点 = normalizeFileList
93
- * (subagent-scheduler.mjs——本谓词在循环后跑——目录错误先于过程档错误)。
90
+ * scripts/*(工程工具面不入域——**层级锚定:仅仓根与直接子层**——目录名在深层不携带
91
+ * face 信息(2026-10-14 收正:#1449——`deployment/<x>/scripts/*` 类交付物放行)——内容
92
+ * 产物 only)与过程档(父侧职责)→ 收集全部违规后抛合并错误(fail-closed——错误即工具
93
+ * 结果 JSON——无排队残留)。目录声明不在本谓词(normalizeFileList 循环内先行拒——错误
94
+ * 序不变)。调用点 = normalizeFileList(subagent-scheduler.mjs——本谓词在循环后跑——
95
+ * 目录错误先于过程档错误)。
94
96
  */
95
97
  export function rejectEngineeringFilePaths(files) {
96
98
  const violations = []
97
99
  for (const f of Array.isArray(files) ? files : []) {
98
100
  const segs = String(f).replace(/\\/g, "/").split("/").filter((s) => s)
99
101
  const base = (segs[segs.length - 1] ?? "").toLowerCase()
100
- if (segs.some((s) => s.toLowerCase() === "scripts")) {
101
- violations.push(`engineering-tools face path ${f} must not be listed in files — scripts/** is the engineering-tools face, not a content product (applies to every session, not engineering-mode-scoped; files carries source/test/design-doc paths only)`)
102
+ if (segs.slice(0, 2).some((s) => s.toLowerCase() === "scripts")) {
103
+ violations.push(`engineering-tools face path ${f} must not be listed in files — scripts/* at the repo root or a direct child package is the engineering-tools face, not a content product (applies to every session, not engineering-mode-scoped; files carries source/test/design-doc paths only)`)
102
104
  } else if (PROCESS_BASENAMES.has(base)) {
103
105
  violations.push(`Parent-side maintained file ${f} must not be listed in files — reconciliation is the parent's duty; use the design-doc path if you need to edit a design doc`)
104
106
  } else if (base === MANIFEST_BASENAME) {