workflow tweak, doc tweaks, working example
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 9 Mar 2026 10:50:29 +0000 (10:50 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 9 Mar 2026 10:50:29 +0000 (10:50 +0000)
13 files changed:
.gitignore
developer/authored/hello.CLI.c [new file with mode: 0644]
developer/authored/hello.cli.c [deleted file]
developer/document/File_directory_naming.html
developer/document/RT_code_format.html
developer/tool/makefile
developer/tool/promote [new file with mode: 0755]
developer/tool/release [deleted file]
document/Intro.html [new file with mode: 0644]
document/Introduction_to_Harmony.html
document/Product_Development_Roles_and_Workflow.html
shared/tool/makefile/target_library_CLI.mk
shared/tool/version

index 9c8dd19..5194c58 100644 (file)
@@ -3,7 +3,7 @@
 !**/scratchpad/.gitignore
 
 # so that .gitignore is not required in consumer/release
-/consumer/release
+/consumer/made
 
 # Python
 __pycache__/
diff --git a/developer/authored/hello.CLI.c b/developer/authored/hello.CLI.c
new file mode 100644 (file)
index 0000000..a626cac
--- /dev/null
@@ -0,0 +1,2 @@
+#include <stdio.h>
+int main(void){ puts("hello from Rabbit CLI"); return 0; }
diff --git a/developer/authored/hello.cli.c b/developer/authored/hello.cli.c
deleted file mode 100644 (file)
index a626cac..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <stdio.h>
-int main(void){ puts("hello from Rabbit CLI"); return 0; }
index 6de0ede..7569629 100644 (file)
@@ -39,7 +39,7 @@
 
       <h2>Hyphens and spaces</h2>
 
-      <p>In a C language culture, file names use underscores between words. In a Lisp language culture use snake-kebab_case. Spaces are generally not used in program file names.
+      <p>In a C language culture, file names use underscores between words. In a Lisp language culture use snake-kebab_case. Spaces are generally not used in program file names. Though the Harmony skeleton is language agnostic, it was first used with C, hence file names use primarily underscores.
       </p>
 
 
         <li><RT-code>.mod.c</RT-code> : Kernel module sources.</li>
       </ul>
 
+      <h1>Exercises</h1>
+      <h2>Exercise 1: Program file names</h2>
+      <p>
+        Rename the following poorly named files to strictly adhere to the RT naming conventions. Pay close attention to the language culture, the capitalization of acronyms, and the rule for spelling out abbreviations in outer scope identifiers.
+      </p>
+      <ul>
+        <li>A C-culture makefile that builds a target library and command line interface: <RT-code>target-lib-cli.mk</RT-code></li>
+        <li>A Lisp-culture source file for an HTML parsing module: <RT-code>html_parser_mod.el</RT-code></li>
+        <li>A C language source file that initializes an HTTP server: <RT-code>init-http-srv.c</RT-code></li>
+      </ul>
+
     </RT-article>
   </body>
 </html>
index e01c98d..4f6c3d2 100644 (file)
 
       <RT-TOC level="1"></RT-TOC>
 
+      <p>
+        We are not big on revising inherited or legacy code for naming conventions, though no one will hold you back. However, new code should follow these conventions.
+      </p>
+
       <h1>Object vs. instance nomenclature</h1>
       <p>
-        We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term <RT-term>instance</RT-term>.
+        We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term <RT-term>instance</RT-term>. It is too much of an ask to remove the word <RT-term>object</RT-term> from the language, and this is reflected in the fact that few people use the term precisely.
+      </p>
+      <p>
+        Hence, a programmer can talk about math objects, as things found in mathematics, and C objects, as things found in the C language, even though they are not instantiated from classes, or might not even be data. For example, the <RT-term>for loop</RT-term> is a C object.
       </p>
 
       <h1>Identifier Names</h1>
         <li>Globals: <RT-code>UPPER_SNAKE_CASE</RT-code></li>
       </ul>
 
+      <h2>Proper nouns and acronyms</h2>
+      <p>
+        Even in <RT-code>PascalCase</RT-code> and <RT-code>snake_case</RT-code>, proper nouns and acronyms remain capitalized, as per standard English language conventions (e.g., <RT-code>IEEE_publication_count</RT-code>).
+      </p>
+
+      <h2>Abbreviations</h2>
+      <p>
+        For outer scope identifiers we spell things out for clarity. This follows Lisp programming culture. This makes it clear for people who are 'not in the club' to be able to get started and read the code, and tends to be self documenting. Our file system names mirror our program identifier rules, so you find things such as 'library', 'source', spelled out.
+      </p>
+      <p>
+        For long words, inner scope identifiers, temporary variables, and conventional suffixes, abbreviations become more common. By the time we get to inner loops variable names, such as loop counters are typically 1 letter long. 
+      </p>
+
       <h2>Primary and secondary separators (snake-kebab_case)</h2>
       <p>
         If a language supports the hyphen (<RT-code>-</RT-code>) in identifiers (such as Common Lisp and Emacs Lisp), the identifier is written in <RT-code>snake-kebab_case</RT-code>. The hyphen is used as the primary word separator. The underscore (<RT-code>_</RT-code>) is then reserved strictly as a secondary separator to group logically related portions of an identifier or to denote semantic boundaries, acting as a structural namespace within the symbol (e.g., <RT-code>city-scape_building-height</RT-code>).
       </ul>
 
       <ul>
+        <li><RT-code>*_dir</RT-code> : directory name</li>
         <li><RT-code>*_dirn</RT-code> : directory name</li>
         <li><RT-code>*_dirp</RT-code> : directory path</li>
         <li><RT-code>*_dirpr</RT-code> / <RT-code>*_dirpa</RT-code> : relative / absolute directory path</li>
+        <li><RT-code>*_file</RT-code> : file name</li>
         <li><RT-code>*_filen</RT-code> : file name</li>
         <li><RT-code>*_filep</RT-code> : file path</li>
         <li><RT-code>*_filepr</RT-code> / <RT-code>*_fpa</RT-code> : relative / absolute file path</li>
         <li><RT-code>*_fsnodp</RT-code> : file system node path (when type is unspecified)</li>
       </ul>
 
-      <h2>Proper nouns and acronyms</h2>
-      <p>
-        Even in <RT-code>PascalCase</RT-code> and <RT-code>snake_case</RT-code>, proper nouns and acronyms remain capitalized, as per standard English language conventions (e.g., <RT-code>IEEE_publication_count</RT-code>).
-      </p>
+
 
       <h1>Comma separated lists</h1>
 
index 0b79a95..8eaf73c 100644 (file)
@@ -3,11 +3,11 @@
 .EXPORT_ALL_VARIABLES:
 
 RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile
-include $(RT_INCOMMON)/make/environment_RT_1.mk
+include $(RT_MAKEFILE_DP)/environment_RT_1.mk
 
 .PHONY: usage 
 usage:
-       @printf "Usage: make [usage|information|all|lib|CLI|kmod|clean]\n"
+       @printf "Usage: make [usage|version|information|all|lib|CLI|kmod|clean]\n"
 
 .PHONY: version 
 version:
diff --git a/developer/tool/promote b/developer/tool/promote
new file mode 100755 (executable)
index 0000000..349bfd7
--- /dev/null
@@ -0,0 +1,289 @@
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+import os ,sys ,shutil ,stat ,pwd ,grp ,glob ,tempfile
+
+HELP = """usage: promote {write|clean|ls|diff|help|dry write}
+  write       Writes promoted files from scratchpad/made into consumer/made. Only updates newer files.
+  clean       Remove all contents of the consumer/made directory.
+  ls          List consumer/made as an indented tree: PERMS  OWNER  NAME.
+  diff        List files in consumer/made that are not in scratchpad/made, or are newer.
+  help        Show this message.
+  dry write   Preview what write would do without modifying the filesystem.
+"""
+
+SETUP_MUST_BE = "developer/tool/setup"
+DEFAULT_DIR_MODE = 0o700
+DEFAULT_FILE_MODE = 0o400
+
+def exit_with_status(msg ,code=1):
+  print(f"release: {msg}" ,file=sys.stderr)
+  sys.exit(code)
+
+def assert_setup():
+  setup_val = os.environ.get("SETUP" ,"")
+  if(setup_val != SETUP_MUST_BE):
+    hint = (
+      "SETUP is not 'developer/tool/setup'.\n"
+      "Enter the project with:  . setup developer\n"
+      "That script exports: ROLE=developer; SETUP=$ROLE/tool/setup"
+    )
+    exit_with_status(f"bad environment: SETUP='{setup_val}'. {hint}")
+
+def repo_home():
+  rh = os.environ.get("REPO_HOME")
+  if( not rh ):
+    exit_with_status("REPO_HOME not set")
+  return rh
+
+def dpath(*parts):
+  return os.path.join(
+    repo_home()
+    ,"developer"
+    ,*parts
+  )
+
+def cpath(*parts):
+  return os.path.join(
+    repo_home()
+    ,"consumer"
+    ,"made"
+    ,*parts
+  )
+
+def dev_root():
+  return dpath()
+
+def consumer_root():
+  return cpath()
+
+def _display_src(p_abs: str) -> str:
+  try:
+    if( os.path.commonpath([dev_root()]) == os.path.commonpath([dev_root() ,p_abs]) ):
+      return os.path.relpath(p_abs ,dev_root())
+  except Exception:
+    pass
+  return p_abs
+
+def _display_dst(p_abs: str) -> str:
+  try:
+    rel = os.path.relpath(
+      p_abs
+      ,consumer_root()
+    )
+    rel = "" if rel == "." else rel
+    return "$REPO_HOME/consumer/made" + ("/" + rel if rel else "")
+  except Exception:
+    return p_abs
+
+def ensure_mode(path_str ,mode):
+  try: os.chmod(path_str ,mode)
+  except Exception: pass
+
+def ensure_dir(path_str ,mode=DEFAULT_DIR_MODE ,dry=False):
+  if(dry):
+    if( not os.path.isdir(path_str) ):
+      shown = _display_dst(path_str) if path_str.startswith(consumer_root()) else (
+        os.path.relpath(path_str ,dev_root()) if path_str.startswith(dev_root()) else path_str
+      )
+      print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
+    return
+  os.makedirs(path_str ,exist_ok=True)
+  ensure_mode(path_str ,mode)
+
+def filemode(m):
+  try: return stat.filemode(m)
+  except Exception: return oct(m & 0o777)
+
+def owner_group(st):
+  try: return f"{pwd.getpwuid(st.st_uid).pw_name}:{grp.getgrgid(st.st_gid).gr_name}"
+  except Exception: return f"{st.st_uid}:{st.st_gid}"
+
+def list_tree(root_dp):
+  if( not os.path.isdir(root_dp) ):
+    return
+
+  entries = []
+  def gather(path_str ,depth ,is_root):
+    try:
+      it = list(os.scandir(path_str))
+    except FileNotFoundError:
+      return
+    dirs = [e for e in it if e.is_dir(follow_symlinks=False)]
+    files = [e for e in it if not e.is_dir(follow_symlinks=False)]
+    dirs.sort(key=lambda e: e.name)
+    files.sort(key=lambda e: e.name)
+
+    if(is_root):
+      for f in ( e for e in files if e.name.startswith(".") ):
+        st = os.lstat(f.path)
+        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
+      for d in dirs:
+        st = os.lstat(d.path)
+        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
+        gather(d.path ,depth + 1 ,False)
+      for f in ( e for e in files if not e.name.startswith(".") ):
+        st = os.lstat(f.path)
+        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
+    else:
+      for d in dirs:
+        st = os.lstat(d.path)
+        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
+        gather(d.path ,depth + 1 ,False)
+      for f in files:
+        st = os.lstat(f.path)
+        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
+
+  gather(root_dp ,1 ,True)
+
+  ogw = 0
+  for _isdir ,_depth ,_perms ,ownergrp ,_name in entries:
+    if( len(ownergrp) > ogw ):
+      ogw = len(ownergrp)
+
+  print("consumer/made/")
+  for isdir ,depth ,perms ,ownergrp ,name in entries:
+    indent = "  " * depth
+    print(f"{perms}  {ownergrp:<{ogw}}  {indent}{name}")
+
+def copy_one(src_abs ,dst_abs ,mode ,dry=False):
+  src_show = _display_src(src_abs)
+  dst_show = _display_dst(dst_abs)
+  parent = os.path.dirname(dst_abs)
+  os.makedirs(parent ,exist_ok=True)
+
+  if(dry):
+    if( os.path.exists(dst_abs) ):
+      print(f"(dry) unlink '{dst_show}'")
+    print(f"(dry) install -m {oct(mode)[2:]} -D '{src_show}' '{dst_show}'")
+    return
+
+  fd ,tmp_path = tempfile.mkstemp(prefix=".tmp." ,dir=parent)
+  try:
+    with os.fdopen(fd ,"wb") as tmpf, open(src_abs ,"rb") as sf:
+      shutil.copyfileobj(sf ,tmpf)
+      tmpf.flush()
+    os.chmod(tmp_path ,mode)
+    os.replace(tmp_path ,dst_abs)
+  finally:
+    try:
+      if( os.path.exists(tmp_path) ):
+        os.unlink(tmp_path)
+    except Exception:
+      pass
+
+  print(f"+ install -m {oct(mode)[2:]} '{src_show}' '{dst_show}'")
+
+def cmd_write(dry=False):
+  assert_setup()
+  ensure_dir(cpath() ,DEFAULT_DIR_MODE ,dry=dry)
+
+  src_root = dpath(
+    "scratchpad"
+    ,"made"
+  )
+  if( not os.path.isdir(src_root) ):
+    exit_with_status(f"cannot find developer scratchpad made at '{_display_src(src_root)}'")
+
+  wrote = False
+  for root ,dirs ,files in os.walk(src_root):
+    dirs.sort()
+    files.sort()
+    for fn in files:
+      src_abs = os.path.join(root ,fn)
+      rel = os.path.relpath(src_abs ,src_root)
+      dst_abs = os.path.join(cpath() ,rel)
+
+      if( os.path.exists(dst_abs) ):
+        src_mtime = os.stat(src_abs).st_mtime
+        dst_mtime = os.stat(dst_abs).st_mtime
+        if(dst_mtime >= src_mtime):
+          continue
+
+      st = os.stat(src_abs)
+      is_exec = st.st_mode & stat.S_IXUSR
+      mode = 0o500 if is_exec else 0o400
+
+      copy_one(
+        src_abs
+        ,dst_abs
+        ,mode
+        ,dry=dry
+      )
+      wrote = True
+
+  if( not wrote ):
+    print(f"(info) nothing new to promote from {_display_src(src_root)}")
+
+def cmd_diff():
+  assert_setup()
+  dst_root = cpath()
+  src_root = dpath(
+    "scratchpad"
+    ,"made"
+  )
+
+  if( not os.path.isdir(dst_root) ):
+    print(f"Consumer made directory {_display_dst(dst_root)} does not exist.")
+    return
+
+  found_diff = False
+  for root ,dirs ,files in os.walk(dst_root):
+    dirs.sort()
+    files.sort()
+    for fn in files:
+      dst_abs = os.path.join(root ,fn)
+      rel = os.path.relpath(dst_abs ,dst_root)
+      src_abs = os.path.join(src_root ,rel)
+
+      if( not os.path.exists(src_abs) ):
+        print(f"Orphaned in consumer made: {rel}")
+        found_diff = True
+      else:
+        dst_mtime = os.stat(dst_abs).st_mtime
+        src_mtime = os.stat(src_abs).st_mtime
+        if(dst_mtime > src_mtime):
+          print(f"Newer in consumer made: {rel}")
+          found_diff = True
+
+  if( not found_diff ):
+    print("No differences found. Consumer made matches developer scratchpad made.")
+
+def cmd_clean():
+  assert_setup()
+  consumer_root_dir = cpath()
+  if( not os.path.isdir(consumer_root_dir) ):
+    return
+  for name in os.listdir(consumer_root_dir):
+    p = os.path.join(consumer_root_dir ,name)
+    if( os.path.isdir(p) and not os.path.islink(p) ):
+      shutil.rmtree(p ,ignore_errors=True)
+    else:
+      try: os.unlink(p)
+      except FileNotFoundError: pass
+
+def CLI():
+  if( len(sys.argv) < 2 ):
+    print(HELP)
+    return
+  cmd ,*args = sys.argv[1:]
+  if(cmd == "write"):
+    cmd_write(dry=False)
+  elif(cmd == "clean"):
+    cmd_clean()
+  elif(cmd == "ls"):
+    list_tree(cpath())
+  elif(cmd == "diff"):
+    cmd_diff()
+  elif(cmd == "help"):
+    print(HELP)
+  elif(cmd == "dry"):
+    if( args and args[0] == "write" ):
+      cmd_write(dry=True)
+    else:
+      print(HELP)
+  else:
+    print(HELP)
+
+if __name__ == "__main__":
+  CLI()
diff --git a/developer/tool/release b/developer/tool/release
deleted file mode 100755 (executable)
index 24f260c..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import os ,sys ,shutil ,stat ,pwd ,grp ,glob ,tempfile
-
-HELP = """usage: release {write|clean|ls|diff|help|dry write}
-  write       Writes promoted files from scratchpad/stage into consumer/release. Only updates newer files.
-  clean       Remove all contents of the consumer/release directory.
-  ls          List consumer/release as an indented tree: PERMS  OWNER  NAME.
-  diff        List files in consumer/release that are not in scratchpad/stage, or are newer.
-  help        Show this message.
-  dry write   Preview what write would do without modifying the filesystem.
-"""
-
-SETUP_MUST_BE = "developer/tool/setup"
-DEFAULT_DIR_MODE = 0o700
-DEFAULT_FILE_MODE = 0o400
-
-def exit_with_status(msg ,code=1):
-  print(f"release: {msg}" ,file=sys.stderr)
-  sys.exit(code)
-
-def assert_setup():
-  setup_val = os.environ.get("SETUP" ,"")
-  if(setup_val != SETUP_MUST_BE):
-    hint = (
-      "SETUP is not 'developer/tool/setup'.\n"
-      "Enter the project with:  . setup developer\n"
-      "That script exports: ROLE=developer; SETUP=$ROLE/tool/setup"
-    )
-    exit_with_status(f"bad environment: SETUP='{setup_val}'. {hint}")
-
-def repo_home():
-  rh = os.environ.get("REPO_HOME")
-  if( not rh ):
-    exit_with_status("REPO_HOME not set")
-  return rh
-
-def dpath(*parts):
-  return os.path.join(
-    repo_home()
-    ,"developer"
-    ,*parts
-  )
-
-def cpath(*parts):
-  return os.path.join(
-    repo_home()
-    ,"consumer"
-    ,"release"
-    ,*parts
-  )
-
-def dev_root():
-  return dpath()
-
-def consumer_root():
-  return cpath()
-
-def _display_src(p_abs: str) -> str:
-  try:
-    if( os.path.commonpath([dev_root()]) == os.path.commonpath([dev_root() ,p_abs]) ):
-      return os.path.relpath(p_abs ,dev_root())
-  except Exception:
-    pass
-  return p_abs
-
-def _display_dst(p_abs: str) -> str:
-  try:
-    rel = os.path.relpath(
-      p_abs
-      ,consumer_root()
-    )
-    rel = "" if rel == "." else rel
-    return "$REPO_HOME/consumer/release" + ("/" + rel if rel else "")
-  except Exception:
-    return p_abs
-
-def ensure_mode(path_str ,mode):
-  try: os.chmod(path_str ,mode)
-  except Exception: pass
-
-def ensure_dir(path_str ,mode=DEFAULT_DIR_MODE ,dry=False):
-  if(dry):
-    if( not os.path.isdir(path_str) ):
-      shown = _display_dst(path_str) if path_str.startswith(consumer_root()) else (
-        os.path.relpath(path_str ,dev_root()) if path_str.startswith(dev_root()) else path_str
-      )
-      print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
-    return
-  os.makedirs(path_str ,exist_ok=True)
-  ensure_mode(path_str ,mode)
-
-def filemode(m):
-  try: return stat.filemode(m)
-  except Exception: return oct(m & 0o777)
-
-def owner_group(st):
-  try: return f"{pwd.getpwuid(st.st_uid).pw_name}:{grp.getgrgid(st.st_gid).gr_name}"
-  except Exception: return f"{st.st_uid}:{st.st_gid}"
-
-def list_tree(root_dp):
-  if( not os.path.isdir(root_dp) ):
-    return
-
-  entries = []
-  def gather(path_str ,depth ,is_root):
-    try:
-      it = list(os.scandir(path_str))
-    except FileNotFoundError:
-      return
-    dirs = [e for e in it if e.is_dir(follow_symlinks=False)]
-    files = [e for e in it if not e.is_dir(follow_symlinks=False)]
-    dirs.sort(key=lambda e: e.name)
-    files.sort(key=lambda e: e.name)
-
-    if(is_root):
-      for f in ( e for e in files if e.name.startswith(".") ):
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-      for d in dirs:
-        st = os.lstat(d.path)
-        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
-        gather(d.path ,depth + 1 ,False)
-      for f in ( e for e in files if not e.name.startswith(".") ):
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-    else:
-      for d in dirs:
-        st = os.lstat(d.path)
-        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
-        gather(d.path ,depth + 1 ,False)
-      for f in files:
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-
-  gather(root_dp ,1 ,True)
-
-  ogw = 0
-  for _isdir ,_depth ,_perms ,ownergrp ,_name in entries:
-    if( len(ownergrp) > ogw ):
-      ogw = len(ownergrp)
-
-  print("consumer/release/")
-  for isdir ,depth ,perms ,ownergrp ,name in entries:
-    indent = "  " * depth
-    print(f"{perms}  {ownergrp:<{ogw}}  {indent}{name}")
-
-def copy_one(src_abs ,dst_abs ,mode ,dry=False):
-  src_show = _display_src(src_abs)
-  dst_show = _display_dst(dst_abs)
-  parent = os.path.dirname(dst_abs)
-  os.makedirs(parent ,exist_ok=True)
-
-  if(dry):
-    if( os.path.exists(dst_abs) ):
-      print(f"(dry) unlink '{dst_show}'")
-    print(f"(dry) install -m {oct(mode)[2:]} -D '{src_show}' '{dst_show}'")
-    return
-
-  fd ,tmp_path = tempfile.mkstemp(prefix=".tmp." ,dir=parent)
-  try:
-    with os.fdopen(fd ,"wb") as tmpf, open(src_abs ,"rb") as sf:
-      shutil.copyfileobj(sf ,tmpf)
-      tmpf.flush()
-    os.chmod(tmp_path ,mode)
-    os.replace(tmp_path ,dst_abs)
-  finally:
-    try:
-      if( os.path.exists(tmp_path) ):
-        os.unlink(tmp_path)
-    except Exception:
-      pass
-
-  print(f"+ install -m {oct(mode)[2:]} '{src_show}' '{dst_show}'")
-
-def cmd_write(dry=False):
-  assert_setup()
-  ensure_dir(cpath() ,DEFAULT_DIR_MODE ,dry=dry)
-
-  src_root = dpath(
-    "scratchpad"
-    ,"stage"
-  )
-  if( not os.path.isdir(src_root) ):
-    exit_with_status(f"cannot find developer scratchpad stage at '{_display_src(src_root)}'")
-
-  wrote = False
-  for root ,dirs ,files in os.walk(src_root):
-    dirs.sort()
-    files.sort()
-    for fn in files:
-      src_abs = os.path.join(root ,fn)
-      rel = os.path.relpath(src_abs ,src_root)
-      dst_abs = os.path.join(cpath() ,rel)
-
-      if( os.path.exists(dst_abs) ):
-        src_mtime = os.stat(src_abs).st_mtime
-        dst_mtime = os.stat(dst_abs).st_mtime
-        if(dst_mtime >= src_mtime):
-          continue
-
-      st = os.stat(src_abs)
-      is_exec = st.st_mode & stat.S_IXUSR
-      mode = 0o500 if is_exec else 0o400
-
-      copy_one(
-        src_abs
-        ,dst_abs
-        ,mode
-        ,dry=dry
-      )
-      wrote = True
-
-  if( not wrote ):
-    print(f"(info) nothing new to promote from {_display_src(src_root)}")
-
-def cmd_diff():
-  assert_setup()
-  dst_root = cpath()
-  src_root = dpath(
-    "scratchpad"
-    ,"stage"
-  )
-
-  if( not os.path.isdir(dst_root) ):
-    print(f"Release directory {_display_dst(dst_root)} does not exist.")
-    return
-
-  found_diff = False
-  for root ,dirs ,files in os.walk(dst_root):
-    dirs.sort()
-    files.sort()
-    for fn in files:
-      dst_abs = os.path.join(root ,fn)
-      rel = os.path.relpath(dst_abs ,dst_root)
-      src_abs = os.path.join(src_root ,rel)
-
-      if( not os.path.exists(src_abs) ):
-        print(f"Orphaned in release: {rel}")
-        found_diff = True
-      else:
-        dst_mtime = os.stat(dst_abs).st_mtime
-        src_mtime = os.stat(src_abs).st_mtime
-        if(dst_mtime > src_mtime):
-          print(f"Newer in release: {rel}")
-          found_diff = True
-
-  if( not found_diff ):
-    print("No differences found. Release matches stage.")
-
-def cmd_clean():
-  assert_setup()
-  consumer_root_dir = cpath()
-  if( not os.path.isdir(consumer_root_dir) ):
-    return
-  for name in os.listdir(consumer_root_dir):
-    p = os.path.join(consumer_root_dir ,name)
-    if( os.path.isdir(p) and not os.path.islink(p) ):
-      shutil.rmtree(p ,ignore_errors=True)
-    else:
-      try: os.unlink(p)
-      except FileNotFoundError: pass
-
-def CLI():
-  if( len(sys.argv) < 2 ):
-    print(HELP)
-    return
-  cmd ,*args = sys.argv[1:]
-  if(cmd == "write"):
-    cmd_write(dry=False)
-  elif(cmd == "clean"):
-    cmd_clean()
-  elif(cmd == "ls"):
-    list_tree(cpath())
-  elif(cmd == "diff"):
-    cmd_diff()
-  elif(cmd == "help"):
-    print(HELP)
-  elif(cmd == "dry"):
-    if( args and args[0] == "write" ):
-      cmd_write(dry=True)
-    else:
-      print(HELP)
-  else:
-    print(HELP)
-
-if __name__ == "__main__":
-  CLI()
diff --git a/document/Intro.html b/document/Intro.html
new file mode 100644 (file)
index 0000000..6852689
--- /dev/null
@@ -0,0 +1,394 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Introduction to Harmony</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-09" 
+        title="Introduction to Harmony">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Purpose</h1>
+      <p>
+        Harmony provides a language-agnostic project directory structure and maintenance tools for long-lived, multi-person team software development. The structure exists to enforce:
+      </p>
+      <ol>
+        <li>Clarity about where things live.
+          <ul>
+            <li>Role-based work areas.</li>
+            <li>Separation of skeleton, team member authored, machine-made, and third-party installed software.</li>
+          </ul>
+        </li>
+        <li>A safe, predictable build and promotion workflow.</li>
+      </ol>
+
+      <p>
+        Harmony is a project skeleton, a set of tools and directories that have been checked into a git repository. To create a Harmony-based project, the project administrator performs these steps:
+      </p>
+      <ol>
+        <li>Clone the Harmony project to a local directory.</li>
+        <li>Remove the <RT-code>.git</RT-code> tree.</li>
+        <li>Rename the Harmony directory to the name of the new project.</li>
+        <li>Rename the <RT-code>0pus_Harmony</RT-code> file to reflect the name of the new project.</li>
+        <li>Add a line to the <RT-code>shared/tool/version</RT-code> file for the new project.</li>
+        <li><RT-code>git init -b core_developer_branch</RT-code></li>
+      </ol>
+
+      <p>
+        The order is important so as to protect the Harmony skeleton project from accidental check-ins from users.
+      </p>
+      <p>
+        Leave the Harmony skeleton version in the version file, so that project administrators can understand what has changed in the skeleton over time.
+      </p>
+      <p>
+        The <RT-code>core_developer_branch</RT-code> is the branch that the core development team works on. Project releases are moved to release branches.
+      </p>
+
+      <h1>Environment Setup</h1>
+      <p>
+        Python programmers who use virtual environments will be familiar with an analogous process. The term 'virtual environment' does not invoke any hardware virtualization features; rather, it is a local environment setup. To avoid confusion, a Harmony user refers instead to the 'project setup'. The command to establish the environment is called 'setup' instead of 'activate'. Also note that in Harmony, unlike in Python, there are multiple setups available, each tailored to the specific role a person takes on.
+      </p>
+      <p>
+        As of the time of this writing, the defined roles are: <strong>administrator</strong>, <strong>consumer</strong>, <strong>developer</strong>, and <strong>tester</strong>. A person takes on a role by sourcing the top-level <RT-code>setup</RT-code> script and giving the target role as an argument. For example, in a bash shell with <RT-code>></RT-code> as the prompt, the command is:
+      </p>
+
+      <RT-code>
+        > . setup &lt;role&gt;
+      </RT-code>
+
+      <p>
+        Specifically for the developer role:
+      </p>
+        
+      <RT-code>
+        > . setup developer
+      </RT-code>
+
+      <p>
+        For the administrator role:
+      </p>
+        
+      <RT-code>
+        > . setup administrator
+      </RT-code>
+
+      <p>
+        Instead of starting with a period, the <RT-code>source</RT-code> command can be spelled out explicitly, for example:
+      </p>
+      
+      <RT-code>
+        > source setup tester
+      </RT-code>
+
+      <p>
+        Behind the scenes, the <RT-code>setup</RT-code> script performs the following actions:
+      </p>
+
+      <ul>
+        <li>Sources the project-wide setup (<RT-code>shared/tool/setup</RT-code>) to establish the core environment variables (e.g., <RT-code>REPO_HOME</RT-code> and <RT-code>PROJECT</RT-code>).</li>
+        <li>Conditionally sources <RT-code>shared/authored/setup</RT-code> (if present) to apply administrator-injected, project-specific tool configurations.</li>
+        <li>Configures the <RT-code>PATH</RT-code> to include shared tools, library environments, and the specific <RT-code>&lt;role&gt;/tool</RT-code> directory.</li>
+        <li>Changes the working directory into the specified role's workspace.</li>
+        <li>Sources the <RT-code>&lt;role&gt;/tool/setup</RT-code> script. While the earlier steps apply the standard Harmony skeleton setup, this final step applies the role setup that is customized for this specific project.</li>
+      </ul>
+
+      <h1>After git clone</h1>
+      <p>
+        Because git does not track certain directories (such as <RT-code>shared/third_party/</RT-code> and <RT-code>consumer/made/</RT-code>), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
+      </p>
+
+      <h2>Third-party tools</h2>
+      <p>
+        Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator configures the project to expect these tools in the <RT-code>shared/third_party</RT-code> directory.
+      </p>
+      <p>
+        Because multiple team members will have to repeat the third-party install process after cloning a project, the administrator should carefully document the third party tools installation steps and place the resulting documents in the <RT-code>shared/document</RT-code> directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under <RT-code>shared/third_party/</RT-code>.)
+      </p>
+      <p>
+        If a person clones the project and does not set up third-party tools, commands will fall through the search path and perhaps find system-installed tools. Using system default tools is generally undesirable because each person who clones the project might be running different versions. This variation makes it harder for team members to coordinate and for consumers of the project work product to build functioning tools.
+      </p>
+
+      <h2>Consumer build</h2>
+      <p>
+        In this section we use the term 'consumer' to mean any team member that wants to make use of the project work product. The tester will want to test it, and the consumer role will want to deploy it, etc.
+      </p>
+      <p>
+        An earlier version of Harmony used platform-specific <RT-code>made</RT-code> directories, but this left binaries in the repository and added excessive complexity.
+        So as to avoid that mess, the current version of Harmony requires a work product consumer to run a local build after cloning the project. The results of the build will appear in the <RT-code>consumer/made</RT-code> directory.
+      </p>
+      <p>
+        To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as <RT-code>developer/document/build.html</RT-code>. 
+      </p>
+      <p>
+        The consumer must then read this document and execute the described steps to compile the source and locally populate the <RT-code>consumer/made</RT-code> directory.
+      </p>
+
+      <p>
+        Typically the consumer build procedure will be a variation of the following:
+      </p>
+
+      <ol>
+        <li>bash</li>
+        <li><RT-code>> cd <project></RT-code></li>
+        <li><RT-code>> . developer</RT-code></li>
+        <li><RT-code>> make CLI</RT-code></li>
+        <li><RT-code>> promote write</RT-code></li>
+        <li><RT-code>> exit</RT-code></li>
+        <li>bash</li>
+        <li><RT-code>> cd <project></RT-code></li>
+        <li><RT-code>> . <role></RT-code></li>
+     </ol>
+
+      <p>This opens a bash shell for the build, makes the work product, then promotes it to the consumer's workspace. The last two lines put the consumer into role specific workspace.  From there the tester can start testing, and the consumer role can work on deployment. (Commonly deployment is a matter of adding the <RT-code>consumer/made</RT-code> directory into the executable search path.)
+      </p>
+
+      <h1>Directory name</h1>
+      <p>
+        This section is discusses our thinking in naming the files and directories found in the Harmony skeleton.
+      </p>
+      <p>
+        A directory name is considered to be a <RT-term>property</RT-term> given to each file contained in the directory. A full path then forms a semantic sentence describing each file.
+      </p>
+      <p>
+        Because a directory name represents a property, it is rarely plural. For example, when each and every file in a directory is a test, the directory is named <RT-code>test</RT-code>.
+      </p>
+      <p>
+        We run into limitations when using a conventional file system as though it were a property based file system. One limitation is that we are forced to choose a single directory name for each file. When a set of files in a directory all share the same multiple properties, we can use a compound directory name with the properties separated by an underscore, but it is impractical to specify overlapping directory groupings, i.e. we can't arbitrarily define any number of properties for a file in this manner. We will see later how this affects Harmony when discussing <RT-term>authored</RT-term> files.
+      </p>
+      <p>
+        The following list presents each property type in order of preference when naming directories.
+      </p>
+      <ul>
+        <li><strong>Role Association</strong> (<RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, <RT-code>consumer</RT-code>): Identifies the persona, whether human or AI, intended to interact with the files.</li>
+        <li><strong>Provenance</strong> (<RT-code>authored</RT-code>, <RT-code>made</RT-code>): Indicates whether the file was created by an intellect or mechanically produced by a tool.</li>
+        <li><strong>Capability</strong> (<RT-code>tool</RT-code>, <RT-code>document</RT-code>, <RT-code>experiment</RT-code>): Describes the primary function or structural nature of the file.</li>
+        <li><strong>Lifecycle State</strong> (<RT-code>scratchpad</RT-code>, <RT-code>stage</RT-code>): Denotes the persistence, volatility, or promotion status of the file.</li>
+        <li><strong>Tracking Status</strong> (<RT-code>tracked</RT-code>, <RT-code>untracked</RT-code>): Specifies the version control expectations for the artifacts.</li>
+      </ul>
+
+      <h1>Document directories</h1>
+      <p>
+        There is a directory for documents that talks about the project as a whole, one for each role, one for tools that are shared among the roles, and the released work product probably comes with a document directory of its own.
+      </p>
+      <ul>
+        <li><RT-code>document/</RT-code> : Top-level onboarding, project-wide structure, such as this document.</li>
+        <li><RT-code>consumer/made/document/</RT-code> : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).</li>
+        <li><RT-code>administrator/document/</RT-code> : Documentation for maintaining the project skeleton and global tools.</li>
+        <li><RT-code>developer/document/</RT-code> : Documentation for developers, including coding standards and internal API guides.</li>
+        <li><RT-code>tester/document/</RT-code> : Documentation for testers detailing test plans and tools.</li>
+        <li><RT-code>shared/document/</RT-code> : Documentation on installing and configuring shared tools.</li>
+      </ul>
+
+      <p>
+        Note that the <RT-code>consumer/made</RT-code> directory is untracked by git and maintained by a tool. (Said tool is <RT-code>developer/tool/promote</RT-code>. It is owned and used by the developer as part of the build process.) Documents that are being promoted for eventual release, and appear in the <RT-code>made</RT-code> directory originate from somewhere from the <RT-code>developer/authored</RT-code> directory depending on how the developer has organized his workspace, but probably in <RT-code>developer/authored/document</RT-code>. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now.
+      </p>
+
+      <p>
+        Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+      </p>
+
+
+      <h1>Authored, made, scratchpad, third_party, inherited, customizations</h1>
+
+      <p>
+        Files found in a directory named <RT-code>authored</RT-code> were written by project team members. They did not come with the Harmony skeleton, nor with the installation of other software. Project build tools treat <RT-code>authored</RT-code> directories as strictly read-only. Typically these files constitute the intellectual property of a project.
+      </p>
+
+      <p>
+      All source code that gets built into a promotion or project release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/make</RT-code> file.
+      </p>
+
+      <p>
+        When the Harmony version line in the <RT-code>shared/tool/version</RT-code> file is left in place, it is straightforward for a project administrator to determine which Harmony skeleton files have been edited in a project, and which new files have been added.
+      </p>
+
+      <p>
+        [As a historical note, in the first version of Harmony a directory or file was marked as <RT-term>authored</RT-term> by adding a pencil suffix, '🖉'. Tools then respected this, for example by refusing to delete such files. However, this ad hoc approach to properties was awkward, and people had difficulty typing the marker. When the pencils were dropped, a number of authored files, such as an edited <RT-code>developer/tool/make🖉</RT-code> file, were no longer explicitly marked as authored.]
+      </p>
+
+      <p>
+        Files found in a directory named <RT-code>scratchpad</RT-code> are not tracked. Hence, a <RT-code>git clone</RT-code> will always return empty <RT-code>scratchpad</RT-code> directories. It is common for tools to place intermediate files on a scratchpad. It is also common for files to be staged on a scratchpad. Tools play nice and use subdirectories on the pad, so a person who is aware of those subdirectory names can use a scratchpad as a temporary directory. There is a scratchpad maintenance tool that comes with the Harmony, called unimaginatively, <RT-code>scratchpad</RT-code>. Pay attention as one of its commands is <RT-code>clear</RT-code>, and that deletes everything on the current directory's scratchpad.
+      </p>
+
+      <p>
+        Third party software is installed under <RT-code>shared/third_party</RT-code>.
+      </p>
+
+      <p>
+        Other files are said to be <RT-term>inherited</RT-term>, or to be <RT-term>customizations</RT-term>.
+      </p>
+
+      <h2>Untracked directories</h2>
+      <ol>
+        <li><RT-code>consumer/made/</RT-code></li>
+        <li><RT-code>shared/third_party/</RT-code></li>
+        <li><RT-code>**/scratchpad/</RT-code></li>
+      </ol>
+
+      <h1>Top-level repository layout</h1>
+      <p>
+        A team member will source the project setup file to take on a role. As of this writing, the supported roles are: <RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, and <RT-code>consumer</RT-code>.
+      </p>
+
+      <ul>
+        <li><RT-code>administrator/</RT-code> : Project-local tools and skeleton maintenance.</li>
+        <li><RT-code>developer/</RT-code> : Primary workspace for developers.</li>
+        <li><RT-code>tester/</RT-code> : Regression and validation workspace for testers.</li>
+        <li><RT-code>consumer/</RT-code> : Consumption workspace holding the <RT-code>consumer/made</RT-code> directory.</li>
+        <li><RT-code>shared/</RT-code> : Shared ecosystem tools and global environments.</li>
+      </ul>
+
+      <h2>The administrator work area</h2>
+      <p>
+        This directory holds the tools and documentation used to manage the project as a whole. It includes the HTML documentation for the project ontology and workflow, as well as project-local tools utilized by the administrator to maintain the Harmony skeleton.
+      </p>
+
+      <h2>The developer work area</h2>
+      <p>
+        This directory is entered by first going to the top-level directory of the project, then sourcing <RT-code>. setup developer</RT-code>.
+      </p>
+      <ul>
+        <li><RT-code>authored/</RT-code> : Human-written source. Tracked by Git.</li>
+        <li><RT-code>made/</RT-code> : Tracked artifacts generated by tools (e.g., links to CLI entry points).</li>
+        <li><RT-code>experiment/</RT-code> : Try-it-here code. Short-lived spot testing.</li>
+        <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>made</RT-code> directory for promotions.</li>
+        <li><RT-code>tool/</RT-code> : Developer-specific tools (like the promote and make scripts).</li>
+      </ul>
+
+      <h2>The tester work area</h2>
+      <p>
+        This directory is dedicated to formal testing, including regression suites. While a developer can run and keep informal spot tests in their <RT-code>experiment/</RT-code> directory, any experiment promoted to a formal test is moved here. This enforces the boundary between writing code and validating it.
+      </p>
+
+      <h2>The shared tree</h2>
+      <p>
+        This directory contains ecosystem tools and global environments available to all roles. This includes the shared tool directory, as well as third-party installations (like Python virtual environments or compilers) required by the project. To assist in project specific modifications to the Harmony skeleton, Harmony comes with an empty <RT-code>shared/authored</RT-code> directory that is listed earlier in the executable search path than <RT-code>shared/tool</RT-code>.
+      </p>
+
+      <h2>The consumer tree</h2>
+      <p>
+        The <RT-code>consumer/made/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/made</RT-code> directory is git-ignored and treated as a transient deployment target.
+      </p>
+      <p>
+        Artifacts arrive in the <RT-code>consumer/made/</RT-code> tree <em>only</em> when the developer invokes the promote script, which performs a flat-copy from the developer's <RT-code>scratchpad/made</RT-code> directory.
+      </p>
+
+      <h1>Workflow</h1>
+
+      <p> See the document "Product Development Roles and Workflow" for more details.</p>
+
+      <h1>Developer promotion and project releases</h1>
+
+      <p>
+        As a first step, a developer creates a <RT-term>promotion candidate</RT-term> inside of the <RT-code>consumer/made/</RT-code> directory. This is typically done by running <RT-code>make</RT-code> to stage the artifacts into <RT-code>scratchpad/made</RT-code>, where they can be experimented on, followed by running <RT-code>promote write</RT-code>.  The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton. The promotion candidate remains stable until the next <RT-term>promotion</RT-term>.
+      </p>
+      <p>
+        Then the tester runs tests on the promotion candidate. Tests must only read from the <RT-code>consumer/made/</RT-code> directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.
+      </p>
+      <p>
+        It is common for a developer to open a second window on his desktop, and then enter the project as a tester in that second window. The developer can then make a promotion candidate, run the tests, edit source code, and perhaps tests, and then quickly spin through the test-debug-fix-promote cycle repeatedly.
+      </p>
+      <p>
+        When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a <RT-term>project release</RT-term>. He will do this by creating a branch called <RT-code>release_v&lt;major&gt;</RT-code> and tagging it. The major release numbers go up incrementally.
+      </p>
+
+      <h1>The version 2.2 Harmony directory tree</h1>
+
+      <RT-code>
+      2026-03-09 01:42:16 Z [Harmony:administrator] Thomas_developer@StanleyPark
+      §/home/Thomas/subu_data/developer/project§
+      > tree Harmony
+      Harmony
+      ├── 0pus_Harmony
+      ├── administrator
+      │ ├── authored
+      │ ├── document
+      │ │ └── setup.js
+      │ └── tool
+      │   ├── archive
+      │   └── setup
+      ├── consumer
+      │ ├── scratchpad
+      │ └── tool
+      │   └── env
+      ├── developer
+      │ ├── authored
+      │ │ └── hello.cli.c
+      </RT-code>
+      <RT-code>
+      │ ├── document
+      │ │ ├── 02_RT_Code_Format.html
+      │ │ ├── 03_Naming_and_Directory_Conventions.html
+      │ │ ├── 04_Language_Addenda.html
+      │ │ └── setup.js
+      │ ├── experiment
+      │ ├── made
+      │ ├── scratchpad
+      │ └── tool
+      │   ├── do_all
+      │   ├── make
+      │   ├── makefile
+      │   ├── promote
+      │   └── setup
+      ├── document
+      │ ├── Introduction_to_Harmony.html
+      │ ├── Product_Development_Roles_and_Workflow.html
+      │ └── setup.js
+      ├── LICENSE
+      </RT-code>
+      <RT-code>
+      ├── README.md
+      ├── scratchpad
+      │ ├── Harmony__79f9d52__2026-03-07_085628Z.tar
+      │ └── Harmony__e665bb7__2026-03-09_013712Z.tar
+      ├── setup
+      ├── shared
+      │ ├── authored
+      │ ├── document
+      │ │ ├── install_generic.org
+      │ │ ├── install_Python.org
+      │ │ └── setup.js
+      │ ├── made
+      │ ├── style_directory_dict.js
+      │ ├── third_party
+      │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/
+      │ │ └── upstream
+      │ └── tool
+      │   ├── scratchpad
+      │   ├── setup
+      </RT-code>
+      <RT-code>
+      │   ├── style
+      │   └── version
+      └── tester
+          ├── authored
+          │ └── test_routine.sh
+          ├── RT_Format
+          │ ├── RT_Format
+          │ ├── RT_Format.el
+          │ ├── test_0_data.c
+          │ └── test_1_data.py
+          └── tool
+              └── setup
+
+      30 directories, 36 files
+
+      2026-03-09 01:42:19 Z [Harmony:administrator] Thomas_developer@StanleyPark
+      §/home/Thomas/subu_data/developer/project§
+      > 
+      </RT-code>
+
+    </RT-article>
+  </body>
+</html>
index 917a3f8..10560fa 100644 (file)
@@ -13,7 +13,7 @@
     <RT-article>
       <RT-title 
         author="Thomas Walker Lynch" 
