@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.35 → 0.1.37

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 (84) hide show
  1. package/bin/adl +2 -2
  2. package/package.json +1 -1
  3. package/packages/agent-hooks/deploy-profile.json +1 -1
  4. package/packages/agent-hooks/src/pre-tool-use/handler.ts +2 -2
  5. package/packages/agent-hooks/src/rules/impl/adl-build-run-role.ts +116 -0
  6. package/packages/agent-hooks/src/rules/impl/adl-git-user-override-block.ts +159 -0
  7. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +12 -6
  8. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +9 -9
  9. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +6 -5
  10. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +15 -14
  11. package/packages/agent-hooks/src/rules/impl/git-core.ts +4 -8
  12. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +1 -1
  13. package/packages/agent-hooks/src/rules/registry.ts +11 -2
  14. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +8 -4
  15. package/packages/agent-skills/assets/code-review/SKILL.md +84 -66
  16. package/packages/agent-skills/assets/diagnosing-bugs/SKILL.md +23 -2
  17. package/packages/agent-skills/assets/diagnosing-bugs/references/FEEDBACK-LOOP-TACTICS.md +1 -1
  18. package/packages/agent-skills/assets/explore-codebase/SKILL.md +3 -2
  19. package/packages/agent-skills/assets/finalize-feature/SKILL.md +63 -0
  20. package/packages/agent-skills/assets/fix-ci/SKILL.md +58 -0
  21. package/packages/agent-skills/assets/handoff/SKILL.md +1 -1
  22. package/packages/agent-skills/assets/implement-feature/SKILL.md +153 -0
  23. package/packages/agent-skills/assets/implement-feature/references/briefs.md +94 -0
  24. package/packages/agent-skills/assets/implement-item/SKILL.md +71 -0
  25. package/packages/agent-skills/assets/mechanics/SKILL.md +38 -0
  26. package/packages/agent-skills/assets/plan-feature/SKILL.md +141 -0
  27. package/packages/agent-skills/assets/prepare-promotion/SKILL.md +91 -0
  28. package/packages/agent-skills/assets/ralplan/SKILL.md +149 -0
  29. package/packages/agent-skills/assets/refactor-safely/SKILL.md +4 -3
  30. package/packages/agent-skills/assets/report-bug/SKILL.md +72 -0
  31. package/packages/agent-skills/assets/review-change/SKILL.md +82 -0
  32. package/packages/agent-skills/assets/review-plan/SKILL.md +124 -0
  33. package/packages/agent-skills/assets/triage-workitems/SKILL.md +99 -0
  34. package/packages/agent-skills/assets/ultragoal/SKILL.md +77 -0
  35. package/packages/agent-skills/assets/validate-design/SKILL.md +70 -0
  36. package/packages/agent-skills/assets/verify-item/SKILL.md +71 -0
  37. package/packages/agent-skills/assets/workitems-process/SKILL.md +147 -0
  38. package/packages/agent-skills/assets/workitems-process/references/ado-and-git.md +194 -0
  39. package/packages/agent-skills/assets/workitems-process/references/evidence.md +74 -0
  40. package/packages/agent-skills/assets/workitems-process/references/workitem-states.md +92 -0
  41. package/packages/agent-skills/assets/write-feature/SKILL.md +95 -0
  42. package/packages/agent-skills/dist/deploy.d.ts +7 -0
  43. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  44. package/packages/agent-skills/dist/deploy.js +52 -5
  45. package/packages/agent-skills/dist/deploy.js.map +1 -1
  46. package/packages/agent-skills/src/deploy.ts +48 -4
  47. package/packages/azdo/scripts/probe-ado-rules.ts +1 -1
  48. package/packages/azdo/src/activity-log.ts +118 -0
  49. package/packages/azdo/src/azdo-attachments.ts +108 -0
  50. package/packages/azdo/src/azdo-pl.ts +100 -3
  51. package/packages/azdo/src/azdo-pr.ts +194 -11
  52. package/packages/azdo/src/azdo-wit.ts +329 -18
  53. package/packages/azdo/src/cli.ts +42 -2
  54. package/packages/azdo/src/lib/client.ts +7 -1
  55. package/packages/azdo/src/lib/validate-pr-metadata.ts +14 -5
  56. package/packages/azdo/src/mcp-server.ts +53 -5
  57. package/packages/azdo/src/tool-dispatch.ts +53 -4
  58. package/packages/azdo/src/workflow-ladder.csv +125 -107
  59. package/packages/azdo/src/workflow-rules.ts +254 -97
  60. package/packages/repo-controls/README.md +12 -4
  61. package/packages/repo-controls/assets/scaffold/docs/AGENTS.md +5 -5
  62. package/packages/repo-controls/assets/scaffold/plans/AGENTS.md +42 -13
  63. package/packages/repo-controls/assets/scaffold/root/CONTRIBUTING.md +5 -5
  64. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +0 -11
  65. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +4 -6
  66. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +15 -0
  67. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +1 -17
  68. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +3 -22
  69. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +39 -182
  70. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/features/az601-clean/intent.md +17 -0
  71. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +90 -321
  72. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plan_metadata.py +31 -5
  73. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +282 -54
  74. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/design.md +35 -0
  75. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/file-map-reference.md +19 -0
  76. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/intent.md +25 -0
  77. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/items/az351-restore-entrypoint.md +25 -0
  78. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/requirements.md +44 -0
  79. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-violation/plans/features/az347-test-coverage/requirements.md +44 -0
  80. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +646 -53
  81. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +3 -3
  82. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +1 -12
  83. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +1 -1
  84. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +1 -1
