@opengsd/gsd-path 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/QUICK.md +1 -1
- package/README.md +10 -27
- package/package.json +1 -1
- package/scripts/isolation.py +20 -0
- package/scripts/lean_verification.py +5 -1
- package/skills/gsd-path/BUILD.md +3 -1
- package/skills/gsd-path/SHIP.md +2 -1
- package/skills/gsd-path/scripts/isolation.py +20 -0
- package/skills/gsd-path/scripts/lean_verification.py +5 -1
- package/skills/gsd-path-build/SKILL.md +3 -1
- package/skills/gsd-path-build/scripts/isolation.py +20 -0
- package/skills/gsd-path-decide/scripts/isolation.py +20 -0
- package/skills/gsd-path-define/scripts/isolation.py +20 -0
- package/skills/gsd-path-docs-audit/scripts/isolation.py +20 -0
- package/skills/gsd-path-inspect/scripts/isolation.py +20 -0
- package/skills/gsd-path-plan/scripts/isolation.py +20 -0
- package/skills/gsd-path-research/scripts/isolation.py +20 -0
- package/skills/gsd-path-roadmap/scripts/isolation.py +20 -0
- package/skills/gsd-path-ship/SKILL.md +2 -1
- package/skills/gsd-path-ship/scripts/isolation.py +20 -0
- package/skills/gsd-path-ship/scripts/lean_verification.py +5 -1
- package/skills/path/BUILD.md +3 -1
- package/skills/path/SHIP.md +2 -1
- package/skills/path/scripts/isolation.py +20 -0
- package/skills/path/scripts/lean_verification.py +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,19 @@ app and the daemon have their own file,
|
|
|
11
11
|
[daemon/app/CHANGELOG.md](daemon/app/CHANGELOG.md). Release bookkeeping and
|
|
12
12
|
test-only changes are not listed; the Git history has them.
|
|
13
13
|
|
|
14
|
+
## [1.6.2] - 2026-10-17
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- record 1.6.2 live host release evidence
|
|
18
|
+
- prepare 1.6.2 patch candidate
|
|
19
|
+
- Document README What's new heading in RELEASE.md
|
|
20
|
+
- update release notes for v1.6.1
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- keep the README What's new section current from release notes
|
|
24
|
+
- read quoted STATE values in the ship checkpoint guard
|
|
25
|
+
- refuse a build checkpoint while STATE is ship/active
|
|
26
|
+
- keep evidence-line citations when final review reuses a wave
|
|
14
27
|
## [1.6.1] - 2026-10-16
|
|
15
28
|
|
|
16
29
|
### Added
|
package/QUICK.md
CHANGED
|
@@ -14,7 +14,7 @@ One router skill drives a gated pipeline: inspect → define → research → de
|
|
|
14
14
|
plan → build → ship. Every handoff lives in `.project/` so any session can resume from disk.
|
|
15
15
|
|
|
16
16
|
**Hosts:** Codex, Claude Code, Grok, OpenCode, Antigravity, Cursor, and Kimi
|
|
17
|
-
Code have a passing live milestone for release 1.6.
|
|
17
|
+
Code have a passing live milestone for release 1.6.2. Copilot CLI, Qwen, Zed,
|
|
18
18
|
Kiro, and Muse Code install, but have no live milestone for that release. See
|
|
19
19
|
[host tiers](README.md).
|
|
20
20
|
|
package/README.md
CHANGED
|
@@ -9,11 +9,11 @@ milestone loop. While a milestone builds, the next one can be planned in
|
|
|
9
9
|
parallel under `.project/next/` (lookahead); a building milestone can also be
|
|
10
10
|
abandoned on an explicit ruling, archiving its partial work for a re-slice.
|
|
11
11
|
|
|
12
|
-
**Hosts with a passing full live milestone for release 1.6.
|
|
12
|
+
**Hosts with a passing full live milestone for release 1.6.2:** Codex, Claude
|
|
13
13
|
Code, Grok, OpenCode, Antigravity CLI, Cursor, and Kimi Code.
|
|
14
14
|
|
|
15
15
|
**Hosts with an installer and a dispatch contract, but no live milestone for
|
|
16
|
-
release 1.6.
|
|
16
|
+
release 1.6.2:** GitHub Copilot CLI (account quota exhausted), Qwen Code, Zed,
|
|
17
17
|
and Kiro (API credits), and Muse Code (no live release evidence recorded yet).
|
|
18
18
|
These hosts are excluded from the release evaluation. Exclusion is not a pass.
|
|
19
19
|
|
|
@@ -24,14 +24,16 @@ The [host trust matrix](https://github.com/open-gsd/gsd-path/blob/main/docs/trus
|
|
|
24
24
|
is the source of truth for live milestone proof and each host's guard tier.
|
|
25
25
|
|
|
26
26
|
<!-- release-docs -->
|
|
27
|
-
|
|
27
|
+
## What's new in 1.6.2 — 2026-10-17
|
|
28
|
+
|
|
29
|
+
**Latest npm release:** [@opengsd/gsd-path@1.6.2](https://www.npmjs.com/package/@opengsd/gsd-path/v/1.6.2) — [release notes](CHANGELOG.md#162---2026-10-17)
|
|
28
30
|
|
|
29
31
|
**Recent highlights**
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- refuse a
|
|
33
|
-
-
|
|
34
|
-
-
|
|
32
|
+
- keep the README What's new section current from release notes
|
|
33
|
+
- read quoted STATE values in the ship checkpoint guard
|
|
34
|
+
- refuse a build checkpoint while STATE is ship/active
|
|
35
|
+
- keep evidence-line citations when final review reuses a wave
|
|
36
|
+
- record 1.6.2 live host release evidence
|
|
35
37
|
|
|
36
38
|
<!-- /release-docs -->
|
|
37
39
|
|
|
@@ -55,25 +57,6 @@ is the source of truth for live milestone proof and each host's guard tier.
|
|
|
55
57
|
| [RELEASE.md](RELEASE.md) | Maintainer CI and npm release cycle |
|
|
56
58
|
| [GUIDE.md](GUIDE.md) | Pointer to the guides above |
|
|
57
59
|
|
|
58
|
-
## What's new — October 2, 2026
|
|
59
|
-
|
|
60
|
-
These changes are merged into the source checkout; npm installs use the latest
|
|
61
|
-
published release.
|
|
62
|
-
|
|
63
|
-
- **Desktop app (pre-release):** one app for macOS, Windows, and Linux shows
|
|
64
|
-
your projects, manages skills in each coding agent, and updates itself. See
|
|
65
|
-
[Desktop app](#desktop-app).
|
|
66
|
-
- **Skills from the published release:** the monitor installs skills from the
|
|
67
|
-
npm release, checks the download against the registry's sha512, and refuses
|
|
68
|
-
one that does not match. A release picker lets you stay on an older version.
|
|
69
|
-
- **Safer monitor writes:** a monitor started by the app accepts changes only
|
|
70
|
-
with the app's local token, so another web page cannot change your setup.
|
|
71
|
-
- **Environment editor:** edit a project's `.env` files with values masked and
|
|
72
|
-
a diff before each save.
|
|
73
|
-
- **Usage charts:** tokens and cost per day, time per phase, tasks per wave,
|
|
74
|
-
and verify history. Missing data shows as missing, never as zero.
|
|
75
|
-
- **Muse Code** is a supported host.
|
|
76
|
-
|
|
77
60
|
## Install from npm
|
|
78
61
|
|
|
79
62
|
Requires **Node.js 18.17+** for the npm installer and **Python 3.9+** for
|
package/package.json
CHANGED
package/scripts/isolation.py
CHANGED
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -108,10 +108,14 @@ def _final_view(relative, text, criteria, reviewed, head, surfaces=None):
|
|
|
108
108
|
fields[field] = contracts._raw_source_field(block, field, relative)
|
|
109
109
|
if "Surface" in fields and not {"Check", "Observed"}.issubset(fields):
|
|
110
110
|
raise contracts.HandoffError(f"{criterion} lacks its recorded surface walkthrough")
|
|
111
|
+
reference = f"{relative} — {criterion}"
|
|
112
|
+
if "Observed" in fields and evidence:
|
|
113
|
+
# The reviewer may cite held-out and judgment ids on the evidence line.
|
|
114
|
+
reference += f"; evidence: {evidence}"
|
|
111
115
|
lines += [f"### {criterion} — {title}", "", "- **Verdict**: met",
|
|
112
116
|
f"- **Check**: {fields.get('Check', 'none')}",
|
|
113
117
|
f"- **Observed**: {fields.get('Observed', evidence)}",
|
|
114
|
-
f"- **Reference**: {
|
|
118
|
+
f"- **Reference**: {reference}",
|
|
115
119
|
"- **Finding**: none", "- **Fix direction**: none"]
|
|
116
120
|
if "Surface" in fields:
|
|
117
121
|
expected = (surfaces or {}).get(criterion)
|
package/skills/gsd-path/BUILD.md
CHANGED
|
@@ -130,7 +130,9 @@ STATE.md-only "finalize" checkpoint for work already checkpointed. The helper
|
|
|
130
130
|
refuses a `build:` checkpoint that deletes or renames a `.project/tasks/`
|
|
131
131
|
brief unless the canonical `build: abandon milestone <slug>` checkpoint moves
|
|
132
132
|
that same brief into `.project/archive/`; build adds fix tasks and repairs
|
|
133
|
-
briefs, it never replans.
|
|
133
|
+
briefs, it never replans. It also refuses a `build:` checkpoint while the
|
|
134
|
+
committed STATE is `ship/active`: final-review outputs stay uncommitted until
|
|
135
|
+
the ship commit.
|
|
134
136
|
|
|
135
137
|
## Execute from runtime receipts
|
|
136
138
|
|
package/skills/gsd-path/SHIP.md
CHANGED
|
@@ -69,7 +69,8 @@ so the position is unchanged. Then:
|
|
|
69
69
|
task logs prove it is the immediately preceding `ship/blocked` finding
|
|
70
70
|
set, the approved patch tasks copied every finding verbatim, and those tasks
|
|
71
71
|
are now done; otherwise it blocks. Git history preserves the superseded
|
|
72
|
-
finding, while final review itself still creates no pre-ship commit
|
|
72
|
+
finding, while final review itself still creates no pre-ship commit
|
|
73
|
+
(`isolation.py checkpoint` refuses a `build:` checkpoint at `ship/active`).
|
|
73
74
|
2. Derive a stable numbered list of cross-wave integration risks from
|
|
74
75
|
interfaces and flows that span waves. List only genuine risks that could
|
|
75
76
|
plausibly fail; never pad the list. Include PLAN.md's project Verify as
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -108,10 +108,14 @@ def _final_view(relative, text, criteria, reviewed, head, surfaces=None):
|
|
|
108
108
|
fields[field] = contracts._raw_source_field(block, field, relative)
|
|
109
109
|
if "Surface" in fields and not {"Check", "Observed"}.issubset(fields):
|
|
110
110
|
raise contracts.HandoffError(f"{criterion} lacks its recorded surface walkthrough")
|
|
111
|
+
reference = f"{relative} — {criterion}"
|
|
112
|
+
if "Observed" in fields and evidence:
|
|
113
|
+
# The reviewer may cite held-out and judgment ids on the evidence line.
|
|
114
|
+
reference += f"; evidence: {evidence}"
|
|
111
115
|
lines += [f"### {criterion} — {title}", "", "- **Verdict**: met",
|
|
112
116
|
f"- **Check**: {fields.get('Check', 'none')}",
|
|
113
117
|
f"- **Observed**: {fields.get('Observed', evidence)}",
|
|
114
|
-
f"- **Reference**: {
|
|
118
|
+
f"- **Reference**: {reference}",
|
|
115
119
|
"- **Finding**: none", "- **Fix direction**: none"]
|
|
116
120
|
if "Surface" in fields:
|
|
117
121
|
expected = (surfaces or {}).get(criterion)
|
|
@@ -130,7 +130,9 @@ STATE.md-only "finalize" checkpoint for work already checkpointed. The helper
|
|
|
130
130
|
refuses a `build:` checkpoint that deletes or renames a `.project/tasks/`
|
|
131
131
|
brief unless the canonical `build: abandon milestone <slug>` checkpoint moves
|
|
132
132
|
that same brief into `.project/archive/`; build adds fix tasks and repairs
|
|
133
|
-
briefs, it never replans.
|
|
133
|
+
briefs, it never replans. It also refuses a `build:` checkpoint while the
|
|
134
|
+
committed STATE is `ship/active`: final-review outputs stay uncommitted until
|
|
135
|
+
the ship commit.
|
|
134
136
|
|
|
135
137
|
## Execute from runtime receipts
|
|
136
138
|
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -69,7 +69,8 @@ so the position is unchanged. Then:
|
|
|
69
69
|
task logs prove it is the immediately preceding `ship/blocked` finding
|
|
70
70
|
set, the approved patch tasks copied every finding verbatim, and those tasks
|
|
71
71
|
are now done; otherwise it blocks. Git history preserves the superseded
|
|
72
|
-
finding, while final review itself still creates no pre-ship commit
|
|
72
|
+
finding, while final review itself still creates no pre-ship commit
|
|
73
|
+
(`isolation.py checkpoint` refuses a `build:` checkpoint at `ship/active`).
|
|
73
74
|
2. Derive a stable numbered list of cross-wave integration risks from
|
|
74
75
|
interfaces and flows that span waves. List only genuine risks that could
|
|
75
76
|
plausibly fail; never pad the list. Include PLAN.md's project Verify as
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -108,10 +108,14 @@ def _final_view(relative, text, criteria, reviewed, head, surfaces=None):
|
|
|
108
108
|
fields[field] = contracts._raw_source_field(block, field, relative)
|
|
109
109
|
if "Surface" in fields and not {"Check", "Observed"}.issubset(fields):
|
|
110
110
|
raise contracts.HandoffError(f"{criterion} lacks its recorded surface walkthrough")
|
|
111
|
+
reference = f"{relative} — {criterion}"
|
|
112
|
+
if "Observed" in fields and evidence:
|
|
113
|
+
# The reviewer may cite held-out and judgment ids on the evidence line.
|
|
114
|
+
reference += f"; evidence: {evidence}"
|
|
111
115
|
lines += [f"### {criterion} — {title}", "", "- **Verdict**: met",
|
|
112
116
|
f"- **Check**: {fields.get('Check', 'none')}",
|
|
113
117
|
f"- **Observed**: {fields.get('Observed', evidence)}",
|
|
114
|
-
f"- **Reference**: {
|
|
118
|
+
f"- **Reference**: {reference}",
|
|
115
119
|
"- **Finding**: none", "- **Fix direction**: none"]
|
|
116
120
|
if "Surface" in fields:
|
|
117
121
|
expected = (surfaces or {}).get(criterion)
|
package/skills/path/BUILD.md
CHANGED
|
@@ -130,7 +130,9 @@ STATE.md-only "finalize" checkpoint for work already checkpointed. The helper
|
|
|
130
130
|
refuses a `build:` checkpoint that deletes or renames a `.project/tasks/`
|
|
131
131
|
brief unless the canonical `build: abandon milestone <slug>` checkpoint moves
|
|
132
132
|
that same brief into `.project/archive/`; build adds fix tasks and repairs
|
|
133
|
-
briefs, it never replans.
|
|
133
|
+
briefs, it never replans. It also refuses a `build:` checkpoint while the
|
|
134
|
+
committed STATE is `ship/active`: final-review outputs stay uncommitted until
|
|
135
|
+
the ship commit.
|
|
134
136
|
|
|
135
137
|
## Execute from runtime receipts
|
|
136
138
|
|
package/skills/path/SHIP.md
CHANGED
|
@@ -69,7 +69,8 @@ so the position is unchanged. Then:
|
|
|
69
69
|
task logs prove it is the immediately preceding `ship/blocked` finding
|
|
70
70
|
set, the approved patch tasks copied every finding verbatim, and those tasks
|
|
71
71
|
are now done; otherwise it blocks. Git history preserves the superseded
|
|
72
|
-
finding, while final review itself still creates no pre-ship commit
|
|
72
|
+
finding, while final review itself still creates no pre-ship commit
|
|
73
|
+
(`isolation.py checkpoint` refuses a `build:` checkpoint at `ship/active`).
|
|
73
74
|
2. Derive a stable numbered list of cross-wave integration risks from
|
|
74
75
|
interfaces and flows that span waves. List only genuine risks that could
|
|
75
76
|
plausibly fail; never pad the list. Include PLAN.md's project Verify as
|
|
@@ -4502,6 +4502,20 @@ def attest(
|
|
|
4502
4502
|
}
|
|
4503
4503
|
|
|
4504
4504
|
|
|
4505
|
+
def _committed_ship_active(repo: Path) -> bool:
|
|
4506
|
+
"""True when the STATE.md committed at HEAD is ship/active."""
|
|
4507
|
+
|
|
4508
|
+
result = run_git(repo, "show", "HEAD:.project/STATE.md")
|
|
4509
|
+
if result.returncode != 0:
|
|
4510
|
+
return False
|
|
4511
|
+
frontmatter = result.stdout.split("\n---", 1)[0]
|
|
4512
|
+
fields = {
|
|
4513
|
+
key: _unquote(value)
|
|
4514
|
+
for key, value in re.findall(r"(?m)^(phase|status):(.*)$", frontmatter)
|
|
4515
|
+
}
|
|
4516
|
+
return fields.get("phase") == "ship" and fields.get("status") == "active"
|
|
4517
|
+
|
|
4518
|
+
|
|
4505
4519
|
def checkpoint(
|
|
4506
4520
|
repo: Path,
|
|
4507
4521
|
expected_head: str,
|
|
@@ -4551,6 +4565,12 @@ def checkpoint(
|
|
|
4551
4565
|
"subject": subject.strip(),
|
|
4552
4566
|
}
|
|
4553
4567
|
raise IsolationError("primary HEAD differs from --expected-head")
|
|
4568
|
+
if subject.strip().startswith("build:") and _committed_ship_active(repo):
|
|
4569
|
+
raise IsolationError(
|
|
4570
|
+
"STATE is ship/active: final review creates no pre-ship commit; leave its "
|
|
4571
|
+
"outputs uncommitted for archive_milestone.py, and send findings through "
|
|
4572
|
+
"the patch plan (see SHIP.md)"
|
|
4573
|
+
)
|
|
4554
4574
|
pending = uncommitted_paths(repo)
|
|
4555
4575
|
forced: Set[str] = set()
|
|
4556
4576
|
# Build only: an approval undo resets to a parent without the ledger and would drop it.
|
|
@@ -108,10 +108,14 @@ def _final_view(relative, text, criteria, reviewed, head, surfaces=None):
|
|
|
108
108
|
fields[field] = contracts._raw_source_field(block, field, relative)
|
|
109
109
|
if "Surface" in fields and not {"Check", "Observed"}.issubset(fields):
|
|
110
110
|
raise contracts.HandoffError(f"{criterion} lacks its recorded surface walkthrough")
|
|
111
|
+
reference = f"{relative} — {criterion}"
|
|
112
|
+
if "Observed" in fields and evidence:
|
|
113
|
+
# The reviewer may cite held-out and judgment ids on the evidence line.
|
|
114
|
+
reference += f"; evidence: {evidence}"
|
|
111
115
|
lines += [f"### {criterion} — {title}", "", "- **Verdict**: met",
|
|
112
116
|
f"- **Check**: {fields.get('Check', 'none')}",
|
|
113
117
|
f"- **Observed**: {fields.get('Observed', evidence)}",
|
|
114
|
-
f"- **Reference**: {
|
|
118
|
+
f"- **Reference**: {reference}",
|
|
115
119
|
"- **Finding**: none", "- **Fix direction**: none"]
|
|
116
120
|
if "Surface" in fields:
|
|
117
121
|
expected = (surfaces or {}).get(criterion)
|