@arbiterforge/ca-pi 0.8.1 → 0.10.2

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 (46) hide show
  1. package/README.md +29 -90
  2. package/package.json +1 -1
  3. package/plugins/ca-pi/CHANGELOG.md +89 -0
  4. package/plugins/ca-pi/COMMANDS.md +141 -64
  5. package/plugins/ca-pi/SKILLS.md +137 -28
  6. package/plugins/ca-pi/agents/INDEX.md +3 -2
  7. package/plugins/ca-pi/agents/checkpoint-aggregator.md +8 -7
  8. package/plugins/ca-pi/agents/finding-triage.md +31 -14
  9. package/plugins/ca-pi/agents/verdict-aggregator.md +64 -0
  10. package/plugins/ca-pi/arbiter.md +12 -3
  11. package/plugins/ca-pi/extensions/codearbiter.js +137 -15
  12. package/plugins/ca-pi/generated/command-catalog.json +386 -186
  13. package/plugins/ca-pi/generated/roles.json +9 -0
  14. package/plugins/ca-pi/hooks/_bashguardlib.py +33 -16
  15. package/plugins/ca-pi/hooks/_gitexec.py +23 -0
  16. package/plugins/ca-pi/hooks/_githooks.py +50 -23
  17. package/plugins/ca-pi/hooks/_hooklib.py +94 -7
  18. package/plugins/ca-pi/hooks/_host.py +9 -1
  19. package/plugins/ca-pi/hooks/_modelib.py +173 -55
  20. package/plugins/ca-pi/hooks/_protectedlib.py +13 -4
  21. package/plugins/ca-pi/hooks/_releaselib.py +278 -48
  22. package/plugins/ca-pi/hooks/_updatelib.py +230 -50
  23. package/plugins/ca-pi/hooks/doctor.py +56 -8
  24. package/plugins/ca-pi/hooks/git-enforce.py +10 -3
  25. package/plugins/ca-pi/hooks/hostapi.py +220 -22
  26. package/plugins/ca-pi/hooks/session-start.py +8 -6
  27. package/plugins/ca-pi/hooks/statusline.py +1 -1
  28. package/plugins/ca-pi/hooks/wire-statusline.py +13 -8
  29. package/plugins/ca-pi/includes/command-compatibility.md +16 -0
  30. package/plugins/ca-pi/includes/routing-table.md +13 -5
  31. package/plugins/ca-pi/routines/INDEX.md +1 -1
  32. package/plugins/ca-pi/routines/decision-lifecycle/SKILL.md +54 -2
  33. package/plugins/ca-pi/routines/decision-lifecycle/references/adr-template.md +9 -1
  34. package/plugins/ca-pi/routines/dispatching-parallel-agents/SKILL.md +4 -4
  35. package/plugins/ca-pi/routines/release/SKILL.md +1 -1
  36. package/plugins/ca-pi/skills/ca-checkpoint/SKILL.md +5 -4
  37. package/plugins/ca-pi/skills/ca-cleanup/SKILL.md +6 -0
  38. package/plugins/ca-pi/skills/ca-context-check/SKILL.md +6 -0
  39. package/plugins/ca-pi/skills/ca-create-context/SKILL.md +6 -0
  40. package/plugins/ca-pi/skills/ca-decompose/SKILL.md +6 -0
  41. package/plugins/ca-pi/skills/ca-doctor/SKILL.md +4 -0
  42. package/plugins/ca-pi/skills/ca-init/SKILL.md +18 -1
  43. package/plugins/ca-pi/skills/ca-pr/SKILL.md +17 -1
  44. package/plugins/ca-pi/skills/ca-review/SKILL.md +3 -4
  45. package/plugins/ca-pi/skills/ca-status/SKILL.md +13 -1
  46. package/plugins/ca-pi/skills/ca-watch/SKILL.md +6 -0