@@ -27,5 +27,12 @@ export interface DeploySkillsResult {
27
27
  */
28
28
  readonly retiredRemovals: readonly string[];
29
29
  }
30
+ /**
31
+ * True when `skillDirAbs` holds exactly the asset skill's files — same
32
+ * relative paths, same bytes, nothing extra. Such a folder carries no user
33
+ * content, so deploy may adopt it (Tarun, 2026-10-01).
34
+ */
35
+ export declare function folderIdenticalToAsset(skillDirAbs: string, assetDirAbs: string, sourceFiles: readonly string[]): boolean;
36
+ export declare function removeEmptyParents(root: string, dir: string): void;
30
37
  export declare function deploySkills(options: DeploySkillsOptions): DeploySkillsResult;
31
38
  //# sourceMappingURL=deploy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,cAAc,CAAC;AAI/E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,6KAA6K;IAC7K,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,+JAA+J;IAC/J,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,8JAA8J;IAC9J,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AA+BD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAiK7E"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAiCA,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,cAAc,CAAC;AAI/E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,6KAA6K;IAC7K,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,+JAA+J;IAC/J,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,8JAA8J;IAC9J,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CASxH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAWlE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA2M7E"}
@@ -17,7 +17,9 @@
17
17
  * When `profile.legacyLock` names a lock the OLD repo-controls engine
18
18
  * left behind, a same-name folder it fully wrote (per legacy-lock.ts) is
19
19
  * taken over on sight rather than reported as a conflict — see
20
- * legacy-lock.ts for the exact ownership rule and lock rewrite.
20
+ * legacy-lock.ts for the exact ownership rule and lock rewrite. A legacy
21
+ * folder whose skill retired from assets/ is removed under the same
22
+ * ownership rule (never a hand-modified folder).
21
23
  *
22
24
  * Fails loud: assets/target validation, an invalid deploy profile, an
23
25
  * invalid adl-config.toml subset, or an unwritable target all throw. There
@@ -37,7 +39,7 @@ import { listSourceSkillNames } from "./skill-meta.js";
37
39
  * relative paths, same bytes, nothing extra. Such a folder carries no user
38
40
  * content, so deploy may adopt it (Tarun, 2026-10-01).
39
41
  */
