feat: stack development in three phases - stack-dev (design), stack-build, stack-close, handed over through tracker states (#168)
CI / verify (push) Successful in 5m15s
CI / pwsh (push) Successful in 2m2s
Release / release (push) Successful in 34s

stack-build and stack-close carry disable-model-invocation, so each phase change is
the operator's slash command; no skill offers a mid-session /model or /effort switch.
Mode rules and the phase table move to instructions/dev/stack-mode.md, publish and CI
waiting to instructions/dev/publish-and-ci.md, the ready definition to issue-tracking.md.

Files changed:
- AGENTS.md
- CHANGES.md
- DEVELOPMENT.md
- README.md
- VERSION
- docs/model-and-effort-selection.md
- instructions/CONTRACT.md
- instructions/dev/commonplace-kb.md
- instructions/dev/dev-setup.md
- instructions/dev/doc-pull-through.md
- instructions/dev/issue-tracking.md
- instructions/dev/publish-and-ci.md
- instructions/dev/stack-build/SKILL.md
- instructions/dev/stack-close/SKILL.md
- instructions/dev/stack-dev/SKILL.md
- instructions/dev/stack-mode.md
- instructions/dev/testing-conventions.md
- instructions/dev/version-parts.md
- tools/chemenu/commands/git_publish.py
- tools/chemenu/tests/test_git_publish.py
- tools/chemenu/tests/test_instructions_cmd.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
torbenandClaude Opus 5.5 committed 2026-10-02 20:33:07 +02:00
1 parent 80488bee38
commit d8224ee2ab
21 files changed
+631 -331

No files matched your search

+9 -8
View File
@@ -393,20 +393,21 @@ STACK_MACHINERY_PREFIXES = ("tools/", "types/", "instructions/")
STACK_MACHINERY_NAMES = ("AGENTS.md",)
STACK_MACHINERY_NOTE = (
"Note: this publish touched stack machinery. What a stack-dev session "
"does next - closing prose, a changelog entry's accuracy, whether a "
"docs/ page went stale - is not covered by docs verify, instructions "
"verify, or pytest. No tool checks it; a session has to."
"Note: this publish touched stack machinery. CI on the pushed commit is "
"the last mechanical check still to come. What follows a green run - "
"closing prose, a changelog entry's accuracy, whether a docs/ page went "
"stale - is covered by no tool; a session has to check it."
)
def touches_stack_machinery(changed_files: list[str]) -> bool:
"""Whether `changed_files` includes a path under the stack version's own
scope - `tools/`, `types/`, `instructions/`, `AGENTS.md`, or a path ending
in `CONTRACT.md` at any depth. A publish in this class is, by construction
of the `stack-dev`/`stack-close` split, always followed by the unchecked
closing phase - `STACK_MACHINERY_NOTE` times a reminder to land exactly
there, for any session, not only one that read the skill that names it.
in `CONTRACT.md` at any depth. A publish in this class is followed first by
CI - still a mechanical check - and then by the unchecked closing phase of
the `stack-dev`/`stack-build`/`stack-close` split. `STACK_MACHINERY_NOTE`
names both, in that order, for any session, not only one that read the
skills that name them.
Deliberately a shade broader than CI's version gate, which matches
`<one-segment>/CONTRACT.md` only: this decides whether to print a sentence,
+12 -1
View File
@@ -395,7 +395,7 @@ def test_below_threshold_publish_goes_straight_through(repo):
def test_publish_notes_stack_machinery_after_success(repo, capsys):
"""The closing-phase reminder lands exactly once, after the OK line, and
"""The CI-then-closing-phase reminder lands exactly once, after the OK line, and
only when the changeset actually falls under version-parts.md's scope."""
(repo / "instructions").mkdir()
(repo / "instructions/example.md").write_text("x\n", encoding="utf-8")
@@ -405,6 +405,17 @@ def test_publish_notes_stack_machinery_after_success(repo, capsys):
assert out.count(STACK_MACHINERY_NOTE) == 1
def test_stack_machinery_note_puts_ci_before_the_unchecked_phase():
"""A stack publish is followed by CI first - a mechanical check - and only
after a green run by the phase nothing checks. The note must not claim the
unchecked phase starts right at the publish (#168): that sent CI waiting
into the closing phase, where a red run turned it back into a build."""
ci = STACK_MACHINERY_NOTE.find("CI")
unchecked = STACK_MACHINERY_NOTE.find("covered by no tool")
assert ci != -1 and unchecked != -1
assert ci < unchecked
def test_publish_stays_quiet_for_ordinary_content(repo, capsys):
_write_files(repo, 1)
_publish(message="ordinary content")
+17 -1
View File
@@ -64,7 +64,7 @@ def test_the_real_repo_publishes_every_skill():
"""Guards the actual layout, not a fixture: these are the skills the
harness is expected to offer, one per naming family - `wiki-` for the
knowledge pipeline, `gtd-` for the commitment layer, `stack-` for the
stack's own development. The `stack-` pair is nested under
stack's own development. The `stack-` trio is nested under
instructions/dev/, discovered the same way as the top-level ones."""
names = {p.name for p in instructions_cmd.skill_dirs()}
assert {
@@ -75,10 +75,26 @@ def test_the_real_repo_publishes_every_skill():
"wiki-status",
"gtd-weekly-review",
"stack-dev",
"stack-build",
"stack-close",
} <= names
def test_only_the_operator_starts_the_build_and_close_phases():
"""#168: the phase changes are the operator's slash command, so
`stack-build` and `stack-close` carry `disable-model-invocation: true`,
while `stack-dev` - the entry point the harness has to find on its own -
does not. `sync` copies the frontmatter byte for byte, which the drift
check in `verify` already holds the published copies to."""
skills = {p.name: p for p in instructions_cmd.skill_dirs()}
flags = {}
for name in ("stack-dev", "stack-build", "stack-close"):
frontmatter, error = instructions_cmd._read_frontmatter(skills[name] / "SKILL.md")
assert frontmatter is not None, error
flags[name] = frontmatter.get("disable-model-invocation", False)
assert flags == {"stack-dev": False, "stack-build": True, "stack-close": True}
_NO_PROVIDER_FORBIDDEN_TERMS = [
"super productivity",
"azure devops",