@@ -1546,10 +1546,14 @@ public static class CodeArbiterJob {
1546
1546
  '@
1547
1547
  try {
1548
1548
  Add-Type -TypeDefinition $source -Language CSharp | Out-Null
1549
+ [Console]::Out.WriteLine('COMPILED')
1550
+ [Console]::Out.Flush()
1549
1551
  $line = [Console]::In.ReadLine()
1550
1552
  if ($null -eq $line -or $line -notmatch '^([1-9][0-9]*) ([1-9][0-9]*)$') { exit 40 }
1551
1553
  [UInt32]$target = $Matches[1]
1552
1554
  [UInt32]$parent = $Matches[2]
1555
+ [Console]::Out.WriteLine('PID_ACCEPTED')
1556
+ [Console]::Out.Flush()
1553
1557
  $job = [CodeArbiterJob]::CreateAndAssign($target)
1554
1558
  if ($job -eq [IntPtr]::Zero) { exit 41 }
1555
1559
  try {
@@ -1642,12 +1646,31 @@ async function awaitProgressTokens(readLine, expected, options) {
1642
1646
  if (idleMs > ceilingMs) throw new Error("progress idleMs must be bounded by the progress ceiling");
1643
1647
  const now = options.now ?? Date.now;
1644
1648
  const deadline = now() + ceilingMs;
1649
+ if (options.windowsJobStages && (expected.length !== 2 || expected[0] !== WINDOWS_JOB_STARTING || expected[1] !== WINDOWS_JOB_READY)) {
1650
+ throw new Error("Windows startup diagnostics require the bounded admission sequence");
1651
+ }
1652
+ let lastStage = "WAITING";
1645
1653
  for (const token of expected) {
1646
- const remaining = deadline - now();
1647
- if (remaining <= 0) return Object.freeze({ state: "stalled", phase: token, waitedMs: 0 });
1648
1654
  const started = now();
1649
- const line = await readLine(Math.min(idleMs, remaining));
1650
- if (line !== token) return Object.freeze({ state: "stalled", phase: token, waitedMs: now() - started });
1655
+ const phaseDeadline = Math.min(deadline, started + idleMs);
1656
+ const stalled = () => Object.freeze({
1657
+ state: "stalled",
1658
+ phase: token,
1659
+ waitedMs: now() - started,
1660
+ ...options.windowsJobStages ? { lastStage } : {}
1661
+ });
1662
+ const diagnostics = options.windowsJobStages && token === WINDOWS_JOB_READY ? ["COMPILED", "PID_ACCEPTED"] : [];
1663
+ for (const diagnostic of diagnostics) {
1664
+ const remaining2 = phaseDeadline - now();
1665
+ if (remaining2 <= 0) return stalled();
1666
+ if (await readLine(remaining2) !== diagnostic) return stalled();
1667
+ lastStage = diagnostic;
1668
+ }
1669
+ const remaining = phaseDeadline - now();
1670
+ if (remaining <= 0) return stalled();
1671
+ const line = await readLine(remaining);
1672
+ if (line !== token) return stalled();
1673
+ if (options.windowsJobStages && token === WINDOWS_JOB_STARTING) lastStage = "STARTING";
1651
1674
  }
1652
1675
  return Object.freeze({ state: "ready" });
1653
1676
  }
@@ -1831,7 +1854,9 @@ function startWindowsJobGuard(pid, timing) {
1831
1854
  let closePending;
1832
1855
  let armed = false;
1833
1856
  let outputEnded = false;
1857
+ let protocolFailed = false;
1834
1858
  let outputBuffer = "";
1859
+ let queuedOutputBytes = 0;
1835
1860
  const outputLines = [];
1836
1861
  const outputWaiters = [];
1837
1862
  let resolveExitCode;
@@ -1851,7 +1876,11 @@ function startWindowsJobGuard(pid, timing) {
1851
1876
  settleExitCode();
1852
1877
  };
1853
1878
  const readOutputLine = (timeoutMs) => {
1854
- if (outputLines.length > 0) return Promise.resolve(outputLines.shift());
1879
+ if (outputLines.length > 0) {
1880
+ const { line, bytes } = outputLines.shift();
1881
+ queuedOutputBytes -= bytes;
1882
+ return Promise.resolve(line);
1883
+ }
1855
1884
  if (outputEnded) return Promise.resolve(void 0);
1856
1885
  return new Promise((resolveLine) => {
1857
1886
  let timer;
@@ -1870,8 +1899,11 @@ function startWindowsJobGuard(pid, timing) {
1870
1899
  helper.stdout.setEncoding("utf8");
1871
1900
  helper.stdout.on("data", (chunk) => {
1872
1901
  if (outputEnded) return;
1873
- outputBuffer += chunk;
1874
- if (Buffer.byteLength(outputBuffer, "utf8") > MAX_JOB_PROTOCOL_BYTES) {
1902
+ if (queuedOutputBytes + Buffer.byteLength(outputBuffer, "utf8") + Buffer.byteLength(chunk, "utf8") > MAX_JOB_PROTOCOL_BYTES) {
1903
+ protocolFailed = true;
1904
+ outputLines.length = 0;
1905
+ queuedOutputBytes = 0;
1906
+ outputBuffer = "";
1875
1907
  finishOutput();
1876
1908
  try {
1877
1909
  helper.stdin.end();
@@ -1879,13 +1911,17 @@ function startWindowsJobGuard(pid, timing) {
1879
1911
  }
1880
1912
  return;
1881
1913
  }
1914
+ outputBuffer += chunk;
1882
1915
  let newline = outputBuffer.indexOf("\n");
1883
1916
  while (newline >= 0) {
1917
+ const bytes = Buffer.byteLength(outputBuffer.slice(0, newline + 1), "utf8");
1884
1918
  const line = outputBuffer.slice(0, newline).replace(/\r$/u, "");
1885
1919
  outputBuffer = outputBuffer.slice(newline + 1);
1886
1920
  const waiter = outputWaiters.shift();
1887
- if (waiter === void 0) outputLines.push(line);
1888
- else waiter(line);
1921
+ if (waiter === void 0) {
1922
+ outputLines.push({ line, bytes });
1923
+ queuedOutputBytes += bytes;
1924
+ } else waiter(line);
1889
1925
  newline = outputBuffer.indexOf("\n");
1890
1926
  }
1891
1927
  });
@@ -1912,8 +1948,9 @@ function startWindowsJobGuard(pid, timing) {
1912
1948
  const finish = (accepted) => {
1913
1949
  if (settled) return false;
1914
1950
  settled = true;
1915
- resolveReady(accepted);
1916
- if (!accepted) {
1951
+ const admitted = accepted && !protocolFailed;
1952
+ resolveReady(admitted);
1953
+ if (!admitted) {
1917
1954
  try {
1918
1955
  helper.stdin.end();
1919
1956
  } catch {
@@ -1930,9 +1967,9 @@ function startWindowsJobGuard(pid, timing) {
1930
1967
  if (!intentional) finish(false);
1931
1968
  });
1932
1969
  helper.once("error", () => finish(false));
1933
- void awaitProgressTokens(readOutputLine, WINDOWS_JOB_READY_TOKENS, { ceilingMs, idleMs }).then((outcome) => {
1970
+ void awaitProgressTokens(readOutputLine, WINDOWS_JOB_READY_TOKENS, { ceilingMs, idleMs, windowsJobStages: true }).then((outcome) => {
1934
1971
  const refused = finish(outcome.state === "ready");
1935
- if (refused && outcome.state === "stalled") stallDiagnostic = `stalled at ${outcome.phase} after ${outcome.waitedMs}ms`;
1972
+ if (refused && outcome.state === "stalled") stallDiagnostic = `stalled at ${outcome.phase} after ${outcome.waitedMs}ms; last startup stage ${outcome.lastStage}`;
1936
1973
  }, () => finish(false));
1937
1974
  try {
1938
1975
  helper.stdin.write(`${pid} ${process.pid}
@@ -1950,7 +1987,7 @@ function startWindowsJobGuard(pid, timing) {
1950
1987
  return stallDiagnostic;
1951
1988
  },
1952
1989
  async arm(rootPid) {
1953
- if (armed || !positivePid(rootPid) || !await ready || closed) return false;
1990
+ if (armed || !positivePid(rootPid) || !await ready || closed || protocolFailed) return false;
1954
1991
  armed = true;
1955
1992
  const watched = readOutputLine(idleMs);
1956
1993
  const written = await new Promise((resolveWrite) => {
@@ -9722,6 +9759,88 @@ async function appendPiCompactionAudit(record2) {
9722
9759
  }
9723
9760
 
9724
9761
  // src/extension.ts
9762
+ var COMMAND_VISIBILITY_ORDER = ["core", "advanced", "alias", "internal", "deprecated"];
9763
+ var COMMAND_WORKFLOW_ORDER = [
9764
+ "evaluate",
9765
+ "initialize",
9766
+ "change",
9767
+ "review",
9768
+ "decide",
9769
+ "ship",
9770
+ "operate",
9771
+ "extend",
9772
+ "help"
9773
+ ];
9774
+ function plainRecord2(value) {
9775
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
9776
+ }
9777
+ function sameStrings2(actual, expected) {
9778
+ return actual.length === expected.length && actual.every((item, index) => item === expected[index]);
9779
+ }
9780
+ function validLegacyRoutes(value) {
9781
+ return Array.isArray(value) && value.every((item) => typeof item === "string" && /^[a-z][a-z0-9-]*$/u.test(item)) && new Set(value).size === value.length && sameStrings2(value, [...value].sort());
9782
+ }
9783
+ function commandCatalogEntries(value) {
9784
+ const envelopeKeys = ["commands", "compatibility", "schemaVersion", "visibilityOrder", "workflowOrder"];
9785
+ if (!plainRecord2(value) || !sameStrings2(Object.keys(value).sort(), envelopeKeys) || value.schemaVersion !== 1 || !Array.isArray(value.visibilityOrder) || !sameStrings2(value.visibilityOrder, COMMAND_VISIBILITY_ORDER) || !Array.isArray(value.workflowOrder) || !sameStrings2(value.workflowOrder, COMMAND_WORKFLOW_ORDER) || !plainRecord2(value.compatibility) || !plainRecord2(value.commands)) {
9786
+ throw new Error("codeArbiter Pi command catalog envelope is invalid; run /ca-doctor.");
9787
+ }
9788
+ const commands = value.commands;
9789
+ const names = Object.keys(commands);
9790
+ if (!sameStrings2(names, [...names].sort()) || names.length === 0) {
9791
+ throw new Error("codeArbiter Pi command catalog envelope has no sorted commands; run /ca-doctor.");
9792
+ }
9793
+ const entries = [];
9794
+ for (const name of names) {
9795
+ const fail2 = () => {
9796
+ throw new Error(`codeArbiter Pi command catalog entry ${name} is invalid; run /ca-doctor.`);
9797
+ };
9798
+ const value2 = commands[name];
9799
+ if (!plainRecord2(value2)) {
9800
+ throw new Error(`codeArbiter Pi command catalog entry ${name} is invalid; run /ca-doctor.`);
9801
+ }
9802
+ const raw = value2;
9803
+ if (raw.name !== name || typeof raw.description !== "string" || raw.description === "" || raw.skillPath !== `skills/ca-${name}/SKILL.md` || !COMMAND_VISIBILITY_ORDER.includes(raw.visibility) || !COMMAND_WORKFLOW_ORDER.includes(raw.workflow)) {
9804
+ fail2();
9805
+ }
9806
+ const visibility = raw.visibility;
9807
+ const commonKeys = ["description", "name", "skillPath", "visibility", "workflow"];
9808
+ let expectedKeys;
9809
+ if (visibility === "core" || visibility === "advanced" || visibility === "internal") {
9810
+ expectedKeys = [...commonKeys, "canonical", "legacyRoutes"].sort();
9811
+ if (raw.canonical !== name || !validLegacyRoutes(raw.legacyRoutes)) fail2();
9812
+ } else if (visibility === "alias") {
9813
+ expectedKeys = [...commonKeys, "canonical", "replacement"].sort();
9814
+ if (typeof raw.canonical !== "string" || !/^[a-z][a-z0-9-]*$/u.test(raw.canonical) || typeof raw.replacement !== "string" || !raw.replacement.startsWith(`${raw.canonical} `)) fail2();
9815
+ } else {
9816
+ expectedKeys = [...commonKeys, "replacement"].sort();
9817
+ if (typeof raw.replacement !== "string" || raw.replacement === "") fail2();
9818
+ }
9819
+ if (!sameStrings2(Object.keys(raw).sort(), expectedKeys)) fail2();
9820
+ entries.push(Object.freeze({ ...raw }));
9821
+ }
9822
+ const entriesByName = new Map(entries.map((entry) => [entry.name, entry]));
9823
+ const expectedLegacyRoutes = /* @__PURE__ */ new Map();
9824
+ for (const entry of entries) {
9825
+ if (entry.visibility === "alias") {
9826
+ const target = entriesByName.get(entry.canonical);
9827
+ if (target === void 0 || target.visibility === "alias" || target.visibility === "deprecated") {
9828
+ throw new Error(`codeArbiter Pi command catalog entry ${entry.name} has an invalid alias target; run /ca-doctor.`);
9829
+ }
9830
+ const routes = expectedLegacyRoutes.get(entry.canonical) ?? [];
9831
+ routes.push(entry.name);
9832
+ expectedLegacyRoutes.set(entry.canonical, routes);
9833
+ }
9834
+ }
9835
+ for (const entry of entries) {
9836
+ if (entry.visibility === "alias" || entry.visibility === "deprecated") continue;
9837
+ const expected = (expectedLegacyRoutes.get(entry.name) ?? []).sort();
9838
+ if (!sameStrings2(entry.legacyRoutes ?? [], expected)) {
9839
+ throw new Error(`codeArbiter Pi command catalog entry ${entry.name} has broken legacy-route closure; run /ca-doctor.`);
9840
+ }
9841
+ }
9842
+ return Object.freeze(entries);
9843
+ }
9725
9844
  var PI_TRUST_REQUIRED_STATUS = "codeArbiter host: pi waiting for project trust - run /trust in Pi, approve this project, then start a new session";
9726
9845
  function hasAffirmativeProjectTrust(context) {
9727
9846
  try {
@@ -10257,7 +10376,9 @@ async function codeArbiterPi(pi) {
10257
10376
  if (parent === packageRoot) throw new Error("codeArbiter could not locate the ca-pi package; run /ca-doctor.");
10258
10377
  packageRoot = parent;
10259
10378
  }
10260
- const catalog = JSON.parse(await readFile6(resolve15(packageRoot, "generated", "command-catalog.json"), "utf8"));
10379
+ const catalog = commandCatalogEntries(
10380
+ JSON.parse(await readFile6(resolve15(packageRoot, "generated", "command-catalog.json"), "utf8"))
10381
+ );
10261
10382
  const toolClasses = loadPiToolClasses(define_CODEARBITER_PI_TOOL_CLASSES_default);
10262
10383
  const rawPermissionSurfaces = define_CODEARBITER_PI_PERMISSION_POLICY_SURFACES_default;
10263
10384
  if (rawPermissionSurfaces === null || typeof rawPermissionSurfaces !== "object" || Array.isArray(rawPermissionSurfaces)) {
@@ -10532,6 +10653,7 @@ export {
10532
10653
  PERSONA_SENTINEL,
10533
10654
  PI_RUNTIME_DIAGNOSIS,
10534
10655
  boundedPiEnvironment,
10656
+ commandCatalogEntries,
10535
10657
  compatibilityDirection,
10536
10658
  createCodeArbiterPi,
10537
10659
  createPiFooterMetricsLoader,