Commit e957737
authored
feat(cli)!: one --refresh[=live|machine] flag for status, system and maintain (ADR-0063) (#263)
* docs(plan): add test temp-folder cleanup research to Branch 9
* docs(plan): Branch 6b code-level plan (one refresh flag)
* docs(plan): split Branch 6b into CLI (6b) and dashboard (6c); no legacy spellings
* fix(limits): ask Codex for its quota only when Codex is found
readLimits gates the app-server spawn on providers.mjs's new
recordedHostPresence('codex'), reading the host-setup evidence detectHosts
already records for every host on each /api/status poll instead of probing.
Absent, stale (>6h) or PATH-invalidated evidence reads as 'unconfirmed', never
'not-found'; either non-'found' outcome serves the last cached figure (if any)
and skips the spawn, and the Limits panel now names both new reasons instead
of falling back to its generic sentence.
* fix(limits): say "not refreshed" when Codex's quota was deliberately not requested
codexFailedShort prefixed every Codex unavailable reason with "last refresh
failed", which is false for host-not-found/host-unconfirmed: B6b-D1's
presence gate skips the app-server spawn entirely for those, so no refresh
was ever attempted. Those two reasons now render "not refreshed" instead,
alongside every other reason keeping "last refresh failed" unchanged. Also
matches the two new CODEX_WHY strings' apostrophes (’) to their
neighbours.
* feat(refresh): one --refresh flag with live and machine strengths, starting with ak status
src/lib/refresh.mjs holds the shared refresh operation (ADR-0063): the
strengths (a bare --refresh, =live, =machine), one ordered stage table,
the stage runner, the CLI stage set over one lazily built collector,
Maintenance service and management facade, and the stage renderer. The
bin rewrites a bare --refresh to --refresh= before parseArgs for commands
whose refresh option takes a value.
ak status takes the flag. --deep and --live are gone and get the parser's
generic unknown-option error. --refresh=live runs the same live checks the
old --live ran, then re-checks local evidence so the rows show them. With
--json, every human line goes to stderr while the stages run and stdout
carries one JSON object with a refresh summary. A failed stage exits 1.
The stale-result hint on remembered live checks now names
ak status --refresh=live.
humanOutputToStderr moves from sync.mjs to output.mjs unchanged.
* fix(refresh): truthful --json help, a live-stage start line, and refusals for stray arguments and half-injected services
The status help said a refresh under --json sends its stage lines to
stderr; no stage lines print there at all, only what a stage itself
writes. The live stage now announces its start with a dim line, since the
checks can run for a minute with nothing else on screen. A refresh with a
positional (ak status --refresh live) is a usage error naming the
one-token spelling instead of silently running the bare strength.
cliRefreshStages refuses a Maintenance service or facade injected without
the other, because the half it would build uses the default control root.
A test pins that the machine stages' ticker stays off stdout under --json.
* feat(refresh): ak system and ak maintain take the same --refresh flag
* docs(plan): fold the drift() failed-fetch fix into Task 9; group 6a leftovers in Branch 9
* refactor(verify): fold ak x verify into ak status --refresh=live
The live checks and slow proofs move to src/lib/live-checks.mjs. --only
names checks for --refresh=live and decides the exit code; without it the
quick checks that apply run (mcp whenever Codex is enabled) and a failure
stays a warning. Slow proofs (learning, harvest, aqe, memory-routes) run
only when named. Results are remembered as status-refresh-live; records
from the earlier sources read as an earlier live check. ak x verify is gone
and gets the generic unknown plumbing command error. The nightly deep
proofs call the new spelling.
* fix(status): with --only, only the named checks decide the exit code
Rows and failed refresh stages still print and reach --json, but with
--only the exit code is 1 only when a named check failed, was
inconclusive or did not run. ak system refuses --only: it reports no live
checks. The live-check evidence store takes its id list from the refresh
vocabulary, and a named check that does not apply says on its line that
its result is not remembered.
* feat(host): consent-gated connection check from the CLI
* fix(host): check-connection always answers --json with one JSON object
* refactor(cli): rename operations that are not refreshes
* fix(security-check): scan the project folder and say so
* fix(sync): --skip versions also skips the online version lookup
`ak sync --skip versions` (and `--skip self`, `--skip ruvnet-brain`) still
made the forced pre-plan lookup for that part, and the plan read fetched
again whenever the 24 h cache had expired. A skipped part is now never
looked up: the plan read and the converge proof get what kit.json recorded
for it, through a new `versionEvidence` on status's collect() context.
The plan-time lookups move out of sync.mjs into
src/commands/sync/plan-versions.mjs: refreshPlanVersions makes the forced
lookups for every part --skip does not name and returns cache-only evidence
for the parts it does; skippedVersionEvidence is recomputed for the converge
proof, so installed versions are read after the apply phase. The health
snapshot reads the same cache-only drift when versions is skipped. A sync
that skips nothing behaves as before (ADR-0063).
versions.mjs gains cachedOnlyLatest, a fetchLatest that never touches the
network. ruvnet-brain.mjs drift() gains cacheOnly, which reports the recorded
release with no lookup and no write; its cached read and its lookup become
two small helpers. converge takes an injectable brainDrift for tests.
* fix(sync): --dry-run previews the online version lookup without recording it
A dry run now makes the lookups a real sync makes before it plans (npm
versions for ruflo, agentic-qe and npm-managed hosts, the kit's own tags,
Ruflo's release dates, the Brain release while ak manages it) and records
nothing: driftReport, selfDrift and the Brain drift take record: false, and
the release dates land in a copy of kit.json. The results reach the plan
read through versionEvidence. Every npm view in the preview runs with its
npm cache in a per-run folder under the OS temp directory (logs off, update
notifier off), removed once the lookups are done, so ~/.npm is untouched.
When every lookup fails, the dry run prints one line saying versions were
not checked online and the plan uses the recorded ones.
A registered ruvector is read the way a real sync's plan read does, looked
up once its cache has expired, again without recording. --dry-run
--no-upgrade makes no lookup and reads every part from the cache. Before
this change, a dry run on an expired cache wrote kit.json and ~/.npm.
A failed lookup never erases a good one: when the Brain's GitHub release or
ruvector's npm lookup answers nothing, the recorded latest (and the Brain's
asset fact) stays, last is not restamped so the next call retries, and the
drift reports it as a cache fallback. This holds on every path, including
ak status --refresh. Each Brain drift helper now returns its own source.
--skip ruvector leaves ruvector's lookup out, like versions, self and
ruvnet-brain: both sync reads get the recorded ruvector drift.
The skip tests now answer newer versions for the skipped part, so a leaked
lookup changes that part's kit.json record and fails the byte comparison.
* fix(versions): a failed lookup keeps the cached version and waits one TTL window before retrying
When the Brain's GitHub release or ruvector's npm lookup answers nothing,
the recorded latest (and the Brain's asset fact, install record and held
refresh) stays in kit.json and is reported as a cache fallback, and last is
restamped. An offline ak status or dashboard poll therefore waits on these
lookups once per TTL window instead of on every read, and ak status
--refresh still forces a retry. A new observedAt field keeps when the
recorded latest was actually seen, carried forward across failures, so the
Brain row's observation label never names the failed attempt. record: false
and cacheOnly still write nothing.
The dry-run preview makes its temporary npm cache folder only when an npm
lookup runs, and a failed removal of that folder no longer fails the
preview. Its offline line gives the age of the parts it could not check,
or the oldest of them when they differ. The sync command tests' dry runs
now answer every version lookup themselves, so a machine with npm in
/usr/bin never queries the real registry from the suite.
* docs(plan): fold 6c into 6b, merge Branches 7 and 8, trim Branch 9 (maintainer restructure)
* refactor(maintain): remove recipe refresh until a registry exists
No registry is configured for any installation, so ak maintain recipes
refresh, the ADR-0048 v2 route, the facade method, and the
recipeRegistry/fetchImpl service options that existed only for it can
never succeed. Remove every user-reachable path; the recipe store's
verified-staging function (allowlist/HTTPS/redirect/signature checks,
maintenance/management/recipes.mjs) and its own tests stay as the
library a future registry calls.
The retired recipes refresh subverb gets the parser's generic
unknown-subverb error, and the retired v2 route answers 405 exactly
like any other unknown method or route.
* fix(maintain): refuse --only and --project-trees without --refresh; never build a default service beside injected stages
* fix(versions): a total lookup failure keeps the cached versions and waits one TTL window
driftReport and selfDrift now follow the rule the Brain and ruvector lookups
already use. When every npm lookup fails, the recorded versions (seen, and the
kit's best candidate) stay in kit.json and last is restamped, so an offline
ak status, dashboard poll or drift nudge waits on the registry once per TTL
window instead of on every read. force (a real ak sync, ak status --refresh)
still retries. observedAt keeps when the recorded versions were actually seen
(per package for driftReport, and now for selfDrift too), taken from the
previous last for a record written before it existed, so the dry-run offline
line still gives the age of the recorded versions. A partial kit lookup whose
cached candidate still wins renews nothing, as before.
record: false never writes. The reads for a part --skip names now pass
cacheOnly (no lookup, no write) instead of a lookup that always fails, which
the new rule would otherwise have restamped; cachedOnlyLatest is removed.
Two tests that read kit.json after a failed forced lookup now compare only
what they meant to: the versions part of the record, and the config the live
checks saw.
* fix(sync): a host probe that fails before planning is reported and the sync goes on
Before it reads the plan, a real ak sync re-checks each enabled host and the
shared host setup. A probe that threw used to end the whole sync. Each host's
probe and the host-setup probe are now guarded: a warning names the host (or
host setup) and the error, and the plan reads that host as ak last recorded
it. kit.json itself is still read unguarded, so an unreadable config keeps its
own error and recovery text.
* test(sync): the real sync tests answer every version lookup themselves
The non-dry sync tests inside withOpencodeCli put /usr/bin on PATH, so on a
machine with /usr/bin/npm their forced version lookups would have queried the
real npm registry. realSync now injects offline lookups, as the dry-run helper
already did, and so does the child-process helper the --json tests use.
* fix(cli): --json failures stay JSON and keep their recovery text; --skip lines print once
A --json invocation now always gets exactly one JSON object on stdout, with
every human line (the message, the help, the recovery commands) on stderr.
- An option the parser rejects answers { error, exitCode: 2 }; ak sync answers
its own empty result shape with error (jsonUsageError). A retired spelling
gets the parser's generic unknown-option text, with no hint.
- The usage errors ak status, ak system and ak maintain raise themselves (a
bad --refresh value, a stray strength argument, --only or --project-trees
misuse, an unknown maintain verb) answer { error, exitCode: 2 }, each
through its command's one usage-error path.
- An unreadable kit.json answers { error, exitCode: 1, recovery } from the
bin, and ak sync --json adds recovery to its result. configErrorRecovery in
config.mjs builds the same mv or Move-Item commands the human output prints.
Without --json the output is unchanged. ak sync no longer repeats a skipped
item's "skipped by request" line in the verdict when the plan announcement
already printed it; the JSON skipped list is unchanged. The help for sync,
status, system and maintain, and the sync --json notes in UPGRADING and
INSTALLATION, describe the JSON failure answers.
* fix(versions): no false "recorded just now", no offline rewrite loop
A failed lookup restamps last even when nothing was ever recorded, so the
dry-run offline line fell back to that last and said "this plan uses the
versions ak recorded just now" (or "2d ago" for an expired ruvector record)
where it should say ak has recorded none. Each part's last now stands for an
observation only when its record holds a latest: seen entries, the kit's
best, the Brain's or ruvector's latest.
A stable install whose recorded kit candidate came from the next channel can
never use it, so its record is never fresh and every offline lookup restamped
and rewrote kit.json. That failure now writes nothing, as before the restamp
rule. Dropping the candidate instead would make an empty record look fresh
and stop the lookup for a TTL window once the registry is back, so ak status
could not show a real latest.
* fix(cli): config errors keep their recovery under maintain --json; --json after -- is positional
ak maintain's catch-all turned every thrown error into a usage refusal, so
an unreadable kit.json under a verb that reads it (discovery, sources add)
answered { error, exitCode: 2 } with no recovery. A config error now reaches
the CLI's fatal handler: { error, exitCode: 1, recovery } under --json, the
recovery commands otherwise, as for every other command.
A rejected option answered in JSON when --json appeared only after the --
terminator, where it is a positional. Only the tokens before -- now decide.
* fix(cli): honor ak host --dry-run; the bare ak hint counts rows without a fix
* fix(cli): maintain --only usage error names ak maintain report, not ak status
* docs(plan): close Branch 6b CLI-only; the dashboard half moves to the next program
* docs: align help, README and docs with the CLI refresh vocabulary
Add tests/kit/refresh-vocabulary-guard.test.mjs (R16-R18) to guard the CLI
vocabulary and rewrite every retired spelling it finds (ak x verify, ak
status --live/--deep, ak system --deep, ak maintain scan, --refresh-inventory,
ak host refresh, ak usage prompts --deep, ak maintain recipes refresh) to its
current CLI spelling across README, docs/, claude/ and src/ comments, with no
old -> new tables. Dashboard docs keep today's control names and state the
new CLI equivalents beside them, as this branch closes CLI-only. Corrects two
stale DASHBOARD.md claims (Maintenance does reload on the shared poll; the
quoted re-measure status text does not exist) and documents the two new
"not refreshed" Codex reasons. Replaces the ledger-only B6b-D1 / Branch 6b
Task 1 labels in source/test comments with ADR-0010, and drops the dangling
(R17) suffix from three test assertion messages.
* docs: state recipe refresh and live-check source facts without history narration
Recipe trust and the live-check evidence glossary row named what used to
exist instead of only the current state (R17); AQE-EMBEDDINGS.md named the
wrong live check for the unmanaged-backend note (the full aqe proof, not
aqe-embedding); the troubleshooting "run everything" example named memory
redundantly alongside memory-routes, which already covers it.
* fix(host): clean up dry-run test fixtures on failure and disclose what pick --dry-run previews
* docs: describe the memory and memory-routes checks exactly, and catch retired verbs in listed alternatives
* docs: name the memory check by id where it describes the quick round trip
* docs(adr): record the CLI refresh vocabulary Branch 6b delivered (ADR-0063)
* docs(adr): tighten the --only exit-code wording in ADR-0063 to match namedCheckFailed
* fix(maintain): refuse --only for every verb; share one refresh collector
ak maintain --refresh=live --only <check> accepted the flag and ran the
live checks, but always exited 0 regardless of a failed named check: unlike
ak status, ak maintain never renders live-check results or reports their
verdict, so the flag was silently accepted and then ignored (final review
I-1). --only is now refused with exit 2 for every verb, including the
default report, mirroring ak system's own refusal.
refreshedReport also built two unrelated collector instances for one
--refresh: defaultStages built its own for the machine/inventory stages and
the management facade, while createMaintenanceService() (no args) built a
second, cold one for the maintenance stage and the later service.report()
read (m-2). The collector is now built once in refreshedReport and threaded
through both.
renderReport printed only the scan-required finding's generic label ("Run
Maintenance scan") on an unmeasured machine, naming no command — a new
user's first hint invited the retired ak maintain scan verb. It now also
prints the finding's own nextAction.steps[0], the exact refresh command
(m-3).
* fix(cli): skip the drift nudge after a run-level usage refusal
The post-command drift nudge ran driftReport() (which shells npm view)
whenever mod.run() returned, without checking its exit code. A run-level
usage refusal a command raises itself — e.g. a rejected
ak status --refresh=bogus — reached the nudge exactly like a successful
run and spent a network call a parse error never did (final review m-1).
The nudge (and the local-artifact-drift check beside it) is now also
skipped when the command's own exit code is 2.
* fix(system): describe what a bare/live refresh actually does in --help
ak system --help said a bare --refresh (or --refresh=live) "refreshes
Maintenance evidence and the inventory, then reprints this same snapshot",
omitting that it also re-checks local status (including the online version
lookups) and that =live additionally runs the live checks (final review
m-6).
* test(refresh-vocabulary-guard): catch retired flags behind bracketed options
The guard's adjacency regexes required a retired flag directly after the
command word, so a bracketed usage line (system [--deep] [--json], status
[--json] [--live]) and ak usage prompts --deep behind unrelated prose slipped
through undetected — exactly how four current-state docs kept the retired
spellings past the original guard (final review I-2). Two new patterns
tolerate intervening single-line bracketed options for status/system, and
scan the whole line for prompts ... --deep.
Also drops the ledger/plan task label from the file header in favor of an
ADR-0063 reference (final review m-8).
* docs: fix four current-state docs still showing retired spellings
machine-footprint.md and ubiquitous-language.md still showed
ak system [--deep] [--json]; ubiquitous-language.md also claimed ak system
and ak about are "both read-only twins" of their dashboard areas, which is
no longer true of ak system --refresh. USAGE-SCORECARD-METRICS.md and
prompt-telemetry.md still named --deep as ak usage prompts's text-bearing
flag; the current flag is --show-text (final review I-2).
* docs(adr): ak status never reads Codex quota; replace ledger-only ids
ADR-0010 and ADR-0063 said /api/limits and ak status both go through
readLimits to ask codex app-server for its quota. readLimits has exactly
one caller, dashboard-server.mjs's /api/limits handler; no status section
imports quota.mjs. Both ADRs now say /api/limits only (final review I-3).
Both ADRs also cited ledger-only ids (B6b-D1, B6b-D3, M-11) that live only
in the gitignored SDD ledger. They now point at the committed branch 6b
plan's named rulings (R9, R17) or its "Closing this branch" section instead
(final review I-3).
Also, ADR-0063's --only bullet read as spanning all three refresh commands;
it now says --only is ak status's alone, and that ak system/ak maintain
both refuse it outright (paired with the ak maintain code fix). Three
precision nits from final review m-5: setup.mjs's collectIntegrationFacts
call passes no cwd; selfRecord restamps on a total failure with no cached
candidate at all (only a partial answer, or an unusable cached candidate,
saves nothing); and ak sync --dry-run's online lookups redirect to, and
clean up, a temporary npm cache under the OS temp folder rather than
writing "nothing at all".
* docs: add check-connection to the host verb row in README
README.md's compact command table listed ak host status|pick|reset-routes|off,
omitting check-connection even though the usage block and ak --help --all
both list it (final review m-7).
* fix(maintain): keep the injected refreshStages path free of a real collector
The m-2 fix built refreshedReport's shared collector unconditionally, even
when deps.refreshStages was injected — a fully-injected test (refreshStages
+ service) used to trigger zero real collector or facade construction
(defaultStages was bypassed entirely); it now dynamically imported
footprint/index.mjs and constructed a real createSystemCollector() for
nothing, contradicting the doc comment two lines above and the plan's
hermeticity rule.
Collector construction is now skipped outright when deps.refreshStages is
injected: the half-injection guard already guarantees deps.service is
present in that case, so nothing downstream would have read it anyway.
Also adds the one test the review's own wording implies but the diff didn't
yet prove: --refresh=live WITHOUT --only stays valid (runs the live stage,
reads service.report(), exits 0).1 parent 0fa5e48 commit e957737
124 files changed
Lines changed: 7658 additions & 1570 deletions
File tree
- .github/workflows
- bin
- claude
- docs
- adr
- audits
- ddd
- superpowers/plans
- src
- commands
- status/sections
- sync
- usage
- x
- lib
- dashboard
- client
- hook-audit
- maintenance
- management
- tests
- kit
- live
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
89 | | - | |
| 89 | + | |
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
45 | | - | |
46 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| |||
89 | 90 | | |
90 | 91 | | |
91 | 92 | | |
92 | | - | |
| 93 | + | |
93 | 94 | | |
94 | 95 | | |
95 | 96 | | |
| |||
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
101 | | - | |
102 | | - | |
103 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
104 | 106 | | |
105 | 107 | | |
106 | 108 | | |
107 | 109 | | |
108 | 110 | | |
109 | | - | |
| 111 | + | |
110 | 112 | | |
111 | 113 | | |
112 | 114 | | |
| |||
150 | 152 | | |
151 | 153 | | |
152 | 154 | | |
153 | | - | |
154 | | - | |
| 155 | + | |
| 156 | + | |
155 | 157 | | |
156 | | - | |
| 158 | + | |
157 | 159 | | |
158 | 160 | | |
159 | 161 | | |
160 | 162 | | |
161 | 163 | | |
162 | | - | |
163 | | - | |
164 | | - | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
165 | 169 | | |
166 | 170 | | |
167 | 171 | | |
168 | 172 | | |
169 | 173 | | |
170 | 174 | | |
171 | 175 | | |
172 | | - | |
| 176 | + | |
| 177 | + | |
173 | 178 | | |
174 | 179 | | |
175 | 180 | | |
| |||
0 commit comments