-        date="2026-03-07
+        date="2026-03-09
         title="Introduction to Harmony">
       </RT-title>
 
@@ -30,7 +30,7 @@
             <li>Separation of skeleton, team member authored, machine-made, and third-party installed software.</li>
           </ul>
         </li>
-        <li>A safe, predictable build and release workflow.</li>
+        <li>A safe, predictable build and promotion workflow.</li>
       </ol>
 
       <p>
         Leave the Harmony skeleton version in the version file, so that project administrators can understand what has changed in the skeleton over time.
       </p>
       <p>
-        The <RT-code>core_developer_branch</RT-code> is the branch that the core development team works on. Releases are moved to release branches.
+        The <RT-code>core_developer_branch</RT-code> is the branch that the core development team works on. Project releases are moved to release branches.
       </p>
 
-      <h1>Setup</h1>
-
-      <p>
-        Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator installs those tools into the <RT-code>shared/third_party</RT-code> directory. The project administrator then modifies the setup files so that paths point to the correct locations and environment variables have the proper values.
-      </p>
-      <p>
-        If a person clones the project and does not set up third-party tools, commands will fall through the search path and perhaps find system-installed tools. Using system default tools is generally undesirable because each person who clones the project might be running different versions. This variation makes it harder for team members to coordinate and for consumers of the project work product to build functioning tools.
-      </p>
-      <p>
-        Because multiple team members and consumers of the work product will have to repeat the third-party install process after cloning a project, the administrator should carefully document the installation steps and place the resulting documents in the <RT-code>shared/document</RT-code> directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under <RT-code>shared/third_party/</RT-code>.)
-      </p>
+      <h1>Environment setup</h1>
       <p>
         Python programmers who use virtual environments will be familiar with an analogous process. The term 'virtual environment' does not invoke any hardware virtualization features; rather, it is a local environment setup. To avoid confusion, a Harmony user refers instead to the 'project setup'. The command to establish the environment is called 'setup' instead of 'activate'. Also note that in Harmony, unlike in Python, there are multiple setups available, each tailored to the specific role a person takes on.
       </p>
         <li>Sources the <RT-code>&lt;role&gt;/tool/setup</RT-code> script. While the earlier steps apply the standard Harmony skeleton setup, this final step applies the role setup that is customized for this specific project.</li>
       </ul>
 
