Imported from genaptic/contract-kit (
conkit/AGENTS.md). Install upstream withnpx skills add genaptic/contract-kit --skill conkit. Copyright stays with the author.
Conkit Crate Agent Guide
This file layers on top of the repository root AGENTS.md. Use it for work in
the conkit crate. Read ARCHITECTURE.md for the structural
flow and full module map; keep operational rules and validation commands here.
Crate Role
- Treat
conkitas the command-line adapter for Contract Kit. - Keep
conkitresponsible forclap, argv parsing, command grammar, operating-system paths, directory walking, byte reads, byte writes, stdout/stderr, process exit behavior, platform display naming, and local archive/report persistence. - Keep
main.rsthin. It should map the root app result toExitCodeand usefutures_executor::block_ononly at the process boundary. - Preserve the
App/CommandContext/AppCommandexecution shape.clapproduces typed parser values,Appinitializes runtime context, and command implementations adapt parsed values into domain requests. - Keep command execution routed through the crate-private
AppCommandtrait with explicit exhaustive match arms. - Keep stdout for command results and human summaries. Put diagnostics and errors on stderr through the application boundary.
Skill Routing
- Use
$use-rust-best-practices-corefor any Rust planning, editing, review, debugging, or validation task in this crate. - Add
$use-rust-shell-cli-best-practiceswhen changing command grammar,clapparsing, command execution, output behavior, filesystem handling, or process boundaries. - Add
$add-new-cli-commandwhen adding or changing aconkitCLI command. - Add
$use-rust-best-practices-architecturewhen changing crate boundaries, module ownership, binary naming, target layout, or test placement. - Add
$use-rust-best-practices-testingwhen changing unit tests, integration tests, scenario coverage, or validation strategy. - Add
$use-rust-best-practices-asyncbefore changingAppCommand, command dispatch, the singleblock_onboundary, or domain future orchestration. - Add
$use-rust-best-practices-abstractionsbefore changing the private command trait, closed dispatch families, or shared adapter boundaries. - Add
$rust-code-structuring-best-practicesbefore changing command structs, enums, receiver-method ownership, repeated data groups, or standalone helper placement. - Add
$use-rust-best-practices-dependencies-platformsbefore changing CLI dependencies, cross-platform path behavior, binary target policy, or release naming.
Workspace Domain Boundaries
Preserve the dependency and ownership boundaries in
ARCHITECTURE.md. The CLI owns OS/process/persistence/archive
behavior; conkit-signature and conkit-sketch remain independent semantic
domains over catalog bytes. CommandContext owns SignatureContractKit
directly, constructs one application-owned Rayon pool for both nominal domain
work configurations, and owns CLI filesystem catalog budgets. Keep worker
threads independent from active and pending root-operation capacities. Keep
signature-to-sketch catalog conversion and linked-seed
adaptation, including the zero-based physical YAML document index, localized
to contracts/sketch.rs, with no dependency between the domains or back into
conkit.
Command Boundary Rules
- Keep the public command surface verb-oriented:
check,generate,archive, anddiff. - Keep
checkandgeneratetarget-oriented:all,signatures, andsketches. - Match
ContractTargetexhaustively once per check or generate workflow, then delegate to concrete signatures, sketches, or all-family receiver methods. Do not use optional response matrices, anincludeshelper, or a generic family runner. - Preserve CLI-level
ContractTarget::Allas orchestration across signature and sketch families.generate allintentionally passesconkit_signature::ContractScope::Allso stale signatures and their linked sketches can be removed together; check and signature-only generation use the signature scope appropriate to their branch. - Preserve singular aliases
signatureandsketchwhile keeping pluralsignaturesandsketchesvisible in help. - Preserve the optional, mutually exclusive check modes. Omitting a mode and
passing
--defaultboth select signatureDefaultand sketchEnforce;--strictselects signatureStrictand sketchEnforce;--warningselectsWarningin both domains. Matching policy remains contract-owned. - Preserve the
generate allpre-write guarantee: finish signature generation, linked-item resolution, and sketch refresh before callingContractsStore::write_generated. The baseline-boundGeneratedContractsvalue pairs the completed document catalog with the exact non-metadata catalog used to compute it. UseContractsStoreto recover an interrupted updating journal before parsing combined documents, without creating metadata for missing or committed ownership. After acquiring the generation lock and recovering ownership, reconciliation requires the current catalog to match that baseline before preflight or mutation. An updating journal stores only its generation and completebefore/afterownership catalogs, with no family provenance. Preflight reads and hashes each existing requested path once, then only stale owned paths absent fromafteronce; retain separate commit-time revalidation, same-file alias checks, digest-bound reservations, and direct root.yml/.yamlownership rules. - Preserve
check allas CLI-owned orchestration: run signature and sketch checks with domain reports disabled, then render one combined CLI report. - Preserve
archive --gzipas an optional format selector. Gzip is currently the only archive format, so both the omitted and explicit forms select it. - Keep the versioned mixed-catalog archive codec in
archive.rs. Contract documents inside an archive use mandatory contract format v2 while the independent archive envelope remains version 1. Decode once in the CLI, then pass the same previous catalog to both domain diff APIs. - Preserve
diffsemantics: a successful comparison exits successfully even when the compared contracts changed. A future opt-in flag can add diff-tool exit-code behavior.
Filesystem And Platform Rules
- Use
Path,PathBuf,OsStr, andOsStringfor OS-facing paths. Convert to UTF-8 strings only at logical catalog boundaries or display boundaries. - Keep path resolution and containment in
catalog/path.rs, source traversal incatalog/source.rs, and contracts-root traversal and atomic file primitives incatalog/store.rs. - Keep symlink following explicit. A selected source or contracts root may itself be a symlink to a directory, but descendants may not traverse symlinks. Every participating source or contract file is component- and containment-checked, opened atomically without following its final symlink or reparse point, verified against the current path identity, and read through that opened handle.
- Apply
CatalogReadLimitswhile traversing and reading source and contract catalogs: count only participating entries, reject metadata-known breaches early, read at most the smaller per-file or remaining-total limit plus one evidence byte through the opened handle, and stop at the first deterministic breach. Domain crates must still revalidate their nominal limits. - Keep portable Windows device-name, reserved-character, C0-control, and
trailing-space/trailing-period validation in
platform/windows_names.rsand apply it through the singlePortablePathRules::validate_componententry point inplatform.rson every host. - Reject distinct ASCII-case-equivalent logical paths, including case-only prior-to-current transitions, before filesystem mutation.
- Preserve binary naming policy:
- Cargo target name:
conkit - runtime/help display name on every supported host:
conkit
- Cargo target name:
- Do not use Cargo features to decide OS behavior. Use
cfg(windows)or target configuration only for real platform differences. - Keep archive file names Windows-safe. The current archive names use Unix
nanoseconds plus
-archive.gzipand collision suffixes. - Open an existing archive with atomic final-component no-follow behavior so
symlink or reparse-point traversal is refused. Verify any opened handle is a
regular file and read through that same handle. Treat metadata length only as
an early rejection, enforce the compressed-byte limit while reading, and
never reopen the archive path for decoding. Charge the physical archive
entry, actual compressed bytes, and decoded logical entries to the command's
existing
CatalogReadBudget; do not reset or double-charge that ledger in the codec. - Do not describe multi-file catalog persistence as one atomic transaction.
Report and generated-file writes are individually atomic, generation uses a
locked, digest-backed journal persisted by the ownership model and
orchestrated by reconciliation, and each logical catalog path is validated
immediately before its sequential file write. Archive publication fully
syncs a sibling temporary and then uses a no-clobber hard link. A failed
publication explicitly removes the temporary and reports a cleanup failure
rather than relying only on
Drop. - Clean only recognized atomic manifest temporaries inside the reserved metadata namespace. Never infer or delete an output-directory temporary as generated ownership after an abrupt process termination.
Internal Layering Rules
Preserve the execution direction mapped in ARCHITECTURE.md:
- Keep
args.rsgrammar-only and keepApplimited to parse, initialize, and delegate. - Keep the shared Rayon pool, independent domain admission values, and CLI
catalog-read policy in
CommandContext; do not construct separate domain pools in command handlers or add a scheduler/core crate. - Keep
command.rsas the exhaustiveAppCommandfacade. Keep check, generation, archive, and diff sequencing incommand/check.rs,command/generate.rs,command/archive.rs, andcommand/diff.rs; these modules must not own contract semantics or archive codec details. - Keep cross-family routing in
contracts.rs, direct-root document validation in thecontracts/documentfacade plus itsheaderandyamlchildren, requested-versus-persisted extraction reconciliation incontracts/extraction.rs, combined layout/source binding incontracts/layout.rs, and substantive sketch adaptation incontracts/sketch.rs. Do not add a signature wrapper around the kit owned byCommandContext. - Keep
compiler.rsas the concrete extractor facade. Itsextractor,probe,limits,process,project,source, anderrorchildren own their named lifecycle boundaries; do not recreate a second probe family, process reaper, rustdoc semantic decoder, or source-sized coordinate index. - Keep
catalog.rsas a facade overcatalog/path.rs,catalog/source.rs,catalog/store.rs,catalog/ownership.rs, andcatalog/reconciliation.rs. Persisted version-3 values and intrinsic validation belong in ownership; locking, recovery, preflight, reservations, mutation, rollback, and final verification belong in reconciliation. - Keep the mixed-catalog codec in
archive.rs, publication mechanics inarchive/publication.rs, and verified existing-archive reads inarchive/source.rs. - Keep fallible human-summary writes in
output.rs; keep report format inference, borrowed domain-view rendering, and individually atomic replacement inreport.rs. Keep the concrete cancellation/byte-ceilingWriteadapter inbounded_output.rs; archive and report callers retain their distinct error and publication semantics. - Keep domain adapters limited to request/response and catalog adaptation, with no filesystem roots, terminal presentation, or process policy.
- Keep layout, catalog, ownership, report, archive, platform, output, and error mechanics in their owning CLI modules; they must not learn signature or sketch parsing semantics.
Validation Defaults
Run the smallest relevant subset while iterating. Before finishing a CLI behavior change, prefer:
cargo fmt --all -- --check
cargo check --locked --workspace --all-targets --all-features
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
cargo test --locked -p conkit --all-targets
cargo test --locked --workspace --all-targets
For documentation-only changes, still run markdown/reference checks and the smallest relevant Cargo check when the docs describe live behavior.
Boundary Test Expectations
- Keep unit tests next to the implementation they exercise.
- Keep CLI integration tests under
conkit/tests. - Use
assert_cmdto invoke the compiledconkitbinary in integration tests. - Keep filesystem tests portable with
assert_fs; include paths with spaces and non-ASCII characters when command behavior touches the filesystem. - Treat the scenario authoring guide as the
canonical manifest, fixture, evidence, and validation contract. Keep the
closed machine registry only in
REQUIRED_COVERAGE_KEYS. - Keep
conkit/tests/scenarios.rsresponsible for recursively discovered product scenarios and their coverage audit. Keeptests/support/scenario.rsas the registry/facade; place reusable mechanics in itserror,manifest,sandbox,steps,suite, andtreechildren. Keeptests/scenario_harness.rsas the harness-regression facade over itsexecution,filesystem,manifest, andsuitetest children. - Keep
conkit/tests/cli_help.rsnarrowly focused on the sharedconkitdisplay name and the test-owned process presentation environment; general grammar and scenario evidence belong in the checked-in leaves governed by the scenario guide. - While changing manifests or the harness, run all seven focused scenario commands maintained in the scenario authoring guide before the workspace gates. Do not duplicate that command list here.
- Keep
conkit/tests/dependency_policy.rsguarding:- the single cross-platform binary target name
conkit - canonical workspace package and dependency names without Cargo aliases
- the absence of compiler-private production dependencies
- the absence of production-scope
#[cfg(test)]shims outside local test modules
- the single cross-platform binary target name
- Do not invoke the
rustcexecutable directly from tests. Use Cargo-level validation, package tests, doctests, and scenario runners.