feature-factory 0.8.6 → 0.8.7
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 +8 -2
- package/bin/factory.js +10 -1
- package/package.json +1 -1
package/WORKFLOW.md
CHANGED
|
@@ -177,7 +177,8 @@ reporting anything:
|
|
|
177
177
|
A park that completes only step 1 is an unreported park with no recovery evidence, which is the state
|
|
178
178
|
this sequence exists to prevent. Verify step 2 the way an outside observer would: qualified status
|
|
179
179
|
reports `park_snapshot` as the published path, or `null` when no snapshot exists. Status reports the path
|
|
180
|
-
only while the snapshot is a complete copy of the live plane by the step 3 inventory
|
|
180
|
+
only while the snapshot is a complete copy of the live plane by the step 3 inventory — `factory.lock`
|
|
181
|
+
excluded, since a heartbeat may land between the copy and the read — and its manifest still
|
|
181
182
|
matches the live one byte for byte, so `null` also covers an interrupted or altered copy and a snapshot
|
|
182
183
|
left by an earlier park: neither is evidence for this park. Publishing again is what makes it correspond.
|
|
183
184
|
|
|
@@ -243,7 +244,12 @@ and "clean up the prior copy" are contradictory instructions once that rename ha
|
|
|
243
244
|
outside `P`; do not copy slice worktrees or any other part of `S`.
|
|
244
245
|
3. **Verify.** Build source and destination inventories exactly as the completed archive does — every
|
|
245
246
|
entry's relative path, type and mode, a SHA-256 for each regular file, a link target for each symlink,
|
|
246
|
-
sorted lexically — and require exact equality
|
|
247
|
+
sorted lexically — and require exact equality, **excluding the plane-root `factory.lock` only**. That
|
|
248
|
+
one entry is session liveness rather than run state and is the only thing in the plane designed to
|
|
249
|
+
change on a timer, so comparing it fails whenever a heartbeat lands between reading the source and
|
|
250
|
+
reading the copy. The exclusion is that exact path and nothing else: a `factory.lock` anywhere below
|
|
251
|
+
the plane root is run state and must match. Qualified status excludes the same single path for the same
|
|
252
|
+
reason. An unverified staging tree is never published.
|
|
247
253
|
4. **Commit.** With no snapshot at the canonical path, rename `.staging-$R` onto it; that rename is the
|
|
248
254
|
commit point. With one present, first rename the canonical snapshot to `.prior-$R`, then rename
|
|
249
255
|
`.staging-$R` onto the canonical path; that second rename is the commit point. If the first rename
|
package/bin/factory.js
CHANGED
|
@@ -23,7 +23,7 @@ import { resolveSpawnExecutable } from "../core/executable.js";
|
|
|
23
23
|
import { dispatchInitPublication } from "./init-publication.js";
|
|
24
24
|
import { CONTROL_PLANE, SCHEMA_VERSION, GATE_NAMES, GATE_STATUSES, MODES, SLICE_STATUSES, STEP_STATUSES, TERMINAL_STATUSES, repositoryRelativePath, validateRun } from "../state/schema.js";
|
|
25
25
|
import {
|
|
26
|
-
claimSessionLock, inspectSessionLock, refreshSessionLock, releaseSessionLock, SessionLockHeldError,
|
|
26
|
+
claimSessionLock, inspectSessionLock, refreshSessionLock, releaseSessionLock, SESSION_LOCK_FILE, SessionLockHeldError,
|
|
27
27
|
} from "../state/session-lock.js";
|
|
28
28
|
|
|
29
29
|
export const COMMANDS = Object.freeze({
|
|
@@ -175,6 +175,15 @@ function briefDigestFor(decision, state, runDir) {
|
|
|
175
175
|
function planeInventory(root) {
|
|
176
176
|
const entries = [];
|
|
177
177
|
const record = (rel, full) => {
|
|
178
|
+
// The session lock is liveness, not run state, and it is the one entry in the plane designed to change
|
|
179
|
+
// on a timer. Comparing it made every snapshot invalid within one heartbeat: a live park published a
|
|
180
|
+
// complete, byte-correct plane and `status` reported `park_snapshot: null` eleven seconds later,
|
|
181
|
+
// because the copy held `heartbeat_at` 23:28:25 and the plane had moved to 23:28:36. The whole point
|
|
182
|
+
// of the field was to answer "did the driver publish it", and it answered "no" for a snapshot that was
|
|
183
|
+
// there -- the same disagree-with-your-own-description defect the 0.8.3 work existed to remove,
|
|
184
|
+
// reintroduced by the check built to remove it. Every existing test publishes and reads back with no
|
|
185
|
+
// heartbeat in between, which is why three review rounds and a real park all missed it.
|
|
186
|
+
if (rel === SESSION_LOCK_FILE) return;
|
|
178
187
|
const stat = lstatSync(full);
|
|
179
188
|
const mode = (stat.mode & 0o7777).toString(8);
|
|
180
189
|
if (stat.isSymbolicLink()) entries.push(`${rel} l ${mode} ${readlinkSync(full)}`);
|