@yunhozz/arc 0.2.1 → 0.2.2

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.
Files changed (46) hide show
  1. package/MANIFEST.sha256 +44 -43
  2. package/README.md +5 -1
  3. package/claude/agents/arc-implementer.md +7 -7
  4. package/claude/agents/arc-planner.md +9 -11
  5. package/claude/agents/arc-qa.md +7 -7
  6. package/claude/agents/arc-reviewer.md +6 -6
  7. package/claude/agents/arc-scouter.md +6 -6
  8. package/claude/skill/SKILL.md +15 -16
  9. package/claude/skill/references/commit.md +1 -1
  10. package/claude/skill/references/coordination.md +20 -36
  11. package/claude/skill/references/handoff-prep.md +3 -3
  12. package/claude/skill/references/handoff.md +10 -9
  13. package/claude/skill/references/limits.md +3 -4
  14. package/claude/skill/references/planning.md +50 -48
  15. package/claude/skill/references/verification.md +7 -7
  16. package/claude/skill/references/visual-companion.md +2 -2
  17. package/codex/agents/arc-implementer.toml +6 -6
  18. package/codex/agents/arc-planner.toml +10 -12
  19. package/codex/agents/arc-qa.toml +7 -7
  20. package/codex/agents/arc-reviewer.toml +6 -6
  21. package/codex/agents/arc-scouter.toml +6 -6
  22. package/codex/skill/SKILL.md +15 -19
  23. package/codex/skill/references/commit.md +4 -6
  24. package/codex/skill/references/coordination.md +23 -42
  25. package/codex/skill/references/git-recovery.md +2 -3
  26. package/codex/skill/references/handoff-prep.md +4 -4
  27. package/codex/skill/references/handoff-worktree.md +5 -9
  28. package/codex/skill/references/handoff.md +15 -18
  29. package/codex/skill/references/limits.md +3 -4
  30. package/codex/skill/references/planning.md +53 -52
  31. package/codex/skill/references/verification.md +12 -14
  32. package/codex/skill/references/visual-companion.md +3 -3
  33. package/lib/installer/manifest.mjs +1 -1
  34. package/lib/management/operations.mjs +15 -2
  35. package/lib/management/update.mjs +19 -10
  36. package/lib/npm/context.mjs +22 -0
  37. package/lib/npm/uninstall.mjs +17 -0
  38. package/lib/npm/upgrade.mjs +25 -0
  39. package/lib/policy/role-policy.json +9 -9
  40. package/lib/tui/app.mjs +32 -63
  41. package/lib/tui/render.mjs +10 -12
  42. package/lib/tui/setup.mjs +11 -9
  43. package/lib/tui/terminal.mjs +8 -8
  44. package/package.json +1 -1
  45. package/scripts/update-superpowers-local.sh +4 -0
  46. package/lib/tui/workflow.mjs +0 -12
@@ -4,28 +4,23 @@ Read before design/plan-depth decisions, spec/plan drafting and approval, and th
4
4
 
5
5
  ## Authorization and scope
6
6
 
7
- An explicit execution request is authorization only for its sufficiently specified scope. Confirm execution intent,
7
+ An explicit execution request authorizes only its sufficiently specified scope. Confirm execution intent,
8
8
  expected outcome, constraints, document depth, and verification risk, with no unresolved material behavior, method,
9
- or risk decision requiring the user. The request itself or a description explicitly referenced by the user must
10
- supply that scope. Record authorization evidence, scope, acceptance criteria, constraints, and depth in the brief or
11
- canonical record. Routine execution and check choices may be recorded by Orchestrator; added product behavior or
12
- material scope cannot be claimed as already approved. A restatement, role handoff, or restart does not create an
13
- approval requirement for the same scope.
9
+ or risk decision requiring the user. That scope must come from the request or a description the user explicitly references. Record authorization evidence, scope, acceptance criteria, constraints, and depth in the brief or
10
+ canonical record. Orchestrator may record routine execution and check choices; added product behavior or material scope is not already approved. Restating or handing off the same scope, or restarting, requires no new approval.
14
11
 
15
12
  Silence, ambiguous assent, approval of other work, and unspecified blanket improvement requests are not authorization.
16
13
  Planner's new material choices are not automatically approved. Ask for the missing material decision; roles route
17
14
  questions to Orchestrator. Required written specs and plans still require their individual approval in the order below.
18
15
 
19
- For material changes to scope, method, or risk, hold affected work and explain the missing decision or evidence and
20
- remaining work. Target changes, unclear ownership, unexpected state, unresolved failures, or missing required inputs
16
+ Material scope, method, or risk changes hold affected work; explain the missing decision or evidence and remaining work. Target changes, unclear ownership, unexpected state, unresolved failures, or missing required inputs
21
17
  also hold affected work under [handoff](handoff.md) and [git-recovery](git-recovery.md). Distinguish new authorization
22
18
  needs from execution issues within existing scope: ordinary corrections and reverification do not require duplicate
23
19
  approval after failure. Revalidate affected gates after resolution.
24
20
 
25
21
  ### Probe scope
26
22
 
27
- A probe-only scope completes with findings, evidence, limits, and remaining uncertainty answering the approved question;
28
- a well-supported infeasibility result can complete it. Probe-only authorization does not authorize product retention
23
+ A probe-only scope completes by answering the approved question with findings, evidence, limits, and remaining uncertainty; well-supported infeasibility can complete it. Probe-only authorization does not authorize product retention
29
24
  or subsequent implementation. Record question, method/scope, criteria, and constraints; a sufficiently specific probe
30
25
  request can supply that approval. Match checks to probe purpose/risk and do not report probe evidence as product proof.
31
26
 
@@ -38,41 +33,53 @@ remains separate.
38
33
 
39
34
  ## Formal drafting boundary
40
35
 
41
- Initial design proposals and brainstorming may precede execution-target choice. Formal spec or plan drafting includes a
42
- complete draft before saving, even when returned only in chat. Before formal drafting and saving, Orchestrator supplies
36
+ Initial design proposals and brainstorming may precede execution-target choice. Formal spec or plan drafting includes a complete draft before saving, even when returned only in chat. Before formal drafting and saving, Orchestrator supplies
43
37
  and records the actual absolute workspace/repository, branch or detached HEAD, HEAD, changes and ownership,
44
38
  explicit choice and preparation evidence, and absolute document paths inside that target. Planner drafts for that
45
39
  verified workspace; Orchestrator saves directly in the selected target. Missing or mismatched evidence stops the
46
- affected drafting or save and is reported to Orchestrator. Planned scope and paths do not establish actual preparation.
40
+ affected drafting or save and is reported to Orchestrator. Planned scope and paths do not prove actual preparation.
47
41
  Initial target preparation does not authorize Implementer; the complete handoff gate still applies.
48
42
 
49
43
 
50
44
  ## Planner drafting preparation and consolidated feedback
51
45
 