-      <h1>Directory name</h1>
+      <h1>After git clone</h1>
       <p>
-        A directory name is considered to be a <RT-term>property</RT-term> given to each file contained in the directory. A full path then forms a semantic sentence describing each file.
+        Because git does not track certain directories (such as <RT-code>shared/third_party/</RT-code> and <RT-code>consumer/made/</RT-code>), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
       </p>
+
+      <h2>Third-party tools</h2>
       <p>
-        Because a directory name represents a property, it is rarely plural. For example, when each and every file in a directory is a test, the directory is named <RT-code>test</RT-code>.
+        Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator configures the project to expect these tools in the <RT-code>shared/third_party</RT-code> directory.
       </p>
       <p>
-        We run into  limitations when using a conventional file system as though it were a property based file system. One limitation is that we are forced to choose a single directory name for each file.  When a set of files in a directory all share the same multiple properties, we can use a compound directory name with the properties separated by an underscore, but it is impractical to specify overlapping directory groupings, i.e. we can't arbitrarily define any number of properties for a file in this manner.  We will see later  how this affects Harmony when discussing <RT-term>authored</RT-term> files.
+        Because multiple team members will have to repeat the third-party install process after cloning a project, the administrator should carefully document the third party tools installation steps and place the resulting documents in the <RT-code>shared/document</RT-code> directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under <RT-code>shared/third_party/</RT-code>.)
       </p>
       <p>
