|
bashdeps
Exact dependency materialization and manifest maintenance tools
|
Accepted
This Architecture Decision Record extends manifest-manager.bash beyond the initial update operation introduced by ADR-020. It defines conservative maintainer-facing contracts for three additional subcommands:
Issue #17 requested these operations after the manifest-manager foundation from issue #16 was complete. This decision keeps all three operations within the source-maintenance boundary already established by ADR-020. They do not change bashdeps.bash, its runtime trust model, the manifest grammar, or the separation between approved manifest source and runtime artifact synchronization.
This ADR defines all three command contracts so their relationship is deliberate rather than accumulated independently. The first implementation increment adds list. add and remove may land in later reviewable increments while remaining governed by this decision.
ADR-020 introduced manifest-manager.bash as a second first-class executable and intentionally limited its initial mutation behavior to surgical updates of already-declared dependencies. That implementation now provides a validated logical view of each manifest record while retaining exact physical record and surrounding source bytes.
The distinction between logical and physical representations is important for the new operations. list needs a stable inspection form without serializing the manifest. add needs to append one deliberately supplied declaration without changing any pre-existing byte. remove needs to delete exactly one logical record without silently treating nearby comments or blank lines as part of that record.
The manager must also continue to support human use, Make, CI, shell pipelines, and stdin/stdout workflows without introducing JSON or another serialization layer. The existing -f, --filename FILE convention should remain coherent across commands even though read-only and mutating stream operations have different output contracts.
list, add, and remove SHALL remain operations of manifest-manager.bash. They SHALL NOT be added to bashdeps.bash.
bashdeps.bash continues to consume already-approved declarations and verify exact artifact bytes. Manifest-manager mutations prepare source changes for review and commit. No new command authorizes retrieved bytes dynamically during bashdeps runtime synchronization.
The public form is:
with:
The default filename remains dependencies.txt.
On success, list SHALL emit each complete validated id value exactly as it appears logically in the manifest, one identity per line, in manifest order. An empty valid manifest produces no output and succeeds.
list SHALL NOT strip version text, reinterpret identities as GitHub package coordinates, emit complete logical records, or normalize source merely to display it. The id field is opaque to bashdeps.bash; a read-only inspection command has no reason to impose the narrower OWNER/REPO@VERSION convention used by update.
list SHALL parse and validate the complete manifest before emitting the first identity. Invalid input therefore produces no partial list.
-f - is meaningful for list: the complete manifest is read from STDIN and the identity list is written to STDOUT. Unlike mutating stream operations, a failed list operation emits no manifest rollback representation because STDOUT belongs to list data, not transformed source.
list SHALL require no network access, GitHub interpretation, or SHA-256 command.
The public form SHALL accept exactly one complete declaration supplied through the same four named fields as the manifest grammar:
The four fields are mandatory. Field order MAY be arbitrary, matching the manifest grammar. Unknown, duplicate, missing, or invalid fields fail.
The initial add operation SHALL NOT infer or invent:
In particular, OWNER/REPO@VERSION alone is insufficient input for add.
The explicitly supplied declaration SHALL be validated against the complete existing manifest before publication. Duplicate identity or destination values fail under the existing grammar rules.
Only newly appended bytes may be canonicalized. Existing source bytes SHALL remain an exact prefix of the successful candidate. The new record SHALL use the canonical one-line field order:
If the original manifest does not end with a line terminator, one separator SHALL be appended before the new record. The implementation SHALL preserve an observed LF or CRLF convention when it can do so unambiguously and SHALL use LF when no existing line-ending convention can be observed, including an empty file. The new record SHALL end with the selected line terminator.
-f - SHALL use the mutating stream transaction contract established by ADR-020: complete candidate on success, complete captured original on failure after input capture, with callers required to inspect the exit status.
Automatic candidate acquisition or digest calculation is not part of this initial add contract. A later explicit feature may propose it under a separate review if a concrete workflow requires it.
The public form SHALL be:
ID means the complete id field value and must match exactly one logical record. remove SHALL NOT reinterpret the argument as a GitHub package prefix or remove a version component before matching.
Zero matches fail. More than one matching record is already invalid manifest state under the version-1 grammar and therefore also fails.
A successful removal deletes exactly the selected record's physical bytes, including all physical continuation lines that belong to that logical record. Comments and blank lines before or after the record SHALL remain independent source chunks and SHALL NOT be implicitly attached to the dependency. Removing a record therefore does not guess whether nearby prose "belongs" to it.
The candidate SHALL preserve every remaining byte exactly. Implementations SHALL prove that the candidate corresponds to the original source with exactly the selected record chunk omitted, then revalidate the complete candidate before publication.
-f - SHALL use the same mutating stream rollback contract as update and add.
These forms SHALL succeed with status 0 and write help to STDOUT:
Top-level help SHALL summarize the available subcommands and common informational forms. Subcommand help SHALL document that command's syntax, arguments, options, file/stream behavior, output contract, and relevant public exit categories.
A command that has not yet been implemented SHALL NOT be advertised as available merely because this ADR defines its future contract.
The manager SHALL continue to use the ADR-020 public status range:
A command uses only categories relevant to its behavior. list, for example, normally uses 0, 2, and 6 and does not manufacture network or hashing failures for capabilities it never needs.
The existing logical parser and exact source-chunk representation SHALL remain the shared foundation.
The implementation SHALL NOT create a generalized rewrite engine that serializes records from parsed fields. Each mutation shape keeps a proof appropriate to its operation:
update replaces explicitly planned raw records and retains its reverse substitution proof;add proves that the original bytes are an exact prefix followed only by the explicitly constructed append sequence; andremove proves that the candidate is the original chunk sequence with exactly one selected record omitted.Shared capture, staging, stream detection, cleanup, and publication lifecycle helpers MAY be factored for reuse when doing so does not weaken command-specific safety properties.
These commands extend the existing manifest-manager.bash executable family. They do not create additional release products or change the twelve-file release asset count established by ADR-020.
Manager-only code remains excluded from the bashdeps.bash source closure and runtime requirements.
Rejected because it would impose the manager's GitHub-oriented update convention on identities that are otherwise opaque manifest data. Complete id values are provider-neutral and unambiguous.
Rejected because pipelines typically need stable identifiers, while emitting folded logical records would either normalize source or create an output format that looks like source without preserving its physical representation.
Rejected because deletion does not require release semantics. Exact complete-ID selection is safer and works for identities unrelated to GitHub.
Rejected because comment ownership is not represented by the manifest grammar. Deleting comments based on proximity would guess author intent and weaken byte preservation.
Rejected because package coordinates do not determine artifact filename, release asset choice, destination, or trust data. Explicit source is preferable to hidden convention.
Deferred. Hashing explicitly selected candidate bytes can be reasonable, but it introduces acquisition and trust-proposal questions that are unnecessary for the core append operation. A later explicit option can be designed if needed.
Rejected because it would rewrite formatting, comments, line endings, physical continuations, and other review-sensitive source bytes that are unrelated to the requested operation.
Rejected. Newline-delimited complete identities, command exit statuses, STDERR diagnostics, and manifest streams provide sufficient Unix-oriented interfaces.
The manager gains a provider-neutral inspection operation before gaining new mutation operations. This creates a low-risk first increment for validating the expanded subcommand and help architecture.
Later add and remove work has a defined contract and does not need to invent selection, newline, comment ownership, or inference semantics during implementation.
The conservative add interface requires callers to know all declaration fields. That is intentional: explicit input keeps trust and destination decisions visible.
Removal may leave blank lines or comments that a human later considers untidy. That is also intentional. Source cleanup is a separate author decision rather than an implicit side effect of deleting a dependency.
The project retains operation-specific preservation proofs, which means some mutation code will remain specialized instead of being compressed into a single abstraction. The additional code is accepted in exchange for inspectability and stronger review guarantees.
update behavior and the bashdeps.bash runtime contract remain unchanged.This decision extends ADR-020 without superseding its update, trust-boundary, release, or product-isolation decisions. It relies on ADR-002 for manifest field semantics and uniqueness, ADR-015 for logical-record folding, ADR-014 and doc/documentation-standard.md for maintained Bash documentation, and ADR-011's general principle that executable CLIs rather than private Bash helpers form the supported interface.