@event4u/agent-config 17.0.0 → 17.1.0

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 (119) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +104 -485
  5. package/README.md +2 -1
  6. package/dist/agent-src/contexts/execution/host-capability-manifest.md +5 -3
  7. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  8. package/dist/agent-src/templates/roadmaps.md +5 -1
  9. package/dist/cli-delegate/{chunk-IROAQ77A.js → chunk-3PK3VZHB.js} +107 -9
  10. package/dist/cli-delegate/{chunk-2SFOOFJJ.js → chunk-BZJ4Y3CI.js} +1 -1
  11. package/dist/cli-delegate/{chunk-CAL76ADI.js → chunk-CMZEKSNN.js} +52 -10
  12. package/dist/cli-delegate/{chunk-ESUNQ5XR.js → chunk-E3CFULNX.js} +22 -4
  13. package/dist/cli-delegate/{chunk-GOMILC3O.js → chunk-QBHEI5N4.js} +4 -3
  14. package/dist/cli-delegate/{chunk-SIOMDAKL.js → chunk-VYZGJLCA.js} +29 -39
  15. package/dist/cli-delegate/chunk-W5JXMF2T.js +77 -0
  16. package/dist/cli-delegate/{chunk-4SHNERBL.js → chunk-XIAPY44N.js} +1 -1
  17. package/dist/cli-delegate/{chunk-JHMMWNJ2.js → chunk-YHAPONWV.js} +1 -1
  18. package/dist/cli-delegate/cmd_conformance.js +9 -8
  19. package/dist/cli-delegate/cmd_doctor.js +4 -4
  20. package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
  21. package/dist/cli-delegate/cmd_explain.js +1 -1
  22. package/dist/cli-delegate/cmd_export.js +6 -6
  23. package/dist/cli-delegate/cmd_fleet.js +3 -3
  24. package/dist/cli-delegate/cmd_preflight.js +3 -3
  25. package/dist/cli-delegate/cmd_prune.js +6 -6
  26. package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
  27. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  28. package/dist/cli-delegate/cmd_sync.js +6 -6
  29. package/dist/cli-delegate/cmd_uninstall.js +6 -6
  30. package/dist/cli-delegate/cmd_update.js +9 -13
  31. package/dist/cli-delegate/cmd_validate.js +6 -6
  32. package/dist/discovery/deprecation-report.md +1 -1
  33. package/dist/discovery/discovery-manifest.json +4 -4
  34. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  35. package/dist/discovery/discovery-manifest.summary.md +1 -1
  36. package/dist/discovery/dormancy-report.md +1 -1
  37. package/dist/discovery/orphan-report.md +1 -1
  38. package/dist/discovery/packs.json +2 -2
  39. package/dist/discovery/trust-report.md +1 -1
  40. package/dist/discovery/workspaces.json +2 -2
  41. package/dist/hooks/dispatch.js +98 -74
  42. package/dist/hooks/dispatch.sha256 +1 -1
  43. package/dist/install/claudeRuleRewrite.js +10 -3
  44. package/dist/install/conflict.js +24 -2
  45. package/dist/install/conflictTracker.js +14 -3
  46. package/dist/install/install.mjs +222 -117
  47. package/dist/install/installThinLayer.js +1 -0
  48. package/dist/install/loggedWrites.js +142 -0
  49. package/dist/install/paths.js +12 -2
  50. package/dist/install/preserve.js +25 -4
  51. package/dist/install/txlog.js +10 -6
  52. package/dist/mcp/registry-manifest.json +2 -2
  53. package/dist/mcp/server.json +2 -2
  54. package/dist/mcp/server.mjs +845 -294
  55. package/dist/scripts/_lib/egress_rules.js +4 -1
  56. package/dist/scripts/_lib/identifier_redaction.js +35 -2
  57. package/dist/scripts/ai_council/seat_policy.js +7 -1
  58. package/dist/server/schemas/settings.js +1 -1
  59. package/package.json +1 -1
  60. package/src/config/ci-local-parity.yml +1 -1
  61. package/src/config/gate-coverage.yml +27 -7
  62. package/src/config/gate-violation-baselines.json +8 -2
  63. package/src/config/hook-bundle-budget.json +2 -2
  64. package/src/config/production-entries.json +18 -0
  65. package/src/config/release-gate-locality.yml +0 -7
  66. package/src/install/claudeRuleRewrite.ts +10 -1
  67. package/src/install/conflict.ts +24 -1
  68. package/src/install/conflictTracker.ts +18 -2
  69. package/src/install/installThinLayer.ts +6 -0
  70. package/src/install/loggedWrites.ts +148 -0
  71. package/src/install/paths.ts +11 -2
  72. package/src/install/preserve.ts +40 -4
  73. package/src/install/txlog.ts +10 -6
  74. package/src/scripts/_cli/cmd_conformance.ts +3 -2
  75. package/src/scripts/_cli/cmd_update.ts +15 -17
  76. package/src/scripts/_lib/council_settings_block.ts +9 -2
  77. package/src/scripts/_lib/egress_rules.ts +4 -1
  78. package/src/scripts/_lib/host_capability.ts +10 -5
  79. package/src/scripts/_lib/identifier_redaction.ts +38 -2
  80. package/src/scripts/_lib/price_freshness.ts +112 -0
  81. package/src/scripts/_lib/production_reach.ts +194 -0
  82. package/src/scripts/_lib/quota_parked.ts +17 -28
  83. package/src/scripts/_lib/release_findings_ingest.ts +13 -335
  84. package/src/scripts/_lib/tier_downshift_verdict.ts +81 -35
  85. package/src/scripts/ai_council/chairman.ts +5 -2
  86. package/src/scripts/ai_council/clients.ts +4 -3
  87. package/src/scripts/ai_council/free_hand_shadow.ts +24 -12
  88. package/src/scripts/ai_council/free_route_catalog.ts +142 -5
  89. package/src/scripts/ai_council/free_route_client.ts +4 -0
  90. package/src/scripts/ai_council/hand_payload.ts +115 -7
  91. package/src/scripts/ai_council/orchestrator.ts +7 -10
  92. package/src/scripts/ai_council/route_chooser.ts +1 -1
  93. package/src/scripts/ai_council/route_member.ts +66 -4
  94. package/src/scripts/ai_council/seat_policy.ts +8 -1
  95. package/src/scripts/ai_council/self_hosted_probe.ts +327 -0
  96. package/src/scripts/ai_council/status_surface.ts +2 -0
  97. package/src/scripts/ask_transport.ts +137 -15
  98. package/src/scripts/check_finding_dispositions.ts +25 -71
  99. package/src/scripts/check_release_pr_shape.ts +3 -3
  100. package/src/scripts/check_step_production_reach.ts +257 -0
  101. package/src/scripts/council_cli.ts +10 -8
  102. package/src/scripts/hook_manifest.json +1 -1
  103. package/src/scripts/hook_manifest.yaml +3 -0
  104. package/src/scripts/hooks/dispatch_hook.ts +2 -2
  105. package/src/scripts/hooks/payload_stub.ts +27 -0
  106. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +29 -70
  107. package/src/scripts/hooks/subagent_ledger_hook.ts +80 -0
  108. package/src/scripts/install.ts +2 -2
  109. package/src/scripts/lint_decision_classes.ts +34 -0
  110. package/src/scripts/lint_hook_manifest.ts +24 -0
  111. package/src/scripts/lint_roadmap_blockers.ts +52 -3
  112. package/src/scripts/release.ts +5 -9
  113. package/src/scripts/release_drill.ts +11 -165
  114. package/src/scripts/release_publication.ts +0 -9
  115. package/src/scripts/report_production_reach.ts +35 -0
  116. package/src/scripts/worker_reuse_shadow_report.ts +399 -1
  117. package/src/server/schemas/settings.ts +1 -1
  118. package/dist/cli-delegate/chunk-A4QJ6ENT.js +0 -50
  119. /package/dist/cli-delegate/{chunk-E6XNBVE4.js → chunk-KR5ZIFPO.js} +0 -0