-        The following list presents each property type in order of preference when naming directories.
+        If a person clones the project and does not set up third-party tools, commands will fall through the search path and perhaps find system-installed tools. Using system default tools is generally undesirable because each person who clones the project might be running different versions. This variation makes it harder for team members to coordinate and for consumers of the project work product to build functioning tools.
       </p>
-      <ul>
-        <li><strong>Role Association</strong> (<RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, <RT-code>consumer</RT-code>): Identifies the persona, whether human or AI, intended to interact with the files.</li>
-        <li><strong>Provenance</strong> (<RT-code>authored</RT-code>, <RT-code>made</RT-code>): Indicates whether the file was created by an intellect or mechanically produced by a tool.</li>
-        <li><strong>Capability</strong> (<RT-code>tool</RT-code>, <RT-code>document</RT-code>, <RT-code>experiment</RT-code>): Describes the primary function or structural nature of the file.</li>
-        <li><strong>Lifecycle State</strong> (<RT-code>scratchpad</RT-code>, <RT-code>release</RT-code>): Denotes the persistence, volatility, or promotion status of the file.</li>
-        <li><strong>Tracking Status</strong> (<RT-code>tracked</RT-code>, <RT-code>untracked</RT-code>): Specifies the version control expectations for the artifacts.</li>
-      </ul>
 