40
- function folderIdenticalToAsset(skillDirAbs, assetDirAbs, sourceFiles) {
42
+ export function folderIdenticalToAsset(skillDirAbs, assetDirAbs, sourceFiles) {
41
43
  let targetFiles;
42
44
  try {
43
45
  targetFiles = walkFiles(skillDirAbs);
@@ -49,7 +51,7 @@ function folderIdenticalToAsset(skillDirAbs, assetDirAbs, sourceFiles) {
49
51
  return false;
50
52
  return sourceFiles.every((file) => Buffer.compare(readFileSync(join(skillDirAbs, file)), readFileSync(join(assetDirAbs, file))) === 0);
51
53
  }
52
- function removeEmptyParents(root, dir) {
54
+ export function removeEmptyParents(root, dir) {
53
55
  let current = dir;
54
56
  while (current !== root && current.startsWith(root)) {
55
57
  if (!existsSync(current)) {
@@ -91,7 +93,45 @@ export function deploySkills(options) {
91
93
  // conflict, with a reason naming what disqualified it.
92
94
  const takeoverFiles = new Set();
93
95
  const takeoverConflictReasons = new Map();
96
+ /** `${folder}/${skill}` in a retired folder that the legacy lock wrote and assets/ retired — removed by the retired-folder cleanup below. */
97
+ const legacyRetiredInRetired = new Set();
94
98
  if (legacyLock !== null) {
99
+ // Legacy retirement: a folder the old engine fully wrote (per the
100
+ // lock) whose skill no longer ships in assets/ is folded into
101
+ // takeoverFiles so the removal pass below deletes it — the header's
102
+ // "dropped from assets/ is removed" promise extended to old-engine
103
+ // folders. A repo-subset exclusion keeps the folder (the subset only
104
+ // limits what THIS deploy writes, it does not retract skills assets
105
+ // still ships). A folder failing the ownership check (hand-modified,
106
+ // extra file) is left untouched: it is becoming user-owned, the same
107
+ // disposition as the takeover conflict case. In retired folders there
108
+ // is no takeover pass: the folder is only recorded, and the
109
+ // retired-folder cleanup removes it under the same conditions.
110
+ const legacyRetired = (folder, foldIntoTakeover) => {
111
+ const prefix = `${folder}/`;
112
+ for (const skill of new Set([...legacyLock.files.keys()]
113
+ .filter((relPath) => relPath.startsWith(prefix))
114
+ .map((relPath) => relPath.slice(prefix.length).split("/", 1)[0]))) {
115
+ if (availableSkills.includes(skill))
116
+ continue; // still shipped — selected or subset path owns it
117
+ const skillDirRel = `${folder}/${skill}`;
118
+ const skillDirAbs = join(targetRepo, skillDirRel);
119
+ if (!existsSync(skillDirAbs))
120
+ continue;
121
+ if (manifest.files.some((file) => file.startsWith(`${skillDirRel}/`)))
122
+ continue; // already ours
123
+ const check = checkLegacyOwnership(legacyLock, targetRepo, skillDirRel, skillDirAbs);
124
+ if (!check.owned)
125
+ continue;
126
+ if (foldIntoTakeover) {
127
+ for (const relFile of walkFiles(skillDirAbs))
128
+ takeoverFiles.add(`${skillDirRel}/${relFile}`);
129
+ }
130
+ else {
131
+ legacyRetiredInRetired.add(skillDirRel);
132
+ }
133
+ }
134
+ };
95
135
  for (const surface of profile.surfaces) {
96
136
  for (const skill of selectedSkills) {
97
137
  const skillDirRel = `${surface.folder}/${skill}`;
@@ -109,7 +149,10 @@ export function deploySkills(options) {
109
149
  takeoverConflictReasons.set(skillDirRel, check.reason);
110
150
  }
111
151
  }
152
+ legacyRetired(surface.folder, true);
112
153
  }
154
+ for (const folder of retiredFolders(profile))
155
+ legacyRetired(folder, false);
113
156
  }
114
157
  const manifestFiles = new Set([...manifest.files, ...takeoverFiles]);
115
158
  const adopted = [];
@@ -195,8 +238,12 @@ export function deploySkills(options) {
195
238
  if (!existsSync(folderAbs))
196
239
  continue;
197
240
  for (const name of directoryNames(folderAbs)) {
198
- if (!availableSkillSet.has(name))
199
- continue; // not ours to manage — left byte-for-byte
241
+ // Ours to remove when the skill still ships in assets/ (a stale
242
+ // duplicate of a live skill) or the legacy lock proves the old
243
+ // engine wrote this copy of a now-retired skill. Anything else was
244
+ // never this package's to manage — left byte-for-byte.
245
+ if (!availableSkillSet.has(name) && !legacyRetiredInRetired.has(`${folder}/${name}`))
246
+ continue;
200
247
  if (!dryRun)
201
248
  removeDirRecursive(join(folderAbs, name));
202
249
  retiredRemovals.push(`${folder}/${name}`);
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC3H,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAsB,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAiCvD;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,WAAmB,EAAE,WAAmB,EAAE,WAA8B;IACtG,IAAI,WAA8B,CAAC;IACnC,IAAI,CAAC;QACH,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,oGAAoG;IACpH,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACtH,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACzI,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,GAAW;IACnD,IAAI,OAAO,GAAG,GAAG,CAAC;IAClB,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO;QAC5C,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IAEvC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,4CAA4C,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,qCAAqC,UAAU,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,WAAW,EAAE,CAAC;IACjD,MAAM,eAAe,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,SAAS,EAAE,CAAC,CAAC;IAE/G,MAAM,SAAS,GAAG,qBAAqB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IACrE,IAAI,CAAC,SAAS,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE7D,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAElE,0EAA0E;IAC1E,oEAAoE;IACpE,uEAAuE;IACvE,uEAAuE;IACvE,qEAAqE;IACrE,qEAAqE;IACrE,qEAAqE;IACrE,uDAAuD;IACvD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1D,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACvC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;gBACnC,MAAM,WAAW,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;gBACjD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;gBAClD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;oBAAE,SAAS;gBACvC,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC;oBAAE,SAAS,CAAC,eAAe;gBAEhG,MAAM,KAAK,GAAG,oBAAoB,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;gBACrF,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAChB,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,WAAW,CAAC;wBAAE,aAAa,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,OAAO,EAAE,CAAC,CAAC;gBAC/F,CAAC;qBAAM,CAAC;oBACN,uBAAuB,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAO,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC;IACrE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA6B,CAAC;IAChE,KAAK,MAAM,KAAK,IAAI,cAAc;QAAE,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAErG,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAoB,EAAE,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACvC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;YACjD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAClD,MAAM,cAAc,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;YAC/C,IAAI,WAAW,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC;YAExF,uEAAuE;YACvE,gEAAgE;YAChE,yEAAyE;YACzE,IAAI,cAAc,IAAI,CAAC,WAAW,IAAI,sBAAsB,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBACvI,WAAW,GAAG,IAAI,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC5B,CAAC;YAED,IAAI,cAAc,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnC,SAAS,CAAC,IAAI,CAAC;oBACb,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,KAAK;oBACL,MAAM,EACJ,uBAAuB,CAAC,GAAG,CAAC,WAAW,CAAC;wBACxC,mFAAmF;iBACtF,CAAC,CAAC;gBACH,SAAS,CAAC,6DAA6D;YACzE,CAAC;YAED,KAAK,MAAM,OAAO,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC1D,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,OAAO,EAAE,CAAC;gBAC9C,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACzB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;gBAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC9D,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpH,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC1B,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBACnD,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBACpC,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACpC,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACrC,iEAAiE;QACjE,oEAAoE;QACpE,wCAAwC;QACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;YAAE,SAAS;QAC5F,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACrB,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,MAAM;QAAE,aAAa,CAAC,UAAU,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAEvD,uEAAuE;IACvE,wEAAwE;IACxE,mEAAmE;IACnE,oEAAoE;IACpE,oEAAoE;IACpE,IAAI,CAAC,MAAM,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACnC,MAAM,kBAAkB,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9G,qBAAqB,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACnD,KAAK,MAAM,MAAM,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS;QAErC,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS,CAAC,0CAA0C;YACtF,IAAI,CAAC,MAAM;gBAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;YACvD,eAAe,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5E,SAAS,CAAC,SAAS,CAAC,CAAC;YACrB,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;QACvB,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE;QAC3B,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;QACvB,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrG,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;QACvB,YAAY,EAAE,eAAe,CAAC,UAAU,CAAC;QACzC,eAAe,EAAE,eAAe,CAAC,IAAI,EAAE;KACxC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,9 @@
17
17
  * When `profile.legacyLock` names a lock the OLD repo-controls engine
18
18
  * left behind, a same-name folder it fully wrote (per legacy-lock.ts) is
19
19
  * taken over on sight rather than reported as a conflict — see
20
- * legacy-lock.ts for the exact ownership rule and lock rewrite.
20
+ * legacy-lock.ts for the exact ownership rule and lock rewrite. A legacy
21
+ * folder whose skill retired from assets/ is removed under the same
22
+ * ownership rule (never a hand-modified folder).
21
23
  *
22
24
  * Fails loud: assets/target validation, an invalid deploy profile, an
23
25
  * invalid adl-config.toml subset, or an unwritable target all throw. There
@@ -69,7 +71,7 @@ export interface DeploySkillsResult {
69
71
  * relative paths, same bytes, nothing extra. Such a folder carries no user
70
72
  * content, so deploy may adopt it (Tarun, 2026-10-01).
71
73
  */
72
- function folderIdenticalToAsset(skillDirAbs: string, assetDirAbs: string, sourceFiles: readonly string[]): boolean {
74
+ export function folderIdenticalToAsset(skillDirAbs: string, assetDirAbs: string, sourceFiles: readonly string[]): boolean {
73
75
  let targetFiles: readonly string[];
74
76
  try {
75
77
  targetFiles = walkFiles(skillDirAbs);
@@ -80,7 +82,7 @@ function folderIdenticalToAsset(skillDirAbs: string, assetDirAbs: string, source
80
82
  return sourceFiles.every((file) => Buffer.compare(readFileSync(join(skillDirAbs, file)), readFileSync(join(assetDirAbs, file))) === 0);
81
83
  }
82
84
 
83
- function removeEmptyParents(root: string, dir: string): void {
85
+ export function removeEmptyParents(root: string, dir: string): void {
84
86
  let current = dir;
85
87
  while (current !== root && current.startsWith(root)) {
86
88
  if (!existsSync(current)) {
@@ -125,7 +127,42 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
125
127
  // conflict, with a reason naming what disqualified it.
126
128
  const takeoverFiles = new Set<string>();
127
129
  const takeoverConflictReasons = new Map<string, string>();
130
+ /** `${folder}/${skill}` in a retired folder that the legacy lock wrote and assets/ retired — removed by the retired-folder cleanup below. */
131
+ const legacyRetiredInRetired = new Set<string>();
128
132
  if (legacyLock !== null) {
133
+ // Legacy retirement: a folder the old engine fully wrote (per the
134
+ // lock) whose skill no longer ships in assets/ is folded into
135
+ // takeoverFiles so the removal pass below deletes it — the header's
136
+ // "dropped from assets/ is removed" promise extended to old-engine
137
+ // folders. A repo-subset exclusion keeps the folder (the subset only
138
+ // limits what THIS deploy writes, it does not retract skills assets
139
+ // still ships). A folder failing the ownership check (hand-modified,
140
+ // extra file) is left untouched: it is becoming user-owned, the same
141
+ // disposition as the takeover conflict case. In retired folders there
142
+ // is no takeover pass: the folder is only recorded, and the
143
+ // retired-folder cleanup removes it under the same conditions.
144
+ const legacyRetired = (folder: string, foldIntoTakeover: boolean) => {
145
+ const prefix = `${folder}/`;
146
+ for (const skill of new Set(
147
+ [...legacyLock!.files.keys()]
148
+ .filter((relPath) => relPath.startsWith(prefix))
149
+ .map((relPath) => relPath.slice(prefix.length).split("/", 1)[0]!),
150
+ )) {
151
+ if (availableSkills.includes(skill)) continue; // still shipped — selected or subset path owns it
152
+ const skillDirRel = `${folder}/${skill}`;
153
+ const skillDirAbs = join(targetRepo, skillDirRel);
154
+ if (!existsSync(skillDirAbs)) continue;
155
+ if (manifest.files.some((file) => file.startsWith(`${skillDirRel}/`))) continue; // already ours
156
+
157
+ const check = checkLegacyOwnership(legacyLock!, targetRepo, skillDirRel, skillDirAbs);
158
+ if (!check.owned) continue;
159
+ if (foldIntoTakeover) {
160
+ for (const relFile of walkFiles(skillDirAbs)) takeoverFiles.add(`${skillDirRel}/${relFile}`);
161
+ } else {
162
+ legacyRetiredInRetired.add(skillDirRel);
163
+ }
164
+ }
165
+ };
129
166
  for (const surface of profile.surfaces) {
130
167
  for (const skill of selectedSkills) {
131
168
  const skillDirRel = `${surface.folder}/${skill}`;
@@ -140,7 +177,10 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
140
177
  takeoverConflictReasons.set(skillDirRel, check.reason!);
141
178
  }
142
179
  }
180
+
181
+ legacyRetired(surface.folder, true);
143
182
  }
183
+ for (const folder of retiredFolders(profile)) legacyRetired(folder, false);
144
184
  }
145
185
 
146
186
  const manifestFiles = new Set([...manifest.files, ...takeoverFiles]);
@@ -234,7 +274,11 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
234
274
  if (!existsSync(folderAbs)) continue;
235
275
 
236
276
  for (const name of directoryNames(folderAbs)) {
237
- if (!availableSkillSet.has(name)) continue; // not ours to manage — left byte-for-byte
277
+ // Ours to remove when the skill still ships in assets/ (a stale
278
+ // duplicate of a live skill) or the legacy lock proves the old
279
+ // engine wrote this copy of a now-retired skill. Anything else was
280
+ // never this package's to manage — left byte-for-byte.
281
+ if (!availableSkillSet.has(name) && !legacyRetiredInRetired.has(`${folder}/${name}`)) continue;
238
282
  if (!dryRun) removeDirRecursive(join(folderAbs, name));
239
283
  retiredRemovals.push(`${folder}/${name}`);
240
284
  }
@@ -95,7 +95,7 @@ const MAIN = [
95
95
  "In Planning",
96
96
  "Awaiting Approval",
97
97
  "Approved",
98
- "Spec Validation",
98
+ "Design Validation",
99
99
  "In Implementation",
100
100
  "In Testing",
101
101
  "In Review",
@@ -0,0 +1,118 @@
1
+ /** A1 agent activity log — design: docs/research/adl-process-handover/process-design.md § Activity log. */
2
+ import { DatabaseSync } from 'node:sqlite';
3
+ import { mkdirSync } from 'node:fs';
4
+ import { basename, dirname, join } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { resolveAgentIdentity } from './lib/agent-identity.ts';
7
+
8
+ export interface ActivityEntry {
9
+ time: string;
10
+ surface: string;
11
+ agent: string;
12
+ repo: string;
13
+ note: string;
14
+ }
15
+
16
+ /** Verified child-process surface markers, first match wins; a marker is listed only with a citation to the surface's own docs. */
17
+ const SURFACE_MARKERS: ReadonlyArray<{ readonly surface: string; readonly env: string; readonly equals?: string }> = [
18
+ // Claude Code spawns set CLAUDECODE=1 (code.claude.com/docs/en/env-vars, verified 2026-10-12).
19
+ { surface: 'claude', env: 'CLAUDECODE', equals: '1' },
20
+ ];
21
+
22
+ /** The surface this process runs under: first verified marker match, else 'cli' (no marker is not an error). */
23
+ export function resolveSurface(env: NodeJS.ProcessEnv = process.env): string {
24
+ for (const marker of SURFACE_MARKERS) {
25
+ const value = env[marker.env];
26
+ if (value !== undefined && (marker.equals === undefined || value === marker.equals)) {
27
+ return marker.surface;
28
+ }
29
+ }
30
+ return 'cli';
31
+ }
32
+
33
+ /** The git work-tree top level of `cwd` (or the process's own), or a loud refusal outside a work tree — the log lives at `<repo>/.adl/agent-activity.sqlite`. */
34
+ export function repoTopLevel(cwd?: string): string {
35
+ const result = spawnSync('git', ['rev-parse', '--show-toplevel'], {
36
+ ...(cwd === undefined ? {} : { cwd }),
37
+ encoding: 'utf8',
38
+ timeout: 5000,
39
+ });
40
+ const topLevel = result.status === 0 ? result.stdout.trim() : '';
41
+ if (!topLevel) {
42
+ throw new Error(
43
+ 'the activity log lives at <repo>/.adl/agent-activity.sqlite, and this working directory is not a git work tree',
44
+ );
45
+ }
46
+ return topLevel;
47
+ }
48
+
49
+ /** The ONE place the log's path is built. */
50
+ export function activityDbPath(cwd?: string): string {
51
+ return join(repoTopLevel(cwd), '.adl', 'agent-activity.sqlite');
52
+ }
53
+
54
+ function openDb(cwd?: string): DatabaseSync {
55
+ const path = activityDbPath(cwd);
56
+ mkdirSync(dirname(path), { recursive: true });
57
+ const db = new DatabaseSync(path);
58
+ db.exec(
59
+ `CREATE TABLE IF NOT EXISTS activity (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ time TEXT NOT NULL,
62
+ surface TEXT NOT NULL,
63
+ agent TEXT NOT NULL,
64
+ repo TEXT NOT NULL,
65
+ note TEXT NOT NULL
66
+ )`,
67
+ );
68
+ return db;
69
+ }
70
+
71
+ function assertNote(note: unknown): asserts note is string {
72
+ if (typeof note !== 'string' || note.trim() === '') {
73
+ throw new Error('note must be a non-empty string');
74
+ }
75
+ }
76
+
77
+ /** Append one entry, filling time/surface/agent/repo; `env`/`now` keep tests hermetic. Returns the stored entry as JSON. */
78
+ export function activityAdd(
79
+ note: unknown,
80
+ opts: { cwd?: string; env?: NodeJS.ProcessEnv; now?: Date } = {},
81
+ ): string {
82
+ assertNote(note);
83
+ const env = opts.env ?? process.env;
84
+ const entry: ActivityEntry = {
85
+ time: (opts.now ?? new Date()).toISOString(),
86
+ surface: resolveSurface(env),
87
+ agent: resolveAgentIdentity(env).agent,
88
+ repo: basename(repoTopLevel(opts.cwd)),
89
+ note,
90
+ };
91
+ const db = openDb(opts.cwd);
92
+ try {
93
+ db.prepare('INSERT INTO activity (time, surface, agent, repo, note) VALUES (?, ?, ?, ?, ?)').run(
94
+ entry.time,
95
+ entry.surface,
96
+ entry.agent,
97
+ entry.repo,
98
+ entry.note,
99
+ );
100
+ } finally {
101
+ db.close();
102
+ }
103
+ return JSON.stringify(entry, null, 2);
104
+ }
105
+
106
+ /** Every entry, oldest first (the reading order of the ledger this replaces). */
107
+ export function activityList(opts: { cwd?: string } = {}): string {
108
+ const db = openDb(opts.cwd);
109
+ let entries: unknown[];
110
+ try {
111
+ entries = db
112
+ .prepare('SELECT time, surface, agent, repo, note FROM activity ORDER BY id ASC')
113
+ .all();
114
+ } finally {
115
+ db.close();
116
+ }
117
+ return JSON.stringify({ repo: basename(repoTopLevel(opts.cwd)), entries }, null, 2);
118
+ }
@@ -0,0 +1,108 @@
1
+ // T2 work-item attachments (board-rules.md § Attachments): Features only, frozen once attached — the one delete allowed is a mistaken upload in its own run, for a plan-r* file while every Feature item is still at or before Awaiting Approval. API facts (WIT REST 7.1): upload POSTs .../_apis/wit/attachments?fileName={name} (octet-stream, returns { id, url }); attach PATCHes the work item adding an AttachedFile relation (test/rev gives optimistic concurrency); Attachments-Delete is 7.2-preview only, so delete detaches and the stored file stays.
2
+ import { AdoClient } from './lib/client.ts';
3
+
4
+ let client: AdoClient | undefined
5
+
6
+ async function getClient(): Promise<AdoClient> {
7
+ client ??= new AdoClient()
8
+ return client
9
+ }
10
+
11
+ /** Test seam: forget the cached client — see azdo-wit.ts's resetClientForTests() for why. */
12
+ export function resetClientForTests(): void {
13
+ client = undefined
14
+ }
15
+
16
+ export type WorkitemAttachmentsArgs = {
17
+ id: number;
18
+ action: 'read' | 'upload' | 'delete';
19
+ /** upload/delete: the attachment file name. */
20
+ fileName?: string;
21
+ /** upload: the file content, sent as an octet-stream body. */
22
+ content?: string;
23
+ };
24
+
25
+ interface AttachmentRelation {
26
+ index: number;
27
+ name: string;
28
+ url: string;
29
+ }
30
+
31
+ function attachedFiles(item: any): AttachmentRelation[] {
32
+ return (item.relations ?? [])
33
+ .map((relation: any, index: number) => ({ relation, index }))
34
+ .filter(({ relation }: any) => relation.rel === 'AttachedFile')
35
+ .map(({ relation, index }: any) => ({
36
+ index,
37
+ name: relation.attributes?.name ?? relation.url,
38
+ url: relation.url,
39
+ }));
40
+ }
41
+
42
+ /** P4 reuses this upload when it attaches the mandatory plan/impl report at PR raise. */
43
+ export async function uploadAttachment(c: AdoClient, workItemId: number, fileName: string, content: string): Promise<void> {
44
+ const uploaded = await c.post<any>(
45
+ '/wit/attachments',
46
+ content,
47
+ { fileName, 'api-version': '7.1' },
48
+ 'application/octet-stream',
49
+ );
50
+ if (!uploaded?.url) throw new Error(`attachment upload for '${fileName}' returned no url`);
51
+ const item = await c.get<any>(`/wit/workitems/${workItemId}`, { 'api-version': '7.1' });
52
+ await c.patch<any>(
53
+ `/wit/workitems/${workItemId}`,
54
+ [
55
+ { op: 'test', path: '/rev', value: item.rev ?? item.fields?.['System.Rev'] },
56
+ { op: 'add', path: '/relations/-', value: { rel: 'AttachedFile', url: uploaded.url } },
57
+ ],
58
+ { 'api-version': '7.1' },
59
+ 'application/json-patch+json',
60
+ );
61
+ }
62
+
63
+ /** The T2 guard (board-rules.md § Attachments): a plan-r* file refuses deletion once any item of its Feature has left Awaiting Approval — the plan G1 approved stays on the board. */
64
+ async function assertPlanAttachmentDeletable(c: AdoClient, feature: any, fileName: string): Promise<void> {
65
+ const relationId = (url: string) => Number(/(\d+)\/?$/.exec(url)?.[1]);
66
+ const childIds = (feature.relations ?? [])
67
+ .filter((relation: any) => relation.rel === 'System.LinkTypes.Hierarchy-Forward')
68
+ .map((relation: any) => relationId(relation.url))
69
+ .filter((id: number) => Number.isInteger(id));
70
+ if (!childIds.length) return;
71
+ const children = await c.post<any>('/wit/workitemsbatch', { ids: childIds, $expand: 'all' }, { 'api-version': '7.1' });
72
+ const left = (children.value ?? []).find((child: any) => !['New', 'In Planning', 'Awaiting Approval'].includes(child.fields?.['System.State']));
73
+ if (left) {
74
+ throw new Error(`refusing to delete '${fileName}': an item of Feature ${feature.id} has left Awaiting Approval (item ${left.id} is at ${left.fields?.['System.State']}) — the approved plan stays on the board`);
75
+ }
76
+ }
77
+
78
+ export async function workitemAttachments(args: WorkitemAttachmentsArgs): Promise<string> {
79
+ const c = await getClient();
80
+ if (args.action === 'upload') {
81
+ if (!args.fileName) throw new Error('fileName is required for upload');
82
+ if (args.content === undefined || args.content === '') throw new Error('content is required for upload');
83
+ await uploadAttachment(c, args.id, args.fileName, args.content);
84
+ return JSON.stringify({ id: args.id, attached: args.fileName }, null, 2);
85
+ }
86
+ const item = await c.get<any>(`/wit/workitems/${args.id}`, { 'api-version': '7.1', $expand: 'all' });
87
+ const files = attachedFiles(item);
88
+ if (args.action === 'read') {
89
+ return JSON.stringify({ id: args.id, attachments: files.map(({ name, url }) => ({ name, url })) }, null, 2);
90
+ }
91
+ if (!args.fileName) throw new Error('fileName is required for delete');
92
+ const target = files.find((file) => file.name === args.fileName);
93
+ if (!target) throw new Error(`work item ${args.id} has no AttachedFile named '${args.fileName}'`);
94
+ if (target.name.startsWith('plan-r')) {
95
+ await assertPlanAttachmentDeletable(c, item, target.name);
96
+ }
97
+ // On 7.1 delete detaches: remove the relation, the stored file stays; test/rev gives the same optimistic concurrency as attach.
98
+ await c.patch<any>(
99
+ `/wit/workitems/${args.id}`,
100
+ [
101
+ { op: 'test', path: '/rev', value: item.rev ?? item.fields?.['System.Rev'] },
102
+ { op: 'remove', path: `/relations/${target.index}` },
103
+ ],
104
+ { 'api-version': '7.1' },
105
+ 'application/json-patch+json',
106
+ );
107
+ return JSON.stringify({ id: args.id, deleted: target.name }, null, 2);
108
+ }
@@ -9,10 +9,38 @@
9
9
  * automatically) falling back to the AdoClient defaults. Nothing is
10
10
  * hardcoded here.
11
11
  *
12
+ * buildRun carries the second half of H1
13
+ * (docs/research/adl-process-handover/board-rules.md § Hooks, row
14
+ * `adl.build-run-role`): the hook has no network access, so it grades only
15
+ * what the call itself proves and names this function as the re-verifier of
16
+ * the caller-declared claim. Before queuing, buildRun requires ALL of:
17
+ * 1. `branch` present (the hook requires it too; the tool refuses on its
18
+ * own, never relying on the hook alone);
19
+ * 2. an OPEN pull request in this repository whose source branch equals
20
+ * the call's `branch` (after `refs/heads/` removal);
21
+ * 3. that PR raised by the CALLING agent identity — `createdBy.id`
22
+ * compared against lib/graph-identity.ts's resolveAgentIdentityId, the
23
+ * ONE identity-form producer in this repo;
24
+ * 4. the named definition is that PR's validation pipeline: a run of
25
+ * definition `id` exists on the PR's merge ref (`refs/pull/<id>/merge`,
26
+ * the sourceBranch ADO queues PR validation builds under — the same ref
27
+ * build_list_or_get's poll hint names).
28
+ * RESIDUAL APPROXIMATION (stated, not silently widened): check 4 proves
29
+ * the definition IS a validation pipeline of THIS PR via a run that
30
+ * already exists; a definition whose policy would queue on the PR but has
31
+ * never produced a run is refused. That is sound for the re-queue use case
32
+ * (an expired check implies a run that existed) but stricter than "any
33
+ * policy-bound definition" — the PR resource carries no build-policy
34
+ * reference (`completionOptions` names no definition), so an existing run
35
+ * on the merge ref is the strongest relationship the REST API exposes.
36
+ *
12
37
  * Timeline model: Stage → Phase → Job → Task, chained by parentId
13
38
  * (Checkpoint records carry no user-facing state and are skipped).
14
39
  */
15
- import { AdoClient } from './lib/client.ts'
40
+ import { AdoClient, resolveRepository } from './lib/client.ts'
41
+ import { resolveAgentIdentity } from './lib/agent-identity.ts';
42
+ import { resolveAgentIdentityId } from './lib/graph-identity.ts';
43
+ import { pullRequestsPath } from './azdo-pr.ts'
16
44
 
17
45
  let client: AdoClient | undefined
18
46
 
@@ -21,6 +49,11 @@ async function getClient(): Promise<AdoClient> {
21
49
  return client
22
50
  }
23
51
 
52
+ /** Test seam: forget the cached client — see azdo-pr.ts's resetClientForTests() for why. */
53
+ export function resetClientForTests(): void {
54
+ client = undefined
55
+ }
56
+
24
57
  /** GET a JSON payload. */
25
58
  async function getJson<T>(c: AdoClient, path: string, params?: Record<string, string>): Promise<T> {
26
59
  return c.get<T>(path, { 'api-version': '7.1', ...params });
@@ -160,13 +193,77 @@ export async function buildListOrGet(args: BuildListOrGetArgs = {}): Promise<str
160
193
 
161
194
  export type BuildRunArgs = { id: string; branch?: string };
162
195
 
196
+ function normalizeBranchRef(ref: string): string {
197
+ return ref.startsWith('refs/heads/') ? ref.slice('refs/heads/'.length) : ref;
198
+ }
199
+
200
+ /**
201
+ * H1's azdo half — see this file's header. Queries (all read-only, all
202
+ * before the queue POST): the open PRs on the named source branch, the
203
+ * caller's own ADO identity id (Graph Subject Query → Storage Keys), and the
204
+ * validation runs on the PR's merge ref. Throws with the exact failed check;
205
+ * queues only when every check passes.
206
+ */
207
+ async function assertOwnPrValidationBuild(c: AdoClient, definitionId: number, branch: string): Promise<void> {
208
+ const sourceBranch = normalizeBranchRef(branch.trim());
209
+ const repository = resolveRepository();
210
+ const { repositoryId } = await resolveRepositoryId(c);
211
+ const agent = resolveAgentIdentity().agent;
212
+
213
+ const prs = await getJson<any>(c, pullRequestsPath(repository), {
214
+ 'searchCriteria.status': 'active',
215
+ 'searchCriteria.sourceRefName': `refs/heads/${sourceBranch}`,
216
+ '$top': '100',
217
+ });
218
+ const candidates = (prs.value ?? []) as any[];
219
+ if (candidates.length === 0) {
220
+ throw new Error(
221
+ `build_run refused (H1): no open pull request in '${repository}' has source branch '${sourceBranch}' — ` +
222
+ 'build_run may only re-queue the validation pipeline of your own open PR, on that PR\'s source branch',
223
+ );
224
+ }
225
+
226
+ const callerId = await resolveAgentIdentityId(agent);
227
+ const mine = candidates.filter((pr) => pr.createdBy?.id === callerId);
228
+ if (mine.length === 0) {
229
+ const authors = [...new Set(candidates.map((pr) => pr.createdBy?.displayName ?? 'unknown'))].join(', ');
230
+ throw new Error(
231
+ `build_run refused (H1): the open pull request on '${sourceBranch}' was raised by ${authors}, not ${agent} — ` +
232
+ 'build_run may only re-queue the validation pipeline of a PR you raised yourself',
233
+ );
234
+ }
235
+
236
+ for (const pr of mine) {
237
+ const runs = await getJson<any>(c, '/build/builds', {
238
+ branchName: `refs/pull/${pr.pullRequestId}/merge`,
239
+ repositoryId,
240
+ repositoryType: 'TfsGit',
241
+ '$top': '50',
242
+ });
243
+ if ((runs.value ?? []).some((run: any) => run.definition?.id === definitionId)) {
244
+ return;
245
+ }
246
+ }
247
+ const prIds = mine.map((pr) => pr.pullRequestId).join(', ');
248
+ throw new Error(
249
+ `build_run refused (H1): build definition ${definitionId} is not a validation pipeline of your pull request(s) ${prIds} ` +
250
+ `(no run of it exists on refs/pull/<id>/merge) — pass the definition id of your PR's validation build, from build_list_or_get`,
251
+ );
252
+ }
253
+
163
254
  export async function buildRun(args: BuildRunArgs): Promise<string> {
164
255
  if (!args.id) throw new Error('id is required');
165
256
  const definitionId = Number(args.id);
166
257
  if (!Number.isInteger(definitionId) || definitionId < 1) throw new Error(`id must be a positive integer definition id; got '${args.id}'`);
258
+ if (typeof args.branch !== 'string' || args.branch.trim() === '') {
259
+ throw new Error(
260
+ 'branch is required: name the source branch of your own open PR — build_run may only re-queue that PR\'s ' +
261
+ 'validation pipeline, on that PR\'s source branch (H1)',
262
+ );
263
+ }
167
264
  const c = await getClient()
168
- const body: any = { definition: { id: definitionId } };
169
- if (args.branch) body.sourceBranch = args.branch;
265
+ await assertOwnPrValidationBuild(c, definitionId, args.branch);
266
+ const body: any = { definition: { id: definitionId }, sourceBranch: args.branch };
170
267
  const build = await c.post<any>('/build/builds', body, { 'api-version': '7.1' });
171
268
  return JSON.stringify({
172
269
  id: build.id,