project-guide 2.7.0__tar.gz → 2.7.1__tar.gz
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.
- {project_guide-2.7.0 → project_guide-2.7.1}/CHANGELOG.md +21 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/PKG-INFO +1 -1
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/features.md +9 -1
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/project-essentials.md +9 -3
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/stories.md +47 -1
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/tech-spec.md +16 -6
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/cli.py +52 -30
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/version.py +1 -1
- {project_guide-2.7.0 → project_guide-2.7.1}/pyproject.toml +1 -1
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_cli.py +56 -21
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/FUNDING.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/dependabot.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/workflows/ci.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/workflows/deploy-docs.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/workflows/publish.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.github/workflows/test.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.gitignore +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.project-guide.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.pyve/config +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/.tool-versions +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/CONTRIBUTING.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/LICENSE +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/README.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/SECURITY.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/.gitignore +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/about/changelog.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/about/license.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/developer-guide/contributing.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/developer-guide/development.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/developer-guide/testing.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/getting-started.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/images/project-guide-banner-landing.png +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/images/project-guide-header-readme.png +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/index.html +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/commands.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/configuration.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/install-options.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/modes.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/overrides.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/site/user-guide/workflow.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-j-modes-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-k-release-lifecycle-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-l-no-input-init-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-m-project-essentials-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-n-mode-naming-cli-memory-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-o-pyve-quiet-embedding-subplan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-o-quiet-non-interactive-embedding-feature.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/project-guide-no-input-spec.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/stories-v1.3.1.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/stories-v2.0.20.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/stories-v2.3.9.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/stories-v2.4.19.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/stories-v2.5.15.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/ux-problems-v2.0.10.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/brand-descriptions.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/concept.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/phase-p-auto-heal-plan.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/mkdocs.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project-guide-old-template.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/__init__.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/__main__.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/actions.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/config.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/exceptions.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/metadata.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/render.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/runtime.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/stories.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/sync.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/.project-guide.yml.template +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/.metadata.yml +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/README.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/best-practices-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/brand-descriptions-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/codecov-setup-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/debug-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/landing-page-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/production-github-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/project-guide.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/developer/python-editable-install.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/brand-descriptions.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/concept.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/features.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/project-essentials.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/pyve-essentials.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/stories.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/artifacts/tech-spec.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/llm_entry_point.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/_header-common.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/_header-cycle.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/_header-sequence.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/_phase-letters.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/archive-stories-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/code-direct-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/code-test-first-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/debug-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/default-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/document-brand-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/document-landing-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-concept-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-features-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-phase-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-production-phase-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-stories-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/plan-tech-spec-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/refactor-document-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/refactor-plan-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/templates/modes/scaffold-project-mode.md +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/requirements-dev.txt +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/__init__.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/conftest.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_actions.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_archive_stories_mode.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_config.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_integration.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_metadata.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_purge.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_render.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_runtime.py +0 -0
- {project_guide-2.7.0 → project_guide-2.7.1}/tests/test_sync.py +0 -0
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.7.1] - 2026-05-11
|
|
11
|
+
|
|
12
|
+
Compatibility fix for IDE-integrated LLM @-mention / fuzzy-search. The v2.6.0–v2.7.0 gitignore block used a clean `<target>/**` + `!<target>/go.md` negation pair — correct per the `.gitignore` spec, but **several IDE tools** (Cursor, parts of the VS Code fork ecosystem, certain LSP-based search backends) implement a subset of gitignore semantics that does not honor re-include negation. Those tools applied the broad `**` rule, hid `go.md`, and defeated the IDE-LLM-visibility constraint that's the entire reason `go.md` is tracked. P.l switches to a negation-free explicit-list form so simplistic parsers handle it reliably.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **`project_guide/cli.py:_build_project_guide_block()`** — rewritten to enumerate the bundled template root (`_get_package_template_dir()`) and emit one anchored `/<target>/<child>[/]` line per top-level entry other than `go.md`, plus a trailing `/<target>/**/*.bak.*` defensive catch-all for top-level backups. The list is dynamic — new top-level files/directories in the bundled tree are picked up automatically by both the writer and the test helper that mirrors it. Default install now writes:
|
|
16
|
+
```
|
|
17
|
+
# project-guide
|
|
18
|
+
/docs/project-guide/.metadata.yml
|
|
19
|
+
/docs/project-guide/README.md
|
|
20
|
+
/docs/project-guide/developer/
|
|
21
|
+
/docs/project-guide/templates/
|
|
22
|
+
/docs/project-guide/**/*.bak.*
|
|
23
|
+
```
|
|
24
|
+
- **`project_guide/cli.py`** — `_recognized_block_lines()` replaced with `_is_recognized_block_line(line, target_dir)` predicate. Accepts the v2.7.1+ form (any line anchored at `/<target>/`) plus every prior legacy line (`<target>/**`, `!<target>/go.md`, `<target>/**/*.bak.*`, `<target>/go.md`). Existing v2.6.x/v2.7.0 installs heal cleanly to the v2.7.1 form on `init --force`; no multi-step migration required.
|
|
25
|
+
- **Tests** — `_EXPECTED_GITIGNORE_BLOCK` constant replaced with `_expected_gitignore_block()` helper that mirrors the writer's enumeration; new `test_init_force_rewrites_v261_three_line_block_to_explicit_list` for the v2.6.1/v2.7.0 → v2.7.1 migration path; the foreign-block test now seeds a line not anchored at `/<target>/` so the predicate correctly flags it as foreign.
|
|
26
|
+
- **Docs** — updated the gitignore prose in `docs/specs/features.md`, `docs/specs/tech-spec.md`, and `docs/specs/project-essentials.md` to document the three-version evolution (v2.6.0 → v2.6.1 → v2.7.1) and the "do not simplify back to negation" warning for future maintainers.
|
|
27
|
+
|
|
28
|
+
### Migration
|
|
29
|
+
None required at the consumer level. The v2.6.x/v2.7.0 negation form and the v2.7.1 explicit-list form produce identical git-tracking outcomes (only `go.md` is tracked). The behavior change is purely in what `_ensure_gitignore_entry()` writes. Consumers whose IDE handles negation correctly may keep their existing block indefinitely; only consumers hitting the IDE bug actually benefit from running `project-guide init --force` to adopt the new form.
|
|
30
|
+
|
|
10
31
|
## [2.7.0] - 2026-05-11
|
|
11
32
|
|
|
12
33
|
New top-level `project-guide git-push` command — a thin wrapper over [gitbetter](https://github.com/pointmatic/gitbetter)'s `git-push` that auto-derives the commit subject from the most-recently-completed-and-not-yet-committed story in `docs/specs/stories.md`. Developer-lane convenience only; the LLM still does not initiate commits.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: project-guide
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.1
|
|
4
4
|
Summary: Stay organized and in control with adaptive LLM workflow prompts.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pointmatic/project-guide
|
|
6
6
|
Project-URL: Documentation, https://pointmatic.github.io/project-guide/
|
|
@@ -376,7 +376,15 @@ The bundled `templates/artifacts/pyve-essentials.md` artifact covers: two-enviro
|
|
|
376
376
|
- `.project-guide.yml` is absent (let `init` bootstrap; the hook does not error).
|
|
377
377
|
- The config fails to load (schema mismatch, parse error) — the subcommand surfaces the error with its own guidance.
|
|
378
378
|
|
|
379
|
-
**Inverted gitignore policy.** `init`'s gitignore writer produces a canonical
|
|
379
|
+
**Inverted gitignore policy.** `init`'s gitignore writer produces a canonical block under a `# project-guide` header that ignores everything under `target_dir` *except* `go.md`. The block has gone through three shapes (P.d → P.j → P.l):
|
|
380
|
+
|
|
381
|
+
- **v2.6.0 (P.d):** 4-line negation form (`<target>/**` + `!<target>/go.md` + redundant `<target>/**/*.bak.*`).
|
|
382
|
+
- **v2.6.1 (P.j):** 3-line negation form — dropped the redundant `.bak.*` line.
|
|
383
|
+
- **v2.7.1 (P.l):** **negation-free explicit-list form** — lists every top-level entry under `target_dir` other than `go.md`, plus a `<target>/**/*.bak.*` catch-all for top-level backups. The list is generated dynamically from the bundled template tree, so new top-level files/subdirectories added in future releases are picked up automatically.
|
|
384
|
+
|
|
385
|
+
P.l abandoned the negation form because several IDE-integrated tools (Cursor, parts of the VS Code fork ecosystem, certain LSP-based search backends) implement a subset of `.gitignore` semantics that does not honor re-include negation — they apply the broad `**` rule, hide `go.md` from @-mention / fuzzy-search, and defeat the IDE-LLM-visibility constraint that's the whole reason `go.md` is tracked.
|
|
386
|
+
|
|
387
|
+
Consumers migrating from a pre-Phase-P install run `project-guide init --force` to refresh the gitignore block; `git rm --cached` is the manual cleanup for previously tracked files. Existing v2.6.x/v2.7.0 installs heal to the v2.7.1 explicit-list form on the next `init --force` — every prior shape stays recognized by `_is_recognized_block_line()`. The track-only-`go.md` policy is unchanged across all three shapes; only the syntax that expresses it differs.
|
|
380
388
|
|
|
381
389
|
### FR-15: Story-Aware `git-push` Wrapper (gitbetter integration)
|
|
382
390
|
|
|
@@ -63,11 +63,17 @@ The hook is silent in the steady state (no drift → no output). It is recursion
|
|
|
63
63
|
|
|
64
64
|
**Skip conditions:** the hook returns silently when `PROJECT_GUIDE_HEALING=1` is set, when `.project-guide.yml` is absent (let `init` bootstrap; `heal` would error otherwise), or when config load / drift detection fails. Missing `.project-guide.yml` is a hard error from the **`heal` subcommand itself** but a silent skip from the **hook** — the original subcommand surfaces the missing-config error with its own guidance.
|
|
65
65
|
|
|
66
|
-
### Inverted gitignore policy (added v2.6.0, tightened v2.6.1)
|
|
66
|
+
### Inverted gitignore policy (added v2.6.0, tightened v2.6.1, IDE-compat reshape v2.7.1)
|
|
67
67
|
|
|
68
|
-
`init`'s gitignore writer produces a canonical
|
|
68
|
+
`init`'s gitignore writer produces a canonical block that ignores everything under `target_dir` *except* `go.md`. The policy has gone through three shapes:
|
|
69
69
|
|
|
70
|
-
**
|
|
70
|
+
- **v2.6.0 (P.d):** 4-line negation form (`<target>/**` + `!<target>/go.md` + redundant `<target>/**/*.bak.*`).
|
|
71
|
+
- **v2.6.1 (P.j):** 3-line negation form — dropped the redundant `.bak.*` line.
|
|
72
|
+
- **v2.7.1 (P.l):** **negation-free explicit-list form** — lists every top-level entry under `target_dir` other than `go.md`, plus a `<target>/**/*.bak.*` defensive catch-all. The list is dynamically enumerated from the bundled template root at write time, so new top-level files/directories added in future stories are picked up automatically.
|
|
73
|
+
|
|
74
|
+
P.l abandoned negation because several IDE-integrated tools (Cursor, parts of the VS Code fork ecosystem, certain LSP-based search backends) implement a subset of `.gitignore` semantics that does **not** honor re-include negation — they apply the broad `**` rule, hide `go.md` from @-mention / fuzzy-search, and defeat the IDE-LLM-visibility constraint the policy is trying to enforce. **Future maintainers: do not "simplify" back to `**` + `!` — the regression is invisible from git's perspective but breaks the IDE workflow.**
|
|
75
|
+
|
|
76
|
+
**Idempotent rewrite:** `_ensure_gitignore_entry()` uses `_is_recognized_block_line(line, target_dir)` as its "ours-vs-foreign" predicate. It accepts anything anchored at `/<target>/` (the v2.7.1+ form) plus every prior legacy form (v2.6.1 3-line, v2.6.0 4-line, pre-P.d `.bak.*`-only, legacy `<target>/go.md`). Any block whose lines all satisfy the predicate is rewritten cleanly to the current canonical form. A foreign hand-customized block under a `# project-guide` header is left untouched with a stderr warning; migrate manually or run `init --force`.
|
|
71
77
|
|
|
72
78
|
### IDE-LLM visibility constraint (added v2.6.0)
|
|
73
79
|
|
|
@@ -34,7 +34,7 @@ Collapse the `docs/project-guide/` install footprint in consumer repos to a sing
|
|
|
34
34
|
|
|
35
35
|
**Implementation order:** `heal` core (P.a) → auto-hook (P.b) → `--no-input` semantics (P.c) → gitignore template flip (P.d). Production hardening items (P.e–P.h) are independent and can ship in any order. P.i is the doc-only story that bundles the v2.6.0 release.
|
|
36
36
|
|
|
37
|
-
**Version cadence:** phase-bundled — stories P.a–P.i ran unversioned and shipped together as **v2.6.0** (P.i owned the single bundled bump). **Stories P.j and P.
|
|
37
|
+
**Version cadence:** phase-bundled — stories P.a–P.i ran unversioned and shipped together as **v2.6.0** (P.i owned the single bundled bump). **Stories P.j, P.k, and P.l were added post-bundle** and follow standard per-story cadence: **P.j → v2.6.1** (patch — gitignore-block tightening, a fix-up to P.d), **P.k → v2.7.0** (minor — new `git-push` wrapper command), **P.l → v2.7.1** (patch — switch the gitignore block from negation to explicit-list form for IDE compatibility). Phases can be extended after their bundled release, but new stories then follow the standard per-story cadence rather than rejoining the (already-shipped) bundle.
|
|
38
38
|
|
|
39
39
|
### Story P.a: Heal command with drift detection and create-missing semantics [Done]
|
|
40
40
|
|
|
@@ -262,6 +262,52 @@ This is a developer-lane convenience command. **The LLM still does not initiate
|
|
|
262
262
|
- A parallel `project-guide git-tag` wrapper. gitbetter has one but the project's release process already uses `bump-version` + raw `git tag`, so the value is lower.
|
|
263
263
|
- Pre-flight `pyve test` / `ruff check` gates before invoking gitbetter. The developer runs those during the story's normal cycle before marking `[Done]`, so re-running them at push time is redundant.
|
|
264
264
|
|
|
265
|
+
### Story P.l: v2.7.1 Negation-free gitignore for IDE LLM compatibility [Done]
|
|
266
|
+
|
|
267
|
+
Switch the canonical `# project-guide` gitignore block from the v2.6.1/v2.7.0 negation form (`<target>/**` + `!<target>/go.md`) to a **negation-free explicit-list** form. Motivation: several IDE-integrated tools (Cursor, parts of the VS Code fork ecosystem, certain LSP-based search backends) implement a subset of `.gitignore` semantics that does not honor re-include negation — they apply the broad `**` rule, hide `go.md` from @-mention and fuzzy-search, and defeat the IDE-LLM-visibility constraint that's the entire reason `go.md` is tracked.
|
|
268
|
+
|
|
269
|
+
The new canonical form lists every gitignored top-level entry explicitly so no negation is required. The list is **dynamically generated** from the bundled template root, so new top-level files or subdirectories added in future stories are picked up automatically — no manual maintenance.
|
|
270
|
+
|
|
271
|
+
- [x] In `project_guide/cli.py:_build_project_guide_block()`: rewrite to enumerate `_get_package_template_dir()` and emit one `/<target>/<child>[/]` line per top-level entry except `go.md`, plus the existing `/<target>/**/*.bak.*` backup-catch-all. New canonical form for the default install layout:
|
|
272
|
+
```
|
|
273
|
+
# project-guide
|
|
274
|
+
/docs/project-guide/.metadata.yml
|
|
275
|
+
/docs/project-guide/README.md
|
|
276
|
+
/docs/project-guide/developer/
|
|
277
|
+
/docs/project-guide/templates/
|
|
278
|
+
/docs/project-guide/**/*.bak.*
|
|
279
|
+
```
|
|
280
|
+
Use a leading slash to anchor each rule at repo root; trailing slash on directories. Children sorted for deterministic output.
|
|
281
|
+
- [x] In `project_guide/cli.py`: update the recognized-block check to accept every form we've ever written: *(replaced `_recognized_block_lines()` with a `_is_recognized_block_line(line, target_dir)` predicate — cleaner because the v2.7.1+ "anything anchored at `/<target>/`" rule isn't expressible as a fixed set)*
|
|
282
|
+
- [x] New v2.7.1+ form: any line starting with `/<target>/`
|
|
283
|
+
- [x] v2.6.1 form: `<target>/**`, `!<target>/go.md`
|
|
284
|
+
- [x] v2.6.0 form: `<target>/**`, `!<target>/go.md`, `<target>/**/*.bak.*`
|
|
285
|
+
- [x] pre-P.d form: `<target>/**/*.bak.*` only
|
|
286
|
+
- [x] Legacy `<target>/go.md` line
|
|
287
|
+
- [x] In `tests/test_cli.py`:
|
|
288
|
+
- [x] Compute the expected block from the bundled tree via `_expected_gitignore_block()` helper instead of a hardcoded constant
|
|
289
|
+
- [x] Add `test_init_force_rewrites_v261_three_line_block_to_explicit_list` for the v2.6.1/v2.7.0 → v2.7.1 migration
|
|
290
|
+
- [x] Renamed the v2.6.0 → v2.7.1 migration test to `test_init_force_rewrites_v260_four_line_block_to_explicit_list` (was the v2.6.1-form rewrite test)
|
|
291
|
+
- [x] Foreign-block warning test updated to use a line not anchored at `/<target>/` so the new predicate flags it correctly
|
|
292
|
+
- [x] Idempotency test passes against the new canonical form (the seed builds from `_expected_gitignore_block()`)
|
|
293
|
+
- [x] All prior P.j/P.d migration tests continue to pass
|
|
294
|
+
- [x] Doc updates (bundled in this story):
|
|
295
|
+
- [x] `docs/specs/features.md` FR-14: amended the "Inverted gitignore policy" paragraph with the three-version evolution (v2.6.0 → v2.6.1 → v2.7.1) and the IDE-compat rationale
|
|
296
|
+
- [x] `docs/specs/tech-spec.md` § `.gitignore Management`: replaced the negation code block with the explicit-list form, added a "Why explicit list instead of `**` + `!go.md`?" paragraph plus an explicit "do not simplify back" warning, and a "Why the trailing `<target>/**/*.bak.*` line?" paragraph
|
|
297
|
+
- [x] `docs/specs/project-essentials.md`: amended the "Inverted gitignore policy" sub-section to cover the v2.7.1 form change and the "future maintainers: do not simplify back to `**` + `!`" warning
|
|
298
|
+
- [x] `README.md`: no user-facing prose changes needed (Quick Start footnote still reads correctly — "ignored except go.md")
|
|
299
|
+
- [x] Bump `project_guide/version.py`: `__version__ = "2.7.1"`
|
|
300
|
+
- [x] Bump `pyproject.toml`: `version = "2.7.1"`
|
|
301
|
+
- [x] Add `## [2.7.1] - <date>` CHANGELOG entry framed as a compatibility fix
|
|
302
|
+
|
|
303
|
+
**Migration:** none required by consumers. The new and old blocks produce identical git-tracking outcomes (only `go.md` is tracked). The behavior change is purely in what `_ensure_gitignore_entry()` writes. Existing v2.6.x/v2.7.0 installs heal to the v2.7.1 form on `init --force` because all prior shapes remain recognized. Consumers whose IDE handles negation correctly can keep their existing block indefinitely — only consumers hitting the IDE bug actually need to migrate.
|
|
304
|
+
|
|
305
|
+
**Why dynamic enumeration:** hardcoding the install footprint in `_build_project_guide_block()` means every new top-level template file or subdirectory requires a writer-code update. Enumerating `_get_package_template_dir()` at write time keeps the canonical block in sync with the bundled tree automatically. The trade-off is that the writer now reads from the package — not a real concern since `init` already does the same to copy the template tree.
|
|
306
|
+
|
|
307
|
+
**Out of scope:**
|
|
308
|
+
- An opt-back-in flag (`--gitignore-style=negation`). YAGNI until someone asks; the new form is strictly better for the documented IDE-LLM-visibility constraint.
|
|
309
|
+
- A `project-guide check` integrity rule that detects "consumer has tracked-but-should-be-ignored files under `target_dir`". Defer until there's a second integrity rule worth shipping (see Future > Integrity & Validation).
|
|
310
|
+
|
|
265
311
|
---
|
|
266
312
|
|
|
267
313
|
## Future
|
|
@@ -128,7 +128,7 @@ project-guide/
|
|
|
128
128
|
| `purge` | Remove all project-guide files with confirmation |
|
|
129
129
|
|
|
130
130
|
**Key functions:**
|
|
131
|
-
- `_ensure_gitignore_entry(target_dir)` — writes the canonical `# project-guide` block: ignore everything under `target_dir` except `go.md` (
|
|
131
|
+
- `_ensure_gitignore_entry(target_dir)` — writes the canonical `# project-guide` block: ignore everything under `target_dir` except `go.md` (negation-free explicit-list form as of P.l / v2.7.1, dynamically enumerated from the bundled template root). Idempotent. Recognized prior blocks (pre-P.d `.bak.*`-only form, v2.6.0 4-line form, v2.6.1/v2.7.0 3-line negation form, legacy `<target>/go.md` line) are rewritten cleanly to the v2.7.1 explicit-list form; foreign hand-customized content under a `# project-guide` header is left alone with a stderr warning. The recognized-line check is `_is_recognized_block_line(line, target_dir)` — accepts anything anchored at `/<target>/` plus the legacy negation entries.
|
|
132
132
|
- `_copy_template_tree(src, dest, force)` — recursive copy preserving structure
|
|
133
133
|
- `_migrate_config_if_needed()` — renames legacy `.project-guides.yml`
|
|
134
134
|
- `_apply_heal(config, config_path)` — apply pending template syncs and re-render `go.md`. Sets `PROJECT_GUIDE_HEALING=1` in `os.environ` before doing any writes so nested subprocess invocations don't re-enter the auto-hook.
|
|
@@ -270,18 +270,28 @@ class ModeDefinition:
|
|
|
270
270
|
|
|
271
271
|
### `.gitignore` Management
|
|
272
272
|
|
|
273
|
-
`init` writes a canonical
|
|
273
|
+
`init` writes a canonical **negation-free explicit-list** block under a `# project-guide` comment header (Story P.d → P.j → P.l). For the default install layout the block is:
|
|
274
|
+
|
|
274
275
|
```
|
|
275
276
|
# project-guide
|
|
276
|
-
docs/project-guide
|
|
277
|
-
|
|
277
|
+
/docs/project-guide/.metadata.yml
|
|
278
|
+
/docs/project-guide/README.md
|
|
279
|
+
/docs/project-guide/developer/
|
|
280
|
+
/docs/project-guide/templates/
|
|
281
|
+
/docs/project-guide/**/*.bak.*
|
|
278
282
|
```
|
|
279
283
|
|
|
284
|
+
The list is **dynamically generated** at write time by enumerating the bundled template root (`_get_package_template_dir()`) and emitting one anchored line per top-level child other than `go.md`. New top-level files or subdirectories added in future releases are picked up automatically — no manual writer update required.
|
|
285
|
+
|
|
280
286
|
**Why this shape:** every file under `target_dir` except `go.md` is bundled static data that `heal` (FR-14) repopulates on first invocation, so tracking the full template tree in the consumer repo would just add ~35 files of noise to `git status` and PR reviews. `go.md` itself **must remain tracked** because IDE-integrated LLMs (Cursor, Claude Code, etc.) typically hide gitignored files from the LLM's view, and the LLM's instruction to `Read docs/project-guide/go.md` requires the file to be visible. The repo-history value of `go.md` is incidental — the file churns on every mode switch — and that churn is the acceptable cost for LLM visibility.
|
|
281
287
|
|
|
282
|
-
**Why
|
|
288
|
+
**Why explicit list instead of `**` + `!go.md`?** The cleaner negation form (used in v2.6.0–v2.7.0) is correct per the `.gitignore` spec, but several IDE-integrated tools (Cursor, parts of the VS Code fork ecosystem, certain LSP-based search backends) implement a subset of `.gitignore` semantics that does **not** honor re-include negation — they apply the broad `**` rule, hide `go.md` from @-mention search, and defeat the IDE-LLM-visibility constraint the policy is trying to enforce. P.l (v2.7.1) switched to the explicit-list form so no negation is required; tools with simplistic parsers handle anchored line-per-entry patterns reliably. Future maintainers: do **not** "simplify" back to `**` + `!` — the regression is invisible from git's perspective but breaks the IDE workflow that motivates tracking `go.md` in the first place.
|
|
289
|
+
|
|
290
|
+
**Why the trailing `<target>/**/*.bak.*` line?** Defensive coverage for backup files that `apply_file_update()` writes next to top-level synced files (e.g., `<target>/README.md.bak.<timestamp>`). Subdirectory backups are already covered by the per-directory entries; this catch-all is a simple recursive glob with no negation, so the IDEs handle it cleanly.
|
|
291
|
+
|
|
292
|
+
**Existing-block detection:** `_ensure_gitignore_entry()` is idempotent. The recognized-line predicate `_is_recognized_block_line(line, target_dir)` accepts any line starting with `/<target>/` (the v2.7.1+ anchor) plus the legacy negation-form lines (`<target>/**`, `!<target>/go.md`, `<target>/**/*.bak.*`, `<target>/go.md`). A block whose every non-empty line satisfies the predicate is rewritten cleanly to the current canonical form; any line that fails the predicate marks the block as hand-customized — the writer leaves it untouched and emits a stderr warning. A `.gitignore` with no `# project-guide` header gets the canonical block appended (separated by a blank line).
|
|
283
293
|
|
|
284
|
-
**
|
|
294
|
+
**Migration:** `project-guide init --force` rewrites prior blocks (pre-P.d `.bak.*`-only, v2.6.0 4-line, v2.6.1/v2.7.0 3-line) to the v2.7.1 explicit-list form in place. `git rm --cached` remains the manual cleanup for files already tracked under the old policy.
|
|
285
295
|
|
|
286
296
|
---
|
|
287
297
|
|
|
@@ -312,39 +312,62 @@ _GITIGNORE_HEADER = "# project-guide"
|
|
|
312
312
|
def _build_project_guide_block(target_dir: str) -> str:
|
|
313
313
|
"""Build the canonical project-guide gitignore block.
|
|
314
314
|
|
|
315
|
-
Policy (Story P.d, tightened in P.j): everything under
|
|
316
|
-
gitignored except ``go.md``
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
``
|
|
315
|
+
Policy (Story P.d, tightened in P.j, reshaped in P.l): everything under
|
|
316
|
+
``target_dir`` is gitignored except ``go.md``. ``go.md`` must remain
|
|
317
|
+
tracked because IDE-integrated LLMs (Cursor, parts of the VS Code fork
|
|
318
|
+
ecosystem, several LSP-based search backends) typically hide gitignored
|
|
319
|
+
files from the LLM's @-mention / fuzzy-search view.
|
|
320
|
+
|
|
321
|
+
P.l (v2.7.1) abandons the cleaner ``<target>/**`` + ``!<target>/go.md``
|
|
322
|
+
shape because several of those same IDEs implement a subset of
|
|
323
|
+
``.gitignore`` semantics that does not honor re-include negation —
|
|
324
|
+
they apply the broad ``**`` rule, hide ``go.md``, and defeat the
|
|
325
|
+
visibility constraint the policy is trying to enforce. The new form
|
|
326
|
+
lists every top-level entry under ``target_dir`` explicitly so no
|
|
327
|
+
negation is required. The list is enumerated from the bundled template
|
|
328
|
+
tree at write time, so future additions to the install footprint are
|
|
329
|
+
picked up automatically.
|
|
330
|
+
|
|
331
|
+
The trailing ``<target>/**/*.bak.*`` rule defensively ignores backup
|
|
332
|
+
files that ``apply_file_update`` writes next to top-level synced files
|
|
333
|
+
(subdirectory backups are already covered by the per-directory entries).
|
|
324
334
|
"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
""
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
pkg_root = _get_package_template_dir()
|
|
336
|
+
entries: list[str] = [_GITIGNORE_HEADER]
|
|
337
|
+
for child in sorted(pkg_root.iterdir(), key=lambda p: p.name):
|
|
338
|
+
if child.name == "go.md":
|
|
339
|
+
continue
|
|
340
|
+
suffix = "/" if child.is_dir() else ""
|
|
341
|
+
entries.append(f"/{target_dir}/{child.name}{suffix}")
|
|
342
|
+
entries.append(f"/{target_dir}/**/*.bak.*")
|
|
343
|
+
return "\n".join(entries) + "\n"
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _is_recognized_block_line(line: str, target_dir: str) -> bool:
|
|
347
|
+
"""Return True when ``line`` is one we plausibly wrote in any past version.
|
|
348
|
+
|
|
349
|
+
A block whose every non-empty line satisfies this predicate is treated
|
|
350
|
+
as ours and rewritten cleanly to the current canonical form; a block
|
|
351
|
+
containing anything that fails this predicate is left untouched with a
|
|
352
|
+
warning. Recognized forms (newest first):
|
|
353
|
+
|
|
354
|
+
- **v2.7.1+ explicit-list form (Story P.l):** any line starting with
|
|
355
|
+
``/<target>/``. The leading slash anchors at repo root; we never
|
|
356
|
+
write unanchored lines, and there is nothing else we plausibly
|
|
357
|
+
generate under that anchor.
|
|
358
|
+
- **v2.6.1 form (Story P.j):** ``<target>/**`` and ``!<target>/go.md``.
|
|
359
|
+
- **v2.6.0 form (Story P.d):** the v2.6.1 lines plus ``<target>/**/*.bak.*``.
|
|
360
|
+
- **pre-P.d form:** ``<target>/**/*.bak.*`` only.
|
|
361
|
+
- **Legacy variants:** ``<target>/go.md`` (incorrectly gitignored, if
|
|
362
|
+
it ever appeared in the wild).
|
|
338
363
|
"""
|
|
339
|
-
|
|
340
|
-
|
|
364
|
+
if line.startswith(f"/{target_dir}/"):
|
|
365
|
+
return True
|
|
366
|
+
return line in {
|
|
341
367
|
f"{target_dir}/**",
|
|
342
368
|
f"!{target_dir}/go.md",
|
|
343
|
-
# v2.6.0 canonical form (Story P.d) — kept recognized so v2.6.0
|
|
344
|
-
# installs heal to the P.j 3-line form on `init --force`.
|
|
345
369
|
f"{target_dir}/**/*.bak.*",
|
|
346
|
-
|
|
347
|
-
f"{target_dir}/go.md", # incorrectly gitignored go.md, if it ever appeared
|
|
370
|
+
f"{target_dir}/go.md",
|
|
348
371
|
}
|
|
349
372
|
|
|
350
373
|
|
|
@@ -388,8 +411,7 @@ def _ensure_gitignore_entry(target_dir: str) -> None:
|
|
|
388
411
|
block_end += 1
|
|
389
412
|
|
|
390
413
|
block_body = [lines[i].strip() for i in range(header_idx + 1, block_end)]
|
|
391
|
-
|
|
392
|
-
foreign = [bl for bl in block_body if bl and bl not in recognized]
|
|
414
|
+
foreign = [bl for bl in block_body if bl and not _is_recognized_block_line(bl, target_dir)]
|
|
393
415
|
|
|
394
416
|
if foreign:
|
|
395
417
|
click.secho(
|
|
@@ -2404,24 +2404,41 @@ def test_hook_under_skip_input_heals_silently_with_notice(runner, tmp_path, hook
|
|
|
2404
2404
|
# --- End Story P.c -----------------------------------------------------------
|
|
2405
2405
|
|
|
2406
2406
|
|
|
2407
|
-
# --- Story P.d / P.j: gitignore block inversion + tightening
|
|
2407
|
+
# --- Story P.d / P.j / P.l: gitignore block inversion + tightening + IDE compat ---
|
|
2408
2408
|
|
|
2409
2409
|
|
|
2410
|
-
|
|
2411
|
-
"
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2410
|
+
def _expected_gitignore_block() -> str:
|
|
2411
|
+
"""Compute the canonical gitignore block from the bundled template tree.
|
|
2412
|
+
|
|
2413
|
+
Mirrors `_build_project_guide_block()`'s enumeration so tests stay
|
|
2414
|
+
honest when the bundled tree changes — adding a new top-level template
|
|
2415
|
+
file/directory will be picked up by both the writer and this helper.
|
|
2416
|
+
"""
|
|
2417
|
+
from project_guide.cli import _get_package_template_dir
|
|
2418
|
+
|
|
2419
|
+
pkg_root = _get_package_template_dir()
|
|
2420
|
+
target = "docs/project-guide"
|
|
2421
|
+
entries = ["# project-guide"]
|
|
2422
|
+
for child in sorted(pkg_root.iterdir(), key=lambda p: p.name):
|
|
2423
|
+
if child.name == "go.md":
|
|
2424
|
+
continue
|
|
2425
|
+
suffix = "/" if child.is_dir() else ""
|
|
2426
|
+
entries.append(f"/{target}/{child.name}{suffix}")
|
|
2427
|
+
entries.append(f"/{target}/**/*.bak.*")
|
|
2428
|
+
return "\n".join(entries) + "\n"
|
|
2415
2429
|
|
|
2416
2430
|
|
|
2417
2431
|
def test_init_fresh_writes_inverted_gitignore_block(runner, tmp_path):
|
|
2418
|
-
"""Fresh `init` writes the canonical
|
|
2432
|
+
"""Fresh `init` writes the canonical explicit-list track-only-go.md block."""
|
|
2419
2433
|
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
2420
2434
|
result = runner.invoke(main, ['init'])
|
|
2421
2435
|
|
|
2422
2436
|
assert result.exit_code == 0, result.output
|
|
2423
2437
|
gitignore = Path(".gitignore").read_text()
|
|
2424
|
-
|
|
2438
|
+
expected = _expected_gitignore_block()
|
|
2439
|
+
assert expected in gitignore
|
|
2440
|
+
# No negation pattern — the whole point of the P.l rewrite.
|
|
2441
|
+
assert "!" not in gitignore.split("# project-guide", 1)[1].split("\n\n", 1)[0]
|
|
2425
2442
|
|
|
2426
2443
|
|
|
2427
2444
|
def test_init_force_rewrites_legacy_bak_only_block_cleanly(runner, tmp_path):
|
|
@@ -2442,17 +2459,15 @@ def test_init_force_rewrites_legacy_bak_only_block_cleanly(runner, tmp_path):
|
|
|
2442
2459
|
gitignore = Path(".gitignore").read_text()
|
|
2443
2460
|
# Old block lines that are not in the canonical form must be gone.
|
|
2444
2461
|
assert gitignore.count("# project-guide") == 1, gitignore
|
|
2445
|
-
assert
|
|
2446
|
-
# The dropped legacy line must not survive.
|
|
2447
|
-
assert "docs/project-guide/**/*.bak.*" not in gitignore
|
|
2462
|
+
assert _expected_gitignore_block() in gitignore
|
|
2448
2463
|
# Surrounding content preserved.
|
|
2449
2464
|
assert "*.pyc\n" in gitignore
|
|
2450
2465
|
|
|
2451
2466
|
|
|
2452
|
-
def
|
|
2453
|
-
"""`init --force` on a v2.6.0-shipped 4-line block
|
|
2467
|
+
def test_init_force_rewrites_v260_four_line_block_to_explicit_list(runner, tmp_path):
|
|
2468
|
+
"""`init --force` on a v2.6.0-shipped 4-line block migrates to the v2.7.1 explicit-list form."""
|
|
2454
2469
|
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
2455
|
-
# v2.6.0 canonical form —
|
|
2470
|
+
# v2.6.0 canonical form — all four lines are still recognized,
|
|
2456
2471
|
# so the writer cleanly rewrites the block.
|
|
2457
2472
|
Path(".gitignore").write_text(
|
|
2458
2473
|
"*.pyc\n"
|
|
@@ -2468,16 +2483,36 @@ def test_init_force_rewrites_v260_four_line_block_to_three_lines(runner, tmp_pat
|
|
|
2468
2483
|
assert result.exit_code == 0, result.output
|
|
2469
2484
|
gitignore = Path(".gitignore").read_text()
|
|
2470
2485
|
assert gitignore.count("# project-guide") == 1, gitignore
|
|
2471
|
-
assert
|
|
2472
|
-
# The
|
|
2473
|
-
assert "docs/project-guide
|
|
2486
|
+
assert _expected_gitignore_block() in gitignore
|
|
2487
|
+
# The old negation form must be gone.
|
|
2488
|
+
assert "!docs/project-guide/go.md" not in gitignore
|
|
2474
2489
|
assert "*.pyc\n" in gitignore
|
|
2475
2490
|
|
|
2476
2491
|
|
|
2492
|
+
def test_init_force_rewrites_v261_three_line_block_to_explicit_list(runner, tmp_path):
|
|
2493
|
+
"""`init --force` on a v2.6.1/v2.7.0-shipped 3-line negation block migrates to v2.7.1 explicit-list."""
|
|
2494
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
2495
|
+
Path(".gitignore").write_text(
|
|
2496
|
+
"*.pyc\n"
|
|
2497
|
+
"\n"
|
|
2498
|
+
"# project-guide\n"
|
|
2499
|
+
"docs/project-guide/**\n"
|
|
2500
|
+
"!docs/project-guide/go.md\n"
|
|
2501
|
+
)
|
|
2502
|
+
|
|
2503
|
+
result = runner.invoke(main, ['init', '--force'])
|
|
2504
|
+
|
|
2505
|
+
assert result.exit_code == 0, result.output
|
|
2506
|
+
gitignore = Path(".gitignore").read_text()
|
|
2507
|
+
assert gitignore.count("# project-guide") == 1, gitignore
|
|
2508
|
+
assert _expected_gitignore_block() in gitignore
|
|
2509
|
+
assert "!docs/project-guide/go.md" not in gitignore
|
|
2510
|
+
|
|
2511
|
+
|
|
2477
2512
|
def test_init_with_existing_canonical_block_is_idempotent(runner, tmp_path):
|
|
2478
2513
|
"""Running `init` over a project whose .gitignore is already canonical does not rewrite."""
|
|
2479
2514
|
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
2480
|
-
Path(".gitignore").write_text("foo\n\n" +
|
|
2515
|
+
Path(".gitignore").write_text("foo\n\n" + _expected_gitignore_block())
|
|
2481
2516
|
before = Path(".gitignore").read_text()
|
|
2482
2517
|
|
|
2483
2518
|
result = runner.invoke(main, ['init'])
|
|
@@ -2493,7 +2528,7 @@ def test_init_warns_on_foreign_project_guide_block_and_leaves_it_untouched(runne
|
|
|
2493
2528
|
foreign_block = (
|
|
2494
2529
|
"# project-guide\n"
|
|
2495
2530
|
"docs/project-guide/**/*.bak.*\n"
|
|
2496
|
-
"
|
|
2531
|
+
"some/unrelated/path\n" # foreign line — not under /docs/project-guide/
|
|
2497
2532
|
)
|
|
2498
2533
|
Path(".gitignore").write_text(foreign_block)
|
|
2499
2534
|
|
|
@@ -2516,10 +2551,10 @@ def test_init_appends_block_when_no_prior_project_guide_section(runner, tmp_path
|
|
|
2516
2551
|
gitignore = Path(".gitignore").read_text()
|
|
2517
2552
|
assert "*.pyc\n" in gitignore
|
|
2518
2553
|
assert ".venv/\n" in gitignore
|
|
2519
|
-
assert
|
|
2554
|
+
assert _expected_gitignore_block() in gitignore
|
|
2520
2555
|
|
|
2521
2556
|
|
|
2522
|
-
# --- End Story P.d / P.j
|
|
2557
|
+
# --- End Story P.d / P.j / P.l ----------------------------------------------
|
|
2523
2558
|
|
|
2524
2559
|
|
|
2525
2560
|
# --- Story P.k: project-guide git-push wrapper ------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/site/images/project-guide-banner-landing.png
RENAMED
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/site/images/project-guide-header-readme.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-k-release-lifecycle-plan.md
RENAMED
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-l-no-input-init-plan.md
RENAMED
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/phase-m-project-essentials-plan.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/docs/specs/.archive/project-guide-no-input-spec.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/.project-guide.yml.template
RENAMED
|
File without changes
|
{project_guide-2.7.0 → project_guide-2.7.1}/project_guide/templates/project-guide/.metadata.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|