-      <h1>Document directories</h1>
+      <h2>Consumer build</h2>
       <p>
-        There is a directory for documents that talks about the project as a whole, one for each role, one for tools that are shared among the roles, and the released work product probably comes with a document directory of its own.
+        In this section we use the term 'consumer' to mean any team member that wants to make use of the project work product. The tester will want to test it, and the consumer role will want to deploy it, etc.
       </p>
-      <ul>
-        <li><RT-code>document/</RT-code> : Top-level onboarding, project-wide structure, such as this document.</li>
-        <li><RT-code>consumer/release/document/</RT-code> : Documentation for end-users of released code (e.g., man pages, application manuals, library API references).</li>
-        <li><RT-code>administrator/document/</RT-code> : Documentation for maintaining the project skeleton and global tools.</li>
-        <li><RT-code>developer/document/</RT-code> : Documentation for developers, including coding standards and internal API guides.</li>
-        <li><RT-code>tester/document/</RT-code> : Documentation for testers detailing test plans and tools.</li>
-        <li><RT-code>shared/document/</RT-code> : Documentation on installing and configuring shared tools.</li>
-      </ul>
-
       <p>
-        Note that the <RT-code>consumer/release</RT-code> directory is maintained by a tool. Said tool is unimaginatively also named <RT-code>release</RT-code> and is found in the developer's tool directory. Hence, documents that are part of a release probably originate from somewhere in the <RT-code>developer/authored</RT-code> directory, perhaps <RT-code>developer/authored/document</RT-code>. 
+        An earlier version of Harmony used platform-specific <RT-code>made</RT-code> directories, but this left binaries in the repository and added excessive complexity.
+        So as to avoid that mess, the current version of Harmony requires a work product consumer to run a local build after cloning the project. The results of the build will appear in the <RT-code>consumer/made</RT-code> directory.
       </p>
