← back to Tk 10965 Zero Price Analysis
Freeze Vienna executor scope and journal offline mutations safely
76455836991e5c5af27d6fde81b08e4c97e04c7d · 2026-09-08 05:52:25 -0700 · Steve Abrams
Files touched
M README.mdA VIENNA-EXECUTOR.mdM apply-fix.mjsM cli-args.mjsM test/cli-preflight.test.mjsA test/fixtures/vienna-manifest.jsonA test/fixtures/vienna-manifest.sha256A test/vienna-boundaries.cjsA test/vienna-executor.test.mjsA verification/vienna-e2e-proof.jsonA vienna-executor.mjsA vienna-offline-adapter.mjs
Diff
commit 76455836991e5c5af27d6fde81b08e4c97e04c7d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 8 05:52:25 2026 -0700
Freeze Vienna executor scope and journal offline mutations safely
---
README.md | 2 +
VIENNA-EXECUTOR.md | 82 ++++++++++++++++++
apply-fix.mjs | 126 ++-------------------------
cli-args.mjs | 52 +++++------
test/cli-preflight.test.mjs | 17 ++--
test/fixtures/vienna-manifest.json | 78 +++++++++++++++++
test/fixtures/vienna-manifest.sha256 | 1 +
test/vienna-boundaries.cjs | 34 ++++++++
test/vienna-executor.test.mjs | 120 ++++++++++++++++++++++++++
verification/vienna-e2e-proof.json | 118 +++++++++++++++++++++++++
vienna-executor.mjs | 162 +++++++++++++++++++++++++++++++++++
vienna-offline-adapter.mjs | 24 ++++++
12 files changed, 659 insertions(+), 157 deletions(-)
diff --git a/README.md b/README.md
index 27563b8..facd60f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+> **TK-11300: live inventory execution is disabled.** The actual CLI now requires a frozen manifest and supports an offline adapter only. Read [VIENNA-EXECUTOR.md](VIENNA-EXECUTOR.md) before using historical commands below.
+
# TK-10965 — zero-price-orderable analysis + verification harness
READ-ONLY analysis of the `zero-price-orderable-canary` FAIL (underlying alert TK-10963):
diff --git a/VIENNA-EXECUTOR.md b/VIENNA-EXECUTOR.md
new file mode 100644
index 0000000..f88c409
--- /dev/null
+++ b/VIENNA-EXECUTOR.md
@@ -0,0 +1,82 @@
+# Vienna executor local increment — TK-11300
+
+**Production transport is disabled.** `designer-laboratory-sandbox.myshopify.com`
+is the production store despite its name. This code is an actual CLI manifest
+consumer and a local, file-backed operational rehearsal. It cannot perform live
+inventory recovery. TK-11299 retains authoritative paginated acquisition, actual
+shop identity, manifest review, token scope verification and explicit live approval.
+No synthetic fixture shop or catalog ID is evidence of a live identity.
+
+`apply-fix.mjs` no longer reads secrets or freshly enumerates products. Historical
+`--all`, numeric `--canary`, and restore-map rollback commands fail closed. The
+old token wrapper must not be used; live transport is absent, rather than hidden
+behind an environment switch. The fixed domain and separate `--expected-store-id`
+are validated against the manifest before the adapter is loaded, then against the
+adapter's own shop result. There are no catalog or price mutations.
+
+All modes except help require `--manifest FILE --expected-sha256 HASH
+--expected-store-id SHOP_GID`. The SHA256 must come from the independent review
+of the exact bytes; do not compute a fresh hash of unreviewed input as approval.
+`--plan` and its `--enumerate` alias only read the manifest and print JSON. They
+refuse adapter/journal options and create no files. Local execution additionally
+requires `--offline-state FILE --journal FILE`. Resuming either original mode, or
+`--rollback`, requires `--journal-sha256 HASH` from a separately trusted checkpoint.
+Never use historical restore maps as journals.
+
+Manifest v1 has exactly `version`, `scope`, `store`, `createdAt`, `expiresAt`,
+`records` and `canary`. `scope` is `{vendor:"Phillipe Romano",line:"Vienna"}`.
+Every record pins product, variant, inventory item and location GIDs plus the
+product/variant preconditions, positive original `onHand` and `target:0`. Every
+record must be a non-sample, zero-price, tracked DENY variant in the frozen line.
+Expiry and maximum age are 24 hours, with ISO UTC timestamps and no future issue
+date. `canary` contains explicit `inventoryItemId@locationId` keys; every frozen
+location of each selected product must be included. The test canary deliberately
+selects the third record to prove this is not a first-N approximation.
+
+Hashes are operator trust anchors for exact bytes. Neither a manifest hash nor a
+journal checkpoint establishes authoritative catalog completeness, arbitrary
+journal provenance, or legal operational authorization. Review and transport
+adapter integration remain separate. Structurally valid fabricated history with
+a newly trusted checkpoint cannot be distinguished from operator-approved input.
+
+Execution compares the full exact record before each compare-and-set write,
+fsyncs intent before the adapter call, appends/fsyncs only confirmed successes,
+then re-reads the exact identity and quantity before appending verification.
+Confirmed rejections are recorded separately and can be retried after another
+precondition comparison. Successfully verified mutations are only re-read on
+resume; never repeated. Rollback selects confirmed successful records in that
+journal, including successful locations from a partially failed product. Rollback
+uses the same intent/result/post-read protocol. Once rollback begins, apply
+cannot reopen a rolled-back run.
+
+An exception or unrecognized result after intent is ambiguous. Missing success
+never proves failure: retry and rollback block before adapter initialization.
+There is intentionally no automatic reconciliation or force flag. A confirmed
+success with interrupted verification can only advance after an exact read; it
+cannot authorize a duplicate mutation. A crashed lock also blocks invocation;
+it must be independently reviewed alongside the journal before any later work.
+Ordinary completed invocation releases its own lock. Checkpoints are rechecked
+under the exclusive journal lock to prevent a concurrent stale replay.
+
+Journal parsing validates the chain, sequence, schema, manifest/store binding,
+frozen selection, exact quantities and valid per-record state transitions. It
+rejects truncated, malformed, unbound or injected histories before the adapter.
+All data reads reject symlinks/hardlinks. Fixture state replacement uses fsynced
+exclusive temporary files, identity checks, atomic rename and directory fsync.
+Failed replacement retains its temporary file and prior durable fixture state.
+
+## Verification and retained evidence
+
+Run `node --test test/cli-preflight.test.mjs test/vienna-executor.test.mjs`.
+Set `VIENNA_EVIDENCE_DIR` to an existing local evidence directory to retain each
+fixture, manifest, journal, state and child CLI output there. Test time is fixed
+by the deny-by-default process preload; the application has no clock override.
+The guard denies canonical secrets, network and child processes and permits only
+specified fixture files. Calibration deliberately hits real protected entry
+points and proves denial. No real credentials, network or inventory are used.
+
+The former 42-check CLI proof remains in git at source commit
+`7fb358ca8948290915678dd2c34f83a02d5a0532` and its existing verification files.
+Legacy write-mode test assertions now require manifest rejection rather than
+reaching runtime initialization. The new proof is `verification/vienna-e2e-proof.json`.
+All fixture directories and failed-run logs are retained; no cleanup is required.
diff --git a/apply-fix.mjs b/apply-fix.mjs
index 77c7b00..2f9e0be 100644
--- a/apply-fix.mjs
+++ b/apply-fix.mjs
@@ -1,126 +1,12 @@
#!/usr/bin/env node
-// TK-10965 — APPROVED remediation (Steve "go", 2026-08-30).
-// Make the $0 non-Sample variant NON-orderable by setting on_hand=0 at every location
-// that currently holds stock (keep inventoryPolicy=DENY, keep tracked). Does NOT touch
-// the $4.25 Sample variant. Fully reversible via the restore-map written before any write.
-//
-// Usage:
-// node apply-fix.mjs --enumerate # scan + write restore-map, NO writes
-// node apply-fix.mjs --canary [N=50] # fix first N, verify
-// node apply-fix.mjs --all # fix everything remaining
-// node apply-fix.mjs --rollback FILE # restore on_hand from a restore-map file
-import fs from 'node:fs';
-import path from 'node:path';
+// TK-11300: the actual CLI is fail-closed; historical approvals do not authorize
+// fresh scope. No credential loading, broad enumeration or production adapter.
import { parseArgs, USAGE } from './cli-args.mjs';
-
-// Preflight must precede runtime directories, credentials and external calls.
let cli;
try { cli = parseArgs(process.argv.slice(2)); }
catch (error) { console.error(`${error.message}\n\n${USAGE}`); process.exit(2); }
if (cli.mode === '--help') { console.log(USAGE); process.exit(0); }
-const arg = cli.mode;
-const arg2 = cli.file;
-
-const HERE = path.dirname(new URL(import.meta.url).pathname);
-const RUNS = path.join(HERE, 'runs'); fs.mkdirSync(RUNS, { recursive: true });
-const env = fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env', 'utf8');
-const val = k => (env.match(new RegExp('^' + k + '=(.*)$', 'm')) || [])[1]?.trim();
-// The canonical secrets-manager SHOPIFY_ADMIN_TOKEN is products-scoped only (no write_inventory).
-// Per TK-10965 guardrail, prefer a sanctioned inventory-write token supplied via the environment
-// (the DW importer runtime's SHOPIFY_ADMIN_ACCESS_TOKEN, which carries read/write_inventory).
-const DOM = process.env.SHOPIFY_STORE_DOMAIN || val('SHOPIFY_STORE_DOMAIN');
-const TOK = process.env.SHOPIFY_ADMIN_ACCESS_TOKEN || process.env.SHOPIFY_ADMIN_TOKEN || val('SHOPIFY_ADMIN_TOKEN');
-const API = `https://${DOM}/admin/api/2024-10/graphql.json`;
-
-if (!DOM || !TOK) throw new Error('Missing SHOPIFY_STORE_DOMAIN or SHOPIFY_ADMIN_TOKEN');
-
-async function gql(q, v) {
- for (let a = 0; a < 8; a++) {
- const r = await fetch(API, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: q, variables: v }) });
- const j = await r.json();
- if (j.errors) { if (JSON.stringify(j.errors).includes('THROTTLED')) { await new Promise(s => setTimeout(s, 1800 * (a + 1))); continue; } throw new Error(JSON.stringify(j.errors)); }
- return j.data;
- }
- throw new Error('gql retries');
-}
-
-const VAR_Q = `variants(first:20){nodes{id title price availableForSale inventoryPolicy inventoryItem{id tracked inventoryLevels(first:10){nodes{location{id} quantities(names:["on_hand"]){name quantity}}}}}}`;
-const isBad = v => !/sample/i.test(v.title || '') && Number(v.price) === 0 && v.availableForSale === true;
-
-async function enumerateBad() {
- const out = []; const seen = new Set();
- // PR via the quote-only tag search (efficient, matches the canary's 1281 set)
- const searches = [
- `status:active AND (tag:'quote-only' OR tag:'Quote Only' OR tag:'quote_only' OR tag:'Quote-Only')`,
- `status:active AND vendor:'Fentucci Naturals'`,
- ];
- for (const q of searches) {
- let after = null, pages = 0;
- do {
- const d = await gql(`query($q:String!,$after:String){products(first:100,query:$q,after:$after){pageInfo{hasNextPage endCursor} nodes{id title vendor ${VAR_Q}}}}`, { q, after });
- for (const p of d.products.nodes) {
- if (seen.has(p.id)) continue; seen.add(p.id);
- const v = p.variants.nodes.find(isBad);
- if (!v) continue;
- const levels = (v.inventoryItem.inventoryLevels.nodes || [])
- .map(l => ({ locationId: l.location.id, onHand: (l.quantities.find(x => x.name === 'on_hand')?.quantity) ?? 0 }))
- .filter(l => l.onHand > 0);
- out.push({ productId: p.id, title: p.title, vendor: p.vendor, variantId: v.id, inventoryItemId: v.inventoryItem.id, levels });
- }
- after = d.products.pageInfo.hasNextPage ? d.products.pageInfo.endCursor : null; pages++;
- } while (after && pages < 60);
- }
- return out;
-}
-
-async function requireInventoryWriteScope() {
- const d = await gql(`query{currentAppInstallation{accessScopes{handle}}}`, {});
- const scopes = d.currentAppInstallation.accessScopes.map(s => s.handle).sort();
- if (!scopes.includes('write_inventory')) {
- throw new Error(`BLOCKED: token lacks write_inventory (present scopes: ${scopes.join(', ')})`);
- }
- return scopes;
-}
-
-async function setOnHand(inventoryItemId, locationId, compareQuantity, quantity) {
- const d = await gql(`mutation($input:InventorySetQuantitiesInput!){inventorySetQuantities(input:$input){userErrors{field message code}}}`,
- { input: { name: 'on_hand', reason: 'correction', quantities: [{ inventoryItemId, locationId, compareQuantity, quantity }] } });
- const errs = d.inventorySetQuantities.userErrors;
- if (errs && errs.length) throw new Error(JSON.stringify(errs));
-}
-
-const stamp = () => new Date().toISOString().replace(/[:.]/g, '-');
-
-if (arg === '--rollback') {
- await requireInventoryWriteScope();
- const map = JSON.parse(fs.readFileSync(arg2, 'utf8'));
- let n = 0; for (const it of map.items) for (const l of it.levels) { await setOnHand(it.inventoryItemId, l.locationId, 0, l.onHand); n++; }
- console.log(`ROLLBACK: restored ${n} inventory levels from ${arg2}`);
- process.exit(0);
-}
-
-const bad = await enumerateBad();
-const restorePath = path.join(RUNS, `restore-map-${stamp()}.json`);
-fs.writeFileSync(restorePath, JSON.stringify({ ts: new Date().toISOString(), count: bad.length, items: bad }, null, 2));
-console.log(`Enumerated ${bad.length} affected products. Restore-map: ${restorePath}`);
-const byV = {}; for (const b of bad) byV[b.vendor] = (byV[b.vendor] || 0) + 1;
-console.log('by vendor:', JSON.stringify(byV));
-
-if (arg === '--enumerate') { console.log('Enumerate-only. No writes.'); process.exit(0); }
-
-const scopes = await requireInventoryWriteScope();
-console.log(`Inventory write scope verified (${scopes.length} total scopes).`);
-
-const limit = arg === '--canary' ? cli.count : bad.length;
-const target = bad.slice(0, limit);
-console.log(`Applying on_hand=0 to ${target.length} products (${arg})...`);
-let done = 0, fail = 0;
-for (const b of target) {
- try {
- if (!b.levels.length) { done++; continue; } // already 0 everywhere
- for (const l of b.levels) await setOnHand(b.inventoryItemId, l.locationId, l.onHand, 0);
- done++;
- if (done % 100 === 0) console.log(` ...${done}/${target.length}`);
- } catch (e) { fail++; console.error(` FAIL ${b.title}: ${e.message}`); }
-}
-console.log(`Applied: ${done} ok, ${fail} failed of ${target.length}. Restore-map: ${restorePath}`);
+try {
+ const { run } = await import('./vienna-executor.mjs');
+ console.log(JSON.stringify(await run(cli), null, 2));
+} catch (error) { console.error('BLOCKED: '+error.message); process.exitCode=2; }
diff --git a/cli-args.mjs b/cli-args.mjs
index 5088c2b..3decc38 100644
--- a/cli-args.mjs
+++ b/cli-args.mjs
@@ -1,29 +1,29 @@
-export const USAGE = `Usage: node apply-fix.mjs <mode>
- --help Show usage without initialization
- --enumerate Scan and save restore map; no inventory writes
- --canary [N] Apply to first N results (default: 50)
- --all Apply to all freshly enumerated results
- --rollback FILE Restore quantities from FILE`;
-
+export const USAGE = `Usage: node apply-fix.mjs <mode> [options]
+ --help Show usage without initialization
+ --plan | --enumerate Validate frozen manifest and print plan; no adapter or journal
+ --canary | --all Execute frozen canary membership or complete manifest OFFLINE
+ --rollback Restore only confirmed journal successes OFFLINE
+Required: --manifest FILE --expected-sha256 HASH --expected-store-id SHOP_GID
+Execution: --offline-state FILE --journal FILE
+Existing journal: --journal-sha256 HASH (external checkpoint required)
+Live execution is disabled. Legacy scans, numeric canaries and restore maps are refused.`;
export function parseArgs(args) {
- const [mode, value] = args;
- if (!['--help', '--enumerate', '--canary', '--all', '--rollback'].includes(mode)) {
- throw new Error(mode ? `Unrecognized mode: ${mode}` : 'A mode is required.');
+ const [mode, ...rest] = args;
+ if (!['--help','--plan','--enumerate','--canary','--all','--rollback'].includes(mode)) throw new Error('A recognized mode is required.');
+ if (mode === '--help') { if (rest.length) throw new Error('Unexpected arguments.'); return {mode}; }
+ const cli = {mode};
+ const options = new Set(['--manifest','--expected-sha256','--expected-store-id','--offline-state','--journal','--journal-sha256']);
+ for (let i=0;i<rest.length;i+=2) {
+ const [k,v] = rest.slice(i,i+2);
+ if (!options.has(k) || Object.hasOwn(cli,k) || !v?.trim() || v.startsWith('-')) throw new Error('Invalid, duplicate or missing option: ' + k);
+ cli[k] = v;
}
- if (mode === '--canary') {
- if (args.length > 2) throw new Error('Unexpected extra arguments for --canary.');
- const count = value === undefined ? 50 : Number(value);
- if ((value !== undefined && !/^[0-9]+$/.test(value)) || !Number.isSafeInteger(count) || count <= 0) {
- throw new Error('--canary count must be a positive safe integer.');
- }
- return { mode, count };
- }
- if (mode === '--rollback') {
- if (args.length !== 2 || !value.trim() || value.startsWith('-')) {
- throw new Error('--rollback requires exactly one file path (use ./ for a path beginning with a dash).');
- }
- return { mode, file: value };
- }
- if (args.length !== 1) throw new Error(`Unexpected extra arguments for ${mode}.`);
- return { mode };
+ for (const k of ['--manifest','--expected-sha256','--expected-store-id']) if (!cli[k]) throw new Error('Required: ' + k);
+ for (const k of ['--expected-sha256','--journal-sha256']) if (cli[k] && !/^[a-f0-9]{64}$/.test(cli[k])) throw new Error('Invalid SHA256: '+k);
+ if (!/^gid:\/\/shopify\/Shop\/[1-9][0-9]*$/.test(cli['--expected-store-id'])) throw new Error('Invalid expected store ID.');
+ const plan = ['--plan','--enumerate'].includes(mode);
+ if (plan && ['--offline-state','--journal','--journal-sha256'].some(k=>cli[k])) throw new Error('Plan cannot initialize adapter or journal.');
+ if (!plan && (!cli['--offline-state'] || !cli['--journal'])) throw new Error('Live execution disabled; offline-state and journal required.');
+ if (mode==='--rollback' && !cli['--journal-sha256']) throw new Error('Rollback requires pinned journal SHA256.');
+ return cli;
}
diff --git a/test/cli-preflight.test.mjs b/test/cli-preflight.test.mjs
index b734bb9..ad59bc7 100644
--- a/test/cli-preflight.test.mjs
+++ b/test/cli-preflight.test.mjs
@@ -30,18 +30,13 @@ test('--help is safe at actual entry point', () => {
assert.match(result.stdout, /Usage: node apply-fix.mjs/);
assert.deepEqual(result.attempts, []);
});
-const recognized = [
- [['--enumerate'], {mode:'--enumerate'}], [['--all'], {mode:'--all'}],
- [['--canary'], {mode:'--canary',count:50}], [['--canary','1'], {mode:'--canary',count:1}],
- [['--canary','9007199254740991'], {mode:'--canary',count:9007199254740991}],
- [['--rollback','file.json'], {mode:'--rollback',file:'file.json'}],
- [['--rollback','./--file.json'], {mode:'--rollback',file:'./--file.json'}]
-];
-for (const [args, expected] of recognized) test('recognized mode reaches blocked initialization only: ' + JSON.stringify(args), () => {
- assert.deepEqual(parseArgs(args), expected);
+// Historical accepted write syntax now fails closed without a frozen manifest.
+const recognized = [['--enumerate'],['--all'],['--canary'],['--canary','1'],['--canary','9007199254740991'],['--rollback','file.json'],['--rollback','./--file.json']];
+for (const args of recognized) test('legacy mode requires frozen inputs: ' + JSON.stringify(args), () => {
+ assert.throws(() => parseArgs(args));
const result = run(args);
- assert.notEqual(result.status, 0);
- assert.deepEqual(result.attempts, ['fs.mkdirSync'], result.stderr);
+ assert.equal(result.status, 2, result.stderr);
+ assert.deepEqual(result.attempts, [], result.stderr);
});
if (process.env.CLI_BASELINE_ENTRY) for (const args of [[], ['--help'], ['--typo']]) {
test('prepatch entry exposes missing guard: ' + JSON.stringify(args), () => {
diff --git a/test/fixtures/vienna-manifest.json b/test/fixtures/vienna-manifest.json
new file mode 100644
index 0000000..7172921
--- /dev/null
+++ b/test/fixtures/vienna-manifest.json
@@ -0,0 +1,78 @@
+{
+ "version": 1,
+ "scope": {
+ "vendor": "Phillipe Romano",
+ "line": "Vienna"
+ },
+ "store": {
+ "domain": "designer-laboratory-sandbox.myshopify.com",
+ "id": "gid://shopify/Shop/900000000001"
+ },
+ "createdAt": "2026-09-08T11:00:00.000Z",
+ "expiresAt": "2026-09-09T11:00:00.000Z",
+ "records": [
+ {
+ "productId": "gid://shopify/Product/1",
+ "variantId": "gid://shopify/ProductVariant/1",
+ "inventoryItemId": "gid://shopify/InventoryItem/1",
+ "locationId": "gid://shopify/Location/1",
+ "vendor": "Phillipe Romano",
+ "line": "Vienna",
+ "title": "Vienna Fixture 1",
+ "variantTitle": "Full Material",
+ "price": 0,
+ "inventoryPolicy": "DENY",
+ "tracked": true,
+ "onHand": 2026,
+ "target": 0
+ },
+ {
+ "productId": "gid://shopify/Product/1",
+ "variantId": "gid://shopify/ProductVariant/1",
+ "inventoryItemId": "gid://shopify/InventoryItem/1",
+ "locationId": "gid://shopify/Location/2",
+ "vendor": "Phillipe Romano",
+ "line": "Vienna",
+ "title": "Vienna Fixture 1",
+ "variantTitle": "Full Material",
+ "price": 0,
+ "inventoryPolicy": "DENY",
+ "tracked": true,
+ "onHand": 2026,
+ "target": 0
+ },
+ {
+ "productId": "gid://shopify/Product/2",
+ "variantId": "gid://shopify/ProductVariant/2",
+ "inventoryItemId": "gid://shopify/InventoryItem/2",
+ "locationId": "gid://shopify/Location/1",
+ "vendor": "Phillipe Romano",
+ "line": "Vienna",
+ "title": "Vienna Fixture 2",
+ "variantTitle": "Full Material",
+ "price": 0,
+ "inventoryPolicy": "DENY",
+ "tracked": true,
+ "onHand": 2026,
+ "target": 0
+ },
+ {
+ "productId": "gid://shopify/Product/3",
+ "variantId": "gid://shopify/ProductVariant/3",
+ "inventoryItemId": "gid://shopify/InventoryItem/3",
+ "locationId": "gid://shopify/Location/1",
+ "vendor": "Phillipe Romano",
+ "line": "Vienna",
+ "title": "Vienna Fixture 3",
+ "variantTitle": "Full Material",
+ "price": 0,
+ "inventoryPolicy": "DENY",
+ "tracked": true,
+ "onHand": 2026,
+ "target": 0
+ }
+ ],
+ "canary": [
+ "gid://shopify/InventoryItem/2@gid://shopify/Location/1"
+ ]
+}
diff --git a/test/fixtures/vienna-manifest.sha256 b/test/fixtures/vienna-manifest.sha256
new file mode 100644
index 0000000..ae6b935
--- /dev/null
+++ b/test/fixtures/vienna-manifest.sha256
@@ -0,0 +1 @@
+1b21ab81983284920c22f857a1480741335b5513d003a23b38f9d1c2378bc1c7
diff --git a/test/vienna-boundaries.cjs b/test/vienna-boundaries.cjs
new file mode 100644
index 0000000..b3dff05
--- /dev/null
+++ b/test/vienna-boundaries.cjs
@@ -0,0 +1,34 @@
+// Calibrated deny-by-default process boundary guard; fixture I/O is explicitly allowlisted.
+const fs=require('node:fs'),url=require('node:url');
+const {syncBuiltinESMExports}=require('node:module');
+const rawWrite=fs.writeSync.bind(fs), rawOpen=fs.openSync.bind(fs), rawClose=fs.closeSync.bind(fs);
+const reads=new Set(JSON.parse(process.env.VIENNA_ALLOWED_READS||'[]'));
+const writes=new Set(JSON.parse(process.env.VIENNA_ALLOWED_WRITES||'[]'));
+const dirs=new Set(JSON.parse(process.env.VIENNA_ALLOWED_DIRS||'[]'));
+const fdPaths=new Map();
+const p=x=>x instanceof URL?url.fileURLToPath(x):typeof x==='number'?fdPaths.get(x):String(x);
+function deny(name){rawWrite(2,'BOUNDARY_DENIED:'+name+'\n');throw new Error('Boundary blocked: '+name);}
+function canRead(x){return reads.has(p(x))||writes.has(p(x))||dirs.has(p(x));}
+function canWrite(x){const name=p(x);return writes.has(name)||[...writes].some(base=>typeof name==='string'&&name.startsWith(base+'.pending-'));}
+fs.openSync=function(file,flags,...rest){
+ const writing=typeof flags==='number'?Boolean(flags&(fs.constants.O_WRONLY|fs.constants.O_RDWR|fs.constants.O_CREAT|fs.constants.O_APPEND|fs.constants.O_TRUNC)):flags!=='r';
+ if(writing?!canWrite(file):!canRead(file)) return deny('fs.openSync');
+ const fd=rawOpen(file,flags,...rest);fdPaths.set(fd,p(file));return fd;
+};
+fs.closeSync=function(fd){const r=rawClose(fd);fdPaths.delete(fd);return r;};
+for(const name of ['readFileSync','readFile','createReadStream']){const orig=fs[name].bind(fs);fs[name]=function(file,...rest){if(!canRead(file))return deny('fs.'+name);return orig(file,...rest);};}
+for(const name of ['writeFileSync','appendFileSync']){const orig=fs[name].bind(fs);fs[name]=function(file,...rest){if(!canWrite(file))return deny('fs.'+name);if(process.env.VIENNA_FAIL_SUCCESS_APPEND==='1'&&String(rest[0]).includes('\"type\":\"success\"'))throw new Error('Simulated crash after confirmed mutation before success append');return orig(file,...rest);};}
+for(const name of ['mkdirSync','rmdirSync']){const orig=fs[name].bind(fs);fs[name]=function(file,...rest){if(!dirs.has(p(file)))return deny('fs.'+name);return orig(file,...rest);};}
+for(const name of ['writeSync','writevSync']){const orig=fs[name].bind(fs);fs[name]=function(fd,...rest){if(fd!==1&&fd!==2&&!canWrite(fd))return deny('fs.'+name);return orig(fd,...rest);};}
+const rawRename=fs.renameSync.bind(fs);fs.renameSync=function(from,to){if(!canWrite(from)||!writes.has(p(to)))return deny('fs.renameSync');return rawRename(from,to);};
+for(const name of ['writeFile','appendFile','rename','unlink','rm','rmdir','mkdir','copyFile','cp','link','symlink','truncate','chmod','chown','utimes','open','createWriteStream']) {
+ if(typeof fs[name]==='function') fs[name]=()=>deny('fs.'+name);
+ if(fs.promises[name])fs.promises[name]=()=>deny('fs.promises.'+name);
+}
+const pr=fs.promises.readFile.bind(fs.promises);fs.promises.readFile=async(file,...rest)=>{if(!canRead(file))return deny('fs.promises.readFile');return pr(file,...rest);};
+for(const [mod,names] of Object.entries({'node:child_process':['spawn','spawnSync','exec','execSync','execFile','fork'],'node:http':['request','get'],'node:https':['request','get'],'node:net':['connect','createConnection','createServer'],'node:tls':['connect'],'node:dns':['lookup','resolve'],'node:dgram':['createSocket']}))for(const name of names)require(mod)[name]=()=>deny(mod+'.'+name);
+require('node:net').Socket.prototype.connect=()=>deny('net.Socket.connect');
+globalThis.fetch=()=>deny('fetch');
+// Clock seam lives only in the test preload. The shipped CLI never accepts a clock override.
+Date.now=()=>Date.parse('2026-09-08T12:00:00.000Z');
+syncBuiltinESMExports();
diff --git a/test/vienna-executor.test.mjs b/test/vienna-executor.test.mjs
new file mode 100644
index 0000000..7ca5268
--- /dev/null
+++ b/test/vienna-executor.test.mjs
@@ -0,0 +1,120 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import path from 'node:path';
+import os from 'node:os';
+import {spawnSync} from 'node:child_process';
+import {fileURLToPath} from 'node:url';
+import {hash,DOMAIN,key} from '../vienna-executor.mjs';
+const root=fileURLToPath(new URL('..',import.meta.url));
+const evidence=process.env.VIENNA_EVIDENCE_DIR||os.tmpdir();
+const guard=path.join(root,'test/vienna-boundaries.cjs');
+const sourceFiles=['apply-fix.mjs','cli-args.mjs','vienna-executor.mjs','vienna-offline-adapter.mjs'].map(f=>path.join(root,f));
+const store={domain:DOMAIN,id:'gid://shopify/Shop/900000000001'}; // SYNTHETIC; never a live identity.
+function fixture(){
+ const dir=fs.mkdtempSync(path.join(evidence,'vienna-fixture-'));
+ const bytes=fs.readFileSync(path.join(root,'test/fixtures/vienna-manifest.json'));
+ assert.equal(hash(bytes),fs.readFileSync(path.join(root,'test/fixtures/vienna-manifest.sha256'),'utf8').trim());
+ const manifest=JSON.parse(bytes),records=manifest.records;
+ const f={dir,manifest,path:path.join(dir,'manifest.json'),state:path.join(dir,'state.json'),journal:path.join(dir,'journal.jsonl')};
+ fs.writeFileSync(f.path,JSON.stringify(manifest,null,2)+'\n');
+ fs.writeFileSync(f.state,JSON.stringify({fixture:true,version:1,store,records:structuredClone(records),calls:[],faults:{}},null,2)+'\n');
+ f.sha=hash(fs.readFileSync(f.path));return f;
+}
+function state(f){return JSON.parse(fs.readFileSync(f.state,'utf8'));}
+function updateState(f,fn){const s=state(f);fn(s);fs.writeFileSync(f.state,JSON.stringify(s,null,2)+'\n');}
+function journal(f){return fs.readFileSync(f.journal,'utf8').trimEnd().split('\n').map(x=>JSON.parse(x));}
+function invoke(f,mode='--all',extra=[],opts={}){
+ const args=[mode,'--manifest',f.path,'--expected-sha256',f.sha,'--expected-store-id',store.id];
+ if(!['--plan','--enumerate'].includes(mode))args.push('--offline-state',f.state,'--journal',f.journal);
+ if(opts.resume)args.push('--journal-sha256',hash(fs.readFileSync(f.journal)));
+ const result=spawnSync(process.execPath,['--require',guard,path.join(root,'apply-fix.mjs'),...args,...extra],{
+ encoding:'utf8',timeout:5000,env:{VIENNA_FAIL_SUCCESS_APPEND:opts.failSuccessAppend?'1':'',VIENNA_ALLOWED_READS:JSON.stringify([...sourceFiles,f.path,f.journal,...(!opts.noWrites||opts.allowAdapterRead?[f.state]:[])]),VIENNA_ALLOWED_WRITES:JSON.stringify(opts.noWrites?[]:[f.state,f.journal]),VIENNA_ALLOWED_DIRS:JSON.stringify(opts.noWrites?[]:[f.dir,f.journal+'.lock'])}
+ });
+ assert.ifError(result.error); assert.equal(result.signal,null,result.stderr);
+ fs.writeFileSync(path.join(f.dir,`result-${fs.readdirSync(f.dir).length}.json`),JSON.stringify({args,...result},null,2));
+ assert.doesNotMatch(result.stderr,/BOUNDARY_DENIED:/,result.stderr);
+ return result;
+}
+function fail(r,re){assert.equal(r.status,2,r.stdout+r.stderr);assert.match(r.stderr,re);}
+function unchanged(f,before){assert.equal(hash(fs.readFileSync(f.state)),before);assert.equal(fs.existsSync(f.journal),false);}
+for(const [name,change,re] of [
+ ['foreign vendor',m=>m.records[0].vendor='Fentucci Naturals',/scope/],
+ ['foreign line',m=>m.records[0].title='Other Line',/scope/],
+ ['malformed ID',m=>m.records[0].productId='gid://shopify/Product/*',/identity/],
+ ['sample injection',m=>m.records[0].variantTitle='Sample',/scope/],
+ ['duplicate item',m=>m.records.push(m.records[0]),/Duplicate/],
+ ['variant collision',m=>m.records[2].variantId=m.records[0].variantId,/collision/],
+ ['stale',m=>m.expiresAt='2026-09-08T11:59:59.000Z',/Stale/],
+ ['future',m=>m.createdAt='2026-09-08T12:01:00.000Z',/Stale/],
+ ['missing identity',m=>delete m.records[0].locationId,/Malformed/],
+ ['unknown field',m=>m.all=true,/Malformed/],
+ ['scope broadening',m=>m.scope.vendor='all',/scope/],
+ ['store mismatch',m=>m.store.id='gid://shopify/Shop/99',/Store/],
+ ['domain mismatch',m=>m.store.domain='other.myshopify.com',/Store/],
+ ['canary injection',m=>m.canary.push('foreign'),/Canary/],
+ ['split canary locations',m=>m.canary=[key(m.records[0])],/every frozen location/],
+ ['quantity injection',m=>m.records[0].onHand=-10,/scope/]
+])test('before adapter: '+name,()=>{const f=fixture();change(f.manifest);fs.writeFileSync(f.path,JSON.stringify(f.manifest));f.sha=hash(fs.readFileSync(f.path));const before=hash(fs.readFileSync(f.state));fail(invoke(f,'--all',[],{noWrites:true}),re);unchanged(f,before);});
+test('external hash rejects valid-looking foreign ID injection',()=>{const f=fixture(),before=hash(fs.readFileSync(f.state));f.manifest.records[0].productId='gid://shopify/Product/777';fs.writeFileSync(f.path,JSON.stringify(f.manifest));fail(invoke(f,'--all',[],{noWrites:true}),/SHA256/);unchanged(f,before);});
+test('malformed JSON and missing manifest fail before adapter',()=>{const f=fixture(),before=hash(fs.readFileSync(f.state));fs.writeFileSync(f.path,'{');f.sha=hash(fs.readFileSync(f.path));fail(invoke(f,'--all',[],{noWrites:true}),/JSON|property/);unchanged(f,before);f.path=path.join(f.dir,'missing.json');fail(invoke(f,'--all',[],{noWrites:true}),/ENOENT/);unchanged(f,before);});
+test('plan and enumerate have no adapter, journal or write effects',()=>{for(const mode of ['--plan','--enumerate']){const f=fixture(),before=hash(fs.readFileSync(f.state));const r=invoke(f,mode,[],{noWrites:true});assert.equal(r.status,0,r.stderr);assert.equal(JSON.parse(r.stdout).mutations,0);unchanged(f,before);}});
+test('adapter actual shop mismatch rejects before state or journal changes',()=>{const f=fixture();updateState(f,s=>s.store={...store,id:'gid://shopify/Shop/99'});const before=hash(fs.readFileSync(f.state));fail(invoke(f,'--all',[],{noWrites:true,allowAdapterRead:true}),/Adapter store/);unchanged(f,before);});
+test('happy all path exact multi-location postread; retry performs zero duplicate writes',()=>{const f=fixture();let r=invoke(f);assert.equal(r.status,0,r.stderr);assert.equal(JSON.parse(r.stdout).writes,4);assert.deepEqual(state(f).records.map(r=>r.onHand),[0,0,0,0]);assert.equal(journal(f).filter(e=>e.type==='verified').length,4);const sets=state(f).calls.filter(c=>c.method==='set').length;r=invoke(f,'--all',[],{resume:true});assert.equal(r.status,0,r.stderr);assert.equal(JSON.parse(r.stdout).writes,0);assert.equal(state(f).calls.filter(c=>c.method==='set').length,sets);});
+test('frozen canary uses explicit membership and cannot broaden on resume',()=>{const f=fixture();const r=invoke(f,'--canary');assert.equal(r.status,0,r.stderr);assert.deepEqual(state(f).records.map(r=>r.onHand),[2026,2026,0,2026]);assert.deepEqual(state(f).calls.filter(c=>c.method==='set').map(c=>c.key),f.manifest.canary);const before=hash(fs.readFileSync(f.state));fail(invoke(f,'--all',[],{resume:true,noWrites:true}),/broaden/);assert.equal(hash(fs.readFileSync(f.state)),before);});
+test('all locations of selected canary product execute',()=>{const f=fixture();f.manifest.canary=f.manifest.records.slice(0,2).map(key);fs.writeFileSync(f.path,JSON.stringify(f.manifest));f.sha=hash(fs.readFileSync(f.path));assert.equal(invoke(f,'--canary').status,0);assert.deepEqual(state(f).records.map(r=>r.onHand),[0,0,2026,2026]);});
+test('quantity and exact foreign identity drift prevent write',()=>{for(const change of [s=>s.records[0].onHand=9,s=>s.records[0].productId='gid://shopify/Product/888']){const f=fixture();updateState(f,change);fail(invoke(f),/precondition drift/);assert.equal(state(f).calls.filter(c=>c.method==='set').length,0);assert.equal(journal(f).length,1);}});
+test('partial multi-location rejection; rollback restores successes only and is idempotent',()=>{const f=fixture();updateState(f,s=>s.faults[key(s.records[1])]='reject');fail(invoke(f),/Confirmed mutation rejection/);assert.deepEqual(state(f).records.map(r=>r.onHand),[0,2026,2026,2026]);assert.equal(journal(f).filter(e=>e.type==='success').length,1);let r=invoke(f,'--rollback',[],{resume:true});assert.equal(r.status,0,r.stderr);assert.equal(JSON.parse(r.stdout).writes,1);assert.deepEqual(state(f).records.map(r=>r.onHand),[2026,2026,2026,2026]);const sets=state(f).calls.filter(c=>c.method==='set').length;r=invoke(f,'--rollback',[],{resume:true});assert.equal(r.status,0,r.stderr);assert.equal(state(f).calls.filter(c=>c.method==='set').length,sets);});
+test('confirmed rejection retry skips successes and compares untouched frozen rows',()=>{const f=fixture();updateState(f,s=>s.faults[key(s.records[1])]='reject');fail(invoke(f),/Confirmed/);updateState(f,s=>s.faults={});const r=invoke(f,'--all',[],{resume:true});assert.equal(r.status,0,r.stderr);assert.equal(state(f).calls.filter(c=>c.method==='set'&&c.key===key(f.manifest.records[0])).length,1);assert.deepEqual(state(f).records.map(r=>r.onHand),[0,0,0,0]);});
+for(const fault of ['throw-before','throw-after'])test('ambiguous '+fault+' blocks retry AND rollback before adapter',()=>{const f=fixture();updateState(f,s=>s.faults[key(s.records[0])]=fault);fail(invoke(f),/Simulated/);assert.equal(journal(f).at(-1).type,'intent');assert.equal(state(f).records[0].onHand,fault==='throw-after'?0:2026);for(const mode of ['--all','--rollback']){const before=hash(fs.readFileSync(f.state));fail(invoke(f,mode,[],{resume:true,noWrites:true}),/Unresolved mutation intent/);assert.equal(hash(fs.readFileSync(f.state)),before);}});
+test('rollback response ambiguity leaves unresolved intent and blocks further recovery',()=>{const f=fixture();assert.equal(invoke(f,'--canary').status,0);updateState(f,s=>s.faults[key(s.records[2])]='throw-after');fail(invoke(f,'--rollback',[],{resume:true}),/Simulated/);assert.equal(state(f).records[2].onHand,2026);assert.equal(journal(f).at(-1).direction,'rollback');fail(invoke(f,'--rollback',[],{resume:true,noWrites:true}),/Unresolved/);});
+test('postread failure retains confirmed success and blocks rollback on identity drift',()=>{const f=fixture();updateState(f,s=>s.faults[key(s.records[0])]='bad-postread');fail(invoke(f),/precondition drift/);assert.equal(journal(f).at(-1).type,'success');const sets=state(f).calls.filter(c=>c.method==='set').length;fail(invoke(f,'--rollback',[],{resume:true}),/precondition drift/);assert.equal(state(f).calls.filter(c=>c.method==='set').length,sets);});
+test('success before verification crash recovers by exact read without duplicate write',()=>{const f=fixture();updateState(f,s=>s.faults[key(s.records[0])]='bad-postread');fail(invoke(f),/precondition drift/);updateState(f,s=>{s.records[0].variantId=f.manifest.records[0].variantId;s.faults={};});const r=invoke(f,'--all',[],{resume:true});assert.equal(r.status,0,r.stderr);assert.equal(state(f).calls.filter(c=>c.method==='set'&&c.key===key(f.manifest.records[0])).length,1);});
+function forge(events){let prev='0'.repeat(64);return events.map((e,i)=>{const {hash:_,...body}=e;body.seq=i;body.prev=prev;prev=hash(JSON.stringify(body));return JSON.stringify({...body,hash:prev})+'\n';}).join('');}
+for(const [name,change] of [
+ ['foreign key',es=>es[1].key='gid://shopify/InventoryItem/999@gid://shopify/Location/1'],
+ ['quantity',es=>es[1].before=999],['missing intent',es=>es.splice(1,1)],
+ ['duplicate success',es=>es.splice(3,0,es[2])],['rollback without applied success',es=>{es[1].direction='rollback';es[1].before=0;es[1].after=2026;}],
+ ['foreign header',es=>es[0].manifestHash='a'.repeat(64)],['unknown event',es=>es[1].type='reconciled'],
+])test('forged history rejected even with recomputed checkpoint: '+name,()=>{const f=fixture();assert.equal(invoke(f,'--canary').status,0);const es=journal(f);change(es);fs.writeFileSync(f.journal,forge(es));const before=hash(fs.readFileSync(f.state));fail(invoke(f,'--rollback',[],{resume:true,noWrites:true}),/journal|Journal|Unbound|Foreign|Unknown|Invalid/);assert.equal(hash(fs.readFileSync(f.state)),before);});
+test('corrupt, truncated and wrong external journal hash refuse before adapter',()=>{for(const mutation of [s=>s.slice(0,-1),s=>s.replace('header','bogus')]){const f=fixture();assert.equal(invoke(f,'--canary').status,0);fs.writeFileSync(f.journal,mutation(fs.readFileSync(f.journal,'utf8')));fail(invoke(f,'--rollback',[],{resume:true,noWrites:true}),/Truncated|Corrupt/);}const f=fixture();assert.equal(invoke(f,'--canary').status,0);fail(invoke(f,'--canary',['--journal-sha256','a'.repeat(64)],{noWrites:true}),/Journal SHA256/);fail(invoke(f,'--canary',[],{noWrites:true}),/Existing journal requires/);});
+test('existing concurrent journal lock refuses without writes',()=>{const f=fixture();assert.equal(invoke(f,'--canary').status,0);fs.mkdirSync(f.journal+'.lock');const before=hash(fs.readFileSync(f.state));fail(invoke(f,'--canary',[],{resume:true}),/EEXIST/);assert.equal(hash(fs.readFileSync(f.state)),before);});
+for(const [code,label] of [["require('node:fs').readFileSync('/Users/macstudio3/Projects/secrets-manager/.env')",'fs.readFileSync'],["fetch('https://example.invalid')",'fetch'],["require('node:https').get('https://example.invalid')",'node:https.get'],["require('node:child_process').spawn('never-launch')",'node:child_process.spawn']])test('denied real boundary calibration '+label,()=>{const r=spawnSync(process.execPath,['--require',guard,'--eval',code],{encoding:'utf8',env:{}});assert.notEqual(r.status,0);assert.match(r.stderr,new RegExp('BOUNDARY_DENIED:'+label.replaceAll('.','\\.')));});
+test('confirmed adapter success then failed success append leaves only intent and blocks both recoveries',()=>{
+ for(const mode of ['--canary','--rollback']) {
+ const f=fixture();if(mode==='--rollback')assert.equal(invoke(f,'--canary').status,0);
+ fail(invoke(f,mode,[],{resume:mode==='--rollback',failSuccessAppend:true}),/after confirmed mutation before success append/);
+ assert.equal(journal(f).at(-1).type,'intent');
+ assert.equal(state(f).records[2].onHand,mode==='--canary'?0:2026);
+ for(const recovery of ['--canary','--rollback'])fail(invoke(f,recovery,[],{resume:true,noWrites:true}),/Unresolved mutation intent/);
+ }
+});
+test('rollback postread failure retains success; verified retry performs no duplicate restore',()=>{
+ const f=fixture();assert.equal(invoke(f,'--canary').status,0);
+ updateState(f,s=>s.faults[key(s.records[2])]='bad-postread');
+ fail(invoke(f,'--rollback',[],{resume:true}),/precondition drift/);
+ assert.equal(journal(f).at(-1).type,'success');assert.equal(journal(f).at(-1).direction,'rollback');
+ const sets=state(f).calls.filter(c=>c.method==='set').length;
+ fail(invoke(f,'--rollback',[],{resume:true}),/precondition drift/);
+ updateState(f,s=>{s.records[2].variantId=f.manifest.records[2].variantId;s.faults={};});
+ assert.equal(invoke(f,'--rollback',[],{resume:true}).status,0);
+ assert.equal(state(f).calls.filter(c=>c.method==='set').length,sets);
+ assert.equal(journal(f).at(-1).type,'verified');
+});
+test('durable helper rejects symlink and hardlink targets without altering victims',async()=>{
+ const {durableWrite}=await import('../vienna-executor.mjs');
+ for(const kind of ['symlink','hardlink']) {
+ const f=fixture(),victim=path.join(f.dir,'victim.json'),alias=path.join(f.dir,'alias.json');
+ fs.writeFileSync(victim,'retained'); if(kind==='symlink')fs.symlinkSync(victim,alias);else fs.linkSync(victim,alias);
+ assert.throws(()=>durableWrite(alias,'replaced'),/Unsafe write target/);assert.equal(fs.readFileSync(victim,'utf8'),'retained');
+ }
+});
+test('actual CLI rejects linked manifest, state and journal without modifying targets',()=>{
+ for(const file of ['path','state','journal']) {
+ const f=fixture();if(file==='journal')assert.equal(invoke(f,'--canary').status,0);
+ const original=f[file],alias=path.join(f.dir,'alias-'+file);fs.symlinkSync(original,alias);f[file]=alias;
+ const before=hash(fs.readFileSync(original));
+ fail(invoke(f,'--canary',[],{resume:file==='journal',noWrites:true,allowAdapterRead:file==='state'}),/ELOOP|symbolic/);
+ assert.equal(hash(fs.readFileSync(original)),before);
+ }
+});
diff --git a/verification/vienna-e2e-proof.json b/verification/vienna-e2e-proof.json
new file mode 100644
index 0000000..35d135a
--- /dev/null
+++ b/verification/vienna-e2e-proof.json
@@ -0,0 +1,118 @@
+{
+ "intent": "Harden actual Vienna inventory CLI and prove the local executor contract with hermetic persisted adapters",
+ "risk_tier": "R3 local integration rehearsal only",
+ "environment": "macOS; detached isolated worktree; no real secrets or network",
+ "baseline_commit": "7fb358ca8948290915678dd2c34f83a02d5a0532",
+ "build_identity": "Content hashes below; local commit reported in handoff",
+ "timestamp": "2026-09-08T12:52:25.579722+00:00",
+ "ticket": "TK-11300-vienna-inventory-executor-freeze-exact-m",
+ "parent_ticket": "TK-11299-zero-price-bin-zsh-orderable-regression",
+ "task_id": "cycle-20260908T1221Z.HzXvuQ/vienna-executor",
+ "worktree": "/private/tmp/vienna-executor.qdoRbR",
+ "commands": [
+ "VIENNA_EVIDENCE_DIR=/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ node --test test/cli-preflight.test.mjs test/vienna-executor.test.mjs",
+ "node --check apply-fix.mjs",
+ "node --check cli-args.mjs",
+ "node --check vienna-executor.mjs",
+ "node --check vienna-offline-adapter.mjs",
+ "git diff --check"
+ ],
+ "results": {
+ "tests": 87,
+ "pass_count": 87,
+ "failed": 0,
+ "skipped": 0
+ },
+ "checks": [
+ {
+ "check": "CLI preflight and help",
+ "verdict": "PASS",
+ "evidence": "42 legacy CLI and calibrated boundary checks preserved with stricter frozen-input rejection"
+ },
+ {
+ "check": "Manifest and store preflight",
+ "verdict": "PASS",
+ "evidence": "Strict schema, external SHA256, store GID/domain, age, exact GIDs, vendor/line, sample/price/tracking/policy, duplicate/collision, canary/full-location checks before denied adapter reads"
+ },
+ {
+ "check": "Plan",
+ "verdict": "PASS",
+ "evidence": "Actual --plan and --enumerate under denied writes/adapter-state read; no journal or fixture changes"
+ },
+ {
+ "check": "Offline execution",
+ "verdict": "PASS",
+ "evidence": "Exact frozen canary and all-record multi-location compare/set/postread with persisted fixture and journal assertions"
+ },
+ {
+ "check": "Retry and rollback",
+ "verdict": "PASS",
+ "evidence": "No duplicate successful writes; confirmed rejection separated; partial multi-location rollback uses only journal successes; idempotent restore"
+ },
+ {
+ "check": "Ambiguous result and receipt gaps",
+ "verdict": "PASS",
+ "evidence": "Throw before/after persisted write and confirmed response before success append; durable intent blocks apply/rollback before denied adapter boundary in both directions"
+ },
+ {
+ "check": "Postread interruptions",
+ "verdict": "PASS",
+ "evidence": "Forward and rollback success retained, drift blocks further writes, restored matching state permits verification-only recovery"
+ },
+ {
+ "check": "Journal integrity and concurrency",
+ "verdict": "PASS",
+ "evidence": "External checkpoint plus chain/schema/state/membership/quantity checks; malformed/forged/truncated history rejection; existing exclusive lock rejected"
+ },
+ {
+ "check": "File protection",
+ "verdict": "PASS",
+ "evidence": "Symlink and hardlink targets refused; exact input aliases rejected; atomic fixture replacement; retained temp/state"
+ },
+ {
+ "check": "Real denied boundaries",
+ "verdict": "PASS",
+ "evidence": "Canonical secret-file read, fetch, HTTPS and child spawn calibration under process preload; no live transport exists"
+ },
+ {
+ "check": "Syntax and diff",
+ "verdict": "PASS",
+ "evidence": "node --check for four shipped modules and git diff --check"
+ },
+ {
+ "check": "Production execution",
+ "verdict": "OUT_OF_SCOPE",
+ "evidence": "No live adapter, authoritative manifest acquisition, actual shop identity, credential scope or approval; separate parent TK11299 gate"
+ }
+ ],
+ "evidence_directory": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ",
+ "final_test_log": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ/vienna-test-boundary-final.txt",
+ "retained_failed_run": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260908T1221Z.HzXvuQ/vienna-test-first.txt",
+ "failed_run_cause": "Test factory aliased shared store object; negative mutation contaminated subsequent fixtures. Fixed cloning, then replaced generated baseline with versioned fixture+SHA256.",
+ "historical_proof": "verification/e2e-proof.json and verification/cli-preflight-test-output.txt remain unchanged; source commit7fb358c",
+ "correlations": [
+ "dm-mtsnoxgl-42285-kfum1h",
+ "action-mtsnr96r-13925-y5u8k8",
+ "action-mtso4bm9-13925-fmifo2"
+ ],
+ "cleanup": "All fixture directories, failing logs and interrupted journals retained. Only own completed-process lock is released by runtime code. No live cleanup.",
+ "trust_boundary": "External manifest and journal hashes are independent operator trust anchors, not catalog completeness or arbitrary journal provenance. Operator cannot derive approval by recomputing an unreviewed hash.",
+ "limitations": [
+ "Production adapter intentionally absent; actual transport semantics and permissions unverified",
+ "Synthetic shop and IDs only; authoritative manifest completeness and actual identity remain external preflight",
+ "No automatic resolution of ambiguous intent or crashed lock; independent reconciliation required",
+ "Local process fault injection proves conservative gaps; no physical power-loss experiment"
+ ],
+ "verdict": "PASS for scoped local consumer/executor and offline adapter proof; production recovery remains gated",
+ "file_sha256": {
+ "apply-fix.mjs": "73a910ebf571fae0e5ff69f894af172ce82e2e58c0f576d995b08739d8f192ba",
+ "cli-args.mjs": "716158c0530c7f06937e439dc26d64118a38d081c6bafc9e2d3ae1b3ac5d72fb",
+ "vienna-executor.mjs": "a444d85b388b04e639d7ccb9778dc770bf95e8840a479a94f6b88157ecf5cb4f",
+ "vienna-offline-adapter.mjs": "62b083184cb3ea0818624a97fd13b3068b9052856bff927ddf782abbc94770fc",
+ "test/vienna-executor.test.mjs": "1311d7988988178e8474c60ed86fa8a69c6f1c39c911ec832761df9ad97d8e21",
+ "test/vienna-boundaries.cjs": "2aad1e0b65227f9c74346465eb177c08c2cc606a601f33fb17d725f95d27c4f4",
+ "test/fixtures/vienna-manifest.json": "1b21ab81983284920c22f857a1480741335b5513d003a23b38f9d1c2378bc1c7",
+ "test/fixtures/vienna-manifest.sha256": "061b8fe166b4744e4cef87c02d642f85561a404ffc8158873835af5a77c9a6f2",
+ "VIENNA-EXECUTOR.md": "3d66ed3a13db4732f23e1f85395e41a758add870744cd1c66f9a7dc0252947d9"
+ }
+}
diff --git a/vienna-executor.mjs b/vienna-executor.mjs
new file mode 100644
index 0000000..a7d21f0
--- /dev/null
+++ b/vienna-executor.mjs
@@ -0,0 +1,162 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import {createHash,randomUUID} from 'node:crypto';
+export const DOMAIN='designer-laboratory-sandbox.myshopify.com'; // PRODUCTION despite its name.
+export const hash = bytes=>createHash('sha256').update(bytes).digest('hex');
+const fail = m=>{throw new Error(m);};
+const eq = (a,b)=>JSON.stringify(a)===JSON.stringify(b);
+function keys(x, fields, what) {
+ if (!x || typeof x!=='object' || Array.isArray(x) || !eq(Object.keys(x).sort(),fields.split(' ').sort())) fail('Malformed '+what);
+}
+export const key = r=>`${r.inventoryItemId}@${r.locationId}`;
+const gid=(s,type)=>typeof s==='string' && new RegExp('^gid://shopify/'+type+'/[1-9][0-9]*$').test(s);
+export function readRegular(file) {
+ const fd=fs.openSync(file, fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW);
+ try {const stat=fs.fstatSync(fd); if(!stat.isFile() || stat.nlink!==1 || stat.size>10_000_000) fail('Unsafe file'); return fs.readFileSync(fd,'utf8');}
+ finally {fs.closeSync(fd);}
+}
+export function validateManifest(bytes, expectedHash, shopId, now=Date.now()) {
+ if (hash(bytes)!==expectedHash) fail('Manifest SHA256 mismatch');
+ const m=JSON.parse(bytes);
+ keys(m,'version scope store createdAt expiresAt records canary','manifest');
+ keys(m.store,'domain id','store'); keys(m.scope,'vendor line','scope');
+ if(m.version!==1 || m.scope.vendor!=='Phillipe Romano' || m.scope.line!=='Vienna') fail('Foreign manifest scope');
+ if(m.store.domain!==DOMAIN || m.store.id!==shopId || !gid(shopId,'Shop')) fail('Store pin mismatch');
+ const born=Date.parse(m.createdAt), expires=Date.parse(m.expiresAt);
+ if(!Number.isFinite(born) || !Number.isFinite(expires) || new Date(born).toISOString()!==m.createdAt || new Date(expires).toISOString()!==m.expiresAt || born>now || expires<=now || expires<=born || expires-born>86400000 || now-born>86400000) fail('Stale or invalid manifest validity');
+ if(!Array.isArray(m.records)||!m.records.length||m.records.length>50000||!Array.isArray(m.canary)||!m.canary.length) fail('Empty or excessive manifest scope');
+ const seen=new Set(), variants=new Map(), items=new Map();
+ for(const r of m.records) {
+ keys(r,'productId variantId inventoryItemId locationId vendor line title variantTitle price inventoryPolicy tracked onHand target','record');
+ for(const [f,t] of [['productId','Product'],['variantId','ProductVariant'],['inventoryItemId','InventoryItem'],['locationId','Location']]) if(!gid(r[f],t)) fail('Invalid exact identity '+f);
+ if(r.vendor!==m.scope.vendor || r.line!=='Vienna' || typeof r.title!=='string' || !/^Vienna(?:\s|$)/.test(r.title) || typeof r.variantTitle!=='string' || /sample/i.test(r.variantTitle) || r.price!==0 || r.inventoryPolicy!=='DENY' || r.tracked!==true || !Number.isSafeInteger(r.onHand) || r.onHand<=0 || r.target!==0) fail('Foreign or unsafe record scope');
+ if(seen.has(key(r))) fail('Duplicate inventory location'); seen.add(key(r));
+ const identity=JSON.stringify([r.productId,r.inventoryItemId]);
+ if(variants.has(r.variantId)&&variants.get(r.variantId)!==identity) fail('Variant identity collision'); variants.set(r.variantId,identity);
+ if(items.has(r.inventoryItemId)&&items.get(r.inventoryItemId)!==r.variantId) fail('Item identity collision'); items.set(r.inventoryItemId,r.variantId);
+ }
+ if(new Set(m.canary).size!==m.canary.length || m.canary.some(k=>!seen.has(k))) fail('Canary outside frozen scope');
+ const selected=new Set(m.canary), products=new Set(m.records.filter(r=>selected.has(key(r))).map(r=>r.productId));
+ if(m.records.some(r=>products.has(r.productId)&&!selected.has(key(r)))) fail('Canary must include every frozen location of each selected product');
+ return m;
+}
+function fsyncDirectory(file) { const fd=fs.openSync(path.dirname(file),'r');try{fs.fsyncSync(fd);}finally{fs.closeSync(fd);} }
+export function durableWrite(file, data, flag='w') {
+ // Atomic fixture replacement preserves the last durable truth on interruption.
+ // Refuse aliases before writing, and retain unfinished temp files for inspection.
+ let before;
+ try { before=fs.lstatSync(file); } catch(e) { if(e.code!=='ENOENT')throw e; }
+ if(before && (!before.isFile() || before.nlink!==1)) fail('Unsafe write target');
+ const target=flag==='wx'?file:file+'.pending-'+randomUUID();
+ const fd=fs.openSync(target,fs.constants.O_WRONLY|fs.constants.O_CREAT|fs.constants.O_EXCL|fs.constants.O_NOFOLLOW,0o600);
+ try {if(!fs.fstatSync(fd).isFile()||fs.fstatSync(fd).nlink!==1)fail('Unsafe write descriptor');fs.writeFileSync(fd,data);fs.fsyncSync(fd);} finally {fs.closeSync(fd);}
+ if(flag!=='wx') {
+ let current; try{current=fs.lstatSync(file);}catch(e){if(e.code!=='ENOENT')throw e;}
+ if(Boolean(current)!==Boolean(before) || (current && (!current.isFile()||current.nlink!==1||current.ino!==before.ino||current.dev!==before.dev))) fail('Write target changed');
+ fs.renameSync(target,file);
+ }
+ fsyncDirectory(file);
+}
+function eventLine(body, prev) {const event={...body,prev};return JSON.stringify({...event,hash:hash(JSON.stringify(event))})+'\n';}
+export function readJournal(bytes, manifestHash, m) {
+ if(!bytes.endsWith('\n')) fail('Truncated journal');
+ const lines=bytes.trimEnd().split('\n'); let prev='0'.repeat(64), header, pending=null;
+ const states=new Map(), records=new Map(m.records.map(r=>[key(r),r]));
+ for(let i=0;i<lines.length;i++) {
+ const e=JSON.parse(lines[i]); const {hash:digest,...body}=e;
+ if(digest!==hash(JSON.stringify(body)) || body.prev!==prev || body.seq!==i) fail('Corrupt journal chain'); prev=digest;
+ if(i===0) {
+ keys(e,'seq type manifestHash store selection prev hash','journal header');
+ if(e.type!=='header'||e.manifestHash!==manifestHash||!eq(e.store,m.store)||!['--canary','--all'].includes(e.selection)) fail('Foreign journal');
+ header=e; continue;
+ }
+ keys(e,'seq type key direction before after prev hash','journal event');
+ const r=records.get(e.key);
+ if(!r || (header.selection==='--canary'&&!m.canary.includes(e.key)) || !['apply','rollback'].includes(e.direction)) fail('Journal scope injection');
+ if(e.before!==(e.direction==='apply'?r.onHand:0)||e.after!==(e.direction==='apply'?0:r.onHand)) fail('Journal quantity injection');
+ const s=states.get(e.key)||'unattempted';
+ if(e.type==='intent') {
+ if(pending || (e.direction==='apply'&&!['unattempted','rejected'].includes(s)) || (e.direction==='rollback'&&s!=='applied')) fail('Invalid journal transition');
+ pending=e;
+ } else if(['success','rejected','verified'].includes(e.type)) {
+ if(e.type==='verified') {
+ if(pending || s!==(e.direction==='apply'?'applied-unverified':'rolledback-unverified')) fail('Unbound verification');
+ states.set(e.key,e.direction==='apply'?'applied':'rolledback');
+ } else {
+ if(!pending || !eq([pending.key,pending.direction,pending.before,pending.after],[e.key,e.direction,e.before,e.after])) fail('Unbound journal outcome');
+ states.set(e.key,e.type==='success'?(e.direction==='apply'?'applied-unverified':'rolledback-unverified'):(e.direction==='apply'?'rejected':'applied'));
+ pending=null;
+ }
+ } else fail('Unknown journal event');
+ }
+ if(pending) fail('Unresolved mutation intent: reconciliation required; never infer failure');
+ return {header,states,seq:lines.length,prev};
+}
+function assertSnapshot(actual,r,quantity) {
+ const expected={...r,onHand:quantity};
+ if(!eq(actual,expected)) fail('Identity, quantity or product precondition drift at '+key(r));
+}
+export async function run(cli) {
+ const manifestPath=path.resolve(cli['--manifest']);
+ const m=validateManifest(readRegular(manifestPath),cli['--expected-sha256'],cli['--expected-store-id']);
+ if(['--plan','--enumerate'].includes(cli.mode)) return {mode:'plan',manifestHash:cli['--expected-sha256'],store:m.store,records:m.records,canary:m.canary,mutations:0};
+ const journalPath=path.resolve(cli['--journal']),statePath=path.resolve(cli['--offline-state']);
+ if(new Set([manifestPath,journalPath,statePath]).size!==3) fail('Input/output paths must be distinct');
+ const exists=fs.existsSync(journalPath);
+ let journal;
+ if(exists) {
+ if(!cli['--journal-sha256']) fail('Existing journal requires external SHA256 checkpoint');
+ const bytes=readRegular(journalPath); if(hash(bytes)!==cli['--journal-sha256']) fail('Journal SHA256 mismatch');
+ journal=readJournal(bytes,cli['--expected-sha256'],m);
+ if(cli.mode!=='--rollback'&&journal.header.selection!==cli.mode) fail('Cannot broaden frozen journal selection');
+ } else if(cli.mode==='--rollback'||cli['--journal-sha256']) fail('Missing pinned journal');
+ // The only adapter shipped by this local increment is hermetic and explicitly selected.
+ // Future live wiring needs separate approval and a reviewed identity/auth contract.
+ const {openOfflineAdapter}=await import('./vienna-offline-adapter.mjs');
+ const adapter=openOfflineAdapter(statePath);
+ if(!eq(await adapter.shop(),m.store)) fail('Adapter store mismatch');
+ const selection=journal?.header.selection||cli.mode;
+ const records=m.records.filter(r=>selection==='--all'||m.canary.includes(key(r)));
+ if(cli.mode!=='--rollback' && [...(journal?.states.values()||[])].some(s=>s.startsWith('rolledback'))) fail('Rolled back run cannot reapply');
+ // Exclusive process lock. A crashed holder leaves the lock for independent reconciliation.
+ const lock=journalPath+'.lock'; fs.mkdirSync(lock,{mode:0o700});
+ try {
+ // Recheck checkpoint under lock: concurrent changes cannot pass a stale validation.
+ if(exists && hash(readRegular(journalPath))!==cli['--journal-sha256']) fail('Journal changed before lock');
+ if(!exists) {
+ const line=eventLine({seq:0,type:'header',manifestHash:cli['--expected-sha256'],store:m.store,selection},'0'.repeat(64));
+ durableWrite(journalPath,line,'wx'); journal=readJournal(line,cli['--expected-sha256'],m);
+ }
+ const append=(type,r,direction)=>{
+ const line=eventLine({seq:journal.seq,type,key:key(r),direction,before:direction==='apply'?r.onHand:0,after:direction==='apply'?0:r.onHand},journal.prev);
+ const fd=fs.openSync(journalPath,fs.constants.O_WRONLY|fs.constants.O_APPEND|fs.constants.O_NOFOLLOW);
+ try{const stat=fs.fstatSync(fd);if(!stat.isFile()||stat.nlink!==1)fail('Unsafe journal descriptor');fs.writeFileSync(fd,line);fs.fsyncSync(fd);}finally{fs.closeSync(fd);}
+ journal.seq++; journal.prev=JSON.parse(line).hash;
+ };
+ let writes=0,skipped=0;
+ for(const r of records) {
+ let s=journal.states.get(key(r))||'unattempted';
+ // A durable success whose post-read was interrupted is never written again.
+ if(s.endsWith('-unverified')) {
+ const direction=s==='applied-unverified'?'apply':'rollback';
+ assertSnapshot(await adapter.read(r),r,direction==='apply'?0:r.onHand);
+ append('verified',r,direction); s=direction==='apply'?'applied':'rolledback';
+ }
+ if(cli.mode==='--rollback' && s!=='applied') {skipped++;continue;}
+ if(cli.mode!=='--rollback' && s==='applied') {assertSnapshot(await adapter.read(r),r,0);skipped++;continue;}
+ const direction=cli.mode==='--rollback'?'rollback':'apply';
+ const before=direction==='apply'?r.onHand:0, after=direction==='apply'?0:r.onHand;
+ assertSnapshot(await adapter.read(r),r,before);
+ append('intent',r,direction); // fsync before crossing the mutation boundary.
+ const result=await adapter.set(r,before,after); // exceptions leave unresolved durable intent.
+ if(result?.kind==='rejected' && result.confirmedNoWrite===true) {
+ append('rejected',r,direction); fail('Confirmed mutation rejection at '+key(r));
+ }
+ if(result?.kind!=='success'||result.key!==key(r)||result.quantity!==after) fail('Ambiguous mutation result; reconciliation required');
+ append('success',r,direction); // only confirmed successes authorize rollback.
+ assertSnapshot(await adapter.read(r),r,after);
+ append('verified',r,direction); writes++;
+ }
+ return {mode:cli.mode,store:m.store,writes,skipped,journal:journalPath,journalSha256:hash(readRegular(journalPath)),manifestHash:cli['--expected-sha256'],adapter:'offline-only'};
+ } finally {fs.rmdirSync(lock);}
+}
diff --git a/vienna-offline-adapter.mjs b/vienna-offline-adapter.mjs
new file mode 100644
index 0000000..3323c6f
--- /dev/null
+++ b/vienna-offline-adapter.mjs
@@ -0,0 +1,24 @@
+// Deterministic file-backed test adapter. No secrets, imports of live clients, or network.
+import {readRegular,durableWrite,key} from './vienna-executor.mjs';
+export function openOfflineAdapter(file) {
+ const state=JSON.parse(readRegular(file));
+ if(state.fixture!==true || state.version!==1 || !Array.isArray(state.records) || !Array.isArray(state.calls)) throw new Error('Offline fixture required');
+ const persist=()=>durableWrite(file,JSON.stringify(state,null,2)+'\n');
+ const mark=(method,k)=>{state.calls.push({method,key:k});persist();};
+ return {
+ async shop(){return state.store;},
+ async read(r){ mark('read',key(r)); return state.records.find(x=>key(x)===key(r)); },
+ async set(r,before,after) {
+ mark('set',key(r));
+ const actual=state.records.find(x=>key(x)===key(r));
+ if(!actual||actual.onHand!==before) return {kind:'rejected',confirmedNoWrite:true};
+ const fault=state.faults?.[key(r)];
+ if(fault==='reject') return {kind:'rejected',confirmedNoWrite:true};
+ if(fault==='throw-before') throw new Error('Simulated transport failure before response');
+ actual.onHand=after; persist();
+ if(fault==='throw-after') throw new Error('Simulated transport failure after persisted write');
+ if(fault==='bad-postread') { actual.variantId='gid://shopify/ProductVariant/999';persist(); }
+ return {kind:'success',key:key(r),quantity:after};
+ }
+ };
+}
← 7fb358c Reject unsafe inventory CLI arguments before initialization
·
back to Tk 10965 Zero Price Analysis
·
Reload fixture truth and serialize competing Vienna CLI writ c57e84a →