52
- Before formal drafting, Orchestrator performs a preflight of relevant policy, reference, profile, test, parity and
53
- manifest surfaces and conflicts. Provide one complete brief with actual absolute workspace/repository, branch or
54
- detached HEAD, HEAD, change state and ownership, explicit target choice and preparation evidence, approved preceding
55
- design/spec/plan versions, scope and exclusions, design/plan depth, dependencies and shared contracts, acceptance
56
- criteria, required QA/focused checks and evidence, hold conditions and open decisions. Resolve missing or conflicting
57
- required evidence first or hold that stage.
58
-
59
- Planner returns a complete proportional draft and self-review for the current approval stage. Orchestrator integrates,
60
- saves, reviews and obtains approval. Orchestrator checks once with a fixed checklist: scope and evidence; targets,
61
- ownership and paths; policy surfaces and conflicts; task boundaries, dependencies and acceptance criteria; checks,
62
- evidence and holds; roles, approval and commit boundaries; draft completeness. Gather all substantive omissions into
63
- one consolidated follow-up request to Planner. Do not split requests by omission or author the missing plan yourself.
64
-
65
- Initial design, required spec approval, and formal plan drafting/approval remain separate ordered stages.
66
- Allow additional Planner calls for substantive omissions or user-approved scope/evidence changes. There is no fixed
67
- call cap; never accept an incomplete draft to minimize calls. Do not add call-count conditions to Planner profiles.
68
-
69
- The complete initial brief identifies original user requirements, criteria IDs, approved decisions and open decisions;
70
- current behavior and connected source, generated, runtime, installer and documentation surfaces; conflicting rules and
71
- resolution direction; exact current-stage deliverable and absolute output path. Keep the shared brief's fixed field order
72
- in [coordination](coordination.md). Link accessible evidence and needed ranges instead of full dialogue or raw logs.
73
- Consolidated feedback contains changed fields only and marks remaining fields `unchanged` relative to the received brief.
74
- Do not call Planner again for already resolved questions. Missing target preparation or required decisions holds formal
75
- drafting; initial design proposals may still precede target selection. Written-spec approval precedes dependent plan drafting.
46
+ 1. Before formal drafting, Orchestrator performs a preflight of relevant policy, reference, profile, test, parity
47
+ and manifest surfaces and conflicts. Apply the Formal drafting boundary above and coordination's instruction
48
+ provenance, input-state and local-skill rules, including required reference paths, authorized bounded read
49
+ alternatives and return conditions. Missing required inputs or unresolved conflicts hold dependent drafting;
50
+ Planner returns them to Orchestrator. Optional read recovery grants no preparation, verification, writing or
51
+ approval authority.
52
+
53
+ 2. Maintain a compact ledger of original requirements/criteria IDs, approved and open decisions, exclusions and
54
+ scope deltas; merge intervening user changes into one next-brief delta. Distinguish user-approved decisions,
55
+ verified source facts with exact paths and lines, assumptions and open decisions.
56
+
57
+ 3. Send one complete brief for the initial call of each stage, using coordination's shared fields and evidence-link
58
+ rules. Include the prepared workspace, branch or detached HEAD, HEAD and ownership; scope, exclusions, depth,
59
+ dependencies, shared contracts, acceptance criteria, required QA/focused checks, evidence and holds. Identify
60
+ preceding approved versions, document/output paths, current behavior and connected source, generated, runtime,
61
+ installer and documentation surfaces, policy conflicts and resolution direction, open decisions and the exact
62
+ stage deliverable. For visual work, include approved sources/versions, criteria and unresolved interpretations.
63
+
64
+ 4. Planner returns a complete proportional draft and self-review. Orchestrator integrates, saves, reviews and
65
+ obtains approval; never authors missing content. Review the complete draft once with this fixed checklist:
66
+ scope and evidence; targets, ownership and paths; policy surfaces and conflicts; task boundaries, dependencies
67
+ and acceptance criteria; checks, evidence and holds; roles, approval and commit boundaries; draft completeness.
68
+ Finish the checklist before sending all substantive omissions in one consolidated follow-up request.
69
+ Identify each criterion, defect, evidence and expected correction. Follow coordination's changed-fields-only
70
+ and `unchanged` rules. Do not split requests by omission.
71
+
72
+ 5. After that correction, additional Planner calls require user-approved material scope/evidence changes or
73
+ concrete unmet checklist items, plus meaningful new input or criterion-state progress. Identify changed input,
74
+ target criterion, expected artifact and expected progress. Distinguish approved scope changes from ordinary
75
+ omissions within existing scope; omissions require no renewed scope approval. If the same criterion remains
76
+ unmet without meaningful new input, evidence or progress, hold the stage and report the criterion, observed
77
+ evidence and exact unblock input or decision. Do not repeat resolved questions, reword the same request, or
78
+ count rewording or agent replacement as meaningful input or progress.
79
+
80
+ Initial design, required spec approval, and formal plan drafting/approval remain separate ordered stages;
81
+ written-spec approval precedes dependent plan drafting. No fixed call cap applies; never accept an incomplete draft
82
+ to reduce calls or add call-count conditions to Planner profiles.
76
83
 
77
84
  ## Design and planning
78
85
 
@@ -127,7 +134,7 @@ approved obligations separately.
127
134
  When a Bounded written spec is required by Orchestrator's recorded plan-depth judgment, record that necessity and scope
128
135
  and use the approved short design as its basis. Planner returns a proportional complete spec draft and self-review;
129
136
  Orchestrator integrates, saves, self-reviews, and obtains individual written-spec approval before dependent plan
130
- drafting and execution preparation. This applies that judgment, not a new spec-necessity rule.
137
+ drafting and execution preparation. This applies that judgment without adding a spec-necessity rule.
131
138
 
132
139
  Design, spec, plan, and brainstorming drafts are authored only by Planner.
133
140
  Orchestrator must not substitute an independently authored spec or plan for Planner's complete draft.
@@ -200,11 +207,9 @@ If evidence is missing or mismatched in either gate, stop the affected save and
200
207
 
201
208
  ### Complete planning-document set
202
209
 
203
- The applicable document set means all specs and plans required for the current approved execution scope, not future
204
- project work. Orchestrator records scope, design/plan depth, absolute document paths and coverage, approved
210
+ The applicable document set covers all specs and plans required for the current approved execution scope, excluding future project work. Orchestrator records scope, design/plan depth, absolute document paths and coverage, approved
205
211
  versions/status, plan dependencies and execution order, skipped stages with rationale, and documents still awaiting writing or approval
206
- in the shared brief. The set is complete only when every required document is written and individually approved and
207
- no unresolved scope or dependency decision requires another document. Do not omit required documents from the list.
212
+ in the shared brief. The set is complete only when every required document is written and individually approved, with no unresolved scope or dependency decision requiring another document. Do not omit required documents from the list.
208
213
 
209
214
  A required plan does not itself require a written spec. Bounded uses Orchestrator's recorded plan-depth judgment to
210
215
  determine whether a written spec is required; do not infer one from plan necessity. Spec steps apply only to Architectural and
@@ -219,9 +224,7 @@ Written-spec approval precedes dependent plan drafting. On document-omission pat
219
224
  steps; preserve target choice, preparation, state/ownership checks and Implementer gates.
220
225
 
221
226
  While documents are being written, reviewed, or approved, do not stage documents or commit planning documents.
222
- Selection and initial target preparation happen before formal drafting; document completion is a staging/commit gate,
223
- not an initial preparation gate. An incomplete set or missing applicable approval blocks document commit and Implementer,
224
- not the initial choice request. No early Implementer work is authorized by approval of only some documents.
227
+ Select and initially prepare the target before formal drafting. Document completion gates staging/commit, not initial preparation. An incomplete set or missing applicable approval blocks document commit and Implementer, but not the initial choice request. No early Implementer work is authorized by approval of only some documents.
225
228
 
226
229
  This ARC explicitly defers the local `brainstorming` instruction to commit the spec before written-spec review:
227
230
  save and self-review the spec, obtain written-spec approval without committing it, then write and obtain approval for
@@ -254,8 +257,7 @@ lifetime and restart record in [handoff.md](handoff.md).
254
257
  ## Plan updates during execution
