@antoneeo/course-creator 0.1.0-beta.17 → 0.1.0-beta.18

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.0-beta.18] - 2026-11-01
4
+
5
+ ### What's new
6
+
7
+ **Branches that add review rows merge without a conflict.** When two branches both add rows to the review log and your project has no merge rule for that file, merging them stops on a conflict you resolve by hand. The check the agent runs when it closes a piece of work now notices the missing rule and prints the exact line to add to `.gitattributes`, so the agent sees what to fix while it works, without anyone reading release notes. Nothing changes at session start, and the note never makes a check fail.
8
+
9
+ #### What to do
10
+
11
+ **1. Update (everyone).** The changes arrive only with the update.
12
+ 1. Run `npm install -g @antoneeo/course-creator@latest`, then `course-creator-install-skill`.
13
+ 2. Close and reopen your agent sessions: the new version loads only at startup.
14
+
15
+ To check: `npm ls -g @antoneeo/course-creator` shows 0.1.0-beta.18.
16
+
17
+ ### Changed
18
+ - The shared validator (F-090): `check` prints a `[note]` when the review log (`<docs>/audit/reviews/REVIEW_LOG.md`) has no `merge` attribute, naming the line to add to `.gitattributes` at the repository root (`git check-attr`, with personal and system attribute files ignored; a per-clone `.git/info/attributes` still counts). A deliberate non-union driver is respected. The exit code is unchanged, and session orientation still runs no git.
19
+ - `ENFORCEMENT.md`: «A review log git would not merge cleanly -- `check` names it» (F-090).
20
+
3
21
  ## [0.1.0-beta.17] - 2026-10-31
4
22
 
5
23
  ### What's new
package/README.md CHANGED
@@ -6,9 +6,9 @@ The package is the fourth lens of the Agentic SDLC family. It uses the shared `a
6
6
 
7
7
  <!-- whats-new:begin -->
8
8
  <!-- generated by scripts/whats_new.js from distributions/course-creator/CHANGELOG.md: edit the CHANGELOG, then run node scripts/whats_new.js --write -->
9
- ## What's new in 0.1.0-beta.17
9
+ ## What's new in 0.1.0-beta.18
10
10
 
11
- **Decisions taken on your behalf are labelled.** In the request record, a decision a session took under a delegation you gave it, and words you gave one session for work another session records, now carry their own heading. They are never read later as something you said yourself, and a delegated decision never overrides one of yours: a choice that would change it comes back to you as a question.
11
+ **Branches that add review rows merge without a conflict.** When two branches both add rows to the review log and your project has no merge rule for that file, merging them stops on a conflict you resolve by hand. The check the agent runs when it closes a piece of work now notices the missing rule and prints the exact line to add to `.gitattributes`, so the agent sees what to fix while it works, without anyone reading release notes. Nothing changes at session start, and the note never makes a check fail.
12
12
 
13
13
  #### What to do
14
14
 
@@ -16,7 +16,7 @@ The package is the fourth lens of the Agentic SDLC family. It uses the shared `a
16
16
  1. Run `npm install -g @antoneeo/course-creator@latest`, then `course-creator-install-skill`.
17
17
  2. Close and reopen your agent sessions: the new version loads only at startup.
18
18
 
19
- To check: `npm ls -g @antoneeo/course-creator` shows 0.1.0-beta.17.
19
+ To check: `npm ls -g @antoneeo/course-creator` shows 0.1.0-beta.18.
20
20
 