@@ -21044,6 +21044,23 @@ function _concern_body_classes(concern) {
21044
21044
  }
21045
21045
  return parseBodyClasses(concern["needs_payload_bodies"]);
21046
21046
  }
21047
+ function applyPayloadFields(concern, env) {
21048
+ const fields = concern["payload_fields"];
21049
+ const payload = env["payload"];
21050
+ if (!Array.isArray(fields) || !isObject2(payload) || concern["fail_closed"] === true || concern["severity"] === "blocking") {
21051
+ return env;
21052
+ }
21053
+ const keep = new Set(fields);
21054
+ const out = { ...payload };
21055
+ for (const cls of BODY_CLASSES) {
21056
+ for (const key of BODY_KEYS[cls]) {
21057
+ const v = payload[key];
21058
+ if (v === void 0 || v === null || isPayloadStub(v)) continue;
21059
+ out[key] = isObject2(v) ? Object.fromEntries(Object.entries(v).filter(([k]) => keep.has(k))) : makePayloadStub(key, cls, v, /* @__PURE__ */ new Map([[key, bodyBytes(v)]]));
21060
+ }
21061
+ }
21062
+ return { ...env, payload: out };
21063
+ }
21047
21064
  function planPayloadShapes(envelope, keepSets, placeholder) {
21048
21065
  const present = presentBodyClasses(envelope);
21049
21066
  const stubbedSomewhere = /* @__PURE__ */ new Set();
@@ -33030,7 +33047,8 @@ function probeHostCapabilities(hostId) {
33030
33047
  const base2 = resolveHostCapabilities(hostId);
33031
33048
  const flag = process9.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
33032
33049
  const probedAgentTeams = typeof flag === "string" && flag !== "";
33033
- return { ...base2, agent_teams: base2.agent_teams || probedAgentTeams };
33050
+ const probedResume = probedAgentTeams && hostId === "claude";
33051
+ return { ...base2, agent_teams: base2.agent_teams || probedAgentTeams, worker_resume: base2.worker_resume || probedResume };
33034
33052
  }
33035
33053
 
33036
33054
  // src/scripts/_lib/auto_dispatch.ts
@@ -37601,6 +37619,7 @@ function openSvgObligation(workspaceRoot3, toolCalls, recordedRaw = null) {
37601
37619
  }
37602
37620
 
37603
37621
  // src/scripts/hooks/subagent_ledger_hook.ts
37622
+ import { execFileSync as execFileSync9 } from "node:child_process";
37604
37623
  import * as crypto12 from "node:crypto";
37605
37624
  import * as fs112 from "node:fs";
37606
37625
  import * as path117 from "node:path";
@@ -37861,6 +37880,51 @@ function appendLedgerLine(root, ts, line) {
37861
37880
  fs112.appendFileSync(file, `${JSON.stringify(line)}
37862
37881
  `, "utf8");
37863
37882
  }
37883
+ var INDEX_SUBDIR = "index";
37884
+ function writeIndexEntry(root, ts, key, value) {
37885
+ if (is_replay_mode()) return;
37886
+ const dir = path117.join(root, LEDGER_DIR, INDEX_SUBDIR, ts.slice(0, 7));
37887
+ fs112.mkdirSync(dir, { recursive: true });
37888
+ const tmp = path117.join(dir, `.${key}.${process.pid}.tmp`);
37889
+ try {
37890
+ fs112.writeFileSync(tmp, value, "utf8");
37891
+ fs112.renameSync(tmp, path117.join(dir, key));
37892
+ } finally {
37893
+ fs112.rmSync(tmp, { force: true });
37894
+ }
37895
+ }
37896
+ function readIndexEntry(root, key, now2) {
37897
+ const prev = new Date(Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth() - 1, 1));
37898
+ for (const month of [now2.toISOString().slice(0, 7), prev.toISOString().slice(0, 7)]) {
37899
+ try {
37900
+ return fs112.readFileSync(path117.join(root, LEDGER_DIR, INDEX_SUBDIR, month, key), "utf8");
37901
+ } catch {
37902
+ }
37903
+ }
37904
+ return null;
37905
+ }
37906
+ function headOf(dir) {
37907
+ try {
37908
+ const env = Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith("GIT_")));
37909
+ return execFileSync9("git", ["-C", dir, "rev-parse", "HEAD"], { encoding: "utf8", timeout: 2e3, stdio: ["ignore", "pipe", "ignore"], env }).trim() || null;
37910
+ } catch {
37911
+ return null;
37912
+ }
37913
+ }
37914
+ function stateStamp(cwd) {
37915
+ let epoch = null;
37916
+ try {
37917
+ epoch = fs112.statSync(fileURLToPath59(import.meta.url)).mtimeMs;
37918
+ } catch {
37919
+ }
37920
+ return typeof cwd === "string" && cwd ? { head: headOf(cwd), cwd_key: refFor(cwd), epoch } : { head: null, cwd_key: null, epoch };
37921
+ }
37922
+ function stopIndexKey(sessionId3, ref) {
37923
+ return refFor(`subagent-stop\0${sessionId3}\0${ref}`);
37924
+ }
37925
+ function recordStopIndex(root, ts, sessionId3, ref) {
37926
+ writeIndexEntry(root, ts, stopIndexKey(sessionId3, ref), "");
37927
+ }
37864
37928
  var WALL_CLOCK_ARMS_MS = [
37865
37929
  { label: "5m", ms: 5 * 60 * 1e3 },
37866
37930
  { label: "15m", ms: 15 * 60 * 1e3 },
@@ -38028,8 +38092,13 @@ function handleStop(root, payload, nowIso, sessionId3) {
38028
38092
  // Beside the count, never instead of it: the count says how wrong the
38029
38093
  // candidate was, this says whether it was aimed at the envelope.
38030
38094
  envelope_field_hits: parse.field_hits,
38031
- concurrent_open: countOpen(root)
38095
+ concurrent_open: countOpen(root),
38096
+ ...stateStamp(payload["cwd"])
38032
38097
  });
