--- /dev/null
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+"""
+Harmony_path-and-staging_patch
+
+One-time patch for the Harmony skeleton. Run it from the top directory of
+the project, after checkpointing with git.
+
+ 1. shared/tool/setup
+ Sources the project dependency .init files first, then adds shared/tool,
+ shared/made, shared/authored, so the search order becomes
+ shared/authored, shared/made, shared/tool, project dependencies.
+ 2. setup
+ Adds the role's tool directory after the shared setup has run, so it is
+ searched before everything else. Removes the earlier PATH injection,
+ which put it behind the shared directories.
+ 3. developer/tool/build_component/copy
+ Stages into scratchpad/made/<namespace>, like the make component.
+ 4. developer/tool/promote
+ Copies from scratchpad/made into consumer/, so build intermediates
+ such as scratchpad/build/object stay on the scratchpad. Its setup hint
+ now says ./setup developer.
+
+Every expected piece of text is checked before any file is written. If one
+is missing, the patch reports it and changes nothing.
+
+Usage: Harmony_path-and-staging_patch <command>
+ help | -h | --help print this message (also printed when given no arguments)
+ preview print what would change, change nothing
+ apply make the changes
+"""
+
+import sys ,re
+from pathlib import Path
+
+EDITS = {
+ "shared/tool/setup": [
+ (
+'''# --------------------------------------------------------------------------------
+# PATH & Plugin Sourcing
+# precedence: last defined, first discovered
+
+ PATH="$REPO_HOME/shared/authored:$PATH"
+ PATH="$REPO_HOME/shared/made:$PATH"
+ PATH="$REPO_HOME/shared/tool:$PATH"
+
+ # Remove duplicates
+ clean_path() {
+ PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//')
+ }
+ clean_path
+
+ # Plugin Sourcing: Dynamically load all the linked project configurations
+ for init_file in "$REPO_HOME/shared/project-dependency/"*.init; do
+ if [[ -f "$init_file" ]]; then
+ source "$init_file"
+ fi
+ done
+'''
+ ,
+'''# --------------------------------------------------------------------------------
+# PATH & Plugin Sourcing
+# Each addition goes to the front, so the last added is searched first.
+# Resulting search order, most specific first:
+# shared/authored, shared/made, shared/tool, project dependencies, the rest.
+# The top-level setup then puts the role's tool directory in front of these.
+
+ # Project dependencies: source each .init file. Each prepends its own entries.
+ for init_file in "$REPO_HOME/shared/project-dependency/"*.init; do
+ if [[ -f "$init_file" ]]; then
+ source "$init_file"
+ fi
+ done
+
+ PATH="$REPO_HOME/shared/tool:$PATH"
+ PATH="$REPO_HOME/shared/made:$PATH"
+ PATH="$REPO_HOME/shared/authored:$PATH"
+
+ # Remove duplicates, keeping the first occurrence
+ clean_path() {
+ PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//')
+ }
+ clean_path
+'''
+ )
+ ]
+ ,"setup": [
+ (
+''' # Python-domain logic: PATH injection
+ path_parts = env.get("PATH", "").split(os.pathsep)
+ if tool_dir not in path_parts:
+ env["PATH"] = f"{tool_dir}{os.pathsep}{env.get('PATH', '')}"
+
+'''
+ ,
+''
+ )
+ ,(
+""" 'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi',
+"""
+ ,
+""" 'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi',
+ '# the role tool directory is searched before everything the shared setup added',
+ f'PATH="{tool_dir}:$PATH"; export PATH',
+"""
+ )
+ ]
+ ,"developer/tool/build_component/copy": [
+ ('all Stages files from authored to scratchpad.'
+ ,'all Stages files from authored to scratchpad/made/<namespace>.')
+ ,('clean Removes the specified namespace from scratchpad.'
+ ,'clean Removes scratchpad/made/<namespace>.')
+ ,(''' src_root = os.path.join("authored", namespace_dir)
+ dst_root = os.path.join("scratchpad", namespace)'''
+ ,''' src_root = os.path.join("authored", namespace_dir)
+ dst_root = os.path.join("scratchpad", "made", namespace)''')
+ ,(''' dst_root = os.path.join("scratchpad", namespace)
+ if os.path.exists(dst_root):'''
+ ,''' dst_root = os.path.join("scratchpad", "made", namespace)
+ if os.path.exists(dst_root):''')
+ ]
+ ,"developer/tool/promote": [
+ ('write Writes promoted files from scratchpad into consumer. Only updates newer files.'
+ ,'write Copies what is staged in scratchpad/made into consumer. Only updates newer files.')
+ ,('diff List missing, orphaned, or out of sync files between scratchpad and consumer.'
+ ,'diff List missing, orphaned, or out of sync files between scratchpad/made and consumer.')
+ ,('"Enter the project with: . setup developer\\n"'
+ ,'"Enter the developer role with: ./setup developer\\n"')
+ ,(''' ensure_dir(dst_root, DEFAULT_DIR_MODE, dry=dry)
+ src_root = dpath("scratchpad")'''
+ ,''' ensure_dir(dst_root, DEFAULT_DIR_MODE, dry=dry)
+ src_root = dpath("scratchpad", "made")''')
+ ,(''' dst_root = cpath()
+ src_root = dpath("scratchpad")
+
+ src_files = set()'''
+ ,''' dst_root = cpath()
+ src_root = dpath("scratchpad", "made")
+
+ src_files = set()''')
+ ,('Orphaned in consumer (missing in scratchpad)'
+ ,'Orphaned in consumer (missing in scratchpad/made)')
+ ,('newer in scratchpad)'
+ ,'newer in scratchpad/made)')
+ ,('Consumer matches developer scratchpad.'
+ ,'Consumer matches developer scratchpad/made.')
+ ]
+}
+
+def print_help() -> int:
+ print(__doc__.strip())
+ return 0
+
+def tolerant(old: str):
+ """A pattern for old that ignores spaces and tabs at the ends of lines."""
+ lines = old.split("\n")
+ last = len(lines) - 1
+ return re.compile("\n".join(
+ re.escape(l.rstrip(" \t")) + ("" if i == last and l == "" else "[ \t]*")
+ for i ,l in enumerate(lines)
+ ))
+
+def plan(root: Path):
+ """Returns (new_texts ,already ,problems). new_texts maps a path to its new
+ text; already lists the files that are already patched."""
+ new_texts = {}
+ already = []
+ problems = []
+ for rel ,edits in EDITS.items():
+ f = root / rel
+ if not f.is_file():
+ problems.append(f"{rel}: file not found")
+ continue
+ text = f.read_text(encoding="utf-8")
+ applied = 0
+ for n ,(old ,new) in enumerate(edits ,1):
+ # The finished form is checked first: an edit's old text can lie
+ # inside its new text, and would otherwise be applied twice.
+ if new != "" and len(tolerant(new).findall(text)) == 1:
+ applied += 1
+ continue
+ count = len(tolerant(old).findall(text))
+ if count == 1:
+ text = tolerant(old).sub(lambda m: new ,text)
+ continue
+ if new == "" and count == 0:
+ applied += 1
+ continue
+ problems.append(f"{rel}: edit {n}: expected text found {count} times, not once")
+ if applied == len(edits):
+ already.append(rel)
+ elif applied:
+ problems.append(f"{rel}: partly patched ({applied} of {len(edits)} edits already present)")
+ else:
+ new_texts[rel] = text
+ return new_texts ,already ,problems
+
+def work(root: Path ,preview: bool) -> int:
+ print(f"Harmony_path-and-staging_patch {'preview' if preview else 'apply'} in {root}")
+ new_texts ,already ,problems = plan(root)
+ if problems:
+ for p in problems: print(f" error {p}")
+ print("Nothing was changed.")
+ return 1
+ for rel in already:
+ print(f" already patched {rel}")
+ for rel ,text in new_texts.items():
+ f = root / rel
+ if preview:
+ print(f" would change {rel} ({len(EDITS[rel])} edits)")
+ else:
+ mode = f.stat().st_mode
+ f.write_text(text ,encoding="utf-8")
+ f.chmod(mode)
+ print(f" changed {rel} ({len(EDITS[rel])} edits)")
+ if preview: print("preview only: nothing was changed")
+ return 0
+
+def CLI(argv=None) -> int:
+ if argv is None: argv = sys.argv[1:]
+ if not argv or argv[0] in ("help" ,"-h" ,"--help"): return print_help()
+ if len(argv) != 1 or argv[0] not in ("preview" ,"apply"):
+ print(f"Error: unknown command '{' '.join(argv)}'.\n" ,file=sys.stderr)
+ print_help()
+ return 1
+ root = Path.cwd()
+ if not (root / "shared" / "tool" / "setup").is_file():
+ print("Error: run this from the top directory of the project." ,file=sys.stderr)
+ return 1
+ return work(root ,argv[0] == "preview")
+
+if __name__ == "__main__":
+ sys.exit(CLI())
## Viewing the Documentation
-To view the project documentation with its intended formatting, a person must provide the RT style library. One way to do this is to clone the styling repository side-by-side with your new project directory, and then to link it into the third-party directory.
+The documents are HTML written against the RT-Style library. With nothing set up, a document loads RT-Style from https://style.ReasoningTechnology.com, so it formats whenever the machine is online.
-From the parent directory of your new project, clone the required style repository:
+To read offline, or against a local copy of RT-Style, put a built RT-Style project on the machine and open its consumer/Manuscript/setup.html once in Chrome, from disk. Chrome then loads RT-Style from that copy for every local document. Opening another copy of setup.html, then reloading the document, will switch to using that other copy.
-```bash
-cd ..
-git clone -b release_v1 https://github.com/Thomas-Walker-Lynch/RT-style-JS_public
-```
-
-Then, from the root of your new project repository, link it:
-
-```bash
-cd shared/linked-project
-ln -s ../../../RT-style-JS_public RT-style-JS_public
-```
-
-Find an introductory document at `document/introduction_Harmony.html'. After the style library is installed, clicking on it in file navigator should open it in a browser.
+Find an introductory document at `document/introduction_Harmony.html`.
The script executes the following sequential lifecycle:
1. Validates the execution environment (requires an active role session).
- 2. Safely extracts a clean copy of all Git-tracked files using an archive stream.
- 3. Renames the boilerplate 0pus_Harmony anchor file to reflect the new project.
- 4. Injects an initial version tracking entry into the shared configuration space.
- 5. Initializes a fresh Git repository bound to the core-developer_branch.
- 6. Stages and commits the pristine skeleton infrastructure as a baseline.
- 7. Maps any upstream remote network targets passed via CLI parameters.
+ 2. Checks the git status of the skeleton. The new project is made from the
+ last commit, so uncommitted changes and untracked files would be left
+ out. When there are any, lists them and asks whether to continue.
+ 3. Safely extracts a clean copy of all Git-tracked files using an archive stream.
+ 4. Renames the boilerplate 0pus_Harmony anchor file to reflect the new project.
+ 5. Injects an initial version tracking entry into the shared configuration space.
+ 6. Initializes a fresh Git repository bound to the core-developer_branch.
+ 7. Stages and commits the pristine skeleton infrastructure as a baseline.
+ 8. Maps any upstream remote network targets passed via CLI parameters.
"""
import sys ,os ,subprocess
except Exception:
return "1970-01-01"
-def work(project_name: str ,remotes: list[str]) -> int:
- """Orchestrates the assembly of the new skeleton instance."""
- repo_home = os.environ.get("REPO_HOME")
- if not repo_home:
- print("Error: REPO_HOME is not set. A person must run this from within a sourced role environment." ,file=sys.stderr)
- return 1
+def uncommitted(repo_path: Path) -> list[str] | None:
+ """Returns the lines of 'git status --porcelain' for the skeleton: one per
+ file with uncommitted changes or untracked. None when git cannot report."""
+ try:
+ res = subprocess.run(
+ ["git" ,"status" ,"--porcelain" ,"--untracked-files=normal"]
+ ,cwd=str(repo_path) ,capture_output=True ,text=True ,check=True
+ )
+ except Exception:
+ return None
+ return [line for line in res.stdout.splitlines() if line.strip()]
+
+def confirm(question: str) -> bool:
+ """Asks a yes or no question at the terminal. Without a terminal to ask
+ at, the answer is no."""
+ if not sys.stdin.isatty():
+ return False
+ try:
+ answer = input(question + " [y/N] ")
+ except EOFError:
+ return False
+ return answer.strip().lower() in ("y" ,"yes")
- repo_path = Path(repo_home).resolve()
+def work(repo_path: Path ,project_name: str ,remotes: list[str]) -> int:
+ """Orchestrates the assembly of the new skeleton instance."""
# Resolve destination relative to the parent space of the current repository
dest_path = Path(project_name)
print("Error: project name is required." ,file=sys.stderr)
return 1
- return work(project_name ,remotes)
+ repo_home = os.environ.get("REPO_HOME")
+ if not repo_home:
+ print("Error: REPO_HOME is not set. A person must run this from within a sourced role environment." ,file=sys.stderr)
+ return 1
+ repo_path = Path(repo_home).resolve()
+
+ status = uncommitted(repo_path)
+ if status is None:
+ print(f"Error: could not read the git status of {repo_path}." ,file=sys.stderr)
+ return 1
+ if status:
+ shown = status[:20]
+ print(f"Warning: {repo_path} has {len(status)} uncommitted or untracked file(s):")
+ for line in shown:
+ print(" " + line)
+ if len(status) > len(shown):
+ print(f" ... and {len(status) - len(shown)} more (see 'git status')")
+ print("The new project is made from the last commit, so these will not be in it.")
+ if not confirm("Continue anyway?"):
+ print("Stopped. Nothing was made. Commit or stash the changes, then run new-project again.")
+ return 1
+
+ return work(repo_path ,project_name ,remotes)
if __name__ == "__main__":
sys.exit(CLI())
<!DOCTYPE html>
-<html lang="en">
- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
import sync_engine
HELP = """usage: copy {all|clean|help}
- all Stages files from authored to scratchpad.
- clean Removes the specified namespace from scratchpad.
+ all Stages files from authored to scratchpad/made/<namespace>.
+ clean Removes scratchpad/made/<namespace>.
help Shows this message.
"""
sys.exit(1)
src_root = os.path.join("authored", namespace_dir)
- dst_root = os.path.join("scratchpad", namespace)
+ dst_root = os.path.join("scratchpad", "made", namespace)
sync_engine.execute_sync(src_root, dst_root, prefix="copy", apply_modes=False)
print("copy: error: NAMESPACE environment variable must be set.", file=sys.stderr)
sys.exit(1)
- dst_root = os.path.join("scratchpad", namespace)
+ dst_root = os.path.join("scratchpad", "made", namespace)
if os.path.exists(dst_root):
shutil.rmtree(dst_root)
print(f"copy: Cleaned {dst_root}")
import sync_engine
HELP = """usage: promote {write|clean|ls|diff|help|dry write}
- write Writes promoted files from scratchpad into consumer. Only updates newer files.
+ write Copies what is staged in scratchpad/made into consumer. Only updates newer files.
clean Remove all contents of the consumer directory (preserves .gitignore).
ls List consumer as an indented tree: PERMS OWNER NAME.
- diff List missing, orphaned, or out of sync files between scratchpad and consumer.
+ diff List missing, orphaned, or out of sync files between scratchpad/made and consumer.
help Show this message.
dry write Preview what write would do without modifying the filesystem.
+
+Promoted files are read-only: 400, or 500 when executable. Directories below
+consumer/ are 500, so not even the owner can add, replace or remove a file in
+them; write and clean open them (700) for the duration and lock them again.
+consumer/ itself stays 700, as it holds the .gitignore git manages.
"""
SETUP_MUST_BE = "developer/tool/setup"
DEFAULT_DIR_MODE = 0o700
+LOCKED_DIR_MODE = 0o500
def exit_with_status(msg, code=1):
print(f"promote: {msg}", file=sys.stderr)
if setup_val != SETUP_MUST_BE:
hint = (
"SETUP is not 'developer/tool/setup'.\n"
- "Enter the project with: . setup developer\n"
+ "Enter the developer role with: ./setup developer\n"
"That script exports: ROLE=developer; SETUP=$ROLE/tool/setup"
)
exit_with_status(f"bad environment: SETUP='{setup_val}'. {hint}")
os.makedirs(path_str, exist_ok=True)
ensure_mode(path_str, mode)
+def consumer_subdirs(root_dp):
+ """Every directory below root_dp; root_dp itself is not included."""
+ found = []
+ for TM_root, TM_dirs, TM_files in os.walk(root_dp):
+ for TM_d in TM_dirs:
+ found.append(os.path.join(TM_root, TM_d))
+ return found
+
+def set_subdir_modes(root_dp, mode):
+ for TM_d in consumer_subdirs(root_dp):
+ ensure_mode(TM_d, mode)
+
def filemode(m):
try: return stat.filemode(m)
except Exception: return oct(m & 0o777)
def cmd_write(dry=False):
dst_root = cpath()
ensure_dir(dst_root, DEFAULT_DIR_MODE, dry=dry)
- src_root = dpath("scratchpad")
-
- sync_engine.execute_sync(
- src_root,
- dst_root,
- prefix="promote",
- dry=dry,
- apply_modes=True,
- ignore_list=[".gitignore"]
- )
+ src_root = dpath("scratchpad", "made")
+
+ # Files are replaced by writing a temporary file beside them and renaming
+ # it into place, which needs write permission on the directory.
+ if not dry: set_subdir_modes(dst_root, DEFAULT_DIR_MODE)
+ try:
+ sync_engine.execute_sync(
+ src_root,
+ dst_root,
+ prefix="promote",
+ dry=dry,
+ apply_modes=True,
+ ignore_list=[".gitignore"]
+ )
+ finally:
+ if not dry: set_subdir_modes(dst_root, LOCKED_DIR_MODE)
def cmd_diff():
dst_root = cpath()
- src_root = dpath("scratchpad")
+ src_root = dpath("scratchpad", "made")
src_files = set()
if os.path.isdir(src_root):
print(f"Pending promotion (missing in consumer): {TM_rel}")
found_diff = True
elif TM_rel not in src_files:
- print(f"Orphaned in consumer (missing in scratchpad): {TM_rel}")
+ print(f"Orphaned in consumer (missing in scratchpad/made): {TM_rel}")
found_diff = True
else:
src_abs = os.path.join(src_root, TM_rel)
dst_mtime = os.stat(dst_abs).st_mtime
if src_mtime > dst_mtime:
- print(f"Pending update (contents differ, newer in scratchpad): {TM_rel}")
+ print(f"Pending update (contents differ, newer in scratchpad/made): {TM_rel}")
else:
print(f"Contents differ (locally modified in consumer): {TM_rel}")
found_diff = True
if not found_diff:
- print("No differences found. Consumer matches developer scratchpad.")
+ print("No differences found. Consumer matches developer scratchpad/made.")
def cmd_clean():
consumer_root_dir = cpath()
if not os.path.isdir(consumer_root_dir):
return
+ # Removing an entry needs write permission on the directory holding it.
+ set_subdir_modes(consumer_root_dir, DEFAULT_DIR_MODE)
for TM_name in os.listdir(consumer_root_dir):
if TM_name == ".gitignore":
continue
<body>
<RT·theme-selector></RT·theme-selector>
<RT·article>
- <RT·title
- author="Thomas Walker Lynch"
- date="2026-06-23"
+ <RT·title
+ author="Thomas Walker Lynch"
+ date="2026-10-09"
title="Introduction to Harmony">
</RT·title>
<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:
+ Harmony is a project skeleton: a language-agnostic directory structure and a set of maintenance tools, kept in a git repository, from which new projects are made. It is designed for long-lived software developed by a team, and serves equally well for one person, who takes on each role in turn. The structure exists to give:
</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>
+ <li>Separation of skeleton, team member authored, machine-made, and depended-upon software.</li>
</ul>
</li>
<li>A safe, predictable build and promotion workflow.</li>
</ol>
+ <h1>Making a project</h1>
<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:
+ A project administrator makes a new project from a clone of Harmony, by taking on the administrator role and running <RT·code>new-project</RT·code> with the name of the new project:
+ </p>
+ <RT·code>
+ > cd Harmony
+ > ./setup administrator
+ > new-project <name>
+ </RT·code>
+ <p>
+ <RT·code>new-project</RT·code> makes the new project as a sibling directory of Harmony, then:
</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>
+ <li>Copies the files of Harmony's last commit, leaving out Harmony's git history, untracked files, and uncommitted changes.</li>
+ <li>Renames <RT·code>0pus_Harmony</RT·code> to <RT·code>0pus_<name></RT·code>.</li>
+ <li>Adds a line for the new project to <RT·code>shared/tool/version</RT·code>.</li>
+ <li>Initializes a git repository on the <RT·code>core-developer_branch</RT·code> branch, and commits the copied skeleton.</li>
+ <li>Adds any remote repositories given as <RT·code>-remote <url></RT·code>.</li>
</ol>
-
<p>
- The order is important so as to protect the Harmony skeleton project from accidental check-ins from users.
+ Because the new project starts with its own git history, work done in it cannot be checked in to the Harmony skeleton by accident.
</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.
+ Leave the Harmony line in the version file. It records which skeleton the project was made from, so that an administrator can later see what has changed in the skeleton since, and bring the project up to date. The administrator tool <RT·code>skeleton_diff</RT·code> compares a project with a Harmony checkout for this purpose.
</p>
+
+ <h1>Taking on a role</h1>
<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.
+ A person works in a project by taking on a <RT·term>role</RT·term>. The roles are <RT·code>administrator</RT·code>, <RT·code>developer</RT·code>, <RT·code>tester</RT·code>, and <RT·code>consumer</RT·code>. From the top directory of the project, run <RT·code>setup</RT·code> with the role:
</p>
-
- <h1>Environment setup</h1>
+ <RT·code>
+ > ./setup developer
+ </RT·code>
<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.
+ <RT·code>setup</RT·code> starts a new bash shell configured for the role, and leaves the person in the role's directory. The prompt shows the project and the role. <RT·code>exit</RT·code> leaves the role and returns to the shell <RT·code>setup</RT·code> was run from. It is common to have several terminal windows or Emacs shells open, each in a different role.
</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:
+ Python programmers will recognize this as similar to a virtual environment. Harmony calls it the project setup, and the command is <RT·code>setup</RT·code> rather than <RT·code>activate</RT·code>. Unlike a Python virtual environment, there is one setup for each role.
</p>
-
- <RT·code>
- > . setup <role>
- </RT·code>
-
<p>
- Specifically for the developer role:
+ In the new shell, the setup:
</p>
-
- <RT·code>
- > . setup developer
- </RT·code>
-
+ <ul>
+ <li>Sets <RT·code>ROLE</RT·code>, <RT·code>ROLE_HOME</RT·code> and <RT·code>SETUP</RT·code>, and puts <RT·code><role>/tool</RT·code> on the <RT·code>PATH</RT·code>.</li>
+ <li>Sources the project-wide <RT·code>shared/tool/setup</RT·code>, which sets <RT·code>REPO_HOME</RT·code> and <RT·code>PROJECT</RT·code>, puts <RT·code>shared/tool</RT·code> and <RT·code>shared/authored</RT·code> on the <RT·code>PATH</RT·code>, and sources each project dependency's <RT·code>.init</RT·code> file.</li>
+ <li>Sources <RT·code>shared/authored/setup</RT·code>, when the project has one, for project-specific additions to every role.</li>
+ <li>Changes into the role's directory and sources <RT·code><role>/tool/setup</RT·code>, the setup customized for that role in this project.</li>
+ <li>When running inside Emacs, copies the environment to Emacs with <RT·code>env_to_emacs</RT·code>.</li>
+ </ul>
<p>
- For the administrator role:
+ Tools rely on this environment. Absolute paths are formed from <RT·code>REPO_HOME</RT·code>, and some tools, such as <RT·code>build</RT·code> and <RT·code>promote</RT·code>, refuse to run outside the role they belong to.
</p>
-
- <RT·code>
- > . setup administrator
- </RT·code>
+ <h1>Roles and their directories</h1>
<p>
- Instead of starting with a period, the <RT·code>source</RT·code> command can be spelled out explicitly, for example:
+ Each role has a directory at the top of the project. The <RT·code>shared</RT·code> directory holds what all roles use.
</p>
-
- <RT·code>
- > source setup tester
- </RT·code>
+ <h2>Administrator</h2>
<p>
- Behind the scenes, the <RT·code>setup</RT·code> script performs the following actions:
+ The administrator keeps the project in step with the Harmony skeleton, maintains the role environments, installs project dependencies, and makes releases. <RT·code>administrator/tool</RT·code> holds the tools for this, among them <RT·code>new-project</RT·code>, <RT·code>skeleton_diff</RT·code>, and <RT·code>archive</RT·code>, which writes an archive of the repository to the scratchpad.
</p>
+ <h2>Developer</h2>
+ <p>
+ The developer writes the project's source and builds it.
+ </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>Dynamically sources all <RT·code>.init</RT·code> files found in the <RT·code>shared/linked-project/</RT·code> directory.</li>
- <li>Configures the <RT·code>PATH</RT·code> to include shared tools, library environments, and the specific <RT·code><role>/tool</RT·code> directory.</li>
- <li>Changes the working directory into the specified role's workspace.</li>
- <li>Sources the <RT·code><role>/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>
+ <li><RT·code>authored/</RT·code> : Source written by the team. Tracked.</li>
+ <li><RT·code>document/</RT·code> : Documents for developers, such as coding and naming conventions.</li>
+ <li><RT·code>experiment/</RT·code> : Informal, short-lived trials.</li>
+ <li><RT·code>scratchpad/</RT·code> : Untracked. Intermediate build files and the staged products of a build.</li>
+ <li><RT·code>tool/</RT·code> : The developer's tools, including <RT·code>build</RT·code> and <RT·code>promote</RT·code>.</li>
</ul>
- <h1>After git clone</h1>
+ <h2>Tester</h2>
<p>
- Because git does not track certain artifact directories (such as <RT·code>consumer/</RT·code> outputs), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
+ The tester holds the formal tests, including regression suites, and runs them against the work product in <RT·code>consumer/</RT·code>. A developer may keep informal tests in <RT·code>developer/experiment</RT·code>. A test that is to be kept is moved to <RT·code>tester/</RT·code>, which keeps writing code and validating it apart.
</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/linked-project</RT·code> directory via the `.init` plugin system.
- </p>
+ <h2>Consumer</h2>
<p>
- Because multiple team members 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>administrator/document</RT·code> directory. The standard installation method is to clone the external tool into the parent directory alongside the project, create a symlink to it under <RT·code>shared/linked-project/</RT·code> using the <RT·code>project/</RT·code> parent link, and supply an <RT·code>.init</RT·code> script to manage the local environment variables.
+ <RT·code>consumer/</RT·code> receives the work product when a developer promotes it. It is untracked, apart from its <RT·code>.gitignore</RT·code>, and is treated as a deployment target: files arrive there only through <RT·code>promote</RT·code>, and nothing is built in it. The consumer role uses the work product as its end users would.
</p>
- <h2>Consumer build</h2>
+ <h2>Shared</h2>
+ <ul>
+ <li><RT·code>shared/tool/</RT·code> : Tools for all roles, and the project-wide setup. The <RT·code>version</RT·code> command prints the version lines.</li>
+ <li><RT·code>shared/authored/</RT·code> : Project-specific additions for all roles. It is on every role's <RT·code>PATH</RT·code>, and may hold a <RT·code>setup</RT·code> that every role sources.</li>
+ <li><RT·code>shared/project-dependency/</RT·code> : Software the project uses but did not author. See "Project dependencies" below.</li>
+ </ul>
+
+ <h1>Building and promoting</h1>
<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.
+ Building and promotion are separate steps. In the developer role, <RT·code>build</RT·code> makes the work product from <RT·code>developer/authored</RT·code> and stages it on the developer's scratchpad. <RT·code>promote write</RT·code> then copies what changed to <RT·code>consumer/</RT·code>. The result is a <RT·term>promotion candidate</RT·term>, which stays as it is until the next promotion.
</p>
<p>
- Because compiled binaries and final layouts are not tracked in the repository, 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 directly in the <RT·code>consumer/</RT·code> directory.
+ Source in <RT·code>developer/authored</RT·code> is grouped into directories named <RT·code><Namespace>.<Tool></RT·code>. The suffix names the build component in <RT·code>developer/tool/build_component</RT·code> that builds that directory, such as <RT·code>make</RT·code> for C, or <RT·code>copy</RT·code> for files used as they are. Given no arguments, <RT·code>build</RT·code> prints its usage and the namespaces it found. <RT·code>promote</RT·code> given no arguments prints its commands, among them <RT·code>dry write</RT·code>, which shows what <RT·code>write</RT·code> would copy.
</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>. The consumer must then read this document and execute the described steps to locally populate their <RT·code>consumer/</RT·code> directory.
+ The tester runs tests against the promotion candidate. Tests read from <RT·code>consumer/</RT·code> and never write to it, though a tester may copy files elsewhere to experiment with them. A developer often has a second window open in the tester role, and cycles through edit, build, promote and test.
</p>
+ <h2>After cloning a project</h2>
<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:
+ Built products are not tracked, so a fresh clone has an empty <RT·code>consumer/</RT·code>. A team member who needs the work product builds it, taking on the developer role to do so:
</p>
-
- <ol>
- <li><RT·code>> bash</RT·code></li>
- <li><RT·code>> cd <project></RT·code></li>
- <li><RT·code>> . setup developer</RT·code></li>
- <li><RT·code>> build <namespace></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 <project></RT·code></li>
- <li><RT·code>> . setup <role></RT·code></li>
- </ol>
-
+ <RT·code>
+ > ./setup developer
+ > build <namespace>
+ > promote write
+ > exit
+ > ./setup <role>
+ </RT·code>
<p>
- 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 <role> workspace, typically for testing or deploying.
+ The developer documents any project-specific build steps in <RT·code>developer/document/build.html</RT·code>. A fresh clone also lacks the project's dependencies, which each team member installs as described beside each dependency in <RT·code>shared/project-dependency</RT·code>.
</p>
-
- <h1>Directory semantic properties</h1>
+ <h1>Project dependencies</h1>
<p>
- This section discusses our thinking in naming the files and directories found in the Harmony skeleton.
+ Harmony is language agnostic. When a project uses C, Python, NodeJS, Java, or other software it did not author, the administrator installs that software as a sibling directory of the project and links it in under <RT·code>shared/project-dependency</RT·code>, with an <RT·code>.init</RT·code> file that every role's setup sources to put the dependency into the environment. Installation notes for each dependency sit beside its <RT·code>.init</RT·code> file, so that each team member can repeat the installation after cloning.
</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.
+ The directory comes with a <RT·code>project</RT·code> link to the directory holding the project and its siblings, through which dependencies are linked, and with Python as an example dependency. The steps are given in <RT·code>administrator/document/how-to_add-project-dependency.html</RT·code>.
</p>
+
+ <h1>Directory names</h1>
<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>.
+ A directory name is a <RT·term>property</RT·term> of each file in the directory, so a full path reads as a description of the file. Because a name states a property of each file, it is rarely plural: when 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.
+ A conventional file system can only approximate a property-based one. Each file sits in one directory, so it receives one property per level of the path. Files sharing several properties can be given a compound name with the properties separated by an underscore, but overlapping groupings cannot be expressed.
</p>
<p>
- The following list presents each property type in order of preference when naming directories:
+ When choosing a directory name, prefer properties in this order:
</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>
+ <li><strong>Role</strong> (<RT·code>administrator</RT·code>, <RT·code>developer</RT·code>, <RT·code>tester</RT·code>, <RT·code>consumer</RT·code>): the role, human or AI, that works with the files.</li>
+ <li><strong>Provenance</strong> (<RT·code>authored</RT·code>, <RT·code>made</RT·code>): whether a person wrote the file or a tool produced it.</li>
+ <li><strong>Capability</strong> (<RT·code>tool</RT·code>, <RT·code>document</RT·code>, <RT·code>experiment</RT·code>): what the file is for.</li>
+ <li><strong>Lifecycle</strong> (<RT·code>scratchpad</RT·code>, <RT·code>stage</RT·code>): how long the file is expected to last, or where it is in promotion.</li>
+ <li><strong>Tracking</strong> (<RT·code>tracked</RT·code>, <RT·code>untracked</RT·code>): what version control does with it.</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/build</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>
- Third party software is installed under <RT·code>shared/linked-project</RT·code>. Other files are said to be <RT·term>inherited</RT·term>, or to be <RT·term>customizations</RT·term>.
+ Files in a directory named <RT·code>authored</RT·code> were written by the project's team. They did not come with the skeleton, nor with the installation of other software, and they typically constitute the project's intellectual property. Build tools treat <RT·code>authored</RT·code> directories as read-only. All source that is built into a promotion or a release is placed in <RT·code>developer/authored</RT·code>.
</p>
-
- <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>.
+ Files that came with the skeleton are <RT·term>inherited</RT·term>. When the team edits one, such as <RT·code>developer/tool/build</RT·code>, it becomes a <RT·term>customization</RT·term>. New documents go in document directories and new tools in tool directories. Because the Harmony line stays in the version file, an administrator can tell which skeleton files a project has customized and which it has added.
</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 acting as the final artifact sink.</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.
+ Files in a directory named <RT·code>scratchpad</RT·code> are untracked, so a clone always has empty scratchpads. Tools place intermediate and staged files there, each in its own subdirectory, so a person who knows those names can also use a scratchpad as a temporary directory. The <RT·code>scratchpad</RT·code> tool maintains the scratchpad of the current directory. Note that its <RT·code>clear</RT·code> command deletes everything on that scratchpad.
</p>
- <h2>The developer work area</h2>
+ <h1>Documents</h1>
<p>
- This directory is entered by first going to the top-level directory of the project, then sourcing <RT·code>. setup developer</RT·code>.
+ Each directory named <RT·code>document</RT·code> holds documents for the role, or the scope, of its parent:
</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 staged namespace directories for promotions.</li>
- <li><RT·code>tool/</RT·code> : Developer-specific tools (like the promote and build scripts).</li>
+ <li><RT·code>document/</RT·code> : The project as a whole, such as this introduction.</li>
+ <li><RT·code>administrator/document/</RT·code> : Maintaining the project skeleton, adding dependencies, and making releases.</li>
+ <li><RT·code>developer/document/</RT·code> : Coding standards, conventions, and the build.</li>
+ <li><RT·code>tester/document/</RT·code> : Test plans and testing tools.</li>
+ <li><RT·code>consumer/<namespace>/document/</RT·code> : Documents released with the work product, such as manuals and API references.</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.
+ Documents are written in HTML with the RT semantic tags, and formatted by RT-Style, a separate project at <RT·code>https://github.com/Thomas-Walker-Lynch/RT-Style</RT·code>. A document loads RT-Style through a short script block at the top of its head. To write a new document, copy an existing one, keep that block as it is, and type over the contents.
</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 symlinks and <RT·code>.init</RT·code> scripts 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>.
+ Documents are read in Chrome. With nothing set up, a document loads RT-Style from <RT·code>style.ReasoningTechnology.com</RT·code>. To read without a network connection, or against a local copy of RT-Style, open that copy's <RT·code>Manuscript/setup.html</RT·code> once in Chrome. Chrome then loads RT-Style from it for every local document.
</p>
-
- <h2>The consumer tree</h2>
<p>
- The <RT·code>consumer/</RT·code> tree is where developers put work product that is ready to be consumed. The entire directory is git-ignored and treated as a transient deployment target. Artifacts arrive in the <RT·code>consumer/</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.
+ <RT·code>document/writing_tool-script.html</RT·code> gives the conventions for writing tools.
</p>
- <h2>Document directories</h2>
+ <h1>What git tracks</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.
+ Everything is tracked except:
</p>
<ul>
- <li><RT·code>document/</RT·code> : Top-level onboarding, project-wide structure, such as this document.</li>
- <li><RT·code>consumer/<namespace>/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>
+ <li><RT·code>consumer/</RT·code>, apart from its <RT·code>.gitignore</RT·code>.</li>
+ <li>Every <RT·code>scratchpad/</RT·code>, apart from its <RT·code>.gitignore</RT·code>.</li>
+ <li>In <RT·code>shared/project-dependency/</RT·code>, whatever its <RT·code>.gitignore</RT·code> does not list as tracked.</li>
+ <li>Archives, editor backups, and compiled files, as listed in the top-level <RT·code>.gitignore</RT·code>.</li>
</ul>
+ <h1>Workflow and releases</h1>
<p>
- Currently, our developers write documents directly in HTML using the RT semantic tags. See the RT-Style project and the documentation there. A common approach is to copy an existing document while keeping the RT-Style script block in the header intact, then and typing over the contents.
+ The project moves forward through a cycle: the developer promotes a candidate, the tester tests it and files issues, and the developer fixes them and promotes again. The <RT·code>core-developer_branch</RT·code> is the branch the core development team works on. When the product manager judges the work product reliable and complete enough, the administrator makes a <RT·term>project release</RT·term> on a branch named <RT·code>release_v<major></RT·code>, and tags it.
</p>
-
- <h2>Untracked directories</h2>
- <ol>
- <li><RT·code>consumer/</RT·code> (Excluding the base <RT·code>.gitignore</RT·code>)</li>
- <li><RT·code>shared/linked-project/</RT·code> (Excluding <RT·code>.init</RT·code> and <RT·code>.gitignore</RT·code> files)</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>promotion candidate</RT·term> inside of the <RT·code>consumer/</RT·code> directory. This is typically done by running <RT·code>build</RT·code> to stage the artifacts into <RT·code>scratchpad/made</RT·code>, 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>.
+ These documents give the details:
</p>
- <p>
- Then the tester runs tests on the promotion candidate. Tests must only read from the <RT·code>consumer/</RT·code> directory, though local copies can be made and edited as experiments.
- </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<major></RT·code> and tagging it. The major release numbers go up incrementally.
- </p>
-
- <h1>The Harmony directory tree</h1>
-
- <RT·code>
- 2026-10-09 09:22:21 Z [Harmony:administrator] Thomas_developer@Yangming
- §/home/Thomas/subu_data/developer/project§
- > tree Harmony
- Harmony
- ├── 0pus_Harmony
- ├── administrator
- │ ├── authored
- │ ├── document
- │ │ ├── how-to_release.html
- │ │ ├── install_generic.org
- │ │ └── install_Python.org
- │ └── tool
- │ ├── archive
- │ ├── html_to_v31
- │ ├── new-project
- │ ├── RT-Style_header_v5_to_v6
- │ ├── setup
- │ └── skeleton_diff
- ├── consumer
- │ └── ExampleGreet
- │ ├── hello
- │ └── libHarmony.a
- ├── developer
- │ ├── authored
- │ │ └── ExampleGreet.make
- │ │ ├── Greeter.lib.c
- │ │ ├── hello.CLI.c
- │ │ └── Math.lib.c
- │ ├── document
- │ │ ├── naming_file-and-directory.html
- │ │ └── single-file_C-module-and-namespace.html
- │ ├── experiment
- │ ├── scratchpad
- │ └── tool
- │ ├── build
- │ ├── build_component
- │ │ ├── copy
- │ │ ├── make
- │ │ ├── makefile
- │ │ └── sync_engine.py
- │ ├── promote
- │ └── setup
- ├── document
- │ ├── introduction_Harmony.html
- │ ├── role-and-workflow_product-development.html
- │ ├── role-and-workflow_product-maintenance.html
- │ └── writing_tool-script.html
- ├── LICENSE
- ├── README.md
- ├── scratchpad
- ├── setup
- ├── shared
- │ ├── authored
- │ ├── document
- │ ├── linked-project
- │ │ ├── project -> ../../../
- │ │ ├── Python-3.12.3 -> project/Python-3.12.3
- │ │ └── Python.init
- │ └── tool
- │ ├── env_to_emacs
- │ ├── makefile
- │ │ ├── Harmony.mk
- │ │ ├── RT_global.h
- │ │ └── target_kernel-module.mk
- │ ├── PATH.sh
- │ ├── RT-formatter
- │ ├── RT-formatter.el
- │ ├── scratchpad
- │ ├── setup
- │ └── version
- └── tester
- ├── authored
- └── tool
- └── setup
-
- 28 directories, 43 files
- </RT·code>
+ <ul>
+ <li><RT·code>document/role-and-workflow_product-development.html</RT·code> : The roles, and how their work interacts.</li>
+ <li><RT·code>document/role-and-workflow_product-maintenance.html</RT·code> : Issues, triage, and patching releases.</li>
+ <li><RT·code>administrator/document/how-to_release.html</RT·code> : Making a release.</li>
+ </ul>
</RT·article>
</body>
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>
<RT·code>
- > . setup administrator
- > . setup developer
- > . setup tester
- > . setup consumer
+ > ./setup administrator
+ > ./setup developer
+ > ./setup tester
+ > ./setup consumer
</RT·code>
- <p>
- It is common for a person to have multiple terminal sessions or IDEs open, each running under a different role environment.
+
+ <p>This will run a new shell with the proper environment setting. It is common for a person to have multiple shells, terminal sessions, or IDEs open, each running under a different role environment. Development and tests is a common pair, though tests are intended to be production tests. The developer has an <RT·code>experiment</RT·code> directory for experiments.
</p>
<h3>Administrator role</h3>
print(f"ROLE_HOME {role_home}")
- # Python-domain logic: PATH injection
- path_parts = env.get("PATH", "").split(os.pathsep)
- if tool_dir not in path_parts:
- env["PATH"] = f"{tool_dir}{os.pathsep}{env.get('PATH', '')}"
-
# Python-domain logic: Directory validation
if not os.path.isdir(role):
print(f"setup: failed to locate directory for role '{role}'")
'if [[ -f ~/.bashrc ]]; then source ~/.bashrc; fi',
'if [[ -f "shared/tool/setup" ]]; then source "shared/tool/setup"; fi',
'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi',
+ '# the role tool directory is searched before everything the shared setup added',
+ f'PATH="{tool_dir}:$PATH"; export PATH',
f'cd "{role}"',
'if [[ -f "tool/setup" ]]; then',
' source "tool/setup"',
# --------------------------------------------------------------------------------
# PATH & Plugin Sourcing
-# precedence: last defined, first discovered
-
- PATH="$REPO_HOME/shared/authored:$PATH"
- PATH="$REPO_HOME/shared/made:$PATH"
- PATH="$REPO_HOME/shared/tool:$PATH"
+# Each addition goes to the front, so the last added is searched first.
+# Resulting search order, most specific first:
+# shared/authored, shared/made, shared/tool, project dependencies, the rest.
+# The top-level setup then puts the role's tool directory in front of these.
- # Remove duplicates
- clean_path() {
- PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//')
- }
- clean_path
-
- # Plugin Sourcing: Dynamically load all the linked project configurations
+ # Project dependencies: source each .init file. Each prepends its own entries.
for init_file in "$REPO_HOME/shared/project-dependency/"*.init; do
if [[ -f "$init_file" ]]; then
source "$init_file"
fi
done
+ PATH="$REPO_HOME/shared/tool:$PATH"
+ PATH="$REPO_HOME/shared/made:$PATH"
+ PATH="$REPO_HOME/shared/authored:$PATH"
+
+ # Remove duplicates, keeping the first occurrence
+ clean_path() {
+ PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//')
+ }
+ clean_path
+
export PATH
# --------------------------------------------------------------------------------