@antoneeo/course-creator 0.1.0-beta.15 → 0.1.0-beta.17
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 +40 -0
- package/README.md +3 -27
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/scripts/init.js +62 -62
- package/scripts/lib.js +98 -98
- package/scripts/postinstall.js +16 -16
- package/scripts/preuninstall.js +12 -12
- package/skills/course-creator/SKILL.md +3 -3
- package/skills/course-creator/elicitation.md +3 -1
- package/skills/course-creator/review.md +31 -1
- package/skills/course-creator/scripts/sdlc_core.py +54 -14
- package/skills/course-creator/scripts/shared_files.py +1 -0
- package/skills/course-creator/scripts/shared_manifest.json +38 -37
- package/skills/course-creator/templates.md +11 -0
- package/skills/course-creator/vision.md +308 -308
|
@@ -100,6 +100,10 @@ CHRONOLOGY_EPOCH = "2026-10-18"
|
|
|
100
100
|
# filled '## Guide check'. The day AFTER the rule was written, so the analyses
|
|
101
101
|
# opened that day by parallel units (which never saw the template) stay quiet.
|
|
102
102
|
GUIDE_CHECK_EPOCH = "2026-10-24"
|
|
103
|
+
# Owner design approval (F-087): an L3 started on/after this date owes a request-
|
|
104
|
+
# record entry labelled `design approved` before implementation. The day AFTER the
|
|
105
|
+
# rule was written, like GUIDE_CHECK_EPOCH.
|
|
106
|
+
DESIGN_APPROVAL_EPOCH = "2026-10-31"
|
|
103
107
|
NEXT_MAX_CHARS = 240 # this repository's longest `next:` was 214 at the epoch
|
|
104
108
|
ID_SCAN_MAX_REFS = 300 # A3 is skipped above it (one advisory says so, T9)
|
|
105
109
|
ID_SCAN_REFS_PER_CALL = 50 # keeps one git command line far below 32k chars
|
|
@@ -1479,17 +1483,50 @@ def id_advisories(root):
|
|
|
1479
1483
|
return out
|
|
1480
1484
|
|
|
1481
1485
|
|
|
1482
|
-
def
|
|
1483
|
-
"""True when an L3 ANALYSIS
|
|
1484
|
-
|
|
1485
|
-
shipped. PLANNED is exempt -- the review is due at the END of Phase 3, so an
|
|
1486
|
-
analysis still being drafted is not late."""
|
|
1486
|
+
def _l3_in_work_since(meta, epoch):
|
|
1487
|
+
"""True when an L3 ANALYSIS is in implementation or done and began on/after
|
|
1488
|
+
`epoch` (an ISO date string)."""
|
|
1487
1489
|
if meta.get("level", "").upper() != "L3":
|
|
1488
1490
|
return False
|
|
1489
1491
|
if meta.get("status") not in ("IN_PROGRESS", "COMPLETED"):
|
|
1490
1492
|
return False
|
|
1491
1493
|
started = parse_iso((meta.get("start_date") or "").strip().strip("'\""))
|
|
1492
|
-
return started is not None and started >= parse_iso(
|
|
1494
|
+
return started is not None and started >= parse_iso(epoch)
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
def design_review_due(meta):
|
|
1498
|
+
"""True when an L3 ANALYSIS owes a design-review row (review.md moment 1):
|
|
1499
|
+
implementation has started or finished, and it began on/after the gate
|
|
1500
|
+
shipped. PLANNED is exempt -- the review is due at the END of Phase 3, so an
|
|
1501
|
+
analysis still being drafted is not late."""
|
|
1502
|
+
return _l3_in_work_since(meta, DESIGN_REVIEW_EPOCH)
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
def design_approval_due(meta):
|
|
1506
|
+
"""True when an L3 ANALYSIS owes the owner's design approval (review.md
|
|
1507
|
+
section When a review is due): in implementation or done, begun on/after
|
|
1508
|
+
DESIGN_APPROVAL_EPOCH. PLANNED is exempt -- the question comes after the PASS."""
|
|
1509
|
+
return _l3_in_work_since(meta, DESIGN_APPROVAL_EPOCH)
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
def requests_naming(root, analysis_name):
|
|
1513
|
+
"""The request records whose `analysis:` field names this ANALYSIS file."""
|
|
1514
|
+
req = ai_path(root, "audit", "requests")
|
|
1515
|
+
if not req.is_dir():
|
|
1516
|
+
return []
|
|
1517
|
+
return [r for r in sorted(req.glob("REQUEST_*.md"))
|
|
1518
|
+
if str(load_frontmatter(read_text(r).splitlines()).get("analysis") or "")
|
|
1519
|
+
.strip().strip("'\"`") == analysis_name]
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
def design_approval_logged(root, analysis_name):
|
|
1523
|
+
"""True when a request record naming this ANALYSIS has an entry whose LABEL
|
|
1524
|
+
begins `design approved` (case-insensitive). Only the label is read, never
|
|
1525
|
+
the quoted words: a quotation is data, and a 'design approved?' inside a
|
|
1526
|
+
refusal approves nothing."""
|
|
1527
|
+
return any(label.lower().startswith("design approved")
|
|
1528
|
+
for r in requests_naming(root, analysis_name)
|
|
1529
|
+
for _, _, label, _ in request_entries(read_text(r))[0])
|
|
1493
1530
|
|
|
1494
1531
|
|
|
1495
1532
|
def review_logged(root, analysis_name):
|
|
@@ -1801,6 +1838,12 @@ def cmd_validate(root, strict=False, hybrid=False):
|
|
|
1801
1838
|
advisories.append(f"{rel}: L3 in implementation with no design-review row in "
|
|
1802
1839
|
f"{review_log_rel()} -- the design was reviewed by nobody but its "
|
|
1803
1840
|
"author before code was written (review.md moment 1)")
|
|
1841
|
+
if (not hybrid and design_approval_due(meta)
|
|
1842
|
+
and not design_approval_logged(root, p.name)):
|
|
1843
|
+
advisories.append(f"{rel}: L3 in implementation with no design approval in its "
|
|
1844
|
+
"request record (no entry whose label begins 'design approved') -- the "
|
|
1845
|
+
"owner never said the design was what they meant before the "
|
|
1846
|
+
"work started (review.md, When a review is due)")
|
|
1804
1847
|
if ledger_due(meta) and not has_ledger_heading(text):
|
|
1805
1848
|
advisories.append(f"{rel}: L3 without '## Capability Ledger' -- the architect pass "
|
|
1806
1849
|
"left no record (architect.md); run it before the Impact")
|
|
@@ -2562,7 +2605,8 @@ def cmd_gate(args):
|
|
|
2562
2605
|
sys.stderr.write(
|
|
2563
2606
|
f"[sdlc gate] '{rel}' is on a protected path but no ANALYSIS_*.md is IN_PROGRESS. "
|
|
2564
2607
|
"If your analysis already exists, set its frontmatter to 'status: IN_PROGRESS' "
|
|
2565
|
-
"(that flip is what opens the gate
|
|
2608
|
+
"(that flip is what opens the gate; at L3, only after the owner's design approval, "
|
|
2609
|
+
"review.md); otherwise write it first (Phase 3).\n")
|
|
2566
2610
|
return 2
|
|
2567
2611
|
|
|
2568
2612
|
|
|
@@ -3063,13 +3107,9 @@ def _unit_documents(root, p):
|
|
|
3063
3107
|
reading or writing any of them is working on the unit."""
|
|
3064
3108
|
slug = p.stem[len("ANALYSIS_"):]
|
|
3065
3109
|
docs = {p, ai_path(root, "audit", f"HANDOFF_{slug}.md")}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
meta = load_frontmatter(read_text(r).splitlines())
|
|
3070
|
-
if str(meta.get("analysis") or "").strip().strip("'\"`") == p.name:
|
|
3071
|
-
docs.add(r)
|
|
3072
|
-
docs.add(ai_path(root, "audit", f"HANDOFF_{r.stem[len('REQUEST_'):]}.md"))
|
|
3110
|
+
for r in requests_naming(root, p.name):
|
|
3111
|
+
docs.add(r)
|
|
3112
|
+
docs.add(ai_path(root, "audit", f"HANDOFF_{r.stem[len('REQUEST_'):]}.md"))
|
|
3073
3113
|
return {_norm_rel(d.resolve().as_posix()) for d in docs}
|
|
3074
3114
|
|
|
3075
3115
|
|
|
@@ -57,6 +57,7 @@ SHARED_FILES = (
|
|
|
57
57
|
"scripts/test_merge_safety.py",
|
|
58
58
|
"scripts/test_audit_refs.py",
|
|
59
59
|
"scripts/test_constraints.py",
|
|
60
|
+
"scripts/test_design_approval.py",
|
|
60
61
|
# the docs site (F-072): generator, its battery and its page
|
|
61
62
|
"scripts/docs_site.py",
|
|
62
63
|
"scripts/test_docs_site.py",
|
|
@@ -1,37 +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": "
|
|
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": "
|
|
19
|
-
"scripts/shared_files.py": "
|
|
20
|
-
"scripts/test_audit_refs.py": "5c207be46da566db47069b08373195e748fca19c047418f09cc2f78841dfd9a5",
|
|
21
|
-
"scripts/test_chronology.py": "
|
|
22
|
-
"scripts/test_constraints.py": "4be3bbff7032e53b464de2f4e2eb18a824311adb2359c77fb9be065aded09239",
|
|
23
|
-
"scripts/
|
|
24
|
-
"scripts/
|
|
25
|
-
"scripts/
|
|
26
|
-
"scripts/
|
|
27
|
-
"scripts/
|
|
28
|
-
"scripts/
|
|
29
|
-
"scripts/
|
|
30
|
-
"scripts/
|
|
31
|
-
"scripts/
|
|
32
|
-
"scripts/
|
|
33
|
-
"scripts/
|
|
34
|
-
"scripts/
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
}
|
|
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
|
+
}
|
|
@@ -71,6 +71,17 @@ Context (agent): "the question it answered, quoted"
|
|
|
71
71
|
entered only as `[NOT VERBATIM — from <source>]`, never as a quote.
|
|
72
72
|
- **Unattended**: R1 quotes the trigger that started the unit, labelled as such.
|
|
73
73
|
Declared assumptions are not owner statements and never enter the record.
|
|
74
|
+
- **Labelled entries** (r41): a decision taken under the owner's delegation is
|
|
75
|
+
`### R<n> — YYYY-MM-DD — decided under delegation R<m> — <what it settles>`, citing
|
|
76
|
+
the delegation entry (in Hybrid also the devPNT proposal it accepted); it is the
|
|
77
|
+
deciding session's choice, never an owner statement, and never changes an earlier
|
|
78
|
+
owner decision — that goes to the owner as a question. Owner words received for a
|
|
79
|
+
unit whose record another session writes are
|
|
80
|
+
`### R<n> — YYYY-MM-DD — received for <unit> — <what it settles>`, then sent to that
|
|
81
|
+
record's writer, who enters them verbatim citing this entry.
|
|
82
|
+
- **Design approval** (L3): the owner's approval of the design is an entry whose
|
|
83
|
+
label begins `design approved`; a no or a correction takes its own label
|
|
84
|
+
(`review.md` §When a review is due).
|
|
74
85
|
|
|
75
86
|
## Canonical analysis — `solutions/ANALYSIS_course_<slug>.md`
|
|
76
87
|
|