38098
+ try {
38099
+ if (sessionId3 !== null) recordStopIndex(root, nowIso, sessionId3, ref);
38100
+ } catch {
38101
+ }
38033
38102
  }
38034
38103
  function processEnvelope3(envelope, consumerRoot) {
38035
38104
  try {
@@ -40819,12 +40888,13 @@ function markerFile(repoRoot, runId) {
40819
40888
  function writeQuotaParked(repoRoot, runId, providers, now2 = () => /* @__PURE__ */ new Date(), source = "council") {
40820
40889
  const file = markerFile(repoRoot, runId);
40821
40890
  const existing = readMarker(file);
40891
+ const host2 = source === "host";
40822
40892
  const merged = [.../* @__PURE__ */ new Set([...existing?.providers ?? [], ...providers])].sort();
40823
40893
  const marker = {
40824
40894
  run_id: runId,
40825
40895
  providers: merged,
40826
- parked_at: existing?.parked_at ?? now2().toISOString(),
40827
- source: existing?.source ?? source
40896
+ parked_at: (host2 ? null : existing?.parked_at) ?? now2().toISOString(),
40897
+ source: host2 ? source : existing?.source ?? source
40828
40898
  };
40829
40899
  fs126.mkdirSync(path130.dirname(file), { recursive: true });
40830
40900
  fs126.writeFileSync(file, `${JSON.stringify(marker, null, 2)}
@@ -40833,20 +40903,18 @@ function writeQuotaParked(repoRoot, runId, providers, now2 = () => /* @__PURE__
40833
40903
  }
40834
40904
  var HOST_RATE_WINDOW_MS = 5 * 36e5;
40835
40905
  function recentHostMarker(repoRoot, nowMs = Date.now(), windowMs = HOST_RATE_WINDOW_MS) {
40836
- let names;
40906
+ return allMarkers(repoRoot).some((m) => {
40907
+ const age = nowMs - Date.parse(m.parked_at);
40908
+ return m.source === "host" && age >= 0 && age < windowMs;
40909
+ });
40910
+ }
40911
+ function allMarkers(repoRoot) {
40912
+ const dir = path130.join(repoRoot, QUOTA_PARKED_DIR_REL);
40837
40913
  try {
40838
- names = fs126.readdirSync(path130.join(repoRoot, QUOTA_PARKED_DIR_REL));
40914
+ return fs126.readdirSync(dir).flatMap((n) => n.endsWith(".json") ? readMarker(path130.join(dir, n)) ?? [] : []);
40839
40915
  } catch {
40840
- return false;
40841
- }
40842
- for (const name of names) {
40843
- if (!name.endsWith(".json")) continue;
40844
- const m = readMarker(path130.join(repoRoot, QUOTA_PARKED_DIR_REL, name));
40845
- if (m === null || m.source !== "host") continue;
40846
- const at = Date.parse(m.parked_at);
40847
- if (Number.isFinite(at) && nowMs - at >= 0 && nowMs - at < windowMs) return true;
40916
+ return [];
40848
40917
  }
40849
- return false;
40850
40918
  }
40851
40919
  function readMarker(file) {
40852
40920
  try {
@@ -40916,72 +40984,19 @@ function isObject23(v) {
40916
40984
  }
40917
40985
  var DISPATCH_TOOL_NAMES2 = /* @__PURE__ */ new Set(["Agent", "Task"]);
40918
40986
  var RESUME_TOOL_NAMES = /* @__PURE__ */ new Set(["SendMessage"]);
40919
- var STOP_SCAN_BYTES = 1024 * 1024;
40920
- function tailOf(file) {
40921
- let fd;
40922
- try {
40923
- fd = fs127.openSync(file, "r");
40924
- } catch {
40925
- return "";
40926
- }
40927
- try {
40928
- const size = fs127.fstatSync(fd).size;
40929
- const length = Math.min(size, STOP_SCAN_BYTES);
40930
- const buf = Buffer.alloc(length);
40931
- fs127.readSync(fd, buf, 0, length, size - length);
40932
- return buf.toString("utf8");
40933
- } finally {
40934
- fs127.closeSync(fd);
40935
- }
40936
- }
40937
- function stoppedInSession(root, ref, sessionId3, now2) {
40938
- const prev = new Date(Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth() - 1, 1));
40939
- for (const month of [now2.toISOString().slice(0, 7), prev.toISOString().slice(0, 7)]) {
40940
- for (const line of tailOf(path131.join(root, LEDGER_DIR, `${month}.jsonl`)).split("\n")) {
40941
- if (!line.includes(ref)) continue;
40942
- try {
40943
- const rec = JSON.parse(line);
40944
- if (rec["event"] === "subagent_stop" && rec["ref"] === ref && rec["session_id"] === sessionId3) {
40945
- return true;
40946
- }
40947
- } catch {
40948
- }
40949
- }
40950
- }
40951
- return false;
40952
- }
40953
40987
  var NAME_MAX = 128;
40954
40988
  function nameKey(sessionId3, name) {
40955
40989
  return refFor(`subagent-name\0${sessionId3}\0${name}`);
40956
40990
  }
40957
- function refForName(root, name, sessionId3, now2) {
40958
- if (name.length > NAME_MAX) return null;
40959
- const key = nameKey(sessionId3, name);
40960
- let found = null;
40961
- const prev = new Date(Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth() - 1, 1));
40962
- for (const month of [prev.toISOString().slice(0, 7), now2.toISOString().slice(0, 7)]) {
40963
- for (const line of tailOf(path131.join(root, LEDGER_DIR, `${month}.jsonl`)).split("\n")) {
40964
- if (!line.includes("subagent_named")) continue;
40965
- try {
40966
- const rec = JSON.parse(line);
40967
- if (rec["event"] === "subagent_named" && rec["name_key"] === key && typeof rec["ref"] === "string") {
40968
- found = rec["ref"];
40969
- }
40970
- } catch {
40971
- }
40972
- }
40973
- }
40974
- return found;
40975
- }
40976
40991
  function classifySend(root, toolInput, sessionId3, now2 = /* @__PURE__ */ new Date()) {
40977
40992
  const input = isObject23(toolInput) ? toolInput : {};
40978
40993
  const recipient = input["to"];
40979
40994
  if (typeof recipient !== "string" || recipient === "") return "message_or_resume";
40980
40995
  if (sessionId3 === null || sessionId3 === "") return "message_or_resume";
40981
- const ref = input["recipient_kind"] === "name" ? refForName(root, recipient, sessionId3, now2) : refFor(recipient);
40982
- if (ref === null) return "message_or_resume";
40996
+ const ref = input["recipient_kind"] !== "name" ? refFor(recipient) : recipient.length > NAME_MAX ? null : readIndexEntry(root, nameKey(sessionId3, recipient), now2);
40997
+ if (!ref) return "message_or_resume";
40983
40998
  if (readOpenRecords(root).has(ref)) return "message_running";
40984
- return stoppedInSession(root, ref, sessionId3, now2) ? "resume" : "message_or_resume";
40999
+ return readIndexEntry(root, stopIndexKey(sessionId3, ref), now2) === null ? "message_or_resume" : "resume";
40985
41000
  }
40986
41001
  function recordNamedSpawn(root, payload, sessionId3, nowIso) {
40987
41002
  const input = isObject23(payload["tool_input"]) ? payload["tool_input"] : {};
@@ -40989,8 +41004,10 @@ function recordNamedSpawn(root, payload, sessionId3, nowIso) {
40989
41004
  const name = input["name"];
40990
41005
  const agentId = result["agentId"];
40991
41006
  if (typeof name !== "string" || name === "" || name.length > NAME_MAX || typeof agentId !== "string" || agentId === "") return;
40992
- if (sessionId3 === null) return;
40993
- appendLedgerLine(root, nowIso, { event: "subagent_named", ts: nowIso, ref: refFor(agentId), name_key: nameKey(sessionId3, name), session_id: sessionId3 });
41007
+ if (!sessionId3) return;
41008
+ const key = nameKey(sessionId3, name);
41009
+ appendLedgerLine(root, nowIso, { event: "subagent_named", ts: nowIso, ref: refFor(agentId), name_key: key, session_id: sessionId3 });
41010
+ writeIndexEntry(root, nowIso, key, refFor(agentId));
40994
41011
  }
40995
41012
  var RESUME_ADMISSION_OBSERVED = /* @__PURE__ */ new Set(["claude"]);
40996
41013
  var CANDIDATES = [
@@ -41034,6 +41051,12 @@ function processEnvelope5(envelope, consumerRoot, deps = {}) {
41034
41051
  const sessionRaw = payload["session_id"] ?? envelope["session_id"];
41035
41052
  const sessionId3 = typeof sessionRaw === "string" ? sessionRaw : null;
41036
41053
  const sendKind = isResume ? classifySend(consumerRoot, toolInput, sessionId3) : null;
41054
+ const reqName = toolInput["name"];
41055
+ const spawnFacts = isResume ? {} : {
41056
+ ...stateStamp(payload["cwd"]),
41057
+ agent_type: typeof toolInput["subagent_type"] === "string" ? toolInput["subagent_type"] : null,
41058
+ ...typeof reqName === "string" && reqName && reqName.length <= NAME_MAX && sessionId3 ? { name_key: nameKey(sessionId3, reqName) } : {}
41059
+ };
41037
41060
  const reuse = isResume ? null : shadowReuseVerdict({
41038
41061
  host_resume: deps.host_resume ?? probeHostCapabilities(platform).worker_resume,
41039
41062
  workers: deps.workers ?? null,
@@ -41064,6 +41087,7 @@ function processEnvelope5(envelope, consumerRoot, deps = {}) {
41064
41087
  // against a threshold that counts runs.
41065
41088
  ...sendKind === "message_running" ? {} : { candidates: verdicts.map((v) => ({ label: v.label, would_deny: v.would_deny, on: v.on })) },
41066
41089
  dispatch_kind: sendKind ?? "spawn",
41090
+ ...spawnFacts,
41067
41091
  resume_admission: platform !== null && RESUME_ADMISSION_OBSERVED.has(platform) ? "counted" : "unobserved",
41068
41092
  ...reuse === null ? {} : { reuse },
41069
41093
  // A recent host-sourced rate marker: a new spawn would have waited.
@@ -44050,7 +44074,7 @@ function main71(argv) {
44050
44074
  if (!_concern_matches_tool(concern, tool_name)) {
44051
44075
  continue;
44052
44076
  }
44053
- const concern_envelope = shapes.shapeFor(keep_classes, egressViewFor(concern));
44077
+ const concern_envelope = applyPayloadFields(concern, shapes.shapeFor(keep_classes, egressViewFor(concern)));
44054
44078
  const stubs_served = shapes.stubsFor(keep_classes);
44055
44079
  const served_classes = shapes.servedBy(keep_classes);
44056
44080
  const concern_started = _now_iso3();
@@ -1 +1 @@
1
- e52615d77445fea7d34fdf0ceb7413be798e18f51f257308e1efc6588d37c428 dispatch.js
1
+ 886ab561301c6376b605aeed89a965b978dae593df52a5c774ba27349860d298 dispatch.js
@@ -46,6 +46,9 @@
46
46
  import * as fs from 'node:fs';
47
47
  import * as path from 'node:path';
48
48
  import { _claude_paths_plan } from './claudePathsPlan.js';
49
+ // Re-exported for `install.ts`, which sits past the source-size ceiling and so
50
+ // reaches it through the namespace it already imports instead of a new line.
51
+ export { onlyDeployed } from './loggedWrites.js';
49
52
  import { parseFrontmatter } from './ruleInScope.js';
50
53
  /**
51
54
  * Frontmatter keys the rewrite must carry across, and why each one.
@@ -109,8 +112,8 @@ export function _withPreservedKeys(rendered, original) {
109
112
  * extraction and never up through a baseline raise. So the caller gets one call
110
113
  * and a list of strings.
111
114
  */
112
- export function rewriteAndReport(rulesDir, quiet, info, warn, wrappers, preserved = new Set()) {
113
- const result = rewriteClaudeRules(rulesDir, preserved);
115
+ export function rewriteAndReport(rulesDir, quiet, info, warn, wrappers, preserved = new Set(), onWrite) {
116
+ const result = rewriteClaudeRules(rulesDir, preserved, onWrite);
114
117
  // The flat-command wrapper report rides along rather than sitting at the
115
118
  // call site: `install.ts` is thousands of lines past
116
119
  // `check_source_size_budget`'s ceiling, and this module is the one place
@@ -205,8 +208,11 @@ export function renderClaudeRule(sourceText) {
205
208
  * disappearing from the frontmatter.
206
209
  *
207
210
  * Returns counts rather than printing. The caller owns the reporting surface.
211
+ * `onWrite` receives each rewritten path, so the installer can log it: the
212
+ * transaction log is read back as proof the bytes on disk are ours, and a
213
+ * write it never saw would leave every rewritten rule unprovable.
208
214
  */
209
- export function rewriteClaudeRules(rulesDir, preserved = new Set()) {
215
+ export function rewriteClaudeRules(rulesDir, preserved = new Set(), onWrite) {
210
216
  const result = { rewritten: 0, scoped: [], dropped: [], failed: [] };
211
217
  let entries;
212
218
  try {
@@ -258,6 +264,7 @@ export function rewriteClaudeRules(rulesDir, preserved = new Set()) {
258
264
  });
259
265
  continue;
260
266
  }
267
+ onWrite?.(full);
261
268
  result.rewritten += 1;
262
269
  }
263
270
  const stem = name.slice(0, -3);
@@ -16,7 +16,8 @@
16
16
  */
17
17
  import { existsSync } from 'node:fs';
18
18
  import { sha256File } from './plan.js';
19
- import { isLedgerMigration } from './preserve.js';
19
+ import { loggedWriteDigests, wroteTheseBytes } from './loggedWrites.js';
20
+ import { isLedgerMigration, logProofApplies, sidecarPathFor } from './preserve.js';
20
21
  import { classifyOwnership, ledgerListedPaths, recordedHashesWithLedger, } from './recordedOwnership.js';
21
22
  /**
22
23
  * Decide what to do with a single planned target.
@@ -161,8 +162,10 @@ export const CONFLICT_BATCH_THRESHOLD = 5;
161
162
  *
162
163
  * `recorded` is injectable for tests; by default it is read from the
163
164
  * manifest at `plan.root`, with the user-scoped ownership ledger laid over it.
165
+ * `logged` likewise; by default it is the install transaction log, opened only
166
+ * for an entry `logProofApplies` to.
164
167
  */
165
- export function computeConflicts(plan, recordedOverride) {
168
+ export function computeConflicts(plan, recordedOverride, loggedOverride) {
166
169
  // Force returns before the manifest is read. As a default parameter the
167
170
  // map was built at call entry and discarded on the next line, so every
168
171
  // force-mode plan paid a read and a YAML parse for nothing.
@@ -173,6 +176,7 @@ export function computeConflicts(plan, recordedOverride) {
173
176
  // digests never reaches the branch that needs this, and an unconditional
174
177
  // read would charge every plan for an answer most of them discard.
175
178
  let ledgerFiles = null;
179
+ let logged;
176
180
  const ledgerLists = (p) => {
177
181
  ledgerFiles ??= ledgerListedPaths();
178
182
  return ledgerFiles.has(p);
@@ -197,6 +201,24 @@ export function computeConflicts(plan, recordedOverride) {
197
201
  const ownership = classifyOwnership(recorded.get(entry.path), onDisk);
198
202
  if (ownership === 'recorded-unchanged')
199
203
  continue;
204
+ // The writer's transaction-log proof, under the writer's own
205
+ // condition (`logProofApplies`): where it holds and the log records
206
+ // these bytes, the writer replaces the file, so the preview must
207
+ // not name it.
208
+ const recordedSha = recorded.get(entry.path);
209
+ const unproven = {
210
+ exists: true,
211
+ recordedSha256: recordedSha,
212
+ onDiskSha256: onDisk,
213
+ force: false,
214
+ ledgerListed: ledgerLists(entry.path),
215
+ recordedIsStaged: typeof recordedSha === 'string' && recordedSha === recorded.get(sidecarPathFor(entry.path)),
216
+ };
217
+ if (logProofApplies(unproven)) {
218
+ logged ??= loggedOverride ?? loggedWriteDigests();
219
+ if (wroteTheseBytes(entry.path, onDisk, logged))
220
+ continue;
221
+ }
200
222
  if (ownership === 'unknown' && plan.policy.knownPaths.has(entry.path))
201
223
  continue;
202
224
  // CANDIDACY, not the verdict, and it is computed BEFORE the
@@ -21,7 +21,8 @@ import * as fs from 'node:fs';
21
21
  import * as os from 'node:os';
22
22
  import * as path from 'node:path';
23
23
  import { mkdirp, pathExists, resolvePath, sha256OfFile } from './fsPrimitives.js';
24
- import { EXIT_COMPLETED_WITH_CONFLICTS, conflictSummaryMessage, decideDeployWrite, foreignSidecarMessage, isLedgerMigration, migrationPreservedMessage, preservedFileMessage, sidecarPathFor, } from './preserve.js';
24
+ import { EXIT_COMPLETED_WITH_CONFLICTS, conflictSummaryMessage, decideDeployWrite, foreignSidecarMessage, isLedgerMigration, logProofApplies, migrationPreservedMessage, preservedFileMessage, sidecarPathFor, } from './preserve.js';
25
+ import { loggedWriteDigests, wroteTheseBytes } from './loggedWrites.js';
25
26
  import { ledgerListedPaths, recordedHashesWithLedger } from './recordedOwnership.js';
26
27
  /**
27
28
  * Build a tracker bound to one installer's reporting surface.
@@ -37,6 +38,7 @@ export function createConflictTracker(host) {
37
38
  root: null,
38
39
  recorded: null,
39
40
  ledgerFiles: null,
41
+ logged: null,
40
42
  preserved: [],
41
43
  migrated: new Set(),
42
44
  staged: new Map(),
@@ -210,6 +212,7 @@ export function createConflictTracker(host) {
210
212
  conflictState.root = root;
211
213
  conflictState.recorded = null;
212
214
  conflictState.ledgerFiles = null;
215
+ conflictState.logged = null;
213
216
  conflictState.preserved = [];
214
217
  conflictState.migrated = new Set();
215
218
  conflictState.staged = new Map();
@@ -222,13 +225,21 @@ export function createConflictTracker(host) {
222
225
  */
223
226
  resolve(target, force, would) {
224
227
  const exists = pathExists(target);
225
- const base = {
228
+ const recordedSha256 = exists ? recordedHashFor(target) : undefined;
229
+ const unproven = {
226
230
  exists,
227
- recordedSha256: exists ? recordedHashFor(target) : undefined,
231
+ recordedSha256,
228
232
  onDiskSha256: exists ? sha256OfFile(target) : null,
229
233
  force,
230
234
  ledgerListed: exists && ledgerLists(target),
235
+ recordedIsStaged: typeof recordedSha256 === 'string' && recordedSha256 === recordedHashFor(sidecarPathFor(target)),
231
236
  };
237
+ // The log is opened only where the record alone would preserve
238
+ // without cause, and read once per run: this run's own appends
239
+ // cannot prove a file it has not reached yet.
240
+ const loggedWrite = logProofApplies(unproven) &&
241
+ wroteTheseBytes(target, unproven.onDiskSha256, (conflictState.logged ??= loggedWriteDigests()));
242
+ const base = { ...unproven, loggedWrite };
232
243
  // The packaged digest costs a temp write and a tag, so it is
233
244
  // computed only for the targets that can consume it. On a tree
234
245
  // where every ledger-listed path carries a digest — which is every