255
258
 
256
259
  Mechanical execution records may be recorded without new user approval: checkbox completion, actual results, evidence
257
- links and task completion state that preserve the approved execution contract. Record their classification, reason and
258
- actual recording time in the canonical handoff record. Required checks' removal, invented success, unsupported acceptance
260
+ links and task completion state that preserve the approved execution contract. Record their classification, reason and actual recording time in the canonical handoff record. Required checks' removal, invented success, unsupported acceptance
259
261
  or scope changes mixed with a completion mark are not mechanical records.
260
262
 
261
263
  Changes to goals, task scope or boundaries, acceptance criteria, method, risk, dependencies or required verification
@@ -265,8 +267,7 @@ follow the document-version approval rules; classify typos, paths, commands and
265
267
 
266
268
  Both mechanical records and approved contract revisions amend the original planning-document commit at a safe checkpoint.
267
269
  Make no separate document commit or fixup commit, including for newly approved scope. Preserve implementation task commits.
268
- This history update does not make actual approval time retroactive: the latest plan in the original commit is not evidence
269
- that its contents were approved from the outset. Never use rewritten history to justify work started before approval.
270
+ This history update does not backdate approval: the latest plan in the original commit does not prove its contents were approved from the outset. Never use rewritten history to justify work started before approval.
270
271
 
271
272
  In the canonical handoff record preserve plan absolute/Git paths; before and after content hashes and versions; prior and
272
273
  revised approval references and actual times; change reason and classification; affected tasks and hold/preparation/resume
@@ -313,11 +314,11 @@ Give the Planner the exact paths and access state for required skills and refere
313
314
  [ARC role policy: contract begin]
314
315
  Generated from lib/policy/role-policy.json; do not edit this managed section manually.
315
316
  - [git.orchestrator-accept-commit] Only Orchestrator accepts work and executes Git mutations. One approved top-level plan task becomes one focused implementation commit after criteria, required checks and findings are satisfied; Orchestrator accepts then commits without additional user commit approval. Reports, passing checks and Reviewer opinions do not replace acceptance. Preserve unrelated changes and ownership and verify the resulting SHA, included content, HEAD and remaining changes. Existing small-task direct and plan-omission paths are unchanged.
316
- - [review.significant-no-plan] For significant/high-risk work without a plan: Implementer → QA and required QA rechecks → Reviewer of the final full change → Orchestrator acceptance. MUST NOT omit Reviewer merely because no plan exists. This creates no new plan obligation.
317
- - [review.significant-plan] For significant/high-risk plan work: each task uses Implementer → fresh QA as required → Orchestrator acceptance and focused commit. After all tasks are accepted and required QA is complete, Reviewer reviews the final full change once. Reviewer is NOT a per-task or per-commit required gate. Preserve required checks and necessary finding rechecks.
317
+ - [review.significant-no-plan] For significant/high-risk work without a plan: Implementer → QA and required QA rechecks → Reviewer of the final full change → Orchestrator acceptance. MUST NOT omit Reviewer because no plan exists. This creates no new plan obligation.
318
+ - [review.significant-plan] For significant/high-risk plan work: each task uses Implementer → fresh QA as required → Orchestrator acceptance and focused commit. Reviewer reviews the final full change once after all tasks are accepted and required QA is complete. Reviewer is NOT a per-task or per-commit required gate. Preserve required checks and necessary finding rechecks.
318
319
  - [review.final-plan-recheck] For final overall Reviewer findings on plan work, including behavior findings, use Implementer → Reviewer without QA reassignment; repeat until resolved. This exception applies only at the final plan stage. Preserve required QA and QA failure rechecks before initial final review and outside this exception.
319
320
  - [planning.plan-amend] During approved task execution, mechanical plan progress records need no new user approval; changed goals, scope, criteria, method, risk, dependencies or required verification require revised-version approval and preparation before affected work. In both cases Orchestrator amends the original planning-document commit at a safe boundary, without a separate document or fixup commit. Preserve actual approval chronology, recoverable old/new contents, hashes, versions and SHA mappings; never retroactively authorize earlier work. Rewrite only unpublished, unshared local linear history with clear ownership and no merges; stop on unknown publication, ownership, unexpected state or replay conflicts, without force push or a fallback document commit. Preserve implementation task boundaries and revalidate affected evidence. See planning, handoff-prep, commit and verification for the full contract.
320
- - [planning.planner-brief] Before formal drafting, Orchestrator supplies one complete stage-specific brief with original requirements and criteria IDs, approved/open decisions, verified target and ownership, document paths and preceding approved versions, current behavior and connected source/generated/runtime/installer/documentation surfaces, policy conflicts, depth, dependencies, checks, holds and exact deliverable. Preserve shared brief field order. Review the complete draft once and consolidate substantive feedback as changed fields with other fields marked unchanged. Preserve sequential design, spec and plan approvals; no fixed call cap or incomplete drafts, and no calls for already resolved questions. Include instruction provenance and input states, required skill/reference paths, bounded read alternatives and return conditions, approved visual sources/versions and criteria, and unresolved interpretation decisions before dependent drafting. Planner returns incomplete required inputs to Orchestrator; optional reading recovery never grants preparation, verification, writing or approval authority.
321
+ - [planning.planner-brief] Follow planning.md's Planner drafting preparation and consolidated feedback procedure, including preflight, ledger, evidence classification, target preparation, preceding approvals, provenance and reference gates. Send one complete stage-specific initial brief. Planner returns a complete proportional draft and self-review; Orchestrator reviews once with the fixed checklist and sends one consolidated correction using changed fields only. Additional Planner calls require user-approved material scope/evidence changes or concrete unmet checklist items after correction, plus meaningful new input or criterion-state progress; state changed input, target criterion, expected artifact and expected progress. Hold repeated unmet criteria without new input, evidence or progress and report the criterion, observed evidence and exact unblock input or decision. Do not split omissions, repeat resolved items, or count rewording or agent replacement as progress. Preserve separate sequential design/spec/plan approvals; no fixed call cap, Planner-profile call-count conditions or incomplete draft acceptance.
321
322
  - [platform.codex] Role restrictions are prompt-level contracts, not a filesystem sandbox. No documented role-specific Git hook or generic per-agent tool allowlist is assumed. Existing shell capabilities are retained because static read access after shell reduction has not been verified; Implementer and QA need shell for approved checks.
322
323
  [ARC role policy: contract end]
323
324
 
@@ -9,9 +9,9 @@ For Orchestrator, **Evidence verdict**: each criterion is proven, unmet, or unve
9
9
 
10
10
  ## Visual Evidence and Adjudication
11
11
 
12
- Each visual criterion requires the exact location, version, approval, and access state of the approved visual source; criterion IDs, properties, and comparison method; and evidence linking the actual output to its revision. An Implementer report does not replace fresh QA's direct comparison. Record results as pass, mismatch, or unverified and connect them to the existing proven/unmet/unverified criterion verdicts. Use pass only when accessible evidence meets the visual criterion. Keep unsupported claims unverified. Missing output-to-revision linkage is unverified; a visible difference is a mismatch. Treat these as separate observations and do not infer the current branch UI state. Hold the comparison if the approved source or actual output is inaccessible or interpretation approval is missing.
12
+ Each visual criterion requires the exact location, version, approval, and access state of the approved visual source; criterion IDs, properties, and comparison method; and evidence linking the actual output to its revision. An Implementer report does not replace fresh QA's direct comparison. Record results as pass, mismatch, or unverified and connect them to the existing proven/unmet/unverified criterion verdicts. Use pass only when accessible evidence meets the visual criterion; keep unsupported claims unverified. Missing output-to-revision linkage is unverified; a visible difference is a mismatch. Record these separately; do not infer the current branch UI state. Hold the comparison if the approved source or actual output is inaccessible or interpretation approval is missing.
13
13
 
