Capability Contract v2 Configuration Batch
Goal
Continue Stage 1 after the evidence-export pilot by migrating the shared provider-configuration surface as one security-sensitive vertical slice:
qiongli_config_statusqiongli_save_provider_configqiongli_configure_providerqiongli_open_config_wizardas a compatibility alias
The batch must define truthful side effects and secret handling, align Lite and Full semantics where compatibility permits, and leave wire-level error carrier convergence for the declared compatibility window.
Baseline Drift
- Lite rejected
cwdfor config status while Full used it for project-local lookup. - Full ignored unknown arguments and coerced invalid values for the three configuration handlers.
- Config status reported two missing activation fields in Full and four in Lite.
- Save output normalization,
saved, warning behavior, and invalid-field error classification differed. - Full provider writes were non-atomic and could overwrite malformed config.
- Full config status could return arbitrary
searchvalues inside an otherwise redacted payload. - Wizard input errors were reported as generic tool failures, Full lacked Lite's bounded listener controls, and the alias was not modeled canonically.
Contract Work
- [x] Add canonical input/output schemas for the three canonical tools.
- [x] Model
qiongli_open_config_wizardonly as a compatibility alias. - [x] Raise registry coverage to
4 / 23canonical records and5 / 24public names without changing pilot status. - [x] Record local reads, local config writes, loopback listener startup, and deferred browser-form writes explicitly.
- [x] Mark the save value as secret-bearing input and the wizard URL as sensitive control output.
- [x] Require every canonical and alias public name to have referenced smoke evidence.
Runtime Alignment
- [x] Make both runtimes advertise the same configuration input schemas.
- [x] Accept
cwdas a typed compatibility context in Lite and reject unknown or mistyped Full arguments. - [x] Use all four activation-required provider fields for status
missingand a shared next-action priority. - [x] Return canonical provider/field names and
saved: trueafter writes. - [x] Classify caller-controlled schema/provider/field failures as semantic
invalid_arguments; preserve runtime/IO failures astool_error. - [x] Add
statusto Full configuration outputs and reuse an active Full wizard session across the canonical and alias names.
Security Hardening
- [x] Make malformed or unreadable global provider JSON fail closed.
- [x] Write provider configuration through a same-directory temporary file, flush/fsync it, and atomically replace the destination. Use mode
0600on Unix; on Windows, Rust Lite applies a protected DACL that grants only the current user full control. The legacy Node MCPB fails closed and remains read-only on Windows because it cannot enforce that DACL. - [x] Keep the prior file and clean temporary state if replacement fails.
- [x] Honor
enabled: falseend to end: omit disabled credentials from command environments, reportstrategy_onlywhen no provider is active, and never fall back to a legacy network search after an explicit provider opt-out. - [x] Resolve
QIONGLI_CONFIG_HOMEidentically across runtimes: accept an fully qualified absolute path or portable~/~/...home notation, reject rooted or drive-prefixed home suffixes and other relative values, use the platform user home by default, and never fall back to the process working directory. - [x] Allowlist typed public search settings in redacted status output.
- [x] Bound the Full wizard to loopback, ten minutes, 16 KiB default request bodies, one successful submission, and fixed secret-free errors.
- [x] Add
no-store,nosniff, and no-referrer response policy.
Verification
- [x] Registry structure, schema references, runtime declarations, alias inheritance, smoke identity, lifecycle, security, and side effects validate.
- [x] Lite and Full golden config-status/save/wizard outputs pass canonical schemas.
- [x] Unknown, mistyped, unsupported, and out-of-range inputs share the
invalid_argumentssemantic class without requiring one wire carrier. - [x] Secret canaries do not appear in success output, errors, status, diagnostics, or wizard responses.
- [x] Malformed-file, config-home resolution, atomic replacement, permission, TTL, body-limit, and single-use negative paths have focused tests.
- [x] Complete the repository-wide and materialized-distribution regression suite before handoff.
Rust Lite Windows writes create the temporary file under a protected current-user-only DACL and verify the persisted DACL after replacement. The remaining release gate is execution of the DACL, reparse-point, and fail-closed cases on the Windows CI runner; the legacy Node MCPB does not write on Windows.
Acceptance
- Registry coverage is honestly reported as
4 / 23and5 / 24. qiongli_open_config_wizardhas no duplicated capability record or schemas.- Both runtimes advertise exact canonical input schemas for the batch.
- Successful outputs validate against the canonical output schemas.
- Invalid caller input cannot write config or start a non-loopback listener.
- Provider secrets never appear in MCP responses or wizard rejection bodies.
- Existing corrupted provider configuration is never silently replaced.
Next Migration
After this batch remains green, continue with qiongli_literature_status and qiongli_search_plan, then migrate literature search inputs, diagnostics, provider failures, and result envelopes.