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
18 changes: 2 additions & 16 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,13 @@ run_local_standard_pin_check() {
return 1
fi

# This first boundary reads only staged local manifests and managed digests.
# The separately managed controller may then ask Goal to verify freshness.
# Commits read only staged local manifests and managed digests. Resolve a
# newer release explicitly in its adoption ticket, outside this boundary.
python3 "$runner" verify-pin --root "$root" --staged >/dev/null
}

run_standard_update_controller() {
local runner="$root/.governance/precommit_standard_update.py"
if [[ ! -f "$runner" ]]; then
if [[ ! -e "$root/.governance/standard-adoption.json" ]]; then
return 0
fi
echo "GOV-STANDARD-UPDATE-001: the managed standard update controller is missing." >&2
echo " Restore the pinned package; never bypass the pre-commit freshness boundary." >&2
return 1
fi
python3 "$runner" --root "$root" --ticket "$ticket"
}

run_commit_guards() {
run_local_standard_pin_check
run_standard_update_controller
run_worktree_guard
}

Expand Down
19 changes: 12 additions & 7 deletions .governance/error/GOV-STANDARD-UPDATE.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# GOV-STANDARD-UPDATE-001: pre-commit cannot safely prepare a standard update
# GOV-STANDARD-UPDATE-001: explicit standard update could not complete

## Situation

The managed hook found a pinned adoption but its controller is missing, Goal is
unavailable or incompatible, release verification failed, or Goal prepared or
refused an update and stopped the commit.
The explicitly invoked compatibility updater found Goal unavailable or
incompatible, release verification failed, or Goal prepared or refused an
update. Its legacy `--pre-commit` protocol prepares changes and returns control
for review; the managed commit hook no longer invokes this updater.

## Meaning

The committed pin remains authoritative. A newer release gains trust only when
Goal verifies its annotated tag, final GitHub Release, full SHA and generated
digests. Preparation does not stage, commit, merge or publish the result.
The managed commit hook checks the staged local immutable pin and worktree
guard only. A new upstream release does not change a feature ticket's pin.

## Safe resolution

Expand All @@ -19,13 +22,15 @@ digests. Preparation does not stage, commit, merge or publish the result.
3. Validate `.governance/standard-adoption.json`; when `executor` is
`koru-goal`, install a compatible Koru supervisor as well.
4. Allocate or resume exactly one standard-adoption ticket in its own worktree.
5. Retry the commit, review the prepared diff, stage it explicitly and retry.
5. Run explicit adoption in that ticket, review the prepared diff, validate it
and stage it explicitly before committing.

## Verification

- The Goal pre-commit adoption command returns zero when the verified release
- The explicitly invoked Goal preparation command returns zero when the verified release
is already pinned.
- A prepared update remains visible and the original commit remains uncreated.
- A prepared update remains visible for review and does not create a commit.
- An ordinary commit does not invoke Goal, Koru or release discovery.
- Managed governance and standard conformance pass after explicit restaging.