14
- Static policy, generated-file, and manifest checks are structural evidence. They do not prove actual role behavior, an installed build, Claude Code CLI runtime compliance, or visual fidelity. Reports include actual changes and exclusion rationale, checks run and omitted, evidence and limits by criterion, and platform availability. Preserve existing baseline failures separately; progress on independent work does not relax required checks or acceptance criteria.
14
+ Static policy, generated-file, and manifest checks are structural evidence. They do not prove actual role behavior, an installed build, Claude Code CLI runtime compliance, or visual fidelity. Reports include actual changes, exclusion rationale, checks run and omitted, each criterion's evidence and limits, and platform availability. Keep baseline failures separate; progress on independent work does not relax required checks or acceptance criteria.
15
15
 
16
16
  ## Risk-scaled execution
17
17
 
@@ -81,8 +81,7 @@ satisfies. For small, low-risk work with clear scope and acceptance evidence and
81
81
  Orchestrator's focused checks suffice; do not call QA or Reviewer automatically. Significant/high-risk work requires
82
82
  independent review of the full change at the final relevant state after the required QA/focused checks and any QA recheck.
83
83
 
84
- Many known metadata files are not significant solely because there are many, and a one-file permission or data-loss
85
- rule may be. A vague request to delete unnecessary files does not establish low risk; reclassify when installer,
84
+ Metadata file count alone does not imply significant risk; a one-file permission or data-loss rule may. A vague request to delete unnecessary files does not establish low risk; reclassify when installer,
86
85
  manifest, or other contracts are affected. For a verified ignore-pattern or metadata cleanup, confirm the targets are
87
86
  unused, required project materials are unaffected, ignore patterns cover only intended paths, and the diff matches the
88
87
  approved scope; Orchestrator's focused checks then suffice without automatically calling QA or Reviewer.
@@ -116,7 +115,7 @@ Structural checks establish written-contract alignment, not live agent complianc
116
115
 
117
116
  Task-local acceptance is distinct from overall final plan verification and acceptance. Orchestrator connects each task's
118
117
  acceptance criteria to actual evidence, currently required checks and findings before accepting it; checkbox
119
- completion, a Implementer report or passing commands alone do not establish acceptance.
118
+ completion, an Implementer report or passing commands alone do not establish acceptance.
120
119
 
121
120
  Task acceptance and its focused commit require only the task's risk-scaled evidence: a FRESH QA instance per task that
