Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/plans/2026-09-28-follow-ups-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Follow ups v2: V4 branch plan

## Status

**Active.** Branch `fix/follow-ups-v2`; exact base `e2f9dcae0554ff63921df618a819fd5e6afe80d2` (develop bootstrap #272). B1 is complete in `fb54f02b`; other rows remain unimplemented. The controller reviews and assigns later rows. One test-first unit commit per row.

The [remediation program V4](2026-09-28-remediation-program-v2.md#v4-fixfollow-ups-v2-every-small-product-cli-and-upstream-item) defines scope. The [archived Branch 9 plan](../archive/2026-09-28-superpowers-plan-branch-9-follow-ups.md) supplies task details. Paths below name current source seams and focused test targets. After an explicit directory prefix, subsequent bare filenames in the same cell use that directory. A new test named below is a proposed file. Later implementers must verify dependencies before editing.

| Row | Source or artifact mapping | Focused proof and prerequisite |
| --- | --- | --- |
| A1 | `bin/agentic-kit.mjs`; `src/commands/usage.mjs`, `models.mjs`, `audit.mjs`, `heal.mjs`, `telemetry.mjs`, `x/host.mjs` | `tests/kit/cli-json-honesty.test.mjs`, `usage-cli.test.mjs`, `models-command.test.mjs`, `telemetry-cli.test.mjs`, `status-command.test.mjs`; include unknown models verb and status positional |
| A2 | `src/commands/x/host.mjs`; `bin/agentic-kit.mjs` | `tests/kit/host-dry-run.test.mjs`, `host-cli-migration.test.mjs`; pick refusal, off, reset-routes under `--dry-run --json` |
| A3 | `src/lib/versions.mjs`; `docs/adr/0063-evidence-store-and-refresh-vocabulary.md` | `tests/kit/version-lookup-record.test.mjs`, `drift-freshness.test.mjs`; offline tried-at TTL and ADR wording |
| A4 | `src/commands/status.mjs`; `src/lib/refresh.mjs` | `tests/kit/refresh.test.mjs`, `status-version-drift-refresh.test.mjs`; injected `refreshStages` plus `service` builds no collector |
| B1 | `src/lib/paths.mjs`; `src/lib/footprint/index.mjs`, `storage.mjs`, `consumers.mjs`, `storage-reclaim-detectors.mjs`, `install.mjs`; `src/lib/host-readiness-local.mjs`, `live/process-sessions.mjs`, `hook-audit/providers/opencode.mjs`, `usage-opencode.mjs`; `src/commands/uninstall.mjs` | `tests/kit/xdg-relative.test.mjs` and specified regressions; exact-head CI gate passed before edit; preserve nullable OpenCode fallback |
| B2 | `src/commands/x/daemon-gc.mjs`, `src/commands/x/host.mjs`, `src/commands/setup.mjs` | New `tests/kit/daemon-gc-rerecord.test.mjs`, `setup-host-rerecord.test.mjs`, `host-pick-rerecord.test.mjs`; Branch 9 Task 8 plus deferred host pick; compare `sync-host-repair.test.mjs` |
| B3 | `src/lib/ruflo-memory.mjs`, `paths.mjs` | `tests/kit/ruflo-memory-location.test.mjs`, `project-memory-status.test.mjs`; compose both unsuitable reasons and make `inside()` exclude equality |
| B4 | `src/commands/status/sections/project-memory.mjs`; `src/lib/ruflo-memory-contract.mjs`, `live-check-evidence.mjs` | D-4 **B approved**: `tests/kit/project-memory-status.test.mjs`, `live-check-evidence.test.mjs`, `verify-memory-routes.test.mjs`; info only after successful installed-version `memory-routes` evidence, warn on upgrade or failure |
| B5 | `src/lib/project-memory.mjs`, `aqe-readiness.mjs`; `src/commands/status/sections/project-memory.mjs`, `aqe.mjs` | `tests/kit/project-memory.test.mjs`, `aqe-readiness.test.mjs`, `project-memory-status.test.mjs`; dot-folder scan, real `~/.agentic-qe`, and agentic-qe#757 hint |
| B6 | `src/commands/status/sections/ruflo-components.mjs`; `src/lib/ruflo-components/states.mjs` | `tests/kit/ruflo-components-status.test.mjs`; applied-but-unverified row; hooks fix line requires #3419 answer first |
| B7 | `src/lib/ruflo-daemon-config.mjs`; `src/commands/sync.mjs`, `sync/plan-versions.mjs` | `tests/kit/sync-daemon-repair.test.mjs`, `sync-dry-run-preview.test.mjs`, `sync-skip-versions.test.mjs`; F6 hidden YAML keys and F7 versions-only preview parity |
| B8 | `src/lib/maintenance/discovery/orchestrator.mjs`, `history.mjs` | `tests/kit/maintenance-discovery-orchestrator.test.mjs`, `maintenance-recovery.test.mjs`; restart after pause shows paused history |
| B9 | `src/lib/exec.mjs`, `execution/process-tree.mjs` | `tests/kit/process-tree.test.mjs`; abort kills descendants; Windows CI required |
| B10 | `src/lib/maintenance/discovery/partitions.mjs`; inventory `src/lib/live/jsonl-tailer.mjs`, `live/transcript-streams.mjs`, `telemetry/store.mjs`, `maintenance/management/service-store.mjs` for additional persisted IDs | `tests/kit/file-identity-bigint.test.mjs`; distinguish IDs above `2^53`; enumerate the exact sites before edit |
| B11 | `src/lib/live-checks.mjs` | `tests/kit/live-checks.test.mjs`; skipped deja-vu check says skipped and check-created temp folders are cleaned |
| B12 | `src/commands/setup.mjs`; `src/lib/memory-probe-cleanup.mjs` | `tests/kit/setup-memory-probe.test.mjs`; disposable real Ruflo reproduction first, fix only if unused `agentdb-memory.db` appears |
| B13 | `src/commands/sync.mjs`; `src/lib/aqe-project-pin.mjs` | `tests/kit/sync-command.test.mjs`, `aqe-project-pin.test.mjs`; only if program §2 step 2 shows sync omitted the AQE pin |
| C1 | `.github/workflows/ci.yml`; `src/lib/aqe-readiness.mjs`; `src/lib/hook-audit/agentic-dependency-constraints.json` | `tests/live/ruflo-memory-routing.test.mjs`, `tests/kit/aqe-readiness.test.mjs`; disposable macOS and temporary Linux/Windows CI busy-rule evidence; remove temporary job before merge; #240 action follows result |
| C2 | `docs/host-support.md`; `src/lib/hook-audit/agentic-dependency-constraints.json` | `tests/kit/ruflo-support-window.test.mjs` plus link check; verify AQE 3.14.4 #528/#532/#535 and Ruflo #2356/#420 first |
| C3 | `.github/workflows/nightly.yml`; vidaunited's `trace-ort.mjs` hook (obtain and verify its exact script path before adding) | `tests/kit/upstream-watch-workflow.test.mjs` plus macOS artifact receipt; exact upstream #2885 post text requires user approval |
| C4 | `scripts/upstream-watch/classify.mjs`, `fetch.mjs`, `ledger.mjs`, `dispatch.mjs`, `render.mjs`; `src/lib/hook-audit/agentic-dependency-constraints.json` | `tests/kit/upstream-watch-script.test.mjs`, `upstream-watch-record.test.mjs`, `upstream-watch-dispatch.test.mjs`, `upstream-watch-registry.test.mjs`; use ignored `reports/n5-253-deferred-minors.md` §2 for M7/M8/minors 1–12; M10 declined |
| C5 | `src/lib/aqe-guidance.mjs`; `src/commands/setup.mjs`; ignored `.superpowers/sdd/2026-09-28-follow-ups-v2/c5-issue-draft.md` | D-6 **A approved**: controller's isolated AQE init reproduction is evidence handoff; draft issue with command/version/expected/actual, then obtain approval of exact posting text. B0-16 draft only if D-16 B |
| C6 | `docs/host-support.md`; `src/lib/ruflo-support-window.mjs`, `aqe-readiness.mjs`; `src/lib/hook-audit/agentic-dependency-constraints.json` | `tests/kit/ruflo-support-window.test.mjs`, `aqe-readiness.test.mjs`; pre-PR live checks against newest supported Ruflo `security secrets --path`, Codex read-only app-server flags, and AQE 3.14.x |

B1 used disposable homes, guarded focused tests, and the ignored B1 report at `.superpowers/sdd/2026-09-28-follow-ups-v2/b1-report.md`. No shared manifests, lockfiles, ADR index, or decision log change belongs to this plan update. The controller owns integration and the whole-branch gate.
4 changes: 2 additions & 2 deletions src/commands/uninstall.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@

function protectedDejaVuRoots(homeDir, env) {
const absolute = (value) => typeof value === 'string' && path.isAbsolute(value);
const configBases = [path.join(homeDir, '.config'), env.XDG_CONFIG_HOME, env.APPDATA]
const configBases = [path.join(homeDir, '.config'), paths.xdgBase('XDG_CONFIG_HOME', null, { env }), env.APPDATA]
.filter(absolute);
const dataBases = [path.join(homeDir, '.local', 'share'), env.XDG_DATA_HOME]
const dataBases = [path.join(homeDir, '.local', 'share'), paths.xdgBase('XDG_DATA_HOME', null, { env })]
.filter(absolute);
return {
sourceRoots: [
Expand All @@ -121,7 +121,7 @@
* Delete only the v0.19 doctor-reported derived index. Raw doctor output and
* the validated path stay inside this function; callers receive reason codes.
*/
export async function purgeDejaVuIndex({

Check warning on line 124 in src/commands/uninstall.mjs

View workflow job for this annotation

GitHub Actions / quality (typecheck, lint, build, audit)

Async function 'purgeDejaVuIndex' has a complexity of 27. Maximum allowed is 25
runner = runCmd,
homeDir = paths.home,
env = process.env,
Expand Down
6 changes: 3 additions & 3 deletions src/lib/footprint/consumers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
import fs from 'node:fs';
import path from 'node:path';
import {
claudeDir, codexDir, configDir, globalRoot, home, isWindows, npxCacheDir,
claudeDir, codexDir, configDir, globalRoot, home, isWindows, npxCacheDir, xdgBase,
} from '../paths.mjs';
import {
hasValue, measured, rootMeasurements, sumMeasurements, unknown, walkTree,
Expand Down Expand Up @@ -167,8 +167,8 @@ export const CONSUMER_WALK_LIMITS = Object.freeze({
// to audit for the sake of a read-only ranking. Kit and host paths still come
// from paths.mjs — nothing home-relative that the kit itself owns is spelled out
// below.
const xdgCache = (env) => env.XDG_CACHE_HOME || path.join(home, '.cache');
const xdgData = (env) => env.XDG_DATA_HOME || path.join(home, '.local', 'share');
const xdgCache = (env) => xdgBase('XDG_CACHE_HOME', path.join(home, '.cache'), { env });
const xdgData = (env) => xdgBase('XDG_DATA_HOME', path.join(home, '.local', 'share'), { env });
const macCache = () => path.join(home, 'Library', 'Caches');
const winLocalAppData = (env) => env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');

Expand Down
6 changes: 3 additions & 3 deletions src/lib/footprint/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import fs from 'node:fs';
import path from 'node:path';
import {
claudeDir, claudeSettingsPath, claudeUserMcpPath, codexConfigPath, codexDir, configDir, home,
claudeDir, claudeSettingsPath, claudeUserMcpPath, codexConfigPath, codexDir, configDir, stateBase,
} from '../paths.mjs';
import { loadKitConfig } from '../config.mjs';
import { defaultOpencodeDbPath } from '../usage-opencode.mjs';
Expand Down Expand Up @@ -65,8 +65,8 @@ export const INCLUDE_PROJECT_TREES_DEFAULT = false;
* point: these are the files that grow fastest between deep scans (ledgers,
* tee files, index caches), and a user watching one grow should not have to
* run a deep scan to see it move. */
function knownFileSpecs() {
const stateRoot = process.env.XDG_STATE_HOME || path.join(home, '.local', 'state');
export function knownFileSpecs() {
const stateRoot = stateBase();
const kit = (name) => path.join(configDir(), name);
// [id, host, category, label, path] — the categories are STORAGE_CATEGORIES'
// vocabulary so a known file and its deep-tier node land in the same bucket.
Expand Down
4 changes: 2 additions & 2 deletions src/lib/footprint/install.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import path from 'node:path';
import { MANAGED_COMPANION_REGISTRY } from '../adapters/companion-registry.mjs';
import { HOST_REGISTRY } from '../adapters/registries.mjs';
import {
home, isWindows, globalRoot, npxCacheDir, claudeDir, codexPluginCacheDir,
home, isWindows, globalRoot, npxCacheDir, claudeDir, codexPluginCacheDir, xdgBase,
} from '../paths.mjs';
import { installedVersion, KIT_PKG } from '../versions.mjs';
import { kbDir, present as brainPresent, installedVersion as brainVersion } from '../ruvnet-brain.mjs';
Expand Down Expand Up @@ -580,7 +580,7 @@ function vibiumCachePath({ env, platform }) {
if (platform === 'win32') {
return path.join(env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'vibium');
}
return path.join(env.XDG_CACHE_HOME || path.join(home, '.cache'), 'vibium');
return path.join(xdgBase('XDG_CACHE_HOME', path.join(home, '.cache'), { env }), 'vibium');
}

const presentFile = (file, fsImpl) => {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/footprint/storage-reclaim-detectors.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
// process.platform, so the wrong-platform root simply reads absent and a machine
// carrying both (a tool that moved its cache) reports both.
import path from 'node:path';
import { home, isWindows } from '../paths.mjs';
import { home, isWindows, xdgBase } from '../paths.mjs';
import { decodeClaudeProjectDir } from './project-sources.mjs';
import {
rootMeasurements, measured, unknown, statNode, sumMeasurements, hasValue,
} from './walk.mjs';
import { candidate } from './storage-reclaim.mjs';

const xdgCache = (env) => env.XDG_CACHE_HOME || path.join(home, '.cache');
const xdgData = (env) => env.XDG_DATA_HOME || path.join(home, '.local', 'share');
const xdgCache = (env) => xdgBase('XDG_CACHE_HOME', path.join(home, '.cache'), { env });
const xdgData = (env) => xdgBase('XDG_DATA_HOME', path.join(home, '.local', 'share'), { env });
const macCache = () => path.join(home, 'Library', 'Caches');
const winLocalAppData = (env) => env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');

Expand Down
9 changes: 5 additions & 4 deletions src/lib/footprint/storage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
// `detectWorktrees` is false.
import fs from 'node:fs';
import path from 'node:path';
import { home, claudeDir, codexDir, configDir } from '../paths.mjs';
import { home, claudeDir, codexDir, configDir, stateBase } from '../paths.mjs';
import { defaultOpencodeDbPath } from '../usage-opencode.mjs';
import { decodeClaudeProjectDir, transcriptMetadata } from './project-sources.mjs';
import { classifyWorkingContext } from './working-context.mjs';
Expand Down Expand Up @@ -118,8 +118,9 @@ const flatDir = () => true;
*
* @returns {StorageRoot[]}
*/
export function defaultStorageRoots({ env = process.env, projects = null } = {}) {
const stateRoot = env.XDG_STATE_HOME || path.join(home, '.local', 'state');
export function defaultStorageRoots({ env = process.env, projects = null,
home: h = home, platform = process.platform, p = path } = {}) {
const stateRoot = stateBase({ env, home: h, platform, p });
const opencodeData = path.dirname(defaultOpencodeDbPath());
const claude = (name) => path.join(claudeDir(), name);
const codex = (name) => path.join(codexDir(), name);
Expand Down Expand Up @@ -183,7 +184,7 @@ export function defaultStorageRoots({ env = process.env, projects = null } = {})
{
id: 'ak-runtime-debug', category: 'ledgers-and-logs', host: 'agentic-kit',
label: 'runtime-debug.log',
path: path.join(stateRoot, 'agentic-kit', 'runtime-debug.log'), layout: 'tree',
path: p.join(stateRoot, 'agentic-kit', 'runtime-debug.log'), layout: 'tree',
},
{
id: 'ak-config', category: 'kit-caches', host: 'agentic-kit',
Expand Down
3 changes: 2 additions & 1 deletion src/lib/hook-audit/providers/opencode.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { xdgBase } from '../../paths.mjs';

import {
normalizedOccurrence, publicSource, readBoundedFile, readJsonSource,
Expand Down Expand Up @@ -90,7 +91,7 @@ function moduleRecords(source) {
}

export function auditOpenCodeHooks({
opencodeRoot = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'opencode'),
opencodeRoot = path.join(xdgBase('XDG_CONFIG_HOME', path.join(os.homedir(), '.config')), 'opencode'),
projectRoots = [process.cwd()],
opencodeVersion = 'unknown',
ownership = null,
Expand Down
7 changes: 4 additions & 3 deletions src/lib/host-readiness-local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import path from 'node:path';
import { createHash } from 'node:crypto';
import { readContextConfig } from './codex-context-config.mjs';
import { withDb } from './sqlite.mjs';
import { xdgBase } from './paths.mjs';

const LIMIT = 1024 * 1024;
const plain = x => x !== null && typeof x === 'object' && !Array.isArray(x);
Expand Down Expand Up @@ -199,8 +200,8 @@ function selectedOpenCodeAgent(config, agentDirs) {
}

function loadOpenCode({ cwd, home, env }, evidence) {
const global = path.join(env.XDG_CONFIG_HOME || path.join(home, '.config'), 'opencode');
const data = path.join(env.XDG_DATA_HOME || path.join(home, '.local/share'), 'opencode');
const global = path.join(xdgBase('XDG_CONFIG_HOME', path.join(home, '.config'), { env }), 'opencode');
const data = path.join(xdgBase('XDG_DATA_HOME', path.join(home, '.local/share'), { env }), 'opencode');
const auth = document(path.join(data, 'auth.json'), evidence, env);
if (Object.values(auth).some(value => value?.type === 'wellknown') || openCodeRemote(data, evidence)) throw new Error('unsupported');
if (fs.existsSync(path.join(global, 'config'))) throw new Error('unsupported'); // legacy TOML migration is native-owned
Expand Down Expand Up @@ -289,7 +290,7 @@ function defaultOpenCode({ config, auth, home, env, allowed, credentialed }, evi
// Native default tries recent available selections, then a configured
// provider. Do not borrow credentials from an unrelated provider.

const recent = document(path.join(env.XDG_STATE_HOME || path.join(home, '.local/state'), 'opencode/model.json'), evidence, env).recent;
const recent = document(path.join(xdgBase('XDG_STATE_HOME', path.join(home, '.local/state'), { env }), 'opencode/model.json'), evidence, env).recent;
if (Array.isArray(recent) && recent.length) throw new Error('unsupported'); // availability requires native provider catalog
const configured = Object.keys(config.provider ?? {}).filter(allowed);
if (configured.length === 1) provider = configured[0];
Expand Down
4 changes: 2 additions & 2 deletions src/lib/live/process-sessions.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { execFile } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';
import { inspectGitWorkspace } from './git-workspace.mjs';
import { stateBase } from '../paths.mjs';

const execFileAsync = promisify(execFile);

Expand All @@ -31,7 +31,7 @@ const WIN32_SURVEY_SCRIPT = fileURLToPath(
function runtimeDebug(stage, fields = {}) {
if (!process?.env || process.env.AK_RUNTIME_DEBUG !== '1') return;
try {
const root = process.env.XDG_STATE_HOME || path.join(os.homedir(), '.local', 'state');
const root = stateBase();
const file = process.env.AK_RUNTIME_DEBUG_FILE || path.join(root, 'agentic-kit', 'runtime-debug.log');
const safeStage = String(stage || 'unknown').replace(/[^a-z0-9._-]/gi, '_').slice(0, 64);
const kv = Object.entries(fields)
Expand Down
Loading
Loading