Repository navigation
fix(cli): refuse relative --target-dir in doctor, update, and uninstall - #388
Conversation
install refused a relative or unclean --target-dir as invalid_target (refactor-ia#369), but doctor accepted it, probed the directory with a temporary file, and exited 0, while update reported invalid_catalog or target_absent. Every command that consumes --target-dir now runs the same validTargetDir check before reading or writing the directory, and refuses with error=invalid_target and exit 64. docs/operating.md used a relative path in its install example; it now uses an absolute one and states the shared rule. Fixes refactor-ia#372
Fleet review (qwen38)Lens: evidence. The PR implements exactly what issue #372 asks: one shared validTargetDir check (absolute and filepath.Clean, extracted from the #369 install check) now gates doctor, runDirectoryUninstall, runDirectoryUpdate, and install before any directory read or write, with the same error=invalid_target exit 64 diagnostic. The new test pins all four commands against a relative existing dir, a relative missing path, and an absolute-but-unclean path that resolves to the existing dir, asserting exit code, exact stderr, empty stdout, an unchanged directory listing (including a foreign sentinel file), and that no path is created — solid, reproducible evidence for the acceptance criterion on Unix. Install behavior is unchanged (pure extraction) and documented absolute-path behavior stays pinned by the four named pre-existing tests. Rollback is a single-commit revert with no format changes. No blocking defect found; two evidence gaps are recorded as a question and a suggestion. Context output was complete (not truncated). Suggestion (1)
Question (1)
Verdict: ready to merge |
Fleet review (glmflash)Lens: adversarial. The fix correctly extracts the #369 install check into a shared validTargetDir (absolute && Clean-identical) and applies it in doctor, update, and uninstall before any stat, read, or write, which satisfies issue #372's stated direction (a target install refuses must be refused the same way everywhere). I attacked boundary paths: Windows drive-relative and rooted-without-drive paths fail IsAbs; trailing separators and '/./' segments fail the Clean comparison; empty --target-dir still routes to host doctor as before; the check runs before doctor's probe write and before update's stat, so the previous misleading invalid_catalog/target_absent outcomes and the doctor probe-file write can no longer occur for relative targets. The new test exercises all four commands through the binary entry point (runWithDependencies) against existing, missing, and unclean-absolute targets and asserts no writes, including the foreign sentinel and the not-created missing path. One nuance: uninstall remains stricter than the shared check (skillroot also refuses the filesystem root and mixed separators, e.g. Windows 'C:...\smoke/rel' passes validTargetDir but is refused by uninstall), so the docs phrase 'refuse ... the same way' is slightly overbroad — but this divergence is fail-closed, explicitly documented in the PR's out-of-scope notes, and does not contradict the issue's one-directional contract. Suggestion (1)
Verdict: ready to merge |
Fleet review (glm)Lens: requirements + adversarial. The diff satisfies issue #372's expected direction: the #369 absolute-and-clean check is extracted into one shared validTargetDir and is now the first thing runDirectoryInstall, runDoctorCommand, runDirectoryUninstall, and runDirectoryUpdate do with --target-dir, before any stat, plan, or write. A code search over the repo confirms --target-dir is consumed only by install.go, doctor.go, and update.go, so no command that takes the flag was missed. Install and uninstall behavior is unchanged (uninstall keeps the stricter skillroot validation behind the shared check, both producing the same invalid_target diagnostic). The new test covers the three adversarial shapes that matter (relative existing, relative missing, absolute-but-unclean naming a real directory), asserts exit 64 with exactly error=invalid_target, empty stdout, no writes to the existing directory, and that the missing relative path is not created. The Windows-harness matrix (drive-relative, rooted-without-drive, mixed separators) is consistent with filepath.IsAbs/filepath.Clean semantics. No blocking findings. No findings. Verdict: ready to merge |
Fleet review (mimo)Lens: requirements. The diff fully implements the path-form half of issue #372: I verified by source inspection that exactly four commands consume --target-dir (install, doctor, update, uninstall) and all four now run the shared validTargetDir guard before any stat or write, with the uniform error=invalid_target / exit 64 diagnostic, plus a test pinning 4 commands x 3 relative/unclean targets. However, against the issue's Expected direction read literally - 'a target that install would refuse as invalid_target should be refused the same way by every command that consumes --target-dir' - the criterion is satisfied only partially: install also refuses absolute missing and non-directory targets as invalid_target (install.go:119-121), while doctor (doctor.go:122 -> 134) and update (update.go:243-245) still accept those same absolute targets with different diagnostics, and the PR's own docs edit states the two rules adjacently without reconciling them (operating.md:81-82). One clarification question remains on the --target-dir= form, which install accepts but doctor/update/uninstall reject as invalid_command before any target validation. Blocking (1)
Question (1)
Verdict: not ready |
Fleet review (deepseek)Lens: evidence + test design. The shared validTargetDir check is wired correctly into install, doctor, update, and uninstall (all four --target-dir consumers in internal/cli), and the new test drives every command through runWithDependencies and asserts exit 64, exact stderr, empty stdout, and an unchanged target listing, so the relative and unclean refusal classes are genuinely pinned for doctor and update. One member of the class the PR claims to close survives: an empty --target-dir value is tested before the guard, and for uninstall that branch is the destructive all-roots path. Evidence and test design: the recorded RED/GREEN evidence is reproducible from the diff for the three covered inputs, but the new test has no case for the empty value or the --target-dir= form, and its uninstall subtests cannot distinguish the added guard from the pre-existing skillroot check. Blocking (1)
Suggestion (2)
Verdict: not ready |
doctor and uninstall treated `--target-dir ""` as an absent flag: doctor ran the host runtime report, and uninstall ran the canonical all-roots uninstall, so `cortex uninstall --target-dir "$TARGET"` with an unset TARGET removed every Cortex-owned skill and exited 0. update refused the same input as invalid_command. All three now track whether the flag was given and refuse an empty value with error=invalid_target, exit 64, like install. The refusal test adds the empty value, Windows path shapes (drive-relative, rooted without a drive, mixed separators), install's --target-dir= form, target-before-catalog precedence for update, and an unchanged home. Its uninstall dependencies fail the test if the canonical roots are ever resolved, so it cannot touch a real home. docs/operating.md now states exactly which inputs every command refuses and which answers stay command-specific. Refs refactor-ia#372
|
Thanks for the reviews. I pushed ff42157. Empty Absolute missing or non-directory targets (mimo, blocking). This one needs your call, because the issue text and doctor's documented contract pull in different directions. My claim comment and your acceptance ("one refusal test per consuming command, error=invalid_target, exit 64, no writes") scoped #372 to the path form. Doctor's documented job (#343, the
Windows path shapes (qwen38). The test now adds Windows-only targets: drive-relative ( Target vs catalog precedence (qwen38). The target check is the first statement of Writes outside the target (deepseek). Each subtest now also asserts the home directory is unchanged after the refusal. Uninstall guard indistinguishable (deepseek). Right. Docs vs uninstall strictness (glmflash). The doc now says uninstall also refuses a filesystem root. One correction: mixed separators like Verification: Linux container (non-root, LF): |
Fleet review (qwen38)Lens: evidence. Issue #372's criterion is that any target install refuses as invalid_target must be refused the same way by every command consuming --target-dir, before any read or write. The diff shares the extracted validTargetDir (absolute and clean) across doctor, update, and uninstall, and tracks flag presence so an empty value can no longer fall back to the host report or the all-roots uninstall. Evidence audit: TestDirectoryTargetCommandsRefuseRelativeTargets goes through runWithDependencies (the binary's entry point) and covers all four commands against four representative target shapes (existing relative dir with a foreign sentinel, missing relative path, absolute-but-unclean path naming the existing directory, empty value), asserting exit 64, stderr exactly error=invalid_target, empty stdout, an unchanged target listing, an uncreated missing path, and an unchanged home; the guarded resolveRoots makes even a RED run unable to touch a real home. I verified from the head source that the helpers exist (canonicalHome install_test.go:41, directoryInstallDependencies install_directory_test.go:16), exitUsage = 64 (doctor.go:35), and the parseUpdateArgs tail only whitelists pi/opencode/claude-code, so runtimeID == "" genuinely implies directory mode — the PR's key claim holds. Every acceptance criterion is backed by a reproducible test; the Windows and container harness runs are additional asserted evidence that the test gates cleanly on non-Windows platforms. One diagnostic-ordering nuance is recorded as a question; nothing blocks. Question (1)
Verdict: ready to merge |
Fleet review (glm)Lens: requirements + adversarial. The PR satisfies issue #372's core requirement: the #369 target check is extracted into validTargetDir and every command that consumes --target-dir (doctor, update, uninstall) now refuses relative, unclean, and empty values with exit 64 error=invalid_target before reading or writing anything. The previously destructive uninstall --target-dir "" fallback to the canonical all-roots uninstall is now refused, which is the most important safety win. The test matrix is thorough (relative existing/missing, unclean absolute naming an existing dir, empty, Windows drive-relative/rooted-without-drive/mixed-separator shapes, equals-form, target-before-catalog precedence) and the guarded uninstall dependencies plus home-unchanged assertions make a RED run safe. Docs match the new behavior. No blocking defects found; two clarifications below, one of which the author already surfaced for a maintainer ruling. Question (2)
Verdict: ready to merge |
Fleet review (deepseek)Lens: evidence + test design. I re-reviewed from scratch at head ff42157 (my previous review of 1227271 blocked on the empty-value hole; the Suggestion (1)
Question (1)
Verdict: ready to merge |
Fleet review (glmflash)Lens: adversarial. Attacked the target-validation boundaries across all four --target-dir consumers and the mode-dispatch change in update, reading the head sources directly. Verified: (1) validTargetDir reproduces the #369 install check exactly (absolute + Clean-identical), so no command is looser than install; Windows drive-relative and rooted-without-drive paths fail IsAbs, and trailing separators, '/./' segments, and mixed separators fail the Clean comparison. (2) The update dispatch on runtimeID == "" cannot misroute a runtime-mode invocation: parseUpdateArgs rejects an empty runtime ID with ok=false -> invalid_command, and ok=true with an empty runtime ID only comes from the targetSet branch, so bare 'cortex update' is still invalid_command, not invalid_target. (3) The targetSet tracking closes two silent fallthroughs (uninstall --target-dir "" reaching the canonical all-roots uninstall, doctor --target-dir "" reaching the host report) and makes the duplicate-flag check strictly stricter (previously --target-dir "" --target-dir x silently used the second value). (4) Code search over the repo shows doctor, install, and update are the only non-test consumers of --target-dir (uninstall lives in doctor.go), so coverage is complete. (5) The refusal tests guard the real home via a failing resolveRoots and assert no writes, including the foreign sentinel and the not-created missing path. The check runs before doctor's probe write and before update's stat, so the misleading invalid_catalog/target_absent outcomes for relative targets can no longer occur. Issue #372's contract is one-directional (a target install refuses must be refused everywhere); uninstall being stricter is fail-closed and fine. No blocking findings. Suggestion (2)
Verdict: ready to merge |
…rget Address the fleet review on refactor-ia#388: - A trailing separator (the form shell completion produces) is refused as invalid_target by every --target-dir command. On main, doctor accepted it and reported presence, update failed with the misleading invalid_catalog (exit 70), and install and uninstall already refused it; the change is pinned and named in docs/operating.md. - update --runtime "" stays a usage error, so the runtime mode never reaches the directory mode through an empty runtime ID. - A valid absolute target is still accepted (doctor control case), and TestValidTargetDir pins the check itself, including over-refusal. Refs refactor-ia#372
|
Thanks for this round. I pushed 12c5662 and ccf3ebf, and updated the PR body for the current head. Usage errors before the target check (qwen38). Confirmed with the built binary at ff42157: Trailing separator (deepseek). A real behavior change from this PR, now pinned and documented. On Valid-target control case (deepseek). Added
Mixed separators (glmflash). These don't pass Duplicate flag with an empty first value (glmflash). Right, that's a stricter behavior: Missing absolute targets (glm, deepseek). This is still the open decision from my previous comment: doctor's presence report vs the issue's literal wording. I'll record whichever ruling you make, either in #372's expected direction or as a follow-up issue for update. Verification at ccf3ebf: Linux container (non-root, LF) with |
Fleet review (mimo)Lens: requirements. Reviewed head ccf3ebf (the PR moved from ff42157 to ccf3ebf during this run; the two new commits add trailing-separator, empty-runtime and valid-absolute control subtests plus TestValidTargetDir, and a docs note that malformed command lines are refused as invalid_command before the target check). Against issue #372 the PR satisfies the acceptance bar set in the issue's approval thread: the #369 path-form check is extracted into the single validTargetDir (install.go:102) and runs before any stat, read or write in all four --target-dir consumers (install, doctor, uninstall, update - confirmed by code search as the only consumers), and the refusal test pins every command against relative, missing-relative, unclean, trailing-separator and empty targets (plus Windows shapes) asserting exit 64, exact error=invalid_target stderr, empty stdout, an unchanged target listing, an unchanged home, and non-creation of the missing path; the empty --target-dir value now reaches the guard instead of falling back to the host doctor report or the canonical all-roots uninstall; docs/operating.md documents the shared contract, the invalid_command precedence, and the deliberate divergence. The single criterion not satisfied as literally worded in the issue body (missing and non-directory absolute targets keep each command's own answer) is a conflict between two texts of the issue itself - the body's Expected direction versus the approval thread's 'keep the existing absolute-path behavior pinned' and doctor's documented presence-reporting contract - and it is now explicitly documented in the PR, listed under Out of Scope as a maintainer decision raised in the review thread, and escalated by the author for a ruling (question 1); question 2 re-asks the equals-form scope call carried from my previous review. No blocking findings. Question (2)
Verdict: ready to merge |
Fleet review (qwen38)Lens: evidence. The PR implements issue #372's acceptance criterion directly: one shared Suggestion (1)
Question (1)
Verdict: ready to merge |
Fleet review (deepseek)Lens: evidence + test design. Reviewed at head ccf3ebf, merging my earlier review of ff42157. The two gaps I raised there are now closed: the table carries a Suggestion (3)
Question (1)
Verdict: ready to merge |
Fleet review (glm)Lens: requirements + adversarial. The PR satisfies issue #372's core requirement: the #369 target check is extracted into validTargetDir and every command that consumes --target-dir (doctor, update, uninstall) now refuses relative, unclean, and empty values with exit 64 error=invalid_target before reading or writing anything. The previously destructive uninstall --target-dir "" fallback to the canonical all-roots uninstall is now refused, which is the most important safety win. The test matrix is thorough (relative existing/missing, unclean absolute naming an existing dir, empty, Windows drive-relative/rooted-without-drive/mixed-separator shapes, equals-form, target-before-catalog precedence) and the guarded uninstall dependencies plus home-unchanged assertions make a RED run safe. Docs match the new behavior. No blocking defects found; two clarifications below, one of which the author already surfaced for a maintainer ruling. Question (2)
Verdict: ready to merge |
Fleet review (glmflash)Lens: adversarial. Reviewed refactor-ia/cortex #388 (head ccf3ebf) against issue #372. The diff extracts the #369 install check into a shared validTargetDir (absolute && Clean-identical) and applies it before any read or write in doctor, update, and uninstall, closing the empty-value fallthroughs (doctor's host report on --target-dir "" and, worse, uninstall's canonical all-roots run) and making the duplicate-flag checks strictly stricter (previously --target-dir "" --target-dir x silently used the second value; the PR table documents the new invalid_command). I verified the Windows path shapes against the Go stdlib source (internal/filepathlite/path_windows.go:184): IsAbs is false for rooted-without-drive (\foo) and drive-relative (C:foo) paths and true only for volume+separator, so the test's Windows refusal cases are consistent with validTargetDir, and trailing separators, '/./' segments, parent elements, and mixed separators all fail the Clean comparison. The update dispatch on runtimeID == "" cannot misroute a runtime invocation: parseUpdateArgs rejects an empty --runtime value with --catalog and no --target-dir as invalid_command (test-pinned), and ok parse with empty runtime only comes from the targetSet branch. The new tests guard the real home via a failing resolveRoots, assert no writes (foreign sentinel, not-created missing path), and pin a valid-absolute control case against over-refusal; the check runs before doctor's probe write and before update's stat, so the misleading invalid_catalog/target_absent outcomes for relative targets can no longer occur. The only deviation from the issue's literal wording (a missing absolute target keeps doctor's presence=absent and update's reason=target_absent instead of being refused as install would) is disclosed, documented, and flagged for maintainer decision in the PR body; it creates no concrete wrong-behavior scenario, so it is recorded as a question, not blocking. No blocking findings. Suggestion (2)
Question (1)
Verdict: ready to merge |
|
The remaining blocking item from correction #390 (missing and non-directory absolute targets) needs your scope ruling. I laid out the three options, with the current behavior per command, in #390. I'll hold further pushes to this branch until then, so the ruling and the latest non-blocking suggestions land in one push. |
|
Maintainer decision (option 2): |
# Conflicts: # internal/cli/directory_lifecycle_test.go
|
Updated per the #390 decision: merged current main (kept #388's target-dir tests and #430's missing-root uninstall tests) and added 79102df.
|
Fleet review (deepseek)Lens: evidence + test design. Reviewed at head 95f1a8c, merging my earlier review of ccf3ebf and the open correction issue #390. The update half of the correction is delivered: Blocking (1)
Suggestion (2)
Verdict: not ready |
Fleet review (glm)Lens: requirements + adversarial. The diff satisfies issue #372's expected direction at head 182af6b: one shared validTargetDir check (absolute and filepath.Clean) now runs in doctor, update, and uninstall before any read or write, matching install's #369 refusal exactly (exit 64, error=invalid_target, empty stdout). Adversarial edges are covered: empty --target-dir no longer falls back to doctor's host report, update's runtime mode, or — the dangerous one — uninstall's canonical all-roots uninstall (guarded by a resolveRoots stub that fails the test if canonical roots are ever resolved); trailing-separator, unclean, and Windows drive-relative/rooted-without-drive/mixed-separator shapes are all refused; duplicate flags after an empty value are invalid_command instead of silently overriding. The earlier open questions are resolved at this head: update now refuses a missing or non-directory absolute target as invalid_target the same way install does (pinned by TestUpdateDirectoryTargetRefusesMissingOrNonDirectory, the #390 ruling), while doctor keeps its documented presence=absent report for missing absolute targets; and an explicit empty --runtime is pinned as invalid_command by a dedicated subtest, so the directory mode cannot be reached through an empty runtime ID. Control cases pin that valid absolute targets are untouched and uninstall's stricter skillroot check is preserved. No blocking findings. No findings. Verdict: ready to merge |
Fleet review (qwen38)Lens: evidence. Reviewed head 182af6b (context diff matches the GitHub files API: docs/operating.md, internal/cli/directory_lifecycle_test.go, doctor.go, install.go, update.go). Against issue #372 and correction #390 the acceptance evidence is strong and reproducible: one shared validTargetDir check now gates all four --target-dir consumers before any stat, plan, read, or write; TestDirectoryTargetCommandsRefuseRelativeTargets drives every command through runWithDependencies (the binary entry point) across relative-existing, relative-missing, unclean, trailing-separator, and empty shapes, asserting exit 64, exact stderr, empty stdout, an unchanged target listing including a foreign sentinel, unchanged home, and that no path is created; guardedUninstallDependencies makes the destructive all-roots fallthrough fail the test instead of touching a real home. The remaining #390 item was resolved by the maintainer ruling (option 2, 2026-11-12): update now refuses a missing or non-directory absolute target as invalid_target via usableTargetDir, pinned by TestUpdateDirectoryTargetRefusesMissingOrNonDirectory, while doctor deliberately keeps presence=absent, pinned by TestDoctorDirectoryTargetReportsMissingAsAbsent — the criterion's doctor half was explicitly amended, so its literal text no longer blocks this head. Residual evidence caveats (stale body section, Windows subtest results taken with #386 merged in a scratch worktree) are non-blocking. Suggestion (2)
Verdict: ready to merge |
Fleet review (mimo)Lens: requirements. At the reviewed head, issue #372's path-form criterion is fully implemented: validTargetDir (internal/cli/install.go:102) is the single shared check (absolute and filepath.Clean) run by install (through usableTargetDir), doctor (internal/cli/doctor.go:120), uninstall (internal/cli/doctor.go:730) and update (internal/cli/update.go:245) before any stat, read or write; a code sweep at head confirms install/doctor/update/uninstall are the only --target-dir consumers; the empty-value fallthroughs (doctor's host report, uninstall's canonical all-roots run, update's runtime mode) are closed by flag-presence tracking; and TestDirectoryTargetCommandsRefuseRelativeTargets pins all four commands across relative existing/missing/unclean/trailing-separator/empty and Windows shapes plus a valid-absolute control, with guarded uninstall dependencies that fail the test if canonical roots are ever resolved. The literal reading of #372's Expected direction ('a target that install would refuse as invalid_target ... refused the same way by every command that consumes --target-dir') still diverges for missing or non-directory absolute targets on doctor (exit 0 presence=absent) and uninstall (exit 3 not_installed), but that divergence is an explicit maintainer ruling rather than a silent narrowing: on correction issue #390 the maintainer chose option 2 (2026-11-12) - update refuses those targets as invalid_target, doctor keeps its presence report, uninstall keeps exit 3 from #358 - the decision was implemented in 79102df, and both behaviors are pinned by TestUpdateDirectoryTargetRefusesMissingOrNonDirectory and TestDoctorDirectoryTargetReportsMissingAsAbsent. docs/operating.md documents all three behaviors at head. One non-blocking item: the PR body's Chain Dependencies / Out of Scope section still describes the pre-ruling state (update reason=target_absent, decision 'raised in the review thread'), contradicting the code, tests and docs that merged. Note: the head moved from 95f1a8c (reviewed via context) to the merged head 182af6b during this run; the delta is only the merge of main carrying #434's qa-usage changes (operating.md, doctor.go qaUsage, qa_test.go, qasetup.go) and touches none of this PR's files or line references. No blocking findings. Suggestion (1)
Verdict: ready to merge |
Pull Request
Linked Issue
Fixes #372
PR Type
type:featureFeaturetype:bugBug fixtype:docsDocumentationtype:refactorRefactoringtype:choreMaintenancetype:breaking-changeBreaking changeSummary
installrefused a relative or unclean--target-dirasinvalid_target(#369), but the other commands that consume the flag did not run that check. Every--target-dircommand now runs the same check before it reads or writes the directory.doctor, existing dirpresence=present writability=writable ownership=unowned, and it created a.cortex-doctor-*probe file in the directoryerror=invalid_target, no writedoctor, missingpresence=absenterror=invalid_targetupdate --apply, existing direrror=invalid_catalog(misleading)error=invalid_targetupdate --apply, missingreason=target_absenterror=invalid_targetinstall,uninstallerror=invalid_targetuninstall --target-dir ""error=invalid_targetdoctor --target-dir ""error=invalid_targetupdate --target-dir "" ...error=invalid_commanderror=invalid_targetdoctor --target-dir /abs/dir/(trailing separator)error=invalid_targetupdate --target-dir /abs/dir/ ...error=invalid_catalog(misleading)error=invalid_targetdoctor/uninstall --target-dir "" --target-dir /abs/direrror=invalid_command(duplicate flag)Changes
internal/cli/install.go: extracts the Reject relative existing --target-dir as invalid_target before composition #369 check intovalidTargetDir(absolute andfilepath.Clean), andrunDirectoryInstallcalls it. Install behavior does not change.internal/cli/doctor.go:runDoctorCommandandrunDirectoryUninstallcallvalidTargetDirfirst. Uninstall already refused relative paths throughskillroot.NewUninstallRoot, which is stricter, so its behavior does not change either; it now shares the check explicitly.internal/cli/update.go:runDirectoryUpdatecallsvalidTargetDirbefore the stat, the plan, and the apply.--target-dirwas given, so an empty value (an unset shell variable) reachesvalidTargetDirinstead of falling back to the host report or the all-roots uninstall. For update,parseUpdateArgsreports the directory mode through an empty runtime ID, which the runtime mode never returns.internal/cli/directory_lifecycle_test.go:TestDirectoryTargetCommandsRefuseRelativeTargetsruns install, doctor, update (with a valid catalog and--apply), and uninstall, each against a relative existing directory, a relative missing path, an absolute but unclean path (<cwd>/./<dir>) that names the existing directory, and an empty value. On Windows it adds drive-relative, rooted-without-drive, and mixed-separator targets. It also pins install's--target-dir=<relative>form and update's target-before-catalog precedence. Every subtest checks that the home directory is unchanged, and its uninstall dependencies fail the test if the canonical roots are ever resolved, so a RED run cannot touch a real home. Each subtest asserts exit 64, stderr exactlyerror=invalid_target\n, and empty stdout. It also checks that the existing directory's listing is unchanged, including a foreign sentinel file, and that the missing path was not created.docs/operating.md: the install example used./agent-skills, which install refuses since Reject relative existing --target-dir as invalid_target before composition #369. It now uses"$PWD/agent-skills", and the doc states that every--target-dircommand refuses relative paths.Focused Tests
Run in a Linux container (
golang:1.26, LF sources, non-root uid 1000), becausemaindoes not build natively on Windows until #386 lands:main:TestDirectoryTargetCommandsRefuseRelativeTargetsfails theexisting,missing, anduncleansubtests ofdoctorandupdate, plusdoctor/empty,update/empty, anduninstall/empty, where the guard reportedcanonical uninstall roots were resolved.--target-dir=form, update's target-before-catalog precedence,update --runtime ""as a usage error, and a valid-absolute control case.TestValidTargetDirpins the check, including over-refusal.go vet ./...andgo test -count=1 ./...pass with no failures, andgofmt -lis clean for the edited files.python -m unittest tests.test_community_policy tests.test_github_policy tests.test_runtime_adapter_contractpasses. No new files, so the inventory does not change.Runtime Harness
The built
cortexbinary frommainand from this branch ran against the same targets, and I compared exit code, stdout, and stderr:rel,./rel,missing/x, and<abs>/./relgive exit 64error=invalid_targeton this branch for install, doctor, update--apply, and uninstall;maingives the "Before" outputs above. The relative directory's contents stay unchanged. Valid absolute targets (doctor present/absent, updatenot_installed, uninstallnot_installed) give identical output on both builds.diffof the two builds' outputs differs only in the three empty-value lines.doctor,uninstall, andupdate --runtime piwithout--target-dir, plus every valid absolute target, give identical exit code, stdout, and stderr.cortex.exe):rel,.\rel, drive-relativeC:rel, rooted without a drive\Users\...\rel, uncleanC:\...\.\rel, and mixed separatorsC:\...\smoke/relall give exit 64error=invalid_targetfor every command, without writes. Valid absolute targets behave as on Linux.TestDirectoryTargetCommandsRefuseRelativeTargetspasses on windows/amd64.The new test calls the commands through
runWithDependencies, the same entry point the binary uses. Absolute-path behavior stays pinned byTestDoctorDirectoryTargetStates,TestUpdateDirectoryTargetPlansAndApplies,TestUninstallDirectoryTargetRemovesOnlyOwnedFiles, andTestInstallDirectoryTargetFailClosedOnInvalidPaths.Rollback
Revert the single commit. No state, manifest, or output format changes for valid targets.
Chain Dependencies / Out of Scope
presence=absent, updatereason=target_absent), because doctor's documented job is to report presence. Whether update should also refuse it asinvalid_targetis a maintainer decision, raised in the review thread.--target-dir=<value>; extending that form to the other commands would add accepted syntax, so it is left for a separate issue.validTargetDirstays exactly as strict as the Reject relative existing --target-dir as invalid_target before composition #369 check. Uninstall'sskillrootvalidation also rejects the filesystem root and mixed separators. Applying that stricter rule to install would change install's diagnostics, so I left it out.internal/cli/install_directory_test.gois not gofmt-clean onmain(from Reject relative existing --target-dir as invalid_target before composition #369). It is untouched here.Checklist
status:approved.type:*label is applied.