@sabaiway/agent-workflow-kit 3.9.0 → 3.10.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 CHANGED
@@ -4,6 +4,31 @@ 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.10.0 — install advice reads the worktree checkout (AD-071)
8
+
9
+ The install advice picked its package manager from MAIN's `package.json` and lockfiles while the
10
+ printed command targets the SATELLITE (`cd <worktree> && <manager> install`) — so a dirty MAIN
11
+ lockfile could push the advice into ambiguity or the wrong manager, and a MAIN advanced past the
12
+ satellite's base steered a satellite it no longer describes. The evidence and the target are now
13
+ the SAME tree:
14
+
15
+ - **Manager evidence reads the worktree's own live checkout** — the `packageManager` field and
16
+ the lockfile scan, the same live lane the dependency-free proof (3.6.0) and the node_modules
17
+ symlink probe already use. MAIN's mutable working tree never steers manager selection; MAIN
18
+ state legitimately steers only the symlink lane (the shared-cache link and its unlink-first
19
+ posture), unchanged.
20
+ - **The contract ships in the worktrees mode doc as one pinned sentence** (a named test locks
21
+ the exact wording): all manifest/lockfile install evidence is read from the worktree's own
22
+ live files at the moment the posture is resolved — deliberately NOT «exactly HEAD», because a
23
+ `post-checkout` hook can shape the checkout before the posture is resolved.
24
+ - **No resume-tolerance smuggling.** The shipped clean-tree `--resume` STOP is pinned byte-exact
25
+ by a real-git test; dirty-resume semantics remain a queued redesign. A satellite behind an
26
+ advanced MAIN now gets advice about ITSELF — pinned by a real-git test where MAIN's lockfile
27
+ advance does not re-steer the satellite's refreshed record.
28
+ - **Honest residual:** a manager signal living only in MAIN's working tree (e.g. an ignored,
29
+ uncommitted lockfile) no longer steers the advice — the checkout is the truth even when that
30
+ yields the npm default.
31
+
7
32
  ## 3.9.0 — the --include copy door proves what it copies (AD-070)
8
33
 
9
34
  Nothing bound the node the provision copy walk read to the node `--include` preflight approved:
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.9.0'
6
+ version: '3.10.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "3.9.0",
6
+ "version": "3.10.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "3.9.0",
3
+ "version": "3.10.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",
@@ -104,8 +104,9 @@ PLUS the three facts a fresh satellite session cannot derive from its own checko
104
104
  isolated-install command when the package manager is unambiguous, the honest install-by-hand
105
105
  advice when it is not, and — when the provisioned `node_modules` is a SYMLINK into main — the
106
106
  unlink-first form, because a plain install through the symlink writes into MAIN and is never
107
- 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
107
+ 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: the checkout's LIVE files at the moment the posture is resolved; on `--resume` a dirty tree is then refused by the clean-tree verify, after the record refresh) 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
108
108
  isolated-install command.
109
+ All manifest/lockfile install evidence — the dependency-free proof AND the package-manager selection (the `packageManager` field, lockfiles) — is read from the worktree's own LIVE files at the moment the posture is resolved (on `--resume` too, where a dirty tree is then refused by the clean-tree verify); MAIN's mutable working tree never steers manager selection.
109
110
 
110
111
  **Honesty:** there is NO preview step on the writers — over-warned by design. The tool never
111
112
  commits, never pushes, never runs a subscription CLI. Every content read and regular-file copy