-
       <p>
-        Our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+        To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as <RT-code>developer/document/build.html</RT-code>. 
+      </p>
+      <p>
+        The consumer must then read this document and execute the described steps to compile the source and locally populate their <RT-code>consumer/made</RT-code> directory.
       </p>
 
+      <p>
+        Because the build and promotion tools are strictly isolated within the developer workspace, the consumer must temporarily put on the developer hat to perform the build. Typically the consumer build procedure will be a variation of the following:
+      </p>
 
-      <h1>Authored, made, scratchpad, third_party, inherited, customizations</h1>
+      <ol>
+        <li><RT-code>> bash</RT-code></li>
+        <li><RT-code>> cd &lt;project&gt;</RT-code></li>
+        <li><RT-code>> . setup developer</RT-code></li>
+        <li><RT-code>> make CLI</RT-code></li>
+        <li><RT-code>> promote write</RT-code></li>
+        <li><RT-code>> exit</RT-code></li>
+        <li><RT-code>> bash</RT-code></li>
+        <li><RT-code>> cd &lt;project&gt;</RT-code></li>
+        <li><RT-code>> . setup &lt;role&gt;</RT-code></li>
+      </ol>
 
       <p>
-        Files found in a directory named <RT-code>authored</RT-code> were written by project team members. They did not come with the Harmony skeleton, nor with the installation of other software. Project build tools treat <RT-code>authored</RT-code> directories as strictly read-only. Typically these files constitute the intellectual property of a project.
+        This sequence opens a bash shell, assumes the developer role to orchestrate the build, makes the work product, then promotes it to the consumer's workspace. The <RT-code>exit</RT-code> command drops the developer role. The last two lines put the person into the &lt;role&gt; workspace, typically for testing or deploying. Commonly, deployment is a matter of adding the <RT-code>consumer/made</RT-code> directory into the executable search path.
       </p>
 
+
+      <h1>Directory semantic properties</h1>
       <p>
-      All source code that gets built into a release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/make</RT-code> file.
+        This section discusses our thinking in naming the files and directories found in the Harmony skeleton.
       </p>
-
       <p>
-        When the Harmony version line in the <RT-code>shared/tool/version</RT-code> file is left in place, it is straightforward for a project administrator to determine which Harmony skeleton files have been edited in a project, and which new files have been added.
+        A directory name is considered to be a <RT-term>property</RT-term> given to each file contained in the directory. A full path then forms a semantic sentence describing each file.
       </p>
-
       <p>
-        [As a historical note, in the first version of Harmony a directory or file was marked as <RT-term>authored</RT-term> by adding a pencil suffix, '🖉'. Tools then respected this, for example by refusing to delete such files. However, this ad hoc approach to properties was awkward, and people had difficulty typing the marker. When the pencils were dropped, a number of authored files, such as an edited <RT-code>developer/tool/make🖉</RT-code> file, were no longer explicitly marked as authored.]
+        Because a directory name represents a property, it is rarely plural. For example, when each and every file in a directory is a test, the directory is named <RT-code>test</RT-code>.
       </p>
-
       <p>
-        Files found in a directory named <RT-code>scratchpad</RT-code> are not tracked. Hence, a <RT-code>git clone</RT-code> will always return empty <RT-code>scratchpad</RT-code> directories. It is common for tools to place intermediate files on a scratchpad. It is also common for files to be staged on a scratchpad. Tools play nice and use subdirectories on the pad, so a person who is aware of those subdirectory names can use a scratchpad as a temporary directory. There is a scratchpad maintenance tool that comes with the Harmony, called unimaginatively, <RT-code>scratchpad</RT-code>. Pay attention as one of its commands is <RT-code>clear</RT-code>, and that deletes everything on the current directory's scratchpad.
+        We run into limitations when using a conventional file system as though it were a property based file system. One limitation is that we are forced to choose a single directory name for each file. When a set of files in a directory all share the same multiple properties, we can use a compound directory name with the properties separated by an underscore, but it is impractical to specify overlapping directory groupings, i.e. we can't arbitrarily define any number of properties for a file in this manner.
       </p>
-
       <p>
-        Third party software is installed under <RT-code>shared/third_party</RT-code>.
+        The following list presents each property type in order of preference when naming directories:
       </p>
+      <ul>
+        <li><strong>Role Association</strong> (<RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, <RT-code>consumer</RT-code>): Identifies the persona, whether human or AI, intended to interact with the files.</li>
+        <li><strong>Provenance</strong> (<RT-code>authored</RT-code>, <RT-code>made</RT-code>): Indicates whether the file was created by an intellect or mechanically produced by a tool.</li>
+        <li><strong>Capability</strong> (<RT-code>tool</RT-code>, <RT-code>document</RT-code>, <RT-code>experiment</RT-code>): Describes the primary function or structural nature of the file.</li>
+        <li><strong>Lifecycle State</strong> (<RT-code>scratchpad</RT-code>, <RT-code>stage</RT-code>): Denotes the persistence, volatility, or promotion status of the file.</li>
+        <li><strong>Tracking Status</strong> (<RT-code>tracked</RT-code>, <RT-code>untracked</RT-code>): Specifies the version control expectations for the artifacts.</li>
+      </ul>
 
