pi-daddy 0.25.2 → 0.26.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.
@@ -285,7 +285,8 @@ export function childEnv(input: ChildEnvInput): Record<string, string> {
285
285
  // whatever was there before — the parent's own, unclamped `PI_GRANTS_APPROVED` — visible to every child.
286
286
  // `parseList("")` is `[]`, so an empty value reads back exactly as an absent one.
287
287
  env[ENV_APPROVED] = inheritApprovals(input.approved ?? [], inheritable).join(",");
288
- if (input.ledgerPath) env[ENV_LEDGER] = input.ledgerPath;
288
+ // Empty is an explicit one-run ledger opt-out and must overwrite a prior publication too.
289
+ if (input.ledgerPath !== undefined) env[ENV_LEDGER] = input.ledgerPath;
289
290
  return env;
290
291
  }
291
292
 
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import { lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import {
5
5
  appendWorkLedgerEventOnce,
@@ -101,8 +101,15 @@ export async function declareWork(input: DeclareWorkInput): Promise<WorkFrozen<D
101
101
  }
102
102
  if (!ID.test(input.id) || typeof input.outcome !== "string" || !input.outcome.trim()) throw new TypeError("work declaration requires an identifier and non-empty outcome");
103
103
  const resolved = paths(input), outcomeDigest = sha256(input.outcome.trim());
104
- await mkdir(dirname(resolved.ledgerPath), { recursive: true, mode: 0o700 });
105
- await mkdir(dirname(resolved.statePath), { recursive: true, mode: 0o700 });
104
+ const projectPi = dirname(resolved.ledgerPath);
105
+ try {
106
+ // Create only a new project control directory as private; never chmod or repurpose an existing one.
107
+ await mkdir(projectPi, { mode: 0o700 });
108
+ } catch (error) {
109
+ if ((error as { code?: string }).code !== "EEXIST") throw error;
110
+ const existing = await lstat(projectPi);
111
+ if (!existing.isDirectory() || existing.isSymbolicLink()) throw Error("existing .pi state is not a directory; it was not followed or changed");
112
+ }
106
113
  return withFileLock(resolved.statePath, "declared work", async () => {
107
114
  const existing = await loadDeclaredWork(resolved.statePath);
108
115
  if (existing) {