jwc-global
  • Status: Proposed simplification.
  • Sources checked: V3 runtime page in kai-chattr docs, E:/hooks/depcruiser-migrations, E:/hooks/_docs/depcruiser-migrations-design-and-verification*.md, E:/hooks/config.json, E:/hooks/_core/config-model.mjs, E:/hooks/quality-completion-gate/quality-verify-manifest.json, E:/hooks/_db/hooks.db, and E:/hooks/.state.

Files the Proposed Plan Would Create

This implementation proposes to create the following hook-side files:

#FilePurpose
1README.mdDocuments the hook bundle, operator workflow, local commands, and recovery behavior.
2dependency-cruiser.cjsProvides the dependency-cruiser rules used by target verification.
3depcruiser-migrations.mjsRuns the PreToolUse hook gate, reads active state, and permits or blocks guarded edits.
4depcruiser-migrations-cli.mjsGives the worker the command surface for initialize, arm, validate-state, and disarm.
5graph.contract.jsonStores the authored migration graph as data.
6generate-closure-manifest.mjsConverts dependency-cruiser donor output into the source manifest used for adjudication.
7validate-closure-manifest.mjsPerforms strict reconcile checks against approved port, adapter, cut, and target_extras decisions.
8verify-active-closure.mjsRuns target dependency-cruiser, typecheck, and manifest closure verification for closure approval.
9initialize-depcruiser-migrations/SKILL.mdDescribes the worker-first workflow and tells the agent which CLI commands to run first.
10lib/path-scope.mjsParses edit paths, including apply_patch, and enforces guarded prefixes and files.
11lib/state-store.mjsOwns atomic .state reads, writes, event appends, and active migration validation.
12tests/depcruiser-migrations.test.mjsCovers the gate, path extraction, state transitions, and strict reconcile behavior.

Runtime Behavior

The user starts the workflow by invoking the initialize-depcruiser-migrations skill.

The skill should work in two packaging modes:

  • as the internal E:/hooks/depcruiser-migrations/initialize-depcruiser-migrations/SKILL.md skill
  • as a self-contained skill plus hook plus script plugin with the same command behavior

The graph contract is available before the workflow starts. The proposed P0 graph has these nodes:

initialize -> adjudication -> implementation -> closure_verification -> disarm

Node 1: initialize

Initiated by: The user invokes initialize-depcruiser-migrations.

What happens: The worker gathers or confirms only the variable migration inputs:

  • migration name
  • donor repo absolute path
  • donor entry file, repo-relative
  • donor tsconfig, repo-relative
  • target repo absolute path
  • target entry file, repo-relative
  • guarded target prefixes and guarded target files
  • manifest directory, normally E:/hooks/depcruiser-migrations/manifests/<name>/

If the user gives only a page, route, or repo pair, the worker inspects the repos and asks only for missing values that cannot be inferred.

After the inputs are known, the worker runs the deterministic initialize command:

node E:/hooks/depcruiser-migrations/depcruiser-migrations-cli.mjs initialize --name <name> --donor-root <donorRepo> --donor-entry <donorEntry> --donor-tsconfig <donorTsconfig> --target-root <targetRepo> --target-entry <targetEntry> --guarded-prefix <prefix> --guarded-file <file>

Repeat --guarded-prefix and --guarded-file as needed.

The script, not the worker, owns the setup procedure:

  • detect package managers from lockfiles
  • parse donor and target package.json
  • check whether dependency-cruiser is already installed
  • install dependency-cruiser only where missing
  • create the manifest directory
  • run the donor dependency-cruiser trace
  • generate the source manifest
  • check for unresolved donor imports
  • write setup artifacts
  • confirm that graph.contract.json can be read

Donor trace command run by the script:

npx depcruise --no-config --output-type json --ts-config <donorTsconfig> --ts-pre-compilation-deps <donorEntry> > E:/hooks/depcruiser-migrations/manifests/<name>/before-edit.donor.json

Source-manifest command run by the script:

node E:/hooks/depcruiser-migrations/generate-closure-manifest.mjs E:/hooks/depcruiser-migrations/manifests/<name>/before-edit.donor.json E:/hooks/depcruiser-migrations/manifests/<name>/<name>.source-manifest.json --entry <donorEntry>

generate-closure-manifest.mjs writes each donor module with verdict: "TBD" and target_path: null.

Files saved:

  • E:/hooks/depcruiser-migrations/manifests/<name>/
  • E:/hooks/depcruiser-migrations/manifests/<name>/inputs.json
  • E:/hooks/depcruiser-migrations/manifests/<name>/setup-result.json
  • E:/hooks/depcruiser-migrations/manifests/<name>/before-edit.donor.json
  • E:/hooks/depcruiser-migrations/manifests/<name>/<name>.source-manifest.json
  • dependency changes in donor or target package.json and lockfile only when dependency-cruiser was missing

Worker/AI does: Resolves inputs, asks for missing user decisions, runs the initialize command with those inputs, and reads the script result.

User does: Supplies missing source or target information and confirms any ambiguous repo, entry, or guarded path decision.

Completed when: Required inputs are known, the initialize command exits successfully, both repos have dependency-cruiser available, the manifest directory exists, setup artifacts are saved, the donor trace exists, the source manifest exists, unresolved donor imports have either been resolved or explicitly reported, and the graph contract loads successfully. Completion starts adjudication.

Node 2: adjudication

Initiated by: initialize completes.

What happens: The worker classifies every module in the source manifest, resolves the human decision loop, and asks the user to approve the adjudicated manifest. User approval is the trigger that completes this node.

Allowed verdicts:

  • port: bring the donor source file into the target and adapt only required runtime seams
  • adapter: replace a donor runtime seam with a target-native adapter
  • cut: exclude the donor module intentionally

For port and adapter, the worker sets target_path to the repo-relative target path.

For target-native files that are required but not present in the donor closure, the worker records target_extras[] with reasons.

Files saved:

  • updated E:/hooks/depcruiser-migrations/manifests/<name>/<name>.source-manifest.json
  • optional E:/hooks/depcruiser-migrations/manifests/<name>/adjudication-notes.md when decisions need explanation
  • E:/hooks/depcruiser-migrations/manifests/<name>/adjudication-approval.json

Worker/AI does: Walks the manifest in port_order, proposes verdicts, fills target_path, identifies adapters, identifies cuts, records target extras, presents the decision summary, and waits for user approval.

User does: Approves or corrects ambiguous verdicts, adapter decisions, cuts, target paths, and target extras before any guarded target edit occurs.

Completed when: No manifest module has verdict: "TBD", every port and adapter has a repo-relative target_path, cuts are intentional, target extras have reasons, and the user explicitly approves the adjudicated manifest. That approval triggers implementation.

Node 3: implementation

Initiated by: User approval completes adjudication.

What happens: The worker first arms and validates the migration, then edits the target repo under the active hook gate.

The worker arms the migration through the proposed CLI:

node E:/hooks/depcruiser-migrations/depcruiser-migrations-cli.mjs arm --name <name> --target-root <targetRepo> --target-entry <targetEntry> --manifest-dir E:/hooks/depcruiser-migrations/manifests/<name> --source-manifest E:/hooks/depcruiser-migrations/manifests/<name>/<name>.source-manifest.json --donor-trace E:/hooks/depcruiser-migrations/manifests/<name>/before-edit.donor.json --target-trace E:/hooks/depcruiser-migrations/manifests/<name>/after-edit.target.json --guarded-prefix <prefix> --guarded-file <file>

Repeat --guarded-prefix and --guarded-file as needed.

The worker validates active state:

node E:/hooks/depcruiser-migrations/depcruiser-migrations-cli.mjs validate-state

The hook gate behavior is:

  • unarmed state allows edits
  • malformed active state denies guarded edits with repair instructions
  • missing source manifest denies guarded edits
  • any manifest module with verdict: "TBD" denies guarded edits
  • all verdicts valid allows guarded edits

The worker ports files in port_order, handles leaves first, writes adapters where approved, and does not create screenshot lookalikes or unrelated target files.

