From: Thomas Walker Lynch Date: Mon, 9 Mar 2026 10:50:29 +0000 (+0000) Subject: workflow tweak, doc tweaks, working example X-Git-Url: https://git.reasoningtechnology.com/%5B%5E?a=commitdiff_plain;h=e870c7b094ca9c88640a795cff39a2dde2f9f6bb;p=Harmony workflow tweak, doc tweaks, working example --- diff --git a/.gitignore b/.gitignore index 9c8dd19..5194c58 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 0000000..a626cac --- /dev/null +++ b/developer/authored/hello.CLI.c @@ -0,0 +1,2 @@ +#include +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 index a626cac..0000000 --- a/developer/authored/hello.cli.c +++ /dev/null @@ -1,2 +0,0 @@ -#include -int main(void){ puts("hello from Rabbit CLI"); return 0; } diff --git a/developer/document/File_directory_naming.html b/developer/document/File_directory_naming.html index 6de0ede..7569629 100644 --- a/developer/document/File_directory_naming.html +++ b/developer/document/File_directory_naming.html @@ -39,7 +39,7 @@

Hyphens and spaces

-

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. +

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.

@@ -72,6 +72,17 @@
  • .mod.c : Kernel module sources.
  • +

    Exercises

    +

    Exercise 1: Program file names

    +

    + 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. +

    +
      +
    • A C-culture makefile that builds a target library and command line interface: target-lib-cli.mk
    • +
    • A Lisp-culture source file for an HTML parsing module: html_parser_mod.el
    • +
    • A C language source file that initializes an HTTP server: init-http-srv.c
    • +
    + diff --git a/developer/document/RT_code_format.html b/developer/document/RT_code_format.html index e01c98d..4f6c3d2 100644 --- a/developer/document/RT_code_format.html +++ b/developer/document/RT_code_format.html @@ -19,9 +19,16 @@ +

    + 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. +

    +

    Object vs. instance nomenclature

    - We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term instance. + We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term instance. It is too much of an ask to remove the word object from the language, and this is reflected in the fact that few people use the term precisely. +

    +

    + 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 for loop is a C object.

    Identifier Names

    @@ -33,6 +40,19 @@
  • Globals: UPPER_SNAKE_CASE
  • +

    Proper nouns and acronyms

    +

    + Even in PascalCase and snake_case, proper nouns and acronyms remain capitalized, as per standard English language conventions (e.g., IEEE_publication_count). +

    + +

    Abbreviations

    +

    + 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. +

    +

    + 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. +

    +

    Primary and secondary separators (snake-kebab_case)

    If a language supports the hyphen (-) in identifiers (such as Common Lisp and Emacs Lisp), the identifier is written in snake-kebab_case. The hyphen is used as the primary word separator. The underscore (_) 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., city-scape_building-height). @@ -57,9 +77,11 @@

      +
    • *_dir : directory name
    • *_dirn : directory name
    • *_dirp : directory path
    • *_dirpr / *_dirpa : relative / absolute directory path
    • +
    • *_file : file name
    • *_filen : file name
    • *_filep : file path
    • *_filepr / *_fpa : relative / absolute file path
    • @@ -67,10 +89,7 @@
    • *_fsnodp : file system node path (when type is unspecified)
    -

    Proper nouns and acronyms

    -

    - Even in PascalCase and snake_case, proper nouns and acronyms remain capitalized, as per standard English language conventions (e.g., IEEE_publication_count). -

    +

    Comma separated lists

    diff --git a/developer/tool/makefile b/developer/tool/makefile index 0b79a95..8eaf73c 100644 --- a/developer/tool/makefile +++ b/developer/tool/makefile @@ -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 index 0000000..349bfd7 --- /dev/null +++ b/developer/tool/promote @@ -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 index 24f260c..0000000 --- a/developer/tool/release +++ /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 index 0000000..6852689 --- /dev/null +++ b/document/Intro.html @@ -0,0 +1,394 @@ + + + + + Introduction to Harmony + + + + + + + + + + +

    Purpose

    +

    + Harmony provides a language-agnostic project directory structure and maintenance tools for long-lived, multi-person team software development. The structure exists to enforce: +

    +
      +
    1. Clarity about where things live. +
        +
      • Role-based work areas.
      • +
      • Separation of skeleton, team member authored, machine-made, and third-party installed software.
      • +
      +
    2. +
    3. A safe, predictable build and promotion workflow.
    4. +
    + +

    + 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: +

    +
      +
    1. Clone the Harmony project to a local directory.
    2. +
    3. Remove the .git tree.
    4. +
    5. Rename the Harmony directory to the name of the new project.
    6. +
    7. Rename the 0pus_Harmony file to reflect the name of the new project.
    8. +
    9. Add a line to the shared/tool/version file for the new project.
    10. +
    11. git init -b core_developer_branch
    12. +
    + +

    + The order is important so as to protect the Harmony skeleton project from accidental check-ins from users. +

    +

    + Leave the Harmony skeleton version in the version file, so that project administrators can understand what has changed in the skeleton over time. +

    +

    + The core_developer_branch is the branch that the core development team works on. Project releases are moved to release branches. +

    + +

    Environment Setup

    +

    + 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. +

    +

    + As of the time of this writing, the defined roles are: administrator, consumer, developer, and tester. A person takes on a role by sourcing the top-level setup script and giving the target role as an argument. For example, in a bash shell with > as the prompt, the command is: +

    + + + > . setup <role> + + +

    + Specifically for the developer role: +

    + + + > . setup developer + + +

    + For the administrator role: +

    + + + > . setup administrator + + +

    + Instead of starting with a period, the source command can be spelled out explicitly, for example: +

    + + + > source setup tester + + +

    + Behind the scenes, the setup script performs the following actions: +

    + +
      +
    • Sources the project-wide setup (shared/tool/setup) to establish the core environment variables (e.g., REPO_HOME and PROJECT).
    • +
    • Conditionally sources shared/authored/setup (if present) to apply administrator-injected, project-specific tool configurations.
    • +
    • Configures the PATH to include shared tools, library environments, and the specific <role>/tool directory.
    • +
    • Changes the working directory into the specified role's workspace.
    • +
    • Sources the <role>/tool/setup 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.
    • +
    + +

    After git clone

    +

    + Because git does not track certain directories (such as shared/third_party/ and consumer/made/), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas. +

    + +

    Third-party tools

    +

    + 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 shared/third_party directory. +

    +

    + 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 shared/document directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under shared/third_party/.) +

    +

    + 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. +

    + +

    Consumer build

    +

    + 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. +

    +

    + An earlier version of Harmony used platform-specific made 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 consumer/made directory. +

    +

    + To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as developer/document/build.html. +

    +

    + The consumer must then read this document and execute the described steps to compile the source and locally populate the consumer/made directory. +

    + +

    + Typically the consumer build procedure will be a variation of the following: +

    + +
      +
    1. bash
    2. +
    3. > cd
    4. +
    5. > . developer
    6. +
    7. > make CLI
    8. +
    9. > promote write
    10. +
    11. > exit
    12. +
    13. bash
    14. +
    15. > cd
    16. +
    17. > .
    18. +
    + +

    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 consumer/made directory into the executable search path.) +

    + +

    Directory name

    +

    + This section is discusses our thinking in naming the files and directories found in the Harmony skeleton. +

    +

    + A directory name is considered to be a property given to each file contained in the directory. A full path then forms a semantic sentence describing each file. +

    +

    + 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 test. +

    +

    + 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 authored files. +

    +

    + The following list presents each property type in order of preference when naming directories. +

    +
      +
    • Role Association (administrator, developer, tester, consumer): Identifies the persona, whether human or AI, intended to interact with the files.
    • +
    • Provenance (authored, made): Indicates whether the file was created by an intellect or mechanically produced by a tool.
    • +
    • Capability (tool, document, experiment): Describes the primary function or structural nature of the file.
    • +
    • Lifecycle State (scratchpad, stage): Denotes the persistence, volatility, or promotion status of the file.
    • +
    • Tracking Status (tracked, untracked): Specifies the version control expectations for the artifacts.
    • +
    + +

    Document directories

    +

    + 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. +

    +
      +
    • document/ : Top-level onboarding, project-wide structure, such as this document.
    • +
    • consumer/made/document/ : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).
    • +
    • administrator/document/ : Documentation for maintaining the project skeleton and global tools.
    • +
    • developer/document/ : Documentation for developers, including coding standards and internal API guides.
    • +
    • tester/document/ : Documentation for testers detailing test plans and tools.
    • +
    • shared/document/ : Documentation on installing and configuring shared tools.
    • +
    + +

    + Note that the consumer/made directory is untracked by git and maintained by a tool. (Said tool is developer/tool/promote. 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 made directory originate from somewhere from the developer/authored directory depending on how the developer has organized his workspace, but probably in developer/authored/document. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now. +

    + +

    + Currently, our developers write documents directly in HTML using the RT semantic tags. See the RT-style-JS_public project and the documentation there. A common approach is to copy another document and the setup.js file, then to type over the top of that other document. Only one setup.js file is used per directory. Be sure to edit the relative root path found at the top of setup.js. Plain text, emacs org, and mark down have all been used in prior versions of Harmony. +

    + + +

    Authored, made, scratchpad, third_party, inherited, customizations

    + +

    + Files found in a directory named authored 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 authored directories as strictly read-only. Typically these files constitute the intellectual property of a project. +

    + +

    + All source code that gets built into a promotion or project release must be placed in the developers' authored 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 developer/tool/make file. +

    + +

    + When the Harmony version line in the shared/tool/version 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. +

    + +

    + [As a historical note, in the first version of Harmony a directory or file was marked as authored 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 developer/tool/make🖉 file, were no longer explicitly marked as authored.] +

    + +

    + Files found in a directory named scratchpad are not tracked. Hence, a git clone will always return empty scratchpad 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, scratchpad. Pay attention as one of its commands is clear, and that deletes everything on the current directory's scratchpad. +

    + +

    + Third party software is installed under shared/third_party. +

    + +

    + Other files are said to be inherited, or to be customizations. +

    + +

    Untracked directories

    +
      +
    1. consumer/made/
    2. +
    3. shared/third_party/
    4. +
    5. **/scratchpad/
    6. +
    + +

    Top-level repository layout

    +

    + A team member will source the project setup file to take on a role. As of this writing, the supported roles are: administrator, developer, tester, and consumer. +

    + +
      +
    • administrator/ : Project-local tools and skeleton maintenance.
    • +
    • developer/ : Primary workspace for developers.
    • +
    • tester/ : Regression and validation workspace for testers.
    • +
    • consumer/ : Consumption workspace holding the consumer/made directory.
    • +
    • shared/ : Shared ecosystem tools and global environments.
    • +
    + +

    The administrator work area

    +

    + 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. +

    + +

    The developer work area

    +

    + This directory is entered by first going to the top-level directory of the project, then sourcing . setup developer. +

    +
      +
    • authored/ : Human-written source. Tracked by Git.
    • +
    • made/ : Tracked artifacts generated by tools (e.g., links to CLI entry points).
    • +
    • experiment/ : Try-it-here code. Short-lived spot testing.
    • +
    • scratchpad/ : Git-ignored directory. Holds all intermediate build outputs, including the made directory for promotions.
    • +
    • tool/ : Developer-specific tools (like the promote and make scripts).
    • +
    + +

    The tester work area

    +

    + This directory is dedicated to formal testing, including regression suites. While a developer can run and keep informal spot tests in their experiment/ directory, any experiment promoted to a formal test is moved here. This enforces the boundary between writing code and validating it. +

    + +

    The shared tree

    +

    + 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 shared/authored directory that is listed earlier in the executable search path than shared/tool. +

    + +

    The consumer tree

    +

    + The consumer/made/ tree is where developers put work product that is ready to be consumed. The entire consumer/made directory is git-ignored and treated as a transient deployment target. +

    +

    + Artifacts arrive in the consumer/made/ tree only when the developer invokes the promote script, which performs a flat-copy from the developer's scratchpad/made directory. +

    + +

    Workflow

    + +

    See the document "Product Development Roles and Workflow" for more details.

    + +

    Developer promotion and project releases

    + +

    + As a first step, a developer creates a promotion candidate inside of the consumer/made/ directory. This is typically done by running make to stage the artifacts into scratchpad/made, where they can be experimented on, followed by running promote write. 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 promotion. +

    +

    + Then the tester runs tests on the promotion candidate. Tests must only read from the consumer/made/ directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool. +

    +

    + 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. +

    +

    + When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a project release. He will do this by creating a branch called release_v<major> and tagging it. The major release numbers go up incrementally. +

    + +

    The version 2.2 Harmony directory tree

    + + + 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 + + + │ ├── 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 + + + ├── 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 + + + │ ├── 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§ + > + + +
    + + diff --git a/document/Introduction_to_Harmony.html b/document/Introduction_to_Harmony.html index 917a3f8..10560fa 100644 --- a/document/Introduction_to_Harmony.html +++ b/document/Introduction_to_Harmony.html @@ -13,7 +13,7 @@ @@ -30,7 +30,7 @@
  • Separation of skeleton, team member authored, machine-made, and third-party installed software.
  • -
  • A safe, predictable build and release workflow.
  • +
  • A safe, predictable build and promotion workflow.
  • @@ -52,20 +52,10 @@ Leave the Harmony skeleton version in the version file, so that project administrators can understand what has changed in the skeleton over time.

    - The core_developer_branch is the branch that the core development team works on. Releases are moved to release branches. + The core_developer_branch is the branch that the core development team works on. Project releases are moved to release branches.

    -

    Setup

    - -

    - 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 shared/third_party directory. The project administrator then modifies the setup files so that paths point to the correct locations and environment variables have the proper values. -

    -

    - 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. -

    -

    - 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 shared/document directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under shared/third_party/.) -

    +

    Environment setup

    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.

    @@ -113,80 +103,100 @@
  • Sources the <role>/tool/setup 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.
  • -

    Directory name

    +

    After git clone

    - A directory name is considered to be a property 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 shared/third_party/ and consumer/made/), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.

    + +

    Third-party tools

    - 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 test. + 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 shared/third_party directory.

    - 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 authored 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 shared/document directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under shared/third_party/.)

    - 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.

    -
      -
    • Role Association (administrator, developer, tester, consumer): Identifies the persona, whether human or AI, intended to interact with the files.
    • -
    • Provenance (authored, made): Indicates whether the file was created by an intellect or mechanically produced by a tool.
    • -
    • Capability (tool, document, experiment): Describes the primary function or structural nature of the file.
    • -
    • Lifecycle State (scratchpad, release): Denotes the persistence, volatility, or promotion status of the file.
    • -
    • Tracking Status (tracked, untracked): Specifies the version control expectations for the artifacts.
    • -
    -

    Document directories

    +

    Consumer build

    - 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.

    -
      -
    • document/ : Top-level onboarding, project-wide structure, such as this document.
    • -
    • consumer/release/document/ : Documentation for end-users of released code (e.g., man pages, application manuals, library API references).
    • -
    • administrator/document/ : Documentation for maintaining the project skeleton and global tools.
    • -
    • developer/document/ : Documentation for developers, including coding standards and internal API guides.
    • -
    • tester/document/ : Documentation for testers detailing test plans and tools.
    • -
    • shared/document/ : Documentation on installing and configuring shared tools.
    • -
    -

    - Note that the consumer/release directory is maintained by a tool. Said tool is unimaginatively also named release and is found in the developer's tool directory. Hence, documents that are part of a release probably originate from somewhere in the developer/authored directory, perhaps developer/authored/document. + An earlier version of Harmony used platform-specific made 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 consumer/made directory.

    -

    - Our developers write documents directly in HTML using the RT semantic tags. See the RT-style-JS_public project and the documentation there. A common approach is to copy another document and the setup.js file, then to type over the top of that other document. Only one setup.js file is used per directory. Be sure to edit the relative root path found at the top of setup.js. 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 developer/document/build.html. +

    +

    + The consumer must then read this document and execute the described steps to compile the source and locally populate their consumer/made directory.

    +

    + 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: +

    -

    Authored, made, scratchpad, third_party, inherited, customizations

    +
      +
    1. > bash
    2. +
    3. > cd <project>
    4. +
    5. > . setup developer
    6. +
    7. > make CLI
    8. +
    9. > promote write
    10. +
    11. > exit
    12. +
    13. > bash
    14. +
    15. > cd <project>
    16. +
    17. > . setup <role>
    18. +

    - Files found in a directory named authored 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 authored 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 exit command drops the developer role. The last two lines put the person into the <role> workspace, typically for testing or deploying. Commonly, deployment is a matter of adding the consumer/made directory into the executable search path.

    + +

    Directory semantic properties

    - All source code that gets built into a release must be placed in the developers' authored 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 developer/tool/make file. + This section discusses our thinking in naming the files and directories found in the Harmony skeleton.

    -

    - When the Harmony version line in the shared/tool/version 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 property given to each file contained in the directory. A full path then forms a semantic sentence describing each file.

    -

    - [As a historical note, in the first version of Harmony a directory or file was marked as authored 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 developer/tool/make🖉 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 test.

    -

    - Files found in a directory named scratchpad are not tracked. Hence, a git clone will always return empty scratchpad 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, scratchpad. Pay attention as one of its commands is clear, 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.

    -

    - Third party software is installed under shared/third_party. + The following list presents each property type in order of preference when naming directories:

    +
      +
    • Role Association (administrator, developer, tester, consumer): Identifies the persona, whether human or AI, intended to interact with the files.
    • +
    • Provenance (authored, made): Indicates whether the file was created by an intellect or mechanically produced by a tool.
    • +
    • Capability (tool, document, experiment): Describes the primary function or structural nature of the file.
    • +
    • Lifecycle State (scratchpad, stage): Denotes the persistence, volatility, or promotion status of the file.
    • +
    • Tracking Status (tracked, untracked): Specifies the version control expectations for the artifacts.
    • +
    +

    Authored, made, scratchpad, inherited

    +

    + Files found in a directory named authored 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 authored directories as strictly read-only. Typically these files constitute the intellectual property of a project. +

    +

    + All source code that gets built into a promotion or project release must be placed in the developers' authored 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 developer/tool/make file. +

    +

    + When the Harmony version line in the shared/tool/version 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. +

    +

    + Files found in a directory named scratchpad are not tracked. Hence, a git clone will always return empty scratchpad 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, scratchpad. Pay attention as one of its commands is clear, and that deletes everything on the current directory's scratchpad. +

    - Other files are said to be inherited, or to be customizations. + Third party software is installed under shared/third_party. Other files are said to be inherited, or to be customizations.

    -

    Top-Level repository layout

    +

    Top-level repository layout

    A team member will source the project setup file to take on a role. As of this writing, the supported roles are: administrator, developer, tester, and consumer.

    @@ -195,7 +205,7 @@
  • administrator/ : Project-local tools and skeleton maintenance.
  • developer/ : Primary workspace for developers.
  • tester/ : Regression and validation workspace for testers.
  • -
  • consumer/ : Consumption workspace holding the consumer/release directory.
  • +
  • consumer/ : Consumption workspace holding the consumer/made directory.
  • shared/ : Shared ecosystem tools and global environments.
  • @@ -212,8 +222,8 @@
  • authored/ : Human-written source. Tracked by Git.
  • made/ : Tracked artifacts generated by tools (e.g., links to CLI entry points).
  • experiment/ : Try-it-here code. Short-lived spot testing.
  • -
  • scratchpad/ : Git-ignored directory. Holds all intermediate build outputs, including the stage directory for releases.
  • -
  • tool/ : Developer-specific tools (like the release and make scripts).
  • +
  • scratchpad/ : Git-ignored directory. Holds all intermediate build outputs, including the made directory for promotions.
  • +
  • tool/ : Developer-specific tools (like the promote and make scripts).
  • The tester work area

    @@ -228,23 +238,52 @@

    The consumer tree

    - The consumer/release/ tree is where developers put work product that is ready to be consumed. The entire consumer/release directory is git-ignored and treated as a transient deployment target. + The consumer/made/ tree is where developers put work product that is ready to be consumed. The entire consumer/made directory is git-ignored and treated as a transient deployment target.

    - Artifacts arrive in the consumer/release/ tree only when the developer invokes the release script, which performs a flat-copy from the developer's scratchpad/stage directory. + Artifacts arrive in the consumer/made/ tree only when the promote script is invoked, which performs a flat-copy from the developer's scratchpad/made directory.

    -

    Releases

    +

    Document directories

    +

    + 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. +

    +
      +
    • document/ : Top-level onboarding, project-wide structure, such as this document.
    • +
    • consumer/made/document/ : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).
    • +
    • administrator/document/ : Documentation for maintaining the project skeleton and global tools.
    • +
    • developer/document/ : Documentation for developers, including coding standards and internal API guides.
    • +
    • tester/document/ : Documentation for testers detailing test plans and tools.
    • +
    • shared/document/ : Documentation on installing and configuring shared tools.
    • +
    + +

    + Note that the consumer/made directory is untracked by git and maintained by a tool. (Said tool is developer/tool/promote. 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 made directory originate from somewhere from the developer/authored directory depending on how the developer has organized his workspace, but probably in developer/authored/document. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now. +

    + +

    + Currently, our developers write documents directly in HTML using the RT semantic tags. See the RT-style-JS_public project and the documentation there. A common approach is to copy another document and the setup.js file, then to type over the top of that other document. Only one setup.js file is used per directory. Be sure to edit the relative root path found at the top of setup.js. Plain text, emacs org, and mark down have all been used in prior versions of Harmony. +

    + +

    Untracked directories

    +
      +
    1. consumer/made/
    2. +
    3. shared/third_party/
    4. +
    5. **/scratchpad/
    6. +
    + +

    Workflow

    +

    See the document "Product Development Roles and Workflow" for more details.

    +

    Developer promotion and project releases

    - As a first step, a developer creates a release candidate inside of the consumer/release/ directory. This is typically done by running make to stage the release, where it can be experimented on, followed by running release write. 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 developer release. + As a first step, a developer creates a promotion candidate inside of the consumer/made/ directory. This is typically done by running make to stage the artifacts into scratchpad/made, where they can be experimented on, followed by running promote write. 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 promotion.

    - Then the tester runs tests on the release candidate. Tests must only read from the consumer/release/ 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 consumer/made/ directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.

    - 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.

    When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a project release. He will do this by creating a branch called release_v<major> and tagging it. The major release numbers go up incrementally. @@ -263,15 +302,15 @@ │ ├── document │ │ └── setup.js │ └── tool - │ ├── archive - │ └── setup + │ ├── archive + │ └── setup ├── consumer │ ├── scratchpad │ └── tool - │ └── env + │ └── env ├── developer │ ├── authored - │ │ └── hello.cli.c + │ │ └── hello.CLI.c │ ├── document @@ -283,11 +322,11 @@ │ ├── 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 @@ -312,12 +351,12 @@ │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/ │ │ └── upstream │ └── tool - │ ├── scratchpad - │ ├── setup + │ ├── scratchpad + │ ├── setup - │ ├── style - │ └── version + │ ├── style + │ └── version └── tester ├── authored │ └── test_routine.sh diff --git a/document/Product_Development_Roles_and_Workflow.html b/document/Product_Development_Roles_and_Workflow.html index 4f46c62..149624b 100644 --- a/document/Product_Development_Roles_and_Workflow.html +++ b/document/Product_Development_Roles_and_Workflow.html @@ -2,7 +2,7 @@ - Product Development Roles and Workflow + Product development roles and workflow