+      <h2>Authored, made, scratchpad, inherited</h2>
+      <p>
+        Files found in a directory named <RT-code>authored</RT-code> were written by project team members. They did not come with the Harmony skeleton, nor with the installation of other software. Project build tools treat <RT-code>authored</RT-code> directories as strictly read-only. Typically these files constitute the intellectual property of a project.
+      </p>
+      <p>
+        All source code that gets built into a promotion or project release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/make</RT-code> file.
+      </p>
+      <p>
+        When the Harmony version line in the <RT-code>shared/tool/version</RT-code> file is left in place, it is straightforward for a project administrator to determine which Harmony skeleton files have been edited in a project, and which new files have been added.
+      </p>
+      <p>
+        Files found in a directory named <RT-code>scratchpad</RT-code> are not tracked. Hence, a <RT-code>git clone</RT-code> will always return empty <RT-code>scratchpad</RT-code> directories. It is common for tools to place intermediate files on a scratchpad. It is also common for files to be staged on a scratchpad. Tools play nice and use subdirectories on the pad, so a person who is aware of those subdirectory names can use a scratchpad as a temporary directory. There is a scratchpad maintenance tool that comes with the Harmony, called unimaginatively, <RT-code>scratchpad</RT-code>. Pay attention as one of its commands is <RT-code>clear</RT-code>, and that deletes everything on the current directory's scratchpad.
+      </p>
       <p>
-        Other files are said to be <RT-term>inherited</RT-term>, or to be <RT-term>customizations</RT-term>.
+        Third party software is installed under <RT-code>shared/third_party</RT-code>. Other files are said to be <RT-term>inherited</RT-term>, or to be <RT-term>customizations</RT-term>.
       </p>
 
-      <h1>Top-Level repository layout</h1>
+      <h1>Top-level repository layout</h1>
       <p>
         A team member will source the project setup file to take on a role. As of this writing, the supported roles are: <RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, and <RT-code>consumer</RT-code>.
       </p>
         <li><RT-code>administrator/</RT-code> : Project-local tools and skeleton maintenance.</li>
         <li><RT-code>developer/</RT-code> : Primary workspace for developers.</li>
         <li><RT-code>tester/</RT-code> : Regression and validation workspace for testers.</li>
-        <li><RT-code>consumer/</RT-code> : Consumption workspace holding the <RT-code>consumer/release</RT-code> directory.</li>
+        <li><RT-code>consumer/</RT-code> : Consumption workspace holding the <RT-code>consumer/made</RT-code> directory.</li>
         <li><RT-code>shared/</RT-code> : Shared ecosystem tools and global environments.</li>
       </ul>
 
         <li><RT-code>authored/</RT-code> : Human-written source. Tracked by Git.</li>
         <li><RT-code>made/</RT-code> : Tracked artifacts generated by tools (e.g., links to CLI entry points).</li>
         <li><RT-code>experiment/</RT-code> : Try-it-here code. Short-lived spot testing.</li>
-        <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>stage</RT-code> directory for releases.</li>
-        <li><RT-code>tool/</RT-code> : Developer-specific tools (like the release and make scripts).</li>
+        <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>made</RT-code> directory for promotions.</li>
+        <li><RT-code>tool/</RT-code> : Developer-specific tools (like the promote and make scripts).</li>
       </ul>
 
       <h2>The tester work area</h2>
 
       <h2>The consumer tree</h2>
       <p>
-        The <RT-code>consumer/release/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/release</RT-code> directory is git-ignored and treated as a transient deployment target.
+        The <RT-code>consumer/made/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/made</RT-code> directory is git-ignored and treated as a transient deployment target.
       </p>
       <p>
-        Artifacts arrive in the <RT-code>consumer/release/</RT-code> tree <em>only</em> when the developer invokes the release script, which performs a flat-copy from the developer's <RT-code>scratchpad/stage</RT-code> directory.
+        Artifacts arrive in the <RT-code>consumer/made/</RT-code> tree <em>only</em> when the promote script is invoked, which performs a flat-copy from the developer's <RT-code>scratchpad/made</RT-code> directory.
       </p>
 
-      <h1>Releases</h1>
+      <h2>Document directories</h2>
+      <p>
+        There is a directory for documents that talks about the project as a whole, one for each role, one for tools that are shared among the roles, and the released work product probably comes with a document directory of its own.
+      </p>
+      <ul>
+        <li><RT-code>document/</RT-code> : Top-level onboarding, project-wide structure, such as this document.</li>
+        <li><RT-code>consumer/made/document/</RT-code> : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).</li>
+        <li><RT-code>administrator/document/</RT-code> : Documentation for maintaining the project skeleton and global tools.</li>
+        <li><RT-code>developer/document/</RT-code> : Documentation for developers, including coding standards and internal API guides.</li>
+        <li><RT-code>tester/document/</RT-code> : Documentation for testers detailing test plans and tools.</li>
+        <li><RT-code>shared/document/</RT-code> : Documentation on installing and configuring shared tools.</li>
+      </ul>
+
+      <p>
+        Note that the <RT-code>consumer/made</RT-code> directory is untracked by git and maintained by a tool. (Said tool is <RT-code>developer/tool/promote</RT-code>. It is owned and used by the developer as part of the build process.) Documents that are being promoted for eventual release, and appear in the <RT-code>made</RT-code> directory originate from somewhere from the <RT-code>developer/authored</RT-code> directory depending on how the developer has organized his workspace, but probably in <RT-code>developer/authored/document</RT-code>. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now.
+      </p>
+
+      <p>
+        Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+      </p>
+
+      <h2>Untracked directories</h2>
+      <ol>
+        <li><RT-code>consumer/made/</RT-code></li>
+        <li><RT-code>shared/third_party/</RT-code></li>
+        <li><RT-code>**/scratchpad/</RT-code></li>
+      </ol>
+
+      <h1>Workflow</h1>
+      <p> See the document "Product Development Roles and Workflow" for more details.</p>
 
+      <h1>Developer promotion and project releases</h1>
       <p>
-        As a first step, a developer creates a <RT-term>release candidate</RT-term> inside of the <RT-code>consumer/release/</RT-code> directory. This is typically done by running <RT-code>make</RT-code> to stage the release, where it can be experimented on, followed by running <RT-code>release write</RT-code>.  The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton.
-        The release candidate remains stable until the next <RT-term>developer release</RT-term>.
+        As a first step, a developer creates a <RT-term>promotion candidate</RT-term> inside of the <RT-code>consumer/made/</RT-code> directory. This is typically done by running <RT-code>make</RT-code> to stage the artifacts into <RT-code>scratchpad/made</RT-code>, where they can be experimented on, followed by running <RT-code>promote write</RT-code>.  The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton. The promotion candidate remains stable until the next <RT-term>promotion</RT-term>.
       </p>
       <p>
-        Then the tester runs tests on the release candidate. Tests must only read from the <RT-code>consumer/release/</RT-code> directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.
+        Then the tester runs tests on the promotion candidate. Tests must only read from the <RT-code>consumer/made/</RT-code> directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.
       </p>
       <p>
-        It is common for a developer to open a second window on his desktop, and then enter the project as a tester in that second window. The developer can then make a release candidate, run the tests, edit source code, and perhaps tests, and then quickly spin through the test-debug-fix-release cycle repeatedly.
+        It is common for a developer to open a second window on his desktop, and then enter the project as a tester in that second window. The developer can then make a promotion candidate, run the tests, edit source code, and perhaps tests, and then quickly spin through the test-debug-fix-promote cycle repeatedly.
       </p>
       <p>
         When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a <RT-term>project release</RT-term>. He will do this by creating a branch called <RT-code>release_v&lt;major&gt;</RT-code> and tagging it. The major release numbers go up incrementally.
       │ ├── document
       │ │ └── setup.js
       │ └── tool
-      │     ├── archive
-      │     └── setup
+      │   ├── archive
+      │   └── setup
       ├── consumer
       │ ├── scratchpad
       │ └── tool
-      │     └── env
+      │   └── env
       ├── developer
       │ ├── authored
-      │ │ └── hello.cli.c
+      │ │ └── hello.CLI.c
       </RT-code>
       <RT-code>
       │ ├── document
       │ ├── made
       │ ├── scratchpad
       │ └── tool
-      │     ├── do_all
-      │     ├── make
-      │     ├── makefile
-      │     ├── release
-      │     └── setup
+      │   ├── do_all
+      │   ├── make
+      │   ├── makefile
+      │   ├── promote
+      │   └── setup
       ├── document
       │ ├── Introduction_to_Harmony.html
       │ ├── Product_Development_Roles_and_Workflow.html
       │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/
       │ │ └── upstream
       │ └── tool
-      │     ├── scratchpad
-      │     ├── setup
+      │   ├── scratchpad
+      │   ├── setup
       </RT-code>
       <RT-code>
-      │     ├── style
-      │     └── version
+      │   ├── style
+      │   └── version
       └── tester
           ├── authored
           │ └── test_routine.sh
index 4f46c62..149624b 100644 (file)
@@ -2,7 +2,7 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8">
-    <title>Product Development Roles and Workflow</title>
+    <title>Product development roles and workflow</title>
     <script src="setup.js"></script>
     <script>
       window.StyleRT.include('RT/theme');
     <RT-article>
       <RT-title 
         author="Thomas Walker Lynch" 
-        date="2026-03-08
-        title="Product Development Roles and Workflow">
+        date="2026-03-09
+        title="Product development roles and workflow">
       </RT-title>
 
       <RT-TOC level="1"></RT-TOC>
 
-      <h1>Roles as Hats</h1>
+      <h1>Roles as hats</h1>
       <p>
         In Harmony, a role is a hat a person wears. There can be multiple people sharing a single role, and a single person can wear many hats. Some roles interact directly with the project directory structure, while others guide the process from outside the codebase. 
       </p>
 
-      <h2>Workspace Roles</h2>
+      <h2>Workspace roles</h2>
       <p>
         These roles interact directly with the repository. To enter a workspace, change directory to the top-level of the project and source the setup file for the desired role:
       </p>
@@ -38,7 +38,7 @@
         It is common for a person to have multiple terminal sessions or IDEs open, each running under a different role environment.
       </p>
 
-      <h3>Administrator Role</h3>
+      <h3>Administrator role</h3>
       <p>Responsibilities:</p>
       <ol>
         <li>Set up the project directory and keep it in sync with the Harmony skeleton.</li>
         <li>Install and maintain shared and third_party tools, addressing issues with the project workflow. Note that the term "third_party" encompasses any software not authored within this specific project.</li>
       </ol>
 