122
121
  needs QA (default to the current QA for post-fix rechecks of the same task while the approved scope, workspace,
@@ -126,7 +125,7 @@ do not delay its acceptance/commit solely for later final plan verification. Cur
126
125
  be deferred to final verification to accept early. State the scope and timing of final-only integration criteria in
127
126
  the plan; with a plan, the required final whole-change review is final plan verification.
128
127
 
129
- A task commit record does not mark unperformed final verification complete. Orchestrator still decides final plan
128
+ A task commit record does not prove unperformed final verification complete. Orchestrator still decides final plan
130
129
  acceptance using final QA/Reviewer/integration evidence and findings, preserving unverified scope and explicit dispositions.
131
130
  Valid evidence may cover both task and plan requirements only under the existing scope/state/independence/freshness/flow
132
131
  rules. For final Reviewer findings on already committed plan work, Orchestrator defines a follow-up task and required
@@ -160,8 +159,7 @@ unmet, or unverified. Passing a check alone does not prove a criterion: evidence
160
159
  expected result to actual observations. Preserve partial confirmation and out-of-scope, missing-check, or environment
161
160
  blocked gaps as unverified; omit no criteria. PASS, if used, means only run status and cannot replace criterion
162
161
  verdicts.
163
- Structural checks prove structural criteria only; they do not prove actual behavior, installation, or live agent runtime
164
- compliance.
162
+ Structural checks prove only structural criteria, not actual behavior, installation, or live agent runtime compliance.
165
163
 
166
164
  For QA execution failure or an unmet criterion within the current task, Orchestrator defaults to the current Implementer
167
165
  for correction and the current QA for a post-fix recheck, subject to the context, task-boundary and amendment rules in
@@ -182,7 +180,7 @@ issues may require both QA verification and Reviewer source review.
182
180
 
183
181
  Orchestrator accepts using criterion verdicts, findings, recheck evidence, and remaining gaps, rather than an overall
184
182
  run result.
185
- Explicit disposition does not convert unverified to proven; completion reports preserve remaining unverified scope.
183
+ Explicit disposition does not turn unverified into proven; completion reports preserve remaining unverified scope.
186
184
  For low-risk work without QA, Orchestrator's focused checks likewise connect each criterion to evidence.
187
185
 
188
186
 
@@ -194,8 +192,8 @@ Read brief-cited `references/superpowers/test-driven-development/SKILL.md`; reta
194
192
  Once applicable authorization, document-depth, execution-target, and handoff conditions hold for the approved scope,
195
193
  continue through execution, correction, reverification, required QA and Reviewer, findings disposition, Orchestrator
196
194
  acceptance, and the result report. Apply risk-scaled timing above; this adds no per-task Reviewer or new QA obligation.
197
- A role completion report, probe findings, or passing commands are not overall completion when approved work remains.
198
- Remaining approved execution is not an optional suggestion waiting for a user response. A probe-only scope
195
+ Role reports, probe findings, or passing commands do not establish overall completion while approved work remains.
196
+ Do not present remaining approved execution as an optional suggestion awaiting a user response. A probe-only scope
199
197
  ends at its approved question's evidence and limits; a combined scope continues under planning's scope rules.
200
198
  Document omission changes neither verification risk nor acceptance responsibility. Hold only affected work for an
201
199
  actual unmet condition and report what is needed to resume. Structural evidence does not prove live agent compliance.
@@ -231,7 +229,7 @@ This is an operator verdict record; it does not replace semantic proof of a crit
231
229
  Failures, unknown, mismatch and unverified outcomes are not passing evidence. Preserve previous results and failure records.
232
230
 
233
231
  A failed check/review leaves an open finding. Link a separate finding to an existing check/review through `record` with
234
- `data: {action: "open", id, checkId, message}`. After a Implementer fix, record
232
+ `data: {action: "open", id, checkId, message}`. After an Implementer fix, record
235
233
  `data: {action: "resolve", id, actor: {id, role: "implementer"}, changeFingerprint}` and the fix evidence messageRef.
236
234
  The actor must be the Implementer assigned to that task, or a relevant task for an overall finding. This marks the finding resolved;
237
235
  it becomes closed only when a subsequent prepare includes it in `findingIds` and revalidation passes for the same change version.
@@ -283,8 +281,8 @@ A changed runtimeHash follows the existing stale/hold rules; the stored version
283
281
  [ARC role policy: contract begin]
284
282
  Generated from lib/policy/role-policy.json; do not edit this managed section manually.
285
283
  - [git.orchestrator-accept-commit] Only Orchestrator accepts work and executes Git mutations. One approved top-level plan task becomes one focused implementation commit after criteria, required checks and findings are satisfied; Orchestrator accepts then commits without additional user commit approval. Reports, passing checks and Reviewer opinions do not replace acceptance. Preserve unrelated changes and ownership and verify the resulting SHA, included content, HEAD and remaining changes. Existing small-task direct and plan-omission paths are unchanged.
286
- - [review.significant-no-plan] For significant/high-risk work without a plan: Implementer → QA and required QA rechecks → Reviewer of the final full change → Orchestrator acceptance. MUST NOT omit Reviewer merely because no plan exists. This creates no new plan obligation.
287
- - [review.significant-plan] For significant/high-risk plan work: each task uses Implementer → fresh QA as required → Orchestrator acceptance and focused commit. After all tasks are accepted and required QA is complete, Reviewer reviews the final full change once. Reviewer is NOT a per-task or per-commit required gate. Preserve required checks and necessary finding rechecks.
284
+ - [review.significant-no-plan] For significant/high-risk work without a plan: Implementer → QA and required QA rechecks → Reviewer of the final full change → Orchestrator acceptance. MUST NOT omit Reviewer because no plan exists. This creates no new plan obligation.
285
+ - [review.significant-plan] For significant/high-risk plan work: each task uses Implementer → fresh QA as required → Orchestrator acceptance and focused commit. Reviewer reviews the final full change once after all tasks are accepted and required QA is complete. Reviewer is NOT a per-task or per-commit required gate. Preserve required checks and necessary finding rechecks.
288
286
  - [review.final-plan-recheck] For final overall Reviewer findings on plan work, including behavior findings, use Implementer → Reviewer without QA reassignment; repeat until resolved. This exception applies only at the final plan stage. Preserve required QA and QA failure rechecks before initial final review and outside this exception.
289
287
  - [planning.plan-amend] During approved task execution, mechanical plan progress records need no new user approval; changed goals, scope, criteria, method, risk, dependencies or required verification require revised-version approval and preparation before affected work. In both cases Orchestrator amends the original planning-document commit at a safe boundary, without a separate document or fixup commit. Preserve actual approval chronology, recoverable old/new contents, hashes, versions and SHA mappings; never retroactively authorize earlier work. Rewrite only unpublished, unshared local linear history with clear ownership and no merges; stop on unknown publication, ownership, unexpected state or replay conflicts, without force push or a fallback document commit. Preserve implementation task boundaries and revalidate affected evidence. See planning, handoff-prep, commit and verification for the full contract.
290
288
  - [platform.codex] Role restrictions are prompt-level contracts, not a filesystem sandbox. No documented role-specific Git hook or generic per-agent tool allowlist is assumed. Existing shell capabilities are retained because static read access after shell reduction has not been verified; Implementer and QA need shell for approved checks.
@@ -13,7 +13,7 @@ acceptance. If declined, continue in text and do not offer again unless the user
13
13
  question whether to use the companion. Orchestrator owns its guide, server/browser, workspace paths, live-session
14
14
  checks, complete keyed URL, screens, session state, feedback/events, stale-screen clearing when returning to text, and
15
15
  cleanup. Check the server before showing a screen; after the user's reply, treat terminal text as primary and events as
16
- supplemental. A UI topic or accepted companion alone does not route every question through a browser.
16
+ supplemental. A UI topic or companion acceptance alone does not route every question through a browser.
17
17
 
18
18
 
19
19
  Upstream guide/scripts: `<installed ARC>/references/superpowers/brainstorming/visual-companion.md` and adjacent `scripts/`.
@@ -22,6 +22,6 @@ ownership. Missing files stop dependent work. MUST NOT use a global fallback.
22
22
 
23
23
  ## Follow-up Verification of Approved Visual Sources
24
24
 
25
- Each visual criterion requires the exact location, version, approval, and access state of the approved visual source; criterion IDs, properties, and comparison method; and evidence linking the actual output to its revision. An Implementer report does not replace fresh QA's direct comparison. Record results as pass, mismatch, or unverified and connect them to the existing proven/unmet/unverified criterion verdicts. Use pass only when accessible evidence meets the visual criterion. Keep unsupported claims unverified. Missing output-to-revision linkage is unverified; a visible difference is a mismatch. Treat these as separate observations and do not infer the current branch UI state. Hold the comparison if the approved source or actual output is inaccessible or interpretation approval is missing.
25
+ Each visual criterion requires the exact location, version, approval, and access state of the approved visual source; criterion IDs, properties, and comparison method; and evidence linking the actual output to its revision. An Implementer report does not replace fresh QA's direct comparison. Record results as pass, mismatch, or unverified and connect them to the existing proven/unmet/unverified criterion verdicts. Use pass only when accessible evidence meets the visual criterion; keep unsupported claims unverified. Missing output-to-revision linkage is unverified; a visible difference is a mismatch. Record these separately; do not infer the current branch UI state. Hold the comparison if the approved source or actual output is inaccessible or interpretation approval is missing.
26
26
 
27
- Static policy, generated-file, and manifest checks are structural evidence. They do not prove actual role behavior, an installed build, Claude Code CLI runtime compliance, or visual fidelity. Reports include actual changes and exclusion rationale, checks run and omitted, evidence and limits by criterion, and platform availability. Preserve existing baseline failures separately; progress on independent work does not relax required checks or acceptance criteria.
27
+ Static policy, generated-file, and manifest checks are structural evidence. They do not prove actual role behavior, an installed build, Claude Code CLI runtime compliance, or visual fidelity. Reports include actual changes, exclusion rationale, checks run and omitted, each criterion's evidence and limits, and platform availability. Keep baseline failures separate; progress on independent work does not relax required checks or acceptance criteria.
@@ -25,7 +25,7 @@ export function verifyPayload({packageRoot,safeFs}){
25
25
  for(const item of FIXED)select(item,false);for(const item of ROOTS)select(item,true);
26
26
  for(const platform of ['codex','claude'])if(!referenceTrees[platform])throw Error('Required superpowers reference tree is missing');
27
27
  for(const name of listed.keys())if(!files.has(name))throw Error(`Manifest entry is missing: ${name}`);
28
- const required=['claude/skill/hooks/role-git-guard.mjs','claude/skill/hooks/git-policy.json','lib/policy/claude-hook.mjs','lib/policy/role-policy.json','lib/policy/render.mjs','scripts/update-role-policy.mjs','lib/limits.mjs','lib/installer/limits.mjs','lib/installer/runtime-config.mjs','README.md','install.command','package.json','bin/arc.mjs','lib/cli/root.mjs','lib/tui/app.mjs','lib/tui/terminal.mjs','lib/tui/render.mjs','lib/tui/setup.mjs','lib/tui/workflow.mjs','lib/management/operations.mjs','lib/management/status.mjs','lib/management/update.mjs','lib/management/workflow.mjs','lib/cli/postinstall.mjs','scripts/postinstall.mjs','lib/cli/upgrade.mjs','bin/arc-upgrade.mjs','lib/installer/shell-completion.mjs','lib/installer/manifest.mjs','lib/installer/targets.mjs','lib/installer/global-config.mjs','lib/installer/plan.mjs','lib/safe-fs/index.mjs','lib/safe-fs/native.mjs','lib/models/index.mjs','lib/models/config.mjs','lib/models/profiles.mjs','vendor/toml/LICENSE','vendor/toml/package.json','vendor/toml/provenance.json','vendor/toml/dist/index.js','prebuilds/index.json'];
28
+ const required=['claude/skill/hooks/role-git-guard.mjs','claude/skill/hooks/git-policy.json','lib/policy/claude-hook.mjs','lib/policy/role-policy.json','lib/policy/render.mjs','scripts/update-role-policy.mjs','lib/limits.mjs','lib/installer/limits.mjs','lib/installer/runtime-config.mjs','README.md','install.command','package.json','bin/arc.mjs','lib/cli/root.mjs','lib/tui/app.mjs','lib/tui/terminal.mjs','lib/tui/render.mjs','lib/tui/setup.mjs','lib/management/operations.mjs','lib/management/status.mjs','lib/management/update.mjs','lib/management/workflow.mjs','lib/cli/postinstall.mjs','scripts/postinstall.mjs','lib/cli/upgrade.mjs','bin/arc-upgrade.mjs','lib/installer/shell-completion.mjs','lib/installer/manifest.mjs','lib/installer/targets.mjs','lib/installer/global-config.mjs','lib/installer/plan.mjs','lib/safe-fs/index.mjs','lib/safe-fs/native.mjs','lib/models/index.mjs','lib/models/config.mjs','lib/models/profiles.mjs','vendor/toml/LICENSE','vendor/toml/package.json','vendor/toml/provenance.json','vendor/toml/dist/index.js','prebuilds/index.json'];
29
29
  required.push('lib/installer/runtime-assets.mjs',...RUNTIME_MODULE_PATHS);
30
30
  for(const platform of ['codex','claude'])required.push(`${platform}/skill/runtime/role-policy.json`,...RUNTIME_MODULE_PATHS.map(name=>`${platform}/skill/runtime/${name.slice('lib/runtime/'.length)}`));
31
31
  for(const tuple of ['darwin-arm64','darwin-x64','linux-arm64-gnu','linux-x64-gnu'])required.push(`prebuilds/${tuple}/arc_safe_fs.node`);
@@ -9,6 +9,8 @@ import {buildPresetPlan,switchPresetTargets} from '../installer/preset.mjs';
9
9
  import {buildUninstallPlan,uninstallTargets} from '../installer/uninstall.mjs';
10
10
  import {installTargets} from '../installer/install.mjs';
11
11
  import {attachShellCompletionCleanup} from '../installer/shell-completion.mjs';
12
+ import {inspectGlobalArc} from '../npm/context.mjs';
13
+ import {uninstallGlobal,sameGlobalArc} from '../npm/uninstall.mjs';
12
14
  import {acquireExternalSkills} from '../installer/acquisition.mjs';
13
15
  /** @typedef {{packageRoot:string,userHome:string,cwd:string,env:object,safeFs:object,signal?:AbortSignal,acquisition?:Function}} ManagementContext */
14
16
  /** @typedef {{kind:string,plans:object[],options:object}} PreparedOperation */
@@ -25,7 +27,8 @@ export async function prepareOperation(context,options,{selections=[]}={}){
25
27
  if(options.kind==='install')attachShellCompletionCleanup(plans,context);
26
28
  // Uninstall plans initially share the completion snapshot; its executor assigns one owner.
27
29
  if(options.kind!=='uninstall')validatePlanSet(plans);
28
- return{kind:options.kind,plans,options:{...options}};
30
+ const globalArc=options.kind==='uninstall'?await(context.inspectGlobalArc??inspectGlobalArc)({env:context.env,cwd:context.cwd,runNpm:context.runNpm}):undefined;
31
+ return{kind:options.kind,plans,options:{...options},...(globalArc?{globalArc}:{})};
29
32
  }
30
33
  export function operationResult(result,successStatuses=['installed']){return{...result,code:result.interrupted?130:result.results.every(item=>successStatuses.includes(item.status)&&!item.error)?0:1,exitSession:false};}
31
34
  export function acquisitionFor(context){return context.acquisition??(async()=>{const work=fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()),'arc-acquire-'));let acquired;try{acquired=await acquireExternalSkills({work,safeFs:context.safeFs,signal:context.signal});}catch(error){if(!error.residualPaths?.length)try{fs.rmdirSync(work);}catch(cleanup){throw Object.assign(new AggregateError([error,cleanup],'Cannot clean private acquisition work'),{code:'ARC_ACQUISITION_CLEANUP',residualPaths:[work]});}throw error;}return{...acquired,dispose:async()=>{await acquired.dispose();fs.rmdirSync(work);}};});}
