@1aboveio/skills 1.0.7 → 1.0.9

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 (224) hide show
  1. package/.codex-plugin/plugin.json +13 -0
  2. package/.codex-plugin/resources.json +1827 -0
  3. package/README.md +22 -0
  4. package/package.json +9 -2
  5. package/skills/app-dev/airflow/SKILL.md +1 -1
  6. package/skills/app-dev/airflow/release-check.mjs +3 -1
  7. package/skills/app-dev/android-foundation/SKILL.md +92 -34
  8. package/skills/app-dev/android-foundation/assets/android-ci-job.yml +13 -7
  9. package/skills/app-dev/android-foundation/references/ci.md +23 -13
  10. package/skills/app-dev/android-foundation/references/delivery-focus.md +29 -0
  11. package/skills/app-dev/android-foundation/references/diagnosis.md +136 -0
  12. package/skills/app-dev/android-foundation/references/gotchas.md +20 -11
  13. package/skills/app-dev/android-foundation/references/setup.md +15 -6
  14. package/skills/app-dev/android-foundation/release-check.mjs +3 -1
  15. package/skills/app-dev/android-foundation/scripts/device-run.sh +148 -42
  16. package/skills/app-dev/app-debug/SKILL.md +1 -1
  17. package/skills/app-dev/app-debug/release-check.mjs +3 -1
  18. package/skills/app-dev/better-auth/SKILL.md +1 -1
  19. package/skills/app-dev/better-auth/release-check.mjs +3 -1
  20. package/skills/app-dev/data-catalog/SKILL.md +1 -1
  21. package/skills/app-dev/data-catalog/release-check.mjs +3 -1
  22. package/skills/app-dev/data-quality/SKILL.md +1 -1
  23. package/skills/app-dev/data-quality/release-check.mjs +3 -1
  24. package/skills/app-dev/ios-foundation/SKILL.md +1 -1
  25. package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
  26. package/skills/app-dev/monorepo/SKILL.md +1 -1
  27. package/skills/app-dev/monorepo/release-check.mjs +3 -1
  28. package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
  29. package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
  30. package/skills/app-dev/prisma-setup/SKILL.md +1 -1
  31. package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
  32. package/skills/app-dev/pyspark/SKILL.md +1 -1
  33. package/skills/app-dev/pyspark/release-check.mjs +3 -1
  34. package/skills/app-dev/python-backend/SKILL.md +1 -1
  35. package/skills/app-dev/python-backend/release-check.mjs +3 -1
  36. package/skills/app-dev/shadcn/SKILL.md +1 -1
  37. package/skills/app-dev/shadcn/release-check.mjs +3 -1
  38. package/skills/app-dev/zod-v4/SKILL.md +1 -1
  39. package/skills/app-dev/zod-v4/release-check.mjs +3 -1
  40. package/skills/cicd/cloud-build/SKILL.md +1 -1
  41. package/skills/cicd/cloud-build/release-check.mjs +3 -1
  42. package/skills/cicd/cloud-debug/SKILL.md +1 -1
  43. package/skills/cicd/cloud-debug/release-check.mjs +3 -1
  44. package/skills/cicd/cloud-deploy/SKILL.md +1 -1
  45. package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
  46. package/skills/cicd/docker/SKILL.md +1 -1
  47. package/skills/cicd/docker/release-check.mjs +3 -1
  48. package/skills/cicd/github-actions/SKILL.md +1 -1
  49. package/skills/cicd/github-actions/release-check.mjs +3 -1
  50. package/skills/cicd/github-flow/SKILL.md +1 -1
  51. package/skills/cicd/github-flow/release-check.mjs +3 -1
  52. package/skills/cicd/google-cloud/SKILL.md +1 -1
  53. package/skills/cicd/google-cloud/release-check.mjs +3 -1
  54. package/skills/cicd/mergify/SKILL.md +1 -1
  55. package/skills/cicd/mergify/release-check.mjs +3 -1
  56. package/skills/cicd/podman/SKILL.md +1 -1
  57. package/skills/cicd/podman/release-check.mjs +3 -1
  58. package/skills/content/article-review/SKILL.md +1 -1
  59. package/skills/content/article-review/release-check.mjs +3 -1
  60. package/skills/content/article-to-image/SKILL.md +1 -1
  61. package/skills/content/article-to-image/release-check.mjs +3 -1
  62. package/skills/content/flowchart-svg/SKILL.md +1 -1
  63. package/skills/content/flowchart-svg/release-check.mjs +3 -1
  64. package/skills/content/image-gen/SKILL.md +1 -1
  65. package/skills/content/image-gen/release-check.mjs +3 -1
  66. package/skills/content/mermaid/SKILL.md +1 -1
  67. package/skills/content/mermaid/release-check.mjs +3 -1
  68. package/skills/content/wechat-article-writer/SKILL.md +1 -1
  69. package/skills/content/wechat-article-writer/release-check.mjs +3 -1
  70. package/skills/content/wechat-publisher/SKILL.md +1 -1
  71. package/skills/content/wechat-publisher/release-check.mjs +3 -1
  72. package/skills/content/wechat-search/SKILL.md +1 -1
  73. package/skills/content/wechat-search/release-check.mjs +3 -1
  74. package/skills/content/xai-search/SKILL.md +88 -13
  75. package/skills/content/xai-search/release-check.mjs +3 -1
  76. package/skills/content/xai-search/scripts/xai_search.sh +125 -149
  77. package/skills/core/distill-writing/SKILL.md +1 -1
  78. package/skills/core/distill-writing/release-check.mjs +3 -1
  79. package/skills/core/doc-to-md/SKILL.md +1 -1
  80. package/skills/core/doc-to-md/release-check.mjs +3 -1
  81. package/skills/core/editorial-review/SKILL.md +1 -1
  82. package/skills/core/editorial-review/release-check.mjs +3 -1
  83. package/skills/core/house-style/SKILL.md +1 -1
  84. package/skills/core/house-style/release-check.mjs +3 -1
  85. package/skills/core/plain-language/SKILL.md +1 -1
  86. package/skills/core/plain-language/release-check.mjs +3 -1
  87. package/skills/core/polish-cn/SKILL.md +1 -1
  88. package/skills/core/polish-cn/release-check.mjs +3 -1
  89. package/skills/core/reg-parser/SKILL.md +1 -1
  90. package/skills/core/reg-parser/release-check.mjs +3 -1
  91. package/skills/core/terminology/SKILL.md +1 -1
  92. package/skills/core/terminology/release-check.mjs +3 -1
  93. package/skills/core/write-doc/SKILL.md +1 -1
  94. package/skills/core/write-doc/release-check.mjs +3 -1
  95. package/skills/engineering/better-review/SKILL.md +1 -1
  96. package/skills/engineering/better-review/release-check.mjs +3 -1
  97. package/skills/engineering/contributing/SKILL.md +1 -1
  98. package/skills/engineering/contributing/release-check.mjs +3 -1
  99. package/skills/engineering/e2e-test/SKILL.md +1 -1
  100. package/skills/engineering/e2e-test/release-check.mjs +3 -1
  101. package/skills/engineering/engineering-runtime/SKILL.md +1 -1
  102. package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
  103. package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
  104. package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
  105. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
  106. package/skills/engineering/ensure-coverage/SKILL.md +1 -1
  107. package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
  108. package/skills/engineering/golden-path/SKILL.md +1 -1
  109. package/skills/engineering/golden-path/release-check.mjs +3 -1
  110. package/skills/engineering/harness-runtime/SKILL.md +1 -1
  111. package/skills/engineering/harness-runtime/release-check.mjs +3 -1
  112. package/skills/engineering/resolve-issues/SKILL.md +11 -20
  113. package/skills/engineering/resolve-issues/references/delivery-progress.md +63 -0
  114. package/skills/engineering/resolve-issues/references/expire.md +24 -11
  115. package/skills/engineering/resolve-issues/references/implementation.md +9 -1
  116. package/skills/engineering/resolve-issues/references/preflight.md +7 -0
  117. package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
  118. package/skills/engineering/resolve-issues/references/task-plan.md +12 -5
  119. package/skills/engineering/resolve-issues/release-check.mjs +3 -1
  120. package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
  121. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
  122. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
  123. package/skills/engineering/resolve-release/SKILL.md +1 -1
  124. package/skills/engineering/resolve-release/release-check.mjs +3 -1
  125. package/skills/engineering/rush-issues/SKILL.md +5 -2
  126. package/skills/engineering/rush-issues/release-check.mjs +3 -1
  127. package/skills/engineering/rush-release/SKILL.md +1 -1
  128. package/skills/engineering/rush-release/release-check.mjs +3 -1
  129. package/skills/engineering/test-quality/SKILL.md +1 -1
  130. package/skills/engineering/test-quality/release-check.mjs +3 -1
  131. package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
  132. package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
  133. package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
  134. package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
  135. package/skills/risk-compliance/contract-review/SKILL.md +1 -1
  136. package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
  137. package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
  138. package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
  139. package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
  140. package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
  141. package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
  142. package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
  143. package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
  144. package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
  145. package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
  146. package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
  147. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -172
  148. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
  149. package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
  150. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
  151. package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
  152. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
  153. package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
  154. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
  155. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
  156. package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
  157. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
  158. package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
  159. package/skills/content/image-gen/scripts/options.test.mjs +0 -245
  160. package/skills/content/xai-search/tests/smoke_test.sh +0 -36
  161. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
  162. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
  163. package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
  164. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
  165. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
  166. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
  167. package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
  168. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
  169. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
  170. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
  171. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
  172. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
  173. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
  174. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
  175. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
  176. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
  177. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
  178. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
  179. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
  180. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
  181. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
  182. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
  183. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
  184. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
  185. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
  186. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
  187. package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
  188. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
  189. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
  190. package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
  191. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
  192. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
  193. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
  194. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
  195. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
  196. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
  197. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
  198. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
  199. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
  200. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
  201. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
  202. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
  203. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
  204. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
  205. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
  206. package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
  207. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
  208. package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
  209. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
  210. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
  211. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
  212. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
  213. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
  214. package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
  215. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
  216. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
  217. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
  218. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
  219. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
  220. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
  221. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
  222. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
  223. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
  224. package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
