Files
ECC/schemas/install-state.schema.json
T
6aaa41e028 feat(install): require an explicit hook decision at the apply layer
The guided installer asks how ECC hooks should run, but that consent
lived only in the wizard path. Running install-apply directly with a
profile that includes hooks-runtime still materialized the hook runtime
with no disclosure and no decision.

Gate the apply layer instead, so every entry point is covered:

- disclose the six hook capability groups when a plan would materialize
  the hook runtime, and refuse to apply until the caller decides
- --enable-hooks confirms the hook runtime; --no-hooks installs the rest
  of the selection without it and records the reduced module closure in
  install-state
- surface the pending decision as a dry-run warning
- show the same capability disclosure in the guided installer's plan
  preview, so the wizard's hook question states what it is asking about

Plans that never materialize hooks (Kimi, --profile minimal,
--without baseline:hooks) are unaffected and need no flag. Repair and
uninstall operate on already-recorded state and stay unchanged.

The capability taxonomy and the held-materialization behavior come from
Samarjeet Singh Tomar's PR #2634, reworked to fit the single-decision
consent model that shipped with the guided installer in #2649.

Co-Authored-By: Samarjeet Singh Tomar <samar_tomar@hotmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:09:34 -04:00

222 lines
4.5 KiB
JSON

{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ECC install state",
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"installedAt",
"target",
"request",
"resolution",
"source",
"operations"
],
"properties": {
"schemaVersion": {
"type": "string",
"const": "ecc.install.v1"
},
"installedAt": {
"type": "string",
"minLength": 1
},
"lastValidatedAt": {
"type": "string",
"minLength": 1
},
"target": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"root",
"installStatePath"
],
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"target": {
"type": "string",
"minLength": 1
},
"kind": {
"type": "string",
"enum": [
"home",
"project"
]
},
"root": {
"type": "string",
"minLength": 1
},
"installStatePath": {
"type": "string",
"minLength": 1
}
}
},
"request": {
"type": "object",
"additionalProperties": false,
"required": [
"profile",
"modules",
"includeComponents",
"excludeComponents",
"legacyLanguages",
"legacyMode"
],
"properties": {
"profile": {
"type": [
"string",
"null"
]
},
"modules": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"includeComponents": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"excludeComponents": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"legacyLanguages": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"legacyMode": {
"type": "boolean"
},
"hookConsent": {
"enum": [
"enabled",
"declined",
null
]
}
}
},
"resolution": {
"type": "object",
"additionalProperties": false,
"required": [
"selectedModules",
"skippedModules"
],
"properties": {
"selectedModules": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"skippedModules": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
}
},
"source": {
"type": "object",
"additionalProperties": false,
"required": [
"repoVersion",
"repoCommit",
"manifestVersion"
],
"properties": {
"repoVersion": {
"type": [
"string",
"null"
]
},
"repoCommit": {
"type": [
"string",
"null"
]
},
"manifestVersion": {
"type": "integer",
"minimum": 1
}
}
},
"operations": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"required": [
"kind",
"moduleId",
"sourceRelativePath",
"destinationPath",
"strategy",
"ownership",
"scaffoldOnly"
],
"properties": {
"kind": {
"type": "string",
"minLength": 1
},
"moduleId": {
"type": "string",
"minLength": 1
},
"sourceRelativePath": {
"type": "string",
"minLength": 1
},
"destinationPath": {
"type": "string",
"minLength": 1
},
"strategy": {
"type": "string",
"minLength": 1
},
"ownership": {
"type": "string",
"minLength": 1
},
"scaffoldOnly": {
"type": "boolean"
},
"contentSha256": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
}
}
}
}
}
}