-      <h3>Developer Role</h3>
+      <h3>Developer role</h3>
       <p>Responsibilities and Boundaries:</p>
       <ol>
         <li>Write and modify <RT-code>authored/</RT-code> source.</li>
-        <li>Run builds and stage artifacts in <RT-code>scratchpad/stage</RT-code>, then execute the <RT-code>release write</RT-code> script to copy artifacts to <RT-code>consumer/release</RT-code> for testing.</li>
+        <li>Run builds and place artifacts in <RT-code>scratchpad/made</RT-code>, then execute the <RT-code>promote write</RT-code> script to copy artifacts to <RT-code>consumer/made</RT-code> for testing.</li>
         <li>Run experiments in <RT-code>experiment/</RT-code>. These experiments can sometimes be promoted to formal tests, but there is no requirement to do so. The developer role should not blur into the tester role; experiments are informal, whereas tests are formal and retained.</li>
         <li><strong>Strict Boundary:</strong> A developer never writes into the <RT-code>tester/</RT-code> directory. Instead, a developer adds tests to <RT-code>developer/experiment/</RT-code> and offers to share them.</li>
       </ol>
 
-      <h3>Tester Role</h3>
+      <h3>Tester role</h3>
       <p>Responsibilities and Boundaries:</p>
       <ol>
-        <li>Evaluate candidates under <RT-code>consumer/release/</RT-code> and run regression suites to confirm:
+        <li>Evaluate candidates under <RT-code>consumer/made/</RT-code> and run regression suites to confirm:
           <ul>
             <li>That the code does not crash.</li>
             <li>That consumers do not have a bad experience.</li>
         <li><strong>Strict Boundary:</strong> A tester never patches code in the <RT-code>developer/</RT-code> directory. Instead, the tester files issues or proposes code fixes on a separate branch.</li>
       </ol>
 
-      <h3>Consumer Role</h3>
+      <h3>Consumer role</h3>
       <p>Responsibilities:</p>
       <ol>
         <li>Act as the end-user simulation environment.</li>
-        <li>Consume and deploy artifacts exclusively from the <RT-code>consumer/release/</RT-code> target.</li>
+        <li>Consume and deploy artifacts exclusively from the <RT-code>consumer/made/</RT-code> target.</li>
         <li>Never author or modify code; strictly run local builds or deployments for architecture-specific testing.</li>
         <li>Report issues. (Anyone can report an issue, and consumers regularly do).</li>
       </ol>
 
-      <h2>External Roles</h2>
+      <h2>External roles</h2>
       <p>
         These roles drive the project forward but do not have a dedicated <RT-code>setup &lt;role&gt;</RT-code> workspace, as they do not directly build or test the code in that capacity. If these individuals need to interface with the code, they simply put on a workspace role hat, such as administer, developer, tester, or consumer.
       </p>
 
-      <h3>Product Manager</h3>
+      <h3>Product manager</h3>
       <p>
         The product manager receives specifications from the architect and sets the specific goals for a release. When the project manager provides updates indicating readiness, the product manager makes the final decision to cut a project release.
       </p>
 
-      <h3>Project Manager</h3>
+      <h3>Project manager</h3>
       <p>
         The project manager owns the schedule (such as the Gantt chart) and monitors progress. They coordinate code reviews, read issues and announcements, reformulate the schedule, and instruct the product manager. When parts of the codebase are outsourced, the project manager serves as the primary point of contact with the external teams.
       </p>
         The architect writes the original specification, sets the technical direction, and performs code reviews. The architect might be a contractor or an in-house team member.
       </p>
 
-      <h1>The Four Interacting Loops</h1>
+      <h1>The four interacting loops</h1>
       <p>
         The project moves forward through the continuous interaction of four distinct operational cycles.
       </p>
       
       <ol>
-        <li><strong>Developer Loop:</strong> Write code, make to the stage directory, promote to the consumer release directory, announce the release to the tester, read issues, and repeat.</li>
-        <li><strong>Tester Loop:</strong> Read developer announcements, read the consumer release directory, run tests, and file issues.</li>
-        <li><strong>Product Manager Loop:</strong> Provide specifications to the developer, receive progress updates from the project manager, and instruct the project administrator to make new git branch releases and deploy.</li>
+        <li><strong>Developer Loop:</strong> Write code, compile to the scratchpad made directory, promote to the consumer made directory, announce the promotion to the tester, read issues, and repeat.</li>
+        <li><strong>Tester Loop:</strong> Read developer announcements, read the consumer made directory, run tests, and file issues.</li>
+        <li><strong>Product Manager Loop:</strong> Provide specifications to the developer, receive progress updates from the project manager, and instruct the project administrator to make new git branch project releases and deploy.</li>
         <li><strong>Project Manager Loop:</strong> Own the Gantt chart, coordinate code reviews, read issues and announcements, reformulate the Gantt chart, and instruct the product manager.</li>
       </ol>
 
-      <h1>Release Promotion Mechanics</h1>
+      <h1>Promotion mechanics</h1>
       <p>
-        Building and promotion are separate activities. The developer compiles and stages files in <RT-code>developer/scratchpad/stage</RT-code>. The developer then runs <RT-code>release write</RT-code> to transfer those files to <RT-code>consumer/release</RT-code>. 
+        Building and promotion are separate activities. The developer compiles and places files in <RT-code>developer/scratchpad/made</RT-code>. The developer then runs <RT-code>promote write</RT-code> to transfer those files to <RT-code>consumer/made</RT-code>. 
       </p>
       <p>
-        The <RT-code>consumer/release</RT-code> directory is strictly an untracked deployment target. No tools are permitted to rebuild during promotion, and no builds are run directly inside the release directory.
+        The <RT-code>consumer/made</RT-code> directory is strictly an untracked deployment target. No tools are permitted to rebuild during promotion, and no builds are run directly inside the consumer made directory.
       </p>
 
     </RT-article>
index ca8c386..8cf8346 100644 (file)
@@ -1,4 +1,4 @@
-# make/target_lib_CLI.mk — build *.lib.c and *.CLI.c
+# make/target_cli_lib.mk — build *.lib.c and *.CLI.c
 # written for the Harmony skeleton, always invoked from cwd  $REPO_HOME/<role>
 # files have two suffixes by convention, e.g.: X.lib.c or Y.CLI.c 
 
 C              ?= gcc
 CFLAGS         ?=
 C_SOURCE_DIR   ?= cc
-LIBRARY_FILE   ?=
-MACHINE_DIR    ?= scratchpad/machine
+BUILD_DIR      ?= scratchpad/build
+LIBRARY_FILE   ?= $(BUILD_DIR)/made/lib$(PROJECT).a
+MACHINE_DIR    ?= $(BUILD_DIR)/made
+OBJECT_DIR     ?= $(BUILD_DIR)/object
 LN_FLAGS       ?=
 
 #--------------------------------------------------------------------------------
@@ -27,8 +29,8 @@ c_base_lib  := $(sort $(patsubst %.lib.c,%, $(notdir $(c_source_lib))))
 c_base_exec := $(sort $(patsubst %.CLI.c,%, $(notdir $(c_source_exec))))
 
 # two sets of object files, one for the lib, and one for the CLI programs
-object_lib  := $(patsubst %, scratchpad/%.lib.o, $(c_base_lib))
-object_exec := $(patsubst %, scratchpad/%.CLI.o, $(c_base_exec))
+object_lib  := $(patsubst %, $(OBJECT_DIR)/%.lib.o, $(c_base_lib))
+object_exec := $(patsubst %, $(OBJECT_DIR)/%.CLI.o, $(c_base_exec))
 
 # executables are made from exec_ sources
 exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec))
@@ -52,7 +54,7 @@ usage:
 
 .PHONY: version
 version:
-       @echo makefile version 7.1
+       @echo makefile version 8.0
        if [ ! -z "$(C)" ]; then $(C) -v; fi
        /bin/make -v
 
@@ -60,6 +62,7 @@ version:
 information:
        @printf "· → Unicode middle dot — visible: [%b]\n" "·"
        @echo "C_SOURCE_DIR: " $(C_SOURCE_DIR)
+       @echo "BUILD_DIR: " $(BUILD_DIR)
        @echo "c_source_lib: " $(c_source_lib)
        @echo "c_source_exec: " $(c_source_exec)
        @echo "c_base_lib: " $(c_base_lib)
@@ -72,6 +75,7 @@ information:
 library: $(LIBRARY_FILE)
 
 $(LIBRARY_FILE): $(object_lib)
+       @mkdir -p $(MACHINE_DIR)
        @if [ -s "$@" ] || [ -n "$(object_lib)" ]; then \
                echo "ar rcs $@ $^"; \
                ar rcs $@ $^; \
@@ -93,13 +97,14 @@ clean:
        rm -f $(LIBRARY_FILE)
        for obj in $(object_lib) $(object_exec); do rm -f $$obj $${obj%.o}.d || true; done
        for i in $(exec_); do [ -e $$i ] && rm $$i || true; done
+       rm -rf $(BUILD_DIR)
 
 
 # recipes
-scratchpad/%.o: $(C_SOURCE_DIR)/%.c
+$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
+       @mkdir -p $(OBJECT_DIR)
        $(C) $(CFLAGS) -o $@ -c $<
 
-$(MACHINE_DIR)/%: scratchpad/%.CLI.o
-       mkdir -p $(MACHINE_DIR)
+$(MACHINE_DIR)/%: $(OBJECT_DIR)/%.CLI.o
+       @mkdir -p $(MACHINE_DIR)
        $(C) -o $@ $< $(LN_FLAGS)
-
index 905a77b..a132902 100755 (executable)
@@ -1,4 +1,4 @@
-echo "Harmony v2.2 2026-03-06"
+echo "Harmony v2.5 2026-03-09"