@@ -34,6 +37,16 @@ export async function executeOperation(context,prepared){
34
37
  const options={safeFs:context.safeFs,signal:context.signal,hooks:context.hooks};
35
38
  if(prepared.kind==='install')return operationResult(await installTargets(prepared.plans,{...options,acquisition:acquisitionFor(context)}));
36
39
  if(prepared.kind==='preset')return operationResult(await switchPresetTargets(prepared.plans,options),['updated','unchanged']);
37
- if(prepared.kind==='uninstall')return operationResult(await uninstallTargets(prepared.plans,options),['uninstalled']);
40
+ if(prepared.kind==='uninstall'){
41
+ let result;
42
+ try{
43
+ const current=await(context.inspectGlobalArc??inspectGlobalArc)({context:prepared.globalArc,env:context.env,cwd:context.cwd,runNpm:context.runNpm});
44
+ if(!sameGlobalArc(prepared.globalArc,current))throw Error('Global ARC installation changed; prepare uninstall again');
45
+ result=operationResult(await uninstallTargets(prepared.plans,options),['uninstalled']);
46
+ if(result.code!==0||context.signal?.aborted)return{...result,code:context.signal?.aborted?130:result.code,cli:{status:'skipped'}};
47
+ const cli=await(context.uninstallGlobal??uninstallGlobal)({context:prepared.globalArc,env:context.env,cwd:context.cwd,signal:context.signal,runNpm:context.runNpm});
48
+ return{...result,code:cli.code??(cli.packageStatus==='failed'?1:0),interrupted:result.interrupted||Boolean(cli.interrupted),exitSession:Boolean(cli.started),cli:{...cli,status:cli.packageStatus},stdout:cli.stdout,stderr:cli.stderr};
49
+ }catch(error){return{...(result??{results:[],interrupted:false}),code:context.signal?.aborted?130:1,exitSession:false,cli:{status:'skipped'},error};}
50
+ }
38
51
  throw Object.assign(Error('Unknown prepared operation'),{code:'ARC_CLI_INPUT'});
39
52
  }
@@ -15,7 +15,7 @@ export function report(result,output,error){
15
15
  for(const item of result.results){output.write(item.platform+': '+item.status+'; rollback: '+item.rollback+'\n');for(const filename of item.residualPaths??[])output.write('Residual path: '+filename+'\n');if(item.error)error.write('Error: '+item.error.message+'\n');}
16
16
  if(result.interrupted)return 130;
17
17
  if(result.results.every(item=>item.status==='installed'&&!item.error)){output.write('ARC version '+result.version+' harness upgraded! Previous harness version: not recorded.\n');return 0;}
18
- error.write('Harness update did not complete. Run arc again and choose Update Harness, or run npx --yes @yunhozz/arc@latest and choose Update Harness.\n');return 1;
18
+ error.write('Harness update did not complete. Run arc again and choose Install / Update to retry the harness update.\n');return 1;
19
19
  }
20
20
  export function failure(cause,error){error.write('Error: '+cause.message+'\n');return ['ARC_CANCELLED','ARC_NPM_CANCELLED'].includes(cause.code)?130:['ARC_CLI_INPUT','ARC_TARGET_CONFIGURATION','ARC_UNSUPPORTED_RUNTIME','ARC_NATIVE_UNAVAILABLE'].includes(cause.code)?2:1;}
