Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"schema_version": 1,
"workflow_version": 2,
"workflow_origin_version": 2,
"id": "lifecycle-commits-stage-only-what-the-change-owns-never-every-untracked-file",
"slug": "lifecycle-commits-stage-only-what-the-change-owns-never-every-untracked-file",
"title": "Lifecycle commits stage only what the change owns, never every untracked file",
"description": "Lifecycle commits stage only what the change owns, never every untracked file",
"kind": "bug_fix",
"state": "accepted",
"canonical_applied": true,
"base_commit": "be3d90d8a67b31204623dd48c761e9c25770636f",
"created_at": 1790399719,
"updated_at": 1790402817,
"affected_specs": [
"cmd_change",
"change"
],
"affected_paths": [
"src/commands/change.rs",
"src/change.rs",
"src/change_tests.rs",
"docs/ADOPTING.md"
],
"no_spec_change": false,
"no_spec_change_rationale": null,
"acceptance_criteria": [
"change check --commit and change ship --push never commit an untracked file outside the paths the change owns: an untracked file elsewhere in the tree stays untracked and is absent from every lifecycle commit, and each one left out is listed on standard error. Those commits still carry the change workspace, its archive package, the canonical spec and requirements files its deltas write, the sequence ledger, and every tracked edit, staged through explicit literal pathspecs rather than git add -A. The run_checked_commit doc comment states what is actually committed when the second verification fails, and that error names the materialize commit already made. Regression tests drive both commands over a tree holding an unrelated untracked file and fail if it is committed."
],
"selected_artifacts": [
"context",
"testing",
"tasks",
"design",
"requirements",
"docs",
"research",
"plan"
],
"dependencies": [],
"answers": {
"architecture_risk": "yes",
"public_contract": "yes"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"approvals": [
{
"gate": "definition",
"actor": "user:0xLeif",
"timestamp": 1790402114,
"digest": "5a998fa452db13dff8d35fe8edbb0266b9173d6baa04e50a2a60831ee7c461f5",
"note": null,
"approved_scope": {
"schema_version": 1,
"change_id": "lifecycle-commits-stage-only-what-the-change-owns-never-every-untracked-file",
"title": "Lifecycle commits stage only what the change owns, never every untracked file",
"description": "Lifecycle commits stage only what the change owns, never every untracked file",
"kind": "bug_fix",
"affected_specs": [
"change",
"cmd_change"
],
"affected_paths": [
"docs/ADOPTING.md",
"src/change.rs",
"src/change_tests.rs",
"src/commands/change.rs"
],
"no_spec_change": false,
"no_spec_change_rationale": null,
"acceptance_criteria": [
"change check --commit and change ship --push never commit an untracked file outside the paths the change owns: an untracked file elsewhere in the tree stays untracked and is absent from every lifecycle commit, and each one left out is listed on standard error. Those commits still carry the change workspace, its archive package, the canonical spec and requirements files its deltas write, the sequence ledger, and every tracked edit, staged through explicit literal pathspecs rather than git add -A. The run_checked_commit doc comment states what is actually committed when the second verification fails, and that error names the materialize commit already made. Regression tests drive both commands over a tree holding an unrelated untracked file and fail if it is committed."
],
"dependencies": [],
"supersedes": [],
"answers": {
"architecture_risk": "yes",
"public_contract": "yes"
}
},
"approved_delta_digests": {
"change": "c65380e2fed99c09858dd7827c0404024d01d92206b0ed91a342fdd7d1596481",
"cmd_change": "75ae96beedc815128d33c82fa26311e8d21529a34b6348256e9bd8debcf182a3"
}
},
{
"gate": "finalization",
"actor": "specsync:finalization",
"timestamp": 1790402816,
"digest": "6fa921faeaa7e5740f31c181921c7e1c1a783e1c2ae3e7f04c08754ed96297db",
"note": "Same-PR finalization closing digest"
}
],
"reopenings": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: lifecycle-commits-stage-only-what-the-change-owns-never-every-untracked-file
state: archived
type: bug_fix
base_commit: be3d90d8a67b31204623dd48c761e9c25770636f
---

# Lifecycle commits stage only what the change owns, never every untracked file

## Intent

Lifecycle commits stage only what the change owns, never every untracked file

## Affected Canonical Specs

- `cmd_change`
- `change`

## Acceptance Criteria

- change check --commit and change ship --push never commit an untracked file outside the paths the change owns: an untracked file elsewhere in the tree stays untracked and is absent from every lifecycle commit, and each one left out is listed on standard error. Those commits still carry the change workspace, its archive package, the canonical spec and requirements files its deltas write, the sequence ledger, and every tracked edit, staged through explicit literal pathspecs rather than git add -A. The run_checked_commit doc comment states what is actually committed when the second verification fails, and that error names the materialize commit already made. Regression tests drive both commands over a tree holding an unrelated untracked file and fail if it is committed.

## No-spec Rationale

Not applicable
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
change: lifecycle-commits-stage-only-what-the-change-owns-never-every-untracked-file
artifact: context
---

# Context

`change check --commit` and `change ship --push` both committed through `git_commit_all` in
`src/commands/change.rs`, which ran `git add -A`. That stages every untracked, non-ignored file
in the project, and `--push` publishes it. In one week it put a private debug zip into a pushed
arcsite commit, and an agent's `.agents/` directory and an `exp2.sh` experiment script into
corvid-bot commits. Nothing warned, because nothing in the lifecycle knew which files were its own.

The same function carried a doc comment on `run_checked_commit` saying nothing is committed unless
verification passes. That holds for the first pass only: when the second pass fails, the
materialize commit is already on the branch.

Constraints a session picking this up needs:

- Verification digests the working tree, tracked and untracked (`project_input_digest` walks
`git ls-files --cached --others --exclude-standard`). A tracked edit left unstaged would be
verified and never committed, so tracked edits stay in the commit.
- An untracked file that is left out stays inside that digest. Committing it later keeps the
evidence current. Removing it stales the evidence, so the warning has to say so.
- `change new` writes `.specsync/workflow-v2-baseline.json` in a freshly adopted project. The
first test run of the fix left it out, which would have committed a change whose origin anchor
never reached history. It is a lifecycle ledger and is owned.
- `.specsync/change.lock` and the transaction journal are runtime files. `init` ignores them,
but a project without that ignore file used to commit the lock through `git add -A`. They are
neither committed nor reported now.
- The command layer holds no lifecycle policy (`specs/cmd_change/context.md`), so the decision
about which untracked paths the change owns lives in `src/change.rs`.

Ruled out: owning the change's `affected_paths`. They are prefixes such as `src/` or `.`, and
owning every untracked file under them brings the sweep back. Also ruled out: owning a whole spec
directory, because a stray file dropped beside a spec is not a spec.
Loading
Loading