Documents / Issues / issue-fde9a7151bd1
`init --force` overwrites a customised `docs-schema.yaml` along with the `.gitignore`
A user re-running init to refresh the .gitignore destroys a customised schema.
Class: unstated · Severity: material
Flow: arch-90c90751344f · Step: docir init --force
Question: None · Frequency: unknown
Finding¶
--force overwrites docs-schema.yaml and .gitignore together, with no separate control, no confirmation prompt, no diff and no backup.
What happens today¶
composition.py:184-192 — both writes are guarded by the same force flag. The help text reads 'Overwrite an existing docs-schema.yaml / .gitignore.'
Impact¶
A user re-running init to refresh the .gitignore destroys a customised schema. The schema is the one file whose loss cannot be reconstructed from the documents.
Proposed default¶
Separate the flags, or refuse when the existing schema differs from the generated one unless a second confirmation is given.
Resolution¶
FIXED 2026-07-29, taking the second option but skipping rather than refusing. --force regenerates the .gitignore and a schema still byte-identical to what init would write; a schema that has been customised is kept, reported as schema_preserved: true, and named in a stderr warning that points at --force-schema. Verified end to end: with a customised schema and a stale .gitignore, init --force exits 0, refreshes the gitignore and leaves the custom type in place. THE FIRST ATTEMPT RAISED, AND WAS WRONG. _may_write_schema threw a SchemaError whose message said "the .gitignore is regenerated either way" — but the raise happened before the gitignore write, so the message was false and the user was left with neither. That is the worst outcome: refreshing the gitignore is the whole reason they ran the command, and the only way to get it became --force-schema, which destroys the schema. Caught by running the scenario from this gap's own impact statement instead of trusting the unit tests, which passed. The asymmetry is the point and is now stated in code: the .gitignore is a constant this module generates, so losing it costs nothing; the schema holds every type, status and cadence a person decided on and cannot be rebuilt from the documents. One flag for two files of such different value was the defect. SIXTH instance of the encoded-defect trap: the existing test was named test_force_overwrites_schema, wrote a customised schema, and asserted it was clobbered.
Actors affected¶
- repository maintainer
Evidence¶
src/docir/entry_points/composition.py:182-192src/docir/entry_points/cli/app.py:97-100
Migrated from the discovery gap register (GAP-026); the register itself now lives in this store.