@@ -1374,8 +1374,10 @@ const LOCKFILE_MANAGERS = Object.freeze([
1374
1374
  const NEUTRAL_INSTALL_ADVICE =
1375
1375
  'install command not printed — package manager is ambiguous or unknown; install dependencies in the worktree by hand';
1376
1376
 
1377
- const resolveInstallAdvice = ({ root, wtRoot, fs }) => {
1378
- const pkg = readFileNoFollow(fs, join(root, 'package.json'));
1377
+ // Advice evidence = the WORKTREE'S OWN LIVE checkout (manifest AND lockfiles): the printed
1378
+ // command runs in the satellite, so what main's working tree happens to hold must not steer it.
1379
+ const resolveInstallAdvice = ({ wtRoot, fs }) => {
1380
+ const pkg = readFileNoFollow(fs, join(wtRoot, 'package.json'));
1379
1381
  let manager = null;
1380
1382
  let inspectLocks = false;
1381
1383
  if (pkg.absent) {
@@ -1406,7 +1408,7 @@ const resolveInstallAdvice = ({ root, wtRoot, fs }) => {
1406
1408
  for (const [name, candidate] of LOCKFILE_MANAGERS) {
1407
1409
  let st;
1408
1410
  try {
1409
- st = fs.lstat(join(root, name));
1411
+ st = fs.lstat(join(wtRoot, name));
1410
1412
  } catch (err) {
1411
1413
  if (err?.code === 'ENOENT') continue;
1412
1414
  return { command: null, instruction: NEUTRAL_INSTALL_ADVICE };
@@ -1481,9 +1483,10 @@ const declaresNativeBuild = (fs, dir) => lstatNoFollow(fs.lstat, join(dir, 'bind
1481
1483
 
1482
1484
  // PROVABLY dependency-free, or nothing — read from the WORKTREE'S OWN LIVE CHECKOUT, never from
1483
1485
  // MAIN's mutable working tree: the evidence is what an install run in THIS worktree would actually
1484
- // read. At provision time that is exactly HEAD; on --resume it follows the session's own edits, in
1485
- // both directions (gained dependencies revoke the proof, shed ones grant it) the same live lane
1486
- // as the node_modules symlink probe. A dirty main manifest must neither grant nor revoke a verdict
1486
+ // read, taken at the moment the posture is resolved (a post-checkout hook may already have shaped
1487
+ // the checkout; on --resume it follows the session's own edits, in both directionsgained
1488
+ // dependencies revoke the proof, shed ones grant it the same live lane
1489
+ // as the node_modules symlink probe). A dirty main manifest must neither grant nor revoke a verdict
1487
1490
  // about content it does not describe. A `workspaces` field of ANY shape
1488
1491
  // is UNKNOWN outright — a workspace install materializes member links and `.bin` shims even with
1489
1492
  // zero dependencies, so a workspace tree is never provably install-free. Everything else the tool
@@ -1512,20 +1515,20 @@ const declaresNoDependencies = ({ wtRoot, fs }) => {
1512
1515
  // an earlier provision left — an install through it writes into MAIN, and the posture must never
1513
1516
  // hide that). Only then may a PROVEN dependency-free checkout short-circuit: a verdict of
1514
1517
  // "nothing to install" must not ride an install instruction.
1515
- const resolveInstallPosture = ({ root, wtRoot, dependencyFree, fs }) => {
1518
+ const resolveInstallPosture = ({ wtRoot, dependencyFree, fs }) => {
1516
1519
  const nmPath = join(wtRoot, 'node_modules');
1517
1520
  const nm = lstatNoFollow(fs.lstat, nmPath);
1518
1521
  if (nm !== null && nm.isSymbolicLink()) {
1519
- const advice = resolveInstallAdvice({ root, wtRoot, fs });
1522
+ const advice = resolveInstallAdvice({ wtRoot, fs });
1520
1523
  const separator = advice.command === null ? ' — ' : ' && ';
1521
1524
  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}`;
1522
1525
  }
1523
1526
  if (dependencyFree) return NO_DEPENDENCIES_POSTURE;
1524
- return resolveInstallAdvice({ root, wtRoot, fs }).instruction;
1527
+ return resolveInstallAdvice({ wtRoot, fs }).instruction;
1525
1528
  };
1526
1529
 
1527
1530
  const provisionNodeModules = ({ root, rootReal, wtRoot, installFlag, dependencyFree, git, fs, report }) => {
1528
- const install = resolveInstallAdvice({ root, wtRoot, fs });
1531
+ const install = resolveInstallAdvice({ wtRoot, fs });
1529
1532
  if (installFlag) {
1530
1533
  const dst = join(wtRoot, 'node_modules');
1531
1534
  const existing = lstatNoFollow(fs.lstat, dst);
@@ -1866,7 +1869,7 @@ const finishProvision = ({ root, rootReal, targetPath, slug, branch, flags, seed
1866
1869
  includes: includesRecorded,
1867
1870
  nodeModules: nodeModulesMode,
1868
1871
  vscode: vscodeMode,
1869
- install: resolveInstallPosture({ root, wtRoot: targetPath, dependencyFree, fs }),
1872
+ install: resolveInstallPosture({ wtRoot: targetPath, dependencyFree, fs }),
1870
1873
  ...orientationFields({ root, slug }),
1871
1874
  },
1872
1875
  fs,