@@ -2,7 +2,7 @@
2
2
  name: reg-parser
3
3
  version: 1.1.0
4
4
  description: "Parse regulatory/scheme documents into structured outputs: convert a regulation (AML, VARS, PCI-DSS, KYC, GDPR) into an audit-ready compliance checklist, or trace contract clauses to a mandate source. Suggest proactively when a regulation PDF accompanies a contract review or compliance task. NOT general summarization (doc-to-md), or writing the gap analysis or policy from the checklist (write-doc)."
5
- release-version: 0.43.0
5
+ release-version: 0.44.0
6
6
  ---
7
7
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
8
8
  # reg-parser
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: terminology
3
3
  description: "Build and keep a project's TERMINOLOGY.md: the preferred term per concept in English, 简体 and 繁體, and the terms to avoid. Use to set it up from the project's documents, when the user settles or corrects a term, or when another writing skill needs the format. NOT for writing documents (write-doc) or the code domain model (domain-modeling)."
4
- release-version: 0.43.0
4
+ release-version: 0.44.0
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Terminology
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: write-doc
3
3
  description: "Write or edit documents and compliance reports in the project's house style: BRD, PRD, gap analysis, technical spec, integration guide, proposal, policy, risk procedure, audit report, external assessment, management report or Visa VARS Review Report; English, Chinese or bilingual. Use automatically whenever asked to draft, write, revise or edit a document, without needing a slash command, including 'write up the findings'. Route explicit requests to remember a writing preference to its persistence owner. Markdown by default; export when requested. NOT for ordinary chat replies (plain-language), learning style from samples or preference-only saves (distill-writing), performing an audit (compliance-audit), or WeChat articles (wechat-article-writer)."