21
21
  Full history: [CHANGELOG](https://github.com/Antoneeo/agentic-sdlc-skill/blob/main/distributions/course-creator/CHANGELOG.md)
22
22
  <!-- whats-new:end -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "course-creator",
3
- "version": "0.1.0-beta.17",
3
+ "version": "0.1.0-beta.18",
4
4
  "description": "Course creation for defined learners, with progressive explanations, sourced facts and diagnostic independent testing.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antoneeo/course-creator",
3
- "version": "0.1.0-beta.17",
3
+ "version": "0.1.0-beta.18",
4
4
  "description": "Design sourced, progressive courses for a defined learner with Agentic SDLC governance and diagnostic learner simulations.",
5
5
  "keywords": ["claude-code", "gemini-cli", "codex", "course-design", "skill", "ai-agent"],
6
6
  "author": "Antonio Pinto (https://github.com/Antoneeo)",
@@ -150,6 +150,16 @@ not see them, because the wired hook legitimately lives in the git-ignored
150
150
  mechanism (Gemini today) keeps the note -- read it as the manual-reads reminder; a
151
151
  `.codex/hooks.json` wired in a shape other than the documented one keeps it too.
152
152
 
153
+ **A review log git would not merge cleanly -- `check` names it (F-090).** When the docs
154
+ root holds `audit/reviews/REVIEW_LOG.md` and git reports no `merge` attribute for it (no
155
+ committed `.gitattributes` line covers it: a project set up before the stanza, a custom
156
+ docs root, a project below the repository root, a course project), `check` prints one
157
+ `[note]` with the exact line to add at the repository root. Same contract as above:
158
+ informational, never the exit code, never a `validate` warning. Personal attribute files
159
+ (except the per-clone `.git/info/attributes`) are ignored, so the note matches what a
160
+ teammate's clone does; a deliberate driver other
161
+ than `union` is respected. Session orientation does not run it: it stays zero-execution.
162
+
153
163
  **Installing the package wires the hook machine-wide (F-042).** On Claude Code
154
164
  with Python, `npm i -g` (and every update) wires a SessionStart orient hook into
155
165
  the USER-level settings (`$CLAUDE_CONFIG_DIR/settings.json` when set, else
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: course-creator
3
- version: 0.1.0-beta.17
3
+ version: 0.1.0-beta.18
4
4
  description: Design and improve courses that teach a defined learner through sourced, progressive explanations. Use when the deliverable is instruction or a learning path; route corpus intake to kb-agentic and persuasive campaigns to marketing-agentic.
5
5
  author: Antonio Pinto (https://github.com/Antoneeo)
6
6
  copyright: (c) 2026 Antonio Pinto
@@ -1377,9 +1377,12 @@ def _open_units(root):
1377
1377
  return units
1378
1378
 
1379
1379
 
1380
- def _git_out(root, *args, timeout=30):
1380
+ def _git_out(root, *args, timeout=30, env=None):
1381
+ # `env` is merged into the inherited environment, never a replacement: a bare
1382
+ # dict would drop PATH and fail silently on Windows.
1381
1383
  r = subprocess.run(["git", *args], cwd=str(root), capture_output=True, text=True,
1382
- encoding="utf-8", errors="replace", timeout=timeout)
1384
+ encoding="utf-8", errors="replace", timeout=timeout,
1385
+ env={**os.environ, **env} if env else None)
1383
1386
  return r.stdout if r.returncode == 0 else None
1384
1387
 
1385
1388
 
@@ -2481,10 +2484,64 @@ def cmd_check(root, strict=False, hybrid=False, extra_validate=None, extra_secti
2481
2484
  failed = any(rc for _, rc in rcs)
2482
2485
  print(f"\ncheck: {'NOT CLEAN' if failed else 'CLEAN'} "
2483
2486
  f"({', '.join(f'{title} rc={rc}' for title, rc in rcs)})")
2484
- print_orient_hook_note(root)
2487
+ print_check_notes(root)
2485
2488
  return 1 if failed else 0
2486
2489
 
2487
2490
 
2491
+ def _attr_pattern(path):
2492
+ """A .gitattributes pattern matching exactly `path`. Order matters (probed):
2493
+ a backslash, glob characters and a leading `#` or `!` (comment, negation)
2494
+ are escaped first, then the result is C-quoted when it holds whitespace,
2495
+ `"` or a backslash."""
2496
+ p = re.sub(r"([\\\[*?])", r"\\\1", path)
2497
+ if p.startswith(("#", "!")):
2498
+ p = "\\" + p
2499
+ if re.search(r'[\s"\\]', p):
2500
+ p = '"' + p.replace("\\", "\\\\").replace('"', '\\"') + '"'
2501
+ return p
2502
+
2503
+
2504
+ def review_log_union_note(root):
2505
+ """F-090: the note for a review log git would not merge with union, or None.
2506
+
2507
+ Git itself is asked (`check-attr`), so patterns and nested files count;
2508
+ personal and system attribute files do not, so the answer is the one a
2509
+ teammate's clone gets. Only `unspecified` is a gap: `union` or any other
2510
+ driver is a choice. Residual: `.git/info/attributes` is still read. Any
2511
+ failure -- no git, not a work tree, timeout -- is no note: `check` never
2512
+ breaks on it."""
2513
+ try:
2514
+ log = ai_path(root, "audit", "reviews", "REVIEW_LOG.md")
2515
+ if not log.is_file():
2516
+ return None
2517
+ prefix = _git_out(root, "rev-parse", "--show-prefix", timeout=3)
2518
+ if prefix is None:
2519
+ return None
2520
+ rel = log.resolve().relative_to(Path(root).resolve()).as_posix()
2521
+ out = _git_out(root, "-c", "core.attributesFile=", "check-attr", "-z", "merge",
2522
+ "--", rel, timeout=3, env={"GIT_ATTR_NOSYSTEM": "1"})
2523
+ if out is None:
2524
+ return None
2525
+ fields = out.split("\0")
2526
+ if len(fields) < 3 or fields[2] != "unspecified":
2527
+ return None
2528
+ path = prefix.rstrip("\r\n") + rel # a folder may begin with a space
2529
+ return (f"[note] {path} has no merge attribute: two branches that both add review "
2530
+ "rows conflict when merged. Fix: add this line to .gitattributes at the "
2531
+ f"repository root: {_attr_pattern(path)} merge=union")
2532
+ except Exception:
2533
+ return None
2534
+
2535
+
2536
+ def print_check_notes(root):
2537
+ """Every check-layer note, informational and never an exit code. The one
2538
+ place an overlay that replaces `check` calls, so a new note cannot miss it."""
2539
+ print_orient_hook_note(root)
2540
+ note = review_log_union_note(root)
2541
+ if note:
2542
+ print(note)
2543
+
2544
+
2488
2545
  # --------------------------------------------------------------------- gate
2489
2546
 
2490
2547
  # --- UserPromptSubmit reminder hook (F-046) --------------------------------
@@ -1,38 +1,38 @@
1
- {
2
- "_comment": "Generated by shared_files.py --update. IDENTICAL in every distribution of the family: if two copies of this file differ, the spine has diverged. Never edit by hand.",
3
- "files": {
4
- "LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
5
- "NOTICE": "8414f424f7c1db19d89d74286fd2d6b1acdd9eedde910fa57a16d6835602e4bd",
6
- "dispatch.md": "d127057198415f81b921c74e3234a47d5794a2af662eeadaf3dff3cc1739ebc8",
7
- "docs_site/app.js": "ce9ed4341f7d24b648dee7531700d09f4af07f7e9ffc4be90aff29ecc276f27f",
8
- "docs_site/index.html": "2b64167470f754e46fa4bb4e73a3d2c34ee632d91fe2f17705a58d18b5018af5",
9
- "docs_site/strings.js": "440c11830f87602152422766b6d5b84da904150c37b0b6347e2ad53a7fec62b8",
10
- "docs_site/style.css": "1cbd71eb2e62bb9251ecb62796fe4cecf6a78eecf2f9a7493703290ae55d1244",
11
- "guides.md": "8fd85ab176d01ad7fc4c72020afa062f37f3c2f6b380f064c20f114c0680b46b",
12
- "memory.md": "765c8cf34e9161143d6f8b982106251de0ac7f4f6f5d768336f3e4b4b150823e",
13
- "review.md": "e7eb85f71d79db52e59415e6b3baed15fa9dae4004a6e882190547b3999c778e",
14
- "routing.md": "5116e8dbd3b215919b897506f28917ac8f1c44cd91f8b64ec901048320e6c1f6",
15
- "scripts/docs_site.py": "bfc7a521142251b85b229e1873ccf2e375bb5c7f1990cef574e1a78ef14f0091",
16
- "scripts/entry_point.py": "4a560d4d41db989d3426182f1b24270cbb934b3db0f3246c700578a4ed93089b",
17
- "scripts/knowledge.py": "6047deb7a2be4cf9ef2949cbcf8a52a8ea61500f4e12d203f1c58076540d50ab",
18
- "scripts/sdlc_core.py": "582eb73e1b9989bb952c72721bd80b5bd76447955bca85bfd789ea3e94a77afc",
19
- "scripts/shared_files.py": "d360c6018c602788dd96194bcc0f8e8409b140671857ae50ec15acee882eb51e",
20
- "scripts/test_audit_refs.py": "5c207be46da566db47069b08373195e748fca19c047418f09cc2f78841dfd9a5",
21
- "scripts/test_chronology.py": "4a6c1801891bbadfba6bb8309399a4d8485166332b87616df84679eba8305ace",
22
- "scripts/test_constraints.py": "4be3bbff7032e53b464de2f4e2eb18a824311adb2359c77fb9be065aded09239",
23
- "scripts/test_design_approval.py": "191856214cb0b4ab8606b57496cf54742568e3bb4bfa0d8f0c64889f2e2d1e63",
24
- "scripts/test_docs_root.py": "19753e0316c0df63ed321d24b411a9ff36eea2058421fd69fc57e507d75566d0",
25
- "scripts/test_docs_site.py": "391ec5c635c67f0fbefe458426e352b5b1ef626ae65c099b1e8e1db491d0a5f9",
26
- "scripts/test_domain_rules.py": "33542b5dc2bb77c05ece55cf0446065cc4226c763ef5e040aa77364f226937bb",
27
- "scripts/test_drift.py": "c7b7121a8a308b0f3fe12c0c0c346444e632b7aaeff47dc8818b621e5c20cb38",
28
- "scripts/test_guide_check.py": "19c7a6f009fa1b7f70b0beba059b97fa05e69255f6b2dec8829a87bd9fba283f",
29
- "scripts/test_merge_safety.py": "8b9241077bf7ecd6dd335907c640044ab38968ff4c9fdc464450e732cb77adf4",
30
- "scripts/test_migrate.py": "c129823bf4feb0614acd975351632a6ae62d7896a0ef32724419a8e9335e3110",
31
- "scripts/test_plan.py": "4c011a63aa834b55e9c5d13fb4ea76b131b742356001364b0a508435ba013e27",
32
- "scripts/test_project_memory.py": "2cb85cef01161d881a11d84cfe9ebb72915104e8d1822e4de739f7d00ae6cd59",
33
- "scripts/test_session_start.py": "723f748c0aef461d1051191dbd18f4ea5697a1e0467df73bccb0af8024d17f56",
34
- "scripts/test_skill_invariants.py": "4c0f5f2960f177fac58d4b533df878880de800d909ea3f522883da60dad3c3dd",
35
- "scripts/test_verdict.py": "8620be2f962f900e9fa0f979f80984671baa373de1ce3f29045104d813e71844",
36
- "vision.md": "a19a7d7d5044215b726b66f74e422c9cdedcc856c0a53151f20a6d9840b8f6c4"
37
- }
38
- }
1
+ {
2
+ "_comment": "Generated by shared_files.py --update. IDENTICAL in every distribution of the family: if two copies of this file differ, the spine has diverged. Never edit by hand.",
3
+ "files": {
4
+ "LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
5
+ "NOTICE": "8414f424f7c1db19d89d74286fd2d6b1acdd9eedde910fa57a16d6835602e4bd",
6
+ "dispatch.md": "d127057198415f81b921c74e3234a47d5794a2af662eeadaf3dff3cc1739ebc8",
7
+ "docs_site/app.js": "ce9ed4341f7d24b648dee7531700d09f4af07f7e9ffc4be90aff29ecc276f27f",
8
+ "docs_site/index.html": "2b64167470f754e46fa4bb4e73a3d2c34ee632d91fe2f17705a58d18b5018af5",
9
+ "docs_site/strings.js": "440c11830f87602152422766b6d5b84da904150c37b0b6347e2ad53a7fec62b8",
10
+ "docs_site/style.css": "1cbd71eb2e62bb9251ecb62796fe4cecf6a78eecf2f9a7493703290ae55d1244",
11
+ "guides.md": "8fd85ab176d01ad7fc4c72020afa062f37f3c2f6b380f064c20f114c0680b46b",
12
+ "memory.md": "765c8cf34e9161143d6f8b982106251de0ac7f4f6f5d768336f3e4b4b150823e",
13
+ "review.md": "e7eb85f71d79db52e59415e6b3baed15fa9dae4004a6e882190547b3999c778e",
14
+ "routing.md": "5116e8dbd3b215919b897506f28917ac8f1c44cd91f8b64ec901048320e6c1f6",
15
+ "scripts/docs_site.py": "bfc7a521142251b85b229e1873ccf2e375bb5c7f1990cef574e1a78ef14f0091",
16
+ "scripts/entry_point.py": "4a560d4d41db989d3426182f1b24270cbb934b3db0f3246c700578a4ed93089b",
17
+ "scripts/knowledge.py": "6047deb7a2be4cf9ef2949cbcf8a52a8ea61500f4e12d203f1c58076540d50ab",
18
+ "scripts/sdlc_core.py": "29eeb987310cc2f2191266fb20395ddcc555c9c40c7e1e6cd18f118a4711ac04",
19
+ "scripts/shared_files.py": "d360c6018c602788dd96194bcc0f8e8409b140671857ae50ec15acee882eb51e",
20
+ "scripts/test_audit_refs.py": "5c207be46da566db47069b08373195e748fca19c047418f09cc2f78841dfd9a5",
21
+ "scripts/test_chronology.py": "4a6c1801891bbadfba6bb8309399a4d8485166332b87616df84679eba8305ace",
22
+ "scripts/test_constraints.py": "4be3bbff7032e53b464de2f4e2eb18a824311adb2359c77fb9be065aded09239",
23
+ "scripts/test_design_approval.py": "191856214cb0b4ab8606b57496cf54742568e3bb4bfa0d8f0c64889f2e2d1e63",
24
+ "scripts/test_docs_root.py": "19753e0316c0df63ed321d24b411a9ff36eea2058421fd69fc57e507d75566d0",
25
+ "scripts/test_docs_site.py": "391ec5c635c67f0fbefe458426e352b5b1ef626ae65c099b1e8e1db491d0a5f9",
26
+ "scripts/test_domain_rules.py": "33542b5dc2bb77c05ece55cf0446065cc4226c763ef5e040aa77364f226937bb",
27
+ "scripts/test_drift.py": "c7b7121a8a308b0f3fe12c0c0c346444e632b7aaeff47dc8818b621e5c20cb38",
28
+ "scripts/test_guide_check.py": "19c7a6f009fa1b7f70b0beba059b97fa05e69255f6b2dec8829a87bd9fba283f",
29
+ "scripts/test_merge_safety.py": "296568fd452e0ffbbb7cfd2bc567deda67a7231ecb7f407fa91594e3b0ebbd02",
30
+ "scripts/test_migrate.py": "c129823bf4feb0614acd975351632a6ae62d7896a0ef32724419a8e9335e3110",
31
+ "scripts/test_plan.py": "4c011a63aa834b55e9c5d13fb4ea76b131b742356001364b0a508435ba013e27",
32
+ "scripts/test_project_memory.py": "2cb85cef01161d881a11d84cfe9ebb72915104e8d1822e4de739f7d00ae6cd59",
33
+ "scripts/test_session_start.py": "723f748c0aef461d1051191dbd18f4ea5697a1e0467df73bccb0af8024d17f56",
34
+ "scripts/test_skill_invariants.py": "4c0f5f2960f177fac58d4b533df878880de800d909ea3f522883da60dad3c3dd",
35
+ "scripts/test_verdict.py": "8620be2f962f900e9fa0f979f80984671baa373de1ce3f29045104d813e71844",
36
+ "vision.md": "a19a7d7d5044215b726b66f74e422c9cdedcc856c0a53151f20a6d9840b8f6c4"
37
+ }
38
+ }