Files saved:

  • target repo files listed by approved target_path
  • target repo files listed by approved target_extras[]
  • E:/hooks/.state/depcruiser-migrations/active.json
  • appended E:/hooks/.state/depcruiser-migrations/events.jsonl

Worker/AI does: Arms through the CLI, validates active state, copies real donor source for port modules, adapts only target runtime seams, writes approved adapters, skips approved cuts, and keeps edits inside guarded prefixes or files.

User does: Answers migration questions that change approved intent. The user does not need to manually drive each file edit.

Completed when: active.json is written atomically, events.jsonl records the arm event, active state validation passes, all approved port, adapter, and target_extras[] files exist in the target repo, and the worker is ready to run closure verification. Completion starts closure_verification.

Node 4: closure_verification

Initiated by: implementation completes.

What happens: The worker runs the proposed verifier:

node E:/hooks/depcruiser-migrations/verify-active-closure.mjs

The verifier runs target dependency-cruiser from the target repo root:

npx depcruise --config E:/hooks/depcruiser-migrations/dependency-cruiser.cjs --output-type json --ts-pre-compilation-deps <targetEntry> > E:/hooks/depcruiser-migrations/manifests/<name>/after-edit.target.json

The verifier runs the target typecheck with the target package manager:

pnpm exec tsc --noEmit
npx tsc --noEmit
yarn tsc --noEmit

The verifier then runs strict reconcile through validate-closure-manifest.mjs.

Strict reconcile must fail on:

  • missing ports
  • leftover cuts
  • unexpected target nodes
  • undeclared target-native files
  • duplicate target paths
  • path traversal
  • dependency-cruiser JSON errors
  • target typecheck failure

If verification fails because implementation is incomplete, the workflow returns to implementation. If verification fails because the adjudication decision was wrong, the workflow returns to adjudication.

After verification passes, the worker presents the closure evidence to the user:

  • donor trace path
  • source manifest path
  • target trace path
  • verification report path
  • target typecheck result
  • strict reconcile result

Files saved:

  • E:/hooks/depcruiser-migrations/manifests/<name>/after-edit.target.json
  • E:/hooks/depcruiser-migrations/manifests/<name>/verification-report.json
  • E:/hooks/depcruiser-migrations/manifests/<name>/closure-approval.json
  • appended E:/hooks/.state/depcruiser-migrations/events.jsonl

Worker/AI does: Runs verification, reads failures, debugs the target implementation, updates only approved manifest decisions when required, reruns verification until closure passes, summarizes the passing evidence, and waits for explicit final closure approval.

User does: Gives direction only when verification requires changing an approved adjudication decision or accepting a new target_extras[] entry. After verification passes, the user approves final closure or sends the workflow back for more implementation or adjudication work.

Completed when: Target dependency-cruiser passes, target tsc --noEmit passes, strict reconcile passes, verification-report.json records the passing evidence, the user explicitly approves final closure, and closure-approval.json is saved. Completion starts disarm.

Node 5: disarm

Initiated by: closure_verification completes.

What happens: The skill instructs the worker to run the deterministic disarm command:

node E:/hooks/depcruiser-migrations/depcruiser-migrations-cli.mjs disarm --name <name>

The worker does not hand-edit .state. The CLI removes or marks inactive the active state atomically, appends an audit event, confirms the hook is unarmed, and returns the final artifact paths.

Files saved:

  • updated or removed E:/hooks/.state/depcruiser-migrations/active.json
  • appended E:/hooks/.state/depcruiser-migrations/events.jsonl
  • optional final run summary or artifact index returned by the CLI

Worker/AI does: Runs the disarm command, reads the CLI result, and reports the final artifact paths.

User does: No action after final approval unless disarm fails.

Completed when: No active depcruiser migration remains armed, the disarm event is recorded, and the worker reports the artifact paths.

State

Use .state first:

E:/hooks/.state/depcruiser-migrations/
  active.json
  events.jsonl

active.json should hold the active migration:

{
  "version": 1,
  "migrationId": "conversation-page",
  "currentNode": "adjudication",
  "targetRoot": "E:/kai-chattr",
  "targetEntry": "apps/web/src/routes/conversation.tsx",
  "guardedPrefixes": ["apps/web/src/"],
  "guardedFiles": [],
  "evidenceDir": "E:/hooks/depcruiser-migrations/manifests/conversation-page",
  "sourceManifest": "E:/hooks/depcruiser-migrations/manifests/conversation-page/conversation-page.source-manifest.json",
  "status": "active"
}

Do not use E:/hooks/_db/hooks.db for depcruiser run state in P0. That DB supports hook telemetry and gate behavior. If JSON state becomes insufficient, add exactly one DB after approval:

E:/hooks/.state/depcruiser-migrations/runs.sqlite

Graph Contract

Use one graph contract file:

{
  "version": 1,
  "nodes": [
    "initialize",
    "adjudication",
    "implementation",
    "closure_verification",
    "disarm"
  ],
  "transitions": [
    ["initialize", "adjudication"],
    ["adjudication", "implementation"],
    ["implementation", "closure_verification"],
    ["closure_verification", "disarm"],
    ["closure_verification", "implementation"],
    ["closure_verification", "adjudication"]
  ]
}

Config And Schema

Add the hook entry only after depcruiser-migrations.mjs exists:

{
  "id": "depcruiser-migrations",
  "name": "Depcruiser Migrations",
  "description": "PreToolUse gate and CLI for governed dependency-cruiser migrations.",
  "category": "gate",
  "event": "PreToolUse",
  "match": { "tools": ["Edit", "Write", "MultiEdit", "NotebookEdit", "apply_patch"] },
  "script": { "path": "depcruiser-migrations/depcruiser-migrations.mjs", "runtime": "node" },
  "scope": { "projects": ["*"], "paths": ["**"] },
  "enabled": false,
  "failPolicy": "open",
  "settings": {
    "stateDir": "E:/hooks/.state/depcruiser-migrations",
    "editTools": ["Edit", "Write", "MultiEdit", "NotebookEdit", "apply_patch"]
  }
}

Do not put active migration data in config.json. Active migration data belongs in .state.

config.schema.json is generated output. Do not hand-edit it. P0 can use the existing config model because hook settings is a plain object unless a hook-specific schema is added.

Quality Gate

After the replacement files exist, add syntax and focused test coverage to the hooks runtime commands:

node --check depcruiser-migrations/depcruiser-migrations.mjs
node --check depcruiser-migrations/depcruiser-migrations-cli.mjs
node --check depcruiser-migrations/verify-active-closure.mjs
node depcruiser-migrations/tests/depcruiser-migrations.test.mjs

Do not add verify-active-closure.mjs to target repo completion gates until it exists and passes local focused tests.

Skill Discovery

Current hook config indexes skills from E:/__skills, not from E:/hooks/depcruiser-migrations.

For the first implementation:

  • keep the nested skill path in the hook bundle
  • invoke it by explicit path/name during development
  • do not change skill-indexer.settings.scanRoots without approval

If the skill must become globally discoverable, publish it through the existing skills warehouse path rather than quietly adding a second scan root.

Implementation Build Order

  1. Create depcruiser-migrations.mjs, depcruiser-migrations-cli.mjs, lib/state-store.mjs, and lib/path-scope.mjs.
  2. Implement unarmed allow, invalid-state deny, guarded-path matching, and apply_patch path extraction.
  3. Add graph.contract.json with the five P0 nodes.
  4. Strengthen validate-closure-manifest.mjs to use repo-relative paths and blocking reconcile.
  5. Add verify-active-closure.mjs.
  6. Update initialize-depcruiser-migrations/SKILL.md so the worker runs the CLI first.
  7. Add one focused test file.
  8. Add the disabled config.json hook entry.
  9. Regenerate config.schema.json.
  10. Run _core/validate-runtime-hooks.mjs and focused depcruiser tests.
  11. Retire old closure-gate* files only after the new runtime passes.

P0 Scope Boundary

Browser verification is not part of P0. P0 proves source closure, target typecheck, strict reconcile, and user-approved closure completion.