4
- release-version: 0.43.0
4
+ release-version: 0.44.0
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Write a document
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -4,7 +4,7 @@ description: "Slash/explicit-only (/better-review). Single-agent adversarial rev
4
4
  disable-model-invocation: true
5
5
  dependencies:
6
6
  - engineering-runtime
7
- release-version: 0.43.0
7
+ release-version: 0.44.0
8
8
  ---
9
9
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
10
10
  # Better Review
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: contributing
3
3
  description: "The conventions a repo's CONTRIBUTING.md holds, starting with tracker routing: which tracker an issue belongs in (GitHub for engineering, Linear for product), duplicate search, and linking back to the source requirement. Use before creating any issue, spec, epic, ticket or sub-issue on GitHub or Linear, including inside /to-spec, /to-tickets and /triage, and whenever work that started from a Linear issue turns into engineering work. Also writes the rules as the Issue tracking section of the repo's CONTRIBUTING.md. NOT for delivering a PR (github-flow) or splitting work into slices (to-tickets)."
4
- release-version: 0.43.0
4
+ release-version: 0.44.0
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # Contributing
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: e2e-test
3
3
  description: "Playwright end-to-end standard: the 3-tier suite (render-health / functional / journey), generated route manifest, and presentation sweep. Use when browser routes or user flows ship or change, or a Playwright test flakes. NOT for non-browser tests, defining what to test (ensure-coverage), the curated ALIVE/DEAD golden-path gate (golden-path), or CI workflow YAML (github-actions)."
4
- release-version: 0.43.0
4
+ release-version: 0.44.0
5
5
  ---
6
6
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
7
7
  # E2E Testing Standard
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -3,7 +3,7 @@ name: engineering-runtime
3
3
  description: Internal support package for shared Engineering Workflow executable behavior. Never select or invoke this package as a user capability.
4
4
  disable-model-invocation: true
5
5
  user-invocable: false
6
- release-version: 0.43.0
6
+ release-version: 0.44.0
7
7
  ---
8
8
  **Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
9
9
  # Engineering Runtime
@@ -369,7 +369,7 @@
369
369
  "repository": "1aboveio/ai",
370
370
  "revision": "main"
371
371
  },
372
- "contentDigest": "8f0be335debec8a0b64bb40201a412eb0bc9ccc402074a7af0a483609daac28e",
372
+ "contentDigest": "ae8ace5c97889e14e669829d72a92bd90d593ba1099c4bd690c98eca8b9c94e3",
373
373
  "digestExcludes": []
374
374
  },
375
375
  {
@@ -382,7 +382,7 @@
382
382
  "repository": "1aboveio/ai",
383
383
  "revision": "main"
384
384
  },
385
- "contentDigest": "54c4c3e8579168b32c9d894f91d9bd6950dde2bf44ebd908cc7c548bdc0f6435",
385
+ "contentDigest": "ddf8e11e23f667cdc677c9d70edb6ed59da53b5a0b82af29f6be36eda35861c2",
386
386
  "digestExcludes": []
387
387
  },
388
388
  {
@@ -395,7 +395,7 @@
395
395
  "repository": "1aboveio/ai",
396
396
  "revision": "main"
397
397
  },
398
- "contentDigest": "8e6ae6a6065c670b6ed61c2b881dbea921847a7d80818916e853c4b73dd02e56",
398
+ "contentDigest": "0e3c87b01b5c337a7628ae5ba5c78a3b208ab404b8d848e4d8299f221224726d",
399
399
  "digestExcludes": []
