Story: Existing Caproni project works unmodified
Story ID: STORY-004 Status: Revision 1 Created: 2026-10-13
Statement
As a developer with an existing Caproni project, I want xianyun to read my current config, environment variables, and on-disk state unchanged, so that I can switch tools without rewriting my project or losing my trusted CA and registry cache.
Trigger
The developer runs any xianyun command in a directory that has caproni.yaml (and possibly caproni.local.yaml, caproni-project.yaml, and ~/.local/share/caproni).
Acceptance Criteria
Scenario: Legacy config filenames load
Given a directory with only "caproni.yaml" and "caproni.local.yaml"
When the developer runs "xianyun config validate"
Then the exit code MUST be 0
And values from "caproni.local.yaml" MUST be merged over "caproni.yaml"
Scenario: An invalid legacy config is reported against the schema
Given a directory with only "caproni.yaml" that sets "cluster.driver: nonsense"
When the developer runs "xianyun config validate"
Then the exit code MUST be non-zero
And the message MUST name "/cluster/driver"
And no cluster MUST be started
Scenario: Legacy variables and environment aliases still work
Given a config value "${CAPRONI_CLUSTER_NAME}" and CAPRONI_NO_CACHE set
When the developer runs "xianyun config print"
Then the value MUST equal the "XIANYUN_CLUSTER_NAME" value
And the cache MUST be disabled as if XIANYUN_NO_CACHE were set
Scenario: Legacy state is reused, not duplicated
Given a CA under "~/.local/share/caproni" and none under "~/.local/share/xianyun"
When the developer runs "xianyun ca inspect"
Then the output MUST show the existing CA
And no new CA MUST be generated
EARS Constraints
- When both a primary and a legacy file exist in one directory, the system shall use the primary. (Covered by FR-001.2’s scenario rather than here.)
- The system shall name the in-cluster ClusterIssuer and Secret
xianyun-caand shall not createcaproni-caobjects. - If
--no-cacheorXIANYUN_NO_CACHEis set, then the system shall disable remote-fragment caching. (Not met: the flag is read but unused, GAP-005.) - When a legacy trust-store entry exists, the system shall remove it on
ca untrust.
Autonomy Limits
Not applicable; the actor is a human.
Security Mapping
| Concern | Addressed By |
|---|---|
| Tampering (legacy file shadowing a primary one) | Second scenario fixes precedence |
| Information disclosure (legacy backup file) | .caproni.bak is still written 0644; SR-010 (not yet met) |
Standards Mapping
- SLSA Level: N/A
- SSDF Practices: N/A
- SCVS Controls: N/A
Dependencies
- Depends on: none
- Blocks: none
- Related ADR: ADR-0003, ADR-0009
- Related spec requirement: FR-001, FR-002
Notes
- The invalid-config scenario was measured on 2026-10-13:
xianyun config validateagainst acaproni.yamlwithdriver: nonsenseexited 1 with/cluster/driver: "nonsense" is not one of [...]. That run also shows a legacycaproni.yamlis found and read. The valid-config scenario needs acluster.networkblock. - The first and third scenarios are covered by unit tests in
xianyun-core; none has been exercised against an on-disk legacy state in a VM test. - Documentation gap: user docs still say
caproni-caand~/.local/share/caproni, andexamples/own-infrastructure.yamlpasses--default-issuer-name=caproni-ca. A user following those againstxianyunreferences an issuer that doesn’t exist. This is the most likely way the “unmodified” promise breaks in practice. - The data-directory scenario is split in the code:
k3d.rsandcolima.rshard-code.local/share/caproni, so some state may be created in the old location even for new profiles.