21
21
  export async function runChild(request,io={},deps={}){
@@ -36,23 +36,32 @@ export async function prepareUpdate(context){
36
36
  const root=context.packageRoot??packageRoot,pkg=identity(root),api=await runtime(context),cwd=fs.realpathSync(context.cwd??process.cwd()),env=context.env??process.env;
37
37
  const state=api.collectInstalledState({safeFs:api.safeFs,userHome:context.userHome??os.homedir(),env,cwd});
38
38
  if(!state.targets.length)throw configurationError('No installed ARC harness found. Run npx --yes @yunhozz/arc to install it first');
39
- let plans,version;
40
- if(!context.upgradeInstalledTargets){const[{verifyPayload},{buildUpgradePlans}]=await Promise.all([import('../installer/manifest.mjs'),import('../installer/upgrade.mjs')]);const payload=verifyPayload({packageRoot:root,safeFs:api.safeFs});plans=buildUpgradePlans({payload,state,safeFs:api.safeFs});version=JSON.parse(payload.files.get('package.json')).version;}
41
- return{kind:'update',root,pkg,cwd,env,state,plans,version};
39
+ const npmContext=await api.inspectNpmContext({packageRoot:root,entryPath:path.join(root,'bin/arc.mjs'),env,cwd});
40
+ if(!['global','npx'].includes(npmContext.kind))throw configurationError('Run arc from a verified global ARC installation or npx');
41
+ let latestVersion=null,latestError;
42
+ try{const latest=await api.resolveLatest({context:npmContext,env,cwd,signal:context.signal});if(!/^\d+\.\d+\.\d+$/.test(latest.version))throw Error('Invalid latest ARC version');latestVersion=latest.version;}catch(error){if(['ARC_CANCELLED','ARC_NPM_CANCELLED'].includes(error.code))throw error;latestError=error;}
43
+ return{kind:'update',root,pkg,cwd,env,state,npmContext,currentVersion:pkg.version,latestVersion,canUpgrade:latestVersion!==null&&newer(latestVersion,pkg.version),latestError};
42
44
  }
45
+ function newer(left,right){const a=left.split('.').map(BigInt),b=right.split('.').map(BigInt);for(let i=0;i<3;i++){if(a[i]!==b[i])return a[i]>b[i];}return false;}
46
+
43
47
  export async function executeUpdate(context,prepared){
44
48
  let packageStarted=false;const messages=[];try{
45
49
  const api=await runtime(context),{root,pkg,cwd,env,state}=prepared;
50
+ if(!prepared.canUpgrade||typeof prepared.latestVersion!=='string'||!/^\d+\.\d+\.\d+$/.test(prepared.latestVersion)||!newer(prepared.latestVersion,prepared.currentVersion))throw configurationError('No newer ARC version is available');
51
+ if(identity(root).version!==pkg.version||pkg.version!==prepared.currentVersion)throw configurationError('ARC package changed after checking its version');
46
52
  const{assertPlanSnapshot}=await import('../installer/transaction.mjs');for(const entry of state.targets)assertPlanSnapshot(entry,{safeFs:api.safeFs});
53
+ const currentState=api.collectInstalledState({safeFs:api.safeFs,userHome:context.userHome??os.homedir(),env,cwd});if(currentState.stateToken!==state.stateToken)throw configurationError('Installed ARC settings changed; check the version again');
47
54
  const npmContext=await api.inspectNpmContext({packageRoot:root,entryPath:path.join(root,'bin/arc.mjs'),env,cwd});
48
- if(!['global','npx'].includes(npmContext.kind))throw configurationError('Run arc from a verified global ARC installation or npx --yes @yunhozz/arc@latest, then choose Update Harness');
55
+ if(!['global','npx'].includes(npmContext.kind)||['kind','packageRoot','entryPath','npmCli','prefix'].some(key=>npmContext[key]!==prepared.npmContext[key]))throw configurationError('ARC npm context changed; check the version again');
56
+ const expectedVersion=prepared.latestVersion;
49
57
  if(npmContext.kind==='npx'){
50
- let result;if(prepared.plans){const[{installTargets},{acquisitionFor,operationResult}]=await Promise.all([import('../installer/install.mjs'),import('./operations.mjs')]);result=operationResult({version:prepared.version,...await installTargets(prepared.plans,{safeFs:api.safeFs,signal:context.signal,acquisition:acquisitionFor({...context,safeFs:api.safeFs})})});}else{const{operationResult}=await import('./operations.mjs');result=operationResult(await api.upgradeInstalledTargets({packageRoot:root,state,safeFs:api.safeFs,signal:context.signal}));}return result;
58
+ const child=await api.runNpxUpgrade({context:npmContext,expectedVersion,expectedStateToken:state.stateToken,env,cwd,signal:context.signal});
59
+ return{...child,code:child.code===0&&!child.signal?0:child.code===130?130:1,exitSession:false,results:[],messages};
51
60
  }
52
- const latest=await api.resolveLatest({context:npmContext,env,cwd,signal:context.signal});packageStarted=true;
53
- const replaced=await api.upgradeGlobal({context:npmContext,expectedVersion:latest.version,env,cwd,signal:context.signal});if(replaced.packageStatus!=='updated')throw replaced.error??Error('CLI replacement failed');
54
- messages.push('CLI: updated to '+latest.version+' (previous '+pkg.version+').\n');
55
- const child=await api.runUpgradeChild({entryPath:path.join(npmContext.packageRoot,'bin/arc-upgrade.mjs'),request:{protocol:1,expectedVersion:latest.version,invocationCwd:cwd,expectedStateToken:state.stateToken},env,cwd,signal:context.signal});
61
+ packageStarted=true;
62
+ const replaced=await api.upgradeGlobal({context:npmContext,expectedVersion,env,cwd,signal:context.signal});if(replaced.packageStatus!=='updated')throw replaced.error??Error('CLI replacement failed');
63
+ messages.push('CLI: updated to '+expectedVersion+' (previous '+pkg.version+').\n');
64
+ const child=await api.runUpgradeChild({entryPath:path.join(npmContext.packageRoot,'bin/arc-upgrade.mjs'),request:{protocol:1,expectedVersion,invocationCwd:cwd,expectedStateToken:state.stateToken},env,cwd,signal:context.signal});
56
65
  return{...child,code:child.code===0&&!child.signal?0:child.code===130?130:1,exitSession:true,results:[],messages};
57
66
  }catch(error){return{code:['ARC_CANCELLED','ARC_NPM_CANCELLED'].includes(error.code)?130:['ARC_CLI_INPUT','ARC_TARGET_CONFIGURATION','ARC_UNSUPPORTED_RUNTIME','ARC_NATIVE_UNAVAILABLE'].includes(error.code)?2:1,exitSession:packageStarted,results:[],error,messages};}
58
67
  }
@@ -34,3 +34,25 @@ export async function inspectNpmContext({packageRoot,entryPath,env=process.env,c
34
34
  return unsupported('ARC is not in a verified global or npm exec installation');
35
35
  }catch{return unsupported('Cannot verify npm installation context');}
36
36
  }
37
+
38
+ // Global absence is a filesystem fact. Identity and context failures must throw,
39
+ // because treating them as absence could allow harness removal before validation.
40
+ export async function inspectGlobalArc({context,env=process.env,cwd=process.cwd(),runNpm=defaultRunNpm}={}){
41
+ const fail=message=>Object.assign(Error(message),{code:'ARC_TARGET_CONFIGURATION'}),npmCli=npmExecutable(env);
42
+ if(context?.npmCli&&context.npmCli!==npmCli)throw fail('Global ARC npm context changed');
43
+ async function query(args){const result=await runNpm(npmCli,args,{env,cwd});if(result.code!==0||result.signal)throw fail('Cannot verify global npm context');const value=result.stdout.trim();if(!path.isAbsolute(value)||value.includes('\n'))throw fail('Invalid global npm context path');return path.resolve(value);}
44
+ const queriedPrefix=await query(['prefix','--global']),queriedRoot=await query(['root','--global']);
45
+ let prefix;try{prefix=realDirectory(queriedPrefix);}catch(error){if(error.code!=='ENOENT')throw error;prefix=queriedPrefix;}
46
+ if(![path.join(queriedPrefix,'lib/node_modules'),path.join(queriedPrefix,'node_modules')].includes(queriedRoot))throw fail('Unverified global npm root');
47
+ const root=path.join(prefix,path.relative(queriedPrefix,queriedRoot)),packageRoot=path.join(root,'@yunhozz/arc');
48
+ for(const relative of [path.relative(prefix,root),'@yunhozz','arc']){
49
+ // Check each component, including lib/node_modules, without following links.
50
+ const base=relative==='@yunhozz'?root:relative==='arc'?path.join(root,'@yunhozz'):prefix;
51
+ let current=base;for(const part of relative.split(path.sep)){current=path.join(current,part);let stat;try{stat=fs.lstatSync(current);}catch(error){if(error.code==='ENOENT')return{kind:'absent'};throw error;}if(stat.isSymbolicLink()||!stat.isDirectory())throw fail('Global ARC directory identity is invalid or symlinked');}
52
+ }
53
+ const filename=path.join(packageRoot,'package.json');if(!fs.lstatSync(filename).isFile()||fs.lstatSync(filename).isSymbolicLink())throw fail('Invalid global ARC package identity');
54
+ let pkg;try{pkg=JSON.parse(fs.readFileSync(filename,'utf8'));}catch{throw fail('Invalid global ARC package identity');}
55
+ if(pkg.name!=='@yunhozz/arc'||!/^\d+\.\d+\.\d+$/.test(pkg.version))throw fail('Invalid global ARC package identity');
56
+ const{createHash}=await import('node:crypto'),stat=fs.lstatSync(packageRoot),packageToken=createHash('sha256').update(fs.readFileSync(filename)).update(String(stat.dev)+':'+String(stat.ino)).digest('hex');
57
+ return{kind:'global',npmCli,prefix,packageRoot,version:pkg.version,packageToken};
58
+ }
@@ -0,0 +1,17 @@
1
+ import {inspectGlobalArc} from './context.mjs';
2
+ import {runNpm as defaultRunNpm} from './process.mjs';
3
+ export function sameGlobalArc(left,right){return['kind','npmCli','prefix','packageRoot','version','packageToken'].every(key=>left?.[key]===right?.[key]);}
4
+ export async function uninstallGlobal({context,env=process.env,cwd=process.cwd(),signal,runNpm=defaultRunNpm}){
5
+ let started=false,result;
6
+ try{
7
+ const current=await inspectGlobalArc({context,env,cwd,runNpm});
8
+ if(!sameGlobalArc(context,current))throw Object.assign(Error('Global ARC installation changed; prepare uninstall again'),{code:'ARC_TARGET_CONFIGURATION'});
9
+ if(signal?.aborted)throw Object.assign(Error('Uninstall interrupted'),{code:'ARC_NPM_CANCELLED'});
10
+ if(current.kind==='absent')return{packageStatus:'absent',code:0,started:false,stdout:'',stderr:''};
11
+ started=true;
12
+ result=await runNpm(current.npmCli,['uninstall','--global','@yunhozz/arc','--prefix',current.prefix,'--ignore-scripts'],{env,cwd,signal});
13
+ if(result.code!==0||result.signal)throw Error('Global CLI removal failed; the package may have changed');
14
+ if((await inspectGlobalArc({context:current,env,cwd,runNpm})).kind!=='absent')throw Error('Global ARC remains after npm uninstall');
15
+ return{...result,packageStatus:'removed',started};
16
+ }catch(error){const interrupted=['ARC_CANCELLED','ARC_NPM_CANCELLED'].includes(error.code)||Boolean(signal?.aborted);return{...result,packageStatus:'failed',code:interrupted?130:result?.code||1,interrupted,started,error};}
17
+ }
@@ -27,3 +27,28 @@ export async function runUpgradeChild({entryPath,request,env,cwd,signal}){
27
27
  child.stdin.end(JSON.stringify(request));
28
28
  });