400
400
  },
401
401
  {
@@ -408,7 +408,7 @@
408
408
  "repository": "1aboveio/ai",
409
409
  "revision": "main"
410
410
  },
411
- "contentDigest": "d5c96b3b0a7272a6ae6d4670f3deca7196c39cdb857d52936b076e880d338bcd",
411
+ "contentDigest": "de98656af030369ea229112e90f441626863dcca5e21f2f358e26041d0624f3e",
412
412
  "digestExcludes": []
413
413
  },
414
414
  {
@@ -421,7 +421,7 @@
421
421
  "repository": "1aboveio/ai",
422
422
  "revision": "main"
423
423
  },
424
- "contentDigest": "077317f2b2e8a142bd46c75d00bf2ba23b34cc24daa3f49bb9e4bb7247f7590c",
424
+ "contentDigest": "c4f55e7aed013006d00fde40504db87c32c48ca8b04ef4aeacf9c7fe31995fed",
425
425
  "digestExcludes": []
426
426
  },
427
427
  {
@@ -434,7 +434,7 @@
434
434
  "repository": "1aboveio/ai",
435
435
  "revision": "main"
436
436
  },
437
- "contentDigest": "a755c90afd1aef1e3de56fd35af08e98fc111b7cea775a63c7a34593ec1e499b",
437
+ "contentDigest": "7ec336f6920ec1195fbe08b40234ac72f8213a3b94f575e2c463108b36884a25",
438
438
  "digestExcludes": []
439
439
  },
440
440
  {
@@ -447,7 +447,7 @@
447
447
  "repository": "1aboveio/ai",
448
448
  "revision": "main"
449
449
  },
450
- "contentDigest": "937c2139aa3ca8f9f0a325c8a1300b1a84faba2f16132f8c608169e4e0732631",
450
+ "contentDigest": "845c78252f5308aa71b8eaabc80718c25a772d016d5d74eb888737cd7ffbe664",
451
451
  "digestExcludes": []
452
452
  },
453
453
  {
@@ -460,7 +460,7 @@
460
460
  "repository": "1aboveio/ai",
461
461
  "revision": "main"
462
462
  },
463
- "contentDigest": "a2e269f07c9309c5404eafe1b2ca2f5913dd390711c9a3ea7bffb3ae13c88a58",
463
+ "contentDigest": "0c9e35d5014ebd7b4512aeb7705cd39977252ccaa639821fa87f8f463364b613",
464
464
  "digestExcludes": []
465
465
  },
466
466
  {
@@ -473,7 +473,7 @@
473
473
  "repository": "1aboveio/ai",
474
474
  "revision": "main"
475
475
  },
476
- "contentDigest": "9c2920ba1dd60e6546831d5c5e0d32c2ce620cfd81cbb6b336c19dde223cacbb",
476
+ "contentDigest": "1c5c3dd92653706d7ca78926f10dbf266e4be4a807e6759ea6e8560b3be72f24",
477
477
  "digestExcludes": []
478
478
  },
479
479
  {
@@ -486,7 +486,7 @@
486
486
  "repository": "1aboveio/ai",
487
487
  "revision": "main"
488
488
  },
489
- "contentDigest": "4b85a0b25eec4594b40d95f41307ae1960b57d648df6e032cc0c780187432805",
489
+ "contentDigest": "d4f589fdb746755388a47eef8d34670a631584fac2bd32af1550da741434b4d6",
490
490
  "digestExcludes": []
491
491
  },
492
492
  {
@@ -499,7 +499,7 @@
499
499
  "repository": "1aboveio/ai",
500
500
  "revision": "main"
501
501
  },
502
- "contentDigest": "ee9204746c76d9734873b6f4953c2bf4854d01544bdd8212fe93df80d51e1c28",
502
+ "contentDigest": "d8b6513b1259b3b91f81c4ac9e9ebe66c2c3735534326ee793b99dc305dc3897",
503
503
  "digestExcludes": []
504
504
  },
505
505
  {
@@ -512,13 +512,13 @@
512
512
  "repository": "1aboveio/ai",
513
513
  "revision": "main"
514
514
  },
515
- "contentDigest": "1924382a6facdc4ad20cf9a70ee608c33c9d3dcba0babc2c1ed900876f6f1bb2",
515
+ "contentDigest": "bf49dc85c84e81e6561b6682003709bb792f4309a295cddad2c5e97e5e85f5b1",
516
516
  "digestExcludes": [
517
517
  "coherence/workflow.json"
518
518
  ]
519
519
  }
520
520
  ],
521
- "releaseIdentity": "931e08c86e3cec4a44ba5568386488328de64187ae452e1db2a952043068d0f6",
521
+ "releaseIdentity": "a7f5ec670bb7b0f8dcfb585c5de1d5cfd0bd55799d72e994034674c7c84f5c40",
522
522
  "lifecycleAuthority": "native-skills-cli",