## Do not
Expand Down
2 changes: 1 addition & 1 deletion .governance/manifest.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"stacks": [],
"standard": {
"id": "wellmanifest/new-project",
"version": "0.20.11"
"version": "0.20.12"
},
"ticket": {
"activeStatuses": [
Expand Down
2 changes: 1 addition & 1 deletion .governance/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
],
"standard": {
"id": "wellmanifest/new-project",
"version": "0.20.11"
"version": "0.20.12"
},
"ticket": {
"activeStatuses": [
Expand Down
12 changes: 6 additions & 6 deletions .governance/manifest.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"managedFiles": {
".aider.conf.yml": "756af4477d7a0d39361919c957ee954807259f15cabf4dcabde81677eac6efd6",
".cursor/rules/new-project-standard.mdc": "72699a5cb3718be9a50603eda9c8a6d960f21b1949fdfe2b35013385b22f9afe",
".githooks/pre-commit": "d6dc5c9dddb9f8b59f235a11d95a384fe4fcf2f2f96efdb63e127a76a85f2681",
".githooks/pre-commit": "8299a163efdb520df8726769a7f59a3bc011d8581a7a028e40e4e3c0eb7f52d4",
".github/copilot-instructions.md": "b31f9ba957ce108a83851f48f8dbff8b4301d1c744a77fe76914e6bb8499aca3",
".github/workflows/new-project-governance.yml": "cc567c87a3a256a3975d93625a575ec6b03cbc4f1b12180abf61f97df35f7de2",
".governance/AGENT_DECISIONS.md": "fb8fcbb00ba4100ec230aea34ef4634852dae30929eb9eaeafe548fbc7b1de87",
Expand Down Expand Up @@ -30,7 +30,7 @@
".governance/error/GOV-INTENT.md": "4dc29dbf4c39d18cd11a5ec1eccc257a06d2bea2f95b560aa58085672611d4a9",
".governance/error/GOV-PACKAGING.md": "4a602c65a655e9b8b631487fa24982df00e0ae874cf5bdb5129f6493bb440c89",
".governance/error/GOV-REMEDIATION-INTENT.md": "ff0f41bf5112a1808738554b51c13a24ce97f7842304f1aca33e9f9846d73aa3",
".governance/error/GOV-STANDARD-UPDATE.md": "212039b1a5fbe8b61cdb0b35ca1cf3169187a7005123051468962ff8a3a848fa",
".governance/error/GOV-STANDARD-UPDATE.md": "4a6c83617dc5308d77a1adb1c79f54ec085f280aa3acc33a6e5329a4aa80109c",
".governance/error/GOV-TICKET-001.md": "61e110b93b6111fde243e538e4f34330df36ae29f5a65fb4f90b91d44c1b801e",
".governance/error/GOV-TICKET-ACTIVITY.md": "f1bc9cab86c36028eed449f1c40a229131cb49141cbea35620580e2ef2d2b642",
".governance/error/GOV-TICKET-ALLOCATION.md": "09f92cac24bbbbe5c2967221497fb6b68b02bcd3bf4f56afe36d36ac7d7b0a58",
Expand All @@ -42,7 +42,7 @@
".governance/governance_check.py": "305fc2fbf1c01a8a05e514346aa244977efabedd348379ebffd7e6562949fa17",
".governance/intent.schema.json": "9755f20cd189efe2205629781a6cf6b613bd93e9368416793c4de53256b0a9c0",
".governance/lock.schema.json": "ad80c98f800a4a3310870336dcdaf0aa689cc4988f71084d25d76bea2df1242f",
".governance/manifest.base.json": "e49a636499e17b46b399ebb0b0329df5ea79ed1d98f4264f21b666c732925d33",
".governance/manifest.base.json": "1a834c8de47b3a03eeece19c686966bce6ea5d9fc6a720305dba4ec70d243d8b",
".governance/manifest.schema.json": "5aa2ccd3f6898834d4e39a78342448145490be56aa132e16ac7c9d64acef8f73",
".governance/package-manifest.json": "a2ea77aca16d59282e1b60105521270ae112ebc52ddb3e08af3eeec26942dc3f",
".governance/precommit_standard_update.py": "c91e2bf9ae9d6ccc77bce0e61450c818a5961edee5bfde3b60426da88e296b0f",
Expand All @@ -69,7 +69,7 @@
".governance/work_continuity.py": "2efb720b530cc295b45413a2bc7384a426bd29b5d9b20452035e5943294c18e4",
".governance/workspace_lifecycle_check.py": "f196f33c4884120ca216e65473f32b163f6c6d262624309cf0cbdc37063d181f",
".governance/worktree_guard.py": "b154f6e67626770ec11c9544d31a27b32d9c215ef73ffc9eb8f52e9c3a9b051b",
".governance/worktree_overlap_check.py": "86336eef38cfd1c2d421052e089e0523643d48ffa1c6fb7437b69cb78e17c785",
".governance/worktree_overlap_check.py": "dfb6463889617352578576ad699f8baa4246c013cf943e1b427a76176d7e19a5",
".governance/worktree_path_check.py": "d3309b76e2c91dd7f046b00322ec8cb48ef744b8b7908d70e475fedf95e5e196",
".governance/worktrees.lock.json": "9ed607dd339fd4ed30263d7de62231249447568e8e35c8b19fdc0231fca39fde",
".governance/worktrees.schema.json": "9cc10d126e06cafc87cc117f11b8b095676f461de4d168d2a1c2bb959f0fccd5",
Expand All @@ -92,7 +92,7 @@
"id": "wellmanifest/new-project",
"publicationStatus": "published",
"sourceRepository": "wellmanifest/new-project",
"sourceRevision": "2cd39286dc0931870a8a60ee11c96dd6448b39e6",
"version": "0.20.11"
"sourceRevision": "bf3099667babd14ee778917d911d6c6bad45dcab",
"version": "0.20.12"
}
}
45 changes: 42 additions & 3 deletions .governance/worktree_overlap_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,43 @@ def merge_tree_conflicts(path: Path, left: str, right: str) -> tuple[str, ...] |
return tuple(sorted(set(conflicted)))


def pending_main_imports(path: Path) -> set[str]:
"""Clean staged imports from the current origin default branch, if proven.

An unfinished merge exposes already integrated main content as index edits.
It is not a competing contribution. Keep reporting that dirty state, but
exclude it from overlap attribution only when all local Git reads agree.
No fetch or index mutation is needed; unknown or older merge heads retain
conservative behavior. Committed feature edits are never exempted.
"""
try:
incoming = run_git(path, "rev-parse", "--verify", "MERGE_HEAD")
merge_file = Path(run_git(path, "rev-parse", "--path-format=absolute", "--git-path", "MERGE_HEAD"))
if merge_file.read_text(encoding="ascii").splitlines() != [incoming]:
return set() # Octopus merges have more than one source of edits.
remote = run_git(path, "rev-parse", "--verify",
f"refs/remotes/origin/{default_branch(path)}")
if not re.fullmatch(r"[0-9a-f]{40}|[0-9a-f]{64}", incoming) or incoming != remote:
return set()
base = run_git(path, "merge-base", "HEAD", incoming)

def names(*args: str) -> set[str]:
return set(run_git(path, *args).split("\0")) - {""}

staged = names("diff", "--cached", "--name-only", "--no-renames", "-z", "HEAD")
different = names("diff", "--cached", "--name-only", "--no-renames", "-z", incoming)
unstaged = names("diff", "--name-only", "--no-renames", "-z")
untracked = names("ls-files", "--others", "--exclude-standard", "-z")
local_commits = names("diff", "--name-only", "--no-renames", "-z", base, "HEAD")
# diff --cached includes unresolved paths; retain an explicit check so
# equality can never be inferred from a non-stage-zero index entry.
unresolved = {entry.split("\t", 1)[1]
for entry in names("ls-files", "--unmerged", "-z")}
return staged - different - unstaged - untracked - local_commits - unresolved
except (AuditError, IndexError, OSError, UnicodeError):
return set()


def contested_paths(
first: "Checkout", second: "Checkout", ignore: tuple[str, ...]
) -> tuple[str, ...]:
Expand All @@ -450,6 +487,8 @@ def contested_paths(
snapshot at the same HEAD contributes no competing committed change.
Unknown ancestry retains the conservative path-intersection fallback.
"""
first_dirty = set(first.dirty_paths) - pending_main_imports(first.path)
second_dirty = set(second.dirty_paths) - pending_main_imports(second.path)
first_changes, second_changes = set(first.changed_paths), set(second.changed_paths)
if first.head and second.head:
base = first.head if first.head == second.head else merge_base(first.path, first.head, second.head)
Expand All @@ -459,11 +498,11 @@ def contested_paths(
try:
first_committed = set(run_git(first.path, "diff", "--name-only", base, first.head).splitlines())
second_committed = set(run_git(second.path, "diff", "--name-only", base, second.head).splitlines())
first_changes = first_committed | set(first.dirty_paths)
second_changes = second_committed | set(second.dirty_paths)
first_changes = first_committed | first_dirty
second_changes = second_committed | second_dirty
except AuditError:
pass
dirty_overlap = (set(first.dirty_paths) & second_changes) | (set(second.dirty_paths) & first_changes)
dirty_overlap = (first_dirty & second_changes) | (second_dirty & first_changes)
conflicts: set[str] = set()
if first.head and second.head and first.head != second.head:
if not is_ancestor(first.path, first.head, second.head) and not is_ancestor(
Expand Down
13 changes: 13 additions & 0 deletions project/ticket-091/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Ticket 091: Immutable pin and main import fix

- **Status**: IN_PROGRESS
- **Workflow state**: PUBLICATION
- **Owner**: codex

SESSION_EXECUTION_AUTHORIZATION: User requested continued Semcod publication. The existing hook blocks PR 124 pending standard freshness; its runbook requires a separate managed adoption ticket. Adopt the already published fix without bypassing the hook.

## Acceptance criteria
- [x] AC-01: Adopt immutable bf3099667babd14ee778917d911d6c6bad45dcab, retaining target settings and package bindings.
- [ ] AC-02: Managed checks and Python CI matrix pass before independent protected publication.

Validation: 723 tests passed, 2 skipped; managed governance and Docker Compose passed. Required Python matrix and independent publication remain pending.
86 changes: 86 additions & 0 deletions project/ticket-091/intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"schema": "new-project.intent/v3",
"ticket": "ticket-091",
"summary": "Adopt published immutable pin and main import governance fix",
"workstream": "governance",
"classification": {
"kind": "BUG",
"priority": "P1",
"origin": "requested"
},
"allowedPaths": [
".githooks/pre-commit",
".governance/error/GOV-STANDARD-UPDATE.md",
".governance/manifest.base.json",
".governance/manifest.json",
".governance/manifest.lock.json",
".governance/worktree_overlap_check.py",
"pyproject.toml",
"project/ticket-091/**"
],
"forbiddenPaths": [
"project/ticket-*/user-*.md"
],
"stacks": [],
"dependsOn": [],
"conflictsWith": [],
"integrationTicket": null,
"delivery": {
"acceptedBaseSha": "b3abc2660105a82050c61e7ea55b4381d391e881",
"targetBranch": "main",
"outcome": "Adopt published new-project 0.20.12 to preserve immutable pins and correctly recognize imports from main, unblocking ticket-090.",
"nonGoals": [
"No product source, action dependencies or protected-check changes"
],
"complexity": "M",
"estimatedMinutes": 60,
"budgets": {
"maxImplementationFiles": 9,
"maxAffectedComponents": 2,
"maxPublicInterfaceChanges": 0,
"maxRuntimeDependencies": 0
},
"architecture": {
"status": "accepted",
"decision": "Use the managed immutable standard updater, retaining target-owned manifest settings and package bindings.",
"components": [
{
"name": "governance",
"paths": [
".githooks/pre-commit",
".governance/error/GOV-STANDARD-UPDATE.md",
".governance/manifest.base.json",
".governance/manifest.json",
".governance/manifest.lock.json",
".governance/worktree_overlap_check.py",
"pyproject.toml"
]
}
],
"responsibilityChanges": false,
"interfaceChanges": [],
"dataChanges": [],
"ui": {
"impact": "none",
"states": [],
"evidence": []
},
"rollback": "Review a subsequent immutable standard update; preserve previous pins in history."
},
"runtimeDependencies": [],
"standardAdoption": {
"sourceRepository": "wellmanifest/new-project",
"fromRevision": "2cd39286dc0931870a8a60ee11c96dd6448b39e6",
"toRevision": "bf3099667babd14ee778917d911d6c6bad45dcab"
},
"validation": [
{
"criterion": "AC-01",
"commands": [
"./project/governance-check.sh --actor agent"
],
"evidence": "receipt:local:ticket-089-local-ci-adoption"
}
]
}
}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ python_files = ["test_*.py"]
addopts = "-p wellmanifest_governance"

[tool.wellmanifest]
standard = "0.20.11"
revision = "2cd39286dc0931870a8a60ee11c96dd6448b39e6"
standard = "0.20.12"
revision = "bf3099667babd14ee778917d911d6c6bad45dcab"
gate = "project/governance-check.sh"

[tool.pfix]
Expand Down