@sabaiway/agent-workflow-kit 3.5.0 → 3.6.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.
- package/CHANGELOG.md +32 -0
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/worktrees.md +1 -1
- package/tools/doc-parity.mjs +7 -5
- package/tools/worktrees.mjs +123 -16
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,38 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
|
|
|
4
4
|
is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
|
|
5
5
|
every `migrations/<version>-<slug>.md` newer than it, in semver order.
|
|
6
6
|
|
|
7
|
+
## 3.6.0 — the dependency-free install posture: a proof, never a default (AD-067)
|
|
8
|
+
|
|
9
|
+
On a provably dependency-free project the provision record and the default-lane report no longer
|
|
10
|
+
print a generic `npm install` hint: both state
|
|
11
|
+
`no install needed — the project declares no dependencies` (recorded node_modules mode
|
|
12
|
+
`no-dependencies`), and a `doc-parity` binding pins the posture string to the mode doc.
|
|
13
|
+
|
|
14
|
+
The verdict is a PROOF granted only on evidence the tool actually read — the WORKTREE'S OWN LIVE
|
|
15
|
+
checkout: what an install run there would actually read, exactly HEAD at provision time and the
|
|
16
|
+
satellite's own committed state on `--resume`, never MAIN's mutable working tree (which can
|
|
17
|
+
diverge from what the satellite actually holds; real-git tests pin both refresh directions).
|
|
18
|
+
Everything the tool cannot vouch for leaves the posture UNKNOWN with the honest advice kept — the
|
|
19
|
+
fail-safe direction, because a false "nothing to install" is worse than a redundant hint:
|
|
20
|
+
|
|
21
|
+
- a `workspaces` field of ANY shape, outright — a workspace install materializes member links and
|
|
22
|
+
`.bin` shims even with zero dependencies, so a workspace tree is never provably install-free;
|
|
23
|
+
- an external workspace manifest beside the root (`pnpm-workspace.yaml`/`.yml`, `lerna.json`);
|
|
24
|
+
- a malformed manifest, dependency field, or `scripts` shape (three-valued verdicts — malformed is
|
|
25
|
+
`unknown`, never "none"; a lifecycle key with a non-string value fails closed);
|
|
26
|
+
- an install-lifecycle script — dependency-free is NOT install-free: the CLOSED, test-pinned set
|
|
27
|
+
is the npm lifecycle (including the deprecated `prepublish`) plus `pnpm:devPreinstall`; a
|
|
28
|
+
native-addon manifest (`binding.gyp`) is a mandatory install too.
|
|
29
|
+
|
|
30
|
+
The posture COMPOSES with the shipped record contract, and LIVE STATE WINS: a node already at the
|
|
31
|
+
worktree — a directory, or a symlink left by an earlier provision, even dangling — records
|
|
32
|
+
`present` before any MAIN-state early return, the AD-065 symlink unlink-first arm is untouched for
|
|
33
|
+
every non-proven project, and `--install` remains an explicit request that is always answered with
|
|
34
|
+
the isolated-install command while the record still states the posture.
|
|
35
|
+
|
|
36
|
+
Third safe slice of the deferred parallel-track work (AD-063); node_modules ownership and
|
|
37
|
+
resume-verify semantics stay separate redesigns.
|
|
38
|
+
|
|
7
39
|
## 3.5.0 — the provision record orients a fresh satellite session (AD-065)
|
|
8
40
|
|
|
9
41
|
The worktrees provision record was an identity stub; a fresh satellite session could not derive
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-workflow-kit
|
|
|
3
3
|
description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
metadata:
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.6.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -70,7 +70,7 @@ PLUS the three facts a fresh satellite session cannot derive from its own checko
|
|
|
70
70
|
isolated-install command when the package manager is unambiguous, the honest install-by-hand
|
|
71
71
|
advice when it is not, and — when the provisioned `node_modules` is a SYMLINK into main — the
|
|
72
72
|
unlink-first form, because a plain install through the symlink writes into MAIN and is never
|
|
73
|
-
presented as isolated. `--install` remains an EXPLICIT request and is always answered with the
|
|
73
|
+
presented as isolated. When the WORKTREE'S OWN LIVE CHECKOUT is provably dependency-free (its `package.json` declares no dependencies, no `workspaces` field of any shape, no install-lifecycle script, no native-addon manifest, no external workspace manifest beside it — the evidence is what an install run in the satellite would actually read: exactly HEAD at provision time, the satellite's own committed state on `--resume`, never MAIN's mutable working tree) the record and the default-lane report both state `no install needed — the project declares no dependencies` and print no install command. A workspace tree is NEVER provably install-free — a workspace install materializes member links and `.bin` shims even with zero dependencies — and anything else the tool cannot enumerate (an absent or unparseable `package.json`, a malformed dependency or scripts field, an install-lifecycle script — dependency-free is not install-free) leaves the posture UNKNOWN and keeps the existing advice: a false "nothing to install" is worse than a redundant hint. `--install` remains an EXPLICIT request and is always answered with the
|
|
74
74
|
isolated-install command.
|
|
75
75
|
|
|
76
76
|
**Honesty:** there is NO preview step on the writers — over-warned by design. The tool never
|
package/tools/doc-parity.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from './recommendations.mjs';
|
|
35
35
|
import { SKIPPED_READONLY } from './setup-backends.mjs';
|
|
36
36
|
import { LATENT_ARM_NOTICE } from './review-state.mjs';
|
|
37
|
-
import { QUEUE_SHARED_RULE, LANDING_FROM_MAIN } from './worktrees.mjs';
|
|
37
|
+
import { QUEUE_SHARED_RULE, LANDING_FROM_MAIN, NO_DEPENDENCIES_POSTURE } from './worktrees.mjs';
|
|
38
38
|
|
|
39
39
|
const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
40
40
|
|
|
@@ -99,10 +99,12 @@ export const BINDINGS = Object.freeze([
|
|
|
99
99
|
// actually emits — a reworded doc dropping the notice fails this pin plus the gate.
|
|
100
100
|
valueBinding('latent-arm-notice', LATENT_ARM_NOTICE, LATENT_ARM_NOTICE, [REVIEW_STATE_DOC]),
|
|
101
101
|
// The provision-record orientation contract (same "the tool knows and does not say" class): the
|
|
102
|
-
// shared-queue rule
|
|
103
|
-
// drop, so
|
|
102
|
+
// shared-queue rule, the landing-from-main fact, and the no-dependencies install posture were
|
|
103
|
+
// prose-only bars a doc could silently drop, so all are pinned to the live strings the record
|
|
104
|
+
// actually carries.
|
|
104
105
|
valueBinding('queue-shared-rule', QUEUE_SHARED_RULE, QUEUE_SHARED_RULE, [WORKTREES_DOC]),
|
|
105
106
|
valueBinding('landing-from-main', LANDING_FROM_MAIN, LANDING_FROM_MAIN, [WORKTREES_DOC]),
|
|
107
|
+
valueBinding('no-dependencies-posture', NO_DEPENDENCIES_POSTURE, NO_DEPENDENCIES_POSTURE, [WORKTREES_DOC]),
|
|
106
108
|
].map((b) => Object.freeze(b)));
|
|
107
109
|
|
|
108
110
|
// ── the pure checker (readText is injectable for hermetic tests) ────────────────────────
|
|
@@ -150,8 +152,8 @@ A CLOSED, exported registry binds each live code constant — the autonomy-docto
|
|
|
150
152
|
table, the status tokens, the trusted-dir allowlist), the recommendations/upgrade presentation
|
|
151
153
|
contract (section header, empty line, verdict templates), the acks-store path, the setup refresh
|
|
152
154
|
degrade token, the review-state clean-tree latent-arm notice, and the worktrees provision-record
|
|
153
|
-
orientation contract (shared-queue rule, landing-from-main) — to
|
|
154
|
-
references/modes/*.md contract must carry, and
|
|
155
|
+
orientation contract (shared-queue rule, landing-from-main, no-dependencies install posture) — to
|
|
156
|
+
the exact token its references/modes/*.md contract must carry, and
|
|
155
157
|
asserts the CURRENT value renders into every bound file. A drifted doc, an unreadable bound file,
|
|
156
158
|
or an absent token FAILS CLOSED.
|
|
157
159
|
|
package/tools/worktrees.mjs
CHANGED
|
@@ -933,6 +933,10 @@ export const QUEUE_BASENAME = 'queue.md';
|
|
|
933
933
|
export const QUEUE_SHARED_RULE =
|
|
934
934
|
'the series index is SHARED and lives ONLY in main: read it at the absolute path above, and never copy it into this worktree, because docs/plans is git-ignored and machine-local, so a copy silently diverges from what main and every other worktree are writing. This worktree never WRITES that file: reaching outside it is an fs_outside_repo action the autonomy policy denies by default. Put new findings in THIS handoff record instead — it is the channel that survives the landing, and main appends them to the index from here';
|
|
935
935
|
export const LANDING_FROM_MAIN = 'landing runs FROM MAIN, never from this worktree';
|
|
936
|
+
export const NO_DEPENDENCIES_POSTURE = 'no install needed — the project declares no dependencies';
|
|
937
|
+
// The recorded node_modules mode for that same verdict: provision neither advised nor created a
|
|
938
|
+
// node_modules for this worktree.
|
|
939
|
+
export const NODE_MODULES_NONE = 'no-dependencies';
|
|
936
940
|
|
|
937
941
|
// The record is LINE-oriented and is parsed back for IDENTITY, so a value carrying a control byte
|
|
938
942
|
// is refused rather than written: a newline spills a second line the parser reads as a real field
|
|
@@ -1250,21 +1254,110 @@ const resolveInstallAdvice = ({ root, wtRoot, fs }) => {
|
|
|
1250
1254
|
return { command, instruction: command };
|
|
1251
1255
|
};
|
|
1252
1256
|
|
|
1257
|
+
// Exported and frozen so the closed sets are test-pinned in BOTH directions (a member can neither
|
|
1258
|
+
// be dropped nor smuggled in without failing the pin).
|
|
1259
|
+
export const DEPENDENCY_FIELDS = Object.freeze(['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']);
|
|
1260
|
+
// Workspace declarations this tool cannot read. Their presence makes the dependency inventory
|
|
1261
|
+
// unknowable from package.json alone, so it is never proof.
|
|
1262
|
+
export const EXTERNAL_WORKSPACE_MANIFESTS = Object.freeze(['pnpm-workspace.yaml', 'pnpm-workspace.yml', 'lerna.json']);
|
|
1263
|
+
|
|
1264
|
+
const readPackageJson = (fs, path) => {
|
|
1265
|
+
const file = readFileNoFollow(fs, path);
|
|
1266
|
+
if (!file.bytes) return null;
|
|
1267
|
+
try {
|
|
1268
|
+
const parsed = JSON.parse(String(file.bytes));
|
|
1269
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
1270
|
+
} catch {
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
|
|
1275
|
+
// A dependency field is read THREE ways, never two: 'has' (a non-empty plain object), 'none' (the
|
|
1276
|
+
// field is absent, or an empty plain object), and 'unknown' for every other shape — a string, an
|
|
1277
|
+
// array, null. A malformed manifest is evidence of NOTHING, so it must never read as "none".
|
|
1278
|
+
const dependencyFieldsVerdict = (pkg) => {
|
|
1279
|
+
let verdict = 'none';
|
|
1280
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
1281
|
+
if (!Object.hasOwn(pkg, field)) continue;
|
|
1282
|
+
const value = pkg[field];
|
|
1283
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return 'unknown';
|
|
1284
|
+
if (Object.keys(value).length > 0) verdict = 'has';
|
|
1285
|
+
}
|
|
1286
|
+
return verdict;
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// A project with NO declared dependencies can still REQUIRE an install: a package-manager install
|
|
1290
|
+
// runs the WHOLE install-lifecycle set — the npm set (including the deprecated `prepublish`, which
|
|
1291
|
+
// historically fired on install) plus pnpm's `pnpm:devPreinstall` — so a manifest declaring any of
|
|
1292
|
+
// them, ROOT or member, needs an install even with no dependencies. dependency-free is NOT
|
|
1293
|
+
// install-free. The list is CLOSED and stated: a hook from a manager this tool has never heard of is
|
|
1294
|
+
// the honest residual of a closed-world check, the same shape as EXTERNAL_WORKSPACE_MANIFESTS.
|
|
1295
|
+
export const INSTALL_LIFECYCLE_SCRIPTS = Object.freeze(['preinstall', 'install', 'postinstall', 'prepare', 'preprepare', 'postprepare', 'prepublish', 'pnpm:devPreinstall']);
|
|
1296
|
+
// Fail-closed like the dependency check: a `scripts` field of the wrong SHAPE, or a lifecycle key
|
|
1297
|
+
// present with a NON-STRING value, is 'unknown' (never "no hook"). An empty-string value is a no-op,
|
|
1298
|
+
// not a trigger; 'has' only when a lifecycle key holds a non-empty command string.
|
|
1299
|
+
const installHookVerdict = (pkg) => {
|
|
1300
|
+
if (!Object.hasOwn(pkg, 'scripts')) return 'none';
|
|
1301
|
+
const scripts = pkg.scripts;
|
|
1302
|
+
if (scripts === null || typeof scripts !== 'object' || Array.isArray(scripts)) return 'unknown';
|
|
1303
|
+
let verdict = 'none';
|
|
1304
|
+
for (const name of INSTALL_LIFECYCLE_SCRIPTS) {
|
|
1305
|
+
if (!Object.hasOwn(scripts, name)) continue;
|
|
1306
|
+
if (typeof scripts[name] !== 'string') return 'unknown';
|
|
1307
|
+
if (scripts[name].length > 0) verdict = 'has';
|
|
1308
|
+
}
|
|
1309
|
+
return verdict;
|
|
1310
|
+
};
|
|
1311
|
+
// A native-addon manifest triggers an implicit `node-gyp rebuild` on install even with an empty
|
|
1312
|
+
// scripts block, so its presence — root or member — is a mandatory install, never proof of none.
|
|
1313
|
+
const declaresNativeBuild = (fs, dir) => lstatNoFollow(fs.lstat, join(dir, 'binding.gyp')) !== null;
|
|
1314
|
+
|
|
1315
|
+
// PROVABLY dependency-free, or nothing — read from the WORKTREE'S OWN LIVE CHECKOUT, never from
|
|
1316
|
+
// MAIN's mutable working tree: the evidence is what an install run in THIS worktree would actually
|
|
1317
|
+
// read. At provision time that is exactly HEAD; on --resume it follows the session's own edits, in
|
|
1318
|
+
// both directions (gained dependencies revoke the proof, shed ones grant it) — the same live lane
|
|
1319
|
+
// as the node_modules symlink probe. A dirty main manifest must neither grant nor revoke a verdict
|
|
1320
|
+
// about content it does not describe. A `workspaces` field of ANY shape
|
|
1321
|
+
// is UNKNOWN outright — a workspace install materializes member links and `.bin` shims even with
|
|
1322
|
+
// zero dependencies, so a workspace tree is never provably install-free. Everything else the tool
|
|
1323
|
+
// cannot enumerate — an absent/unparseable package.json, a malformed dependency or scripts field,
|
|
1324
|
+
// an install-lifecycle script, a native-addon manifest (binding.gyp), an external workspace
|
|
1325
|
+
// manifest — leaves the posture UNKNOWN, and unknown keeps the existing install advice: a false
|
|
1326
|
+
// "nothing to install" is worse than a redundant hint.
|
|
1327
|
+
const declaresNoDependencies = ({ wtRoot, fs }) => {
|
|
1328
|
+
const pkg = readPackageJson(fs, join(wtRoot, 'package.json'));
|
|
1329
|
+
if (pkg === null || dependencyFieldsVerdict(pkg) !== 'none' || installHookVerdict(pkg) !== 'none') return false;
|
|
1330
|
+
if (Object.hasOwn(pkg, 'workspaces')) return false;
|
|
1331
|
+
if (declaresNativeBuild(fs, wtRoot)) return false;
|
|
1332
|
+
// A workspace set can be declared OUTSIDE package.json too. The list is CLOSED and stated: an
|
|
1333
|
+
// exotic third manifest this tool has never heard of would still read as proof, which is the
|
|
1334
|
+
// honest residual of a closed-world check (the same shape as INSTALL_LIFECYCLE_SCRIPTS).
|
|
1335
|
+
for (const manifest of EXTERNAL_WORKSPACE_MANIFESTS) {
|
|
1336
|
+
if (lstatNoFollow(fs.lstat, join(wtRoot, manifest)) !== null) return false;
|
|
1337
|
+
}
|
|
1338
|
+
return true;
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1253
1341
|
// What the RECORD states about installing — the resolved posture for THIS worktree (the runnable
|
|
1254
1342
|
// isolated-install command, or the honest by-hand advice), never a lane-dependent hint. Probed on
|
|
1255
|
-
// the LIVE worktree after the node_modules step
|
|
1256
|
-
//
|
|
1257
|
-
//
|
|
1258
|
-
|
|
1259
|
-
|
|
1343
|
+
// the LIVE worktree after the node_modules step, and LIVE STATE WINS: a symlinked node_modules
|
|
1344
|
+
// records the unlink-first form even on a dependency-free checkout (`--resume` can meet a symlink
|
|
1345
|
+
// an earlier provision left — an install through it writes into MAIN, and the posture must never
|
|
1346
|
+
// hide that). Only then may a PROVEN dependency-free checkout short-circuit: a verdict of
|
|
1347
|
+
// "nothing to install" must not ride an install instruction.
|
|
1348
|
+
const resolveInstallPosture = ({ root, wtRoot, dependencyFree, fs }) => {
|
|
1260
1349
|
const nmPath = join(wtRoot, 'node_modules');
|
|
1261
1350
|
const nm = lstatNoFollow(fs.lstat, nmPath);
|
|
1262
|
-
if (nm
|
|
1263
|
-
|
|
1264
|
-
|
|
1351
|
+
if (nm !== null && nm.isSymbolicLink()) {
|
|
1352
|
+
const advice = resolveInstallAdvice({ root, wtRoot, fs });
|
|
1353
|
+
const separator = advice.command === null ? ' — ' : ' && ';
|
|
1354
|
+
return `the provisioned node_modules is a symlink into MAIN (an install through it writes into MAIN) — for isolation remove it first: rm ${shellQuoteArg(nmPath)}${separator}${advice.instruction}`;
|
|
1355
|
+
}
|
|
1356
|
+
if (dependencyFree) return NO_DEPENDENCIES_POSTURE;
|
|
1357
|
+
return resolveInstallAdvice({ root, wtRoot, fs }).instruction;
|
|
1265
1358
|
};
|
|
1266
1359
|
|
|
1267
|
-
const provisionNodeModules = ({ root, rootReal, wtRoot, installFlag, git, fs, report }) => {
|
|
1360
|
+
const provisionNodeModules = ({ root, rootReal, wtRoot, installFlag, dependencyFree, git, fs, report }) => {
|
|
1268
1361
|
const install = resolveInstallAdvice({ root, wtRoot, fs });
|
|
1269
1362
|
if (installFlag) {
|
|
1270
1363
|
const dst = join(wtRoot, 'node_modules');
|
|
@@ -1280,6 +1373,22 @@ const provisionNodeModules = ({ root, rootReal, wtRoot, installFlag, git, fs, re
|
|
|
1280
1373
|
: ` node_modules: install it yourself (zero spawn): ${install.instruction}`);
|
|
1281
1374
|
return 'install-printed';
|
|
1282
1375
|
}
|
|
1376
|
+
// LIVE STATE WINS the whole default lane: a node already at the worktree — a directory, or a
|
|
1377
|
+
// symlink an earlier provision left, even dangling — is what the record states; reporting
|
|
1378
|
+
// MAIN's state (`absent`) beside an existing node would contradict record.install.
|
|
1379
|
+
const dst = join(wtRoot, 'node_modules');
|
|
1380
|
+
if (lstatNoFollow(fs.lstat, dst) !== null) {
|
|
1381
|
+
report.push(' node_modules: already present in the worktree');
|
|
1382
|
+
return 'present';
|
|
1383
|
+
}
|
|
1384
|
+
// Only then may a PROVEN dependency-free checkout short-circuit the rest. Composing the posture
|
|
1385
|
+
// into one of the arms below instead produced a self-contradicting line ("after your own
|
|
1386
|
+
// install there, re-run --resume, or: no install needed"). `--install` is untouched above —
|
|
1387
|
+
// that request was explicit.
|
|
1388
|
+
if (dependencyFree) {
|
|
1389
|
+
report.push(` node_modules: ${NO_DEPENDENCIES_POSTURE}`);
|
|
1390
|
+
return NODE_MODULES_NONE;
|
|
1391
|
+
}
|
|
1283
1392
|
const mainNm = join(root, 'node_modules');
|
|
1284
1393
|
const node = classifyNodeNoFollow(mainNm, fs);
|
|
1285
1394
|
if (node.kind === 'absent') {
|
|
@@ -1313,11 +1422,6 @@ const provisionNodeModules = ({ root, rootReal, wtRoot, installFlag, git, fs, re
|
|
|
1313
1422
|
report.push(` node_modules: main's resolves outside the repo — not symlinked; ${install.instruction}`);
|
|
1314
1423
|
return 'outside-repo';
|
|
1315
1424
|
}
|
|
1316
|
-
const dst = join(wtRoot, 'node_modules');
|
|
1317
|
-
if (lstatNoFollow(fs.lstat, dst) !== null) {
|
|
1318
|
-
report.push(' node_modules: already present in the worktree');
|
|
1319
|
-
return 'present';
|
|
1320
|
-
}
|
|
1321
1425
|
guardDst(fs, wtRoot, dst);
|
|
1322
1426
|
try {
|
|
1323
1427
|
fs.symlink(mainNm, dst);
|
|
@@ -1544,7 +1648,10 @@ const finishProvision = ({ root, rootReal, targetPath, slug, branch, flags, seed
|
|
|
1544
1648
|
|
|
1545
1649
|
writeSeedPlan({ wtRoot: targetPath, srcAbs: seed.srcAbs, name: seed.name, fs, report });
|
|
1546
1650
|
const includesRecorded = provisionIncludes({ rootReal, wtRoot: targetPath, includeSources, git, fs, report, copied });
|
|
1547
|
-
|
|
1651
|
+
// Computed ONCE, from the satellite's own checkout, and threaded to both consumers — the report
|
|
1652
|
+
// lane and the record must state the SAME verdict.
|
|
1653
|
+
const dependencyFree = declaresNoDependencies({ wtRoot: targetPath, fs });
|
|
1654
|
+
const nodeModulesMode = provisionNodeModules({ root, rootReal, wtRoot: targetPath, installFlag: flags.install, dependencyFree, git, fs, report });
|
|
1548
1655
|
const vscodeMode = provisionVscode({ root, wtRoot: targetPath, slug, git, fs, report });
|
|
1549
1656
|
|
|
1550
1657
|
rebasePins({ root, wtRoot: targetPath, git, fs, report });
|
|
@@ -1559,7 +1666,7 @@ const finishProvision = ({ root, rootReal, targetPath, slug, branch, flags, seed
|
|
|
1559
1666
|
includes: includesRecorded,
|
|
1560
1667
|
nodeModules: nodeModulesMode,
|
|
1561
1668
|
vscode: vscodeMode,
|
|
1562
|
-
install: resolveInstallPosture({ root, wtRoot: targetPath, fs }),
|
|
1669
|
+
install: resolveInstallPosture({ root, wtRoot: targetPath, dependencyFree, fs }),
|
|
1563
1670
|
...orientationFields({ root, slug }),
|
|
1564
1671
|
},
|
|
1565
1672
|
fs,
|