feature-factory 0.7.1 → 0.7.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.
package/WORKFLOW.md CHANGED
@@ -1,8 +1,17 @@
1
1
  # Feature Factory — host-neutral workflow
2
2
 
3
3
  This document is the authoritative, host-neutral feature-factory workflow. It is not a discoverable
4
- skill by itself. A host integration must ship its own `SKILL.md`, place an exact copy of this file next
5
- to that skill as `WORKFLOW.md`, and require the run driver to read it completely before any effect.
4
+ skill by itself. A host integration must ship its own `SKILL.md` and place an exact copy of this file next
5
+ to that skill as `WORKFLOW.md`.
6
+
7
+ **Where the driver reads this file from, and when.** `factory init` stages an exact copy into the run
8
+ directory and returns its path as `workflow`. The driver reads THAT copy, completely, before any state read,
9
+ dispatch, gate, or factory command other than `init` itself — admission and the `init` invocation are
10
+ specified by the host `SKILL.md`, everything after them here. A host whose agents may read outside the
11
+ workspace may instead read the copy beside its skill; a host that denies such reads must use the staged copy,
12
+ because the packaged one is unreadable there and a run that depends on it fails on a permission refusal
13
+ rather than on anything about the work. Either way the bytes are identical, and a driver that cannot read
14
+ either copy stops without effects.
6
15
 
7
16
  The host adapter owns only invocation admission, placement, session identity, specialist dispatch, and
8
17
  result delivery. This workflow owns the durable chain, gates, repository lifecycle, evidence rules, and
package/bin/factory.js CHANGED
@@ -17,7 +17,7 @@ import { assertPublicationReady, assertReviewBinding, observeMergeProof, readEvi
17
17
  import { readRepositoryConfig, RepositoryConfigError } from "../observe/repository-config.js";
18
18
  import { reverifyRepair } from "../observe/repair-reverification.js";
19
19
  import { archiveReviewAttempt } from "../state/review-archive.js";
20
- import { writeProtectedJsonAtomic } from "../core/atomic-write.js";
20
+ import { writeProtectedFileAtomic, writeProtectedJsonAtomic } from "../core/atomic-write.js";
21
21
  import { enforceEffectivePushTarget } from "../core/effective-push.js";
22
22
  import { resolveSpawnExecutable } from "../core/executable.js";
23
23
  import { dispatchInitPublication } from "./init-publication.js";
@@ -1336,9 +1336,21 @@ export async function dispatchInit(positional, flags, operations = INIT_OPERATIO
1336
1336
  } catch (error) {
1337
1337
  throw new CliError(`final manifest validation failed for sandbox '${S}'; sandbox was retained`, { cause: error });
1338
1338
  }
1339
+ // `external_directory` is denied for every agent and the canonical workflow ships outside the workspace, so
1340
+ // without this the driver cannot perform the read its own contract requires; the only other way past it is
1341
+ // `--auto`, which blanket-approves every permission ask. Protected writer, not a copy: `bootstrap` has run
1342
+ // repository-controlled commands by now, so a planted destination symlink would redirect a plain write out
1343
+ // of the sandbox, and `assertSafeTarget` rechecks immediately before the rename. Before publication, so a
1344
+ // failure aborts init while a retry is still possible.
1345
+ const workflowBytes = readFileSync(new URL("../WORKFLOW.md", import.meta.url));
1346
+ await writeProtectedFileAtomic(runDir, "WORKFLOW.md", workflowBytes);
1347
+ const workflow = joinPath(runDir, "WORKFLOW.md");
1348
+ if (!readFileSync(workflow).equals(workflowBytes)) {
1349
+ throw new CliError(`staged workflow at '${workflow}' does not match the canonical copy; sandbox was retained`);
1350
+ }
1339
1351
  const { observedRun } = await dispatchInitPublication({ runDir, sandboxPath: S, candidate: run, finalGuard: proveContainedBranch });
1340
1352
  return emit(flags, {
1341
- run_id: observedRun.run_id, run_dir: runDir, sandbox_path: proof.sandboxPath,
1353
+ run_id: observedRun.run_id, run_dir: runDir, workflow, sandbox_path: proof.sandboxPath,
1342
1354
  branch: observedRun.branch, worktree: observedRun.worktree, pr_base: observedRun.pr_base,
1343
1355
  status: observedRun.status, mode: observedRun.mode,
1344
1356
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feature-factory",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Durable, observed control plane for /feature runs. Host-agnostic: no opencode dependency.",
5
5
  "type": "module",
6
6
  "license": "MIT",