523
523
  "repairRecipe": {
524
524
  "id": "engineering-workflow-dependency-first",
@@ -2,7 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join } from 'node:path';
5
+ import { dirname, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
@@ -46,6 +46,8 @@ function writeCache(path, value) {
46
46
 
47
47
  function main(now = Date.now()) {
48
48
  if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
49
+ const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
50
+ if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
49
51
  const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
50
52
  const name = frontmatterValue(skill, 'name');
51
53
  const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
@@ -0,0 +1,191 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs';
3
+ import { isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { isDeepStrictEqual } from 'node:util';
5
+ import { contentDigest, contentFiles, firstPartySource } from './workflow-coherence.mjs';
6
+ import { reviewedSourceInjections } from './invocation-policy.mjs';
7
+ import { isMainModule } from './main-module.mjs';
8
+
9
+ export const NATIVE_METADATA_PATH = '.codex-plugin/resources.json';
10
+ export const NATIVE_PLUGIN_NAME = '1aboveio-skills';
11
+ const SHA256 = /^[a-f0-9]{64}$/;
12
+ const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
13
+ export const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
14
+
15
+ function freeze(value) {
16
+ if (value && typeof value === 'object') {
17
+ for (const child of Object.values(value)) freeze(child);
18
+ Object.freeze(value);
19
+ }
20
+ return value;
21
+ }
22
+
23
+ export function containedPath(root, path) {
24
+ if (!isAbsolute(root) || typeof path !== 'string' || !path || isAbsolute(path)
25
+ || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..')) {
26
+ throw new Error('Invalid installation root or relative resource path');
27
+ }
28
+ const target = resolve(root, path);
29
+ if (relative(root, target).startsWith('..')) throw new Error('Resource escapes selected installation');
30
+ // Reject links at every component, including the root. No cache or shared-store fallback.
31
+ if (realpathSync(root) !== root) throw new Error('Selected installation root changed or is ambiguous');
32
+ let current = root;
33
+ for (const part of path.split('/')) {
34
+ current = join(current, part);
35
+ if (lstatSync(current).isSymbolicLink()) throw new Error(`Resource symlink is not supported: ${path}`);
36
+ }
37
+ return target;
38
+ }
39
+
40
+ export function metadataIdentity(metadata) {
41
+ const { releaseIdentity, ...body } = metadata;
42
+ return sha256(JSON.stringify(body));
43
+ }
44
+
45
+ export function validateResourceMetadata(metadata) {
46
+ if (metadata?.schemaVersion !== 1 || metadata.pluginName !== NATIVE_PLUGIN_NAME
47
+ || metadata.packageName !== '@1aboveio/skills' || !SHA256.test(metadata.releaseIdentity ?? '')
48
+ || metadataIdentity(metadata) !== metadata.releaseIdentity || !Array.isArray(metadata.members)
49
+ || metadata.members.length === 0) throw new Error('Invalid native resource metadata identity');
50
+ const names = new Set();
51
+ const paths = new Set();
52
+ for (const member of metadata.members) {
53
+ if (!NAME.test(member.canonicalName ?? '') || names.has(member.canonicalName)
54
+ || !SHA256.test(member.contentDigest ?? '') || !member.expectedSource
55
+ || !Array.isArray(member.digestExcludes) || !member.resourceDigests
56
+ || (firstPartySource(member.expectedSource) && !SHA256.test(member.resourceDigests['SKILL.md'] ?? ''))) throw new Error('Invalid or duplicate resource member');
57
+ names.add(member.canonicalName);
58
+ const source = member.expectedSource;
59
+ if (firstPartySource(source)) {
60
+ if (source.id !== 'first-party' || source.repository !== '1aboveio/ai' || source.revision !== 'main'
61
+ || !NAME.test(member.packageId ?? '') || member.bundlePath !== `skills/${member.packageId}/${member.canonicalName}`
62
+ || paths.has(member.bundlePath)) throw new Error('Invalid first-party resource ownership');
63
+ paths.add(member.bundlePath);
64
+ } else if (source.type !== 'external' || !SHA256.test(member.contentDigest)
65
+ || !/^[a-f0-9]{40}$/.test(source.revision ?? '') || !source.repository) {
66
+ throw new Error('Invalid pinned external identity');
67
+ }
68
+ }
69
+ return metadata;
70
+ }
71
+
72
+ function readPinnedMetadata(root, path, digest, identity) {
73
+ const bytes = readFileSync(containedPath(root, path));
74
+ if (sha256(bytes) !== digest) throw new Error('Resource metadata digest mismatch');
75
+ const metadata = validateResourceMetadata(JSON.parse(bytes));
76
+ if (metadata.releaseIdentity !== identity) throw new Error('Selected release identity mismatch');
77
+ return metadata;
78
+ }
79
+
80
+ // Callers obtain these pins from a verified artifact or trusted generation, not the mutable
81
+ // package group index. The JSON descriptor is safe to persist in a run and pass to workers.
82
+ export function selectInstallation({ layout, root, metadataPath, metadataDigest, releaseIdentity, externalRoots = {} }) {
83
+ if (!['source', 'installed', 'bundle'].includes(layout) || typeof root !== 'string' || !isAbsolute(root)
84
+ || !SHA256.test(metadataDigest ?? '') || !SHA256.test(releaseIdentity ?? '')) {
85
+ throw new Error('Select one absolute installation root and expected metadata/release identities');
86
+ }
87
+ const canonicalRoot = realpathSync(root);
88
+ if (canonicalRoot !== root) throw new Error('Selected installation root changed or is ambiguous');
89
+ const path = metadataPath ?? (layout === 'bundle' ? NATIVE_METADATA_PATH : null);
90
+ const metadata = readPinnedMetadata(canonicalRoot, path, metadataDigest, releaseIdentity);
91
+ const roots = {};
92
+ for (const [name, mapping] of Object.entries(externalRoots)) {
93
+ const member = metadata.members.find((candidate) => candidate.canonicalName === name);
94
+ if (!member || firstPartySource(member.expectedSource) || !isAbsolute(mapping?.root ?? '')
95
+ || !isDeepStrictEqual(mapping.expectedSource, member.expectedSource)) {
96
+ throw new Error(`Unknown or unpinned external root: ${name}`);
97
+ }
98
+ const externalRoot = realpathSync(mapping.root);
99
+ if (externalRoot !== mapping.root || externalRoot === canonicalRoot
100
+ || Object.values(roots).some((entry) => entry.root === externalRoot)) {
101
+ throw new Error(`Ambiguous external root: ${name}`);
102
+ }
103
+ roots[name] = { root: externalRoot, expectedSource: structuredClone(mapping.expectedSource) };
104
+ }
105
+ return freeze({ layout, root: canonicalRoot, metadataPath: path, metadataDigest, releaseIdentity, externalRoots: roots });
106
+ }
107
+
108
+ export function installationMetadata(installation) {
109
+ const selected = selectInstallation(installation);
110
+ return freeze(readPinnedMetadata(selected.root, selected.metadataPath, selected.metadataDigest, selected.releaseIdentity));
111
+ }
112
+
113
+ export function memberPath(installation, member) {
114
+ if (!firstPartySource(member.expectedSource)) {
115
+ const mapping = installation.externalRoots?.[member.canonicalName];
116
+ if (!mapping || !isDeepStrictEqual(mapping.expectedSource, member.expectedSource)) {
117
+ throw new Error(`Missing pinned external dependency: ${member.canonicalName}`);
118
+ }
119
+ // The explicit external root is the member itself, not a directory to search.
120
+ containedPath(mapping.root, 'SKILL.md');
121
+ return mapping.root;
122
+ }
123
+ return containedPath(installation.root, installation.layout === 'bundle' ? member.bundlePath
124
+ : installation.layout === 'installed' ? member.canonicalName : member.sourcePath);
125
+ }
126
+
127
+ export function resolveResource(installation, canonicalName, resource = 'SKILL.md', { hostIdentity = null } = {}) {
128
+ const selected = selectInstallation(installation);
129
+ const metadata = installationMetadata(selected);
130
+ const member = metadata.members.find((candidate) => candidate.canonicalName === canonicalName);
131
+ if (!member) throw new Error(`Unknown canonical skill: ${canonicalName}`);
132
+ if (firstPartySource(member.expectedSource) && !Object.hasOwn(member.resourceDigests, resource)) {
133
+ throw new Error(`Unknown resource: ${canonicalName}/${resource}`);
134
+ }
135
+ const directory = memberPath(selected, member);
136
+ let injected = [];
137
+ if (!firstPartySource(member.expectedSource) && member.invocationPolicy) {
138
+ try { injected = reviewedSourceInjections(directory, member.invocationPolicy); } catch { /* Raw upstream is also allowed. */ }
139
+ }
140
+ const digestOptions = { exclude: member.digestExcludes, published: firstPartySource(member.expectedSource), injected };
141
+ const actual = contentDigest(directory, digestOptions);
142
+ if (actual !== member.contentDigest) throw new Error(`Modified skill resources: ${canonicalName}`);
143
+ const pinned = Object.hasOwn(member.resourceDigests, resource);
144
+ if (!pinned && !contentFiles(directory, digestOptions).some((file) => file.relativePath === resource)) {
145
+ throw new Error(`Unverified resource: ${canonicalName}/${resource}`);
146
+ }
147
+ const path = containedPath(directory, resource);
148
+ const actualResourceDigest = sha256(readFileSync(path));
149
+ if (pinned && actualResourceDigest !== member.resourceDigests[resource]) {
150
+ throw new Error(`Modified resource: ${canonicalName}/${resource}`);
151
+ }
152
+ return freeze({ canonicalName, hostIdentity, path, directory, expectedSource: structuredClone(member.expectedSource),
153
+ contentDigest: actual, resourceDigest: actualResourceDigest, releaseIdentity: selected.releaseIdentity, installation: selected });
154
+ }
155
+
156
+ export function workerResources(installation, requests) {
157
+ if (!Array.isArray(requests) || requests.length === 0) throw new Error('Worker resources must be explicit');
158
+ return freeze(requests.map(({ canonicalName, resource, hostIdentity }) =>
159
+ resolveResource(installation, canonicalName, resource, { hostIdentity })));
160
+ }
161
+
162
+ export function diagnoseOverlap(installation, sharedRoot) {
163
+ if (!isAbsolute(sharedRoot ?? '')) throw new Error('Overlap diagnosis needs an explicit shared root');
164
+ return installationMetadata(installation).members.filter((member) => firstPartySource(member.expectedSource)
165
+ && existsSync(join(sharedRoot, member.canonicalName, 'SKILL.md')))
166
+ .map((member) => ({ canonicalName: member.canonicalName, selected: memberPath(installation, member),
167
+ shared: join(sharedRoot, member.canonicalName), requiresExplicitSelection: true }));
168
+ }
169
+
170
+ // Identify only the fixed bundle ancestor of a known executing skill. Never scan caches.
171
+ export function executingBundleRoot(skillDirectory) {
172
+ const root = resolve(skillDirectory, '../../..');
173
+ try {
174
+ const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
175
+ if (pkg.name === '@1aboveio/skills' && existsSync(join(root, NATIVE_METADATA_PATH))
176
+ && relative(root, skillDirectory).startsWith('skills/')) return root;
177
+ } catch { /* Source and flat installations have no bundle marker. */ }
178
+ return null;
179
+ }
180
+
181
+ if (isMainModule(import.meta.url)) {
182
+ try {
183
+ const [command, descriptor, name, resource] = process.argv.slice(2);
184
+ const selected = JSON.parse(readFileSync(descriptor, 'utf8'));
185
+ const result = command === 'select' ? selectInstallation(selected)
186
+ : command === 'resolve' ? resolveResource(selected, name, resource)
187
+ : command === 'overlap' ? diagnoseOverlap(selected, name) : null;
188
+ if (!result) throw new Error('Usage: installation-resolver.mjs <select|resolve|overlap> <descriptor.json> [canonical-name|shared-root] [resource]');
189
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
190
+ } catch (error) { process.stderr.write(`${error.message}\n`); process.exitCode = 1; }
191
+ }
@@ -3,6 +3,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
3
3
  import { isAbsolute, join, normalize, relative } from 'node:path';
4
4
  import { isDeepStrictEqual } from 'node:util';
5
5
 
6
+ import { containedPath, resolveResource, selectInstallation } from './installation-resolver.mjs';
6
7
  import { reviewedSourceInjections } from './invocation-policy.mjs';
7
8
  import { isMainModule } from './main-module.mjs';
8
9
  import {
@@ -190,6 +191,7 @@ function collectFiles(baseDir, currentDir, excludedPaths, files, published) {
190
191
  }
191
192
 
192
193
  /**
194
+ * List the exact digest-covered files, including reviewed replacements for invocation overlays.
193
195
  * @param {object} [options]
194
196
  * @param {string[]} [options.exclude] member-relative paths to omit
195
197
  * @param {object[]} [options.injected] generated content to digest instead of reading from disk
@@ -198,13 +200,17 @@ function collectFiles(baseDir, currentDir, excludedPaths, files, published) {
198
200
  * published release; left off for external members, whose authority is the reviewed upstream tree
199
201
  * byte for byte. See {@link publishedContentExcluded}.
200
202
  */
201
- export function contentDigest(directory, { exclude = [], injected = [], published = false } = {}) {
203
+ export function contentFiles(directory, { exclude = [], injected = [], published = false } = {}) {
202
204
  const files = [];
203
205
  const excluded = new Set(exclude);
204
206
  for (const file of injected) excluded.add(file.relativePath);
205
207
  collectFiles(directory, directory, excluded, files, published);
206
208
  for (const file of injected) files.push({ relativePath: file.relativePath, contents: file.contents });
207
- files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
209
+ return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
210
+ }
211
+
212
+ export function contentDigest(directory, options = {}) {
213
+ const files = contentFiles(directory, options);
208
214
  const hash = createHash('sha256');
209
215
  for (const file of files) {
210
216
  hash.update(file.relativePath);
@@ -354,9 +360,10 @@ function validateManifest(manifest) {
354
360
  }
355
361
 
356
362
  function memberDirectory(member, options) {
357
- if (options.layout === 'installed') return join(options.skillsRoot, member.installName);
363
+ if (options.installation) return resolveResource(options.installation, member.installName).directory;
364
+ if (options.layout === 'installed') return containedPath(options.skillsRoot, member.installName);
358
365
  const sourceRoot = options.sourceRoots?.[member.expectedSource.id];
359
- return sourceRoot ? join(sourceRoot, member.sourcePath) : null;
366
+ return sourceRoot ? containedPath(sourceRoot, member.sourcePath) : null;
360
367
  }
361
368
 
362
369
  function missingManifestResult(layout, code, detail, invokedSkills) {
@@ -380,11 +387,32 @@ function missingManifestResult(layout, code, detail, invokedSkills) {
380
387
  }
381
388
 
382
389
  export function verifyWorkflowCoherence(options = {}) {
390
+ let result;
391
+ try {
392
+ const installation = options.installation ? selectInstallation(options.installation) : null;
393
+ result = verifySelectedWorkflow({ ...options, ...(installation ? { installation, layout: installation.layout } : {}) });
394
+ } catch (error) {
395
+ result = missingManifestResult(options.installation?.layout ?? options.layout ?? 'installed',
396
+ 'installation-invalid', error.message, options.invokedSkills);
397
+ }
398
+ if (options.installation?.layout === 'bundle') {
399
+ result.repairRecipe = null;
400
+ for (const entry of result.findings) entry.repairRecipe = null;
401
+ }
402
+ return result;
403
+ }
404
+
405
+ function verifySelectedWorkflow(options = {}) {
383
406
  const layout = options.layout ?? 'installed';
384
- if (!['installed', 'source'].includes(layout)) {
407
+ if (!['installed', 'source', 'bundle'].includes(layout) || layout === 'bundle' && !options.installation) {
385
408
  return missingManifestResult(layout, 'layout-invalid', 'The requested installation layout is unsupported.', options.invokedSkills);
386
409
  }
387
- const manifestPath = options.manifestPath ?? (layout === 'installed'
410
+ const selectedManifest = options.installation
411
+ ? resolveResource(options.installation, 'engineering-runtime', 'coherence/workflow.json').path : null;
412
+ if (selectedManifest && options.manifestPath && options.manifestPath !== selectedManifest) {
413
+ throw new Error('A selected installation cannot use another coherence manifest');
414
+ }
415
+ const manifestPath = selectedManifest ?? options.manifestPath ?? (layout === 'installed'
388
416
  ? join(options.skillsRoot ?? '', 'engineering-runtime', 'coherence', 'workflow.json')
389
417
  : join(options.sourceRoots?.['first-party'] ?? '', WORKFLOW_COHERENCE_PATH));
390
418
  if (!manifestPath || !existsSync(manifestPath)) {
@@ -451,7 +479,14 @@ export function verifyWorkflowCoherence(options = {}) {
451
479
 
452
480
  const selectedMembers = manifest.members.filter((member) => selectedNames.has(member.installName));
453
481
  for (const member of selectedMembers) {
454
- const directory = memberDirectory(member, { ...options, layout });
482
+ let directory;
483
+ try { directory = memberDirectory(member, { ...options, layout }); } catch (error) {
484
+ if (error.code !== 'ENOENT') {
485
+ findings.push(finding({ state: 'missing', code: 'member-resolution-invalid', member: member.installName,
486
+ ownership: member.ownership, expectedSource: member.expectedSource, manifest, detail: error.message }));
487
+ continue;
488
+ }
489
+ }
455
490
  if (!directory || !existsSync(directory)) {
456
491
  findings.push(finding({
457
492
  state: 'missing',
@@ -475,7 +510,7 @@ export function verifyWorkflowCoherence(options = {}) {
475
510
  // proved `expectedSource` deep-equal to the trusted policy above (`member-source-identity-
476
511
  // invalid` returns before this loop), so the derivation reads trusted data, not manifest claims.
477
512
  let policyInjections = [];
478
- if (layout === 'installed' && member.invocationPolicy) {
513
+ if (layout !== 'source' && member.invocationPolicy) {
479
514
  try {
480
515
  policyInjections = reviewedSourceInjections(directory, member.invocationPolicy);
481
516
  } catch {
@@ -520,7 +555,8 @@ export function verifyWorkflowCoherence(options = {}) {
520
555
  const memberByName = new Map(manifest.members.map((member) => [member.installName, member]));
521
556
  for (const identity of selectedGenerated.values()) {
522
557
  const member = memberByName.get(identity.member);
523
- const directory = member ? memberDirectory(member, { ...options, layout }) : null;
558
+ let directory = null;
559
+ try { directory = member ? memberDirectory(member, { ...options, layout }) : null; } catch { /* Report missing identity below. */ }
524
560
  const generatedPath = directory ? join(directory, identity.path) : null;
525
561
  let actualDigest = null;
526
562
  if (generatedPath && existsSync(generatedPath)) {
@@ -555,7 +591,8 @@ export function verifyWorkflowCoherence(options = {}) {
555
591
  export function runWorkflowCoherenceCli(args) {
556
592
  if (args[0] !== 'verify') {
557
593
  process.stderr.write(
558
- 'Usage: workflow-coherence.mjs verify --layout installed --skills-root <path> [--manifest <path>] [--invoked-skill <name>]...\n'
594
+ 'Usage: workflow-coherence.mjs verify --installation <descriptor.json> [--invoked-skill <canonical-name>]...\n'
595
+ + ' or: workflow-coherence.mjs verify --layout installed --skills-root <path> [--manifest <path>] [--invoked-skill <name>]...\n'
559
596
  + ' or: workflow-coherence.mjs verify --layout source --source-root <id=path>... [--manifest <path>] [--invoked-skill <name>]...\n',
560
597
  );
561
598
  return 2;
@@ -570,6 +607,10 @@ export function runWorkflowCoherenceCli(args) {
570
607
  const key = argument === '--layout' ? 'layout' : argument === '--skills-root' ? 'skillsRoot' : 'manifestPath';
571
608
  options[key] = value;
572
609
  index += 1;
610
+ } else if (argument === '--installation') {
611
+ if (!value) return 2;
612
+ try { options.installation = JSON.parse(readFileSync(value, 'utf8')); } catch { return 2; }
613
+ index += 1;
573
614
  } else if (argument === '--source-root') {
574
615
  const separator = value?.indexOf('=') ?? -1;
575
616
  if (separator < 1 || separator === value.length - 1) return 2;