29
29
  }
30
+ // Resolve the npm-exec payload from its own bin, then use only that payload's
31
+ // verified management runtime. The global-only private child is not reused.
32
+ export async function runNpxUpgrade({context,expectedVersion,expectedStateToken,env,cwd,signal,runNpm=defaultRunNpm}){
33
+ if(context.kind!=='npx'||!validVersion(expectedVersion)||typeof expectedStateToken!=='string'||!/^[a-f0-9]{64}$/.test(expectedStateToken))throw Error('Invalid pinned npx upgrade request');
34
+ if(signal?.aborted)throw cancelled();
35
+ const bootstrap=`import fs from 'node:fs';import path from 'node:path';import os from 'node:os';import {pathToFileURL} from 'node:url';
36
+ try{
37
+ const expectedVersion=${JSON.stringify(expectedVersion)},expectedStateToken=${JSON.stringify(expectedStateToken)},cwd=${JSON.stringify(cwd)};
38
+ const bin=process.env.PATH.split(path.delimiter).map(p=>path.join(p,'arc')).find(p=>fs.existsSync(p));
39
+ if(!bin)throw Error('Cannot locate pinned ARC payload');
40
+ const entry=fs.realpathSync(bin),root=path.dirname(path.dirname(entry)),pkg=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8'));
41
+ if(pkg.name!=='@yunhozz/arc'||pkg.version!==expectedVersion||entry!==path.join(root,'bin/arc.mjs'))throw Error('Pinned ARC package identity mismatch');
42
+ const management=await import(pathToFileURL(path.join(root,'lib/management/update.mjs')));
43
+ if(typeof management.runtime!=='function'||typeof management.report!=='function')throw Error('Pinned ARC management runtime is unavailable');
44
+ const api=await management.runtime({});
45
+ if(typeof api.inspectNpmContext!=='function'||typeof api.collectInstalledState!=='function'||typeof api.upgradeInstalledTargets!=='function')throw Error('Pinned ARC management API is unavailable');
46
+ const context=await api.inspectNpmContext({packageRoot:root,entryPath:entry,env:process.env,cwd});
47
+ if(context.kind!=='npx'||context.packageRoot!==root)throw Error('Cannot verify pinned npm exec context');
48
+ const state=api.collectInstalledState({safeFs:api.safeFs,userHome:os.homedir(),env:process.env,cwd});
49
+ if(state.stateToken!==expectedStateToken||!state.targets.length)throw Error('Installed ARC settings changed before pinned upgrade');
50
+ const controller=new AbortController(),abort=()=>controller.abort();process.once('SIGINT',abort);process.once('SIGTERM',abort);
51
+ try{process.exitCode=management.report(await api.upgradeInstalledTargets({packageRoot:root,state,safeFs:api.safeFs,signal:controller.signal}),process.stdout,process.stderr);}finally{process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);}
52
+ }catch(error){console.error('Error: '+error.message);process.exitCode=2;}`;
53
+ return runNpm(context.npmCli,['exec','--yes','--package','@yunhozz/arc@'+expectedVersion,'--',process.execPath,'--input-type=module','-e',bootstrap],{env,cwd,signal});
54
+ }